/*
* 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.
}
}
}