/* * Created on Apr 2, 2004, Modified Oct 8, 2004. * * VisualQueueApplet * 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 Queue. * 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. */ /** * @author Steve Tanimoto, Copyright, 2004. * */ import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class VisualQueueApplet 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; MyQueue theQueue; // The data structure being demonstrated Font QueueFont; int cellHeight = 50; // For drawing the Queue. int cellWidth = 200; // How wide to plot pink rectangles int cellGap = 4; // vertical space between successive cells int topMargin = 25; // Space above top of Queue. int fontSize = 16; // Height of font for displaying Queue 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(); theQueue = new MyQueue(); QueueFont = new Font("Helvetica", Font.PLAIN, 20); history = new TextArea("VisQueueApplet history:\n", 20, 40); } class ScrolledPanel extends JPanel { public void paintComponent(Graphics g) { super.paintComponent(g); paintQueue(g); } } class MyScrollPane extends JScrollPane { MyScrollPane(JPanel p) { super(p, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); } } class MyQueue extends Vector { int n; // number of elements in the Queue int nenqueues; // number of PUSH operations so far. int ndequeues; // number of POP operations so far. void init() { n = 0; nenqueues = 0; ndequeues = 0; } void enqueue(Object elt) { add(n, elt); n++; nenqueues++; } Object dequeue() { if (n == 0) { return null; } Object o = firstElement(); n--; ndequeues++; remove(0); 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 VisQueueApplet"); 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")) { theQueue = new MyQueue(); updateDisplay(); return; } if (firstToken.equals("SIZE")) { String stats = "Current number of elements: " + theQueue.n; statusLine.setText(stats); history.appendText("; " + stats + "\n"); return; } if (firstToken.equals("ZOOM")) { cellHeight *= 2; checkScrolledPanelSize(); updateDisplay(); return; } if (firstToken.equals("STATS")) { String stats = "npushes: " + theQueue.nenqueues + "; npops: " + theQueue.ndequeues; 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("ENQUEUE")) { arg = "UNDEFINED ELEMENT"; if (st.hasMoreTokens()) { arg = st.nextToken(); } theQueue.enqueue(arg); checkScrolledPanelSize(); updateDisplay(); return; } if (firstToken.equals("DEQUEUE")) { theQueue.dequeue(); updateDisplay(); return; } history.appendText("[Unknown Queue command]\n"); statusLine.setText("Unknown Queue 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 Queue. // It's called by the ScrolledPanel paintComponent method. void paintQueue(Graphics g) { g.setFont(QueueFont); g.drawString( "Top of Queue", 10,20); int ystart = theQueue.n * (cellHeight + cellGap) + topMargin; int ycentering = (cellHeight - fontSize) / 2; int ypos = ystart; for (Enumeration e = theQueue.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 // Queue, 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 Queue // becomes empty. This could easily be changed. void checkScrolledPanelSize() { int heightNeeded = topMargin + theQueue.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. } } }