Main Page | Alphabetical List | Class List | File List | Class Members

JWebPresenterSS.java

00001 /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
00002  * Project     : JWebPresenter                                          *
00003  * Filename    : JWebPresenterSS.java                                   *
00004  * Introduction: JWebPresenter is a bitmap-image based presenting tool. *
00005  *               This is a client program, which is a simple solution   *
00006  *               (series SS), see also series AS (advanced solution).   *
00007  * Author      : Liang Zhao (writebear \at yahoo \dot com)              *
00008  * Version     : v1.0  2004/02/29, initial public release               *
00009  *               v1.01 2004/03/10, remove the (useless) mcMode test     *
00010  *               v1.1  2004/04/20, change AWT code to JFC (SWING) code! *
00011  *                                 pop-up menu added.                   *
00012  *                                 other major changes.                 *
00013  *               v1.2  2004-05-21, dynamic presenting URLs to treat     *
00014  *                                 the cache problem more cleverly.     *
00015  *               v1.21 2004-06-18, add a reset function                 *
00016  *               v1.22 2004-07-13, documentation improvement.           *
00017  *                     You can use doxygen doxygen.cfg to get the docs. *
00018  *               v1.23 2005-01-21, pre-arranged size added, thanks Dave *
00019  *                                 better command line parameters.      *
00020  *               v1.3  2005-01-23, browser embed mode, thanks Dave.     *
00021  * Developed by: Linux, Java 2 SDK SE v1.4.2, etc.                      *
00022  * Note        : It was originally written in pure AWT code for my PDA  *
00023  *               (Zaurus SL-C760), but finally I got tired to use some  *
00024  *               odd features of AWT model such as focus.... Thus from  *
00025  *               version 1.1 (2004-04-17), I decided to use Swing.      *
00026  *               v1.0x is based on AWT, though there is no new feature. *
00027  * Requirement : JRE 1.4 or above with Swing support.                   *
00028  ************************************************************************
00029  * Copyright (C) 2004-2005 Liang Zhao                                   *
00030  *                                                                      *
00031  * This program is free software; you can redistribute it and/or modify *
00032  * it under the terms of the GNU General Public License as published by *
00033  * the Free Software Foundation; either version 2 of the License, or    *
00034  * (at your option) any later version.                                  *
00035  *                                                                      *
00036  * This program is distributed in the hope that it will be useful,      *
00037  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
00038  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
00039  * GNU General Public License for more details.                         *
00040  *                                                                      *
00041  * You should have received a copy of the GNU General Public License    *
00042  * along with this program; if not, write to the Free Software          *
00043  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 *
00044  * USA, or you can get it from http://www.gnu.org/licenses/gpl.txt      *
00045  *>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
00046 
00047 import java.awt.*;
00048 import java.awt.image.*;
00049 import java.awt.event.*;
00050 import java.io.*;
00051 import java.net.*;
00052 import javax.swing.*;
00053 
00054 /**
00055  * The main class.
00056  * We basically have two running mode: applet or application.
00057  * When in applet mode, parameters can be specified by the WWW page
00058  * where JWebPresenterSS is embeded (see the sample index.html for
00059  * detailed usage). In application mode, on the other hand, parameters
00060  * must be given from the command line.
00061  */
00062 public class JWebPresenterSS extends JApplet {
00063     /**
00064      * Version description.
00065      */
00066     final static String ver = "JWebPresenter SS 1.3 (2005-01-23), L. Zhao";
00067 
00068     /**
00069      * Reference to myself (the applet).
00070      */
00071     final JApplet me = this;
00072 
00073     /**
00074      * Reference to the frame instance, valid only for nonempty cmdText.
00075      */
00076     JWPFrameSS frame = null;
00077 
00078     /**
00079      * We can initialize the applet instance by adding a button, which shows
00080      * the frame when pressed (parameters are passed by JApplet.getParameter()).
00081      * 2005-01-23 Alternatively, we can show the picture in the viewer (e.g.,
00082      * browser) with no new window by giving empty (or null) cmdText.
00083      * @param cmdText text (empty for no window) of the start button, 2005-01-23.
00084      * @param slideURL URL of the FIRST slide (e.g., http://host/slide-1.png).
00085      * @param debugMode 0 to disable the debug function, nonzero to enable.
00086      * @param ctlURL URL for the initial control parameters, see document too.
00087      *        If given ctlURL is invalid, then slideURL is used repeatedly.
00088      * @param fetchTime default time interval (in 100ms) to fetch parameters.
00089      * @param baseURL the base URL, see the note of 2004-04-21.
00090      * @param windowWidth the width of JWebPresenter window (see NOTICE), 2005-01-20
00091      * @param windowHeight the height of JWebPresenter window, 2005-01-20
00092      * <p>NOTICE: 2004-04-21, both slideURL and ctlURL can be (re-)specified
00093      *            by control parameters Slide and nextCtl (ctlURL) respectively.
00094      *            They (slideURL and ctlURL) are determined as follows:
00095      *              If Slide (nextCtl) is an absolute URL, use it;
00096      *              otherwise use baseURL + Slide (baseURL + nextCtl).</p>
00097      * <p>NOTICE: 2005-01-20, Dove told me that specifying window size at
00098      *            start time could be helpful. This is. The default size is
00099      *            full screen if width or height is invalid.
00100      */
00101     public void init() {
00102         Container pane = getContentPane(); // get the drawing pane.
00103 
00104         String cmdText = me.getParameter("cmdText"); // Get the cmdText string.
00105         if (cmdText == null) cmdText = "";           // Then cmdText is not null.
00106         if (cmdText.length() == 0) { // empty string means not to create window.
00107             pane.add(new JWPPanelSS(null, // no window
00108                                     me.getParameter("slideURL"),
00109                                     me.getParameter("debugMode"),
00110                                     me.getParameter("ctlURL"),
00111                                     me.getParameter("baseURL"),
00112                                     me.getParameter("fetchTime"),
00113                                     true)); // "true" is for applet.
00114         }
00115         else { // create the window
00116             JButton startButton = new JButton(cmdText);
00117             startButton.addActionListener(new ActionListener() {
00118                 public void actionPerformed(ActionEvent e) {
00119                     if (frame == null) {
00120                         frame = new JWPFrameSS(me.getParameter("slideURL"),
00121                                                me.getParameter("debugMode"),
00122                                                me.getParameter("ctlURL"),
00123                                                me.getParameter("baseURL"),
00124                                                me.getParameter("fetchTime"),
00125                                                me.getParameter("windowWidth"),
00126                                                me.getParameter("windowHeight"),
00127                                                true); // "true" is for applet.
00128                     }
00129                     frame.setVisible(true);
00130                 }
00131             });
00132             pane.add(startButton);
00133         }
00134     }
00135     
00136     /**
00137      * main function, hence we can also run as an application.
00138      */
00139     public static void main(String argv[]) {
00140         if (argv.length < 1) {
00141             System.out.println("JWebPresenter Project :: " + ver + "\n"
00142                 + "Usage: java JWebPresenterSS parameters\n"
00143                 + "Parameters can be given like name=value (no space around =) in any order.\n"
00144                 + "  slideURL:  initial URL to get slides, can be omitted if given by ctlURL.\n"
00145                 + "  ctlURL:    initial URL to get control parameters;\n"
00146                 + "  debugMode: debug mode, 0 to disable (default), 1 or above to enable;\n"
00147                 + "  baseURL:   base URL, used if a URL obtained from ctlURL is not absolute;\n"
00148                 + "  fetchTime: time in 100ms to fetch targets (default 50);\n"
00149                 + "  width:     width of presentation window (can be modified after start);\n"
00150                 + "  height:    height of presentation window (can be modified after start).\n"
00151                 + "If no width or height is specified, full screen mode is used.\n"
00152                 + "Parameter's name can be abbreviated if no confusion happens.\n"
00153                 + "For example: s=ftp://host/slide.png c=http://host/ctl.txt b=ftp://host/\n");
00154             System.exit(1);
00155         }
00156         
00157         String slideURL  = "";
00158         String ctlURL    = "";
00159         String debugMode = "";
00160         String baseURL   = "";
00161         String fetchTime = "";
00162         String width     = "";
00163         String height    = "";
00164 
00165         for (int i=0; i<argv.length; i++) {
00166             int j = argv[i].indexOf("=");
00167             if (j == -1) {
00168                 System.out.println("Bad parameter ("+argv[i] +") omitted");
00169                 continue;
00170             }
00171             // else {
00172             switch (argv[i].charAt(0)) {
00173                 case 's' : slideURL  = argv[i].substring(j+1); break;
00174                 case 'c' : ctlURL    = argv[i].substring(j+1); break;
00175                 case 'd' : debugMode = argv[i].substring(j+1); break;
00176                 case 'b' : baseURL   = argv[i].substring(j+1); break;
00177                 case 'f' : fetchTime = argv[i].substring(j+1); break;
00178                 case 'w' : width     = argv[i].substring(j+1); break;
00179                 case 'h' : height    = argv[i].substring(j+1); break;
00180                 default  :
00181                     System.out.println("Bad parameter ("+argv[i]+") omitted.");
00182             }
00183         }
00184 
00185         new JWPFrameSS(slideURL, debugMode, ctlURL, baseURL, fetchTime, width, height, false).setVisible(true);
00186     }
00187 }
00188 
00189 /**
00190  * The frame class (not used if the applet is embeded in a viewer).
00191  * <p>We want to show in fullscreen mode, however,
00192  * it is not possible in Java 1.4. Thus we show a maximized frame.
00193  */
00194 class JWPFrameSS extends JFrame {
00195     /**
00196      * Constructor.
00197      * @param s String for slideURL.
00198      * @param d String for debugMode.
00199      * @param c String for ctlURL.
00200      * @param b String for baseURL.
00201      * @param f String for fetchTime.
00202      * @param w String for width.
00203      * @param h String for height.
00204      * @param app true for application, false for applet.
00205      */
00206     JWPFrameSS(String s, String d, String c, String b, String f, String w, String h, boolean app) {
00207         setDefaultCloseOperation(app?DISPOSE_ON_CLOSE:EXIT_ON_CLOSE);
00208 
00209         int width  = 0;
00210         try {
00211             width = Integer.parseInt(w);
00212         }
00213         catch (NumberFormatException e) {
00214             width = 0;
00215         }
00216 
00217         int height = 0;
00218         try {
00219             height = Integer.parseInt(h);
00220         }
00221         catch (NumberFormatException e) {
00222             height = 0;
00223         }
00224 
00225         Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
00226         if (width < 1 || width > screen.width) width = screen.width;
00227         if (height < 1 || height > screen.height) height = screen.height;
00228         setSize(width, height);
00229 
00230         // Set the window be undecoreated for full screen mode -- require JRE 1.4+.
00231         if (width == screen.width && height == screen.height) setUndecorated(true);
00232 
00233         getContentPane().add(new JWPPanelSS(this, s, d, c, b, f, app));
00234    }
00235 }
00236  
00237 /**
00238  * The panel class.
00239  * <p>This is the main part of this program.
00240  *  A Timer task is performed repeatedly to get control parameters
00241  *  from ctlURL, and new slide is feteched if needed.</p>
00242  */
00243 class JWPPanelSS extends JPanel implements ActionListener {
00244     /**
00245      * Reference to my ancestor frame (null if embeded in a viewer).
00246      */
00247     JFrame frame;
00248 
00249     /**
00250      * Am I an applet or not? Method quit() depends on it.
00251      */
00252     boolean isApplet;
00253 
00254     /**
00255      * Timer object to control the refresh process.
00256      */
00257     Timer timer;
00258 
00259     /**
00260      * Default toolkit to fetch the slide.
00261      */
00262     Toolkit tk;
00263 
00264     /**
00265      * The base URL (String) for ctlURL and slideURL.
00266      */
00267     String baseURL;
00268 
00269     /**
00270      * The original control URL (given to constructor, used for reset).
00271      */
00272     URL orig_ctlURL;
00273 
00274     /**
00275      * The original slide URL (given to constructor, used for reset).
00276      */
00277     URL orig_slideURL;
00278 
00279     /**
00280      * URL for the control commands.
00281      */
00282     URL urlCtl;
00283 
00284     /**
00285      * URL for the slide image.
00286      */
00287     URL urlSlide;
00288 
00289     /**
00290      * Fetch time interval (in ms!).
00291      */
00292     int fetchTime;
00293 
00294     /**
00295      * Debug mode: true to debug, false to not (2004-04-19).
00296      */
00297     boolean debugMode;
00298 
00299     /**
00300      * Connection object for getting parameters.
00301      */
00302     URLConnection c;
00303 
00304     /**
00305      * Counter for the number of fetched slides.
00306      */
00307     int countSlide = 0;
00308 
00309     /**
00310      * Fetched counter for control parameters.
00311      */
00312     int countCtl = 0;
00313 
00314     /**
00315      * Shall we use advanced mode (= skip slide with the same sequence no.)? See actionPerformed().
00316      */
00317     boolean advancedMode;
00318 
00319     /**
00320      * Image object to hold the target slide.
00321      */
00322     Image img;
00323 
00324     /**
00325      * Mediatracker to get img.
00326      */
00327     MediaTracker tracker = new MediaTracker(this);
00328 
00329     /**
00330      * FastMode setting: if true, fetch ctlURL repeatedly until error (disregard fetchTime);
00331      * If false, fetch ctlURL repeatedly according to the timer.
00332      * <p>This is useful for slow clients, who can drop slides to keep up with the latest slide.</p>
00333      */
00334     boolean fastMode = false;
00335 
00336     // The follows variables are used in processing control parameters. 
00337 
00338     /**
00339      * BufferedReader for reading the control parameters from urlCtl.
00340      */
00341     BufferedReader in;
00342 
00343     /**
00344      * Temporary one line text read from in.
00345      */
00346     String str;
00347 
00348     /**
00349      * Version of the command set, valid values are 1, 2...
00350      */
00351     int jwpVersion;
00352 
00353     /**
00354      * Previous sequence number, -1 for none (or nonexist).
00355      */
00356     long lastSeq = -1;
00357 
00358     /**
00359      * Current sequence number, valid values are 0, 1, 2...
00360      */
00361     int seq;
00362     
00363     // 2004-04-17: add a popup menu to give the user some optional setting.
00364 
00365     /**
00366      * Popup menu.
00367      */
00368     JPopupMenu menu = new JPopupMenu("Popup Menu");
00369 
00370     /**
00371      * CheckBox menu item to enable/disable the decoration of window.
00372      * 2005-01-22: all decorate related items are valid only for non-null frame.
00373      */
00374     JCheckBoxMenuItem decorateItem;
00375 
00376     /**
00377      * CheckBox menu item to fill/not fill the image to window.
00378      */
00379     JCheckBoxMenuItem fillItem = new JCheckBoxMenuItem("Fill Image to Window");
00380 
00381     /**
00382      * Show if we should resize the image to fill window.
00383      */
00384     boolean bResizeToFill = false;
00385 
00386     /**
00387      * Show if we should decorate the window.
00388      * 2005-01-22: all decorate related items are valid only for non-null frame.
00389      */
00390     boolean bDecorate;
00391 
00392     /**
00393      * Information dialog.
00394      */
00395     JDialog infoDialog;
00396 
00397     /**
00398      * Label to show information in infoDialog.
00399      */
00400     JLabel infoLabel;
00401 
00402     /**
00403      * When the name is "More...", show more information.
00404      */
00405     JButton moreButton;
00406     
00407     /**
00408      * The debug function (output to err if debugMode is true).
00409      */
00410     public void debugMsg(String msg) {
00411         if (debugMode) System.err.println("(info) " + msg);
00412     }
00413     
00414     /**
00415      * quit function (hide the frame for applet, otherwise stop the system).
00416      */
00417     public void quit() {
00418         if (isApplet && frame != null) frame.setVisible(false);
00419         else System.exit(0);
00420     }
00421         
00422     /**
00423      * Constructor: initializations.
00424      * @param f JFrame for the ancestor frame instance.
00425      * @param slide String for slideURL.
00426      * @param debug String for debugMode.
00427      * @param ctl String for ctlURL.
00428      * @param base String for baseURL.
00429      * @param fetch String for fetchTime.
00430      * @param app true for application, false for applet.
00431      */
00432     JWPPanelSS(JFrame f, String slide, String debug, String ctl, String base, String fetch, boolean app) {
00433         frame    = f;
00434         baseURL  = (base==null)?"":base; // non-null for simplicity.
00435         isApplet = app;
00436         tk       = Toolkit.getDefaultToolkit();
00437         
00438         // Add a hide/exit method: ESC key. This seems not work, why?!!
00439         addKeyListener(new KeyAdapter() {
00440             public void keyPressed(KeyEvent e) {
00441                 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
00442                     quit();
00443                 }
00444             }
00445         });
00446 
00447         // Mouse triple (not double to avoid mistake) clicks can also quit.
00448         addMouseListener(new MouseAdapter() {
00449             public void mouseClicked(MouseEvent e) {
00450                 // 2004-04-21, changed to > 2 to reduce miss operation.
00451                 if (e.getClickCount() > 2)  {
00452                     quit();
00453                 }
00454             }
00455            // Catch the next mouse events to show the popup menu.
00456             public void mouseReleased(MouseEvent e) {
00457                 if (e.isPopupTrigger()) {
00458                     menu.show(e.getComponent(), e.getX(), e.getY());
00459                 }
00460             }
00461             
00462             public void mousePressed(MouseEvent e) {
00463                 if (e.isPopupTrigger()) {
00464                     menu.show(e.getComponent(), e.getX(), e.getY());
00465                 }
00466             }
00467         });
00468 
00469         // First, let us get the debugMode. Set false (i.e. no debug) for error.
00470         try {
00471             debugMode = (Integer.parseInt(debug) != 0);
00472         }
00473         catch (NumberFormatException e) {
00474             debugMode = false;
00475         }
00476 
00477         // Get the control URL if it is valid, otherwise null.
00478         try {
00479             urlCtl = new URL(ctl);
00480             orig_ctlURL = urlCtl; // save a copy (!) to orig_ctlURL
00481         }
00482         catch (MalformedURLException e) {
00483             debugMsg("Bad or no ctlURL (" + ctl + "). Set to NONE.");
00484             orig_ctlURL = null;
00485             urlCtl      = null;
00486         }
00487 
00488         // Get the target URL if it is valid, otherwise null.
00489         try {
00490             urlSlide = new URL(slide);
00491             orig_slideURL = urlSlide; // save a copy.
00492         }
00493         catch (MalformedURLException e) {
00494             debugMsg("Bad or no slideURL (" + slide + "). Set to NONE.");
00495             orig_slideURL = null;
00496             urlSlide      = null;
00497         }
00498 
00499         // We can do nothing (thus quit) if both urlCtl and urlSlide are both null.
00500         if ((urlCtl == null) && (urlSlide == null)) {
00501             debugMsg("Oops, what can I do for null ctlURL and null slideURL?");
00502             JButton btnQuit = new JButton("(Invalid input) Check ctlURL and slideURL! (Click to quit)");
00503             btnQuit.addActionListener(new ActionListener() {
00504                 public void actionPerformed(ActionEvent e) {
00505                     quit();
00506                 }
00507             });
00508             add(btnQuit);
00509             return; // Skip other initializations.
00510         }
00511 
00512         // Get the fetchTime, set to 5s (default) for exception.
00513         try {
00514             fetchTime = Integer.parseInt(fetch) * 100;
00515         }
00516         catch (NumberFormatException e) {
00517             debugMsg("Bad or no fetchTime. Set to 5s.");
00518             fetchTime = 5000;
00519         }
00520         
00521         try {
00522             debugMsg("Java version = " + System.getProperty("java.version"));
00523         }
00524         catch (SecurityException e) {}
00525         
00526         debugMsg("--- Parsed parameters are as follows ---\n" +
00527                  "  slideURL  = " + urlSlide + "\n" +
00528                  "  debugMode = " + debugMode + "\n" +
00529                  "  ctlURL    = " + urlCtl + "\n" +
00530                  "  baseURL   = " + baseURL + "\n" +
00531                  "  fetchTime = " + fetchTime + "ms\n" +
00532                  "  Parameters accepted :-).");
00533 
00534         // Add popup menu items.
00535         // Full screen menu if frame is not null
00536         if (frame != null) {
00537             menu.add(new AbstractAction("Full Screen") {
00538                 public void actionPerformed(ActionEvent e) {
00539                     Dimension screen = tk.getScreenSize();
00540                     bDecorate = false;      // disable window docoration
00541                     decorateItem.setSelected(false);
00542                     bResizeToFill = false;  // disable the fill option
00543                     fillItem.setSelected(false);
00544                     frame.dispose(); // we need this to change decoration style
00545                     frame.setUndecorated(true);
00546                     frame.setLocation(0, 0);
00547                     frame.setSize(screen.width, screen.height);
00548                     setSize(screen.width, screen.height);
00549                     frame.setVisible(true);
00550                     repaint();
00551                 }
00552             });
00553         }
00554 
00555         /* 2004-06-18: add a reset function which reset urlSlide and
00556          * urlCtl to orig_slideURL and orig_ctlURL. This may be helpful
00557          * in trouble, and one does not want to restart the Java VM.
00558          */
00559         menu.add(new AbstractAction("Reset (= restore URLs to initial values)") {
00560             public void actionPerformed(ActionEvent e) {
00561                 urlCtl   = orig_ctlURL;
00562                 urlSlide = orig_slideURL;
00563                 timer.setDelay(fetchTime);  // restart the timer. 
00564                 timer.setInitialDelay(0);
00565                 timer.restart();
00566                 debugMsg("URLs resetted. Starting new fetching...");
00567             }
00568         });
00569 
00570         /* 2004-05-21: Zoom in/out seems not useful for presentations.
00571          * Besides, they require more complicate coding, e.g., mouse handling.
00572          * Therefore I removed the not-finished zoom function.
00573          */
00574         menu.addSeparator();
00575 
00576         // decorate/undecoreate window item
00577         if (frame != null) {
00578             decorateItem = new JCheckBoxMenuItem("Decorate Window (then resize, move, etc)");
00579             bDecorate = !frame.isUndecorated();
00580             decorateItem.setSelected(bDecorate);
00581             decorateItem.addActionListener(new ActionListener() {
00582                 public void actionPerformed(ActionEvent e) {
00583                     bDecorate = !bDecorate;
00584                     frame.dispose();
00585                     frame.setUndecorated(!bDecorate);
00586                     decorateItem.setSelected(bDecorate);
00587                     frame.setVisible(true);
00588                     repaint();
00589                 }
00590             });
00591             menu.add(decorateItem);
00592         }
00593 
00594         fillItem.setSelected(bResizeToFill);
00595         fillItem.addActionListener(new ActionListener() {
00596             public void actionPerformed(ActionEvent e) {
00597                 bResizeToFill = !bResizeToFill;
00598                 repaint();
00599             }
00600         });
00601         menu.add(fillItem);
00602 
00603         JCheckBoxMenuItem fastItem = new JCheckBoxMenuItem("Fast Mode (= skip intermediate slides)");
00604         fastItem.setSelected(fastMode);
00605         fastItem.addActionListener(new ActionListener() {
00606             public void actionPerformed(ActionEvent e) {
00607                 fastMode = !fastMode;
00608             }
00609         });
00610         menu.add(fastItem);
00611 
00612         JCheckBoxMenuItem debugItem = new JCheckBoxMenuItem("Debug to Java Console");
00613         debugItem.setSelected(debugMode);
00614         debugItem.addActionListener(new ActionListener() {
00615             public void actionPerformed(ActionEvent e) {
00616                 debugMode = !debugMode;
00617             }
00618         });
00619         menu.add(debugItem);
00620 
00621         menu.addSeparator();
00622         
00623         menu.add(new AbstractAction("Information...") {
00624             public void actionPerformed(ActionEvent e) {
00625                 if (infoDialog != null) infoDialog.setVisible(true);
00626             }
00627         });
00628       
00629         // quit menu is only available if frame is not null. 
00630         if (frame != null) { 
00631             menu.add(new AbstractAction("Quit") {
00632                 public void actionPerformed(ActionEvent e) {
00633                     quit();
00634                 }
00635             });
00636         }
00637 
00638         add(menu);
00639 
00640         // create the information dialog.
00641         infoDialog = new JDialog(frame, "Information", true);
00642         Container con = infoDialog.getContentPane();
00643         infoLabel = new JLabel("<HTML><BR><CENTER><B>"+JWebPresenterSS.ver
00644             +"</B></CENTER><BR></HTML>", SwingConstants.CENTER);
00645         con.add(infoLabel);
00646         JPanel pOK = new JPanel();
00647         JButton ok = new JButton("OK");
00648         ok.addActionListener(new ActionListener() {
00649             public void actionPerformed(ActionEvent e) {
00650                 infoDialog.setVisible(false);
00651                 infoLabel.setText("<HTML><BR><CENTER><B>"+JWebPresenterSS.ver
00652                     +"</B></CENTER><BR></HTML>");
00653                 moreButton.setText("More...");
00654                 infoDialog.pack();
00655             }
00656         });
00657         pOK.add(ok);
00658         
00659         moreButton = new JButton("More...");
00660         moreButton.addActionListener(new ActionListener() {
00661             public void actionPerformed(ActionEvent e) {
00662                 String command = e.getActionCommand();
00663                 
00664                 if (command.equals("More...")) {
00665                     infoLabel.setText("<HTML><BR><CENTER><B>"+JWebPresenterSS.ver
00666                         +"</B></CENTER><BR>"
00667                         +"Base URL = "+baseURL+"<BR>"
00668                         +"Slide URL = "+((urlSlide==null)?"N/A":urlSlide.toString())+"<BR>"
00669                         +"Control URL = "+((urlCtl==null)?"N/A":urlCtl.toString())+"<BR>"
00670                         +"Fetch interval = "+fetchTime+"ms<BR>"
00671                         +"JWP version = "+((jwpVersion==0)?"N/A":String.valueOf(jwpVersion))+"<BR>"
00672                         +"Advanced mode = "+(advancedMode?"Enabled":"Disabled")+"<BR>"
00673                         +"Fetch counter (control/slide) = "+countCtl+"/"+countSlide+"<BR>"
00674                         +"</HTML>");
00675                     moreButton.setText("Less...");
00676                 }
00677                 else {
00678                     infoLabel.setText("<HTML><BR><CENTER><B>"+JWebPresenterSS.ver
00679                         +"</B></CENTER><BR></HTML>");
00680                     moreButton.setText("More...");
00681                 }
00682                 infoDialog.pack();
00683             }
00684         });
00685         pOK.add(moreButton);
00686         
00687         con.add(pOK, BorderLayout.SOUTH);
00688         infoDialog.pack();
00689 
00690         debugMsg("Initilization finished. Ready to run...");
00691 
00692         timer = new Timer(fetchTime, this);
00693         timer.setInitialDelay(0);
00694         timer.start();
00695     }
00696 
00697     /**
00698      * ActionEvent hander, which can happen only for our timer event.
00699      * @param e ActionEvent.
00700      * <p>First, we have to get control parameters in order to keep up
00701      * with the presentation. Second, we get the slide according to
00702      * control parameters, except for when the frame is not being shown.
00703      * If fastMode is true, repeat parseCommand() until it returns
00704      * false, which means an I/O error happened in fetch control parameter
00705      * (or ctlURL = null. But it is rare). 2004-05-21</p>
00706      */
00707     public void actionPerformed(ActionEvent e) {
00708         int oldFetchTime = fetchTime;
00709         boolean succ;
00710 
00711         do {
00712             debugMsg("Time stamp: " + System.currentTimeMillis());
00713             succ = parseCommands(); // urlSlide, urlCtl, fetchTime, seq may change.
00714         } while (fastMode & succ);
00715 
00716         // Reschedule if fetch interval (fetchTime) is changed.
00717         if (fetchTime != oldFetchTime) {
00718             debugMsg("Fetch timer is re-schduled to " + fetchTime + "ms.");
00719             timer.setDelay(fetchTime); // restart the timer. 
00720             timer.restart();
00721         }
00722             
00723         // Skip the slide fetch if frame is non-null but not being shown.
00724         if (frame != null && !frame.isShowing()) return;
00725 
00726         // 2005-05-18: notice that either urlSlide != null or urlCtl != null.
00727         // See the constructor JWPPanelSS() and parseCommands().
00728 
00729         if ((advancedMode == true) && (seq == lastSeq)) {
00730             debugMsg("Target fetch omitted due to the same sequence number.");
00731         }
00732         else { // fetch the new slide.
00733             if (urlSlide == null) return;   // For a bad control parameter Slide.
00734             countSlide++;                   // Else fetch the slide.
00735             tracker.removeImage(img);       // Remove to avoid memory leak!
00736             img = tk.createImage(urlSlide); // createImage has no cache problem
00737             tracker.addImage(img, 0);       // Use tracker to watch the load.
00738             try { tracker.waitForID(0); }
00739             catch (InterruptedException ie) {
00740                 debugMsg("Interrupted in loading image from "+urlSlide);
00741                 tracker.removeImage(img);   // For interrupted load, remove it.
00742                 img = null;                 // And set the image to null.
00743             }
00744             if (img == null) {              // For any error,
00745                 debugMsg("Target fetch failed (count = " + countSlide + ").");
00746             } else {                        // Otherwise,
00747                 debugMsg("Target fetch succeeded (count = " + countSlide + ").");
00748                 if (advancedMode == true) { lastSeq = seq; }
00749                 repaint();
00750             }
00751         }
00752     }
00753     
00754     /**
00755      * paintComponent (draw the image at the center after download finishes).
00756      * @param g Graphics to draw the image.
00757      * <p>The text drawing position (15, 15) is rather dirty. I'd like to improve if I have more time.
00758      */
00759     public void paintComponent(Graphics g) {
00760         super.paintComponent(g);
00761 
00762         if (img == null) { // only happens for error including remote not ready.
00763             debugMsg("No image is available for painting!");
00764             g.drawString("No image! Error or remote not ready?", 15, 15);
00765             return;
00766         }
00767         
00768         if (tracker.statusAll(false) == MediaTracker.COMPLETE) {
00769             if (bResizeToFill == false) {
00770                 int upper_left  = (getWidth() - img.getWidth(this)) >> 1;
00771                 int lower_right = (getHeight() - img.getHeight(this)) >> 1;
00772                 g.drawImage(img, upper_left, lower_right, this);
00773             } else {
00774                 g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
00775             }
00776         }
00777         else {
00778             debugMsg("Painted image is not ready!");
00779             g.drawString("Waiting for the load of image...", 15, 15);
00780         }
00781     }
00782 
00783     /**
00784      * Get the unsigned int parameter.
00785      * @param msg String for the message to extract.
00786      * @param pattern String for the pattern to handle.
00787      * @return int of the result, negative for errors.
00788      * <p>msg should be in "pattern=value" format (NO space before and after =).</p>
00789      */
00790     public static int getUInt(String msg, String pattern) {
00791         int value = -1; // -1 shows error.
00792         
00793         if (msg.startsWith(pattern+"=") == true) {
00794             try {
00795                 value = Integer.parseInt(msg.substring(msg.indexOf("=")+1));
00796             }
00797             catch (NumberFormatException e) {
00798                 value = -1; // -1 means error.
00799             }
00800         }
00801         return value;
00802     }
00803     
00804     /**
00805      * Get the URL parameter, null for errors.
00806      * @param msg String for the message to extract.
00807      * @param pattern String for the pattern to handle.
00808      * @return URL for the result, null for errors.
00809      * <p>msg should be in "pattern=value" format. We use baseURL!</p>
00810      */
00811     public URL getURL(String msg, String pattern) {
00812         URL u = null;
00813         
00814         if (msg.startsWith(pattern+"=") == true) {
00815             try { u = new URL(msg.substring(msg.indexOf("=")+1)); }
00816             catch (MalformedURLException e) { u = null; }
00817             // In case of error, we try to use baseURL as the base.
00818             if (u == null) {
00819                 try {
00820                     u = new URL(baseURL + msg.substring(msg.indexOf("=")+1));
00821                 }
00822                 catch (MalformedURLException e) { u = null; }
00823             }
00824         }
00825         return u;
00826     }
00827       
00828     /**
00829      * Parse the control parameters by a connection.
00830      * @return true for success, false for failure.
00831      * <p>It sets advancedMode to true for valid descriptions.
00832      * Besides, any old value is not changed if new value is invalid.
00833      */
00834     private boolean parseCommands() {
00835         int v, s, t;    // temporary variables for jwpVersion, seq and fetchTime
00836         URL cURL, sURL; // temporary variables to find the ctlURL and slideURL
00837         
00838         advancedMode = false;
00839         if (urlCtl == null) return false; // return (advancedMode is thus false)
00840        
00841         advancedMode = true; // Must set to true as ctlURL may not be ready.
00842         countCtl++;
00843         try {
00844             c = urlCtl.openConnection();
00845             c.connect();
00846             in = new BufferedReader(new InputStreamReader(c.getInputStream()));
00847             debugMsg("Parsing the control commands ...");
00848             v = s = t = -1;  // initialize temporary variables
00849             cURL = sURL = null;
00850             while ((str = in.readLine()) != null) {
00851                 debugMsg("Remote control paramters>> " + str);
00852                 if (sURL == null) sURL = getURL(str, "Slide");
00853                 if (cURL == null) cURL = getURL(str, "NextCtl");
00854                 str = str.trim().toLowerCase(); // URL is case sensitive, but...
00855                 // Find valid descriptions for jwpVersion, Seq and fetchTime.
00856                 if (v < 1) v = getUInt(str, "jwpversion");
00857                 if (s < 0) s = getUInt(str, "seq");
00858                 if (t < 0) t = getUInt(str, "fetchtime");
00859             }
00860             if (v >= 1)     jwpVersion = v;     // renew the version
00861             if (s >= 0)     seq        = s;     // renew the seq
00862             if (t >= 1)     fetchTime  = t*100; // renew the fetch time
00863             if (cURL != null) urlCtl   = cURL;  // renew the urlCtl
00864             if (sURL != null) urlSlide = sURL;  // renew the urlSlide
00865             // Turn off the advanced mode here for invalid parameters only.
00866             if ((v < 1) || (s < 0)) advancedMode = false;
00867         }
00868         catch (IOException e) {
00869             debugMsg("IO error in reading from " + urlCtl);
00870             return false;
00871         }
00872         debugMsg("*** Settings after parsing control command ***\n" +
00873                  "  JWPVersion = " + jwpVersion + "\n" +
00874                  "  Seq = " + seq + "\n" +
00875                  "  FetchTime = " + fetchTime+ "ms\n" +
00876                  "  Slide = " + urlSlide + "\n" +
00877                  "  NextCtl = " + urlCtl +
00878                  (advancedMode?"":"\n  Advanced mode disabled."));
00879         return true;
00880     }
00881 }

Generated on Sun Jan 23 16:25:29 2005 for JWebPresenter by  doxygen 1.3.9.1