/* AlgAnimFrame.java Dual drawing panel version .. before and after frames */ package ciips.animation; import java.awt.*; import java.awt.event.*; import java.applet.*; import java.io.*; import java.net.*; import java.lang.reflect.*; /** * The AlgAnimFrame class is a top-level window with a title * and border. The layout used is BorderLayout. There is a menubar * created at the north part of the panel. The drawing panel is at the * center and a commentary panel is located at the south. *

* This extended frame class holds a set references to the panel/frame objects * used in the algorithm animation. * @see Frame */ public class AlgAnimFrame extends Frame implements ItemListener { private AlgAnimApp parentApp; private URL sourceURL; private String algname; private AlgThread alg = null; private int delay = 200; private BasicDrawingPanel dp[]; private FlowPanel panel; private DrawingPanel dpAfter; // to be obsoleted! private BasicDrawingPanel dpBefore; // to be obsoleted! private TextFrame tf; private LegendFrame lf; private ComPanel cpanel; public static int COMPANEL_LINES = 4; private String data_file_name; private DataSets data_sets; // Supplied data sets /** Value returned by getSelected() when user input has been selected **/ public static final int UI_SELECTED = -1; private boolean step = false, stepWait = false; private Dimension frameSize = new Dimension(1000, 700); private MenuBar mb; // control panel private MenuItem quitItem; private Button runItem, stopItem, stepItem, skipItem; private CheckboxMenuItem enableAnim, disableAnim; private CheckboxMenuItem[] dataChoice, delayChoice; private int dataSelected = 0; private MenuItem user_input; private Menu dataMenu, delayMenu, animStepMenu; private static int N_ANIM_STEPS = 4; // Options menu private Menu optionsMenu = null; private CheckboxMenuItem[] optChoice; private Menu ActionsMenu = null; private boolean noAnim = false; Font helv10 = new Font("Helvetica", Font.PLAIN, 10); private Font helv14 = new Font("Helvetica", Font.PLAIN, 14); Font helv18 = new Font("Helvetica", Font.PLAIN, 18); Font helv24 = new Font("Helvetica", Font.PLAIN, 24); private ControlPanel cp = null; private UIFrame ui_frame = null; private static final String thread_suffix = "AlgThread"; private static final boolean DEBUG = true; private static AlgAnimFrame frame; /** * Creates and shows the frame consists of a drawing panel, commentary * panel and control panel. The text frame is now displayed on a separate * window. * @param parentApp The applet which results in the creation of this frame * @param sourceURL The URL of the source code to be displayed on the text * frame * @see URL */ public AlgAnimFrame( AlgAnimApp parentApp, URL sourceURL ) { this.parentApp = parentApp; this.sourceURL = sourceURL; this.algname = parentApp.getParameter("algname"); data_file_name = parentApp.getParameter("algfile"); data_sets = new DataSets( this, data_file_name ); if( DEBUG ) System.out.println("AlgAnimFrame: algname [" + algname + "]"); setTitle( algname ); setLocation(0, 0); // Construct the algorithm runner thread here so that its constructor // can make calls back to this class to set configuration parameters alg = makeAlgThread(); if ( alg == null ) { System.out.println("Failed to construct algorithm runner thread"); return; } else { if( DEBUG ) System.out.println("AlgAnimFrame - " + alg.getClass().toString() + " loaded"); } // setLayout( new BorderLayout() ); String opt[] = alg.getOptions(); if ( opt != null ) setOptions( opt, alg ); ActionsMenu = alg.getActionMenu(); setFont(helv14); /* Control panel */ cp = new ControlPanel(this, algname); add("North", cp); runItem = cp.getRunButton(); stopItem = cp.getStopButton(); stepItem = cp.getStepButton(); skipItem = cp.getSkipButton(); /* Source panel */ tf = new TextFrame( sourceURL ); /* Commentary panel */ cpanel = new ComPanel( algname, COMPANEL_LINES ); add( "South", cpanel ); setMenuBar(createMenuBar()); panel = new FlowPanel( this ); pack(); validate(); show(); //if (tf.getTextPanel().getNumLines() > 0) //tf.toFront(); cp.refreshButtons(); /* Drawing panels */ int panel_count = alg.getDataPanelCount(); dp = new BasicDrawingPanel[ panel_count ]; panel.addDrawingPanels( dp ); add("Center", panel); if( panel_count > 1 ) { dpBefore = dp[panel_count-2]; } dpAfter = (DrawingPanel)dp[panel_count-1]; pack(); validate(); show(); // generate initial data set // alg.generateData(); } // init() /** Add an optional options menu to the frame: this method may be called multiple times if the available options change at various stages in the animation **/ private void setOptions( String[] opt_list, ItemListener listener ) { int k, n = opt_list.length; if( DEBUG ) System.out.println("AlgAnimFrame:setOptions " + n ); if ( optionsMenu == null ) { optionsMenu = new Menu( "Options" ); if( DEBUG ) System.out.println("AlgAnimFrame:setOptions " + n ); if ( mb != null ) mb.add( optionsMenu ); } // Clear the menu optionsMenu.removeAll(); optChoice = new CheckboxMenuItem[ n ]; for( k=0; k 0 ) dataChoice[0].setState(true); } dataMenu.addSeparator(); user_input = new MenuItem("User Input"); user_input.setEnabled( UIFrame.isUIAllowed() ); dataMenu.add( user_input ); dataMenu.addSeparator(); quitItem = new MenuItem("Quit"); dataMenu.add(quitItem); Menu animMenu = new Menu("Animation"); mb.add(animMenu); enableAnim = new CheckboxMenuItem("Enable"); enableAnim.setState(true); disableAnim = new CheckboxMenuItem("Disable"); animMenu.add(enableAnim); animMenu.add(disableAnim); animMenu.addSeparator(); delayMenu = new Menu("Delay"); animMenu.add(delayMenu); delayChoice = new CheckboxMenuItem[5]; for (int i = 0; i < 5; i++) { delayChoice[i] = new CheckboxMenuItem(""+((i+1)*200)+" msec"); delayMenu.add(delayChoice[i]); } delayChoice[0].setState(true); animStepMenu = new Menu("Animation steps"); animMenu.add(animStepMenu); animStepMenu.add(new CheckboxMenuItem("No steps")); for(int i=0;i= 0 ) { dpAfter.setAnimStep( index ); if ( index == 0 ) dpAfter.setNoAnim( true ); } } else if (parent == optionsMenu) { MenuItem mk = null; index = -1; for ( k = 0; k < optionsMenu.getItemCount(); k++) { mk = optionsMenu.getItem( k ); if (target == mk) { index = k; } } if ( index > 0 ) alg.optionChanged( index, ((CheckboxMenuItem)mk).getState() ); } else { // parent is Animation.. -> enable/disable anim if (target == enableAnim) { disableAnim.setState(!enableAnim.getState()); noAnim = false; dpAfter.setNoAnim(noAnim); } else if (target == disableAnim) { enableAnim.setState(!disableAnim.getState()); noAnim = true; dpAfter.setNoAnim(noAnim); } } } else { if (target instanceof MenuItem) { String text = ((MenuItem)target).getLabel(); if (text.trim().equals("Source Code")) { tf.show(); tf.toFront(); } else if(text.trim().equals("Legend Panel")) { lf = new LegendFrame (); lf.show(); lf.toFront(); } else if (text.trim().equals("Credits")) { setText(0, "Author: Woi L Ang Supervised by: John Morris"); } else if (text.trim().equals("Copyrights")) { setText(0, "Copyright (c) 1998 The Department of Electrical and Electronic Engineering. University of Western Australia"); } } } return false; } // action() /** * Sets the text string to be displayed on a specific text field on the * commentary panel return from getComPanel. * @see AlgAnimFrame#getComPanel * @param n The text field to display the string. First is 0. * @param s The string to be displayed. */ public void setText( int n, String s ) { cpanel.setText( n, s ); } /** * Highlights the specified line of the source code on the text panel. * If the line is beyond the scroll pane, it will be scrolled to the * center of the window. * @param n The line to be highlighted. */ public void Highlight( int n ) { if( DEBUG ) System.out.println("AlgAnimFrame:Highlight " + n ); if (tf.getTextPanel().getNumLines() < 1) return; if (!((Component)tf).isShowing()) return; tf.getTextPanel().Highlight(n); int numLineVisible = tf.getTextPanel().getSize().height/tf.getTextPanel().getLineSpace(); if ( (n < (tf.getTextPanel().getStart() + 2)) || (n > (tf.getTextPanel().getStart() + numLineVisible - 2))) { int max = tf.getVertScrollbar().getMaximum(); int min = tf.getVertScrollbar().getMinimum(); int startLine = n - numLineVisible/2; if (startLine > 0) { tf.getTextPanel().setStart(startLine); tf.getVertScrollbar().setValue(startLine * (max - min) / tf.getTextPanel().getNumLines()); } else { tf.getTextPanel().setStart(0); tf.getVertScrollbar().setValue(0); } } try { Thread.sleep(delay/4); } catch (InterruptedException e) {} } /** * Restore the drawing panel at the end of the animation or during * initialization. */ public void restoreDrawingPanel() { alg.restoreDrawingPanel(); } /** * Start the animation algorithm if the run or step * button is pressed. */ public void startAlg() { if (!stepWait) { ((ImageButton)runItem).setDisable(); dataMenu.setEnabled(false); } ((ImageButton)stopItem).setEnable(); ((ImageButton)stepItem).setEnable(); if (alg.isAlive() && !stepWait) { alg.stop(); alg = makeAlgThread(); alg.start(); } else if (alg.isAlive() && stepWait) { //alg.resume(); step = !step; stepWait = false; } else { // alg.isAlive() == false alg = makeAlgThread(); alg.start(); } } /** * This method is invoked at the end of the animation or when * the stop button is pressed. It restores the buttons * status on the control panel. */ public void finishAlg() { ((ImageButton)stopItem).setDisable(); ((ImageButton)runItem).setEnable(); ((ImageButton)skipItem).setEnable(); dataMenu.setEnabled(true); ((ImageButton)stepItem).setEnable(); step = false; alg.restoreDrawingPanel(); } /** * This method is called when the step execution mode is used. * It is normally added to the line where the execution will wait * for the step button to be pressed. */ public void waitStep() { if( DEBUG ) System.out.println("AlgAnimFrame:waitStep - step " + step ); if (step) { ((ImageButton)stepItem).setEnable(); repaint(); step = false; stepWait = true; // Sleep to prevent CPU busy waiting for user while ( !step ) { setText(0, "Click NEXT STEP..."); try { Thread.sleep(100); } catch (InterruptedException e) {} } if (!stepWait) step = false; setText(0, ""); if( DEBUG ) System.out.println("AlgAnimFrame:waitStep exit - step " + step ); } } /** * This method is called when the skip execution mode is used. * It is normally added to the line where the execution will wait * after the skip button to be pressed. */ public void waitSkip() { alg.waitSkip(); } /** * Sets the attribute which indicate if the skip execution * mode is current. */ public void setSkip(boolean skip) { dpAfter.setSkip(skip); } /** * Sets the attribute which indicate if the step execution * mode is current. */ public void setStep(boolean step) { System.out.println("AlgAnimFrame:setStep " + step ); this.step = step; ((ImageButton)stepItem).setDisable(); ((ImageButton)runItem).setEnable(); dataMenu.setEnabled(true); if (step) { ((ImageButton)stepItem).setDisable(); stepWait = true; } else stepWait = false; } /** * Returns the reference to the AlgThread which contains the details and * execution sequences of the algorithm. * @see AlgThread */ public AlgThread getAlg() { return alg; } /** Return a reference to the user input frame * @see UIFrame **/ public UIFrame getUIFrame() { return ui_frame; } /** * Set the delay for highlighting text. */ public void setDelay(int delay) { this.delay = delay; } /** * Get the delay for highlighting text. */ public int getDelay() { return delay; } /** * Get the applet which contains a button to start up this window. * @return Returns the applet which contains the button to start up * this window. */ public AlgAnimApp getApplet() { return parentApp; } /** * Returns an instance of the drawing panel which is cast to its super class * Panel. */ public BasicDrawingPanel[] getDrawingPanel() { return dp; } public BasicDrawingPanel getBeforeDp() { if ( dp.length > 1 ) return dp[dp.length-2]; return null; } /** * Shuffles the images down and draws a label on the "before" image * (the one immediately preceding the current one */ public void drawBeforeLabel( ShadowLabel label ) { panel.drawBeforeLabel( label ); } /** * Sets the drawing panel which is cast to its super class * Panel. This instance is used to set the GridBagConstraint * of the layout manager ??????. * @see DrawingPanel */ public void setDrawingPanel(DrawingPanel panel) { this.dpAfter = panel; } /** * Returns an instance of the TextFrame used to set the layout * constraints and highlight certain lines of the source code. * @see TextFrame */ public TextFrame getTextFrame() { return tf; } /** * Get the commentary panel that displays messages of any type. * @return Commentary panel, in which each text field within can be set to * display text string from this class. * @see ComPanel */ public ComPanel getComPanel() { return cpanel; } /** * Get the index of selected choice from the 'Select' pull menu. * @return The index of the data set selected or UI_SELECTED to indicate * user input has been selected */ public int getDataChoice() { return dataSelected; } /** * Get the skip button from the control panel. * @return The skip button. */ public Button getSkipItem() { return skipItem; } /** * Get the run button from the control panel. * @return The run button. */ public Button getRunItem() { return runItem; } /** * Get the stop button from the control panel. * @return The stop button. */ public Button getStopItem() { return stopItem; } /** * Obtain the status of the preferred animation style. * @return True is the animation is kept to a minimum for the animated * algorithm; false otherwise. */ public boolean isNoAnim() { return noAnim; } /** * Get the menu item which specify if the animation is enabled. * @return The checkbox menu item to enable the Animation of the alg. */ public CheckboxMenuItem getEnableAnim() { return enableAnim; } /** * Get the menu item which specify if the animation is disabled. * @return The checkbox menu item to disable the Animation of the alg. */ public CheckboxMenuItem getDisableAnim() { return disableAnim; } public DrawingPanel getCurrentPanel() { return (DrawingPanel)dp[ dp.length-1 ]; } public BasicDrawingPanel getPreviousPanel() { if ( dp.length > 1 ) return dp[ dp.length-2 ]; return null; } /* ----------------- Data file related methods --------------------- */ public NamedIntList getNamedIntList( int k ) { if ( data_sets != null ) { return data_sets.getNamedIntList( k ); } return null; } /* ------ static methods allow the animation to run without having a reference to the animation frame ----- */ public static AlgAnimFrame makeAlgAnimFrame(AlgAnimApp parentApp, URL sourceURL) { frame = new AlgAnimFrame( parentApp, sourceURL ); return frame; } public static void pause() { try { Thread.sleep(100); } catch (InterruptedException e) {} } public static void pauseStep() { frame.waitStep(); } public static void pauseSkip(){ frame.waitSkip(); } public static void highlightText( int n ){ frame.Highlight ( n ); } public static void showText( int n, String s ) { System.out.println("AlgAnimFrame:showText [" + s + "]" ); frame.cpanel.setTopText( n, s ); } public static DrawingPanel getPanel(){ DrawingPanel dp = frame.getCurrentPanel(); return dp; } public void clearPanels() { int n_panels = dp.length; if( DEBUG ) System.out.println("AlgAnimFrame:clearPanels - " + n_panels + " panels" ); for(int k=0;k