  // Benötigte Library-Klassen dem Compiler bekannt machen.
import java.awt.Graphics; 
import java.awt.Color; 
import java.awt.Font; 
import java.awt.FontMetrics; 
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.black );

  // Die Größe der Schrift.
		int size = (int)((double)size().height * 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, size().width-1, size().height, -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(size().width-1, 0,
			   size().width-1, size().height);
	}

  // 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;
	}
}
