/******************************************************************** * ViewPlots is an Applet that allows a number of graphics to be looped. * * ViewPlots is designed that the user may select critera whereby * a set of graphics in a specific directory are choosen. These * graphics can then be looped through automatically at different * rates, choosen specically, or manualy framed forward and back. * * Change Log: * 03 Mar 2004: Initial re-write * 12 Mar 2004: Various bug-fixes, added support for environment variables * 26 Mar 2004: Re-write to version 1.1 of Java2, compatability with all browsers * 23 Apr 2004: Bug fix, ready for prime-time * 17 May 2004: Bug fix. *******************************************************************/ import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Component; import java.awt.Container; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.Image; import java.awt.Label; import java.awt.List; import java.awt.MediaTracker; import java.awt.Panel; import java.awt.TextArea; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.ImageObserver; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.util.Hashtable; import java.util.StringTokenizer; import java.util.Vector; /******************************************************************** * ViewPlots Applet and Application * * This class will display a list of all image files in the current * directory and a list of all the sub-directories in the current * directory. It will then allow you to loop through the images, or * a sub-set of the images. Giving manual control for forward and * back. You also have the option of going to one of the sub- * directories and looping through the images there. * * A coding convention has been followed to establish the scope of * a variable. All variables of the form: its represent * globally scoped variables available in all methods. All variables * of the form: the represent formal arguments of a method. All * variables of the form: a[n] represent locally declared * variables. * * @author Daniel J. Adams * @version 2.1.0 23 Apr 2004 *******************************************************************/ public class ViewPlots extends Applet implements ActionListener, ItemListener, KeyListener { /** Represents spacing between widgets, can be 0-MAXINT */ private static final int itsGap = 1; /** Represents the key being held down for multi-select in list */ private boolean itsModKey = false; /** Utilizes the IconImage as component for the image display */ private AnImage itsImage; /** Trigger for the looping mechanism */ private Timer itsTimer; /** Index in the image list of the currently displayed image */ private int itsIndex = 0; /** Flag to indicate running state as Applet(true) or Application(false) */ private boolean itsAppletFlag = false; /** The widget containing the list of sub-directories of the current dir. */ private AList itsDirList; /** The widget containing the list of images in the current dir. */ private AList itsImageList; /** The widget containing the list of filters for the current dir. */ private AList itsFilterList; /** The actual filename filters that coorilate with itsFilterList */ private Vector itsFilters; /** The container that holds the various Lists */ private Panel itsListPane; /** Holder for the sub-set when looping on a subset of images */ private int[] itsSelectedIndices = new int[0]; /** Flag to indicate doing a menu update, ignore slected events during update */ private boolean itsUpdateFlag = false; /** holder for popup dialog box */ private Dialog itsUpdatePopup = null; /** Current directory, relative to root directory where this was started */ private String itsSubDir = ""; /** Root directory, where this was started */ private String itsRootDir; /** Environ, parameters from the command line or from the HTML file */ private Environ itsEnvironment = new Environ(); /******************************************************************** * Empty constructor * * Empty contsructor called when executing as an Applet *******************************************************************/ public ViewPlots() { this(true); // call the normal constructor, setting applet flag } /******************************************************************** * Normal constructor. * * Normal constructor, called directly by main() to indicate * that we are not running as an Applet and need to make sure * we use filesystem, nor URL resources. Also called by the applet * constructor with true to insure that we are running in a browser. * * @param theAppletFlag true if running as an applet, false if an application *******************************************************************/ public ViewPlots(boolean theAppletFlag) { itsAppletFlag = theAppletFlag; // if(itsAppletFlag) // This code avoids the system event access message in the browsers // getRootPane().putClientProperty("defeatSystemEventQueueCheck", Boolean.TRUE); // putClientProperty("defeatSystemEventQueueCheck", Boolean.TRUE); itsTimer = new Timer(400, this); // Initial pause between images is .4 seconds } /******************************************************************** * Application entry point * * This method allows the applet to be run as an application. It sets * up the necessary components for a single display window that acts * like the applet portion of a browser window. When this class is * run as an application, the main window is closed to end it. * * @param theArgs Arguments passed in on the command line. A single * argument is used to determine the root directory, default is '.' * with no argument. *******************************************************************/ public static void main(String[] theArgs) { ViewPlots anApp = new ViewPlots(); Frame aFrame = new Frame("Application version: ViewPlots Applet"); // // THIS IS THE ANONYMOUS INNER CLASS (ViewPlots$1.class) // aFrame.addWindowListener(new WindowAdapter() // Simple adapter class to exit the application on a window close event { public void windowClosing(WindowEvent anEvent) { System.exit(0); } }); ViewPlots anApplet = new ViewPlots(false); try { anApplet.itsEnvironment = anApplet.new Environ(theArgs); } catch(Exception theX) { errorHalt(); } aFrame.add(anApplet.makeContentPane()); aFrame.pack(); aFrame.setVisible(true); aFrame.pack(); } /******************************************************************** * Method called when aborting a run. * * For consistancy, this is a method that displays the usage * information for running this as an application. *******************************************************************/ private static void errorHalt() { System.err.println("Illegal arguments; looking for the following:"); System.err.println("\"show_dirs = [true|false]\""); System.err.println("\"date_chng = [true|false]\""); System.err.println("\"root_dir = \""); System.err.println("\"filters = ,,...\""); System.err.println("Where a is of the form:"); System.err.println(" \":[/]\""); System.err.println(" FilterName - The name to display to the user when selecting filters."); System.err.println(" FilterString - String with optional wildcard chars '?','*' for matching file names."); System.err.println(" FilterExclusion - An optional string to search for files that will NOT be matched."); System.exit(-1); } /******************************************************************** * Method called as an Applet, prior to display. * * Required method for all Applets to do setups prior to the display * of the Applet. This init sets the Applets content pane to a * newly constructed pane. *******************************************************************/ public void init() { add(makeContentPane()); this.setSize(1000,1000); } /******************************************************************** * Method called as an Applet, prior to exit. * * Required method for all Applets to do cleanup prior to the closing * of the Applet. This init sets the Applets content pane to a * newly constructed pane. *******************************************************************/ public void destroy() { itsTimer.stop(); } /******************************************************************** * Displays the image * * Displays the image in itsImageList pointed to by itsIndex. * Checking is done prior to display, to insure that itsIndex is * within bounds of itsImageList or itsSelectedIndices, wrapping over * as required. * * Side Effect: itsImage may be changed to either the first legal * element if itsIndex overflows the limits, or to the * last legal element if itsIndex underflows the limits. *******************************************************************/ private void showImage() { int aStartLimit = 0; int anEndLimit = itsImageList.getItemCount() - 1; if (anEndLimit == -1) // No items in image list return; if (!(itsSelectedIndices.length == 0)) // restricted to the selected section { aStartLimit = itsSelectedIndices[0]; anEndLimit = itsSelectedIndices[1]; } if (itsIndex > anEndLimit) // check for overflow itsIndex = aStartLimit; else if (itsIndex < aStartLimit) // and for underflow itsIndex = anEndLimit; itsUpdateFlag = true; // don't let this fire a new event itsImageList.deselectAll(); itsImageList.select(itsIndex); // ensure that we are choosing the selected image in itsImageList itsUpdateFlag = false; // don't let this fire a new event Image anOldImage = itsImage.itsScreenImage; try { if (itsAppletFlag) itsImage.setImage(getImage(new URL(getDocumentBase(),itsSubDir + itsImageList.getSelectedItem()))); else itsImage.setImage(Toolkit.getDefaultToolkit().getImage(itsRootDir + itsSubDir + File.separatorChar + itsImageList.getSelectedItem())); } catch(MalformedURLException theEx) { System.err.println("Unable to get URL for image:\n"+theEx); theEx.printStackTrace(); } if (anOldImage != null) anOldImage.flush(); anOldImage = null; } /******************************************************************** * Responds to Timer events, and Button events * * This method handles events from both the Buttons, and itsTimer * itsTimer events cause itsImage to advance * Button events cause the proscribed action to occur. * * Side Effects: itsImage is adjusted on itsTimer ticks, Forward * button press, and Backward button press. On View button press * itsSubDir is reset, and both menus updated. * * @param theEvent Action event from itsTimer or a JButton *******************************************************************/ public void actionPerformed(ActionEvent theEvent) { String aCommand = theEvent.getActionCommand(); if (aCommand == null) // Then event is from itsTimer { ++itsIndex; showImage(); } else // Only button events left { if (aCommand.equalsIgnoreCase("play") && !itsTimer.isRunning()) // Start itsTimer { itsTimer.start(); } else if (aCommand.equalsIgnoreCase("stop")) // Stop itsTimer itsTimer.stop(); else if (aCommand.equalsIgnoreCase("faster") && itsTimer.getDelay() > 50) // Speed up itsTimer itsTimer.setDelay(itsTimer.getDelay() - 50); else if (aCommand.equalsIgnoreCase("slower") && itsTimer.getDelay() < 1500) // Slow down itsTimer itsTimer.setDelay(itsTimer.getDelay() + 50); else if (aCommand.equalsIgnoreCase("back")) // Go back one image { itsTimer.stop(); --itsIndex; showImage(); } else if (aCommand.equalsIgnoreCase("forward")) // Go forward one image { itsTimer.stop(); ++itsIndex; showImage(); } else if (aCommand.equalsIgnoreCase("View")) // Reset back to root directory { itsUpdateFlag = true; itsTimer.stop(); itsSubDir = ""; itsIndex = 0; if(itsFilterList.getSelectedIndex() != -1) itsImageList.setListData(getAllImages(),((String)itsFilters.elementAt(itsFilterList.getSelectedIndex())).substring(((String)itsFilters.elementAt(itsFilterList.getSelectedIndex())).indexOf(':') + 1).trim()); else itsImageList.setListData(getMenu(false)); if (itsImageList.getItemCount() < 1) itsListPane.remove(itsImageList); else if (itsImageList.getParent() == null) itsListPane.add(itsImageList); itsDirList.setListData(getMenu(true)); if (itsDirList.getItemCount() < 1) itsListPane.remove(itsDirList); else if (itsDirList.getParent() == null) { if (itsFilterList.getParent() == null) itsListPane.add(itsDirList,0); else itsListPane.add(itsDirList,1); } itsListPane.validate(); itsUpdateFlag = false; } } } private void startUpdate(boolean thePopupFlag) { itsUpdateFlag = true; if (thePopupFlag && itsUpdatePopup == null) { Object aFrame = this; while (! (aFrame instanceof Frame)) aFrame = ((Component) aFrame).getParent(); if (aFrame != null) { itsUpdatePopup = new Dialog((Frame)aFrame,"Applet Busy",false); itsUpdatePopup.add(new Label("Please wait while the correct files are loaded"),BorderLayout.CENTER); itsUpdatePopup.pack(); itsUpdatePopup.show(); } } } private void stopUpdate() { itsUpdateFlag = false; if (itsUpdatePopup != null) { itsUpdatePopup.dispose(); } } /******************************************************************** * Respond to select events in the lists. * * Allow the list selection events to occur. This can cause a new * Image to be displayed, or a new set of contraints on the images. * * Side Effects: On selecting a new sub directory, the lists are * regenerated. * * @param theEvent List Selection event from one of the lists. *******************************************************************/ public void itemStateChanged(ItemEvent theEvent) { AList aList = (AList)theEvent.getSource(); // Can be either Dir, Filter or Image int anIndex = aList.getSelectedIndex(); String aSelect = null; int aTmpIndex = -1; if (anIndex == -1 && !itsModKey) // Multi-select { for (int anotherIndex = 0;anotherIndex < aList.getSelectedIndexes().length;++anotherIndex) if (aList.getSelectedIndexes()[anotherIndex] != itsIndex) { aTmpIndex = aList.getSelectedIndexes()[anotherIndex]; break; } } else aTmpIndex = anIndex; if (!itsModKey) aSelect = aList.getItem(aTmpIndex); if (!itsModKey && !itsUpdateFlag) { startUpdate(false); aList.deselectAll(); aList.select(aTmpIndex); stopUpdate(); } if (!itsTimer.isRunning()) // Then we are allowed to set, or clear, a new set of constraints { if (aList.getSelectedIndexes().length > 1) // Multiple selections { itsSelectedIndices = new int[2]; itsSelectedIndices[0] = aList.getMinSelectionIndex(); itsSelectedIndices[1] = aList.getMaxSelectionIndex(); } else // Single selection, clear constraints itsSelectedIndices = new int[0]; } if (!itsUpdateFlag && aSelect != null) // Make sure we are not in the middle of a software driven change { if (aList == itsImageList && isImage(aSelect) && !itsTimer.isRunning() && itsSelectedIndices.length == 0) { itsIndex = aTmpIndex; showImage(); // Change to the just selected image } else if (aList == itsFilterList) { startUpdate(true); itsIndex = 0; if (itsSubDir.equals("")) { itsImageList.setListData(getRecursiveMenu(),((String)itsFilters.elementAt(aTmpIndex)).substring(((String)itsFilters.elementAt(aTmpIndex)).indexOf(':') + 1).trim()); if (itsImageList.getItemCount() < 1) itsListPane.remove(itsImageList); else if (itsImageList.getParent() == null) itsListPane.add(itsImageList); } else { itsImageList.setListData(getAllImages(),((String)itsFilters.elementAt(aTmpIndex)).substring(((String)itsFilters.elementAt(aTmpIndex)).indexOf(':') + 1).trim()); if (itsImageList.getItemCount() < 1) itsListPane.remove(itsImageList); else if (itsImageList.getParent() == null) itsListPane.add(itsImageList); } itsListPane.validate(); stopUpdate(); } else if (aList == itsDirList && isDir(aSelect)) // Selected a new subdirectory { startUpdate(true); itsFilterList.deselectAll(); itsSubDir = itsSubDir + aSelect; itsDirList.setListData(getMenu(true)); itsIndex = 0; if (itsDirList.getItemCount() < 1) itsListPane.remove(itsDirList); else if (itsDirList.getParent() == null) itsListPane.add(itsDirList,0); itsImageList.setListData(getMenu(false)); if (itsImageList.getItemCount() < 1) itsListPane.remove(itsImageList); else if (itsImageList.getParent() == null) itsListPane.add(itsImageList); itsListPane.validate(); stopUpdate(); } } aList = null; aSelect = null; } /******************************************************************** * Build the contents of the Applet * * This method builds and initializes all of the widgets for the * display. It is called once at initialization of the applet. * * @return Container (DoublePanel) for root content pane. *******************************************************************/ private Container makeContentPane() { Panel aContentPane = new DoublePanel(); // Global content pane BorderLayout aLayout = new BorderLayout(); aLayout.setHgap(itsGap); // astetic for border aLayout.setVgap(itsGap); // astetic for border aContentPane.setLayout(aLayout); aContentPane.setSize(1000,1000); itsRootDir = itsEnvironment.getRootDir(); itsSubDir = itsEnvironment.getRootDir(); Container aButtonPane = new DoublePanel(); aButtonPane.setLayout(new GridLayout(1,0,itsGap,itsGap)); // Fill the top of the content pane with control buttons // First button is pattern for all buttons Button aButton = new Button("Play"); // Button display name aButton.setActionCommand("Play"); // Command for event aButton.addActionListener(this); // We listen to it aButtonPane.add(aButton); // add the button aButton = new Button("Stop"); aButton.setActionCommand("Stop"); aButton.addActionListener(this); aButtonPane.add(aButton); aButton = new Button("Faster"); aButton.setActionCommand("Faster"); aButton.addActionListener(this); aButtonPane.add(aButton); aButton = new Button("Slower"); aButton.setActionCommand("Slower"); aButton.addActionListener(this); aButtonPane.add(aButton); aButton = new Button("Back One"); aButton.setActionCommand("Back"); aButton.addActionListener(this); aButtonPane.add(aButton); aButton = new Button("Forwad One"); aButton.setActionCommand("Forward"); aButton.addActionListener(this); aButtonPane.add(aButton); if (itsEnvironment.getShowDirs() && itsEnvironment.getDateChange()) { aButton = new Button("Change Date"); aButton.setActionCommand("View"); aButton.addActionListener(this); aButtonPane.add(aButton); } aContentPane.add(aButtonPane, BorderLayout.NORTH); // Fill the right of the content pane with the menu items itsListPane = new DoublePanel(); itsListPane.setLayout(new GridLayout(0,1,itsGap,itsGap)); itsListPane.setSize(new Dimension(200, 150 * 5)); itsFilters = itsEnvironment.getFilters(); itsFilterList = new AList(10); itsFilterList.addKeyListener(this); itsFilterList.setMultipleMode(false); // Only a single filter at a time selected itsFilterList.addItemListener(this); if (itsFilters.size() > 0) { for (int anIndex = 0;anIndex < itsFilters.size();++anIndex) { String aString = (String)itsFilters.elementAt(anIndex); itsFilterList.add(aString.substring(0,aString.indexOf(':'))); } itsFilterList.setSize(new Dimension(200, 125)); itsListPane.add(itsFilterList); } if (itsEnvironment.getShowDirs()) { itsDirList = new AList(20,getMenu(true)); itsDirList.addKeyListener(this); itsDirList.setMultipleMode(false); // Only a single dir at a time selected itsDirList.addItemListener(this); itsDirList.setSize(new Dimension(200,250)); if (itsDirList.getItemCount() > 0) itsListPane.add(itsDirList); } itsImageList = new AList(30,getMenu(false)); itsImageList.addKeyListener(this); itsImageList.setMultipleMode(true); // Ranges allowed for images itsImageList.addItemListener(this); itsImageList.requestFocus(); itsImageList.setSize(new Dimension(200,375)); if (itsImageList.getItemCount() > 0) itsListPane.add(itsImageList); aContentPane.add(itsListPane, BorderLayout.EAST); // The center of the content pane holds the image area itsImage = new AnImage(this); Dimension aDim = new Dimension(800,800); itsImage.setSize(aDim); aContentPane.add(itsImage, BorderLayout.CENTER); return aContentPane; } /******************************************************************** * Get a set of items for a List display * * Gather a list of all sub-directories (o1n theDirFlag->true) or * all images (theDirFlag->false) in the current directory. * * @param theDirFlag Flag to indicate if collecting directories * @return Vector List of menu items for indicated menu. *******************************************************************/ private Vector getMenu(boolean theDirFlag) { Vector aReturn = new Vector(); String aLine = ""; InputStreamReader aStream = null; BufferedReader aReader = null; InputStream anInput = null; try { if (itsAppletFlag) // Determine Stream source { String aDir = (getDocumentBase().getFile()).substring(0,getDocumentBase().getFile().lastIndexOf("/")); anInput = new URL("http",getDocumentBase().getHost(),aDir+"/"+itsSubDir).openStream(); } else anInput = new URL("file","",itsRootDir + itsSubDir).openStream(); aStream = new InputStreamReader(anInput); aReader = new BufferedReader(aStream); // Buffered read on source as URL while ((aLine = aReader.readLine()) != null) // Read a line at a time { if (itsAppletFlag) aLine = extractReference(aLine); if (!theDirFlag && aLine != null && aLine.length() > 0 && isImage(aLine)) // Image when looking for image? aReturn.addElement(aLine); else if (theDirFlag && aLine != null && aLine.length() > 0 && isDir(aLine)) // Directory when looking for directory? aReturn.addElement(aLine); } } catch (IOException theEx) { if (itsRootDir != null &&!itsRootDir.equals(".")) // Allow fail if a bad argument was sent to main { System.err.println("Unkown root diretory (" + itsRootDir + ") choosen as starting point."); System.exit(-1); } System.err.println("Exception caught in directory read:"); theEx.printStackTrace(); } finally { try { if (anInput != null) anInput.close(); } catch(Exception x) {} try { if (aStream != null) aStream.close(); } catch(Exception x) {} try { if (aReader != null) aReader.close(); } catch(Exception x) {} } aLine = null; aStream = null; aReader = null; anInput = null; return aReturn; } /******************************************************************** * Get a set of images in subdirectories for an Image display * * Gather a list of all images in all sub-directories of the current * directory. * * @return Vector List of menu items for indicated menu. *******************************************************************/ private Vector getAllImages() { Vector aReturn = new Vector(); String aLine = ""; InputStream aDirInput = null; InputStreamReader aDirStream = null; BufferedReader aDirReader = null; InputStream aImgInput = null; InputStreamReader aImgStream = null; BufferedReader aImgReader = null; try { if (itsAppletFlag) // Determine Stream source { String aDir = (getDocumentBase().getFile()).substring(0,getDocumentBase().getFile().lastIndexOf("/")); aDirInput = new URL("http",getDocumentBase().getHost(),aDir+"/"+itsSubDir).openStream(); } else aDirInput = new URL("file","",itsRootDir + itsSubDir).openStream(); aDirStream = new InputStreamReader(aDirInput); aDirReader = new BufferedReader(aDirStream); // Buffered read on source as URL while ((aLine = aDirReader.readLine()) != null) // Read a line at a time { if (itsAppletFlag) aLine = extractReference(aLine); if (aLine != null && aLine.length() > 0 && isImage(aLine)) // Image when looking for image? aReturn.addElement(aLine); else if (aLine != null && aLine.length() > 0 && isDir(aLine)) // Directory when looking for directory? { String aDirName = aLine; if (itsAppletFlag) // Determine Stream source { String aDir = (getDocumentBase().getFile()).substring(0,getDocumentBase().getFile().lastIndexOf("/")); aImgInput = new URL("http",getDocumentBase().getHost(),aDir+/*"/" + */aDirName).openStream(); } else aImgInput = new URL("file","",itsRootDir + itsSubDir + aDirName).openStream(); aImgStream = new InputStreamReader(aImgInput); aImgReader = new BufferedReader(aImgStream); // Buffered read on source as URL while ((aLine = aImgReader.readLine()) != null) // Read a line at a time { if (itsAppletFlag) aLine = extractReference(aLine); if (aLine != null && aLine.length() > 0 && isImage(aLine)) // Image when looking for image? aReturn.addElement(aLine); } } if (aImgStream != null) { aImgStream.close(); aImgStream = null; } if (aImgReader != null) { aImgReader.close(); aImgStream = null; } } } catch (IOException theEx) { if (itsRootDir != null &&!itsRootDir.equals(".")) // Allow fail if a bad argument was sent to main { System.err.println("Unkown root diretory (" + itsRootDir + ") choosen as starting point."); System.exit(-1); } System.err.println("Exception caught in directory read:"); theEx.printStackTrace(); } finally { try { if (aDirInput != null) aDirInput.close(); } catch(Exception x) {} try { if (aDirStream != null) aDirStream.close(); } catch(Exception x) {} try { if (aDirReader != null) aDirReader.close(); } catch(Exception x) {} try { if (aImgInput != null) aImgInput.close(); } catch(Exception x) {} try { if (aImgStream != null) aImgStream.close(); } catch(Exception x) {} try { if (aImgReader != null); aImgReader.close(); } catch(Exception x) {} } aLine = null; aDirInput = null; aDirStream = null; aDirReader = null; aImgInput = null; aImgStream = null; aImgReader = null; return aReturn; } /******************************************************************** * Get a set of items for a List display * * Gather a list of all * all images (theDirFlag->false) in all sub-directories. * * @return Vector List of menu items for indicated menu. *******************************************************************/ private Vector getRecursiveMenu() { Vector aReturn = new Vector(); String aSubDir = ""; String aLine = ""; InputStream anInput = null; InputStreamReader aStream = null; BufferedReader aReader = null; InputStream anImageInput = null; InputStreamReader anImageStream = null; BufferedReader anImageReader = null; try { // read in the directories if (itsAppletFlag) // Determine Stream source { String aDir = (getDocumentBase().getFile()).substring(0,getDocumentBase().getFile().lastIndexOf("/")); anInput = new URL("http",getDocumentBase().getHost(),aDir).openStream(); } else anInput = new URL("file","",itsRootDir).openStream(); aStream = new InputStreamReader(anInput); aReader = new BufferedReader(aStream); // Buffered read on source as URL while ((aSubDir = aReader.readLine()) != null) // Read a line at a time { if (itsAppletFlag) aSubDir = extractReference(aSubDir); if (aSubDir != null && aSubDir.length() > 0 && isDir(aSubDir)) // Directory when looking for directory? { if (itsAppletFlag) // Determine Stream source { String aDir = (getDocumentBase().getFile()).substring(0,getDocumentBase().getFile().lastIndexOf("/")); anImageInput = new URL("http",getDocumentBase().getHost(),aDir+"/"+aSubDir).openStream(); } else anImageInput = new URL("file","",itsRootDir + aSubDir).openStream(); anImageStream = new InputStreamReader(anImageInput); anImageReader = new BufferedReader(anImageStream); // Buffered read on source as URL while ((aLine = anImageReader.readLine()) != null) // Read a line at a time { if (itsAppletFlag) aLine = extractReference(aLine); if (aLine != null && aLine.length() > 0 && isImage(aLine)) // Image when looking for image? aReturn.addElement(aSubDir + "/" + aLine); } } } if (anImageInput != null) { anImageInput.close(); anImageInput = null; } if (anImageStream != null) { anImageStream.close(); anImageStream = null; } if (anImageReader != null) { anImageReader.close(); anImageReader = null; } } catch (IOException theEx) { if (itsRootDir != null &&!itsRootDir.equals(".")) // Allow fail if a bad argument was sent to main { System.err.println("Unkown root diretory (" + itsRootDir + ") choosen as starting point."); System.exit(-1); } System.err.println("Exception caught in directory read:"); theEx.printStackTrace(); } finally { try { if (anInput != null) anInput.close(); } catch(Exception x) {} try { if (aStream != null) aStream.close(); } catch(Exception x) {} try { if (aReader != null) aReader.close(); } catch(Exception x) {} try { if (anImageInput != null) anImageInput.close(); } catch(Exception x) {} try { if (anImageStream != null) anImageStream.close(); } catch(Exception x) {} try { if (anImageReader != null) anImageReader.close(); } catch(Exception x) {} } aSubDir = null; aLine = null; anInput = null; aStream = null; aReader = null; anImageInput = null; anImageStream = null; anImageReader = null; return aReturn; } /******************************************************************** * Given a file name, return if it looks like an image * * Class method return true if String looks like an image file * * @param theFileName String to test for Image pattern * @return true if theFileName matches the Image pattern *******************************************************************/ private static boolean isImage(String theFileName) { if (((theFileName.indexOf(".jpg") != -1) || // Check for valid image extensions (theFileName.indexOf(".JPG") != -1) || (theFileName.indexOf(".jpeg") != -1) || (theFileName.indexOf(".JPEG") != -1) || (theFileName.indexOf(".gif") != -1) || (theFileName.indexOf(".GIF") != -1)) && theFileName.indexOf("back.gif") == -1) return true; return false; } /******************************************************************** * Given a file name, return if it looks like a directory * * Return true if the String looks like a directory String * * @param theFileName String to test for directory pattern * @return true if theFileName matches the directory pattern *******************************************************************/ private boolean isDir(String theFileName) { if (itsAppletFlag) // See if it ends in the file seperator return ((theFileName.charAt(theFileName.length() - 1) == '/') && theFileName.indexOf("0") != -1); else { File aFile = new File(itsRootDir + itsSubDir + theFileName); return aFile.isDirectory(); } } /******************************************************************** * Given an HTML formatted line, return only the link referenced file name * * Return the reference contents of the anchor tag * so: Some File * returns foobar.txt * * @param theString String to search for reference * @return null, or the reference without HTML tag info. *******************************************************************/ static private String extractReference(String theString) { String aStartTag = "= 1 && anExp.charAt(0) == '?') // if followed by '?' { anExp = anExp.substring(1); theString = theString.substring(1); } while (anExp.length() >= 1 && anExp.charAt(0) == '*') // if folowed by '*' anExp = anExp.substring(1); return findExp('*' + anExp, theString); } } catch (Exception x) { } return false; } /******************************************************************** * KeyListener method, detect a key press * * @param theEvent KeyEvent of key pressed *******************************************************************/ public void keyPressed(KeyEvent theEvent) { if (theEvent.getKeyCode() == KeyEvent.VK_CONTROL) itsModKey = true; } /******************************************************************** * KeyListener method, detect a key release * * @param theEvent KeyEvent of key released *******************************************************************/ public void keyReleased(KeyEvent theEvent) { if (theEvent.getKeyCode() == KeyEvent.VK_CONTROL) itsModKey = false; } /******************************************************************** * Placeholder method for KeyListener * * @param theEvent KeyEvent of key typed *******************************************************************/ public void keyTyped(KeyEvent theEvent) { return; } /******************************************************************** * Method for releasing resources *******************************************************************/ public void finalize() throws Throwable { itsImage = null; itsTimer = null; itsDirList = null; itsImageList = null; itsFilterList = null; itsFilters = null; itsListPane = null; itsSubDir = null; itsRootDir = null; itsEnvironment = null; } /******************************************************************** * Environment supporting class for ViewPlots * * This class handles the retrieving of environment settings for both * the applet (passed from html), or from the aplication (command line * arguments). It simplifies the ViewPlots code to reduce the confusion * the mode can add. * * @author Daniel J. Adams * @version 1.5.0 23 Apr 2004 *******************************************************************/ class Environ { /** Hashtable to store environment info in, from argument line in application case */ Hashtable itsHash = null; /******************************************************************** * Default constructor. * * Used as an applet, does not initialize itsHash. *******************************************************************/ public Environ() { } /******************************************************************** * Application constructor * * Used for creating a new Environment from the parameters passed in * on the command line. * * @param theArgs The arguments from the command line to be parsed. *******************************************************************/ public Environ(String[] theArgs) { itsHash = new Hashtable(); for (int anIndex = 0;anIndex < theArgs.length;++anIndex) { StringTokenizer aTokenizer = new StringTokenizer(theArgs[anIndex].trim(),"="); if (!aTokenizer.hasMoreTokens()) break; String aKey = aTokenizer.nextToken().trim(); String aValue = aTokenizer.nextToken().trim(); if (!aKey.equals("show_dirs") && !aKey.equals("date_chng") && !aKey.equals("root_dir") && !aKey.equals("filters")) { errorHalt(); } else itsHash.put(aKey, aValue); } } /******************************************************************** * Returns the root directory. * * From the environment for both applets and applications. * * @return String The directory to use as the root directory. *******************************************************************/ public String getRootDir() { String aReturn = "."; if (!itsAppletFlag) { Object anObj = itsHash.get("root_dir"); if (anObj != null) aReturn = (String)anObj; } else aReturn = getParameter("root_dir"); return aReturn; } /******************************************************************** * Returns the show directory flag. * * From the environment for both applets and applications. This flag * determines if the directory list will be displayed or not. * * @return boolean true if we should show sub-directories, else false. *******************************************************************/ public boolean getShowDirs() { boolean aReturn = false; if (!itsAppletFlag) { Object anObj = itsHash.get("show_dirs"); if (anObj != null && ((String)anObj).equalsIgnoreCase("true")) aReturn = true; } else aReturn = (getParameter("show_dirs") != null && getParameter("show_dirs").equalsIgnoreCase("true")); return aReturn; } /******************************************************************** * Returns the change directory flag. * * From the environment for both applets and applications. This flag * determines if the change directory button will be displayed or not. * * @return boolean true if we should show sub-directories, else false. *******************************************************************/ public boolean getDateChange() { boolean aReturn = true; if (!itsAppletFlag) { Object anObj = itsHash.get("date_chng"); if (anObj != null && ((String)anObj).equalsIgnoreCase("false")) aReturn = false; } else aReturn = !(getParameter("date_chng") != null && getParameter("date_chng").equalsIgnoreCase("false")); return aReturn; } /******************************************************************** * Returns the file filters. * * From the environment for both applets and applications. * * @return Vector A List of filters for this directory *******************************************************************/ public Vector getFilters() { Vector aReturn = new Vector(); String aParsableString = null; if (!itsAppletFlag) { Object anObj = itsHash.get("filters"); if (anObj != null) aParsableString = (String)anObj; } else if (getParameter("filters") != null) aParsableString = getParameter("filters"); if (aParsableString == null) return aReturn; StringTokenizer aTokenizer = new StringTokenizer(aParsableString.trim(),","); while (aTokenizer.hasMoreTokens()) { String aToken = aTokenizer.nextToken().trim(); aReturn.addElement(aToken ); } return aReturn; } } /******************************************************************** * AList supporting class for ViewPlots * * This class extends the java.awt.List class to add a few methods that * resemble javax.swing.JList methods. This was to accomidate the * re-write from swing code to 1.1 for browser support. * * @author Daniel J. Adams * @version 1.0.0 26 Mar 2004 *******************************************************************/ class AList extends List { /******************************************************************** * Base constructor for AList * * Initialize AList to the data passed in, set the size of AList * to 25 lines. * * @param theData Vector of String to set the list to. *******************************************************************/ public AList(int theSize, Vector theData) { super(theSize); setListData(theData); } /******************************************************************** * Empty constructor for AList * * Initialize AList size AList to 25 lines. *******************************************************************/ public AList(int theSize) { super(theSize); } /******************************************************************** * Sets the elements of AList * * Clears AList, and then adds each member of the data passed in. * * @param theData Vector of String to set the list to. *******************************************************************/ public void setListData(Vector theData) { removeAll(); if (theData != null) for (int anIndex = 0;anIndex < theData.size();++anIndex) add((String)theData.elementAt(anIndex)); } /******************************************************************** * Sets the elements of AList, filtering data * * Clears AList, and then adds each member of the data passed in that * matches the filter that is passed in. * * @param theData Vector of String to set the list to. * @param theFilter String to match data to for add *******************************************************************/ public void setListData(Vector theData, String theFilter) { String aString; removeAll(); addListData(theData,theFilter); } /******************************************************************** * Adds the elements of AList, filtering data * * Adds each member of the data passed in that * matches the filter that is passed in. * * @param theData Vector of String to set the list to. * @param theFilter String to match data to for add *******************************************************************/ public void addListData(Vector theData, String theFilter) { String aString; String aYesFilter = theFilter; String aNoFilter = ""; int aPtr = theFilter.indexOf('/'); if (aPtr != -1) { aYesFilter = theFilter.substring(0,aPtr).trim(); aNoFilter = theFilter.substring(aPtr + 1).trim(); } if (theData != null) for (int anIndex = 0;anIndex < theData.size();++anIndex) { aString = (String)theData.elementAt(anIndex); if ((aYesFilter.indexOf('?') != -1) || (aYesFilter.indexOf('*') != -1)) { if (findExp(aYesFilter,aString)) { if (aNoFilter.equals("") || aString.indexOf(aNoFilter) == -1) add(aString); } } else { if (aString.indexOf(aYesFilter) != -1) { if (aNoFilter.equals("") || aString.indexOf(aNoFilter) == -1) add(aString); } } } } /******************************************************************** * Get the lowest index that is selected * * Returns the selected index closest to the begining of the list. * * @return int The index of the first selected item. *******************************************************************/ public int getMinSelectionIndex() { return getSelectedIndexes()[0]; } /******************************************************************** * Get the highest index that is selected * * Returns the selected index closest to the end of the list. * * @return int The index of the last selected item. *******************************************************************/ public int getMaxSelectionIndex() { int[] aList = getSelectedIndexes(); return aList[aList.length - 1]; } /******************************************************************** * Insure that no items are selected * * Un-select each selected index in the list, to insure that there are * NO selected items in the list. *******************************************************************/ public void deselectAll() { int[] aList = getSelectedIndexes(); for (int anIndex = 0;anIndex < aList.length;++anIndex) deselect(aList[anIndex]); } } /******************************************************************** * Timer supporting class for ViewPlots * * This class is a substitue class for the javax.swing.Timer class. * This was to accomidate the re-write from swing code to 1.1 for * browser support. * * @author Daniel J. Adams * @version 1.0.0 26 Mar 2004 *******************************************************************/ class Timer { /** Independant thread used for timing purposes */ private TimerThread itsThread = new TimerThread(); /******************************************************************** * Basic constructor for Timer * * Allows the initial delay between 'ticks' to be set, as well as * setting which object will be the consumer of these ticks. * * @param theInterval Delay between ticks in microseconds * @param theListener ActionListener that consumes timer ticks. *******************************************************************/ public Timer(long theInterval, ActionListener theListener) { itsThread.itsInterval = theInterval; itsThread.itsListener = theListener; } /******************************************************************** * Fetches the current timer delay period * * @return long The current delay between ticks in microseconds. *******************************************************************/ public long getDelay() { return itsThread.itsInterval; } /******************************************************************** * Sets the timer delay period to a new setting. * * @param theInterval Delay between timer ticks in microseconds *******************************************************************/ public void setDelay(long theInterval) { itsThread.itsInterval = theInterval; } /******************************************************************** * Check for timer thread running * * Returns true if there is a timer thread currently executing, * otherwise returns false. * * @return boolean State of timer thread *******************************************************************/ public boolean isRunning() { return itsThread.itsRunningFlag; } /******************************************************************** * Halts any executing timer thread. * * Safe method that will cause the timer thread to expire on its * next wakeup. *******************************************************************/ public void stop() { itsThread.itsRunningFlag = false; } /******************************************************************** * Starts a timer thread executing. * * Safe method that will start a new timer thread, unless there is a * currently executing timer thread. *******************************************************************/ public void start() { if (itsThread.itsRunningFlag) return; itsThread.itsRunningFlag = true; new Thread(itsThread).start(); } } /******************************************************************** * TimerThread supporting class for ViewPlots.Timer * * This class handles the implmentation of the background thread for * the image animation. * * @author Daniel J. Adams * @version 1.0.0 26 Mar 2004 *******************************************************************/ class TimerThread implements Runnable { /** Flag to track execution status of thread */ public boolean itsRunningFlag; /** Storage for the tick interval in microseconds */ public long itsInterval; /** Pointer to the consumer of timer ticks */ private ActionListener itsListener = null; /******************************************************************** * run method override from Runnable class, thread execution. * * This method is the action portion of the thread. When running, it * sends a 'tick' event to the listener after sleeping for the * specified interval. It also checks for safe-thread stop by checking * the execution flag. *******************************************************************/ public void run() { do { try { Thread.currentThread().sleep(itsInterval); itsListener.actionPerformed(new ActionEvent(this,0,null)); } catch(Exception theX) {} } while(itsRunningFlag); } } /******************************************************************** * AnImage supporting class for ViewPlots * * This class extends the java.awt.Component class to provide a * image component. It replaces the original code's use of * javax.swing.ImageIcon. This was to accomidate the * re-write from swing code to 1.1 for browser support. * * @author Daniel J. Adams * @version 1.0.0 26 Mar 2004 *******************************************************************/ class AnImage extends Component { /** Actual image being displayed */ private Image itsScreenImage = null; /** Viewport for image display calculations */ private ImageObserver itsObserver; /** Width of the component */ private int itsWidth = 800; /** Height of the component */ private int itsHeight = 800; /******************************************************************** * Default constructor for AnImage * * Initialized AnImage, setting viewport as well. * * @param theOb Valid ViewPort used for image calculations *********************************************************2**********/ public AnImage(ImageObserver theOb) { itsObserver = theOb; setSize(new Dimension(itsWidth,itsHeight)); } /******************************************************************** * Sets a new image for the component * * Allows the displayed image to be changed dynamically. * * @param theImage New image to display for the component *******************************************************************/ public void setImage(Image theImage) { itsScreenImage = theImage; // Set a media-tracker to insure that the image is fully loaded Toolkit.getDefaultToolkit().prepareImage(itsScreenImage,0,0,this); MediaTracker aTracker = new MediaTracker(this); aTracker.addImage(itsScreenImage,0); try { // Wait for the image to be fully loaded before we continue aTracker.waitForID(0); } catch(InterruptedException x) {} aTracker = null; // Make sure the newly loaded image replaces the current screen image. repaint(); } /******************************************************************** * Override of Component paint method to draw image. * * This method overrides the superclasses paint method to draw the * image when the underlaying component is updated. * * @param theGraphic The screen graphic to be drawn to. *******************************************************************/ public void paint(Graphics theGraphic) { super.paint(theGraphic); if (itsScreenImage != null) theGraphic.drawImage(itsScreenImage,0,0,itsObserver); } /******************************************************************** * Override of underlaying components method. * * This ensures that the component will be rendered in the proper * size. * * @return Dimension The preferred size of this component *******************************************************************/ public Dimension getPreferredSize() { return new Dimension(itsWidth,itsHeight); } /******************************************************************** * Override of underlaying components method. * * This ensures that the component will be rendered in the proper * size. * * @return Dimension The minimum size of this component *******************************************************************/ public Dimension getMinimumSize() { return new Dimension(itsWidth,itsHeight); } /******************************************************************** * Override of underlaying components method. * * This ensures that the component will be rendered in the proper * size. * * @return Dimension The maximum size of this component *******************************************************************/ public Dimension getMaximumSize() { return new Dimension(itsWidth,itsHeight); } public void finalize() throws Throwable { itsScreenImage.flush(); itsScreenImage = null; itsObserver = null; } } /******************************************************************** * DoublePanel supporting class for ViewPlots * * This class extends the java.awt.Panel class to provide a * double-buffered Panel. It is utilized in all panels that are * displayed to insure that all screen writtings are double-buffered. * This is to replace the original codes Swing use which implements * double-buffering as default. * * @author Daniel J. Adams * @version 1.0.0 26 Mar 2004 *******************************************************************/ class DoublePanel extends Panel { /** Image buffer used to contain image that will be written to screen, after buffer */ private Image itsScreenBuffer = null; /** The graphics buffer used to double buffer this Panel */ private Graphics itsBuffer; /******************************************************************** * Simple over-ride of java.awt.Panel to cause all updates to double * buffer. * * @param theGraphic The Graphics that are the real screen to be * written to. *******************************************************************/ public void update(Graphics theGraphic) { if (itsScreenBuffer == null) { itsScreenBuffer = createImage(getSize().width, getSize().height); itsBuffer = itsScreenBuffer.getGraphics(); } itsBuffer.setColor(getBackground()); itsBuffer.fillRect(0, 0, getSize().width, getSize().height); itsBuffer.setColor(getForeground()); paint(itsBuffer); theGraphic.drawImage(itsScreenBuffer,0,0,this); itsScreenBuffer.flush(); } public void finalize() throws Throwable { itsScreenBuffer.flush(); itsScreenBuffer = null; itsBuffer.dispose(); itsBuffer = null; } } }