// Benötigte Library-Klassen dem Compiler bekannt machen.
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.util.StringTokenizer;

/**
 * Laufschrift: a one-line text chaser.. or call it a ticker tape..
 * 
 * @author Carl v. Loesch <LynX@impact.pages.de>
 * @version 2.1
 * @home http://java.pages.de/
 * 
 * color parametering contributed by Thorsten Ludewig
 * 
 * @attribs width=int height=int delay=int message=string font.size=int
 *          font.family=string bg.color=int,int,int fg.color=int,int,int
 * 
 * @usage <applet code="Laufschrift.class" height=50 width=100%> <param
 *        name="delay" value=0> <param name="message" value="The Message">
 *        </applet>
 */

// Neues Objekt, basierend auf "java.applet.Applet", ausführbar von Klasse
// "Thread".
public class Laufschrift extends java.applet.Applet implements Runnable {
	// Der Text für die Laufschrift, falls nicht anders angegeben.
	String msg = "                                "
			+ " yet another ticker tape.. v2.0..   "
			+ " send postcards to CvL@java.pages.de ";
	// Schrifttyp und Infos dazu.
	Font myFont;
	FontMetrics myFontM;
	// Verzögerung pro Pixel.
	int delay = 30;
	// Vorder- und Hintergrundfarbe
	Color fgcolor = Color.orange, bgcolor = Color.black;

	// Ein Thread zur Animation der Laufschrift.
	protected Thread daemon;
	protected boolean threadSuspended = false;
	// Anzahl der zu scrollenden Pixels bis dieser Buchstabe rausgerutscht ist.
	protected int pixelsToGo = 0;

	// StrToColor konvertiert einen String im Format "rrr,ggg,bbb" in eine Farbe
	// Dez. 1995 - Thorsten Ludewig - http://www.fh-wolfenbuettel.de/~th/
	private Color StrToColor(String str, Color c) {
		if (str != null) {
			int r, g, b;
			StringTokenizer t = new StringTokenizer(str, ",");
			try {
				r = Integer.valueOf(t.nextToken()).intValue();
				g = Integer.valueOf(t.nextToken()).intValue();
				b = Integer.valueOf(t.nextToken()).intValue();
				return new Color(r, g, b);
			} catch (Exception e) {
				return c;
			}
		}
		return c;
	}

	// Dies ist kein Konstruktor. Wird vom Browser nach dem Download aufgerufen.
	public void init() {
		// Hole die HTML-<PARAM>eter aus dem <APPLET> Aufruf:

		// Der darzustellende Text.
		String tmp = getParameter("message");
		if (tmp != null)
			msg = tmp;

		// Wie schnell soll die Laufschrift sein?
		tmp = getParameter("delay");
		if (tmp != null)
			delay = Integer.parseInt(tmp);
		// Null würde den Brauser total blockieren. Nicht gut.
		if (delay == 0)
			delay++;

		// Die Farbe der Schrift
		tmp = getParameter("fg.color");
		fgcolor = StrToColor(tmp, Color.black);

		// Die Farbe des Hintergrundes
		tmp = getParameter("bg.color");
		bgcolor = StrToColor(tmp, Color.white);

		// Die Größe der Schrift.
		int size = (int) ((double) getHeight() * 0.66);
		tmp = getParameter("font.size");
		if (tmp != null)
			size = Integer.parseInt(tmp);

		// Die Schriftfamilie.
		tmp = getParameter("font.family");
		if (tmp == null)
			tmp = "Helvetica";

		// Schrift holen inklusive Schriftgrösseninfos.
		myFont = new Font(tmp, Font.PLAIN, size);
		myFontM = getFontMetrics(myFont);

		// Hintergrundfarbe einstellen.
		setBackground(bgcolor);
	}

	// Diese Methode wird bei jedem Fensteraufbau aufgerufen,
	// z.B. bei überlappenden Fenstern.
	public void paint(Graphics g) {

		// Wir tun hier gar nichts, müssen das aber hiermit klarstellen,
		// da sonst die Default-paint() Methode von »Applet« verwendet wird,
		// welche eine Schachtel zeichnet.
	}

	// Diese Methode wird von der repaint() Funktion aufgerufen
	// um Veränderungen am bestehenden Erscheinungsbild des Applets zu machen.
	public void update(Graphics g) {

		// Hier wird die Bitmap vollständig um 1 Pixel nach links geschoben.
		g.copyArea(1, 0, getWidth() - 1, getHeight(), -1, 0);

		// Schriftfarbe einstellen
		g.setColor(fgcolor);

		// Nicht die vom Browser zuletzt verwendete Schrift, sondern...
		g.setFont(myFont);

		// Und nun wird der Text hingeschrieben, was nicht draufpasst wird
		// nicht dargestellt. drawString zeichnet von unten nach oben, deshalb
		// muß die Fontgröße als y-Koordinate mitgegeben werden.
		g.drawString(msg, pixelsToGo, myFontM.getHeight());

		// Haben wir den aktuellen Buchstaben vollständig herausgeschoben?
		if (--pixelsToGo < 0) {
			// Den nächsten Buchstaben herausschneiden.
			String buchstabe = msg.substring(0, 1);
			// Die Breite des Buchstabens merken.
			pixelsToGo = myFontM.stringWidth(buchstabe) - 1;
			// Den Buchstaben ans Ende des Strings stellen.
			msg = msg.substring(1) + buchstabe;
		}

		// Wieder die Hintergrundsfarbe einstellen.
		g.setColor(bgcolor);
		// An den rechten Rand einen schwarzen Strich zeichnen, damit der
		// copyArea
		// keinen Mist baut.
		g.drawLine(getWidth() - 1, 0, getWidth() - 1, getHeight());
	}

	// Wird aufgerufen jedesmal wenn die Seite mit dem Applet
	// auf dem Schirm erscheint - bei HotJava jedenfalls.
	public synchronized void start() {
		// Läuft unser Animationsthread schon?
		if (daemon == null) {
			// Nein, na dann allozieren wir einen. Ein Thread, der mit einem
			// Objekt (this)
			// konstruiert wird, tut beim start() nichts anderes als this.run()
			// aufzurufen.
			daemon = new Thread(this);
			daemon.start();
		}
	}

	// Wird aufgerufen, wenn der Benutzer auf eine andere Web Seite wechselt.
	// Das java.applet.Applet kann im Hintergrund weiter aktiv bleiben.
	public synchronized void stop() {
		// Wir halten unseren Hintergrundsthread wieder an.
		daemon.stop();
		// Und löschen zusätzlich noch die Variable, damit
		// die Garbage-Collection den Thread dealloziert.
		daemon = null;
	}

	// Der neue Thread ruft diese Methode auf, sie läuft während der gesamten
	// Animation.
	public void run() {
		// Andere Aktivitäten sind wichtiger als wir.
		Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
		while (daemon != null) {
			// Der Maschine ein bisschen Pause gönnen.
			try {
				Thread.sleep(delay);
			} catch (InterruptedException e) {
			}
			// update() aufrufen lassen um die Laufschrift zu erneuern.
			repaint();
		}
		// Die Variable wurde gelöscht, also sind wir unerwünscht und gehen.
	}

	// Zusatzfeature: Mit der Maus kann man die Laufschrift anhalten.
	public boolean mouseDown(java.awt.Event evt, int x, int y) {
		if (threadSuspended)
			daemon.resume();
		else
			daemon.suspend();

		threadSuspended = !threadSuspended;
		return true;
	}
}

