/* 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(); } } } } }