package jsetool.gui; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.io.*; import java.util.ArrayList; import java.util.Iterator; import javax.swing.*; /** * @author Mike Lawson * * @since Nov 2, 2004 * * The FSMDrawingPane class is a specialized drawing pane that understands how * to edit and draw FSM objects. * * */ public class FSMDrawingPane extends DrawingPane { /** * A simple inner class that calls out to the FSMDrawingPane when the user * presses the delete key. * * */ class DeleteSelectedAction extends AbstractAction { public DeleteSelectedAction() { super("Delete"); } /** * Called when the user presses the key associated with this action. * * @param e */ public void actionPerformed(ActionEvent e) { deleteSelectedObject(); } } /** * A simple inner class that calls out to the FSMDrawingPane when the user * presses the escape key. * */ class SetNormalOpsAction extends AbstractAction { /** * Called when the user presses the key associated with this action.s * * @param e */ public void actionPerformed(ActionEvent e) { drawingNormalOpsSelected(); } } /** * A simple inner class that tells the gui to make the currently selected * object the "initial state" * */ class SetSelectedAsInitialState extends AbstractAction { public SetSelectedAsInitialState() { super("Set as Initial"); } /** * Called when the user presses the key associated with this action.s * * @param e */ public void actionPerformed(ActionEvent e) { setCurrentlySelectedAsInitialState(); } } /** * Simple action for the popup menu. */ class AddStateAction extends AbstractAction { public AddStateAction() { super("State"); } public void actionPerformed(ActionEvent e) { drawingStateToolSelected(); } } /** * Simple action for the popup menu. */ class ConnectStatesAction extends AbstractAction { public ConnectStatesAction() { super("Connect"); } public void actionPerformed(ActionEvent e) { drawingEdgeToolSelected(); } } /** * Simple action for the popup menu. */ class EditObjectAction extends AbstractAction { public EditObjectAction() { super("Edit"); } public void actionPerformed(ActionEvent e) { editObject(); } } protected DeleteSelectedAction deleteSelectedAction = new DeleteSelectedAction(); protected SetSelectedAsInitialState setSelectedAsInitial = new SetSelectedAsInitialState(); protected AddStateAction addStateAction = new AddStateAction(); protected ConnectStatesAction connectStatesActions = new ConnectStatesAction(); protected EditObjectAction editAction = new EditObjectAction(); protected FSMDrawingState currentState; protected DrawingAnEdgeState drawingEdgeState; protected DrawingAStateObjectState drawingStateObject; protected NormalOperationsState normalOps; protected JPopupMenu popupMenu; protected transient Stroke showInitialStateStroke = new BasicStroke(4, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] { 5, 5 }, 0); private LabeledState initialState; /** * Constructs a new, empty, finite state machine drawing pane. * */ public FSMDrawingPane() { super(); initializeStateObjects(); initializeKeyStrokes(); initializePopupMenu(); } /** * Called when the user selects to draw an edge */ public void drawingEdgeToolSelected() { if (currentState.equals(drawingEdgeState)) { return; } else { currentState = drawingEdgeState; currentState.enter(); } } /** * Called when normal operations are resumed. * */ public void drawingNormalOpsSelected() { if (currentState.equals(normalOps)) { return; } else { currentState = normalOps; currentState.enter(); } } /** * Called when the user selects to draw a state */ public void drawingStateToolSelected() { if (currentState.equals(drawingStateObject)) { return; } else { currentState = drawingStateObject; currentState.enter(); } } /** * Called when the mouse is clicked on the drawing pane. * * @param e */ public void mouseClicked(MouseEvent e) { super.mouseClicked(e); try { currentState.mouseClicked(e); } catch (Exception e1) { showMessage(e1); } repaint(); } /** * Called when the mouse is dragged. * * @param e */ public void mouseDragged(MouseEvent e) { super.mouseDragged(e); try { currentState.mouseDragged(e); } catch (Exception e1) { showMessage(e1); } repaint(); } /** * Called when the mouse is pressed and held. * * @param e */ public void mousePressed(MouseEvent e) { super.mousePressed(e); try { currentState.mousePressed(e); } catch (Exception e1) { showMessage(e1); } repaint(); } /** * Called when the mouse has been released after a mouse pressed event. * * @param e */ public void mouseReleased(MouseEvent e) { super.mouseReleased(e); try { currentState.mouseReleased(e); } catch (Exception e1) { showMessage(e1); } repaint(); } /** * Extends the DrawingPane's paint method to decorate the initial state, if * selected. */ public void paint(Graphics g) { super.paint(g); if (initialState != null) { decorateInitialState(g); } } /** * Decorates the initial state so that it stands out on the display. * * @param g */ protected void decorateInitialState(Graphics g) { if (initialState == null) return; Graphics2D g2d = (Graphics2D) g.create(); Stroke foo = new BasicStroke(4, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] { 5, 5 }, 0); g2d.setStroke(foo); g2d.setPaint(Color.BLACK); g2d.drawRect((int) initialState.getX(), (int) initialState.getY(), (int) initialState.getWidth(), (int) initialState.getHeight()); } /** * Deletes the object that is currently selected. * */ protected void deleteSelectedObject() { if (getSelectedObject() == null) return; if (getSelectedObject() == initialState) { initialState = null; } // Tell the selected object to delete itself. getSelectedObject().objectIsBeingDeleted(); // Remove all objects that say they should be deleted. ArrayList toBeDeleted = new ArrayList(); for (int i = 0; i < getGraphicsComponents().size(); i++) { Drawable d = (Drawable) getGraphicsComponents().get(i); if (d.toBeDeleted() == true) { toBeDeleted.add(d); } } // for all known graphics components. for (int i = 0; i < toBeDeleted.size(); i++) { removeDrawingObject((Drawable) toBeDeleted.get(i)); } repaint(); } /** * Returns a popup menu for the display. * * @param e * @return JPopupManu */ protected JPopupMenu getPopupMenu(MouseEvent e) { addStateAction.setEnabled(false); connectStatesActions.setEnabled(true); // Let the user always select this. deleteSelectedAction.setEnabled(false); setSelectedAsInitial.setEnabled(false); editAction.setEnabled(false); Drawable drawingObject = getSelectedObject(); if (drawingObject != null) { deleteSelectedAction.setEnabled(true); editAction.setEnabled(true); if (drawingObject instanceof LabeledState) { setSelectedAsInitial.setEnabled(true); } } else { addStateAction.setEnabled(true); connectStatesActions.setEnabled(true); } return popupMenu; } /** * Initializes key strokes for the editor. */ protected void initializeKeyStrokes() { getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("DELETE"), "deleteSelected"); getActionMap().put("deleteSelected", deleteSelectedAction); // Setup the escape key Action escapeAction = new SetNormalOpsAction(); getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("ESCAPE"), "escapeAction"); getActionMap().put("escapeAction", escapeAction); } /** * Creates the popup menu for the desktop. */ protected void initializePopupMenu() { popupMenu = new JPopupMenu("JSETool"); popupMenu.add(addStateAction); popupMenu.add(connectStatesActions); popupMenu.add(editAction); popupMenu.add(deleteSelectedAction); popupMenu.add(setSelectedAsInitial); popupMenu.pack(); } /** * Initializes the state objects */ protected void initializeStateObjects() { drawingEdgeState = new DrawingAnEdgeState(this); normalOps = new NormalOperationsState(this); drawingStateObject = new DrawingAStateObjectState(this); currentState = normalOps; } /** * Captures what state the user has selected to be the initial state for the * FSM. * */ protected void setCurrentlySelectedAsInitialState() { initialState = (LabeledState) getSelectedObject(); } /** * Called when the user selects to edit the object * */ protected void editObject() { if (getSelectedObject() == null) return; getSelectedObject().showEditor(null); } /** * Shows a message stating an exception * * @param e1 * The exception to display */ protected void showMessage(Exception e1) { JOptionPane.showMessageDialog(this, e1.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } /** * @return */ public LabeledState getInitialState() { return initialState; } /** * Resets the explored state of all objects to false. * */ public void resetObjects() { for (Iterator iter = getGraphicsComponents().iterator(); iter.hasNext();) { Drawable d = (Drawable) iter.next(); d.setExplored(false); } } /** * Externalizable implementation. * @param in * @throws IOException * @throws ClassNotFoundException */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); initialState = (LabeledState) in.readObject(); int i = in.readInt(); drawingStateObject.setStateNumber(i); } /** * Externalizable implementation. * @param out * @throws IOException */ public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); out.writeObject(initialState); out.writeInt(drawingStateObject.getStateNumber()); } }