/* * Created on Apr 2, 2004 * * VisStackApplet * An applet that can be modified to produce an interactive * demonstration for a data structure. Written for CSE 373. * It is currently set up to show a stack. * It takes textual commands in a TextArea and when the user * clicks on the Execute button, it processes the commands, * updating the display as it goes. * Here is a sample command sequence: */ /* PUSH John PUSH Mary SIZE POP STATS RESET ; This is a comment - we are beginning with a new stack. DELAY 500 ; from here wait only 500 ms between updates PUSH 3.14159 PUSH 25 PUSH 13 STATS */ /** * @author Steve Tanimoto, Copyright, 2004. * */ import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class VisStackApplet extends JApplet implements ActionListener, Runnable { ScrolledPanel visPanel; //Where to paint graphics MyScrollPane msp; Button executeButton; Button historyButton; TextArea userInputText; TextArea history; JFrame historyFrame; JTextField statusLine; MyStack theStack; // The data structure being demonstrated Font stackFont; int cellHeight = 20; // For drawing the stack. int cellWidth = 200; // How wide to plot pink rectangles int cellGap = 4; // vertical space between successive cells int topMargin = 25; // Space above top of stack. int fontSize = 16; // Height of font for displaying stack elemens. int leftMargin = 20; // x value for left side of cells int bottomMargin = 10; // Minimum space betw. bot. of visPanel and bot. of lowest cell. int leftOffset = 5; // space between left side of cell and contents string. int delay = 300; // default is to wait 300 ms between updates. Thread displayThread = null; public void init() { setSize(300,300); // default size of applet. visPanel = new ScrolledPanel(); visPanel.setPreferredSize(new Dimension(400,400)); msp = new MyScrollPane(visPanel); msp.setPreferredSize(new Dimension(400,200)); Container c = getContentPane(); c.setLayout(new BorderLayout()); c.add(msp, BorderLayout.CENTER); JPanel buttons = new JPanel(); buttons.setLayout(new FlowLayout()); JPanel controls = new JPanel(); controls.setLayout(new BorderLayout()); executeButton = new Button("Execute"); executeButton.addActionListener(this); buttons.add(executeButton); historyButton = new Button("History"); historyButton.addActionListener(this); buttons.add(historyButton); userInputText = new TextArea(";Enter commands here."); statusLine = new JTextField(); statusLine.setBackground(Color.lightGray); controls.add(buttons, BorderLayout.WEST); controls.add(userInputText, BorderLayout.CENTER); controls.add(statusLine, BorderLayout.SOUTH); controls.setPreferredSize(new Dimension(400,100)); c.add(controls, BorderLayout.SOUTH); c.validate(); theStack = new MyStack(); stackFont = new Font("Helvetica", Font.PLAIN, 20); history = new TextArea("VisStackApplet history:\n", 20, 40); } class ScrolledPanel extends JPanel { public void paintComponent(Graphics g) { super.paintComponent(g); paintStack(g); } } class MyScrollPane extends JScrollPane { MyScrollPane(JPanel p) { super(p, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); } } class MyStack extends Vector { int n; // number of elements in the stack int npushes; // number of PUSH operations so far. int npops; // number of POP operations so far. void init() { n = 0; npushes = 0; npops = 0; } void push(Object elt) { add(n, elt); n++; npushes++; } Object pop() { if (n == 0) { return null; } Object o = lastElement(); n--; npops++; remove(n); return o; } } public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("Execute")) { displayThread = new Thread(this); displayThread.start(); return; } if (e.getActionCommand().equals("History")) { if (historyFrame == null) { historyFrame = new JFrame("History of the VisStackApplet"); historyFrame.getContentPane().add(history); historyFrame.setSize(new Dimension(300,300)); } historyFrame.show(); System.out.println("Should have displayed the history window"); } } // The following is executed by a separate thread for the display. public void run() { String commands = userInputText.getText(); String line = ""; StringTokenizer lines; for (lines = new StringTokenizer(commands, "\n\r\f"); lines.hasMoreTokens();) { line = lines.nextToken(); process(line); } userInputText.setText(""); // Erase all the processed input. } // Helper function called by the run method above: void process(String command) { String arg = ""; StringTokenizer st = new StringTokenizer(command); if (! st.hasMoreTokens()) { return; } String firstToken = st.nextToken(); if (firstToken.startsWith(";")) { return; } history.appendText(command + "\n"); statusLine.setText(command); if (firstToken.equals("RESET")) { theStack = new MyStack(); updateDisplay(); return; } if (firstToken.equals("SIZE")) { String stats = "Current number of elements: " + theStack.n; statusLine.setText(stats); history.appendText("; " + stats + "\n"); return; } if (firstToken.equals("STATS")) { String stats = "npushes: " + theStack.npushes + "; npops: " + theStack.npops; statusLine.setText(stats); history.appendText("; " + stats + "\n"); return; } if (firstToken.equals("DELAY")) { if (st.hasMoreTokens()) { arg = st.nextToken(); try { delay =(new Integer(arg)).intValue(); } catch(NumberFormatException e) { delay = 0; } statusLine.setText("delay = " + delay); } history.appendText("; delay is now " + delay + "\n"); return; } if (firstToken.equals("PUSH")) { arg = "UNDEFINED ELEMENT"; if (st.hasMoreTokens()) { arg = st.nextToken(); } theStack.push(arg); checkScrolledPanelSize(); updateDisplay(); return; } if (firstToken.equals("POP")) { theStack.pop(); updateDisplay(); return; } history.appendText("[Unknown Stack command]\n"); statusLine.setText("Unknown Stack command: " + command); } // Here is a "middleman" method that updates the display waiting with // the current time delay after each repaint request. void updateDisplay() { visPanel.repaint(); if (delay > 0) { try { Thread.sleep(delay); } catch(InterruptedException e) {} } } // Here is the graphics method to actually draw the stack. // It's called by the ScrolledPanel paintComponent method. void paintStack(Graphics g) { g.setFont(stackFont); g.drawString( "Top of Stack", 10,20); int ystart = theStack.n * (cellHeight + cellGap) + topMargin; int ycentering = (cellHeight - fontSize) / 2; int ypos = ystart; for (Enumeration e = theStack.elements(); e.hasMoreElements();) { String elt = (String) e.nextElement(); g.setColor(Color.pink); g.fillRect(leftMargin, ypos, cellWidth, cellHeight); g.setColor(Color.black); g.drawString(elt, leftMargin + leftOffset, ypos+cellHeight - ycentering); ypos -= (cellHeight + cellGap); } } // The following computes the height of the display area needed by the current // stack, and if it won't fit in the scrolled panel, it enlarges the scrolled panel. // In the current implementation, the panel never gets smaller, even if the stack // becomes empty. This could easily be changed. void checkScrolledPanelSize() { int heightNeeded = topMargin + theStack.n * (cellHeight + cellGap) + cellHeight+ bottomMargin; Dimension d = visPanel.getPreferredSize(); int currentHeight = (int) d.getHeight(); int currentWidth = (int) d.getWidth(); if (heightNeeded > currentHeight) { visPanel.setPreferredSize(new Dimension(currentWidth, heightNeeded)); visPanel.revalidate(); // Adjust the vertical scroll bar. } } }