0001: /*
0002: * $Id: JGraphpadPane.java,v 1.8 2007/07/28 09:41:33 gaudenz Exp $
0003: * Copyright (c) 2001-2005, Gaudenz Alder
0004: *
0005: * All rights reserved.
0006: *
0007: * See LICENSE file for license details. If you are unable to locate
0008: * this file please contact info (at) jgraph (dot) com.
0009: */
0010: package com.jgraph.pad.factory;
0011:
0012: import java.awt.BorderLayout;
0013: import java.awt.Component;
0014: import java.awt.Container;
0015: import java.awt.GridLayout;
0016: import java.awt.KeyboardFocusManager;
0017: import java.beans.PropertyChangeEvent;
0018: import java.beans.PropertyChangeListener;
0019: import java.lang.reflect.Method;
0020: import java.util.HashMap;
0021: import java.util.Hashtable;
0022: import java.util.Iterator;
0023: import java.util.Map;
0024:
0025: import javax.swing.ImageIcon;
0026: import javax.swing.JDesktopPane;
0027: import javax.swing.JFrame;
0028: import javax.swing.JInternalFrame;
0029: import javax.swing.JMenuBar;
0030: import javax.swing.JPanel;
0031: import javax.swing.JSplitPane;
0032: import javax.swing.JTabbedPane;
0033: import javax.swing.JToolBar;
0034: import javax.swing.SwingUtilities;
0035: import javax.swing.event.InternalFrameAdapter;
0036: import javax.swing.event.InternalFrameEvent;
0037: import javax.swing.event.TreeModelEvent;
0038: import javax.swing.event.TreeModelListener;
0039: import javax.swing.tree.TreeModel;
0040: import javax.swing.tree.TreeNode;
0041:
0042: import org.jgraph.JGraph;
0043: import org.w3c.dom.Node;
0044:
0045: import com.jgraph.JGraphEditor;
0046: import com.jgraph.JGraphpad;
0047: import com.jgraph.editor.JGraphEditorAction;
0048: import com.jgraph.editor.JGraphEditorDiagram;
0049: import com.jgraph.editor.JGraphEditorFactory;
0050: import com.jgraph.editor.JGraphEditorFile;
0051: import com.jgraph.editor.JGraphEditorModel;
0052: import com.jgraph.editor.JGraphEditorResources;
0053: import com.jgraph.editor.JGraphEditorSettings;
0054: import com.jgraph.editor.factory.JGraphEditorDiagramPane;
0055: import com.jgraph.editor.factory.JGraphEditorFactoryMethod;
0056: import com.jgraph.editor.factory.JGraphEditorNavigator;
0057: import com.jgraph.editor.factory.JGraphEditorToolbox;
0058: import com.jgraph.pad.JGraphpadFile;
0059: import com.jgraph.pad.JGraphpadLibrary;
0060: import com.jgraph.pad.action.JGraphpadFileAction;
0061: import com.jgraph.pad.util.JGraphpadFocusManager;
0062: import com.jgraph.pad.util.JGraphpadImageIcon;
0063: import com.jgraph.pad.util.JGraphpadMouseAdapter;
0064: import com.jgraph.pad.util.JGraphpadTreeModelAdapter;
0065:
0066: /**
0067: * Main application panel consisting of a menubar and toolbar, two tabbed panes,
0068: * one on the left and one on the bottom, a desktop pane in the center, and a
0069: * status bar.
0070: */
0071: public class JGraphpadPane extends JPanel {
0072:
0073: /**
0074: * Nodename to be used for the desktop popup menu configuration.
0075: */
0076: public static String NODENAME_DESKTOPPOPUPMENU = "desktoppopupmenu";
0077:
0078: /**
0079: * Nodename to be used for the desktop popup menu configuration.
0080: */
0081: public static String NODENAME_INTERNALFRAMEPOPUPMENU = "internalframepopupmenu";
0082:
0083: /**
0084: * Defines the key used to identify the navigator split divider location.
0085: */
0086: public static String KEY_DESKTOPPANE = "desktopPane";
0087:
0088: /**
0089: * Defines the key used to identify the left split divider location.
0090: */
0091: public static String KEY_LEFTSPLIT = "leftSplit";
0092:
0093: /**
0094: * Defines the key used to identify the right split divider location.
0095: * setting.
0096: */
0097: public static String KEY_RIGHTSPLIT = "rightSplit";
0098:
0099: /**
0100: * Defines the key used to identify the right split divider location.
0101: * setting.
0102: */
0103: public static String KEY_NAVIGATORSPLIT = "navigatorSplit";
0104:
0105: /**
0106: * Holds the desktop pane.
0107: */
0108: protected JDesktopPane desktopPane = new JDesktopPane();
0109:
0110: /**
0111: * Maps from diagrams to internal frames.
0112: */
0113: protected Map internalFrames = new HashMap();
0114:
0115: /**
0116: * References the enclosing editor.
0117: */
0118: protected JGraphEditor editor;
0119:
0120: /**
0121: * Constructs a new editor pane for the specified enclosing editor.
0122: *
0123: * @param editor
0124: * The editor that contains the pane.
0125: */
0126: public JGraphpadPane(JGraphEditor editor) {
0127: setLayout(new BorderLayout());
0128: this .editor = editor;
0129: desktopPane.addMouseListener(new JGraphpadMouseAdapter(editor,
0130: NODENAME_DESKTOPPOPUPMENU));
0131:
0132: // Stores a reference to the desktop pane in the editor
0133: // settings for later wiring by the createApplication code.
0134: editor.getSettings().putObject(KEY_DESKTOPPANE, desktopPane);
0135:
0136: // Creates the left and bottom tab components using the respective
0137: // factory methods. This way, plugins can add new tabs to the main
0138: // window by replacing/overriding these factory methods.
0139: Component bottomTab = editor.getFactory().executeMethod(
0140: BottomTabFactoryMethod.NAME);
0141:
0142: // Constructs the split panes and registers them for restoring
0143: // the divider locations. The restoring of the locations
0144: // is only possible when the split panes are visible.
0145: JSplitPane rightSplit = editor.getFactory().createSplitPane(
0146: desktopPane, bottomTab, JSplitPane.VERTICAL_SPLIT);
0147: rightSplit.setOneTouchExpandable(true);
0148: rightSplit.setResizeWeight(1.0);
0149: editor.getSettings().putObject(KEY_RIGHTSPLIT, rightSplit);
0150:
0151: // Constructs the left split pane and adds the left tab created
0152: // with the factory method and the right (inner) split with
0153: // the bottom tab and desktop pane.
0154: if (JGraphpad.INNER_LIBRARIES) {
0155: add(rightSplit, BorderLayout.CENTER);
0156: } else {
0157: Component leftTab = editor.getFactory().executeMethod(
0158: LeftTabFactoryMethod.NAME);
0159: JSplitPane leftSplit = editor.getFactory().createSplitPane(
0160: leftTab, rightSplit, JSplitPane.HORIZONTAL_SPLIT);
0161: leftSplit.setOneTouchExpandable(true);
0162: editor.getSettings().putObject(KEY_LEFTSPLIT, leftSplit);
0163: add(leftSplit, BorderLayout.CENTER);
0164: }
0165:
0166: // Adds a shutdown hook to the settings to store the divider
0167: // locations when the program terminates.
0168: editor.getSettings().addShutdownHook(
0169: new JGraphEditorSettings.ShutdownHook() {
0170:
0171: // Takes the window bounds and stores the into the in-core
0172: // user configuration, which is later saved to disk.
0173: public void shutdown() {
0174: JGraphpadPane.this .editor.getSettings()
0175: .storeSplitPane(
0176: JGraphpad.NAME_USERSETTINGS,
0177: KEY_LEFTSPLIT);
0178: JGraphpadPane.this .editor.getSettings()
0179: .storeSplitPane(
0180: JGraphpad.NAME_USERSETTINGS,
0181: KEY_RIGHTSPLIT);
0182: }
0183: });
0184: }
0185:
0186: /**
0187: * Adds an internal frame for the specified file to the desktop pane. The
0188: * {@link #getFileTitle(JGraphEditorFile)} method is used to determine the
0189: * title for the internal frame.
0190: *
0191: * @param file
0192: * The file to be added.
0193: */
0194: public void addFile(JGraphEditorFile file) {
0195: JInternalFrame internalFrame = (JInternalFrame) internalFrames
0196: .get(file);
0197:
0198: // Creates the internal frame for the file if the frame does not yet
0199: // exist. Else this method does nothing.
0200: if (internalFrame == null) {
0201: internalFrame = new JInternalFrame(getFileTitle(file),
0202: true, true, true, true);
0203:
0204: // Associates the internal frame with the file and
0205: // adds it to the desktop pane.
0206: internalFrames.put(file, internalFrame);
0207: desktopPane.add(internalFrame);
0208: internalFrame.setBounds(0, 0, 600, 280);
0209: internalFrame.setVisible(true);
0210: internalFrame.setFocusable(false);
0211: internalFrame.getContentPane()
0212: .setLayout(new BorderLayout());
0213:
0214: // Adds a tabbed pane to hold the diagrams in the file
0215: final JTabbedPane tabbedPane = editor.getFactory()
0216: .createTabbedPane(JTabbedPane.BOTTOM);
0217:
0218: // Adds the libraries and navigator into the document
0219: if (JGraphpad.INNER_LIBRARIES) {
0220: JTabbedPane leftTab = (JTabbedPane) editor.getFactory()
0221: .executeMethod(LeftTabFactoryMethod.NAME);
0222:
0223: final JGraphEditorNavigator navigator = (JGraphEditorNavigator) ((Container) leftTab
0224: .getComponent(0)).getComponent(0);
0225: final JInternalFrame frame = internalFrame;
0226:
0227: // Updates the current graph in the navigator based on the
0228: // global focus traversal.
0229: JGraphpadFocusManager focusedGraph = JGraphpadFocusManager
0230: .getCurrentGraphFocusManager();
0231: focusedGraph
0232: .addPropertyChangeListener(new PropertyChangeListener() {
0233: public void propertyChange(
0234: PropertyChangeEvent e) {
0235: String prop = e.getPropertyName();
0236: if (JGraphpadFocusManager.FOCUSED_GRAPH_PROPERTY
0237: .equals(prop)) {
0238:
0239: JGraph graph = (JGraph) e
0240: .getNewValue();
0241:
0242: if (frame.isAncestorOf(graph)) {
0243: navigator
0244: .setCurrentGraph((JGraph) e
0245: .getNewValue());
0246: }
0247: }
0248: }
0249: });
0250:
0251: JSplitPane leftSplit = editor.getFactory()
0252: .createSplitPane(leftTab, tabbedPane,
0253: JSplitPane.HORIZONTAL_SPLIT);
0254: leftSplit.setOneTouchExpandable(true);
0255:
0256: internalFrame.getContentPane().add(leftSplit,
0257: BorderLayout.CENTER);
0258:
0259: // Adds child libs
0260: JTabbedPane libraryTabs = (JTabbedPane) ((Container) leftTab
0261: .getComponent(0)).getComponent(1);
0262:
0263: int childCount = editor.getModel().getChildCount(file);
0264: for (int i = 0; i < childCount; i++) {
0265: Object child = editor.getModel().getChild(file, i);
0266: if (child instanceof JGraphpadLibrary) {
0267: JGraphpadLibrary library = (JGraphpadLibrary) child;
0268: final JGraphpadLibraryPane libraryPane = new JGraphpadLibraryPane(
0269: editor, library);
0270: Component pane = editor.getFactory()
0271: .createScrollPane(libraryPane);
0272: libraryTabs.addTab(library.toString(), pane);
0273: libraryTabs.setSelectedComponent(pane);
0274: }
0275: }
0276: } else {
0277: internalFrame.getContentPane().add(tabbedPane,
0278: BorderLayout.CENTER);
0279: }
0280:
0281: tabbedPane.addMouseListener(new JGraphpadMouseAdapter(
0282: editor, NODENAME_INTERNALFRAMEPOPUPMENU));
0283:
0284: // Installs a listener to react to clicks on the close icon.
0285: // The close action will be executed and the internal frame
0286: // will be removed if the document is removed from the model.
0287: internalFrame
0288: .addInternalFrameListener(new InternalFrameAdapter() {
0289: public void internalFrameClosing(
0290: InternalFrameEvent e) {
0291: JInternalFrame frame = e.getInternalFrame();
0292: frame
0293: .setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE);
0294: try {
0295: JGraphEditorAction closeAction = editor
0296: .getKit()
0297: .getAction(
0298: JGraphpadFileAction.NAME_CLOSE);
0299: if (closeAction != null) {
0300:
0301: // Requests the focus to the graph to make sure
0302: // the correct file is closed by the action. This
0303: // is because the action finds the current file
0304: // based on the focus.
0305: ((JGraphEditorDiagramPane) tabbedPane
0306: .getSelectedComponent())
0307: .getGraph().requestFocus();
0308: SwingUtilities
0309: .invokeLater(new Runnable() {
0310: public void run() {
0311: editor
0312: .getKit()
0313: .getAction(
0314: "close")
0315: .actionPerformed(
0316: null);
0317: }
0318: });
0319: }
0320: } catch (Exception ex) {
0321: // don't dispose
0322: }
0323: }
0324: });
0325: }
0326: }
0327:
0328: public JInternalFrame getInternalFrame(Object file) {
0329: return (JInternalFrame) internalFrames.get(file);
0330: }
0331:
0332: /**
0333: * Removes the internal frame for the specified file from the desktop pane.
0334: *
0335: * @param file
0336: * The file to be removed.
0337: */
0338: public void removeFile(JGraphEditorFile file) {
0339: JInternalFrame internalFrame = (JInternalFrame) internalFrames
0340: .remove(file);
0341: if (internalFrame != null) {
0342: desktopPane.remove(internalFrame);
0343: internalFrame.dispose();
0344: desktopPane.repaint();
0345: }
0346: }
0347:
0348: /**
0349: * Updates the internal frame title for the specified file. The
0350: * {@link #getFileTitle(JGraphEditorFile)} method is used to determine the
0351: * title for the internal frame.
0352: *
0353: * @param file
0354: * The file who's internal frame should be updated.
0355: */
0356: public void updateFileTitle(JGraphEditorFile file) {
0357: JInternalFrame internalFrame = (JInternalFrame) internalFrames
0358: .get(file);
0359: if (internalFrame != null)
0360: internalFrame.setTitle(getFileTitle(file));
0361: }
0362:
0363: protected JTabbedPane getDiagramContainer(JInternalFrame frame) {
0364: JTabbedPane tabbedPane = null;
0365: if (JGraphpad.INNER_LIBRARIES) {
0366: tabbedPane = (JTabbedPane) ((Container) frame
0367: .getContentPane().getComponent(0)).getComponent(1);
0368: } else {
0369: tabbedPane = (JTabbedPane) frame.getContentPane()
0370: .getComponent(0);
0371: }
0372: return tabbedPane;
0373: }
0374:
0375: /**
0376: * Creates a diagram pane using
0377: * {@link JGraphEditorFactory#createDiagramPane(JGraphEditorDiagram)},
0378: * configures it using
0379: * {@link #configureDiagramPane(JGraphEditorDiagramPane, JGraphEditorDiagram)}
0380: * and adds it the the tabbed pane of the internal frame for the parent
0381: * file. The {@link JGraphEditorDiagram#getName()} method is used to set the
0382: * tab's title.
0383: *
0384: * @param diagram
0385: * The diagram to be added.
0386: */
0387: public void addDiagram(JGraphEditorDiagram diagram) {
0388: JGraphEditorFile file = JGraphEditorModel
0389: .getParentFile(diagram);
0390: JInternalFrame internalFrame = (JInternalFrame) internalFrames
0391: .get(file);
0392: if (internalFrame != null) {
0393: final JGraphEditorDiagramPane diagramPane = editor
0394: .getFactory().createDiagramPane(diagram);
0395: configureDiagramPane(diagramPane, diagram);
0396:
0397: // Adds the new diagram pane to the tabbed pane of
0398: // the parent's internal frame and selects the new tab.
0399: JTabbedPane tabbedPane = getDiagramContainer(internalFrame);
0400: tabbedPane.addTab(diagram.getName(), diagramPane);
0401: tabbedPane.setSelectedComponent(diagramPane);
0402:
0403: // Transfers the focus to the new graph in the diagram pane
0404: // after the component hierarchy has been revalidated.
0405: SwingUtilities.invokeLater(new Runnable() {
0406: public void run() {
0407: diagramPane.getGraph().requestFocus();
0408: }
0409: });
0410: }
0411: }
0412:
0413: /**
0414: * Adds the specified library to the library pane.
0415: *
0416: * @param library
0417: * The library to be added.
0418: */
0419: public void addLibrary(JGraphpadLibrary library) {
0420: // TODO
0421: }
0422:
0423: /**
0424: * Configures the newly created diagram pane to reflect the properties of
0425: * the specified diagram. This also installs a listener to keep the
0426: * properties of the diagram up-to-date with the graph for the next creation
0427: * after persistence.
0428: *
0429: * @param diagramPane
0430: * The diagram pane to be configured.
0431: * @param diagram
0432: * The diagram to be configured.
0433: */
0434: protected void configureDiagramPane(
0435: JGraphEditorDiagramPane diagramPane,
0436: final JGraphEditorDiagram diagram) {
0437:
0438: // Listens to JGraph properties
0439: diagramPane.getGraph().addPropertyChangeListener(
0440: new PropertyChangeListener() {
0441:
0442: /*
0443: * (non-Javadoc)
0444: */
0445: public void propertyChange(PropertyChangeEvent event) {
0446:
0447: // Checks if it's an interesting property and stores
0448: // the interesting ones back in the diagram.
0449: String name = event.getPropertyName();
0450: if (name.equals(JGraph.ANTIALIASED_PROPERTY)
0451: || name
0452: .equals(JGraph.EDITABLE_PROPERTY)
0453: || name
0454: .equals(JGraph.GRID_COLOR_PROPERTY)
0455: || name
0456: .equals(JGraph.GRID_SIZE_PROPERTY)
0457: || name
0458: .equals(JGraph.GRID_VISIBLE_PROPERTY)
0459: || name
0460: .equals(JGraph.HANDLE_COLOR_PROPERTY)
0461: || name
0462: .equals(JGraph.HANDLE_SIZE_PROPERTY)
0463: || name
0464: .equals(JGraph.LOCKED_HANDLE_COLOR_PROPERTY)
0465: || name
0466: .equals(JGraph.PORTS_VISIBLE_PROPERTY)
0467: || name
0468: .equals(JGraph.PORTS_SCALED_PROPERTY)
0469: || name.equals(JGraph.SCALE_PROPERTY))
0470: if (event.getNewValue() == null)
0471: diagram.getProperties().remove(
0472: "graph." + name);
0473: else
0474: diagram.getProperties().put(
0475: "graph." + name,
0476: event.getNewValue());
0477: }
0478: });
0479:
0480: // Listens to diagram pane properties
0481: diagramPane
0482: .addPropertyChangeListener(new PropertyChangeListener() {
0483:
0484: /*
0485: * (non-Javadoc)
0486: */
0487: public void propertyChange(PropertyChangeEvent event) {
0488:
0489: // Checks if it's an interesting property and stores
0490: // the interesting ones back in the diagram.
0491: String name = event.getPropertyName();
0492: if (name
0493: .equals(JGraphEditorDiagramPane.PROPERTY_AUTOSCALEPOLICY)
0494: || name
0495: .equals(JGraphEditorDiagramPane.PROPERTY_BACKGROUNDIMAGE)
0496: || name
0497: .equals(JGraphEditorDiagramPane.PROPERTY_METRIC)
0498: || name
0499: .equals(JGraphEditorDiagramPane.PROPERTY_PAGEFORMAT)
0500: || name
0501: .equals(JGraphEditorDiagramPane.PROPERTY_PAGESCALE)
0502: || name
0503: .equals(JGraphEditorDiagramPane.PROPERTY_PAGEVISIBLE)
0504: || name
0505: .equals(JGraphEditorDiagramPane.PROPERTY_RULERSVISIBLE))
0506: if (event.getNewValue() == null)
0507: diagram.getProperties().remove(
0508: "diagramPane." + name);
0509: else
0510: diagram.getProperties().put(
0511: "diagramPane." + name,
0512: event.getNewValue());
0513: }
0514: });
0515:
0516: // Tries to set all current properties from the diagram to the graph.
0517: // Note: A hashtable contains the object to resolve the prefixes.
0518: Map objects = new Hashtable();
0519: objects.put("diagramPane", diagramPane);
0520: objects.put("graph", diagramPane.getGraph());
0521:
0522: Iterator it = diagram.getProperties().entrySet().iterator();
0523: while (it.hasNext()) {
0524: Map.Entry entry = (Map.Entry) it.next();
0525: setProperty(objects, String.valueOf(entry.getKey()), entry
0526: .getValue());
0527: }
0528: }
0529:
0530: /**
0531: * Utility method to set the property of an object using reflection.
0532: *
0533: * @param objects
0534: * Maps from prefixes to objects.
0535: * @param property
0536: * The name of the property to be changed.
0537: * @param value
0538: * The value of the property to be set.
0539: */
0540: public static void setProperty(Map objects, String property,
0541: Object value) {
0542: // Analyze prefix
0543: int delim = property.indexOf('.');
0544: if (delim > 0) {
0545: String prefix = property.substring(0, delim);
0546: property = property.substring(delim + 1);
0547: Object obj = objects.get(prefix);
0548: if (obj != null) {
0549: // Does type conversion to basic types
0550: Class clazz = value.getClass();
0551: if (clazz == Boolean.class)
0552: clazz = boolean.class;
0553: else if (clazz == Integer.class)
0554: clazz = int.class;
0555: else if (clazz == Long.class)
0556: clazz = long.class;
0557: else if (clazz == Float.class)
0558: clazz = float.class;
0559: else if (clazz == Double.class)
0560: clazz = double.class;
0561: else if (clazz == JGraphpadImageIcon.class)
0562: clazz = ImageIcon.class;
0563: String name = String.valueOf(property);
0564: name = name.substring(0, 1).toUpperCase()
0565: + name.substring(1);
0566: try {
0567: Method setter = obj.getClass().getMethod(
0568: "set" + name, new Class[] { clazz });
0569: setter.invoke(obj, new Object[] { value });
0570: } catch (Exception e) {
0571: // ignore
0572: }
0573: }
0574: }
0575: }
0576:
0577: /**
0578: * Removes the diagram pane for the specified diagram from the tabbed pane
0579: * in the previous parent's internal frame.
0580: *
0581: * @param previousParent
0582: * The previous parent file of the removed diagram.
0583: * @param diagram
0584: * The diagram to be removed.
0585: */
0586: public void removeDiagram(JGraphEditorFile previousParent,
0587: JGraphEditorDiagram diagram) {
0588: JInternalFrame internalFrame = (JInternalFrame) internalFrames
0589: .get(previousParent);
0590: if (internalFrame != null) {
0591: JTabbedPane tabbedPane = getDiagramContainer(internalFrame);
0592:
0593: // Loops through all tabs of the tabbed pane to find the
0594: // correct tab. This assumes the diagram pane was added
0595: // as a tab directly, with no decorator components.
0596: for (int i = 0; i < tabbedPane.getTabCount(); i++) {
0597: Component tab = tabbedPane.getComponentAt(i);
0598: if (tab instanceof JGraphEditorDiagramPane) {
0599: JGraphEditorDiagramPane pane = (JGraphEditorDiagramPane) tab;
0600: if (pane.getDiagram() == diagram) {
0601: tabbedPane.removeTabAt(i);
0602: continue; // exit for
0603: }
0604: }
0605: }
0606: }
0607: }
0608:
0609: /**
0610: * Updates the tab title for the specified diagram in the tabbed pane of the
0611: * parent's internal frame. The {@link JGraphEditorDiagram#getName()} method
0612: * is used to set the tab's title.
0613: *
0614: * @param diagram
0615: * The diagram who's tab should be updated.
0616: */
0617: public void updateDiagramTitle(JGraphEditorDiagram diagram) {
0618: JGraphEditorFile file = JGraphEditorModel
0619: .getParentFile(diagram);
0620: JInternalFrame internalFrame = (JInternalFrame) internalFrames
0621: .get(file);
0622: if (internalFrame != null) {
0623: JTabbedPane tabbedPane = getDiagramContainer(internalFrame);
0624:
0625: // Loops through all tabs of the tabbed pane to find the
0626: // correct tab. This assumes the diagram pane was added
0627: // as a tab directly, with no decorator components.
0628: for (int i = 0; i < tabbedPane.getTabCount(); i++) {
0629: Component tab = tabbedPane.getComponentAt(i);
0630: if (tab instanceof JGraphEditorDiagramPane) {
0631: JGraphEditorDiagramPane pane = (JGraphEditorDiagramPane) tab;
0632: if (pane.getDiagram() == diagram) {
0633: tabbedPane.setTitleAt(i, diagram.getName());
0634: continue; // exit for
0635: }
0636: }
0637: }
0638: }
0639: }
0640:
0641: /**
0642: * Hook for subclassers to return the internal frame title for a file. This
0643: * implementation returns <code>String.valueOf(file)</code> appending an
0644: * asterisk (*) if {@link JGraphEditorFile#isModified()} returns true.
0645: *
0646: * @param file
0647: * The file to return the title for.
0648: * @return Return the title for <code>file</code>.
0649: */
0650: protected String getFileTitle(JGraphEditorFile file) {
0651: String title = String.valueOf(file);
0652: if (file.isModified())
0653: title += " *";
0654: return title;
0655: }
0656:
0657: /**
0658: * Utility class to establish a listener in a editor's document model and
0659: * update an editor pane.
0660: */
0661: protected static class DocumentTracker extends
0662: JGraphpadTreeModelAdapter {
0663:
0664: /**
0665: * References the editor pane to be updated.
0666: */
0667: protected JGraphpadPane pane;
0668:
0669: /**
0670: * Constructs a new diagram tracker for updating the specified pane. The
0671: * diagram tracker must be added as a tree model listener to an editor's
0672: * document model.
0673: *
0674: * @param pane
0675: * The pane to be updated on document model changes.
0676: */
0677: public DocumentTracker(JGraphpadPane pane) {
0678: this .pane = pane;
0679: }
0680:
0681: /**
0682: * Calls {@link #treeNodeInserted(TreeModel, Object)} with the last path
0683: * component of {@link TreeModelEvent#getTreePath()} as the root of the
0684: * resursion.
0685: *
0686: * @param arg0
0687: * The object that describes the event.
0688: */
0689: public void treeNodesInserted(TreeModelEvent arg0) {
0690: TreeModel source = (TreeModel) arg0.getSource();
0691: Object[] children = arg0.getChildren();
0692: for (int i = 0; i < children.length; i++)
0693: treeNodeInserted(source, children[i]);
0694: }
0695:
0696: /**
0697: * Calls {@link JGraphpadPane#addFile(JGraphEditorFile)} or
0698: * {@link JGraphpadPane#addDiagram(JGraphEditorDiagram)} recursively on
0699: * all inserted nodes of the respective type.
0700: *
0701: * @param source
0702: * The source tree model.
0703: * @param object
0704: * The node that has been inserted.
0705: */
0706: public void treeNodeInserted(TreeModel source, Object object) {
0707: if (object instanceof JGraphEditorDiagram)
0708: pane.addDiagram((JGraphEditorDiagram) object);
0709: else if (object instanceof JGraphpadFile)
0710: pane.addFile((JGraphpadFile) object);
0711:
0712: // Inserts all children recursively
0713: if (object != null) {
0714: int count = source.getChildCount(object);
0715: for (int i = 0; i < count; i++)
0716: treeNodeInserted(source, source.getChild(object, i));
0717: }
0718: }
0719:
0720: /**
0721: * Calls {@link JGraphpadPane#updateFileTitle(JGraphEditorFile)} or
0722: * {@link JGraphpadPane#updateDiagramTitle(JGraphEditorDiagram)} on all
0723: * changed nodes of the respective type.
0724: *
0725: * @param event
0726: * The object that describes the event.
0727: */
0728: public void treeNodesChanged(TreeModelEvent event) {
0729: Object[] children = event.getChildren();
0730: for (int i = 0; i < children.length; i++) {
0731: if (children[i] instanceof JGraphEditorDiagram)
0732: pane
0733: .updateDiagramTitle((JGraphEditorDiagram) children[i]);
0734: else if (children[i] instanceof JGraphpadFile)
0735: pane.updateFileTitle((JGraphpadFile) children[i]);
0736: }
0737: }
0738:
0739: /**
0740: * Calls {@link JGraphpadPane#removeFile(JGraphEditorFile)} or
0741: * {@link JGraphpadPane#removeDiagram(JGraphEditorFile, JGraphEditorDiagram)}
0742: * on all removed nodes of the respective type.
0743: *
0744: * @param arg0
0745: * The object that describes the event.
0746: */
0747: public void treeNodesRemoved(TreeModelEvent arg0) {
0748: Object[] children = arg0.getChildren();
0749: for (int i = 0; i < children.length; i++) {
0750: if (children[i] instanceof JGraphEditorFile) {
0751: pane.removeFile((JGraphEditorFile) children[i]);
0752: } else if (children[i] instanceof JGraphEditorDiagram
0753: && arg0.getTreePath().getLastPathComponent() instanceof TreeNode) {
0754: pane.removeDiagram(JGraphEditorModel
0755: .getParentFile((TreeNode) arg0
0756: .getTreePath()
0757: .getLastPathComponent()),
0758: (JGraphEditorDiagram) children[i]);
0759: }
0760: }
0761: }
0762:
0763: }
0764:
0765: /**
0766: * Provides a factory method to construct an editor pane.
0767: */
0768: public static class FactoryMethod extends JGraphEditorFactoryMethod {
0769:
0770: /**
0771: * Defines the default name for factory methods of this kind.
0772: */
0773: public static String NAME = "createFrame";
0774:
0775: /**
0776: * Node name used for menu configurations.
0777: */
0778: public static final String NODENAME_MENUBAR = "menubar";
0779:
0780: /**
0781: * Node name used for toolbar configurations.
0782: */
0783: public static final String NODENAME_TOOLBAR = "toolbar";
0784:
0785: /**
0786: * Node name used for toolbox configurations.
0787: */
0788: public static final String NODENAME_TOOLBOX = "toolbox";
0789:
0790: /**
0791: * References the enclosing editor.
0792: */
0793: protected JGraphEditor editor;
0794:
0795: /**
0796: * Constructs a new factory method for the specified enclosing editor
0797: * using {@link #NAME}.
0798: *
0799: * @param editor
0800: * The editor that contains the factory method.
0801: */
0802: public FactoryMethod(JGraphEditor editor) {
0803: super (NAME);
0804: this .editor = editor;
0805: }
0806:
0807: /*
0808: * (non-Javadoc)
0809: */
0810: public Component createInstance(Node configuration) {
0811: final JFrame frame = new JFrame();
0812: frame.setIconImage(JGraphEditorResources.getImage(
0813: JGraphEditorResources.getString("logo.icon"))
0814: .getImage());
0815: frame.getContentPane().setLayout(new BorderLayout());
0816:
0817: // Fetches the menu bar configuration and constructs
0818: // the menubar for the frame.
0819: Node menuBarConfiguration = JGraphEditorSettings
0820: .getNodeByName(configuration.getChildNodes(),
0821: NODENAME_MENUBAR);
0822: frame.setJMenuBar((JMenuBar) editor.getFactory()
0823: .createMenuBar(menuBarConfiguration));
0824:
0825: // Creates container for multiple toolbars and
0826: // adds it to the main window.
0827: JPanel toolbars = new JPanel(new GridLayout(2, 1));
0828: frame.getContentPane().add(toolbars, BorderLayout.NORTH);
0829:
0830: // Fetches the toolbar configuration and create the toolbar
0831: Node toolbarConfiguration = JGraphEditorSettings
0832: .getNodeByName(configuration.getChildNodes(),
0833: NODENAME_TOOLBAR);
0834: JToolBar toolbar = editor.getFactory().createToolBar(
0835: toolbarConfiguration);
0836: toolbars.add(toolbar);
0837:
0838: // Fetches the toolbox configuration and creates the toolbox
0839: Node toolboxConfiguration = JGraphEditorSettings
0840: .getNodeByName(configuration.getChildNodes(),
0841: NODENAME_TOOLBOX);
0842: final JGraphEditorToolbox toolbox = editor.getFactory()
0843: .createToolbox(toolboxConfiguration);
0844: toolbars.add(toolbox);
0845:
0846: // Creates and adds the editor pane and adds a diagram tracker to
0847: // listen to the model and update the internal frames and tabs in
0848: // the editor pane.
0849: final JGraphpadPane editorPane = new JGraphpadPane(editor);
0850: frame.getContentPane().add(editorPane, BorderLayout.CENTER);
0851: editor.getModel().addTreeModelListener(
0852: new DocumentTracker(editorPane));
0853:
0854: // Adds the status bar using its factory method
0855: Component statusBar = editor.getFactory().executeMethod(
0856: JGraphpadStatusBar.FactoryMethod.NAME);
0857: if (statusBar != null)
0858: frame.getContentPane().add(statusBar,
0859: BorderLayout.SOUTH);
0860:
0861: // Updates the frame title on focus traversal and various
0862: // other changes (selection, model, cache, properties...)
0863: // and keeps the installed graph in the toolbox up-to-date.
0864: JGraphpadFocusManager focusedGraph = JGraphpadFocusManager
0865: .getCurrentGraphFocusManager();
0866: focusedGraph
0867: .addPropertyChangeListener(new PropertyChangeListener() {
0868: public void propertyChange(PropertyChangeEvent e) {
0869: frame.setTitle(getWindowTitle(editorPane));
0870:
0871: // Updates the installed graph in the toolbox
0872: String prop = e.getPropertyName();
0873: if (prop
0874: .equals(JGraphpadFocusManager.FOCUSED_GRAPH_PROPERTY)
0875: && e.getNewValue() instanceof JGraph) {
0876: toolbox.setGraph((JGraph) e
0877: .getNewValue());
0878: }
0879: }
0880: });
0881:
0882: // Additionally updates the window title on all changes
0883: // to the global focus owner.
0884: KeyboardFocusManager focusManager = KeyboardFocusManager
0885: .getCurrentKeyboardFocusManager();
0886: focusManager
0887: .addPropertyChangeListener(new PropertyChangeListener() {
0888:
0889: /*
0890: * (non-Javadoc)
0891: */
0892: public void propertyChange(PropertyChangeEvent e) {
0893: String prop = e.getPropertyName();
0894: if (prop.equals("permanentFocusOwner"))
0895: frame
0896: .setTitle(getWindowTitle(editorPane));
0897: }
0898: });
0899:
0900: // On an any document model changes
0901: editor.getModel().addTreeModelListener(
0902: new TreeModelListener() {
0903:
0904: /*
0905: * (non-Javadoc)
0906: */
0907: public void treeNodesChanged(TreeModelEvent e) {
0908: frame.setTitle(getWindowTitle(editorPane));
0909: }
0910:
0911: /*
0912: * (non-Javadoc)
0913: */
0914: public void treeNodesInserted(TreeModelEvent e) {
0915: frame.setTitle(getWindowTitle(editorPane));
0916: }
0917:
0918: /*
0919: * (non-Javadoc)
0920: */
0921: public void treeNodesRemoved(TreeModelEvent e) {
0922: frame.setTitle(getWindowTitle(editorPane));
0923: }
0924:
0925: /*
0926: * (non-Javadoc)
0927: */
0928: public void treeStructureChanged(
0929: TreeModelEvent e) {
0930: frame.setTitle(getWindowTitle(editorPane));
0931: }
0932:
0933: });
0934:
0935: return frame;
0936: }
0937:
0938: /**
0939: * Hook for subclassers to return the window title for the specified
0940: * editor pane. This implementation appens the parent file title for the
0941: * focused diagram or library using
0942: * {@link JGraphpadFileAction#getPermanentFocusOwnerFile()} and
0943: * {@link JGraphpadPane#getFileTitle(JGraphEditorFile)} to
0944: * {@link JGraphpad#APPTITLE}.
0945: *
0946: * @param pane
0947: * The editor pane to create the title for.
0948: * @return Returns the window title.
0949: */
0950: protected String getWindowTitle(JGraphpadPane pane) {
0951: JGraphEditorFile file = JGraphpadFileAction
0952: .getPermanentFocusOwnerFile();
0953: String title = "";
0954: if (file != null)
0955: title = " - " + pane.getFileTitle(file);
0956: return JGraphpad.APPTITLE + title;
0957: }
0958:
0959: }
0960:
0961: /**
0962: * Provides a factory method to construct the left tab of an editor pane.
0963: */
0964: public static class LeftTabFactoryMethod extends
0965: JGraphEditorFactoryMethod {
0966:
0967: /**
0968: * Defines the default name for factory methods of this kind.
0969: */
0970: public static String NAME = "createLeftTab";
0971:
0972: /**
0973: * References the enclosing editor.
0974: */
0975: protected JGraphEditor editor;
0976:
0977: /**
0978: * Constructs a new factory method for the specified enclosing editor
0979: * using {@link #NAME}.
0980: *
0981: * @param editor
0982: * The editor that contains the factory method.
0983: */
0984: public LeftTabFactoryMethod(JGraphEditor editor) {
0985: super (NAME);
0986: this .editor = editor;
0987: }
0988:
0989: /*
0990: * (non-Javadoc)
0991: */
0992: public Component createInstance(Node configuration) {
0993: JTabbedPane tabPane = editor.getFactory().createTabbedPane(
0994: JTabbedPane.TOP);
0995:
0996: final JGraphEditorNavigator navigator = (JGraphEditorNavigator) editor
0997: .getFactory().executeMethod(
0998: JGraphEditorNavigator.FactoryMethod.NAME);
0999:
1000: // Updates the current graph in the navigator based on the
1001: // global focus traversal.
1002: if (!JGraphpad.INNER_LIBRARIES) {
1003: JGraphpadFocusManager focusedGraph = JGraphpadFocusManager
1004: .getCurrentGraphFocusManager();
1005: focusedGraph
1006: .addPropertyChangeListener(new PropertyChangeListener() {
1007: public void propertyChange(
1008: PropertyChangeEvent e) {
1009: String prop = e.getPropertyName();
1010: if (JGraphpadFocusManager.FOCUSED_GRAPH_PROPERTY
1011: .equals(prop)) {
1012: navigator
1013: .setCurrentGraph((JGraph) e
1014: .getNewValue());
1015: }
1016: }
1017: });
1018: }
1019:
1020: // Executes the factory method to create the repository. The factory
1021: // method has been previously registered by the default factory.
1022: Component libraryPane = editor.getFactory().executeMethod(
1023: JGraphpadLibraryPane.FactoryMethod.NAME);
1024:
1025: JSplitPane navigatorSplit = editor.getFactory()
1026: .createSplitPane(navigator, libraryPane,
1027: JSplitPane.VERTICAL_SPLIT);
1028: editor.getSettings().putObject(KEY_NAVIGATORSPLIT,
1029: navigatorSplit);
1030:
1031: // Adds a shutdown hook to the settings to store the divider
1032: // locations when the program terminates.
1033: editor.getSettings().addShutdownHook(
1034: new JGraphEditorSettings.ShutdownHook() {
1035:
1036: // Takes the window bounds and stores the into the
1037: // in-core
1038: // user configuration, which is later saved to disk.
1039: public void shutdown() {
1040: editor.getSettings().storeSplitPane(
1041: JGraphpad.NAME_USERSETTINGS,
1042: KEY_NAVIGATORSPLIT);
1043: }
1044: });
1045:
1046: tabPane.addTab(
1047: JGraphEditorResources.getString("Navigator"),
1048: navigatorSplit);
1049:
1050: return tabPane;
1051: }
1052:
1053: }
1054:
1055: /**
1056: * Provides a factory method to construct the bottom tab of an editor pane.
1057: */
1058: public static class BottomTabFactoryMethod extends
1059: JGraphEditorFactoryMethod {
1060:
1061: /**
1062: * Defines the default name for factory methods of this kind.
1063: */
1064: public static String NAME = "createBottomTab";
1065:
1066: /**
1067: * References the enclosing editor.
1068: */
1069: protected JGraphEditor editor;
1070:
1071: /**
1072: * Constructs a new factory method for the specified enclosing editor
1073: * using {@link #NAME}.
1074: *
1075: * @param editor
1076: * The editor that contains the factory method.
1077: */
1078: public BottomTabFactoryMethod(JGraphEditor editor) {
1079: super (NAME);
1080: this .editor = editor;
1081: }
1082:
1083: /*
1084: * (non-Javadoc)
1085: */
1086: public Component createInstance(Node configuration) {
1087: JTabbedPane tabPane = editor.getFactory().createTabbedPane(
1088: JTabbedPane.TOP);
1089:
1090: // Adds a console tab at the bottom
1091: Component console = editor.getFactory().executeMethod(
1092: JGraphpadConsole.FactoryMethod.NAME);
1093: if (console != null)
1094: tabPane.addTab(JGraphEditorResources
1095: .getString("Errors"), editor.getFactory()
1096: .createScrollPane(console));
1097:
1098: return tabPane;
1099: }
1100:
1101: }
1102:
1103: }
|