/* * @(#)DrawApplication.java * * Project: JHotdraw - a GUI framework for technical drawings * http://www.jhotdraw.org * http://jhotdraw.sourceforge.net * Copyright: ? by the original author(s) and all contributors * License: Lesser GNU Public License (LGPL) * http://www.opensource.org/licenses/lgpl-license.html */ package org.jhotdraw.application; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; import java.util.ListIterator; import javax.swing.*; import org.jhotdraw.contrib.*; import org.jhotdraw.figures.*; import org.jhotdraw.framework.*; import org.jhotdraw.standard.*; import org.jhotdraw.util.*; /** * DrawApplication defines a standard presentation for * standalone drawing editors. The presentation is * customized in subclasses. * The application is started as follows: *
 * public static void main(String[] args) {
 *     MayDrawApp window = new MyDrawApp();
 *     window.open();
 * }
 * 
* * @version <$CURRENT_VERSION$> */ public class DrawApplication extends JFrame implements DrawingEditor, PaletteListener, VersionRequester { private Tool fTool; private Iconkit fIconkit; private JTextField fStatusLine; private DrawingView fView; private ToolButton fDefaultToolButton; private ToolButton fSelectedToolButton; private String fApplicationName; private StorageFormatManager fStorageFormatManager; private UndoManager myUndoManager; protected static String fgUntitled = "untitled"; /** * List is not thread safe, but should not need to be. If it does we can * safely synchronize the few methods that use this by synchronizing on * the List object itself. */ private java.util.List listeners; private DesktopListener fDesktopListener; /** * This component acts as a desktop for the content. */ private Desktop fDesktop; // the image resource path private static final String fgDrawPath = "/CH/ifa/draw/"; public static final String IMAGES = fgDrawPath + "images/"; protected static int winCount = 0; /** * The index of the file menu in the menu bar. */ public static final int FILE_MENU = 0; /** * The index of the edit menu in the menu bar. */ public static final int EDIT_MENU = 1; /** * The index of the alignment menu in the menu bar. */ public static final int ALIGNMENT_MENU = 2; /** * The index of the attributes menu in the menu bar. */ public static final int ATTRIBUTES_MENU = 3; /** * Constructs a drawing window with a default title. */ public DrawApplication() { this("JHotDraw"); } /** * Constructs a drawing window with the given title. */ public DrawApplication(String title) { super(title); listeners = CollectionsFactory.current().createList(); setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); setApplicationName(title); } /** * Factory method which can be overriden by subclasses to * create an instance of their type. * * @return newly created application */ protected DrawApplication createApplication() { return new DrawApplication(); } /** * Open a new view for this application containing a * view of the drawing of the currently activated window. */ public void newView() { if (view() == null) { return; } DrawApplication window = createApplication(); window.open(view()); if (view().drawing().getTitle() != null ) { window.setDrawingTitle(view().drawing().getTitle() + " (View)"); } else { window.setDrawingTitle(getDefaultDrawingTitle() + " (View)"); } } /** * Open a new window for this application containing the passed in drawing, * or a new drawing if the passed in drawing is null. */ public void newWindow(Drawing initialDrawing) { DrawApplication window = createApplication(); if (initialDrawing == null) { window.open(); } else { window.open(window.createDrawingView(initialDrawing)); } } public final void newWindow() { newWindow(createDrawing()); } /** * Opens a new window */ public void open() { open(createInitialDrawingView()); } /** * Opens a new window with a drawing view. */ protected void open(final DrawingView newDrawingView) { getVersionControlStrategy().assertCompatibleVersion(); setUndoManager(new UndoManager()); setIconkit(createIconkit()); getContentPane().setLayout(new BorderLayout()); // status line must be created before a tool is set setStatusLine(createStatusLine()); getContentPane().add(getStatusLine(), BorderLayout.SOUTH); // create dummy tool until the default tool is activated during toolDone() setTool(new NullTool(this), ""); setView(newDrawingView); JToolBar tools = createToolPalette(); createTools(tools); JPanel activePanel = new JPanel(); activePanel.setAlignmentX(LEFT_ALIGNMENT); activePanel.setAlignmentY(TOP_ALIGNMENT); activePanel.setLayout(new BorderLayout()); activePanel.add(tools, BorderLayout.NORTH); setDesktopListener(createDesktopListener()); setDesktop(createDesktop()); activePanel.add((Component)getDesktop(), BorderLayout.CENTER); getContentPane().add(activePanel, BorderLayout.CENTER); JMenuBar mb = new JMenuBar(); createMenus(mb); setJMenuBar(mb); Dimension d = defaultSize(); if (d.width > mb.getPreferredSize().width) { setSize(d.width, d.height); } else { setSize(mb.getPreferredSize().width, d.height); } addListeners(); setStorageFormatManager(createStorageFormatManager()); //no work allowed to be done on GUI outside of AWT thread once //setVislble(true) must be called before drawing added to desktop, else //DND will fail. on drawing added before with a NPE. note however that //a nulldrawingView will not fail because it is never really added to the desltop setVisible(true); Runnable r = new Runnable() { public void run() { if (newDrawingView.isInteractive()) { getDesktop().addToDesktop(newDrawingView , Desktop.PRIMARY); } toolDone(); } }; if (java.awt.EventQueue.isDispatchThread() == false) { try { java.awt.EventQueue.invokeAndWait(r); } catch(java.lang.InterruptedException ie) { System.err.println(ie.getMessage()); exit(); } catch(java.lang.reflect.InvocationTargetException ite) { System.err.println(ite.getMessage()); exit(); } } else { r.run(); } toolDone(); } /** * Registers the listeners for this window */ protected void addListeners() { addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent event) { endApp(); } public void windowOpened(WindowEvent event) { winCount++; } public void windowClosed(WindowEvent event) { if (--winCount == 0) { System.exit(0); } } } ); } /** * Creates the standard menus. Clients override this * method to add additional menus. */ protected void createMenus(JMenuBar mb) { addMenuIfPossible(mb, createFileMenu()); addMenuIfPossible(mb, createEditMenu()); addMenuIfPossible(mb, createAlignmentMenu()); addMenuIfPossible(mb, createAttributesMenu()); addMenuIfPossible(mb, createDebugMenu()); } protected void addMenuIfPossible(JMenuBar mb, JMenu newMenu) { if (newMenu != null) { mb.add(newMenu); } } /** * Creates the file menu. Clients override this * method to add additional menu items. */ protected JMenu createFileMenu() { CommandMenu menu = new CommandMenu("File"); Command cmd = new AbstractCommand("New", this, false) { public void execute() { promptNew(); } }; menu.add(cmd, new MenuShortcut('n')); cmd = new AbstractCommand("Open...", this, false) { public void execute() { promptOpen(); } }; menu.add(cmd, new MenuShortcut('o')); cmd = new AbstractCommand("Save As...", this, true) { public void execute() { promptSaveAs(); } }; menu.add(cmd, new MenuShortcut('s')); menu.addSeparator(); cmd = new AbstractCommand("Print...", this, true) { public void execute() { print(); } }; menu.add(cmd, new MenuShortcut('p')); menu.addSeparator(); cmd = new AbstractCommand("Exit", this, true) { public void execute() { endApp(); } }; menu.add(cmd); return menu; } /** * Creates the edit menu. Clients override this * method to add additional menu items. */ protected JMenu createEditMenu() { CommandMenu menu = new CommandMenu("Edit"); menu.add(new UndoableCommand( new SelectAllCommand("Select All", this)), new MenuShortcut('a')); menu.addSeparator(); menu.add(new UndoableCommand( new CutCommand("Cut", this)), new MenuShortcut('x')); menu.add(new CopyCommand("Copy", this), new MenuShortcut('c')); menu.add(new UndoableCommand( new PasteCommand("Paste", this)), new MenuShortcut('v')); menu.addSeparator(); menu.add(new UndoableCommand( new DuplicateCommand("Duplicate", this)), new MenuShortcut('d')); menu.add(new UndoableCommand(new DeleteCommand("Delete", this))); menu.addSeparator(); menu.add(new UndoableCommand(new GroupCommand("Group", this))); menu.add(new UndoableCommand(new UngroupCommand("Ungroup", this))); menu.addSeparator(); menu.add(new UndoableCommand(new SendToBackCommand("Send to Back", this))); menu.add(new UndoableCommand(new BringToFrontCommand("Bring to Front", this))); menu.addSeparator(); menu.add(new UndoCommand("Undo Command", this)); menu.add(new RedoCommand("Redo Command", this)); return menu; } /** * Creates the alignment menu. Clients override this * method to add additional menu items. */ protected JMenu createAlignmentMenu() { CommandMenu menu = new CommandMenu("Align"); menu.addCheckItem(new ToggleGridCommand("Toggle Snap to Grid", this, new Point(4,4))); menu.addSeparator(); menu.add(new UndoableCommand( new AlignCommand(AlignCommand.Alignment.LEFTS, this))); menu.add(new UndoableCommand( new AlignCommand(AlignCommand.Alignment.CENTERS, this))); menu.add(new UndoableCommand( new AlignCommand(AlignCommand.Alignment.RIGHTS, this))); menu.addSeparator(); menu.add(new UndoableCommand( new AlignCommand(AlignCommand.Alignment.TOPS, this))); menu.add(new UndoableCommand( new AlignCommand(AlignCommand.Alignment.MIDDLES, this))); menu.add(new UndoableCommand( new AlignCommand(AlignCommand.Alignment.BOTTOMS, this))); return menu; } /** * Creates the debug menu. Clients override this * method to add additional menu items. */ protected JMenu createDebugMenu() { CommandMenu menu = new CommandMenu("Debug"); Command cmd = new AbstractCommand("Simple Update", this) { public void execute() { this.view().setDisplayUpdate(new SimpleUpdateStrategy()); } }; menu.add(cmd); cmd = new AbstractCommand("Buffered Update", this) { public void execute() { this.view().setDisplayUpdate(new BufferedUpdateStrategy()); } }; menu.add(cmd); return menu; } /** * Creates the attributes menu and its submenus. Clients override this * method to add additional menu items. */ protected JMenu createAttributesMenu() { JMenu menu = new JMenu("Attributes"); menu.add(createColorMenu("Fill Color", FigureAttributeConstant.FILL_COLOR)); menu.add(createColorMenu("Pen Color", FigureAttributeConstant.FRAME_COLOR)); menu.add(createArrowMenu()); menu.addSeparator(); menu.add(createFontMenu()); menu.add(createFontSizeMenu()); menu.add(createFontStyleMenu()); menu.add(createColorMenu("Text Color", FigureAttributeConstant.TEXT_COLOR)); return menu; } /** * Creates the color menu. */ protected JMenu createColorMenu(String title, FigureAttributeConstant attribute) { CommandMenu menu = new CommandMenu(title); for (int i=0; i