0001: /*
0002: * $Id: JGraphpad.java,v 1.24 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;
0011:
0012: import java.awt.BorderLayout;
0013: import java.awt.Color;
0014: import java.awt.Dimension;
0015: import java.awt.Font;
0016: import java.awt.KeyboardFocusManager;
0017: import java.awt.Toolkit;
0018: import java.awt.Window;
0019: import java.awt.event.WindowAdapter;
0020: import java.awt.event.WindowEvent;
0021: import java.awt.geom.Point2D;
0022: import java.awt.geom.Rectangle2D;
0023: import java.beans.DefaultPersistenceDelegate;
0024: import java.beans.Encoder;
0025: import java.beans.Expression;
0026: import java.beans.PersistenceDelegate;
0027: import java.beans.PropertyChangeEvent;
0028: import java.beans.PropertyChangeListener;
0029: import java.io.File;
0030: import java.io.FileNotFoundException;
0031: import java.io.IOException;
0032: import java.io.InputStream;
0033: import java.io.OutputStream;
0034: import java.net.Authenticator;
0035: import java.net.MalformedURLException;
0036: import java.net.URL;
0037: import java.util.Hashtable;
0038: import java.util.Iterator;
0039: import java.util.LinkedList;
0040: import java.util.List;
0041: import java.util.Map;
0042: import java.util.Properties;
0043:
0044: import javax.imageio.ImageIO;
0045: import javax.swing.BorderFactory;
0046: import javax.swing.ImageIcon;
0047: import javax.swing.JComponent;
0048: import javax.swing.JDesktopPane;
0049: import javax.swing.JFrame;
0050: import javax.swing.JLabel;
0051: import javax.swing.JWindow;
0052: import javax.swing.ToolTipManager;
0053: import javax.swing.UIManager;
0054: import javax.swing.border.Border;
0055: import javax.swing.tree.MutableTreeNode;
0056: import javax.xml.parsers.ParserConfigurationException;
0057:
0058: import org.jgraph.JGraph;
0059: import org.jgraph.event.GraphSelectionEvent;
0060: import org.jgraph.event.GraphSelectionListener;
0061: import org.jgraph.graph.AttributeMap;
0062: import org.jgraph.graph.ConnectionSet;
0063: import org.jgraph.graph.DefaultEdge;
0064: import org.jgraph.graph.DefaultGraphCell;
0065: import org.jgraph.graph.DefaultPort;
0066: import org.jgraph.graph.Edge;
0067: import org.jgraph.graph.GraphCell;
0068: import org.jgraph.graph.GraphConstants;
0069: import org.jgraph.graph.GraphLayoutCache;
0070: import org.w3c.dom.Node;
0071: import org.xml.sax.SAXException;
0072:
0073: import com.jgraph.editor.JGraphEditorAction;
0074: import com.jgraph.editor.JGraphEditorFactory;
0075: import com.jgraph.editor.JGraphEditorKit;
0076: import com.jgraph.editor.JGraphEditorModel;
0077: import com.jgraph.editor.JGraphEditorPlugin;
0078: import com.jgraph.editor.JGraphEditorResources;
0079: import com.jgraph.editor.JGraphEditorSettings;
0080: import com.jgraph.editor.JGraphEditorTool;
0081: import com.jgraph.editor.factory.JGraphEditorComboBox;
0082: import com.jgraph.editor.factory.JGraphEditorNavigator;
0083: import com.jgraph.pad.JGraphpadDiagram;
0084: import com.jgraph.pad.JGraphpadFile;
0085: import com.jgraph.pad.JGraphpadLibrary;
0086: import com.jgraph.pad.action.JGraphpadCellAction;
0087: import com.jgraph.pad.action.JGraphpadEditAction;
0088: import com.jgraph.pad.action.JGraphpadFileAction;
0089: import com.jgraph.pad.action.JGraphpadFormatAction;
0090: import com.jgraph.pad.action.JGraphpadHelpAction;
0091: import com.jgraph.pad.action.JGraphpadViewAction;
0092: import com.jgraph.pad.dialog.JGraphpadAuthenticator;
0093: import com.jgraph.pad.factory.JGraphpadComboBox;
0094: import com.jgraph.pad.factory.JGraphpadConsole;
0095: import com.jgraph.pad.factory.JGraphpadLibraryPane;
0096: import com.jgraph.pad.factory.JGraphpadOpenRecentMenu;
0097: import com.jgraph.pad.factory.JGraphpadPane;
0098: import com.jgraph.pad.factory.JGraphpadStatusBar;
0099: import com.jgraph.pad.factory.JGraphpadWindowMenu;
0100: import com.jgraph.pad.graph.JGraphpadBusinessObject;
0101: import com.jgraph.pad.graph.JGraphpadEdgeView;
0102: import com.jgraph.pad.graph.JGraphpadGraph;
0103: import com.jgraph.pad.graph.JGraphpadGraphConstants;
0104: import com.jgraph.pad.graph.JGraphpadGraphLayoutCache;
0105: import com.jgraph.pad.graph.JGraphpadGraphModel;
0106: import com.jgraph.pad.graph.JGraphpadHeavyweightRenderer;
0107: import com.jgraph.pad.graph.JGraphpadMarqueeHandler;
0108: import com.jgraph.pad.graph.JGraphpadPortView;
0109: import com.jgraph.pad.graph.JGraphpadRichTextValue;
0110: import com.jgraph.pad.graph.JGraphpadTransferHandler;
0111: import com.jgraph.pad.graph.JGraphpadVertexRenderer;
0112: import com.jgraph.pad.graph.JGraphpadVertexView;
0113: import com.jgraph.pad.tool.JGraphpadEdgeTool;
0114: import com.jgraph.pad.tool.JGraphpadVertexTool;
0115: import com.jgraph.pad.util.JGraphpadFocusManager;
0116: import com.jgraph.pad.util.JGraphpadImageIcon;
0117: import com.jgraph.pad.util.JGraphpadParallelEdgeRouter;
0118: import com.jgraph.pad.util.JGraphpadParallelSplineRouter;
0119: import com.jgraph.pad.util.JGraphpadShadowBorder;
0120:
0121: /**
0122: * Class that constructs a new editor by creating a custom document model, kit
0123: * and factory. The document model defines the persistence delegates for xml
0124: * encoding, the kit and factory contain tools, actions and factory methods
0125: * respectively. The class also constructs all plugins and provides the methods
0126: * for creating the custom graph, graph cells and user objects to be used in
0127: * this editor and to exit the application. For this purpose it provides two
0128: * inner anonymous classes which override the respective methods, namely
0129: * {@link JGraphEditor#exit(int)} and
0130: * {@link JGraphEditorFactory#createGraph(GraphLayoutCache)} editor's exit
0131: * method and the factory's createGraph method.
0132: */
0133: public class JGraphpad {
0134:
0135: /**
0136: * Global static product identifier.
0137: */
0138: public static final String VERSION_NUMBER = "6.0.4.0";
0139:
0140: /**
0141: * Holds the application title for dialogs.
0142: */
0143: public static String APPTITLE = "JGraphpad Pro";
0144:
0145: /**
0146: * Global static product identifier.
0147: */
0148: public static final String VERSION = APPTITLE + " (v"
0149: + VERSION_NUMBER + ")";
0150:
0151: /**
0152: * Specifies if libraries should reside inside documents. If this flag is true, then
0153: * the navigator and libraries are inside the internal frames for documents. The
0154: * libraries will be treated as part of their enclosing files. Default is false.
0155: */
0156: public static boolean INNER_LIBRARIES = false;
0157:
0158: /**
0159: * Defines the look and feel argument name.
0160: */
0161: public static String ARG_SYSTEMLOOKANDFEEL = "S";
0162:
0163: /**
0164: * Defines the look and feel argument name.
0165: */
0166: public static String ARG_JGOODIESLOOKANDFEEL = "J";
0167:
0168: /**
0169: * Defines the look and feel argument name.
0170: */
0171: public static String ARG_VERSION = "V";
0172:
0173: /**
0174: * Defines the path to the UI config file.
0175: */
0176: public static String PATH_UICONFIG = "/com/jgraph/pad/resources/ui.xml";
0177:
0178: /**
0179: * Defines the path to the splash image file.
0180: */
0181: public static String PATH_SPLASHIMAGE = "/com/jgraph/pad/images/splash.jpg";
0182:
0183: /**
0184: * Defines the path to the UI config file.
0185: */
0186: public static String PATH_DEFAULTLIBRARY = "/com/jgraph/pad/resources/default.xml";
0187:
0188: /**
0189: * Defines the path the the user settings file. This should also work with
0190: * URLs (untested).
0191: */
0192: public static String PATH_DEFAULTSETTINGS = "/com/jgraph/pad/resources/default.ini";
0193:
0194: /**
0195: * Defines the path the the user settings file. This should also work with
0196: * URLs (untested).
0197: */
0198: public static String PATH_USERSETTINGS;
0199:
0200: /**
0201: * Defines the filename for the settings file. Default is .jgraphpad.ini
0202: */
0203: public static String NAME_SETTINGSFILE = ".jgraphpad.ini";
0204:
0205: /**
0206: * Defines the name for the ui XML document in the editor settings.
0207: */
0208: public static String NAME_UICONFIG = "ui";
0209:
0210: /**
0211: * Defines the name for the user properties in the editor settings.
0212: */
0213: public static String NAME_USERSETTINGS = "user";
0214:
0215: /**
0216: * Defines the key used to identify the main window settings.
0217: */
0218: public static String KEY_MAINWINDOW = "mainWindow";
0219:
0220: /**
0221: * Defines the key used to identify the recent files settings.
0222: */
0223: public static String KEY_RECENTFILES = "recentFiles";
0224:
0225: /**
0226: * Defines the key used to identify the group prototype settings.
0227: */
0228: public static String KEY_GROUPPROTOTYPE = "groupPrototype";
0229:
0230: /**
0231: * Defines the key used to identify the vertex prototype settings.
0232: */
0233: public static String KEY_VERTEXPROTOTYPE = "vertexPrototype";
0234:
0235: /**
0236: * Defines the key used to identify the edge prototype settings.
0237: */
0238: public static String KEY_EDGEPROTOTYPE = "edgePrototype";
0239:
0240: /**
0241: * Defines the name for the selectTool.
0242: */
0243: public static final String NAME_SELECTTOOL = "selectTool";
0244:
0245: /**
0246: * Defines the name for the textTool.
0247: */
0248: public static final String NAME_TEXTTOOL = "textTool";
0249:
0250: /**
0251: * Defines the name for the vertexTool.
0252: */
0253: public static final String NAME_VERTEXTOOL = JGraphpadVertexTool.NAME_VERTEXTOOL;
0254:
0255: /**
0256: * Defines the name for the roundedTool.
0257: */
0258: public static final String NAME_ROUNDEDTOOL = "roundedTool";
0259:
0260: /**
0261: * Defines the name for the circleTool.
0262: */
0263: public static final String NAME_CIRCLETOOL = "circleTool";
0264:
0265: /**
0266: * Defines the name for the diamondTool.
0267: */
0268: public static final String NAME_DIAMONDTOOL = "diamondTool";
0269:
0270: /**
0271: * Defines the name for the triangleTool.
0272: */
0273: public static final String NAME_TRIANGLETOOL = "triangleTool";
0274:
0275: /**
0276: * Defines the name for the diamondTool.
0277: */
0278: public static final String NAME_CYLINDERTOOL = "cylinderTool";
0279:
0280: /**
0281: * Defines the name for the imageTool.
0282: */
0283: public static final String NAME_IMAGETOOL = "imageTool";
0284:
0285: /**
0286: * Defines the name for the imageTool.
0287: */
0288: public static final String NAME_HEAVYTOOL = "heavyTool";
0289:
0290: /**
0291: * Defines the name for the edgeTool.
0292: */
0293: public static final String NAME_EDGETOOL = JGraphpadEdgeTool.NAME_EDGETOOL;
0294:
0295: /**
0296: * Defines the name for the orthogonalEdgeTool.
0297: */
0298: public static final String NAME_ORTHOGONALEDGETOOL = "orthogonalEdgeTool";
0299:
0300: /**
0301: * Defines the name of the createShapeCombo factory method.
0302: */
0303: public static final String METHOD_CREATESHAPECOMBO = "createShapeCombo";
0304:
0305: /**
0306: * Defines the name of the createGradientCombo factory method.
0307: */
0308: public static final String METHOD_CREATEGRADIENTCOMBO = "createGradientCombo";
0309:
0310: /**
0311: * Defines the name of the createGradientCombo factory method.
0312: */
0313: public static final String METHOD_CREATELINECOLORCOMBO = "createLineColorCombo";
0314:
0315: /**
0316: * Adds resources bundles and prepares classes for xml encoding.
0317: *
0318: * @see JGraphEditorResources#addBundles
0319: * @see JGraphEditorModel#makeCellViewFieldsTransient(Class)
0320: * @see JGraphEditorModel#makeTransient
0321: */
0322: static {
0323:
0324: // Adds resource bundles
0325: JGraphEditorResources.addBundles(new String[] {
0326: "com.jgraph.pad.resources.actions",
0327: "com.jgraph.pad.resources.menus",
0328: "com.jgraph.pad.resources.strings",
0329: "com.jgraph.pad.resources.tools" });
0330:
0331: // Prepares cell views for xml encoding (recommended for all cell views)
0332: JGraphEditorModel
0333: .makeCellViewFieldsTransient(JGraphpadVertexView.class);
0334: JGraphEditorModel
0335: .makeCellViewFieldsTransient(JGraphpadPortView.class);
0336: JGraphEditorModel
0337: .makeCellViewFieldsTransient(JGraphpadEdgeView.class);
0338:
0339: // Prepares class bean infos for xml encoding
0340: JGraphEditorModel.makeTransient(DefaultPort.class, "edges");
0341: JGraphEditorModel.makeTransient(DefaultEdge.class, "source");
0342: JGraphEditorModel.makeTransient(DefaultEdge.class, "target");
0343: JGraphEditorModel.makeTransient(GraphLayoutCache.class,
0344: "visibleSet");
0345: JGraphEditorModel
0346: .makeTransient(JGraphpadFile.class, "modified");
0347: JGraphEditorModel.makeTransient(JGraphpadFile.class,
0348: "userObject");
0349: JGraphEditorModel
0350: .makeTransient(JGraphpadFile.class, "filename");
0351: JGraphEditorModel.makeTransient(JGraphpadFile.class, "new");
0352: JGraphEditorModel.makeTransient(JGraphpadLibrary.class,
0353: "modified");
0354: JGraphEditorModel.makeTransient(JGraphpadLibrary.class,
0355: "userObject");
0356: JGraphEditorModel.makeTransient(JGraphpadLibrary.class,
0357: "filename");
0358: JGraphEditorModel.makeTransient(JGraphpadLibrary.class, "new");
0359: JGraphEditorModel.makeTransient(ConnectionSet.class, "edges");
0360:
0361: // Installs an additional border in the border combo box
0362: JGraphEditorComboBox.defaultBorders = new Border[] {
0363: BorderFactory.createRaisedBevelBorder(),
0364: BorderFactory.createLoweredBevelBorder(),
0365: BorderFactory.createEtchedBorder(),
0366: BorderFactory.createLineBorder(Color.black),
0367: JGraphpadShadowBorder.sharedInstance };
0368:
0369: // Installs basic authentication dialog
0370: try {
0371: Authenticator.setDefault(new JGraphpadAuthenticator());
0372: } catch (Exception e) {
0373: // ignore
0374: }
0375: }
0376:
0377: /**
0378: * The class names for the defaut plugins.
0379: */
0380: public String[] defaultPlugins = new String[] {
0381: "com.jgraph.l2fplugin.JGraphpadL2FPlugin",
0382: "com.jgraph.layoutplugin.JGraphpadLayoutPlugin",
0383: "com.jgraph.svgplugin.JGraphpadSVGPlugin",
0384: "com.jgraph.bshplugin.JGraphpadBshPlugin",
0385: "com.jgraph.jgxplugin.JGraphpadJGXPlugin",
0386: "com.jgraph.codecplugin.JGraphpadCodecPlugin",
0387: "com.jgraph.epsplugin.JGraphpadEPSPlugin",
0388: "com.jgraph.pdfplugin.JGraphpadPDFPlugin",
0389: "com.jgraph.browserplugin.JGraphpadBrowserPlugin",
0390: "com.jgraph.twikiplugin.JGraphpadTWikiPlugin" };
0391:
0392: /**
0393: * Defines the default port locations.
0394: */
0395: public Point2D[] defaultPortLocations = new Point2D[] { null };
0396:
0397: /**
0398: * Defines the default vertex bounds.
0399: */
0400: public Rectangle2D defaultBounds = new Rectangle2D.Double(0, 0, 20,
0401: 20);
0402:
0403: /**
0404: * Defines the default border color.
0405: */
0406: public Color defaultBorderColor = Color.BLACK;
0407:
0408: /**
0409: * Defines the default edge font.
0410: */
0411: public Font defaultEdgeFont = GraphConstants.DEFAULTFONT
0412: .deriveFont(10);
0413:
0414: /**
0415: * Defines the default end and begin decorations for edges.
0416: */
0417: public int defaultEndDecoration = GraphConstants.ARROW_TECHNICAL,
0418: defaultBeginDecoration = GraphConstants.ARROW_NONE;
0419:
0420: /**
0421: * Constructs JGraphpad as an applet.
0422: */
0423: public JGraphpad() {
0424: // Some general variable assignments that require control logic.
0425: // This one is likely to throw a security exception in an applet.
0426: // In applications it uses the user's homedir to save settings.
0427: try {
0428: PATH_USERSETTINGS = System.getProperty("user.home")
0429: + File.separator + NAME_SETTINGSFILE;
0430: } catch (SecurityException e) {
0431: // ignore
0432: }
0433: }
0434:
0435: /**
0436: * Hook for subclassers to implement the application exit method. This
0437: * implementation calls System.exit with the specified code.
0438: */
0439: protected void exit(int code) {
0440: System.exit(code);
0441: }
0442:
0443: /**
0444: * Constructs a new editor application and returns its main window. Shows a
0445: * splash window while the application is being constructed and returns the
0446: * main window in visible state. <br>
0447: * The parameters are obtained by parsing the arguments passed to the main
0448: * method as follows. For all arguments -A B the value B is stored in the
0449: * arguments under the A, for other arguments, eg. C D E the values C, D and
0450: * E and passed in as elements of the list.
0451: *
0452: * @param files
0453: * The list of filenames passed to the Java command.
0454: * @param args
0455: * The arguments to use for constructing the editor settings.
0456: * @return Returns a new application main window.
0457: */
0458: public Window createApplication(List files, Map args)
0459: throws ParserConfigurationException, SAXException,
0460: IOException {
0461:
0462: // Checks the lookandfeel argument
0463: if (args != null && args.remove(ARG_SYSTEMLOOKANDFEEL) != null) {
0464: try {
0465: UIManager.setLookAndFeel(UIManager
0466: .getSystemLookAndFeelClassName());
0467: } catch (Exception e) {
0468: e.printStackTrace();
0469: }
0470: } else if (args != null
0471: && args.remove(ARG_JGOODIESLOOKANDFEEL) != null) {
0472: try {
0473: UIManager
0474: .setLookAndFeel("com.jgoodies.looks.plastic.Plastic3DLookAndFeel");
0475: } catch (Exception e) {
0476: e.printStackTrace();
0477: }
0478: }
0479:
0480: // Constructs and displays a splash window. Also checks to
0481: // see if it's the user's first session and opens an
0482: // initial new document if it is.
0483: Window splashWindow = createSplashWindow();
0484: splashWindow.setVisible(true);
0485:
0486: boolean firstTime = (PATH_USERSETTINGS != null) ? !(new File(
0487: PATH_USERSETTINGS).exists()) : false;
0488:
0489: // Constructs the editor
0490: final JGraphEditor editor = createEditor(args);
0491:
0492: // Adds the plugins settings, actions, tools and methods
0493: // and initializes the plugins.
0494: createPlugins(editor);
0495:
0496: // Constructs the main window and keep a reference (restore bounds)
0497: // for storage of the bounds when the program terminates (hook).
0498: Window mainWindow = createMainWindow(editor,
0499: JGraphpadPane.FactoryMethod.NAME);
0500:
0501: // ** Application construction complete **
0502:
0503: // Puts the main window into the settings map and
0504: // restores its bounds from the user settings.
0505: editor.getSettings().putObject(KEY_MAINWINDOW, mainWindow);
0506: editor.getSettings().restoreWindow(NAME_USERSETTINGS,
0507: KEY_MAINWINDOW);
0508:
0509: // Installs a shutdown hook to store the bounds if the
0510: // main window in the properties (in memory) for later
0511: // storing to disk.
0512: editor.getSettings().addShutdownHook(
0513: new JGraphEditorSettings.ShutdownHook() {
0514:
0515: // Takes the window bounds and stores the into the in-core
0516: // user configuration, which is later saved to disk.
0517: public void shutdown() {
0518: editor.getSettings().storeWindow(
0519: NAME_USERSETTINGS, KEY_MAINWINDOW);
0520: }
0521: });
0522:
0523: // This is how we link two factory methods, in this case the
0524: // createFrame and the createWindowMenu, which needs to be updated
0525: // when internal frames are added to or removed from the desktop
0526: // pane: Both factoryMethods store a reference to the desktop pane
0527: // and the menu and the code below fetches the references and wires
0528: // them up properly, eg. adds the menu as a listener to the desktop
0529: // pane. This needs to be done after createMainWindow was called.
0530: JDesktopPane desktopPane = (JDesktopPane) editor.getSettings()
0531: .getObject(JGraphpadPane.KEY_DESKTOPPANE);
0532: JGraphpadWindowMenu windowMenu = (JGraphpadWindowMenu) editor
0533: .getSettings().getObject(
0534: JGraphpadWindowMenu.KEY_WINDOWMENU);
0535: // Wires the two references up by setting the desktop in the menu
0536: if (desktopPane != null && windowMenu != null) {
0537: windowMenu.setDesktopPane(desktopPane);
0538: }
0539:
0540: // Opens the built-in library and remove the filename
0541: // as it has been loaded from within the jar file to
0542: // where it can not be saved back. Mark it new afterwards.
0543: try {
0544: JGraphpadLibrary library = (JGraphpadLibrary) editor
0545: .getModel().addFile(PATH_DEFAULTLIBRARY);
0546: if (library != null) {
0547: editor.getModel().setFilename(
0548: library,
0549: JGraphEditorResources
0550: .getString("DefaultLibrary"));
0551: library.setNew(true);
0552: }
0553: } catch (Exception e) {
0554: // ignore
0555: }
0556:
0557: // Disposes the splash window
0558: splashWindow.dispose();
0559:
0560: // Restores the divider locations
0561: // We must return a visible main window to be able to
0562: // restore the splitpane divider locations, which require
0563: // the split panes to be visible (Swing Bug).
0564: mainWindow.setVisible(true);
0565: editor.getSettings().restoreSplitPane(NAME_USERSETTINGS,
0566: JGraphpadPane.KEY_NAVIGATORSPLIT);
0567: editor.getSettings().restoreSplitPane(NAME_USERSETTINGS,
0568: JGraphpadPane.KEY_LEFTSPLIT);
0569: editor.getSettings().restoreSplitPane(NAME_USERSETTINGS,
0570: JGraphpadPane.KEY_RIGHTSPLIT);
0571:
0572: // Opens the specified documents
0573:
0574: // Removes file actions from the kit for the demo version.
0575: if (files != null && !files.isEmpty()) {
0576: JGraphEditorModel model = editor.getModel();
0577: Iterator it = files.iterator();
0578: while (it.hasNext()) {
0579: String filename = String.valueOf(it.next());
0580: try {
0581: editor.getModel().addFile(filename);
0582: } catch (Exception e) {
0583: JGraphpadFile file = new JGraphpadFile(filename);
0584: JGraphpadDiagram newDiagram = new JGraphpadDiagram(
0585: JGraphEditorResources.getString("Diagram")
0586: + "1");
0587: model.addRoot(file);
0588: model.addChild(newDiagram, file);
0589: }
0590: }
0591: }
0592:
0593: // Opens a document if it's the users first session
0594: else if (firstTime)
0595: editor.getKit().getAction(
0596: JGraphpadFileAction.NAME_NEWDOCUMENT)
0597: .actionPerformed(null);
0598:
0599: return mainWindow;
0600: }
0601:
0602: /**
0603: * Helper method that invokes the specified factory method and configures
0604: * the main window by setting its bounds and installing window listeners.
0605: * This implementation invokes the exit action in the editor kit if the
0606: * window is closed by the user.
0607: *
0608: * @param editor
0609: * The editor to create the main window for.
0610: * @param factoryMethod
0611: * The name of the factory method to invoke.
0612: * @return Returns a new main window for the specified editor.
0613: *
0614: * @see JGraphEditorFactory#executeMethod(String, Node)
0615: * @see JGraphEditorKit#getAction(String)
0616: * @see #center(Window)
0617: */
0618: protected Window createMainWindow(final JGraphEditor editor,
0619: String factoryMethod) {
0620:
0621: // Invokes the specified factory method and passes
0622: // along the ui configuration.
0623: Window wnd = (Window) editor.getFactory().executeMethod(
0624: factoryMethod,
0625: editor.getSettings().getDocument(NAME_UICONFIG)
0626: .getDocumentElement());
0627:
0628: // Positions the window, sets the default size and
0629: // installs a window listener.
0630: wnd.setSize(800, 640);
0631: center(wnd);
0632:
0633: // Makes sure the window is only closed by means of the exit
0634: // operation, which shuts down the VM.
0635: if (wnd instanceof JFrame)
0636: ((JFrame) wnd)
0637: .setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
0638: wnd.addWindowListener(new WindowAdapter() {
0639: public void windowClosing(WindowEvent e) {
0640: editor.getKit().getAction("exit").actionPerformed(null);
0641: }
0642: });
0643: return wnd;
0644: }
0645:
0646: /**
0647: * Constructs a splash window to be displayed during the construction of the
0648: * application.
0649: *
0650: * @return Returns a reference to the displaying splash window.
0651: */
0652: protected Window createSplashWindow() {
0653: JWindow splashWindow = new JWindow();
0654:
0655: // Loads the splash image from the resources into a JLabel
0656: // using the instance variable for the image path. Then adds
0657: // a raised bevel border an adds the label to the window.
0658: JLabel image = new JLabel(JGraphEditorResources
0659: .getImage(PATH_SPLASHIMAGE));
0660: image.setBorder(BorderFactory.createRaisedBevelBorder());
0661: splashWindow.getContentPane().add(image, BorderLayout.CENTER);
0662: JLabel label = new JLabel("v" + VERSION_NUMBER);
0663: image.setLayout(null);
0664: image.add(label);
0665: label.setFont(label.getFont().deriveFont(Font.BOLD));
0666: label.setForeground(Color.GRAY);
0667: label.setBounds(202, 118, 200, 30);
0668:
0669: // Sets size and position and displays the window
0670: splashWindow.pack();
0671: center(splashWindow);
0672:
0673: return splashWindow;
0674: }
0675:
0676: /**
0677: * Constructs a new {@link JGraphEditor} using the specified settings and
0678: * document model model and calls
0679: * {@link #configureEditor(JGraphEditor, Map)} on the new instance. The
0680: * returned instance has an overridden exit hook which disposes the main
0681: * window and terminates the VM in the proper way for how the application
0682: * was started.
0683: *
0684: * @param args
0685: * The arguments passed to createApplication.
0686: *
0687: * @return Returns a new configured editor.
0688: * @throws IOException
0689: * @throws SAXException
0690: * @throws ParserConfigurationException
0691: */
0692: protected JGraphEditor createEditor(Map args)
0693: throws ParserConfigurationException, SAXException,
0694: IOException {
0695:
0696: // Overrides the global application exit hook to dispose the
0697: // window. Note: Calls System.exit if thisis not an applet. The exit
0698: // method is called after all user interface interaction has
0699: // terminated from within the exit method implemented in
0700: // JGraphpadFileExit.
0701: JGraphEditor editor = new JGraphEditor() {
0702:
0703: /**
0704: * Fetches the main window from the settings and disposes it. This
0705: * will terminate the VM if there are no more open windows left.
0706: */
0707: public void exit(int code) {
0708: super .exit(code);
0709: Window wnd = (Window) getSettings().getObject(
0710: KEY_MAINWINDOW);
0711: if (wnd != null) {
0712: wnd.setVisible(false);
0713: wnd.dispose();
0714: }
0715: JGraphpad.this .exit(code);
0716: }
0717: };
0718: // Configures the editor kit, factory and constructs plugins
0719: configureEditor(editor, args);
0720:
0721: return editor;
0722: }
0723:
0724: /**
0725: * Hook for subclassers to configure new editors. This implementation
0726: * configures the editor kit and -factory and invokes
0727: * {@link #createPlugins(JGraphEditor)}for the editor.
0728: *
0729: * @param editor
0730: * The editor to be configured.
0731: * @param args
0732: * The arguments passed to createApplication.
0733: * @throws IOException
0734: * @throws SAXException
0735: * @throws ParserConfigurationException
0736: *
0737: * @see #createKit(JGraphEditor)
0738: * @see #createFactory(JGraphEditor)
0739: */
0740: protected void configureEditor(JGraphEditor editor, Map args)
0741: throws ParserConfigurationException, SAXException,
0742: IOException {
0743: editor.setSettings(createSettings(args));
0744: editor.setModel(createModel());
0745: editor.setKit(createKit(editor));
0746: editor.setFactory(createFactory(editor));
0747: }
0748:
0749: /**
0750: * Constructs the editor settings. This implementation constructs a new
0751: * instance of JGraphEditorSettings using args as the initial object map and
0752: * passes it to the {@link #configureSettings(JGraphEditorSettings)}method.
0753: *
0754: * @param args
0755: * The arguments passed to the command line.
0756: * @return Returns a configured editor settings object.
0757: */
0758: protected JGraphEditorSettings createSettings(Map args)
0759: throws ParserConfigurationException, SAXException,
0760: IOException {
0761: JGraphEditorSettings settings = new JGraphEditorSettings(args);
0762: configureSettings(settings);
0763: return settings;
0764: }
0765:
0766: /**
0767: * Hook for subclassers to configure new editor settings. This
0768: * implementation adds the ui configuration ({@link #PATH_UICONFIG}) to
0769: * the
0770: *
0771: * {@link #NAME_UICONFIG}and user settings ({@link #PATH_USERSETTINGS})
0772: * to the {@link #NAME_USERSETTINGS}.
0773: *
0774: * @param settings
0775: * The editor settings to be configured.
0776: * @throws IOException
0777: * @throws SAXException
0778: * @throws ParserConfigurationException
0779: * @throws FileNotFoundException
0780: * @throws MalformedURLException
0781: *
0782: * @see JGraphEditorSettings#parse(InputStream)
0783: * @see JGraphEditorSettings#add(String, InputStream)
0784: */
0785: protected void configureSettings(JGraphEditorSettings settings)
0786: throws MalformedURLException, FileNotFoundException,
0787: ParserConfigurationException, SAXException, IOException {
0788:
0789: // Parses the ui configuration file. We have to use the resources'
0790: // getInputStream because we have no model yet. This means the
0791: // user configuration must not be compressed, which is acceptable.
0792: settings.add(NAME_UICONFIG, JGraphEditorSettings
0793: .parse(JGraphEditorResources
0794: .getInputStream(PATH_UICONFIG)));
0795:
0796: // Reads the user settings if they exist. Note: User settings do
0797: // never exist on the first run of this program. They are created
0798: // or updated when the program exits.
0799: if (PATH_USERSETTINGS != null
0800: && new File(PATH_USERSETTINGS).canRead())
0801: settings.add(NAME_USERSETTINGS, JGraphEditorResources
0802: .getInputStream(PATH_USERSETTINGS));
0803: else
0804: settings.add(NAME_USERSETTINGS, JGraphEditorResources
0805: .getInputStream(PATH_DEFAULTSETTINGS));
0806:
0807: // Adds a shutdown hook to save the user settings when
0808: // the program terminates. This shutdown hook is added
0809: // at creation time to make sure it is the last hook
0810: // invoked in the shutdown method of the settings.
0811: final Properties userSettings = settings
0812: .getProperties(JGraphpad.NAME_USERSETTINGS);
0813: settings
0814: .addShutdownHook(new JGraphEditorSettings.ShutdownHook() {
0815: public void shutdown() {
0816: if (userSettings != null
0817: && PATH_USERSETTINGS != null) {
0818: try {
0819: OutputStream out = JGraphEditorResources
0820: .getOutputStream(JGraphpad.PATH_USERSETTINGS);
0821: userSettings.store(out, "");
0822: out.flush();
0823: out.close();
0824:
0825: // Allows PATH_USERSETTINGS to be a URL
0826: if (JGraphEditor
0827: .isURL(JGraphpad.PATH_USERSETTINGS)) {
0828: URL url = new URL(
0829: JGraphpad.PATH_USERSETTINGS);
0830: JGraphpadFileAction.postPlain(url,
0831: url.getFile(), out);
0832: }
0833: } catch (Exception e) {
0834: // ignore
0835: }
0836: }
0837: }
0838: });
0839:
0840: // Adds prototypes for groups, vertices and edges to the
0841: // settings for later use by plugins and other interested
0842: // parties.
0843: settings.putObject(KEY_GROUPPROTOTYPE, createGroup());
0844: settings.putObject(KEY_VERTEXPROTOTYPE, createVertex());
0845: settings.putObject(KEY_EDGEPROTOTYPE, createEdge());
0846: }
0847:
0848: /**
0849: * Constructs a document model for new editors. This implementation
0850: * constructs a new instance of {@link JGraphEditorModel}and passes it to
0851: * the {@link #configureModel(JGraphEditorModel)}method.
0852: *
0853: * @return Returns a configured document model for an editor.
0854: */
0855: protected JGraphEditorModel createModel() {
0856: JGraphEditorModel model = new JGraphEditorModel();
0857: configureModel(model);
0858: return model;
0859: }
0860:
0861: /**
0862: * Hook for subclassers to configure new document models. This
0863: * implementation adds persistence delegates for the following classes:
0864: * {@link JGraphpadDiagram},{@link JGraphpadGraphModel},
0865: * com.jgraph.graph.ConnectionSet, {@link JGraphpadGraphLayoutCache},
0866: * com.jgraph.graph.DefaultGraphCell, com.jgraph.graph.DefaultEdge,
0867: * com.jgraph.graph.DefaultPort,{@link JGraphpadBusinessObject},
0868: * {@link JGraphpadRichTextValue}and {@link JGraphpadShadowBorder}.<br>
0869: * <b>JGraphpadDiagram </b> <br>
0870: * Constructs a persistence delegate for the diagram class. This uses the
0871: * fact that all information is stored in the model, not the layout cache by
0872: * ignoring the layout cache and accessing the model stored in the layout
0873: * cache directly through the model bean property. Note: To allow this kind
0874: * of encoding the diagram class offers a special constructor that takes a
0875: * model and constructs a new graph layout cache for it. <br>
0876: * <b>JGraphpadGraphModel </b> <br>
0877: * To encode graph models we do not want the files to contain redundant
0878: * connectivity information in the ports.edges and edges.source and target
0879: * fields, so we add a method to the graph model that returns a connection
0880: * set which describes the connections without redundancy. (Note: In the
0881: * static initializer of this class we make sure that the edges, source and
0882: * target of the respective classes or not encoded.) <br>
0883: * <b>ConnectionSet </b> <br>
0884: * The complete information of a connection set is stored in the actual
0885: * connections, thus we only store the connections and use special
0886: * constructor to restore the state of the complete object when de- coding.
0887: * (Note: For connection sets this will update the edges field.) <br>
0888: * <b>JGraphpadGraphLayoutCache </b> <br>
0889: * The graph layout cache is encoded by encoding the various member fields,
0890: * using a special constructor to restore the state of the layout cache upon
0891: * decoding. Note that this is currently not used. <br>
0892: * <b>DefaultGraphCell, DefaultEdge, DefaultPort </b> <br>
0893: * Makes sure the cells are only encoded along with their user objects, the
0894: * attributes, connections and tree-structure is stored in other objects and
0895: * does not need to be encoded here. <br>
0896: * <b>JGraphpadBusinessObject, JGraphpadRichTextData </b> <br>
0897: * Allows to encode custom business objects used in JGraphpad. Since this
0898: * object implements the bean interface we do only require a default
0899: * persistence delegates with no special constructor calls to decode the
0900: * object. Same holds for the rich text data object, which is a special
0901: * value that can hold text formatting information. <br>
0902: * <b>JGraphShadowBorder </b> <br>
0903: * Since the shadow border is a singleton we must tell the decoder which
0904: * method to use in order to find the shared instance of the class.
0905: *
0906: * @param model
0907: * The document model to be configured.
0908: */
0909: protected void configureModel(JGraphEditorModel model) {
0910: model.addPersistenceDelegate(JGraphpadDiagram.class,
0911: new DefaultPersistenceDelegate(new String[] { "name",
0912: "graphLayoutCache", "properties" }));
0913: model.addPersistenceDelegate(JGraphpadLibrary.class,
0914: new DefaultPersistenceDelegate(new String[] {
0915: "filename", "graphLayoutCache" }));
0916: model.addPersistenceDelegate(JGraphpadGraphModel.class,
0917: new DefaultPersistenceDelegate(new String[] { "roots",
0918: "attributes", "connectionSet" }));
0919: model.addPersistenceDelegate(ConnectionSet.class,
0920: new DefaultPersistenceDelegate(
0921: new String[] { "connections" }));
0922:
0923: model.addPersistenceDelegate(JGraphpadGraphLayoutCache.class,
0924: new DefaultPersistenceDelegate(new String[] { "model",
0925: "visibleSet", "partial" }));
0926:
0927: model.addPersistenceDelegate(DefaultGraphCell.class,
0928: new DefaultPersistenceDelegate(
0929: new String[] { "userObject" }));
0930: model.addPersistenceDelegate(DefaultEdge.class,
0931: new DefaultPersistenceDelegate(
0932: new String[] { "userObject" }));
0933: model.addPersistenceDelegate(DefaultPort.class,
0934: new DefaultPersistenceDelegate(
0935: new String[] { "userObject" }));
0936:
0937: model.addPersistenceDelegate(JGraphpadBusinessObject.class,
0938: new DefaultPersistenceDelegate());
0939: model.addPersistenceDelegate(JGraphpadRichTextValue.class,
0940: new DefaultPersistenceDelegate());
0941:
0942: // Shadow Border instance
0943: model.addPersistenceDelegate(JGraphpadShadowBorder.class,
0944: new PersistenceDelegate() {
0945: protected Expression instantiate(
0946: Object oldInstance, Encoder out) {
0947: return new Expression(oldInstance,
0948: JGraphpadShadowBorder.class,
0949: "getSharedInstance", null);
0950: }
0951: });
0952:
0953: // Two shared routing instances
0954: model.addPersistenceDelegate(JGraphpadParallelEdgeRouter.class,
0955: new PersistenceDelegate() {
0956: protected Expression instantiate(
0957: Object oldInstance, Encoder out) {
0958: return new Expression(oldInstance,
0959: JGraphpadGraphConstants.class,
0960: "getParallelEdgeRouting", null);
0961: }
0962: });
0963: model.addPersistenceDelegate(
0964: JGraphpadParallelSplineRouter.class,
0965: new PersistenceDelegate() {
0966: protected Expression instantiate(
0967: Object oldInstance, Encoder out) {
0968: return new Expression(oldInstance,
0969: JGraphpadGraphConstants.class,
0970: "getParallelSplineRouting", null);
0971: }
0972: });
0973: }
0974:
0975: /**
0976: * Constructs a default kit for new editors. This implementation constructs
0977: * a new instance of {@link JGraphEditorKit}and passes it to
0978: * {@link #configureKit(JGraphEditor, JGraphEditorKit)}.
0979: *
0980: * @param editor
0981: * The editor for which to create an editor kit.
0982: * @return Returns a configured editor kit for the specified editor.
0983: */
0984: protected JGraphEditorKit createKit(JGraphEditor editor) {
0985: JGraphEditorKit kit = new JGraphEditorKit();
0986: configureKit(editor, kit);
0987: return kit;
0988: }
0989:
0990: /**
0991: * Boilerplate method for configuring new editor kits. This implementation
0992: * calls {@link #addActions(JGraphEditor, JGraphEditorKit)},
0993: * {@link #addTools(JGraphEditor, JGraphEditorKit)}and registers the kit
0994: * with the listeners required to update the state. This method is called
0995: * from {@link #createKit(JGraphEditor)}.
0996: *
0997: * @param editor
0998: * The editor for which to configure the editor kit.
0999: * @param kit
1000: * The new editor kit to be configured.
1001: */
1002: protected void configureKit(JGraphEditor editor,
1003: final JGraphEditorKit kit) {
1004: addActions(editor, kit);
1005: addTools(editor, kit);
1006:
1007: // A selection listener for library pane's backing graph that
1008: // needs to be listened to for updating the actions in the kit.
1009: final GraphSelectionListener selectionListener = new GraphSelectionListener() {
1010:
1011: /**
1012: * Updates the actions in the factory kit when the selection of a
1013: * library pane (ie of its backing graph) changes.
1014: */
1015: public void valueChanged(GraphSelectionEvent e) {
1016: kit.update();
1017: }
1018:
1019: };
1020:
1021: KeyboardFocusManager focusManager = KeyboardFocusManager
1022: .getCurrentKeyboardFocusManager();
1023: focusManager
1024: .addPropertyChangeListener(new PropertyChangeListener() {
1025:
1026: /*
1027: * (non-Javadoc)
1028: */
1029: public void propertyChange(PropertyChangeEvent e) {
1030: if (e.getPropertyName().equals(
1031: "permanentFocusOwner"))
1032: kit.update();
1033:
1034: // Keeps the selection listener installed with the library
1035: // pane's backing graph. Travels along with the focus.
1036: if (e.getOldValue() instanceof JGraphpadLibraryPane)
1037: ((JGraphpadLibraryPane) e.getOldValue())
1038: .getBackingGraph()
1039: .getSelectionModel()
1040: .removeGraphSelectionListener(
1041: selectionListener);
1042: if (e.getNewValue() instanceof JGraphpadLibraryPane)
1043: ((JGraphpadLibraryPane) e.getNewValue())
1044: .getBackingGraph()
1045: .getSelectionModel()
1046: .addGraphSelectionListener(
1047: selectionListener);
1048: }
1049: });
1050:
1051: JGraphpadFocusManager.getCurrentGraphFocusManager()
1052: .addPropertyChangeListener(
1053: new PropertyChangeListener() {
1054:
1055: /*
1056: * (non-Javadoc)
1057: */
1058: public void propertyChange(
1059: PropertyChangeEvent e) {
1060: kit.update();
1061: }
1062: });
1063: kit.update();
1064: }
1065:
1066: //
1067: // Actions
1068: //
1069:
1070: /**
1071: * Adds the action bundles for {@link JGraphpadEditAction},
1072: * {@link JGraphpadFileAction},{@link JGraphpadViewAction},
1073: * {@link JGraphpadFormatAction}and {@link JGraphpadCellAction}. Uses the
1074: * createVertex and createEdge method to construct prototypes for
1075: * {@link JGraphpadCellAction#NAME_GROUP}and
1076: * {@link JGraphpadCellAction#NAME_CONNECT}. This method is called from
1077: * configureEditorKit.
1078: *
1079: * @param editor
1080: * The editor for which to create the actions.
1081: * @param kit
1082: * The editor kit to add the actions to.
1083: *
1084: * @see #configureKit(JGraphEditor, JGraphEditorKit)
1085: * @see JGraphEditorKit#addBundle(JGraphEditorAction.Bundle)
1086: * @see #createVertex()
1087: * @see #createEdge()
1088: */
1089: protected void addActions(JGraphEditor editor, JGraphEditorKit kit) {
1090:
1091: // Adds action bundles for each of the menus. To simplify
1092: // searching for specific actions all actions reside in
1093: // the classes corresponding to the menu they are in.
1094: kit.addBundle(new JGraphpadEditAction.AllActions());
1095: kit.addBundle(new JGraphpadViewAction.AllActions());
1096: kit.addBundle(new JGraphpadFormatAction.AllActions());
1097: kit.addBundle(new JGraphpadHelpAction.AllActions());
1098:
1099: // Adds actions that require prototype cells
1100: kit.addBundle(new JGraphpadFileAction.AllActions(editor));
1101: kit.addBundle(new JGraphpadCellAction.AllActions(editor));
1102: }
1103:
1104: //
1105: // Editor Tools
1106: //
1107:
1108: /**
1109: * Adds the following tools to <code>kit</code>:{@link #NAME_SELECTTOOL},
1110: * {@link #NAME_VERTEXTOOL},{@link #NAME_ROUNDEDTOOL},
1111: * {@link #NAME_CIRCLETOOL},{@link #NAME_DIAMONDTOOL},{@link #NAME_TRIANGLETOOL},
1112: * {@link #NAME_IMAGETOOL},{@link #NAME_HEAVYTOOL},{@link #NAME_EDGETOOL}and
1113: * {@link #NAME_ORTHOGONALEDGETOOL}. This method is called from
1114: * configureEditorKit.
1115: *
1116: * @param editor
1117: * The editor for which to create the tools.
1118: * @param kit
1119: * The editor kit to add the tools to.
1120: *
1121: * @see #configureKit(JGraphEditor, JGraphEditorKit)
1122: * @see JGraphEditorKit#addTool(JGraphEditorTool)
1123: * @see #createVertexTool(String, Object, int, ImageIcon)
1124: * @see #createEdgeTool(String, String, Edge.Routing)
1125: */
1126: protected void addTools(JGraphEditor editor, JGraphEditorKit kit) {
1127: kit.addTool(new JGraphEditorTool(NAME_SELECTTOOL, false));
1128: JGraphpadVertexTool tool = createVertexTool(NAME_TEXTTOOL,
1129: new JGraphpadRichTextValue(""), -1, null, true);
1130: tool.setPreviewEnabled(false);
1131: kit.addTool(tool);
1132: kit.addTool(createVertexTool(NAME_VERTEXTOOL,
1133: new JGraphpadRichTextValue(""),
1134: JGraphpadVertexRenderer.SHAPE_RECTANGLE, null));
1135: JComponent c = new JGraphpadHeavyweightRenderer();
1136: kit.addTool(createVertexTool(NAME_HEAVYTOOL, c,
1137: JGraphpadVertexRenderer.SHAPE_RECTANGLE, null));
1138: kit.addTool(createVertexTool(NAME_ROUNDEDTOOL,
1139: new JGraphpadRichTextValue(""),
1140: JGraphpadVertexRenderer.SHAPE_ROUNDED, null));
1141: kit.addTool(createVertexTool(NAME_CIRCLETOOL,
1142: new JGraphpadRichTextValue(""),
1143: JGraphpadVertexRenderer.SHAPE_CIRCLE, null));
1144: kit.addTool(createVertexTool(NAME_DIAMONDTOOL,
1145: new JGraphpadRichTextValue(""),
1146: JGraphpadVertexRenderer.SHAPE_DIAMOND, null));
1147: kit.addTool(createVertexTool(NAME_TRIANGLETOOL,
1148: new JGraphpadRichTextValue(""),
1149: JGraphpadVertexRenderer.SHAPE_TRIANGLE, null));
1150: kit.addTool(createVertexTool(NAME_CYLINDERTOOL,
1151: new JGraphpadRichTextValue(""),
1152: JGraphpadVertexRenderer.SHAPE_CYLINDER, null));
1153: // Uses default image resource from classpath
1154: JGraphpadImageIcon icon = new JGraphpadImageIcon(
1155: "/com/jgraph/pad/images/noimage.gif");
1156: kit.addTool(createVertexTool(NAME_IMAGETOOL, "",
1157: JGraphpadVertexRenderer.SHAPE_RECTANGLE, icon));
1158: kit.addTool(createEdgeTool(NAME_EDGETOOL,
1159: new JGraphpadRichTextValue(""), null));
1160: kit.addTool(createEdgeTool(NAME_ORTHOGONALEDGETOOL,
1161: new JGraphpadRichTextValue(""),
1162: GraphConstants.ROUTING_SIMPLE));
1163: }
1164:
1165: /**
1166: * Invokes
1167: * {@link #createVertexTool(String, Object, int, ImageIcon, boolean)} with
1168: * post editing set to false.
1169: */
1170: protected JGraphpadVertexTool createVertexTool(String name,
1171: Object defaultValue, int shape, ImageIcon icon) {
1172: return createVertexTool(name, defaultValue, shape, icon, false);
1173: }
1174:
1175: /**
1176: * Helper method to create and return a new vertex tool. This uses the
1177: * createVertexUserObject or creates a new {@link JGraphpadRichTextValue}
1178: * object based on <code>isRichText</code> as the user object, which it
1179: * passes to createVertex to create the graph cell. The method sets the
1180: * shape on the created graph cell, and returns a new JGraphpadVertexTool
1181: * with the specified name.
1182: *
1183: * @param name
1184: * The name of the tool to be created.
1185: * @param defaultValue
1186: * The defaultValue for the vertices that this tool creates.
1187: * @param shape
1188: * The shape for the vertices that this tool creates. Use -1 for
1189: * no border.
1190: * @param icon
1191: * The icon for the vertices that this tool creates.
1192: * @param postEdit
1193: * If in-place editing should be triggered after inserting the
1194: * cell.
1195: * @return Returns a new vertex tool.
1196: *
1197: * @see JGraphpadRichTextValue
1198: * @see #createVertexUserObject(Object)
1199: * @see #createVertex(Object)
1200: * @see JGraphpadGraphConstants#setVertexShape(Map, int)
1201: * @see JGraphpadVertexTool
1202: */
1203: protected JGraphpadVertexTool createVertexTool(String name,
1204: Object defaultValue, int shape, ImageIcon icon,
1205: boolean postEdit) {
1206: Object userObject = createVertexUserObject(defaultValue);
1207: GraphCell vertex = createVertex(userObject);
1208: if (shape >= 0) {
1209: JGraphpadGraphConstants.setVertexShape(vertex
1210: .getAttributes(), shape);
1211: } else {
1212: vertex.getAttributes().remove(GraphConstants.BORDERCOLOR);
1213: vertex.getAttributes().remove(GraphConstants.BORDER);
1214: }
1215: if (icon != null) {
1216: GraphConstants.setIcon(vertex.getAttributes(), icon);
1217: }
1218: if (postEdit) {
1219: return new JGraphpadVertexTool(name, vertex) {
1220: protected void execute(JGraph graph, Object cell) {
1221: super .execute(graph, cell);
1222: graph.startEditingAtCell(cell);
1223: }
1224: };
1225: } else {
1226: return new JGraphpadVertexTool(name, vertex);
1227: }
1228: }
1229:
1230: /**
1231: * Helper method to create and return a new edge tool. This passes the
1232: * return value of createEdgeUserObject to createEdge to create the graph
1233: * cell. The method sets the routing on the created graph cell if it is not
1234: * <code>null</code>, and returns a new JGraphpadEdgeTool with the
1235: * specified name.
1236: *
1237: * @param name
1238: * The name of the tool to be created.
1239: * @param defaultValue
1240: * The defaultValue for the edges that this tool creates.
1241: * @param routing
1242: * The routing for the edges that this tool creates.
1243: * @return Returns a new edge tool.
1244: *
1245: * @see #createEdgeUserObject(Object)
1246: * @see #createEdge(Object)
1247: * @see GraphConstants#setRouting(Map, Edge.Routing)
1248: * @see JGraphpadEdgeTool
1249: */
1250: protected JGraphpadEdgeTool createEdgeTool(String name,
1251: Object defaultValue, Edge.Routing routing) {
1252: DefaultEdge edge = createEdge(createEdgeUserObject(defaultValue));
1253: if (routing != null)
1254: GraphConstants.setRouting(edge.getAttributes(), routing);
1255: return new JGraphpadEdgeTool(name, edge);
1256: }
1257:
1258: /**
1259: * Constructs a default factory for new editors. This implementation
1260: * constructs a new instance of JGraphEditorFactory and overrides
1261: * {@link JGraphEditorFactory#createGraph(GraphLayoutCache)} to call
1262: * {@link #createGraph(JGraphEditor, GraphLayoutCache)} and configures the
1263: * factory using
1264: * {@link #configureFactory(JGraphEditor, JGraphEditorFactory)}.
1265: *
1266: * @param editor
1267: * The editor for which to create an editor factory.
1268: * @return Returns a configured editor factory for the specified editor.
1269: */
1270: protected JGraphEditorFactory createFactory(
1271: final JGraphEditor editor) {
1272: JGraphEditorFactory factory = new JGraphEditorFactory(editor
1273: .getKit()) {
1274: public JGraph createGraph(GraphLayoutCache graphLayoutCache) {
1275: return JGraphpad.this .createGraph(editor,
1276: graphLayoutCache);
1277: }
1278: };
1279: configureFactory(editor, factory);
1280: return factory;
1281: }
1282:
1283: /**
1284: * Hook for subclassers to configure new editor factories. This
1285: * implementation adds various following factory methods.
1286: *
1287: * @param editor
1288: * The editor to create the factory methods for.
1289: * @param factory
1290: * The factory to be configured.
1291: */
1292: protected void configureFactory(JGraphEditor editor,
1293: JGraphEditorFactory factory) {
1294:
1295: // Adds the factory methods from the Editor Framework
1296: factory.addMethod(new JGraphEditorNavigator.FactoryMethod(
1297: editor));
1298: factory
1299: .addMethod(new JGraphEditorComboBox.BorderComboFactoryMethod());
1300: factory
1301: .addMethod(new JGraphEditorComboBox.LineDecorationComboFactoryMethod());
1302: factory
1303: .addMethod(new JGraphEditorComboBox.LineWidthComboFactoryMethod());
1304: factory
1305: .addMethod(new JGraphEditorComboBox.DashPatternComboFactoryMethod());
1306:
1307: // Adds custom factory methods
1308: factory.addMethod(new JGraphpadPane.FactoryMethod(editor));
1309: factory
1310: .addMethod(new JGraphpadPane.LeftTabFactoryMethod(
1311: editor));
1312: factory.addMethod(new JGraphpadPane.BottomTabFactoryMethod(
1313: editor));
1314: factory
1315: .addMethod(new JGraphpadComboBox.VertexShapeComboFactoryMethod());
1316: factory
1317: .addMethod(new JGraphpadComboBox.ColorComboFactoryMethod());
1318: factory
1319: .addMethod(new JGraphpadComboBox.ColorComboFactoryMethod(
1320: METHOD_CREATELINECOLORCOMBO,
1321: JGraphpadComboBox.TYPE_LINECOLOR));
1322: factory
1323: .addMethod(new JGraphpadComboBox.ColorComboFactoryMethod(
1324: METHOD_CREATEGRADIENTCOMBO,
1325: JGraphpadComboBox.TYPE_GRADIENT));
1326: factory
1327: .addMethod(new JGraphpadLibraryPane.FactoryMethod(
1328: editor));
1329: factory
1330: .addMethod(new JGraphpadWindowMenu.FactoryMethod(editor));
1331: factory.addMethod(new JGraphpadOpenRecentMenu(editor));
1332: factory.addMethod(new JGraphpadConsole.FactoryMethod());
1333: factory.addMethod(new JGraphpadStatusBar.FactoryMethod(editor));
1334: }
1335:
1336: /**
1337: * Hook for subclassers to create plugins for a new editor. This
1338: * implementation creates the {@link #defaultPlugins}. This method is
1339: * called from {@link #configureEditor(JGraphEditor, Map)}.
1340: *
1341: * @param editor
1342: * The editor for which to create the plugins.
1343: */
1344: protected void createPlugins(JGraphEditor editor) {
1345: if (defaultPlugins != null) {
1346: for (int i = 0; i < defaultPlugins.length; i++) {
1347: try {
1348: System.out.print("Loading " + defaultPlugins[i]
1349: + "... ");
1350: JGraphEditorPlugin plugin = (JGraphEditorPlugin) Class
1351: .forName(defaultPlugins[i]).newInstance();
1352: plugin.initialize(editor, null);
1353: System.out.println("Done");
1354: } catch (Throwable e) {
1355: System.out.println("Not Available");
1356: }
1357: }
1358: }
1359: }
1360:
1361: //
1362: // Custom graph and factory methods
1363: //
1364:
1365: /**
1366: * Hook for subclassers to provide a custom graph for the user interface.
1367: * This method is invoked by the default custom factory returned by the
1368: * createEditorFactory method. It invokes
1369: * {@link #configureGraph(JGraphEditor, JGraph)} to configure the new graph
1370: * instance.
1371: *
1372: * @see #createFactory(JGraphEditor)
1373: */
1374: protected JGraph createGraph(JGraphEditor editor,
1375: GraphLayoutCache graphLayoutCache) {
1376: JGraph graph = new JGraphpadGraph(graphLayoutCache);
1377: configureGraph(editor, graph);
1378: return graph;
1379: }
1380:
1381: /**
1382: * Hook for subclassers to configure a new graph. This implementation adds a
1383: * {@link JGraphpadTransferHandler} and {@link JGraphpadMarqueeHandler} to
1384: * the instance. (Note: {@link #createVertex()} is used to create the
1385: * prototype cell for the transfer handler.)
1386: *
1387: * @param graph
1388: * The graph to be configured
1389: */
1390: protected void configureGraph(JGraphEditor editor, JGraph graph) {
1391: ToolTipManager.sharedInstance().registerComponent(graph);
1392: graph.setTransferHandler(new JGraphpadTransferHandler(
1393: createVertex()));
1394: graph.setMarqueeHandler(new JGraphpadMarqueeHandler(editor));
1395: graph.setInvokesStopCellEditing(true);
1396: graph.setJumpToDefaultPort(true);
1397: graph.setMoveOutOfGroups(true);
1398: graph.setMoveIntoGroups(true);
1399: graph.setDragEnabled(true);
1400: graph.setCloneable(true);
1401: graph.setOpaque(false);
1402: }
1403:
1404: //
1405: // Custom Cells (Vertices, Groups, Edges & Ports) and User Objects
1406: //
1407:
1408: /**
1409: * Hook for subclassers to construct vertices with default user objects.
1410: * This implementation invokes {@link #createVertexUserObject(Object)}and
1411: * passes the return value to {@link #createVertex(Object)}.
1412: *
1413: * @return Returns a new vertex with a default user object.
1414: */
1415: public GraphCell createVertex() {
1416: return createVertex(createVertexUserObject(null));
1417: }
1418:
1419: /**
1420: * Returns a new DefaultGraphCell containing the specified user object. This
1421: * implementation uses {@link #createAttributeMap()}to create the map that
1422: * holds the attributes for the new vertex, and
1423: * {@link #configureVertex(GraphCell)}to configure the vertex.
1424: *
1425: * @param userObj
1426: * The user object that the vertex should contain.
1427: * @return Returns a new vertex.
1428: */
1429: public GraphCell createVertex(Object userObj) {
1430: DefaultGraphCell vertex = new DefaultGraphCell(userObj,
1431: createAttributeMap());
1432: configureVertex(vertex);
1433: return vertex;
1434: }
1435:
1436: /**
1437: * Hook for subclassers to configure the specified vertex. This
1438: * implementation sets the {@link #defaultBorderColor}and adds ports if the
1439: * vertex implements the {@link MutableTreeNode}interface.
1440: *
1441: * @param vertex
1442: * The vertex to be configured.
1443: *
1444: * @see #addPorts(MutableTreeNode, Point2D[])
1445: */
1446: protected void configureVertex(GraphCell vertex) {
1447: AttributeMap attributes = vertex.getAttributes();
1448: if (defaultBorderColor != null) {
1449: GraphConstants.setOpaque(attributes, false);
1450: GraphConstants.setBorderColor(attributes,
1451: defaultBorderColor);
1452: GraphConstants.setGroupOpaque(attributes, false);
1453: JGraphpadGraphConstants.setGroupResize(attributes, true);
1454: JGraphpadGraphConstants.setInset(attributes, 4);
1455: }
1456: if (vertex instanceof MutableTreeNode)
1457: addPorts((MutableTreeNode) vertex, defaultPortLocations);
1458: }
1459:
1460: /**
1461: * Hook for subclassers to construct groups with default user objects. This
1462: * implementation invokes {@link #createVertexUserObject(Object)}and passes
1463: * the return value to {@link #createGroup(Object)}.
1464: *
1465: * @return Returns a new group with a default user object.
1466: */
1467: public GraphCell createGroup() {
1468: return createVertex(createVertexUserObject(null));
1469: }
1470:
1471: /**
1472: * Returns a new DefaultGraphCell containing the specified user object. This
1473: * implementation uses {@link #createAttributeMap()}to create the map that
1474: * holds the attributes for the new vertex, and
1475: * {@link #configureVertex(GraphCell)}to configure the vertex.
1476: *
1477: * @param userObj
1478: * The user object that the group should contain.
1479: * @return Returns a new group.
1480: */
1481: public GraphCell createGroup(Object userObj) {
1482: DefaultGraphCell vertex = new DefaultGraphCell(userObj,
1483: createAttributeMap());
1484: configureVertex(vertex);
1485: return vertex;
1486: }
1487:
1488: /**
1489: * Adds ports to <code>parent</code> using <code>offsets</code> as the
1490: * port relative offsets. The method uses
1491: * {@link #createPortUserObject(Object)},
1492: * {@link #createPort(MutableTreeNode, Object)}and
1493: * {@link #configurePort(GraphCell, Point2D)}to create the ports and their
1494: * user objects, configure them and add them to the parent.
1495: *
1496: * @param parent
1497: * The parent to add the ports to.
1498: * @param offsets
1499: * The points defining the port locations.
1500: */
1501: protected void addPorts(MutableTreeNode parent, Point2D[] offsets) {
1502: for (int i = 0; i < offsets.length; i++) {
1503: GraphCell port = createPort(parent,
1504: createPortUserObject(null));
1505: configurePort(port, offsets[i]);
1506: }
1507: }
1508:
1509: /**
1510: * Creates a port containing the specified user object and adds it to
1511: * <code>parent</code>.
1512: *
1513: * @param userObject
1514: * The user object that the port should contain.
1515: * @return Returns a new port.
1516: */
1517: public GraphCell createPort(MutableTreeNode parent,
1518: Object userObject) {
1519: DefaultGraphCell port = new DefaultPort(userObject);
1520: parent.insert(port, parent.getChildCount());
1521: port.setParent(parent);
1522: return port;
1523: }
1524:
1525: /**
1526: * Hook for subclassers to configure the specified port using the
1527: * <code>offset</code> as the relative location.
1528: *
1529: * @param port
1530: * The port to be configured.
1531: * @param offset
1532: * The relative offset of the port.
1533: */
1534: public void configurePort(GraphCell port, Point2D offset) {
1535: AttributeMap map = port.getAttributes();
1536: if (offset != null)
1537: GraphConstants.setOffset(map, offset);
1538: }
1539:
1540: /**
1541: * Hook for subclassers to construct edgges with default user objects. This
1542: * implementation invokes {@link #createEdgeUserObject(Object)}and passes
1543: * the return value to {@link #createEdge(Object)}.
1544: *
1545: * @return Returns a new edge with a default user object.
1546: */
1547: public DefaultEdge createEdge() {
1548: return createEdge(createEdgeUserObject(null));
1549: }
1550:
1551: /**
1552: * Returns a new DefaultEdge containing the specified user object. This
1553: * implementation uses {@link #createAttributeMap()}to create the map that
1554: * holds the attributes for the new edge and
1555: * {@link #configureEdge(GraphCell)}to configure the edge.
1556: *
1557: * @param userObj
1558: * The user object that the edge should contain.
1559: * @return Returns a new edge.
1560: */
1561: public DefaultEdge createEdge(Object userObj) {
1562: DefaultEdge edge = new DefaultEdge(userObj,
1563: createAttributeMap());
1564: configureEdge(edge);
1565: return edge;
1566: }
1567:
1568: /**
1569: * Hook for subclassers to configure the specified edge. This implementation
1570: * sets the {@link #defaultEdgeFont},{@link #defaultEndDecoration}and
1571: * {@link #defaultBeginDecoration}.
1572: *
1573: * @param edge
1574: * The edge to be configured.
1575: */
1576: protected void configureEdge(GraphCell edge) {
1577: AttributeMap attributes = edge.getAttributes();
1578: if (defaultEdgeFont != null)
1579: GraphConstants.setFont(attributes, defaultEdgeFont);
1580: if (defaultEndDecoration != GraphConstants.ARROW_NONE)
1581: GraphConstants.setLineEnd(attributes, defaultEndDecoration);
1582: if (defaultBeginDecoration != GraphConstants.ARROW_NONE)
1583: GraphConstants.setLineBegin(attributes,
1584: defaultBeginDecoration);
1585: if (edge instanceof MutableTreeNode)
1586: addPorts((MutableTreeNode) edge, defaultPortLocations);
1587: }
1588:
1589: /**
1590: * Hook for subclassers to create a user object for edges that contains the
1591: * specified value. This implementation calls
1592: * {@link #createVertexUserObject(Object)}.
1593: *
1594: * @param value
1595: * The value that the user object should contain.
1596: * @return Returns a new user object containing <code>value</code>.
1597: */
1598: protected Object createEdgeUserObject(Object value) {
1599: return createVertexUserObject(value);
1600: }
1601:
1602: /**
1603: * Hook for subclassers to create a user object for ports that contains the
1604: * specified value. This implementation calls
1605: * {@link #createVertexUserObject(Object)}.
1606: *
1607: * @param value
1608: * The value that the user object should contain.
1609: * @return Returns a new user object containing <code>value</code>.
1610: */
1611: protected Object createPortUserObject(Object value) {
1612: return createVertexUserObject(value);
1613: }
1614:
1615: /**
1616: * Returns a new {@link JGraphpadBusinessObject}for the specified value.
1617: * This implementation replaces all <code>null</code> values with an empty
1618: * {@link JGraphpadRichTextValue}.
1619: *
1620: * @param value
1621: * The value that the user object should contain.
1622: * @return Returns a new user object containing <code>value</code>.
1623: *
1624: * @see JGraphpadBusinessObject
1625: */
1626: protected Object createVertexUserObject(Object value) {
1627: return new JGraphpadBusinessObject((value != null) ? value
1628: : new JGraphpadRichTextValue(""));
1629: }
1630:
1631: /**
1632: * Hook for subclassers to construct attribute map for cells. This
1633: * implementation returns a new instance of {@link AttributeMap}.
1634: *
1635: * @return Returns a new attribute map.
1636: */
1637: protected AttributeMap createAttributeMap() {
1638: return new AttributeMap();
1639: }
1640:
1641: /**
1642: * Returns true if the specified filename has an image extension, namely one
1643: * in {@link javax.imageio.ImageIO#getReaderFormatNames()}.
1644: *
1645: * @param filename
1646: * The filename to be checked.
1647: * @return Returns true if the filename is an image file.
1648: */
1649: public static boolean isImage(String filename) {
1650: String[] formats = ImageIO.getReaderFormatNames();
1651: filename = filename.toLowerCase();
1652: for (int j = 0; j < formats.length; j++) {
1653: if (filename.endsWith(formats[j].toLowerCase()))
1654: return true;
1655: }
1656: return false;
1657: }
1658:
1659: /**
1660: * Centers the specified window on the screen, taking into account the
1661: * current size of the window.
1662: *
1663: * @param wnd
1664: * The window to be centered.
1665: */
1666: public static void center(Window wnd) {
1667: Dimension screenSize = Toolkit.getDefaultToolkit()
1668: .getScreenSize();
1669: Dimension frameSize = wnd.getSize();
1670: wnd.setLocation(screenSize.width / 2 - (frameSize.width / 2),
1671: screenSize.height / 2 - (frameSize.height / 2));
1672: }
1673:
1674: //
1675: // Main
1676: //
1677:
1678: /**
1679: * Constructs and displays a new application window.
1680: *
1681: * @param args
1682: * The command line arguments to pass to the application.
1683: */
1684: public static void main(String[] args) {
1685: try {
1686: Map arguments = new Hashtable();
1687: List files = new LinkedList();
1688: if (args != null) {
1689:
1690: String key = null;
1691: String arg = null;
1692: for (int i = 0; i < args.length; i++) {
1693: arg = args[i];
1694: if (arg.startsWith("-")) {
1695: key = arg.substring(1);
1696: arguments.put(key, "");
1697: if (key.equals(ARG_VERSION))
1698: key = null;
1699: } else if (key != null) {
1700: arguments.put(key, arg);
1701: key = null;
1702: } else if (arg.length() > 0) {
1703: files.add(arg);
1704: }
1705: }
1706: }
1707: if (arguments.containsKey("?")
1708: || arguments.containsKey("help")
1709: || arguments.containsKey("-?")
1710: || arguments.containsKey("-help")) {
1711: System.out.println(USAGE);
1712: } else if (arguments.containsKey(ARG_VERSION)) {
1713: System.out.println(JGraphpad.VERSION + "\n"
1714: + JGraph.VERSION);
1715: } else {
1716: // in plugin init
1717: new JGraphpad().createApplication(files, arguments);
1718: }
1719: } catch (Exception e) {
1720: e.printStackTrace();
1721:
1722: // Terminates abnormally
1723: System.exit(1);
1724: }
1725: }
1726:
1727: /**
1728: * Defines the usage information (use --help), see
1729: * http://java.sun.com/docs/books/tutorial/uiswing/misc/plaf.html#programmatic
1730: * on setting the look and feel.
1731: */
1732: public static final String USAGE = "Usage: java com.jgraph.JGraphpad [OPTION]...\n"
1733: + " -"
1734: + ARG_SYSTEMLOOKANDFEEL
1735: + " use system look and feel\n"
1736: + ARG_JGOODIESLOOKANDFEEL
1737: + " jgoodies look and feel\n"
1738: + " -"
1739: + ARG_VERSION
1740: + " print version\n"
1741: + "\nTip: java -Dswing.defaultlaf=com.jgoodies.looks.plastic.Plastic3DLookAndFeel\n"
1742: + "Exit status is 0 if OK, 1 if minor problems, 2 if serious trouble.\n"
1743: + "\nReport bugs via http://www.jgraph.com/tracker\n\n";
1744:
1745: }
|