package edu.princeton.cs.algs4.growingtree.framework;
/*
* @(#)TreeJPanel.java
*
* Last Modified: 9/01/02
*/
import java.io.File;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import java.beans.*;
/** * This class provides the panel for a Tree
. It keeps the graphics for drawing
* the tree and the image for redrawing. It also keeps a timer for the animation and all changes
* to the tree proceed through the panel. This is a class that is extended for each specific tree.
*
* @author Corey Sanders
* @version 3.5 9/15/01
*/
public class TreeJPanel
extends DrawingJPanel implements OptionListener, ActionListener, TreeMessageListener { /** * TreeHead kept for the current Panel. */ private AnimatingTreeHead
tree;
/**
* Timer for the animation.
*/
javax.swing.Timer animationTimer;
/**
* The delay rate for the animation, set to DEFAULT_DELAY.
*/
private int delayRate;
/**
* Represents the current type of inserting value.
*/
private String keyType;
/**
* Flag whether the tree is animating or not.
*/
private boolean animating = true;
/**
* Flag whether the tree is stepping or not.
*/
private boolean step = false;
/**
* Listeners for the tree messages passed.
*/
private LinkedList tree) {
if (getTree() != null) {
getTree().removeTreeMessageListener(this);
}
this.tree = tree;
getTree().addTreeMessageListener(this);
}
/**
* Sets the delay rate for the timer for the animation.
*
* param t int delay rate.
*/
public void setDelayRate(int t) {
//delayRate = (int)((t*-10) + 1000);
delayRate = t;
if (getAnimationTimer() != null) {
getAnimationTimer().setDelay(delayRate);
}
}
/**
* Sets whether the tree is animating or not.
*
* @param animating boolean flag as to whether the tree is animating.
*/
public void setAnimating(boolean animating) {
this.animating = animating;
setDrawTree(true);
repaint();
}
/**
* Sets whether the tree is stepping or not.
*
* @param step boolean flag as to whether the tree is stepping.
*/
public void setStep(boolean step) {
this.step = step;
getTree().setStepPause(step);
}
/**
* Sets the animation timer for the animation of the panel.
*
* @param animationTimer javax.swing.Timer defining the steps of animation.
*/
protected void setAnimationTimer(javax.swing.Timer animationTimer) {
this.animationTimer = animationTimer;
}
/**
* Sets the key type for the tree.
*
* @param keyType String defining the type of key in the tree.
*/
protected void setKeyType(String keyType) {
this.keyType = keyType;
}
/**
* Given a String, converts the string into the specific type set currently for the tree.
* A getTree() {
return tree;
}
/**
* Gets whether the tree is animating or not.
*
* @param true if the tree is animating.
*/
public boolean isAnimating() {
return animating;
}
/**
* Gets whether the tree is stepping or not.
*
* @param true if the tree is stepping.
*/
public boolean isStep() {
return step;
}
/**
* Gets the key type for the tree.
*
* @return String defining the type of key in the tree.
*/
public String getKeyType() {
return keyType;
}
/**
* Gets the animation timer for the animation of the panel.
*
* @return javax.swing.Timer defining the steps of animation.
*/
protected javax.swing.Timer getAnimationTimer() {
return animationTimer;
}
/**
* Gets the input options for the current tree.
*
* @return String array of the options for the current tree.
*/
public void getInputOptions() {
String[] options = {INTEGER, CHARACTER, DOUBLE};
TreeMessageEvent messageEvent = new TreeMessageEvent(this, TreeMessageEvent.PANEL, TreeMessageEvent.SET_INPUT_OPTIONS, options);
ListIterator list = treeListeners.listIterator(0);
while (list.hasNext()) {
((TreeMessageListener)list.next()).treeMessageEventPerformed(messageEvent);
}
}
/**
* Gets a List of objects made from the String text. The method first gets a List of
* Strings using returnTree = getTree().select(getTree().getChild(), key);
if (returnTree == null) {
messageAction(TreeMessageEvent.ERROR_MESSAGE, "You must enter an integer less\nthan the size of that node's subtree!");
}
}
catch (NumberFormatException e) {
makeTypeIntegerErrorMessage(" selection.");
break;
}
}
setDrawTree(true);
repaint();
}
/**
* Deletes the given text from the tree. The method uses the protected method
* node) {
// Set initial status.
setAnimationStatus();
getTree().remove(node);
setDrawTree(true);
repaint();
}
/**
* Partitions the given node. A JOptionPane appears requesting the entering of the kth element
* with which to partition.
*
* @param node the node to partition.
*/
protected void partitionNode(Tree node) {
// Set initial status.
setAnimationStatus();
if (node == null)
return;
// Get the kth element.
String response = JOptionPane.showInputDialog(this, "Please enter the kth\nelement to partition:", "Partition Element", JOptionPane.QUESTION_MESSAGE);
try {
int key = (Integer.decode(response)).intValue();
Tree returnTree = getTree().partition(node, key);
if (returnTree == null) {
JOptionPane.showMessageDialog(this, "You must enter an integer less than the size of that node's subtree!", "Type Error", JOptionPane.ERROR_MESSAGE);
}
}
catch (NumberFormatException e) {
JOptionPane.showMessageDialog(this, "You must enter an integer count for a partition!", "Type Error", JOptionPane.ERROR_MESSAGE);
}
setDrawTree(true);
repaint();
}
/**
* Selects the given node. A JOptionPane appears requesting the entering of the kth element
* with which to select.
*
* @param node the node to select from.
*/
protected void selectNode(Tree node) {
// Set initial status.
setAnimationStatus();
if (node == null)
return;
// Get the kth element.
String response = JOptionPane.showInputDialog(this, "Please enter the kth\nelement to select:", "Select Element", JOptionPane.QUESTION_MESSAGE);
try {
int key = (Integer.decode(response)).intValue();
Tree returnTree = getTree().select(node, key);
if (returnTree == null) {
JOptionPane.showMessageDialog(this, "You must enter an integer less than the size of that node's subtree!", "Type Error", JOptionPane.ERROR_MESSAGE);
}
}
catch (NumberFormatException e) {
JOptionPane.showMessageDialog(this, "You must enter an integer count for a partition!", "Type Error", JOptionPane.ERROR_MESSAGE);
}
setDrawTree(true);
repaint();
}
/**
* Changes the input according to the text string. The tree is cleared if it is not empty.
* Also a key type change message is sent to all listeners.
*
* @param text the String representing the input change.
*/
protected void inputChange(String text) {
// Already set
if (getKeyType().equals(text))
return;
// Clear tree
clear();
setKeyType(text);
}
/**
* Clears the tree. Initiates a JOptionPane to confirm clearing.
*/
protected int clearTree() {
// Set initial status.
setAnimationStatus();
// Confirm
int response = JOptionPane.showConfirmDialog(this, "Are you sure you wish to clear the tree?", "Clear Tree", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if (response == JOptionPane.YES_OPTION) {
// Clears tree
clear();
// Cleared tree
return 1;
}
// Didn't clear
return 0;
}
/**
* Clears the tree. Does not initiate a JOptionPane to confirm clearing. Just clears.
*/
protected void clear() {
// Clears tree
getTree().clear();
setDrawTree(true);
repaint();
}
/************************************************/
/* Commands that are called after OptionEvents. */
/************************************************/
public void setSubtreeCountsCommand(boolean visible)
{
setDrawTree(true);
repaint();
}
/**
* Insert command. Simply calls insertKeys with the text. Overide if necessary.
*
* @param text String to insert.
*/
public void insertCommand(String text) {
insertKeys(text);
}
/**
* Search command. Simply calls searchKeys with the text. Overide if necessary.
*
* @param text String to search.
*/
public void searchCommand(String text) {
searchKeys(text);
}
/**
* Delete command. Simply calls deleteKeys with the text. Overide if necessary.
*
* @param text String to delete.
*/
public void deleteCommand(String text) {
deleteKeys(text);
}
/**
* Insert command. Simply calls insertKeys with the text. Overide if necessary.
*
* @param node Tree to delete.
*/
public void deleteCommand(Tree node) {
deleteNode(node);
}
/**
* Select command. Simply calls selectKeys with the text. Overide if necessary.
*
* @param text String to select.
*/
public void selectCommand(String text) {
selectKeys(text);
}
/**
* Select command. Simply calls selectKeys with the text. Overide if necessary.
*
* @param text String to select.
*/
public void selectCommand(Tree node) {
selectNode(node);
}
/**
* Clear command. Simply calls clearTree. Overide if necessary.
*/
public void clearCommand() {
clearTree();
}
/**
* Clear all command. Simply calls clear. Overide if necessary.
*/
public void clearAllCommand() {
clear();
}
/**
* Partition command. Simply calls partitionNode. Overide if necessary.
*
* @param node Tree to partition.
*/
public void partitionCommand(Tree node) {
partitionNode(node);
}
/**
* RotateUp command. Does Nothing (not all trees have rotations). Overide if necessary.
*
* @param node Tree to rotateUp.
*/
public void rotateUpCommand(Tree node) {
}
/**
* RotateToTop command. Does Nothing (not all trees have rotations). Overide if necessary.
*
* @param node Tree to rotateToTop.
*/
public void rotateToTopCommand(Tree node) {
}
/**
* RotateUpDouble command. Does Nothing (not all trees have rotations). Overide if necessary.
*
* @param node Tree to rotateUpDouble.
*/
public void rotateUpDoubleCommand(Tree node) {
}
/**
* Splay command. Does Nothing (not all trees have rotations). Overide if necessary.
*
* @param node Tree to splay.
*/
public void splayCommand(Tree node) {
}
/**
* Balance command. Does Nothing. Overide if necessary.
*
* @param node Tree to balance.
*/
public void balanceCommand(Tree node) {
}
/**
* Traverse command. Does Nothing. Overide if necessary.
*
* @param traverseType int that defines the traverseType.
*/
public void traverseCommand(int traverseType) {
}
/**
* Play command.
*/
public void playCommand() {
getTree().setJumpStep(false);
getTree().setStepPause(false);
getTree().play();
}
/**
* Play command.
*/
public void playStepCommand() {
getTree().setJumpStep(false);
getTree().setStepPause(true);
getTree().play();
}
/**
* Play command.
*/
public void playFastCommand() {
getTree().setJumpStep(true);
getTree().setStepPause(true);
getTree().play();
}
/**
* Play command.
*/
public void rewindCommand() {
getTree().setJumpStep(false);
getTree().setStepPause(false);
getTree().rewind();
}
/**
* Play command.
*/
public void rewindStepCommand() {
getTree().setJumpStep(false);
getTree().setStepPause(true);
getTree().rewind();
}
/**
* Play command.
*/
public void rewindFastCommand() {
getTree().setJumpStep(true);
getTree().setStepPause(true);
getTree().rewind();
}
/**
* Pause command.
*/
public void pauseCommand() {
getTree().pause();
}
/**
* Stop command.
*/
public void stopCommand() {
getTree().stop();
}
/**
* Animation command.
*
* @param step boolean turning animation on or off.
*/
public void animatingCommand(boolean animatingCommand) {
setAnimating(animatingCommand);
}
/**
* Animation quality command.
*
* @param quality int setting the quality of the animation.
*/
public void animationQualityCommand(int quality) {
setDelayRate(quality);
}
/**
* Animation speed command.
*
* @param speed int setting the speed of the animation.
*/
public void animationSpeedCommand(int speed) {
getTree().setTreeAnimationsStepSize(speed);
}
/**
* Input Change Command.
*
* @param input string setting the new input type.
*/
public void inputChangeCommand(String input) {
inputChange(input);
}
public void saveTreeCommand(TreeJPanel panel) {
if (panel != this) {
panel.setSettings(this);
}
}
/**
* Constructs a popupmenu, using the actionlistener passed. The popupMenu
* contains all of the options available through the current tree. For this
* class, no items are added, making it necessary to overide the method.
*
* @param actionListener the listener add to the actions of all the items made in the menu.
*
* @return JPopupMenu which is the menu constructed within the panel.
*/
public JPopupMenu makeJPopupMenu(ActionListener actionListener) {
JPopupMenu popupMenu = new JPopupMenu();
return popupMenu;
}
/**
* Passes a message to make the color scheme options for the current Panel. Generally called
* if this panel becomes selected. A tree message is sent with panel) {
}
/**
* Recieved an event from an OptionJPanel with one of the given commands. The Panel
* responds accordingly. The Methods it calls (for example: insertCommand, rotateCommand...)
* should be overiden in extending classes to functionable.
*
* @param e OptionEvent recieved from the event performed.
*/
public void optionEventPerformed(OptionEvent e) {
if (e.getActionCommand().equals(OptionEvent.INTEGER_FIELDS_ON)) {
setSubtreeCountsCommand(true);
}
if (e.getActionCommand().equals(OptionEvent.INTEGER_FIELDS_OFF)) {
setSubtreeCountsCommand(false);
}
if (e.getActionCommand().equals(OptionEvent.INSERT)) {
insertCommand((String)e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.CLEAR)) {
clearCommand();
}
if (e.getActionCommand().equals(OptionEvent.CLEAR_ALL)) {
clearAllCommand();
}
if (e.getActionCommand().equals(OptionEvent.ROTATE_CLICK)) {
rotateUpCommand((Tree )e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.ROTATE_TOP_CLICK)) {
rotateToTopCommand((Tree )e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.ROTATE_DOUBLE_CLICK)) {
rotateUpDoubleCommand((Tree )e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.SPLAY_CLICK)) {
splayCommand((Tree )e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.PARTITION_CLICK)) {
partitionCommand((Tree )e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.DELETE)) {
deleteCommand((String)e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.DELETE_CLICK)) {
deleteCommand((Tree )e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.INPUT_CHANGE_ALL)) {
inputChangeCommand((String)e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.SEARCH)) {
searchCommand((String)e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.SELECT)) {
selectCommand((String)e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.SELECT_CLICK)) {
selectCommand((Tree )e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.BALANCE)) {
balanceCommand(null);
}
if (e.getActionCommand().equals(OptionEvent.BALANCE_CLICK)) {
balanceCommand((AnimatingTree )e.getObjectValue());
}
if (e.getActionCommand().equals(OptionEvent.PREORDER_TRAVERSAL)) {
traverseCommand(TreeHead.PREORDER_TRAVERSAL);
}
if (e.getActionCommand().equals(OptionEvent.POSTORDER_TRAVERSAL)) {
traverseCommand(TreeHead.POSTORDER_TRAVERSAL);
}
if (e.getActionCommand().equals(OptionEvent.INORDER_TRAVERSAL)) {
traverseCommand(TreeHead.INORDER_TRAVERSAL);
}
if (e.getActionCommand().equals(OptionEvent.LEVELORDER_TRAVERSAL)) {
traverseCommand(TreeHead.LEVELORDER_TRAVERSAL);
}
if (e.getActionCommand().equals(OptionEvent.ANIMATION_PAUSE)) {
pauseCommand();
}
if (e.getActionCommand().equals(OptionEvent.ANIMATION_STOP)) {
stopCommand();
}
if (e.getActionCommand().equals(OptionEvent.ANIMATION_PLAY)) {
playCommand();
}
if (e.getActionCommand().equals(OptionEvent.ANIMATION_REWIND)) {
rewindCommand();
}
if (e.getActionCommand().equals(OptionEvent.ANIMATION_STEP_REWIND)) {
rewindStepCommand();
}
if (e.getActionCommand().equals(OptionEvent.ANIMATION_STEP_FORWARD)) {
playStepCommand();
}
if (e.getActionCommand().equals(OptionEvent.ANIMATION_FAST_REWIND)) {
rewindFastCommand();
}
if (e.getActionCommand().equals(OptionEvent.ANIMATION_FAST_FORWARD)) {
playFastCommand();
}
if (e.getActionCommand().equals(OptionEvent.ANIMATION_ON)) {
animatingCommand(true);
}
if (e.getActionCommand().equals(OptionEvent.ANIMATION_OFF)) {
animatingCommand(false);
}
if (e.getActionCommand().equals(OptionEvent.ANIMATION_QUALITY)) {
animationQualityCommand(((Integer)e.getObjectValue()).intValue());
}
if (e.getActionCommand().equals(OptionEvent.ANIMATION_SPEED)) {
animationSpeedCommand(((Integer)e.getObjectValue()).intValue());
}
if (e.getActionCommand().equals(OptionEvent.GET_INPUT_OPTIONS)) {
getInputOptions();
}
if (e.getActionCommand().equals(OptionEvent.SAVE_ALL)) {
saveTreeCommand((TreeJPanel )e.getObjectValue());
}
}
/************************************************/
/* Implements TreeMessage Listener */
/************************************************/
/**
* Listens to tree message events.
*
* @param e TreeMessageEvent that contains information about the tree.
*/
public void treeMessageEventPerformed(TreeMessageEvent e) {
// FINISH message or REDRAW message
if ((e.getMessage().equals(Animation.FINISH)) || (e.getMessage().equals(Animation.REDRAW))) {
setDrawTree(true);
repaint();
}
// Else send the message out further to those listenning to this object
if (!(e.getMessage().equals(Animation.REDRAW))) {
messageAction(e.getMessage(), e.getMessageObject());
}
}
/************************************************/
/* Implements Action Listener */
/************************************************/
/**
* Listens to action events.
*
* @param e ActionEvent that contains information about the tree.
*/
public void actionPerformed(ActionEvent e) {
repaint();
}
}
NumberFormatException
is caught and an error message String is returned instead
* of an object.
*
* @param text String to be converted into an object.
*
* @return the object after converting to the type of the tree, or a String error message.
*/
protected KeyType stringToType(String text) {
String errors;
KeyType returnObject = null;
// Allow flexibility for the first item
if (this.getTree().getChild() == null) {
try {
returnObject = new KeyType(Integer.parseInt(text));
setKeyType(INTEGER);
}
catch (NumberFormatException e) {
try {
returnObject = new KeyType(Double.parseDouble(text));
setKeyType(DOUBLE);
}
catch (NumberFormatException e2) {
returnObject = new KeyType(text.charAt(0));
setKeyType(CHARACTER);
}
}
}
// Make sure the key type is correct
else {
try {
if (getKeyType().equals(INTEGER)) {
returnObject = new KeyType(Integer.parseInt(text));
}
if (getKeyType().equals(CHARACTER)) {
returnObject = new KeyType(text.charAt(0));
}
if (getKeyType().equals(DOUBLE)) {
returnObject = new KeyType(Double.parseDouble(text));
}
}
catch (NumberFormatException e) {
errors = new String(" "+text+" ");
returnObject = new KeyType(errors);
}
}
return returnObject;
}
/****************/
/* Accesors */
/****************/
/**
* Gets the title for the tree.
*
* @return String defining the title of the tree.
*/
public String getTitle() {
return "None";
}
/**
* Get the tree message. The method calls TreeStatusMessage
of the tree within the panel.
*/
public String getTreeStatusMessage() {
return getTree().getTreeStatusMessage();
}
/**
* Gets the head of the tree currently drawn in the Panel.
*
* @return TreeHead the tree head.
*/
public AnimatingTreeHeadgetStringList
. Then the method uses stringToType
* repeatedly, constructing a LinkedList of objects. Any incorrect strings, the error message
* is automatically made and sent in the form of a Dialog.
*
* @param text String to be made into an Object list.
*
* @return LinkedList List of objects.
*/
protected LinkedListgetObjectList
to produce objects. Then, those objects are searched for.
*
* @param text String to be searched into the tree.
*/
protected void searchKeys(String text) {
// Set initial status.
setAnimationStatus();
// Object list constructed
LinkedListgetObjectList
to produce objects. Then, those objects are selected.
*
* @param text String to be selected into the tree.
*/
protected void selectKeys(String text) {
// Set initial status.
setAnimationStatus();
// Object list constructed
LinkedListgetObjectList
to produce objects. Then, those objects are deleted.
*
* @param text String to be deleted into the tree.
*/
protected void deleteKeys(String text) {
// Set initial status.
setAnimationStatus();
StringBuffer errors = null;
// Object list constructed
LinkedListTreeMessageEvent.COLOR_PANEL
with
* this as the object.
*/
public void makeColorSchemeOptions() {
messageAction(TreeMessageEvent.COLOR_PANEL, this);
}
/**
* Passes a message to make the color settings options for the current Panel. Generally called
* if this panel becomes selected. A tree message is sent with TreeMessageEvent.SET_PRESET_COLOR_OPTIONS
with
* this as the object.
*/
public void makeColorSettings() {
messageAction(TreeMessageEvent.SET_PRESET_COLOR_OPTIONS, this);
}
/**
* Constructs the color settings for this tree, using the given parameter.
*
* @param allTreeToolsPanel Panel to construct the color settings.
*/
public void constructColorOptions(ColorOptionsJPanel allTreeToolsPanel) {
}
/**
* Constructs the color settings combo box and sets the box for the given colorOptionsPanel.
*
* @param colorOptionsPanel the colorOptionsPanel for which the JComboBox is set.
*/
public void constructPresetColorOptions(ColorOptionsJPanel colorOptionsPanel) {
}
/************************/
/* Tree Message Methods */
/************************/
/**
* Adds an TreeMessageListener from the TREE, according to
* the TreeMessageListener interface and the TreeMessageEvent
.
*
* @param l the listener added recieves the TreeMessageEvents occuring.
*/
public void addTreeMessageListener(TreeMessageListener l) {
treeListeners.add(l);
}
/**
* Removes an TreeMessageListener from the TREE, according to
* the TreeMessageListener interface and the TreeMessageEvent
.
*
* @param l the listener removed from recieving the TreeMessageEvents occuring.
*/
public void removeTreeMessageListener(TreeMessageListener l) {
treeListeners.remove(l);
}
/**
* Calls all of the treeListeners of the Tree and passes the tree message information information regarding the
* status of the Tree.
*
* @param msg String message for the action of the message.
* @param msgObj the accompanying object for the message.
*/
protected void messageAction(String msg, Object msgObj) {
// Returns if the panel is not shown, and the message is not an error or tree status (msgObj = tree).
if (!isComponentShown() && !(msg.equals(TreeMessageEvent.ERROR_MESSAGE)) && !(msg.equals(TreeMessageEvent.COLOR_PANEL)) && !(msgObj == getTree()) && !(msg.equals(TreeMessageEvent.SET_PRESET_COLOR_OPTIONS))) {
return;
}
if (msgObj == getTree()) {
TreeMessageEvent messageEvent = new TreeMessageEvent(this, TreeMessageEvent.PANEL, TreeMessageEvent.STATUS_MESSAGE);
ListIterator list = treeListeners.listIterator(0);
while (list.hasNext()) {
((TreeMessageListener)list.next()).treeMessageEventPerformed(messageEvent);
}
}
TreeMessageEvent messageEvent = new TreeMessageEvent(this, TreeMessageEvent.PANEL, msg, msgObj);
ListIterator list = treeListeners.listIterator(0);
while (list.hasNext()) {
((TreeMessageListener)list.next()).treeMessageEventPerformed(messageEvent);
}
}
/******************************/
/* Implements OptionListener. */
/******************************/
/**
* Sets the settings for the panel param.
*
* @param panel the panel for the settings to be set.
*/
public void setSettings(TreeJPanel