/* 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;
// 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 );
move(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; kgetComPanel
.
* @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().size().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.disable();
}
((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.enable();
((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.enable();
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