/* * @(#)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 CH.ifa.draw.application; import CH.ifa.draw.framework.*; import CH.ifa.draw.standard.*; import CH.ifa.draw.figures.*; import CH.ifa.draw.util.*; import javax.swing.*; import javax.swing.event.EventListenerList; import java.awt.*; import java.awt.event.*; import java.util.*; import java.io.*; /** * 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"; private final EventListenerList listenerList = new EventListenerList(); // 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); setApplicationName(title); winCount++; } /** * 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(createDrawingView(initialDrawing)); } } /** * Opens a new window */ public void open() { open(createInitialDrawingView()); } /** * Opens a new window with a drawing view. */ protected void open(DrawingView newDrawingView) { getVersionControlStrategy().assertCompatibleVersion(); setUndoManager(new UndoManager()); fIconkit = new Iconkit(this); getContentPane().setLayout(new BorderLayout()); // status line must be created before a tool is set fStatusLine = createStatusLine(); getContentPane().add(fStatusLine, BorderLayout.SOUTH); // create dummy tool until the default tool is activated during toolDone() setTool(new NullTool(this), ""); setView(newDrawingView); JComponent contents = createContents(view()); contents.setAlignmentX(LEFT_ALIGNMENT); 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); activePanel.add(contents, 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(); setVisible(true); fStorageFormatManager = createStorageFormatManager(); toolDone(); } /** * Registers the listeners for this window */ protected void addListeners() { addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent event) { exit(); } } ); } /** * 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() { exit(); } }; 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 executable() { this.view().setDisplayUpdate(new SimpleUpdateStrategy()); } }; menu.add(cmd); cmd = new AbstractCommand("Buffered Update", this) { public void executable() { 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", "FillColor")); menu.add(createColorMenu("Pen Color", "FrameColor")); menu.add(createArrowMenu()); menu.addSeparator(); menu.add(createFontMenu()); menu.add(createFontSizeMenu()); menu.add(createFontStyleMenu()); menu.add(createColorMenu("Text Color", "TextColor")); return menu; } /** * Creates the color menu. */ protected JMenu createColorMenu(String title, String attribute) { CommandMenu menu = new CommandMenu(title); for (int i=0; i=0 ; i-=2) { if (listeners[i] == ViewChangeListener.class) { vsl = (ViewChangeListener)listeners[i+1]; vsl.viewSelectionChanged(oldView, newView); } } } protected void fireViewCreatedEvent(DrawingView view) { final Object[] listeners = listenerList.getListenerList(); ViewChangeListener vsl = null; for (int i = listeners.length-2; i>=0 ; i-=2) { if (listeners[i] == ViewChangeListener.class) { vsl = (ViewChangeListener)listeners[i+1]; vsl.viewCreated(view); } } } protected void fireViewDestroyingEvent(DrawingView view) { final Object[] listeners = listenerList.getListenerList(); ViewChangeListener vsl = null; for (int i = listeners.length-2; i>=0 ; i-=2) { if (listeners[i] == ViewChangeListener.class) { vsl = (ViewChangeListener)listeners[i+1]; vsl.viewDestroying( view ); } } } /** * Shows a status message. * @see DrawingEditor */ public void showStatus(String string) { fStatusLine.setText(string); } public void setTool(Tool t, String name) { // SF bug-tracker id: #490665 // deactivate only those tools that have been activated before if ((tool() != null) && (tool().isActive())) { tool().deactivate(); } fTool = t; if (tool() != null) { showStatus(name); tool().activate(); } } private void setSelected(ToolButton button) { if (fSelectedToolButton != null) { fSelectedToolButton.reset(); } fSelectedToolButton = button; if (fSelectedToolButton != null) { fSelectedToolButton.select(); } } /** * Exits the application. You should never override this method */ public void exit() { destroy(); setVisible(false); // hide the JFrame dispose(); // tell windowing system to free resources winCount--; if (winCount == 0) { System.exit(0); } } /** * Handles additional clean up operations. Override to destroy * or release drawing editor resources. */ protected void destroy() { } /** * Resets the drawing to a new empty drawing. */ public void promptNew() { toolDone(); view().setDrawing(createDrawing()); view().drawing().setTitle(getDefaultDrawingTitle()); } /** * Shows a file dialog and opens a drawing. */ public void promptOpen() { toolDone(); JFileChooser openDialog = createOpenFileChooser(); getStorageFormatManager().registerFileFilters(openDialog); if (openDialog.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { StorageFormat foundFormat = getStorageFormatManager().findStorageFormat(openDialog.getFileFilter()); if (foundFormat != null) { loadDrawing(foundFormat, openDialog.getSelectedFile().getAbsolutePath()); } else { showStatus("Not a valid file format: " + openDialog.getFileFilter().getDescription()); } } } /** * Shows a file dialog and saves drawing. */ public void promptSaveAs() { toolDone(); JFileChooser saveDialog = createSaveFileChooser(); getStorageFormatManager().registerFileFilters(saveDialog); if (saveDialog.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) { StorageFormat foundFormat = getStorageFormatManager().findStorageFormat(saveDialog.getFileFilter()); if (foundFormat != null) { saveDrawing(foundFormat, saveDialog.getSelectedFile().getAbsolutePath()); } else { showStatus("Not a valid file format: " + saveDialog.getFileFilter().getDescription()); } } } /** * Create a file chooser for the open file dialog. Subclasses may override this * method in order to customize the open file dialog. */ protected JFileChooser createOpenFileChooser() { JFileChooser openDialog = new JFileChooser(); openDialog.setDialogTitle("Open File..."); return openDialog; } /** * Create a file chooser for the save file dialog. Subclasses may override this * method in order to customize the save file dialog. */ protected JFileChooser createSaveFileChooser() { JFileChooser saveDialog = new JFileChooser(); saveDialog.setDialogTitle("Save File..."); return saveDialog; } /** * Prints the drawing. */ public void print() { tool().deactivate(); PrintJob printJob = getToolkit().getPrintJob(this, "Print Drawing", null); if (printJob != null) { Graphics pg = printJob.getGraphics(); if (pg != null) { ((StandardDrawingView)view()).printAll(pg); pg.dispose(); // flush page } printJob.end(); } tool().activate(); } /** * Save a Drawing in a file */ protected void saveDrawing(StorageFormat storeFormat, String file) { // Need a better alert than this. if(view() == null) { return; } try { String name = storeFormat.store(file, view().drawing()); view().drawing().setTitle(name); setDrawingTitle(name); } catch (IOException e) { showStatus(e.toString()); } } /** * Load a Drawing from a file */ protected void loadDrawing(StorageFormat restoreFormat, String file) { try { Drawing restoredDrawing = restoreFormat.restore(file); if (restoredDrawing != null) { restoredDrawing.setTitle(file); newWindow(restoredDrawing); } else { showStatus("Unknown file type: could not open file '" + file + "'"); } } catch (IOException e) { showStatus("Error: " + e); } } /** * Switch to a new Look&Feel */ private void newLookAndFeel(String landf) { try { UIManager.setLookAndFeel(landf); SwingUtilities.updateComponentTreeUI(this); } catch (Exception e) { System.err.println(e); } } /** * Set the title of the currently selected drawing */ protected void setDrawingTitle(String drawingTitle) { if (getDefaultDrawingTitle().equals(drawingTitle)) { setTitle(getApplicationName()); } else { setTitle(getApplicationName() + " - " + drawingTitle); } } /** * Return the title of the currently selected drawing */ protected String getDrawingTitle() { return view().drawing().getTitle(); } /** * Set the name of the application build from this skeleton application */ public void setApplicationName(String applicationName) { fApplicationName = applicationName; } /** * Return the name of the application build from this skeleton application */ public String getApplicationName() { return fApplicationName; } protected void setUndoManager(UndoManager newUndoManager) { myUndoManager = newUndoManager; } public UndoManager getUndoManager() { return myUndoManager; } protected VersionControlStrategy getVersionControlStrategy() { return new StandardVersionControlStrategy(this); } /** * Subclasses should override this method to specify to which versions of * JHotDraw they are compatible. A string array is returned so it is possible * to specify several version numbers of JHotDraw to which the application * is compatible with. * * @return all versions number of JHotDraw the application is compatible with. */ public String[] getRequiredVersions() { String[] requiredVersions = new String[1]; // return the version of the package we are in requiredVersions[0] = VersionManagement.getPackageVersion(DrawApplication.class.getPackage()); return requiredVersions; } protected String getDefaultDrawingTitle() { return fgUntitled; } }