/** File: NewsBannerApplet.java Author: Angus McIntyre Date: 26.11.96 Updated: 27.11.96 1:25 pm Simple Java applet to display a set of strings as a banner. The banner is refreshed at intervals. HISTORY ------- 26.11.96 SLAM First version implemented 27.11.96 SLAM Added multi-directional scrolling. Did so at first in the most baroque possible manner, then, trying to work around a quirk with clipRect on certain JVM's, suddenly realised that it could be done much more cleanly and simply. I'm not even going to tell you how I did it before - it's too embarassing. TO-DO ----- Write some decent file-parsing routines which will (a) handle commented lines and (b) accept line-initial '.' characters. The StreamTokenizer appears to munge these. Write a tweak to allow strings to be shown in random order. Write a version of this that uses images rather than strings. Write a version that allows multi-part banners along the lines of the applet used at 'http://www.cnn.com'. LEGAL ----- This software is free. It can be used and modified in any way you choose, but it may not be sold, either separately or as part of a collection without explicit prior permission from the author. The author assumes no liability for any loss, damage or mental or physical trauma you may incur through use of or inability to use this software. This disclaimer must appear on any modified or unmodified version of the software in which the name of the author also appears. **/ /* ---------------------------------------------------------------------- * IMPORTS * ---------------------------------------------------------------------- */ import java.awt.*; import java.net.*; import java.io.*; import ExtendedApplet; /* ---------------------------------------------------------------------- * CLASSES * ---------------------------------------------------------------------- */ // Class: NewsBannerApplet // // An applet to display strings one after the other in a rectangular // area on the screen. public class NewsBannerApplet extends ExtendedApplet implements Runnable { // Defaults static final Font default_font = new Font("TimesRoman",Font.BOLD,14); static final Color default_background_color = Color.blue; static final Color default_text_color = Color.white; static final Color default_frame_color = Color.black; static final int default_delay = 5; // Parameters static final int maximum_strings = 20; static final int frame_margin = 2; static final int text_h_margin = 2; static final int scroll_time = 500; // Constants static final int align_left = 0; static final int align_center = 1; static final int align_right = 2; static final int scroll_direction_up = 0; static final int scroll_direction_down = 1; static final int scroll_direction_left = 2; static final int scroll_direction_right = 3; static final int scroll_direction_random = 4; // Instance variables private Thread timer_thread = null; protected String[] strings; protected Color text_color; protected Color background_color; protected Color frame_color; protected Font font; protected int delay; protected int number_of_strings; protected int frame = 0; protected Image offscreen; protected int alignment = align_left; protected int scroll_direction = scroll_direction_up; protected String string_file_path; // Method: getAppletInfo() // // Return information about the applet. public String getAppletInfo() { return "NewsBannerApplet 1.0 by Angus McIntyre / "; } // Method: getParameterInfo() // // Return information about the applet's parameter options. Read this // if you want to see what parameters this applet supports. public String[][] getParameterInfo() { String[][] info = { {"string-file", "String", "file containing strings to show (required)"}, {"font", "String", "specification of font used for display"}, {"text-alignment", "String", "alignment of text - left, right, center"}, {"scroll-direction", "String", "scroll direction - left, right, up, down, random"}, {"text-color", "Color", "color used for display"}, {"frame-color", "Color", "color used for frame of clock"}, {"background-color", "Color", "color used for display background"}, {"delay", "int", "time between each frame (seconds)"} }; return info; } // Method: init() // // Initialize the applet. public void init() { super.init(); // Read in all the parameters font = parseFontParameter("font",default_font); text_color = parseColorParameter("text-color",default_text_color); frame_color = parseColorParameter("frame-color",default_frame_color); background_color = parseColorParameter("background-color",default_background_color); delay = parseIntegerParameter("delay",default_delay); string_file_path = this.getParameter("string-file"); // Read the text alignment. Alignment defaults to left, but can be // set to center or right by passing the appropriate parameter. String align_text = this.getParameter("text-alignment"); if (align_text != null) if (align_text.equalsIgnoreCase("right")) alignment = align_right; else if (align_text.equalsIgnoreCase("center")) alignment = align_center; // Read the scroll direction. There *must* be a better way to write // this expression - I hate nested 'if's. String scroll_text = this.getParameter("scroll-direction"); if (scroll_text != null) if (scroll_text.equalsIgnoreCase("up")) scroll_direction = scroll_direction_up; else if (scroll_text.equalsIgnoreCase("down")) scroll_direction = scroll_direction_down; else if (scroll_text.equalsIgnoreCase("left")) scroll_direction = scroll_direction_left; else if (scroll_text.equalsIgnoreCase("right")) scroll_direction = scroll_direction_right; else if (scroll_text.equalsIgnoreCase("random")) scroll_direction = scroll_direction_random; // Create an array to hold all the strings we need, up to the // permitted maximum, and then fetch them from the indicated file. strings = new String[maximum_strings]; fetchStringFile(); } // Method: start() // // The animation control thread is started by this method. If it's // null, a new thread is created and launched. public void start() { if (timer_thread == null) { timer_thread = new Thread(this); timer_thread.start(); } } // Method: stop() // // Stop the thread. If the user moves off to another window, this // method will get called to stop animation. public void stop() { if ((timer_thread != null) && (timer_thread.isAlive())) timer_thread.stop(); timer_thread = null; } // Method: run() // // This method causes the applet to sleep repeatedly for a fixed // length of time, waking at intervals to update the display. // It will continue indefinitely. public void run() { while(true) { try { Thread.sleep(delay * 1000); } catch (InterruptedException e) { } updateBanner(this.getGraphics()); } } // Method: paint(Graphics) // // Called by the system to paint the applet. This in turn calls // the 'drawBanner()' method. public void paint(Graphics g) { drawBanner(g); } // Method: updateBanner(Graphics) // // Update the banner. Increment the frame counter by one and, if // we reach the limit, wrap around. Call 'mergeBanner' to scroll // the new banner into place. public void updateBanner(Graphics g) { frame++; if (frame == number_of_strings) frame = 0; mergeBanner(g); } // Method: mergeBanner(Graphics) // // Merge a banner into place. This writes the next string into an // offscreen buffer and then scrolls it into place. The direction // of the scroll is controlled by a parameter, and can be left, // right, up, down or random. public void mergeBanner (Graphics g) { int image_x,image_y, image_x_inc, image_y_inc; int lines_to_scroll, line, scroll_delay; Rectangle bounds = this.bounds(); // Set up the clip rect so that we don't draw over the frame. g.clipRect(bounds.x + frame_margin, bounds.y + frame_margin, bounds.width - (2 * frame_margin), bounds.height - (2 * frame_margin)); // Draw the new image into an offscreen buffer this.drawOffscreen(); // Find out which direction we need to scroll in. If the // parameter setting was "random", then pick a direction // at random. int scroll_dir = scroll_direction; if (scroll_dir == scroll_direction_random) scroll_dir = (int) (Math.random() * 4); // The big switch. This sets up the parameters for the scroll. // Note the use of 'default' cases throughout, in order to get // the compiler to shut up about uninitialized variables. switch (scroll_dir) { // Horizontal scroll case scroll_direction_left: case scroll_direction_right: image_y = frame_margin; image_y_inc = 0; lines_to_scroll = bounds.width - (frame_margin * 2); switch (scroll_dir) { case scroll_direction_right: image_x_inc = 1; image_x = (frame_margin - lines_to_scroll); break; case scroll_direction_left: default: image_x_inc = -1; image_x = (bounds.width - frame_margin) - 1; break; } break; // Vertical scroll case scroll_direction_up: case scroll_direction_down: default: image_x = frame_margin; image_x_inc = 0; lines_to_scroll = bounds.height - (frame_margin * 2); switch (scroll_dir) { case scroll_direction_down: image_y_inc = 1; image_y = (frame_margin - lines_to_scroll); break; case scroll_direction_up: default: image_y_inc = -1; image_y = (bounds.height - frame_margin) - 1; break; } break; } // Having set everything up, scroll the sucker. Basically, // just move the top-left corner of the image and draw it // repeatedly, with a timed pause so that it doesn't go to // fast. On slow machines, the overhead of executing the // timed pause may be such that the delay is longer than the // time alotted to pause in - in which case scrolling will // probably be jerky. scroll_delay = scroll_time / lines_to_scroll; for(line=0;line 0)) { strings[strings_read++] = tokenizer.sval; } } while ((token_read != StreamTokenizer.TT_EOF) && (strings_read < maximum_strings)); // Note how many strings we read. number_of_strings = strings_read; } // Catch exceptions. We report the error on the console, and // set up a single default string to let the user know that // it didn't work. catch (IOException e) { System.err.println("Strings couldn't be loaded because " + " the error " + e + " occurred"); strings[0] = "No strings loaded"; number_of_strings = 1; } } }