/*
This file is part of the algoviz@vt collection of algorithm
visualizations.
This program is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this distribution. If not, see
.
*/
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
/**
* This is the main class behind the BST applet. It has a feedback panel, main
* panel, and control panel. The main panel holds a TreePanel
* which controls manipulation of the BST itself.
*
* @see TreePanel
* @author Andrew Whitaker (aawhitak@vt.edu)
*/
public class TreeApplet extends JApplet
{
/**
*
*/
private static final long serialVersionUID = 1L;
/*
* Styles for feedback. INTERMEDIATE_STYLE = The style of an intermediate
* step in an operation. FINAL_STYLE = The style of the last step of an
* operation (could come in handy, not currently used). UNFOCUSED_STYLE =
* The style of an unfocused step thats displayed in feedback. For example,
* this style would be applied after a state is stepped over.
*/
public static Style INTERMEDIATE_STYLE, MAIN_STYLE, UNFOCUSED_STYLE;
private DefaultStyledDocument doc;
private Container content;
/* The main components of the TreeApplet. */
private JPanel controlsPanel, feedbackPanel;
/* Radio buttons the user will use to select an operation */
private JRadioButton findRadioButton, deleteRadioButton, insertRadioButton;
private JTextPane feedback;
/* User-input text field */
private JTextField insertTextField;
/*
* Not used, but could be used to display a message to the user in the
* controls panel
*/
private JLabel message;
/* Control buttons */
private JButton startButton, next, previous, first, last;
/*
* Represents the current step number while iterating through a BST
* operation.
*/
private int stepNum;
private int docPos;
/* TreePanel holding/controlling the BST */
private TreePanel treePanel;
/**
* Creates a new TreeApplet. The applet has a BorderLayout with the
* TreePanel in the center, feedback to the right, and controls at the
* bottom.
*/
public TreeApplet()
{
super();
/* Setup styles and other things necessary for feedback. */
initializeFeedback();
/* Set up applet */
content = new Container();
content.setLayout(new BorderLayout());
/* Controls */
controlsPanel = new JPanel();
controlsPanel.setLayout(new BoxLayout(controlsPanel, BoxLayout.X_AXIS));
ButtonGroup modeGroup = new ButtonGroup();
insertRadioButton = new JRadioButton("Insert");
insertRadioButton.setBackground(Color.WHITE);
insertRadioButton.addActionListener(new RadioButtonListener());
findRadioButton = new JRadioButton("Find");
findRadioButton.addActionListener(new RadioButtonListener());
deleteRadioButton = new JRadioButton("Delete");
deleteRadioButton.setBackground(Color.WHITE);
deleteRadioButton.addActionListener(new RadioButtonListener());
findRadioButton.setBackground(Color.WHITE);
modeGroup.add(findRadioButton);
modeGroup.add(deleteRadioButton);
modeGroup.add(insertRadioButton);
JPanel modePanel = new JPanel();
modePanel.setLayout(new BoxLayout(modePanel, BoxLayout.Y_AXIS));
modePanel.add(findRadioButton);
modePanel.add(deleteRadioButton);
modePanel.add(insertRadioButton);
modePanel.setBackground(Color.WHITE);
controlsPanel.add(modePanel);
controlsPanel.setBackground(Color.WHITE);
controlsPanel.setBorder(BorderFactory.createEtchedBorder());
content.add(controlsPanel, BorderLayout.SOUTH);
/* Text field and buttons */
JPanel inputPanel = new JPanel();
JPanel stepsPanel = new JPanel(new FlowLayout());
inputPanel.setLayout(new BoxLayout(inputPanel, BoxLayout.Y_AXIS));
insertTextField = new JTextField(10);
stepsPanel.add(insertTextField);
startButton = new JButton("Start");
startButton.setEnabled(false);
insertTextField.setEnabled(false);
stepsPanel.add(startButton);
next = new JButton(">");
previous = new JButton("<");
first = new JButton("<<");
last = new JButton(">>");
stepsPanel.add(first);
stepsPanel.add(previous);
stepsPanel.add(next);
stepsPanel.add(last);
first.setEnabled(false);
previous.setEnabled(false);
next.setEnabled(false);
last.setEnabled(false);
JPanel messagePanel = new JPanel();
message = new JLabel();
message.setForeground(Color.RED);
messagePanel.add(message);
inputPanel.add(stepsPanel);
inputPanel.add(messagePanel);
controlsPanel.add(inputPanel);
/* Add Listeners */
startButton.addActionListener(new ControlButtonListener());
next.addActionListener(new ControlButtonListener());
previous.addActionListener(new ControlButtonListener());
first.addActionListener(new ControlButtonListener());
last.addActionListener(new ControlButtonListener());
/* End Controls */
/* Feedback */
feedbackPanel = new JPanel();
feedbackPanel.setLayout(new BoxLayout(feedbackPanel, BoxLayout.Y_AXIS));
JScrollPane scrollPanel = new JScrollPane(feedback);
scrollPanel.setPreferredSize(new Dimension(150, 700));
feedbackPanel.setBackground(Color.WHITE);
JPanel labelPanel = new JPanel(new FlowLayout());
labelPanel.add(new JLabel("Step History"));
labelPanel.setBackground(Color.WHITE);
feedbackPanel.add(labelPanel);
feedbackPanel.add(scrollPanel);
content.add(feedbackPanel, BorderLayout.EAST);
setContentPane(content);
/* End Feedback */
/* Makes scrolling for large BST's possible */
treePanel = new TreePanel(this);
treePanel.setPreferredSize(new Dimension(2000, 1200));
JScrollPane scroll = new JScrollPane(treePanel);
// scroll.setPreferredSize(new Dimension(1200, 1200));
content.add(scroll, BorderLayout.CENTER);
}
/**
* Sets up styles and the Document that handles feedback. See the
* Document
javadocs for more information on how Documents in
* Java work.
*/
private void initializeFeedback()
{
StyleContext sc = new StyleContext();
doc = new DefaultStyledDocument(sc);
docPos = 0;
feedback = new JTextPane(doc);
feedback.setEditable(false);
INTERMEDIATE_STYLE = sc.addStyle("Intermediate", null);
INTERMEDIATE_STYLE
.addAttribute(StyleConstants.Foreground, Color.ORANGE);
INTERMEDIATE_STYLE.addAttribute(StyleConstants.LeftIndent, 1.2);
MAIN_STYLE = sc.addStyle("Final", null);
MAIN_STYLE.addAttribute(StyleConstants.Foreground, Color.BLUE);
MAIN_STYLE.addAttribute(StyleConstants.LeftIndent, 1.2);
UNFOCUSED_STYLE = sc.addStyle("Unfocused", null);
UNFOCUSED_STYLE.addAttribute(StyleConstants.Foreground, Color.GRAY);
UNFOCUSED_STYLE.addAttribute(StyleConstants.LeftIndent, 1.2);
}
/**
* Focuses a state in the current BST operation.
*
* @see BasicState.java
* @param s
* The state to focus.
*/
public void focusState(State s)
{
System.out.println("FOCUSING " + s.getNode().toString());
String docText = new String(), text = s.getText();
GraphicalNode currentNode = s.getNode();
System.out.println(currentNode);
/*
* If this is the first time the node will be displayed, animate it in.
* For more information about animation, see TreePanel.java
*/
if (currentNode.isHidden())
{
treePanel.animateNodeEntry(currentNode);
// currentNode.select();
// treePanel.repaint();
}
else
{
currentNode.select();
treePanel.setRepaint(true);
treePanel.setRecalculateTree(false);
treePanel.repaint();
}
try
{
docText = doc.getText(0, doc.getLength());
}
catch (BadLocationException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
String workingText = docText.substring(docPos);
if (workingText.lastIndexOf(text) != -1)
{
doc.setCharacterAttributes(docText.lastIndexOf(text),
text.length(), INTERMEDIATE_STYLE, true);
}
else
{
try
{
doc.insertString(doc.getLength(), s.getText(),
INTERMEDIATE_STYLE);
}
catch (BadLocationException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
/**
* Unfocuses a state. Changes the color of the node and changes the selected
* text in the feedback area.
*
* @see BasicState.java
*
* @param s
* The State to focus
*/
public void unfocusState(State s)
{
System.out.println("UNFOCUSING: " + s.getNode().toString());
String docText = new String(), text = s.getText();
System.out.println(s.getNode());
/* Unselect the node */
s.getNode().unSelect();
try
{
docText = doc.getText(0, doc.getLength());
}
catch (BadLocationException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
doc.setCharacterAttributes(docText.lastIndexOf(text), text.length(),
UNFOCUSED_STYLE, true);
}
/**
* Listens to RadioButton clicks. This is only used when the application
* initially loads. At first, no radiobuttons are selected, but when the
* user selects one, the input text field should become visible, and so
* should the "Start" button.
*
* @author Andrew Whitaker (aawhitak@vt.edu)
*
*/
public class RadioButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
startButton.setEnabled(true);
insertTextField.setEnabled(true);
}
}
/**
* Internal class that handles control buttons (start, first, end, last,
* prev).
*
* @author Andrew Whitaker (aawhitak@vt.edu)
*
*/
public class ControlButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
JButton src = (JButton) e.getSource();
/* Handle a start button click */
if (src == startButton)
{
if (insertTextField.getText() != "")
{
ArrayList l = treePanel.getStateList();
if (l.size() > 0)
{
for (int n = 0; n < l.size(); n++)
{
unfocusState(l.get(n));
}
}
Integer i = new Integer(insertTextField.getText());
if (insertRadioButton.isSelected())
{
treePanel.insert(i);
try
{
doc
.insertString(doc.getLength(),
"Operation: Insert " + i + "\n",
MAIN_STYLE);
}
catch (BadLocationException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
else if (findRadioButton.isSelected())
{
treePanel.find(i);
try
{
doc.insertString(doc.getLength(),
"Operation: Find " + i + "\n", MAIN_STYLE);
}
catch (BadLocationException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
insertTextField.setText("");
/* Reset the start number */
stepNum = -1;
docPos = doc.getLength();
/* Set the buttons correctly */
next.setEnabled(true);
last.setEnabled(true);
previous.setEnabled(false);
first.setEnabled(false);
}
}
/* Handle a next button click */
else if (src == next)
{
stepNum++;
previous.setEnabled(true);
first.setEnabled(true);
ArrayList list = treePanel.getStateList();
State s = list.get(stepNum);
System.out.println("size: " + list.size());
System.out.println("step: " + stepNum);
if (stepNum > 0)
{
System.out.println("unfocusing state " + (stepNum - 1));
unfocusState(list.get(stepNum - 1));
}
focusState(s);
if (stepNum == list.size() - 1)
{
last.setEnabled(false);
next.setEnabled(false);
startButton.setEnabled(true);
}
}
/* Handle a previous button click */
else if (src == previous)
{
ArrayList list = treePanel.getStateList();
unfocusState(list.get(stepNum));
stepNum--;
if (stepNum == 0)
{
first.setEnabled(false);
previous.setEnabled(false);
}
next.setEnabled(true);
last.setEnabled(true);
State s = list.get(stepNum);
focusState(s);
System.out.println("previous");
System.out.println("size: " + list.size());
System.out.println("step: " + stepNum);
}
/* Handles when the first ('<<') button is pressed */
else if (src == first)
{
// Count down from the current step number and
while (previous.isEnabled())
{
// Programmatically hammer down the prev button
// to go back all the way.
previous.doClick();
}
// Reset the step number.
}
else if (src == last)
{
// See "first" button action for details.
while (next.isEnabled())
{
System.out.println("CLICK>>");
next.doClick();
}
}
}
}
}