/* AlgAnimFrame.java
Dual drawing panel version .. before and after frames
*/
package ciips.animation;
import java.awt.*;
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 {
private AlgAnimApp parentApp;
private URL sourceURL;
private String algname;
private AlgThread alg = null;
private int delay = 200;
private DrawingPanel dp[];
private FlowPanel panel;
private DrawingPanel dpBefore, dpAfter; // to be obsoleted!
private TextFrame tf;
private LegendFrame lf;
private ComPanel cpanel;
public static int COMPANEL_LINES = 4;
private boolean step = false,
stepWait = false;
private Dimension frameSize = new Dimension(850, 700);
// control panel
private MenuItem quitItem;
private Button runItem, stopItem, stepItem, skipItem;
private CheckboxMenuItem enableAnim, disableAnim;
private CheckboxMenuItem[] dataChoice, delayChoice;
private Menu dataMenu, delayMenu;
private int dataSelected = 0;
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 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");
if( DEBUG ) System.out.println("AlgAnimFrame: algname [" + algname + "]");
setTitle(algname);
move(0, 0);
alg = makeAlgThread();
if( DEBUG ) System.out.println("AlgAnimFrame - " +
alg.getClass().toString() + " loaded");
setLayout( new BorderLayout() );
setFont(helv14);
cp = new ControlPanel(this, algname);
add("North", cp);
runItem = cp.getRunButton();
stopItem = cp.getStopButton();
stepItem = cp.getStepButton();
skipItem = cp.getSkipButton();
int panel_count = alg.getDataPanelCount();
dp = new DrawingPanel[ panel_count ];
panel = new FlowPanel(this, dp);
add("Center", panel);
if( panel_count > 1 ) {
dpBefore = dp[panel_count-2];
}
dpAfter = dp[panel_count-1];
/* Source panel */
tf = new TextFrame( sourceURL );
/* Commentary panel */
cpanel = new ComPanel( algname, COMPANEL_LINES );
add( "South", cpanel );
setMenuBar(createMenuBar());
// setMenuBar(createMenuBar());
// generate initial data set
alg.generateData();
pack();
validate();
show();
//if (tf.getTextPanel().getNumLines() > 0)
//tf.toFront();
cp.refreshButtons();
} // init()
private AlgThread makeAlgThread() {
Constructor c;
AlgThread alg = null;
String thread_name = algname + thread_suffix;
if( DEBUG ) System.out.println("AlgAnimFrame:makeAlgThread - " + thread_name );
try {
Class alg_thread = Class.forName( thread_name );
if ( alg_thread == null ) {
System.out.println("forName( " + thread_name + " ) returns null!");
}
if( DEBUG ) System.out.println("AlgAnimFrame:makeAlgThread - class " +
thread_name + " loaded" );
Class arg_classes[] = new Class[ 1 ];
// arg_classes[0] = Class.forName( "ciips.animation.AlgAnimFrame" );
arg_classes[0] = this.getClass();
if( DEBUG ) System.out.println("AlgAnimFrame:makeAlgThread - cons args formed" );
if( DEBUG ) System.out.println("AlgAnimFrame:makeAlgThread - arg_classes " + arg_classes );
if( DEBUG ) System.out.println("AlgAnimFrame:makeAlgThread - arg_classes[0] " + arg_classes[0] );
// try {
// SecurityManager sm = System.getSecurityManager();
// sm.checkMemberAccess( alg_thread, Member.DECLARED );
// } catch( Exception e ) {
// System.out.println("SecManager - member declared exception");
// }
try {
c = alg_thread.getConstructor( arg_classes );
if( DEBUG ) System.out.println("AlgAnimFrame:makeAlgThread - cons found" );
Object args[] = new Object[ 1 ];
args[ 0 ] = this;
try {
alg = (AlgThread)(c.newInstance( args ));
// alg = (AlgThread)(alg_thread.newInstance());
if( DEBUG ) System.out.println(
"AlgAnimFrame:makeAlgThread - AlgThread constructed" );
}
catch( InvocationTargetException ite ) {
Throwable ee = ite.getTargetException();
System.out.println("newIns: " + ite.getClass() + ":" + ite.getMessage() );
System.out.println("newIns: threw " + ee.getClass() + ":" + ee.getMessage() );
}
catch( Exception e ) {
System.out.println("newIns: " + e.getClass() + ":" + e.getMessage() );
}
}
catch( Exception e ) {
System.out.println("getDecCons: " + e.getClass() + ":" + e.getMessage() );
}
}
catch( Exception e ) {
if( DEBUG ) System.out.println("AlgAnimFrame:cons " + e.getClass().toString() + " " + e.getMessage() );
}
return alg;
}
private MenuBar createMenuBar() {
MenuBar mb = new MenuBar();
mb.setFont(helv14);
dataMenu = new Menu("Select");
mb.add(dataMenu);
String dataSets[] = alg.getDataSets();
dataChoice = new CheckboxMenuItem[dataSets.length];
for (int i = 0; i < dataSets.length; i++) {
dataChoice[i] = new CheckboxMenuItem(dataSets[i]);
dataMenu.add(dataChoice[i]);
}
if (dataSets.length > 0)
dataChoice[0].setState(true);
dataMenu.addSeparator();
quitItem = new MenuItem("Quit");
dataMenu.add(quitItem);
/*
Menu algMenu = new Menu("Algorithm", true);
mb.add(algMenu);
runItem = new MenuItem("Run " + algname);
stopItem = new MenuItem("Stop " + algname);
stepItem = new MenuItem("Next Step...");
skipItem = new MenuItem("Skip...");
algMenu.add(runItem);
algMenu.add(stopItem);
algMenu.add(stepItem);
algMenu.add(skipItem);
*/
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);
Menu viewMenu = new Menu("View");
mb.add(viewMenu);
MenuItem srcCode = new MenuItem("Source Code");
if (tf.getTextPanel().getNumLines() < 1)
srcCode.disable();
viewMenu.add(new MenuItem("Source Code"));
viewMenu.add(new MenuItem("Legend Panel"));
Menu helpMenu = new Menu("About");
mb.setHelpMenu(helpMenu);
helpMenu.add(new MenuItem("Credits"));
helpMenu.add(new MenuItem("Copyrights"));
return mb;
} // createMenuBar()
/**
* Returns the preferred size of the frame. By default, it is set
* to 850x700. It can be modified based on the specific application.
* @return the dimension of the frame
*/
public Dimension preferredSize() {
return frameSize;
}
/**
* Sets the size of the frame.
* @param size The desired sized of the frame.
*/
public void setDimension(Dimension size) {
this.frameSize = size;
}
/**
* Event handler of the frame. The main purpose of this method is to
* cleanup upon receival of the WINDOW_DESTROY event message.
*/
public boolean handleEvent(Event event) {
if (cp != null)
cp.refreshButtons();
if (event.id == Event.WINDOW_DESTROY) {
if (alg != null) {
// alg.dpAfter = null;
if (alg.isAlive()) alg.stop();
}
parentApp.start_button.enable();
tf.dispose();
dispose();
}
return super.handleEvent(event);
}
/**
* Action handler for the buttons and choice buttons in the control
* panel.
* @param e Event invoked
* @param arg Object that invokes the event
*/
public boolean action(Event e, Object arg) {
Object target = e.target;
if (target == quitItem) {
getApplet().start_button.enable();
if (getAlg() != null && getAlg().isAlive())
getAlg().stop();
getTextFrame().dispose();
dispose();
}
else if (target instanceof CheckboxMenuItem) {
Menu parent = (Menu)((MenuItem)target).getParent();
if (parent == dataMenu) {
for (int i = 0; i < dataChoice.length; i++) {
if (target == dataChoice[i]) {
dataSelected = i;
alg.generateData();
dataChoice[i].setState(true);
}
else
dataChoice[i].setState(false);
}
}
else if (parent == delayMenu) {
for (int i = 0; i < delayChoice.length; i++) {
if (target == delayChoice[i]) {
setDelay((i+1)*200);
getAlg().setDelay(getDelay());
setText(0, "Animation delay now set to " +
getDelay() + " msec...");
delayChoice[i].setState(true);
}
else
delayChoice[i].setState(false);
}
}
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().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;
}
/**
* 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 DrawingPanel[] getDrawingPanel() {
return dp;
}
public DrawingPanel 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 choice of the data 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 dp[ dp.length-1 ];
}
public DrawingPanel getPreviousPanel() {
if ( dp.length > 1 ) return dp[ dp.length-2 ];
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 ){
frame.setText( n, s );
}
public static DrawingPanel getPanel(){
DrawingPanel dp = frame.getCurrentPanel();
return dp;
}
} // class AlgAnimApp