/* * JFLAP - Formal Languages and Automata Package * * * Susan H. Rodger * Computer Science Department * Duke University * August 27, 2009 * Copyright (c) 2002-2009 * All rights reserved. * JFLAP is open source software. Please see the LICENSE for terms. * */ package gui.pumping; import javax.swing.*; import java.util.*; import java.awt.*; import java.awt.geom.*; /** * A Canvas is an area where strings, {@link gui.pumping.Text}, * are animated as {@link gui.pumping.MovingText} and will move * to construct the pumped string. * * @author Jinghui Lim * @see gui.pumping.PumpingLemmaInputPane * */ public class Canvas extends JPanel { /** * A list of labels. */ private ArrayList myLabelText; /** * A list of the initial text on the canvas. */ private ArrayList myInitialText; /** * A list of the text that should be animated. */ private ArrayList myMovingText; /** * A list of the text that has already moved (animated) to its final position. */ private ArrayList myFinalText; /** * A point marking the first row, where the initial text is painted. */ private static final Point2D.Double FIRST_ROW = new Point2D.Double(40, 40); /** * A point marking the second row, where the animated text is moved to. */ private static final Point2D.Double SECOND_ROW = new Point2D.Double(80, 80); /** * The minimum size of the animation area. */ private static final Dimension MIN_SIZE = new Dimension(300, 80); /** * A button that moves the animation forward one step. */ private JButton myStepButton; /** * A button that restarts the animation. */ private JButton myRestartButton; /** * A variable that tells us whether the animation should be running. */ private boolean wait; /** * Constructs a canvas. * */ public Canvas() { super(new BorderLayout()); myLabelText = new ArrayList(); myInitialText = new ArrayList(); myMovingText = new ArrayList(); myFinalText = new ArrayList(); wait = true; this.setMinimumSize(MIN_SIZE); this.setPreferredSize(MIN_SIZE); } /** * Sets the "enabled-ness" of the restart button. * * @param b "enabled-ness" of the restart button */ public void setRestartEnabled(boolean b) { myRestartButton.setEnabled(b); } /** * Starts the animation. This should be first called when the * decomposition of w is set. * */ public void start() { wait = false; this.getRootPane().repaint(); } /** * Halts the animation. * */ public void stop() { wait = true; } /** * Sets the step animation button of this canvas. * * @param b the button to set it to */ public void setStepButton(JButton b) { myStepButton = b; } /** * Sets the restart animation button of this canvas. * * @param b the button to set it to */ public void setRestartButton(JButton b) { myRestartButton = b; } /** * Resets the canvas for new input. * */ public void reset() { stop(); myLabelText.clear(); myInitialText.clear(); myMovingText.clear(); myFinalText.clear(); myStepButton.setEnabled(false); myRestartButton.setEnabled(false); } /** * Add a string to the initial text of this canvas that has a label. * * @param s string to add * @param label label of the string * @return the new Text object of the string just added */ public Text addText(String s, String label) { Text u = addText(s); myLabelText.add(Text.getLabel(this.getGraphics(), u, label)); return u; } /** * Add a string to the initial text of this canvas. * * @param s string to add * @return the new Text object of the string just added */ public Text addText(String s) { Point2D p; if(myInitialText.isEmpty()) { p = FIRST_ROW; } else { Text t = (Text)myInitialText.get(myInitialText.size() - 1); Point2D q = t.getPos(); p = new Point2D.Double(q.getX() + t.getWidth(this.getGraphics()) + Text.SPACE.getWidth(this.getGraphics()), q.getY()); } Text u = new Text(s, p); if(u.toString().length() == 0) u = new Text(Text.SPACE.toString(), p); myInitialText.add(u); return u; } /** * Creates a set of moves for the initial text to the final position. The * position of the final text calculated. Array n states how * many copies of each string, previously placed with {@link #addText(String)} * or {@link #addText(String, String)} the final text should be. The numbers * in the array should be the order that the text was added in. * * @param n the number of copies of final text */ public void moveText(int[] n) { Point2D p; double distance = 0; for(int i = 0; i < myInitialText.size(); i++) { Text s = (Text) myInitialText.get(i); for(int j = 0; j < n[i]; j++) { if(s.toString().length() == 0 || s.toString().equals(Text.SPACE.toString())) continue; if(myMovingText.isEmpty()) p = SECOND_ROW; else p = new Point2D.Double(SECOND_ROW.x + distance, SECOND_ROW.y); distance += s.getWidth(this.getGraphics()) + 2; // + 2 is a little extra space between words Text t = new MovingText(s, p); myMovingText.add(t); } } } /** * Paints all the text on the canvas and executes a move if the animation * has been started. * * @see #start() * @see #stop() */ public void paintComponent(Graphics pen) { for(int i = 0; i < myLabelText.size(); i++) { Text l = (Text)(myLabelText.get(i)); l.paint(pen); } for(int i = 0; i < myInitialText.size(); i++) { Text t = (Text)(myInitialText.get(i)); t.paint(pen); } for(int i = 0; i < myFinalText.size(); i++) { Text t = (Text)(myFinalText.get(i)); t.paint(pen); } if(wait) return; else myRestartButton.setEnabled(true); if(!myMovingText.isEmpty()) { MovingText m = (MovingText) myMovingText.get(0); if(m.move()) { myFinalText.add(m.finalText()); for(int i = 0; i < myFinalText.size(); i++) { Text t = (Text)(myFinalText.get(i)); t.paint(pen); } myMovingText.remove(0); if(myMovingText.isEmpty()) myStepButton.setEnabled(false); return; } m.paint(pen); } this.getRootPane().repaint(); } }