/** * @(#)DynamicBillboard.java * @version 1.51 04/06/97 * @author Robert Temple (robertt@starwave.com) */ import java.awt.*; import java.awt.image.ImageObserver; import java.net.URL; /** * Applet which displays an image on an HTML page. The image changes to a * new image after a delay. This change from one image to the next has * some kind of special effect associated with it. See the BillTransition * class for more information on these special effects. Images can have * URLs associated with them. * * USAGE NOTE: * The images this applet uses all must be the same size. The size of the * applet must be the same size of these images. * * HTML tag: * * * * * * * * * * * * * HTML tag NOTES * width - the width of the applet. Must be the same size as the images * height - the height of the applet. Must be the same size as the images * delay - the time between images in milliseconds * billboards - the number of different BillBoards(images) that you will * use * bill# - the image followed by the linking URL for a given BillBoard, * starting with number 0. this followed by a string that will appear on * status bar when the mouse is over the billboard. * The image and URL should be separated * by a comma, *NO SPACES* * bill#... - this parameter should appear 0 to one less then the value of * the "billboards" parameter. Replace # with the BillBoards * number * transitions - the number of classes that will be used as transitions, * followed by the names of these classes, separated by commas * *NO SPACES* * bgcolor - OPTIONAL - the background color the applet will use. The value is * similar to how one specifies the bgcolor tag in HTML. * target - OPTIONAL - the frame target the applet should goto when the mouse * is pressed over the applet. * ## I hope that not too many people want different targets for different * ## billboards * * DESIGN NOTES: * FAST CONTENT: * Java Applets take much longer to get to downloaded and displayed then * most other things appearing on a HTML page under Netscape 2.0b4. I * believe that most web surfers do not have the patience to wait for most * applets to load. Because of this I attempted to make this applet load * and get content to the screen as fast as possible. * * To accomplish this, The applet does little processing before waiting for * the first image to be displayed on the screen. This is unlike most other * Java applet. Most load all images and classes before showing anything * but a gray box. * * Later, the applet loads other images and classes only as they are needed. * * EXCEPTIONS: * I really would like to put more exceptions in the code, but I did not * because I wanted the fastest possible loading of classes. Hence the * smallest bytecode * * PUBLIC DATA MEMBERS: * A lot of data members are made public even though in good OO-programming * they should not be. This is done for the same reason as above, bytecode * size. * * Making data members protected, and then creating a function that allows * read only access to this data increases the bytecode size. Even when * the one line function is made final, and the code is compiled with * optimizations. :( */ public class DynamicBillboard extends java.applet.Applet implements Runnable { /** Array which holds all of the billboards */ BillData[] billboards; /** Index into the billboards array of the current billboard */ int current_billboard; /** Index into the billboards array of the next billboard */ int next_billboard; /** Array of Strings which hold the names of the different transition classes */ String[] transition_classes; /** The main thread which drives the program */ Thread thread = null; /** Current Image displayed on the screen */ Image image = null; /** * The delay time from the completion of one transition to the next transtion * Delay is initially set to -1. This is used to check to see if the * finish init function has been called before. It has if delay is * not -1 */ long delay = -1; /** Flag the keep track if the mouse is currently located inside the applet */ boolean mouse_inside_applet; /** The frame the applet will use as a target when going to a new HTML document */ String link_target_frame; /** * Initializes the applet. Performs minimal work to get the applet to the * screen ASAP. The next method, finishInit completes the initialization */ public void init() { // Check to see if the user wanted a certain background color String s = getParameter("bgcolor"); if(s != null) { Color color = new Color(Integer.parseInt(s.substring(1), 16)); setBackground(color); getParent().setBackground(color); getParent().repaint(); } // Get the total number of billboards that the applet will use & // Create an array to store the Data for each billboard billboards = new BillData[Integer.parseInt(getParameter("billboards"))]; // Chose a random billboard to start with current_billboard = next_billboard = (int)(Math.random() * billboards.length); // create the BillData object for the first billboard parseBillData(); } /** * Gets the applet parameter info about the current_billboard, and creates * a new BillData object from this info. */ void parseBillData() { // get the parameter for the next_billboard info String s = getParameter("bill" + next_billboard); int field_end = s.indexOf(","); // get the billboard's image Image new_image = getImage(getDocumentBase(), s.substring(0, field_end)); // get the billboard's URL link URL link; try { link = new URL(getDocumentBase(), s.substring(field_end + 1)); } catch (java.net.MalformedURLException e) { e.printStackTrace(); link = getDocumentBase(); } // construct the new billboard billboards[next_billboard] = new BillData(link, new_image); if(image == null) { image = new_image; } else { // force loading of the image prepareImage(new_image, this); // create the image pixels billboards[next_billboard].initPixels(size().width, size().height); } } /** * finishes the Initialization the applet */ void finishInit() { // Make sure this is only called once, otherwise, when // a user leaves our page, and comes back, this gets // called again, and it messes things up. if(delay != -1) { return; } // Get the delay between transitions in milliseconds delay = Long.parseLong(getParameter("delay")); // read in the optional target parameter link_target_frame = getParameter("target"); if(link_target_frame == null) { link_target_frame = "_top"; } // get the number of transition classes that will be used String s = getParameter("transitions"); int field_end = s.indexOf(","); // get the total number of transitions the applet will use int trans_count = Integer.parseInt(s.substring(0, field_end)); // get the transition classes that will be used transition_classes = new String[trans_count]; for(--trans_count; trans_count > 0; --trans_count) { s = s.substring(field_end + 1); field_end = s.indexOf(","); transition_classes[trans_count] = s.substring(0, field_end); } transition_classes[0] = s.substring(field_end + 1); // initialize the pixel data for the first billboard billboards[next_billboard].initPixels(size().width, size().height); mouse_inside_applet = false; } /** * Called When the mouse moves over the applet. Displays the URL link in * the status bar. Sets the mouse_inside_applet flag to true */ public boolean mouseMove(Event evt, int x, int y) { // show the URL on the status bar mouse_inside_applet = true; showStatus(billboards[current_billboard].link.toExternalForm()); return true; } /** * Called When the mouse moves out of the applet. Sets the * mouse_inside_applet flag to false */ public boolean mouseExit(Event evt, int x, int y) { // clear the URL on the status bar mouse_inside_applet = false; showStatus(""); return true; } /** * Called When the mouse button is released over the applet. Goes to the * URL link */ public boolean mouseUp(Event evt, int x, int y) { // go to the URL link of the billboard getAppletContext().showDocument(billboards[current_billboard].link, link_target_frame); return true; } /** * Overide to prevent flickering */ public void update(Graphics g) { paint(g); } /** * Called when the applet needs to be painted. Draws the image */ public void paint(Graphics g) { g.drawImage(image, 0, 0, this); } /** * Called to start the execution of the applet. */ public void start() { // there is not a next bill_board at this time... next_billboard = current_billboard; // need to set the current image image = billboards[current_billboard].image; // make the mouse appear as a link cursor when the mouse is over // the applet if(getParent() instanceof Frame) { ((Frame)getParent()).setCursor(Frame.HAND_CURSOR); } if(thread == null) { thread = new Thread(this); thread.start(); } } /** * Called to stop the execution of the applet */ public void stop() { if(thread != null) { thread.stop(); thread = null; } } /** * the main execution method of the applet */ public void run() { // Get the first image to the screen ASAP while((checkImage(image, this) & ImageObserver.ALLBITS) == 0) { try { Thread.sleep(600); } catch (InterruptedException e) {} } // do the rest of the initialization required finishInit(); // Index into the transition_classes array of the current transition int last_transition_type = -1; // reference to the actual transition object BillTransition transition; // variable to hold the time of the next transition long next_billboard_time; while(true) { // Schedule the beginning of the next transition next_billboard_time = System.currentTimeMillis() + delay; // determine which billboard to display next current_billboard = next_billboard; if(++next_billboard >= billboards.length) { next_billboard = 0; } // Load the billboard if it has not yet been loaded if(billboards[next_billboard] == null) { parseBillData(); try { Thread.sleep(120); } catch (InterruptedException e) {} } // Randomly Determine the next transition to use, don't include // the last transition in the random set, so that the applet will // not use the same transition consecutively. int transition_type = (int)(Math.random() * (transition_classes.length - 1)); if(transition_type >= last_transition_type) { ++transition_type; } last_transition_type = transition_type; try { transition = (BillTransition)Class.forName(transition_classes[ transition_type]).newInstance(); } catch(Exception e) { // NOTE: Was your class part of a package? You might need // "package_name.XXX" e.printStackTrace(); continue; } // initialize this transition transition.init(this, billboards[current_billboard].image_pixels, billboards[next_billboard].image_pixels); // get the current time and compare it against the next scheduled // transition time. If it is not yet time for the transition, wait // whatever time is needed. if(System.currentTimeMillis() < next_billboard_time) { try { Thread.sleep(next_billboard_time - System.currentTimeMillis()); } catch (InterruptedException e) {} } Graphics g = getGraphics(); // loop through each frame of the transition for(int c = 0; c < transition.cells.length; ++c) { // show the next transition image image = transition.cells[c]; // immediately paint the new image g.drawImage(image, 0, 0, null); getToolkit().sync(); try { Thread.sleep(transition.delay); } catch (InterruptedException e) {} } // the next billboard should be shown is its entirety image = billboards[next_billboard].image; // immediately paint the new image g.drawImage(image, 0, 0, null); getToolkit().sync(); transition.flushCells(); g.dispose(); // if the mouse is currently is in the applet, show the new link if(mouse_inside_applet == true) { showStatus(billboards[next_billboard].link.toExternalForm()); } // clean up resources from the completed transition transition = null; try { Thread.sleep(120); } catch (InterruptedException e) {} } } }