libjgraph-java-5.12.4.2+dfsg.orig/0000775000175000017500000000000011524541167016004 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/Installer$3.class0000644000175000017500000000121210421406442021100 0ustar gregoagregoa.+       val$frameLjavax/swing/JFrame; Synthetic(Ljavax/swing/JFrame;)VCodeactionPerformed(Ljava/awt/event/ActionEvent;)V ! "# $#% &'( )* Installer$3 InnerClassesjava/lang/Objectjava/awt/event/ActionListener()V InstallerTXT_NACKLjava/lang/String;TXT_INSTALLATIONjavax/swing/JOptionPaneshowMessageDialog<(Ljava/awt/Component;Ljava/lang/Object;Ljava/lang/String;I)Vjava/lang/Systemexit(I)V      **+* libjgraph-java-5.12.4.2+dfsg.orig/META-INF/0000755000175000017500000000000011256667042017145 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/META-INF/MANIFEST.MF0000644000175000017500000000022211256667040020571 0ustar gregoagregoaManifest-Version: 1.0 Ant-Version: Apache Ant 1.6.5 Created-By: 1.4.2_17-b06 (Sun Microsystems Inc.) Main-Class: Installer Built-By: david libjgraph-java-5.12.4.2+dfsg.orig/WHATSNEW0000644000175000017500000007701211256667036017202 0ustar gregoagregoaChanges from JGraph 5.10.0.0 beta 1 to JGraph 5.10.0.0 beta 2 ============================================================= This release is a bug fix release resolving all issues reported with the Beta 1 release to date. Changes from JGraph 5.9.2.0 to JGraph 5.10.0.0 beta 1 ===================================================== This release makes a number of, currently, experimental changes that mean it is not assured to be stable and that Java 1.3 can no longer be used. The double buffering support has been developed so that the buffered image is retained constantly making panning and scroll much faster and reducing the time taken to start a drag or resize in a large graph. The option to request that graphics hardware acceleration is used for the double buffering is available, but switched off by default. Also added is the concept of a buffered overlay image for transparent drawing on the foreground without having to redraw the static buffered graph. Changes from JGraph 5.9.1.0 to JGraph 5.9.2.0 ============================================= In addition to supporting an image on the background, a Component may now be drawn as the JGraph background for applications such as mapping. Some performance improvements have been made in the GraphLayoutCache relating to making cells invisible. In the model there have also been performance improvements around the functionality that removes cells. Changes from JGraph 5.9.0.0 to JGraph 5.9.1.0 ============================================= This release adds support for a background image that is refreshed during all necessary drag, resize, and model change events. Changes from JGraph 5.8.3.1 to JGraph 5.9.0.0 ============================================= Double buffering in JGraph previously consisted of off-screen buffers created in the root handles and size handles to buffer drag-and-resize previews on a per-case basis. However, the review buffered the entire graph image, so recreating the buffer on each operation was expensive in CPU terms. The off-screen buffer and off-screen graphics that are the graphics object of the buffer have been moved to the JGraph class. Also, the cachedLabelBounds and cachedExtraLabelBounds variables in EdgeView were never set (since getLabelBounds() and getExtraLabelBounds() were never called) and have been removed. Changes from JGraph 5.8.3.0 to JGraph 5.8.3.1 ============================================= A bug has been fixed where removing the user object did not action correctly. This has been fixed with an additional check in handleAttributes() in DefaultGraphModel. Also added is the getInsertionOffset() hook in GraphTransferHandler to enable positioning of cloned cells without copying the entire importData() method. Finally, a possible NPE in EdgeRenderer.getLabelPosition() has been enclosed in all the necessary null checks. Changes from JGraph 5.8.0.0 to JGraph 5.8.1.0 ============================================= Serialization of the WeakHashMap in the GraphLayoutCache has been resolved by adding custom write and read methods. Scaling the graph now is centered correctly and a new setScale method is available to zoom to a custom point. The edgeLabelMovable switch was added to the JGraph class to allow dragging of edge labels to be optionally disabled. There were a number of other minor bug fixes. Changes from JGraph 5.7.4.8 to JGraph 5.8.0.0 ============================================= The graph variable in EdgeRenderer has been replaced with a weak reference to ensure that static edge renderers release their reference to the current graph when all other objects do. Also, an example showing the use of a custom cell view factory and an example showing a selection model being synchronized between a graph and a tree were added to the examples package. Changes from JGraph 5.7.4.6 to JGraph 5.7.4.8 ============================================= This release adds the PREVIEW_EDGE_GROUP global switch to the GraphContext class, which makes the preview of edge with children optional. A CompoundVertexView example has been added to GraphEd to allow groups to be resized independently of their children (thanks Jerry). The graphics object in JGraph.getImage is manually disposed of to clean up resources quicker. Changes from JGraph 5.7.4.5 to JGraph 5.7.4.6 ============================================= A bug fix to a performance improvement made to the default graph model in 5.7.4.5 was required. Fixed ports that connected to floating ports were connecting to the wrong perimeter position on the floating port, this is now resolved. The "port magic" functionality that moves ports to attempt to straighten edges was incorrectly using the mouse position, it now no longer does so. Changes from JGraph 5.7.4.4 to JGraph 5.7.4.5 ============================================= A number of performance improvements were made in specific areas, mostly related to lazy instantiation and correct sizing of collections. addPort() was added to DefaultGraphCell to make creating ports easier. hiddenMapping in the GraphLayoutCache is now a WeakHashMap to avoid any possible memory leaks when cells are deleted. Also, in the GraphLayoutCache a helper method, removeViewLocalAttribute(), assists in the process of removing view local attributes and copying them into the model if required. A version of getPortViewAt with a custom tolerance was added to the JGraph class. Changes from JGraph 5.7.4.3 to JGraph 5.7.4.4 ============================================= Fixes occasional wrong index when adding new control points to edges, fixes selection check uses cell view in mouse handle, adds marquee handler as an optional serializable field to JGraph and the listener list in the layout cache is no longer transient. GraphEd adds new hooks for subclassers and new examples for serialization and XML encoding have been added to the commercial version. Changes from JGraph 5.7.4.2 to JGraph 5.7.4.3 ============================================= A number of changes were made to resolve various serialization issues. The use of transient modifiers was reviewed throughout the code. A problem were undos of cuts of groups could behave incorrectly was corrected. Various hooks for the example code were added. Changes from JGraph 5.7.4.1 to JGraph 5.7.4.2 ============================================= Fixes an issue with artifacts being left on the display when cells are dragged. Changes from JGraph 5.7.4 to JGraph 5.7.4.1 =========================================== An automatic resizing feature has been added so the graph expands as you drag out of the bottom or right-hand edges of a scroll pane. An appropriate flag, autoResizeGraph, to toggle the functionality have been added to the JGraph. A bug in the new collapse/expand example, GraphEdX, was fixed. The JGraph User Manual has received a number of corrections and the sections on XML encoding, image and SVG export and use in a headless environment have been improved. Changes from JGraph 5.7.3.1 to JGraph 5.7.4 =========================================== The DefaultCellViewFactory is now Serializable, so serializing the main part of the core should work correctly. Some example code from JGraph Layout Pro has been moved into the new GraphEdX example, this includes an XML encoding/ decoding example and how to create collapsible groups of cells. The image creation utility method in JGraph has been changed to use print instead of paint to draw graphics, this make certain server-side operations easier and more efficient. Also, a number of minor bugs have been fixed. Changes from JGraph 5.7.3 to JGraph 5.7.3.1 =========================================== The release fixes a critical bug whereby the initialisation ordering of certain static variables was not deterministic and could caused editing to always fail. Changes from JGraph 5.7.2 to JGraph 5.7.3 ========================================= Adds helper methods, fixes problems in headless environments, allows combined insert/removes/edits in DefaultGraphModel, adds switch to disable port magic. Changes from JGraph 5.7.1 to JGraph 5.7.2 ========================================= Fixes handling of connectable attribute for edges, sizeable- & moveable axis for groups. Fixes bugs for group translation and sizing, makes cloneCells more stable and fixes the label position for non-loops with multiple points. Changes from JGraph 5.7 to JGraph 5.7.1 ======================================= Fixes a bug in the attribute map cloning for extra label positions, adds a new helper method to collapse/expand cells, adds automatic resizing and moving of parent cells on collapse and switches to (not) show hidden cells on changes. Changes from JGraph 5.6.3 to JGraph 5.7 ======================================= The routing interface has changed so that the routing methods now return a list of points instead of accepting a list of points as a parameter. The routing of loops has been separated from the non-loop implementation for the default case so that it is easier to create a new routing implementation and inherit the default loop routing algorithm. Children cells moved as part of a collapsed group now are moved when the parent cell is, if required, using the movesChildrenOnExpand flag. Changes from JGraph 5.6.2.1 to JGraph 5.6.3 =========================================== The default vertex and port renderers kept references to the last graph used and this meant that removed JGraph instances could have a static memory footprint. The edge renderer still needs to be removed, however. Edges may now be painted with zero width, so you only see the label. The tolerance in JGraph.getNextViewAt is now scaled according to the current zoom value. Changes from JGraph 5.6.2 to JGraph 5.6.2.1 =========================================== Fixed critical bug in attribute map undo. Changes from JGraph 5.6.1 to JGraph 5.6.2 ========================================== Added User Manual to commercial distribution. Changes from JGraph 5.6 to JGraph 5.6.1 ========================================== getSelectionCellAt was added as a utility method to JGraph and returns the first selected cell whose bounds the parameter point lies within. The moves-into-group functionality has been corrected so it is based on whether the cells bounds is fully within the bounds of the group cell. Extra labels can now be cloned using ctrl-drag and deleted using shift and mouse press. A bounds cloning bug in JGraph.getCellBounds that caused cells to have a large height occasionally was fixed. getPerimeterPoint for an EdgeView now returns the center of the edge to make edge to edge connection easier. A number of other bugs were also fixed. Changes from JGraph 5.5.3 to JGraph 5.6 ========================================== A number of improvements have been made to the expand and collapse functionality. When a group is collapsed edges connecting to now-hidden vertices _visually_ attach to the perimeter point of the first visible parent group. Expanding the group shows the true state of connections again. It is now possible to connect edges directly to other edges. A large number of other bug fixes and functionality improvements were also made, see the ChangeLog for more details. Changes from JGraph 5.5.2 to JGraph 5.5.3: ========================================== The main JGraph class now has switches named moveIntoGroups and moveOutOfGroups that enable drag and drop of cells in and out of group cells. The getImage method was added to make the creation of images of JGraphs easier. A selection of helper methods were added and a number of minor bugs fixed. GraphModel.getValue was added and DefaultGraphModel.getUserObject deprecated. Changes from JGraph 5.5.1 to JGraph 5.5.2: ========================================== Adds a series of constructors and hooks to ease customization, fixes port floating for loops, adds encoding hooks to the default graph model, fixes auto-selection in the examples, adds hooks for vertex rendering and fixes a bug related to finding default ports with overlapping cells. Changes from JGraph 5.5 to JGraph 5.5.1: ========================================== This release makes a number of performance and memory footprint improvements, in particular, in relation to edges. A number of hooks have been added to make it possible to disable various functionality to obtain performance improvements. Various examples and documents were added to the purchased version. Also, a number of minor bugs were fixed and code tidy-ups performed. Changes from JGraph 5.4.7 to JGraph 5.5: ========================================== valueForCellChanged() has been added to the GraphModel interface to enable changing the value of the cell without performing typecasts. Edge labels that cross into negative co-ordinate space are moved into back into the visible area and in-place editing cannot now occur off the screen. Labels on self-references can now be moved. Various casts and type checks were added and various minor bugs fixed. Changes from JGraph 5.4.6 to JGraph 5.4.7: ========================================== Another review of the javadocs was made on the frozen 5.4.x API. Various changes were made to resolve issues with edges whose source and target are ports on the same vertex. Labels on such edges can now be moved and the routing functionality has been corrected. PortView.getLocation now has an additional parameter to support these changes, but no methods have been removed from the API. Changes from JGraph 5.4.5 to JGraph 5.4.6: ========================================== Since the JGraph API has been frozen in the 5.4.x series for stability, the first review of many of the javadocs was undertaken. All warnings were removed and various corrections and updates made. A divide by zero bug was fixed where the label position of an edge is calculated. If the source and target ports were the same port the zero length between the ports went into a calculation as the divisor. This was changed to simply return the label position as being on the port. The helper method GraphLayoutCache.createNestedMap was added and AbstractCellView changed so that it clones cell attributes in the refresh method. Changes from JGraph 5.4.4 to JGraph 5.4.5: ========================================== Fixes major bugs in edge handle for correct handling of model callbacks and placement of labels on scaled graphs, initial label bounds for extra labels, adds a helper method to JGraph and fixes the DefaultEdge.clone method. Changes from JGraph 5.4.3 to JGraph 5.4.4: ========================================== Adds new helper methods in GraphLayoutCache and GraphConstants, improves edge renderer performance, fixes bugs in BasicGraphUI.MouseHandler, GraphEd.connect, adds graph-parameter in BasicMarqueeHandler.paint and overlay. Changes from JGraph 5.4.2 to JGraph 5.4.3: ========================================== Uses new edge labels, reduces memory footprint, fixes bug in layout cache to reload roots, fixes some minor bugs, new cell labels in GraphEd. Changes from JGraph 5.4.1 to JGraph 5.4.2: ========================================== Minor performance ehancements and bug fixes, makes the GraphEd example an applet and adds more hooks. Changes from JGraph 5.4 to JGraph 5.4.1: ======================================== Adds performance improvements, new helper methods and hooks in GraphEd, changes variable namings, uses insets in getPreferredSize, adds default inset in GraphConstants and fixes possible orphan ports and other minor bugs. Changes from JGraph 5.3.2 to JGraph 5.4: ======================================== Adds event notification to GraphLayoutCache, removes user object from attribute maps, adds various helper methods, adds selection and size-control attributes, adds Abbott-bases tests and fixes numerous bugs. Changes from JGraph 5.3 to JGraph 5.3.2: ======================================== Fixes major bugs, improved automatic cell showing/hiding in layout cache, adds more helper methods to model and cache, refactors updateAutoSize and ignores possible NPE in BasicGraphUI.createHandle. Changes from JGraph 5.2.1 to JGraph 5.3: ======================================== Adds helper methods to layout cache and graph model, fixes bugs in splines, edge- & portviews, moves transferhandler to its own class, makes rectangles and points xml encodable, general performance improvements. Changes from JGraph 5.2 to JGraph 5.2.1: ======================================== Adds new switches, re-enables auto selection, objects ready for XML encoding, major bug fixes for partial layout caches, adds new utility methods to layout cache and graph model, minor API changes. Changes from JGraph 5.1 to JGraph 5.2: ====================================== Uses a triple license model (MPL added), has major API changes such as removed dependencies for cell views and layout caches, enhanced setup for extra edge labels, extended observable pattern, new cell view factory, and many bug fixes. Changes from JGraph 5.0.4 to JGraph 5.1: ======================================== Adds multiple edge labels, jump to default port, fixes constrained edge editing, new cell selection, allows edges as groups, unifies parameter scaling, cell & cellview order, adds group border attribute, and various helper methods. Changes from JGraph 5.0.3 to JGraph 5.0.4: ========================================== Keeps user objects and attributes in sync, fixes flickering edge connection, class cast exception in example code, changes default group border to 20px. Changes from JGraph 5.0.2 to JGraph 5.0.3: ========================================== Added opaque groups with group borders, fixed handling of in-place edit and cloning for custom user objects, fixed minor bugs. Changes from JGraph 5.0.1 to JGraph 5.0.2: ========================================== Added edge renderer gradient painting mode, fixed some minor bugs. Changes from JGraph 5.0 to JGraph 5.0.1: ======================================== Added switches for autosize on value change, disable cell selection, dash offset for edges, gradient background for vertices, per-axis relative or absolute port positions, fixed acceptsSource/Target and preview, fixed attribute map storage and transport separation, and cleaned up code and added more hooks for subclassers. Changes from JGraph 4.0 to JGraph 5.0: ====================================== Switched to dual licensing model. Fixed some bugs. Changes from JGraph 3.4.1 to JGraph 4.0: ======================================== - Replaced ValueChangeHandler with GraphLayoutCache.valueForCellChanged - Moved createPoint, createRect to AttributeMap - Added AttributeMap, factory method in GraphModel - Fixed GraphEd add/remove point with shift - Replaced Map with AttributeMap (major API change) Changes from JGraph 3.4 to JGraph 3.4.1: ======================================== Disables some default behaviour, allows spline and bezier edges with an arbitrary number of control points, fixes minor bugs. Changes from JGraph 3.3 to JGraph 3.4: ====================================== Can handle overlapping edges, makes inner handles static, moves some control methods to handles. Minor API changes. Changes from JGraph 3.2 to JGraph 3.3: ====================================== Supports view-local attributes, sizeable in-place editors, sizing and moving along one axis, and offers a series of minor API changes and bug fixes. Includes latest examples. Changes from JGraph 3.1 to JGraph 3.2: ====================================== Can draw labels along edges, uses double precision coordinates, fixes handling of negative coordinates and in-place editing, adds map to default graph model. Changes from JGraph 3.0 to JGraph 3.1: ====================================== Ant-based build environment. BSD-style license. A series of bug fixes and minor API changes. Changes from JGraph 2.2.2 to JGraph 3.0: ======================================== This is a package renaming change and directory cleanup. More changes for 3.x are planned. Changes from JGraph 2.2.1 to JGraph 2.2.2: ========================================== This release fixes major bugs in the clipboard, as well as the ordering of cells in the clipboard, and adds a hook in the EdgeRenderer class. Changes from JGraph 2.2 to JGraph 2.2.1: ======================================== This is a bug fix release that fixes a bug in the clipboard. Some minor API changes go with this release.. Changes from JGraph 2.1.1 to JGraph 2.2: ======================================== Painting performance improvements, cleaned up API, removed dependency between ParentMap and GraphModel, added clone method, added methods to access the previous state of the model after a change, new vertex renderer constructor, and numerous bug fixes. Changes from JGraph 2.1 to 2.1.1 ================================ Minor changes such as bean property methods and empty constructor added to ConnectionSet for XMLEncoding, added Null-Check to isCellEditable, fixed shared points bug in DefaultEdge, disable autosize during in-place edit. Changes from JGraph 2.0 to 2.1 ============================== Moved all GraphCell dependencies to the GraphModel, added multiple grid styles, new font attribute, and the API is now ready for combined port/vertex cells. Bug fixes include improved live-preview, better zoom/grid integration, handling of negative coordinates, and minor bug fixes. Java 1.4 is the default version. Changes from JGraph 1.0.7 to 2.0 ================================ This release has a new API with new features. The get an overview of the most important changes and how they affect your code you should read the "New Features" and "Upgrading from 1.0.x" chapters. New Features: ------------- * Cell Visiblity: The GraphView, which is now called GraphLayoutCache offers a set of methods to show/hide cells, namely the setVisible method. This method fully supports undo/redo and in contrast to the earlier implementation using the visible attribute, this implementation does not allocate memory for invisible cells in a cache. The GraphLayoutCache contains built-in functions to automatically show edges between visible cells. When inserting and editing cells in a partial view, that is, one that does not show all cells of the model, then you should use the GraphLayoutCache's insert and edit method to allow the view to make the cells automatically visible (this way, the cell becomes visible in the view where is was inserted. The view (aka. cache) can be set up to insert new edges automatically in all views where the source and target ports are visible. Use the following GraphLayoutCache members to control automatic display: - showAllEdgesForVisibleVertices: Show edges on insert or change if their source and target vertex is visible - showEdgesOnShow: Show edges if their source and target vertex is displayed - hideEdgesOnHide: Hide edges if their source and target vertex is hidden - hideEdgesOnBecomeInvisible: Hide edges if their source and target becomes invisible (for example after removal) These members are set to true by default. Note: This feature allows to implement collapse/expand of groups. * View-Local Attributes: In the 1.0.x version, the GraphModel decided if it was an attribute store by use of the isAttributeStore method. In 2.0.x, it is the view who decides which attributes are view-local and which are global. Also, the edit method can now be called with a single attribute map that uses cells as the keys. The GraphLayoutCache provides a hook for subclassers, namely the createLocalEdit method to implement attribute splitting. The CellView's implementation was changed to override the global attributes with the view-local ones for. To enable this feature the code must use the GraphLayoutCache's edit, insert and remove methods instead of the model's. The view analyzes these method arguments, and extracts the view-local attributes before updating the model for the change. Note: This new feature allows more fine-grained control of view-local and global attributes. By removing the isAttributeStore and isOrdered methods it is not any more required that the model is aware of this feature. The ROUTING and POINTS attributes should be kept at the same location. Tip: Use GraphLayoutCache.rememberCellViews to control if view-local attributes should be remembered for hidden cells. * Edge Routing & Self References: A new attribute ROUTING was added that is interpreted by the EdgeView. The values for this key must implement the Edge.Routing interface, which extends the Serializable interface. The GraphConstants class provides a static instance ROUTING_SIMPLE of the DefaultEdge.DefaultRouting class. As the name implies this is the default implementation of the Routing interface. It allows simple routing and handles self-references. * Smaller Command History: The command history does only store the actual change, resulting in a smaller command history. In earlier versions, the complete state of all changed cells and views was stored. API Changes: ------------ (Only the most important changes are listed below.) Removed Members: * GraphConstants.createPropertyMap * GraphModel.isOrdered * GraphModel.isAttributeStore * GraphConstants.is/setVisible * CellRenderer.supportsAttribute * AbstractCellView.isControlAttribute New members: * BasicGraphUI.is/setSnapSelectedView A view under mousepointer may be snapped to the grid lines or moved by a constant increment during a drag operation based on this value. * Edge.Routing * GraphConstants.ROUTING_SIMPLE * DefaultEdge.DefaultRouting class * GraphConstants.get/setRouting * GraphConstants.createAttributes This is a helper method that may be used to create an attribute map * GraphLayoutCache.reset, is/setVisible, partial, ordered * DefaultGraphModel.getSource/TargetVertex Static helper method that calls getParent(getSource/Target) on the model * GraphLayoutCache.hiddenSet Used to remember local attributes of visible cells * JGraph.VERSION (use JGraph -version) * VertexRenderer.paintSelectionBorder Provided for subclassers to control drawing the selection border * VertexRenderer.is/setHideGroups Controls whether groups are fully painted by the renderer * Provide a handleEditTrigger Hook in BasicGraphUI.MouseHandler Changes: * GraphView renamed to GraphLayoutCache In accordance with JTree.TreeLayoutCache * JGraph.get/setView renamed to JGraph.get/setGraphLayoutCache To reflect this change * JGraph.SnapSize renamed to Tolerance (including getter, setter etc.) * JGraph.convertValueToString support view-local values The CellView decides what label to display * DefaultGraphCell.setAttributes renamed to changeAttributes Setter and getter methods should be used for bean properties. * Port.add/remove renamed to Port.addEdge/removeEdge (see http://sourceforge.net/forum/forum.php?thread_id=773281&forum_id=140880) * GraphConstants.ARROW prefix added to arrow styles * GraphConstants.STYLE prefix added to line styles * getPerimterPoint method was moved to the renderer * GraphViewChange.getAttributeMap renamed to getAttributes * GraphModelChange.getStoredAttributeMap renamed to getPreviousAttributes * DefaultGraphModel.handlePropertyMap renamed to handleAttributes * importData may return false to signal sender to not remove cells (Override importDataImpl instead of importData in GraphTransferHandler) * DefaultGraphSelectionModel.isChildrenSelectable now supports a cell argument * GraphLayoutCache.toBack/toFront take cells as arguments * GraphModel.insert and edit Argument order unified Fixed bugs: ----------- * Rounding erros on large zoom levels removed: The BasicGraphUI.RootHandles implementation did not work property for large zoom levels. Thanks to Jenya for the patch! * The maximum number of edges to paint in live-preview is now MAXCELLS * EdgeHandle does now support the SHIFT-key for constrained moving * Added some accessor methods to BasicMarqueeHandler for subclassers * BasicGraphUI.PropertyChangeListener calls repaint after GraphLayoutCache change * JGraph.setGraphLayoutCache checks and updates the cache's model if necessary * Vertices are not removed when their last port is removed dynamically Groups are still automatically removed when their last child cell is removed * EdgeRenderer now cached the created Shape in the corresponding EdgeView * BasicGraphUI.isDescendant uses GraphModel interface to return its data * Live preview during real DND (only for JDK < 1.4.0, see BasicGraphUI line 28) * DefaultEdge.constructor offers user object und boolean (allows children) * JGraph.disconnectOnMove must check the CONNECTABLE/DISCONNECTABLE attributes * Vertex, Port and Edge may carry the CONNECTABLE/DISCONNECTABLE attributes * NPE on edge change when new the port was not visible in other view removed * Groups may contain ports (Concurrency side-effects in EdgeRenderer.createShape) * Cache the bounds property of groups and recompute on change of children only * GetBounds-Infinite-Loop solved by exluding childedges between childs to group * Propagate CellView.update to parent instead of child (bubble up) * TransferHandler now supports move and DnD across multiple views, models and JVMS * Clone edges when reconnected and the Control key is pressed * ExecutableGraphChange interface added to execute all changes in model (Delegation) Also the GraphLayoutCache's changes are executed and dispatched by the model * In-place editing from empty to non-empty content is incorrectly undone * Removed in-place manipulation of BOUNDS-attribute in SizeHandle and RootHandle * Changed execution order of compound edits: first model then args in-order * Use GraphModel interface in DefaultGraphModel.getRoots only (no typecast) * GraphLayoutCache.getMapping may return null (if cell is not visible) * GraphUndoManager.redo throws CannotRedoException instead of CannotUndoException * Handles.initOffscreen is protected instead of package private Other changes: -------------- * To-do list added: A new to-do list for JGraph was added. The list is intended for developers as a roadmap for additional features, examples and research activities. * Version Naming Changed: The new version naming uses Swiss cities, where each City denotes a major release of the API. This means, all JGraph 2.0.x are said to be based on the Geneva API. * New JGraphpad Administrator: Please welcome Van Woods as the new JGraphpad administrator! See http://sourceforge.net/forum/message.php?msg_id=1833812 * JGraphpad updated: JGraphpad ported to JGraph 2.0. Release date is later this week. * Examples updated: All example have been updated to use JGraph 2.0. Currently, the examples are only available for download. The CVS repository still contains the old code. * Website updated: Many minor changes and updates on the website. Main changes include an optional license fee and donate button, a new banner and some additional links in the showcase etc. Upgrading from 1.0.x: --------------------- Here is an example. Please note the following in the example below: => We use Cells as keys to create the nested map instead of CellViews => We use the GraphLayoutCache's edit method instead of the model's => The order of the edit/insert method's arguments has changed => We use graph.getLayoutCache instead of graph.getView * JGraph 1.0.x: if (!graph.getModel().isAttributeStore()) cells = getView().getMapping(cells); Map viewMap = new Hashtable(); for (int i = 0; i < cells.length; i++) viewMap.put(cells[i], GraphConstants.cloneMap(map)); if (graph.getModel().isAttributeStore()) graph.getModel().edit(null, viewMap, null, null); else graph.getView().edit(viewMap); * JGraph 2.0.x (Geneva Code): Map nested = new Hashtable(); for (int i = 0; i < cells.length; i++) nested.put(cells[i], GraphConstants.cloneMap(map)); graph.getGraphLayoutCache().edit(nested, null, null, null); libjgraph-java-5.12.4.2+dfsg.orig/Installer$1.class0000644000175000017500000000114110421406442021077 0ustar gregoagregoa.&      val$installLjavax/swing/JButton; Synthetic val$agreeLjavax/swing/JRadioButton;2(Ljavax/swing/JButton;Ljavax/swing/JRadioButton;)VCode stateChanged"(Ljavax/swing/event/ChangeEvent;)V   !"# $% Installer$1 InnerClassesjava/lang/Object javax/swing/event/ChangeListener()Vjavax/swing/JRadioButton isSelected()Zjavax/swing/JButton setEnabled(Z)V     **+*,** libjgraph-java-5.12.4.2+dfsg.orig/src/0000755000175000017500000000000011256667036016577 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/src/org/0000755000175000017500000000000011256667036017366 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/0000755000175000017500000000000011256667036020641 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/util/0000755000175000017500000000000011256667036021616 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/util/Spline.java0000644000175000017500000001070411256667036023715 0ustar gregoagregoa/* This code is PUBLIC DOMAIN */ package org.jgraph.util; import java.util.Arrays; /** * Interpolates given values by B-Splines. * * @author krueger */ public class Spline { private double[] xx; private double[] yy; private double[] a; private double[] b; private double[] c; private double[] d; /** tracks the last index found since that is mostly commonly the next one used */ private int storageIndex = 0; /** * Creates a new Spline. * @param xx * @param yy */ public Spline(double[] xx, double[] yy) { setValues(xx, yy); } /** * Set values for this Spline. * @param xx * @param yy */ public void setValues(double[] xx, double[] yy) { this.xx = xx; this.yy = yy; if (xx.length > 1) { calculateCoefficients(); } } /** * Returns an interpolated value. * @param x * @return the interpolated value */ public double getValue(double x) { if (xx.length == 0) { return Double.NaN; } if (xx.length == 1) { if (xx[0] == x) { return yy[0]; } else { return Double.NaN; } } int index = Arrays.binarySearch(xx, x); if (index > 0) { return yy[index]; } index = - (index + 1) - 1; //TODO linear interpolation or extrapolation if (index < 0) { return yy[0]; } return a[index] + b[index] * (x - xx[index]) + c[index] * Math.pow(x - xx[index], 2) + d[index] * Math.pow(x - xx[index], 3); } /** * Returns an interpolated value. To be used when a long sequence of values * are required in order, but ensure checkValues() is called beforehand to * ensure the boundary checks from getValue() are made * @param x * @return the interpolated value */ public double getFastValue(double x) { // Fast check to see if previous index is still valid if (storageIndex > -1 && storageIndex < xx.length-1 && x > xx[storageIndex] && x < xx[storageIndex + 1]) { } else { int index = Arrays.binarySearch(xx, x); if (index > 0) { return yy[index]; } index = - (index + 1) - 1; storageIndex = index; } //TODO linear interpolation or extrapolation if (storageIndex < 0) { return yy[0]; } double value = x - xx[storageIndex]; return a[storageIndex] + b[storageIndex] * value + c[storageIndex] * (value * value) + d[storageIndex] * (value * value * value); } /** * Used to check the correctness of this spline */ public boolean checkValues() { if (xx.length < 2) { return false; } else { return true; } } /** * Returns the first derivation at x. * @param x * @return the first derivation at x */ public double getDx(double x) { if (xx.length == 0 || xx.length == 1) { return 0; } int index = Arrays.binarySearch(xx, x); if (index < 0) { index = - (index + 1) - 1; } return b[index] + 2 * c[index] * (x - xx[index]) + 3 * d[index] * Math.pow(x - xx[index], 2); } /** * Calculates the Spline coefficients. */ private void calculateCoefficients() { int N = yy.length; a = new double[N]; b = new double[N]; c = new double[N]; d = new double[N]; if (N == 2) { a[0] = yy[0]; b[0] = yy[1] - yy[0]; return; } double[] h = new double[N - 1]; for (int i = 0; i < N - 1; i++) { a[i] = yy[i]; h[i] = xx[i + 1] - xx[i]; // h[i] is used for division later, avoid a NaN if (h[i] == 0.0) { h[i] = 0.01; } } a[N - 1] = yy[N - 1]; double[][] A = new double[N - 2][N - 2]; double[] y = new double[N - 2]; for (int i = 0; i < N - 2; i++) { y[i] = 3 * ((yy[i + 2] - yy[i + 1]) / h[i + 1] - (yy[i + 1] - yy[i]) / h[i]); A[i][i] = 2 * (h[i] + h[i + 1]); if (i > 0) { A[i][i - 1] = h[i]; } if (i < N - 3) { A[i][i + 1] = h[i + 1]; } } solve(A, y); for (int i = 0; i < N - 2; i++) { c[i + 1] = y[i]; b[i] = (a[i + 1] - a[i]) / h[i] - (2 * c[i] + c[i + 1]) / 3 * h[i]; d[i] = (c[i + 1] - c[i]) / (3 * h[i]); } b[N - 2] = (a[N - 1] - a[N - 2]) / h[N - 2] - (2 * c[N - 2] + c[N - 1]) / 3 * h[N - 2]; d[N - 2] = (c[N - 1] - c[N - 2]) / (3 * h[N - 2]); } /** * Solves Ax=b and stores the solution in b. */ public void solve(double[][] A, double[] b) { int n = b.length; for (int i = 1; i < n; i++) { A[i][i - 1] = A[i][i - 1] / A[i - 1][i - 1]; A[i][i] = A[i][i] - A[i - 1][i] * A[i][i - 1]; b[i] = b[i] - A[i][i - 1] * b[i - 1]; } b[n - 1] = b[n - 1] / A[n - 1][n - 1]; for (int i = b.length - 2; i >= 0; i--) { b[i] = (b[i] - A[i][i + 1] * b[i + 1]) / A[i][i]; } } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/util/Bezier.java0000644000175000017500000000531411256667036023704 0ustar gregoagregoa/* * Copyright (c) 2005 David Benson * * See LICENSE file in distribution for licensing details of this source file */ package org.jgraph.util; import java.awt.Point; import java.awt.geom.Point2D; /** * Interpolates given points by a bezier curve. The first * and the last two points are interpolated by a quadratic * bezier curve; the other points by a cubic bezier curve. * * Let p a list of given points and b the calculated bezier points, * then one get the whole curve by: * * sharedPath.moveTo(p[0]) * sharedPath.quadTo(b[0].x, b[0].getY(), p[1].x, p[1].getY()); * * for(int i = 2; i < p.length - 1; i++ ) { * Point b0 = b[2*i-3]; * Point b1 = b[2*i-2]; * sharedPath.curveTo(b0.x, b0.getY(), b1.x, b1.getY(), p[i].x, p[i].getY()); * } * * sharedPath.quadTo(b[b.length-1].x, b[b.length-1].getY(), p[n - 1].x, p[n - 1].getY()); * * @author krueger */ public class Bezier { private static final float AP = 0.5f; private Point2D[] bPoints; /** * Creates a new Bezier curve. * @param points */ public Bezier(Point2D[] points) { int n = points.length; if (n < 3) { // Cannot create bezier with less than 3 points return; } bPoints = new Point[2 * (n - 2)]; double paX, paY; double pbX = points[0].getX(); double pbY = points[0].getY(); double pcX = points[1].getX(); double pcY = points[1].getY(); for (int i = 0; i < n - 2; i++) { paX = pbX; paY = pbY; pbX = pcX; pbY = pcY; pcX = points[i + 2].getX(); pcY = points[i + 2].getY(); double abX = pbX - paX; double abY = pbY - paY; double acX = pcX - paX; double acY = pcY - paY; double lac = Math.sqrt(acX * acX + acY * acY); acX = acX /lac; acY = acY /lac; double proj = abX * acX + abY * acY; proj = proj < 0 ? -proj : proj; double apX = proj * acX; double apY = proj * acY; double p1X = pbX - AP * apX; double p1Y = pbY - AP * apY; bPoints[2 * i] = new Point((int) p1X, (int) p1Y); acX = -acX; acY = -acY; double cbX = pbX - pcX; double cbY = pbY - pcY; proj = cbX * acX + cbY * acY; proj = proj < 0 ? -proj : proj; apX = proj * acX; apY = proj * acY; double p2X = pbX - AP * apX; double p2Y = pbY - AP * apY; bPoints[2 * i + 1] = new Point((int) p2X, (int) p2Y); } } /** * Returns the calculated bezier points. * @return the calculated bezier points */ public Point2D[] getPoints() { return bPoints; } /** * Returns the number of bezier points. * @return number of bezier points */ public int getPointCount() { return bPoints.length; } /** * Returns the bezier points at position i. * @param i * @return the bezier point at position i */ public Point2D getPoint(int i) { return bPoints[i]; } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/util/RectUtils.java0000644000175000017500000000346011256667036024402 0ustar gregoagregoa/* * $Id: RectUtils.java,v 1.2 2008/02/28 14:38:48 david Exp $ * * Copyright (c) 2008 Gaudenz Alder * */ package org.jgraph.util; import java.awt.geom.Rectangle2D; public class RectUtils { /** * Unions the pair of source Rectangle2D objects and puts the * result into the returned Rectangle2D object. This method * extends the Rectangle2D version by checking for null parameters, the * returned value will also be null if the two input * rectangles are null * * @param src1 * the first of a pair of Rectangle2D objects to * be combined with each other * @param src2 * the second of a pair of Rectangle2D objects to * be combined with each other * */ public static Rectangle2D union(Rectangle2D src1, Rectangle2D src2) { Rectangle2D result = null; if (src1 == null && src2 == null) { result = null; } else if (src1 != null && src2 != null) { double x1 = Math.min(src1.getMinX(), src2.getMinX()); double y1 = Math.min(src1.getMinY(), src2.getMinY()); double x2 = Math.max(src1.getMaxX(), src2.getMaxX()); double y2 = Math.max(src1.getMaxY(), src2.getMaxY()); result = new Rectangle2D.Double(); result.setFrameFromDiagonal(x1, y1, x2, y2); } else if (src1 != null) { double x1 = src1.getMinX(); double y1 = src1.getMinY(); double x2 = src1.getMaxX(); double y2 = src1.getMaxY(); result = new Rectangle2D.Double(); result.setFrameFromDiagonal(x1, y1, x2, y2); } else { // only src2 is non-null double x1 = src2.getMinX(); double y1 = src2.getMinY(); double x2 = src2.getMaxX(); double y2 = src2.getMaxY(); result = new Rectangle2D.Double(); result.setFrameFromDiagonal(x1, y1, x2, y2); } return result; } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/util/ParallelEdgeRouter.java0000644000175000017500000003307511256667036026213 0ustar gregoagregoa/* * $Id: ParallelEdgeRouter.java,v 1.4 2008/11/13 00:12:39 david Exp $ * * Copyright (c) 2001-2007 Gaudenz Alder * Copyright (c) 2004-2007 David Benson * */ package org.jgraph.util; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.jgraph.JGraph; import org.jgraph.graph.AbstractCellView; import org.jgraph.graph.CellView; import org.jgraph.graph.DefaultGraphModel; import org.jgraph.graph.Edge; import org.jgraph.graph.EdgeView; import org.jgraph.graph.GraphConstants; import org.jgraph.graph.GraphLayoutCache; import org.jgraph.graph.GraphModel; import org.jgraph.graph.DefaultEdge.LoopRouting; /** * A routing algorithm that */ public class ParallelEdgeRouter extends LoopRouting { /** * Singleton to reach parallel edge router */ protected static final ParallelEdgeRouter sharedInstance = new ParallelEdgeRouter(); /** * Distance between each parallel edge */ private static double edgeSeparation = 10.; /** * Distance between intermediate and source/target points */ private static double edgeDeparture = 10.; /** * Getter for singleton managing parallel edges * * @return ParallelEdgeRouter for parallel edges */ public static ParallelEdgeRouter getSharedInstance() { return ParallelEdgeRouter.sharedInstance; } /** * Calc of intermediates points * * @param edge * Edge for which routing is demanding */ public List routeEdge(GraphLayoutCache cache, EdgeView edge) { List newPoints = new ArrayList(); CellView nodeFrom = edge.getSource(); CellView nodeTo = edge.getTarget(); // Check presence of source/target nodes if (null == nodeFrom) { nodeFrom = edge.getSourceParentView(); } if (null == nodeTo) { nodeTo = edge.getTargetParentView(); } if ((null == nodeFrom) || (null == nodeTo)) { // System.out.println("EdgeView has no source or target view : " // + edge.toString()); return null; } if (nodeFrom == nodeTo) { // System.out.println("nodeFrom and NodeTo are the same cell view"); return null; } List points = edge.getPoints(); Object startPort = points.get(0); Object endPort = points.get(points.size() - 1); newPoints.add(startPort); // Promote edges up to first visible connected parents // if (graph == null) { // System.out // .println("graph variable not correctly set, must be set to obtain parallel routing"); // } // Check presence of parallel edges Object[] edges = getParallelEdges(cache, edge, nodeFrom, nodeTo); if (edges == null) { return null; } // For one edge, no intermediate point if (edges.length >= 2) { // System.out.println("EdgeView indicates " + edges.length // + " parallel edges"); // Looking for position of edge int position = 0; // System.out.println(); // System.out.println("edges.length = " + edges.length); for (int i = 0; i < edges.length; i++) { // System.out // .println("edge value = " // + String.valueOf(((DefaultGraphCell) edges[i]) // .getUserObject())); // System.out // .println("compared edge value = " // + String.valueOf(((DefaultGraphCell) edge.getCell()) // .getUserObject())); Object e = edges[i]; if (e == edge.getCell()) { position = i + 1; } } // System.out.println("position = " + position); // Looking for position of source/target nodes (edge=>port=>vertex) Point2D from; Point2D perimeterPoint = edge.getTarget() != null ? edge .getPoint(edge.getPointCount() - 1) : AbstractCellView .getCenterPoint(nodeTo); if (perimeterPoint == null) { perimeterPoint = AbstractCellView.getCenterPoint(nodeTo); } if (edge.getSource() == null || edge.getSource().getParentView() == null) { // System.out.println(edge+"-source promoted"); from = nodeFrom.getPerimeterPoint(edge, AbstractCellView.getCenterPoint(nodeFrom), perimeterPoint); } else { from = edge.getSource().getParentView().getPerimeterPoint(edge, AbstractCellView.getCenterPoint(edge.getSource().getParentView()), (edge.getTarget() != null && edge.getTarget().getParentView() != null) ? AbstractCellView.getCenterPoint(edge.getTarget().getParentView()) : AbstractCellView.getCenterPoint(nodeTo)); } Point2D to; if (edge.getTarget() == null || edge.getTarget().getParentView() == null) { // INV: nodeTo != null // System.out.println(edge+"-target promoted"); to = nodeTo.getPerimeterPoint(edge, AbstractCellView.getCenterPoint(nodeTo), from); } else { to = edge.getTarget().getParentView().getPerimeterPoint (edge, AbstractCellView.getCenterPoint(edge.getTarget().getParentView()), from); } // System.out.println("from Point = " + String.valueOf(from)); // System.out.println("to Point = " + String.valueOf(to)); if (from != null && to != null) { double dy = from.getY() - to.getY(); double dx = from.getX() - to.getX(); if (dy == 0 && dx == 0) { return null; } double theta = 0; if (dy == 0) { theta = Math.PI / 2.0; } else if (dx == 0) { theta = 0; } else { double m = dy / dx; theta = Math.atan(-1 / m); } // Calc of radius double length = Math.sqrt(dx * dx + dy * dy); // System.out.println("length = " + length); double rx = dx / length; double ry = dy / length; // Memorize size of source/target nodes double sizeFrom = Math.max(nodeFrom.getBounds().getWidth(), nodeFrom.getBounds().getHeight()) / 2.; double sizeTo = Math.max(nodeTo.getBounds().getWidth(), nodeTo .getBounds().getHeight()) / 2.; // Calc position of central point double edgeMiddleDeparture = (Math.sqrt(dx * dx + dy * dy) - sizeFrom - sizeTo) / 2 + sizeFrom; // Calc position of intermediates points double edgeFromDeparture = edgeDeparture + sizeFrom; double edgeToDeparture = edgeDeparture + sizeTo; // Calc distance between edge and mediane source/target double r = edgeSeparation * Math.floor(position / 2); if (0 == (position % 2)) { r = -r; } // Convert coordinate double ex = r * Math.cos(theta); double ey = r * Math.sin(theta); // Check if is not better to have only one intermediate point if (edgeMiddleDeparture <= edgeFromDeparture) { double midX = from.getX() - rx * edgeMiddleDeparture; double midY = from.getY() - ry * edgeMiddleDeparture; Point2D controlPoint = new Point2D.Double(ex + midX, ey + midY); // Add intermediate point newPoints.add(controlPoint); } else { double midXFrom = from.getX() - rx * edgeFromDeparture; double midYFrom = from.getY() - ry * edgeFromDeparture; double midXTo = to.getX() + rx * edgeToDeparture; double midYTo = to.getY() + ry * edgeToDeparture; Point2D controlPointFrom = new Point2D.Double( ex + midXFrom, ey + midYFrom); Point2D controlPointTo = new Point2D.Double(ex + midXTo, ey + midYTo); // Add intermediates points newPoints.add(controlPointFrom); newPoints.add(controlPointTo); } // Reposition the label, only if it's not been moved from its // default location Point2D labelPos = edge.getLabelPosition(); if (labelPos != null) { double x = labelPos.getX(); if (x == GraphConstants.PERMILLE / 2) { Map allAttributes = edge.getAllAttributes(); if (allAttributes != null) { // Reverse the direction of r for up to down // connections if (dy < 0) { r = -r; } int lineStyle = getPreferredLineStyle(edge); if (lineStyle == Edge.Routing.NO_PREFERENCE) { lineStyle = GraphConstants .getLineStyle(allAttributes); } // The middle of the edge (where the label is) can // vary in height if (lineStyle == GraphConstants.STYLE_BEZIER || lineStyle == GraphConstants.STYLE_SPLINE) { // TODO, sort this magic number out GraphConstants.setLabelPosition(allAttributes, new Point2D.Double(x, r * edgeMiddleDeparture / 79)); } else { GraphConstants.setExactSegmentLabel(allAttributes,true); } } } } } } newPoints.add(endPort); return newPoints; } /** * Getter to obtain the distance between each parallel edge * * @return Distance */ public static double getEdgeSeparation() { return ParallelEdgeRouter.edgeSeparation; } /** * Setter to define distance between each parallel edge * * @param edgeSeparation * New distance */ public static void setEdgeSeparation(double edgeSeparation) { ParallelEdgeRouter.edgeSeparation = edgeSeparation; } /** * Getter to obtain the distance between intermediate and source/target * points * * @return Distance */ public static double getEdgeDeparture() { return ParallelEdgeRouter.edgeDeparture; } /** * Setter to define distance between intermediate and source/target points * * @param edgeDeparture * New distance */ public static void setEdgeDeparture(double edgeDeparture) { ParallelEdgeRouter.edgeDeparture = edgeDeparture; } /** * Getter to obtain the list of parallel edges * * @param edge * Edge on which one wants to know parallel edges * @return Object[] Array of parallel edges (include edge passed on * argument) */ protected Object[] getParallelEdges(GraphLayoutCache cache, EdgeView edge, CellView cellView1, CellView cellView2) { GraphModel model = cache.getModel(); Object cell1 = cellView1.getCell(); Object cell2 = cellView2.getCell(); // Need to exit if a load has just been performed and the model // isn't in place properly yet Object[] roots = DefaultGraphModel.getRoots(model); if (roots.length == 0) { return null; } // Need to order cells so direction of the edges doesn't // affect the ordering of the output edges Object[] cells = new Object[] { cell1, cell2 }; cells = DefaultGraphModel.order(model, cells); if (cells == null || cells.length < 2) { return null; } cell1 = cells[0]; cell2 = cells[1]; // System.out // .println("cell1 of parallel edges = " // + String.valueOf(((DefaultGraphCell) cell1) // .getUserObject())); while (model.getParent(cell1) != null && !cache.isVisible(cell1)) { cell1 = model.getParent(cell1); // if (cache.isVisible(cell1)) { // System.out // .println("cell1 promoted to = " // + String.valueOf(((DefaultGraphCell) cell1) // .getUserObject())); // } } // System.out // .println("cell2 of parallel edges = " // + String.valueOf(((DefaultGraphCell) cell2) // .getUserObject())); while (model.getParent(cell2) != null && !cache.isVisible(cell2)) { cell2 = model.getParent(cell2); // if (cache.isVisible(cell2)) { // System.out // .println("cell2 promoted to = " // + String.valueOf(((DefaultGraphCell) cell2) // .getUserObject())); // } } List cell1Children = DefaultGraphModel.getDescendants(model, new Object[] { cell1 }); List cells1 = new ArrayList(); cells1.add(cell1); Iterator iter = cell1Children.iterator(); while (iter.hasNext()) { Object childCell = iter.next(); if (DefaultGraphModel.isVertex(model, childCell) && (!cache.isVisible(childCell))) { cells1.add(childCell); // System.out // .println("cell1 has child " // + String.valueOf(((DefaultGraphCell) childCell) // .getUserObject())); } } List cell2Children = DefaultGraphModel.getDescendants(model, new Object[] { cell2 }); List cells2 = new ArrayList(); cells2.add(cell2); iter = cell2Children.iterator(); while (iter.hasNext()) { Object childCell = iter.next(); if (DefaultGraphModel.isVertex(model, childCell) && (!cache.isVisible(childCell))) { cells2.add(childCell); // System.out // .println("cell2 has child " // + String.valueOf(((DefaultGraphCell) childCell) // .getUserObject())); } } // Optimise for the standard case of no child cells if (cells1.size() == 1 && cells2.size() == 1) { // System.out.println("cells have no valid children"); return DefaultGraphModel .getEdgesBetween(model, cell1, cell2, false); } // The object array to be returned Object[] edgesBetween = null; Iterator iter1 = cells1.iterator(); while (iter1.hasNext()) { Object tempCell1 = iter1.next(); Iterator iter2 = cells2.iterator(); while (iter2.hasNext()) { Object tempCell2 = iter2.next(); Object[] edges = DefaultGraphModel.getEdgesBetween(model, tempCell1, tempCell2, false); if (edges.length > 0) { // for (int i = 0; i < edges.length; i++) { // System.out // .println("edge between " // + i // + " = " // + String // .valueOf(((DefaultGraphCell) edges[i]) // .getUserObject())); // System.out // .println("between cell " // + String.valueOf(((DefaultGraphCell) tempCell1) // .getUserObject())); // System.out // .println("and cell " // + String.valueOf(((DefaultGraphCell) tempCell2) // .getUserObject())); // } if (edgesBetween == null) { edgesBetween = edges; } else { // need to copy everything into a new array Object[] newArray = new Object[edges.length + edgesBetween.length]; System.arraycopy(edgesBetween, 0, newArray, 0, edgesBetween.length); System.arraycopy(edges, 0, newArray, edgesBetween.length, edges.length); edgesBetween = newArray; } } } } return edgesBetween; } /** * @deprecated graph instance retained internally * @param graph * The graph to set. */ public static void setGraph(JGraph graph) { // No longer used from 5.10 API } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/util/Spline2D.java0000644000175000017500000000537211256667036024110 0ustar gregoagregoa/* * @(#)Spline2D.java * * Copyright (c) 2003 Martin Krueger * Copyright (c) 2005 David Benson * */ package org.jgraph.util; import java.awt.geom.Point2D; /** * Interpolates points given in the 2D plane. The resulting spline * is a function s: R -> R^2 with parameter t in [0,1]. * * @author krueger */ public class Spline2D { /** * Array representing the relative proportion of the total distance * of each point in the line ( i.e. first point is 0.0, end point is * 1.0, a point halfway on line is 0.5 ). */ private double[] t; private Spline splineX; private Spline splineY; /** * Total length tracing the points on the spline */ private double length; /** * Creates a new Spline2D. * @param points */ public Spline2D(Point2D[] points) { double[] x = new double[points.length]; double[] y = new double[points.length]; for(int i = 0; i< points.length; i++) { x[i] = points[i].getX(); y[i] = points[i].getY(); } init(x, y); } /** * Creates a new Spline2D. * @param x * @param y */ public Spline2D(double[] x, double[] y) { init(x, y); } private void init(double[] x, double[] y) { if (x.length != y.length) { throw new IllegalArgumentException("Arrays must have the same length."); } if (x.length < 2) { throw new IllegalArgumentException("Spline edges must have at least two points."); } t = new double[x.length]; t[0] = 0.0; // start point is always 0.0 // Calculate the partial proportions of each section between each set // of points and the total length of sum of all sections for (int i = 1; i < t.length; i++) { double lx = x[i] - x[i-1]; double ly = y[i] - y[i-1]; // If either diff is zero there is no point performing the square root if ( 0.0 == lx ) { t[i] = Math.abs(ly); } else if ( 0.0 == ly ) { t[i] = Math.abs(lx); } else { t[i] = Math.sqrt(lx*lx+ly*ly); } length += t[i]; t[i] += t[i-1]; } for(int i = 1; i< (t.length)-1; i++) { t[i] = t[i] / length; } t[(t.length)-1] = 1.0; // end point is always 1.0 splineX = new Spline(t, x); splineY = new Spline(t, y); } /** * @param t 0 <= t <= 1 */ public double[] getPoint(double t) { double[] result = new double[2]; result[0] = splineX.getValue(t); result[1] = splineY.getValue(t); return result; } /** * Used to check the correctness of this spline */ public boolean checkValues() { return (splineX.checkValues() && splineY.checkValues()); } public double getDx(double t) { return splineX.getDx(t); } public double getDy(double t) { return splineY.getDx(t); } public Spline getSplineX() { return splineX; } public Spline getSplineY() { return splineY; } public double getLength() { return length; } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/package.html0000644000175000017500000000013011256667036023114 0ustar gregoagregoa JGraph's topmost package which contains the JGraph class. libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/event/0000755000175000017500000000000011256667036021762 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/event/GraphLayoutCacheListener.java0000644000175000017500000000127611256667036027524 0ustar gregoagregoa/* * @(#)GraphModelListener.java 1.0 03-JUL-04 * * Copyright (c) 2001-2005 Gaudenz Alder * * See LICENSE file in distribution for licensing details of this source file */ package org.jgraph.event; import java.util.EventListener; /** * Defines the interface for an object that listens to changes in a GraphModel. * * @author Gaudenz Alder * @version 1.0 1/1/02 */ public interface GraphLayoutCacheListener extends EventListener { /** * Invoked after a cell has changed in some way. The vertex/vertices may * have changed bounds or altered adjacency, or other attributes have * changed that may affect presentation. */ void graphLayoutCacheChanged(GraphLayoutCacheEvent e); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/event/GraphSelectionListener.java0000644000175000017500000000115411256667036027243 0ustar gregoagregoa/* * @(#)GraphSelectionListener.java 1.0 03-JUL-04 * * Copyright (c) 2001-2005 Gaudenz Alder * * See LICENSE file in distribution for licensing details of this source file */ package org.jgraph.event; import java.util.EventListener; /** * The listener that's notified when the selection in a GraphSelectionModel * changes. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public interface GraphSelectionListener extends EventListener { /** * Called whenever the value of the selection changes. * @param e the event that characterizes the change. */ void valueChanged(GraphSelectionEvent e); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/event/package.html0000644000175000017500000000017711256667036024250 0ustar gregoagregoa Contains event classes and listener interfaces that are used to react to events fired by JGraph. libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/event/GraphModelEvent.java0000644000175000017500000000651611256667036025661 0ustar gregoagregoa/* * @(#)GraphModelEvent.java 1.0 03-JUL-04 * * Copyright (c) 2001-2005 Gaudenz Alder * * See LICENSE file in distribution for licensing details of this source file */ package org.jgraph.event; import java.util.EventObject; import org.jgraph.graph.CellView; import org.jgraph.graph.ConnectionSet; import org.jgraph.graph.GraphLayoutCache; import org.jgraph.graph.ParentMap; /** * Encapsulates information describing changes to a graph model, and is used to * notify graph model listeners of the change. * * @author Gaudenz Alder * @version 1.0 1/1/2 * */ public class GraphModelEvent extends EventObject { /** * The object that constitutes the change. */ protected GraphModelChange change; /** * Used to create an event when cells have been changed, inserted, or * removed, identifying the change as a ModelChange object. * * @param source * the Object responsible for generating the event (typically the * creator of the event object passes this for its * value) * @param change * the object that describes the change * @see org.jgraph.graph.GraphCell * */ public GraphModelEvent(Object source, GraphModelChange change) { super(source); this.change = change; } /** * Returns the object that constitutes the change. * * @return the object that constitutes the change. */ public GraphModelChange getChange() { return change; } /** * Defines the interface for objects that may be included into a * GraphModelEvent to describe a model change. */ public static interface GraphModelChange extends GraphLayoutCacheEvent.GraphLayoutCacheChange { /** * Returns a connection set representing the graph structure after the * change was applied * * @return the connection set of the graph after the change */ public ConnectionSet getConnectionSet(); /** * Returns a connection set representing the graph structure before the * change was applied ( an "undo" of the change). * * @return the connection set of the graph before the change */ public ConnectionSet getPreviousConnectionSet(); /** * Returns a parent map representing the group structure after the * change was applied * * @return the changed parent map */ public ParentMap getParentMap(); /** * Returns a parent map representing the group structure before the * change was applied ( an "undo" of the change ) * * @return the previous parent map */ public ParentMap getPreviousParentMap(); /** * Allows a GraphLayoutCache to store cell views for * cells that have been removed. Such cell views are used for * re-insertion and restoring the visual attributes. * * @param view * the GraphLayoutCache to store the removed * cells * @param cellViews * the cell views to be stored */ public void putViews(GraphLayoutCache view, CellView[] cellViews); /** * Allows a GraphLayoutCache to retrieve an array of * CellViews that was previously stored with * putViews(GraphLayoutCache, CellView[]). * * @param view * the GraphLayoutCache whose stored cells are * to be retrieved */ public CellView[] getViews(GraphLayoutCache view); } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/event/GraphModelListener.java0000644000175000017500000000153611256667036026362 0ustar gregoagregoa/* * @(#)GraphModelListener.java 1.0 03-JUL-04 * * Copyright (c) 2001-2005 Gaudenz Alder * * See LICENSE file in distribution for licensing details of this source file */ package org.jgraph.event; import java.util.EventListener; /** * Defines the interface for an object that listens to changes in a GraphModel. * * @author Gaudenz Alder * @version 1.0 1/1/02 */ public interface GraphModelListener extends EventListener { /** * Invoked after a cell has changed in some way. The vertex/vertices may * have changed bounds or altered adjacency, or other attributes have * changed that may affect presentation. * Note : Read the notes on the GraphModelEvent class carefully. * A GraphModelEvent is the undo of the event that has just occurred, * i.e. if you undo this event is executed. */ void graphChanged(GraphModelEvent e); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/event/GraphSelectionEvent.java0000644000175000017500000000762511256667036026550 0ustar gregoagregoa/* * @(#)GraphSelectionEvent.java 1.0 03-JUL-04 * * Copyright (c) 2001-2005 Gaudenz Alder * * See LICENSE file in distribution for licensing details of this source file */ package org.jgraph.event; import java.util.EventObject; /** * An event that characterizes a change in the current selection. The change is * based on any number of cells. GraphSelectionListeners will generally query * the source of the event for the new selected status of each potentially * changed cell. * * @see GraphSelectionListener * @see org.jgraph.graph.GraphSelectionModel * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class GraphSelectionEvent extends EventObject { /** Cells this event represents. */ protected Object[] cells; /** * For each cell identifies whether or not that cell is newly selected. */ protected boolean[] areNew; /** * Represents a change in the selection of a GraphSelectionModel. * cells identifies the cells that have been either added or * removed from the selection. * * @param source * source of event * @param cells * the paths that have changed in the selection * @param areNew * for each cell, defines whether or not that cell is newly * selected */ public GraphSelectionEvent(Object source, Object[] cells, boolean[] areNew) { super(source); this.cells = cells; this.areNew = areNew; } /** * Returns the cells that have been added or removed from the selection. * * @return added or removed cells */ public Object[] getCells() { int numCells; Object[] retCells; numCells = cells.length; retCells = new Object[numCells]; System.arraycopy(cells, 0, retCells, 0, numCells); return retCells; } /** * Returns the first cell. * * @return the first selected cell */ public Object getCell() { return cells[0]; } /** * Returns true if the first cell has been added to the selection, a return * value of false means the first cell has been removed from the selection. * * @return whether or not the first cell has been added or removed */ public boolean isAddedCell() { return areNew[0]; } /** * Returns true if the cell identified by cell was added to the selection. A * return value of false means the cell was in the selection but is no * longer in the selection. This will raise if cell is not one of the cells * identified by this event. * * @param cell * the cell that is to be indicated as newly selected or not * @return true if the specified cell is newly selected */ public boolean isAddedCell(Object cell) { for (int counter = cells.length - 1; counter >= 0; counter--) if (cells[counter].equals(cell)) return areNew[counter]; throw new IllegalArgumentException( "cell is not a cell identified by the GraphSelectionEvent"); } /** * Returns true if the cell identified by index was added to * the selection. A return value of false means the cell was in the * selection but is no longer in the selection. This will raise an exception * if index < 0 || >=getPaths .length. * * @param index * the index of areNew of the cell that is to be * indicated as newly selected or not * @return whether or not the cell is newly selected or not */ public boolean isAddedCell(int index) { if (cells == null || index < 0 || index >= cells.length) { throw new IllegalArgumentException( "index is beyond range of added cells identified by GraphSelectionEvent"); } return areNew[index]; } /** * Returns a copy of the receiver, but with the source being newSource. * * @param newSource * the new event source * @return the cloned event with the specified source */ public Object cloneWithSource(Object newSource) { // Fix for IE bug - crashing return new GraphSelectionEvent(newSource, cells, areNew); } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/event/GraphLayoutCacheEvent.java0000644000175000017500000000752611256667036027024 0ustar gregoagregoa/* * $Id: GraphLayoutCacheEvent.java,v 1.2 2008/02/15 11:09:17 david Exp $ * * Copyright (c) 2001-2008 Gaudenz Alder * * See LICENSE file in distribution for licensing details of this source file */ package org.jgraph.event; import java.awt.geom.Rectangle2D; import java.util.EventObject; import java.util.Map; /** * Encapsulates information describing changes to a graph layout cache, and is * used to notify graph layout cache listeners of the change. Note that graph * layout cache events do not repeat information in graph model events if there * is no view specific information. The idea of this event is to provide * information on what has changed in the graph layout cache only. */ public class GraphLayoutCacheEvent extends EventObject { /** * The object that constitutes the change. */ protected GraphLayoutCacheChange change; /** * Used to create an event when cells have been changed, inserted, or * removed, identifying the change as a GraphLayoutCacheChange object. * * @param source * the Object responsible for generating the event (typically the * creator of the event object passes this for its * value) * * @param change * the object that describes the change */ public GraphLayoutCacheEvent(Object source, GraphLayoutCacheChange change) { super(source); this.change = change; } /** * Returns the object that constitutes the change. * * @return the object that constitutes the change */ public GraphLayoutCacheChange getChange() { return change; } /** * Defines the interface for objects that may be used to represent a change * to the graph layout cache. */ public static interface GraphLayoutCacheChange { /** * Returns the source of this change. This can either be a view or a * model, if this change is a GraphModelChange. Note: This is not * necessarily the same as the source of the event and is used * separately in the graphundomanager. * * @return the source fo this change */ public Object getSource(); /** * Returns the cells that have changed. * * @return the cell changed */ public Object[] getChanged(); /** * Returns the cells that have been inserted. * * @return the cells that were inserted by the change */ public Object[] getInserted(); /** * Returns the cells that have been removed. * * @return the cells that were removed by the change */ public Object[] getRemoved(); /** * Returns a map that contains (object, map) pairs which holds the new * attributes for each changed cell. Note: This returns a map of (cell, * map) pairs for an insert on a model that is not an attribute store. * Use getPreviousAttributes to access the attributes that have been * stored in the model. */ public Map getAttributes(); /** * Returns a map that contains (object, map) pairs which holds the * previous attributes for the changed cells. * * @return map of attributes before the change */ public Map getPreviousAttributes(); /** * Returns the dirty region for the original position of the * changed cells before the change happened. * @return the dirty region prior to the event */ public Rectangle2D getDirtyRegion(); /** * In some cases the class firing this event will not have access * to the dirty region prior to the change. It is then up to the * receiving class to set it once. * @param dirty */ public void setDirtyRegion(Rectangle2D dirty); /** * Returns the objects that have not changed explicitly, but implicitly * because one of their dependent cells has changed. This is typically * used to return the edges that are attached to vertices, which in turn * have been resized or moved. * * @return array of contextual cells */ public Object[] getContext(); } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/0000755000175000017500000000000011256667036021742 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/VertexView.java0000644000175000017500000003536211256667036024726 0ustar gregoagregoa/* * @(#)VertexView.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.Color; import java.awt.Cursor; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.jgraph.JGraph; import org.jgraph.plaf.GraphUI; import org.jgraph.plaf.basic.BasicGraphUI; /** * The default implementation of a vertex view. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class VertexView extends AbstractCellView { /** Renderer for the class. */ public static transient VertexRenderer renderer; // Headless environment does not allow vertex renderer static { try { renderer = new VertexRenderer(); } catch (Error e) { // No vertex renderer } } public final static Rectangle2D defaultBounds = new Rectangle2D.Double(10, 10, 20, 20); /** Reference to the bounds attribute */ protected Rectangle2D bounds; /** * Constructs an empty vertex view. */ public VertexView() { super(); } /** * Constructs a vertex view for the specified model object and the specified * child views. * * @param cell * reference to the model object */ public VertexView(Object cell) { super(cell); } // // CellView Interface // /** * Overrides the parent method to udpate the cached points. */ public void update(GraphLayoutCache cache) { super.update(cache); bounds = GraphConstants.getBounds(allAttributes); if (bounds == null) { bounds = allAttributes.createRect(defaultBounds); GraphConstants.setBounds(allAttributes, bounds); } groupBounds = null; } public Rectangle2D getCachedBounds() { return bounds; } public void setCachedBounds(Rectangle2D bounds) { this.bounds = bounds; } /** * Returns a renderer for the class. */ public CellViewRenderer getRenderer() { return renderer; } /** * Returns a cell handle for the view, if the graph and the view are * sizeable. */ public CellHandle getHandle(GraphContext context) { if (GraphConstants.isSizeable(getAllAttributes()) && !GraphConstants.isAutoSize(getAllAttributes()) && context.getGraph().isSizeable()) return new SizeHandle(this, context); return null; } /** * Returns the cached bounds for the vertex. */ public Rectangle2D getBounds() { Rectangle2D rect = super.getBounds(); if (rect == null) rect = bounds; return rect; } /** * @deprecated replaced by * {@link AbstractCellView#getCenterPoint(CellView vertex)} * @return the center point of this vertex */ public Point2D getCenterPoint() { return AbstractCellView.getCenterPoint(this); } /** * @deprecated replaced by * {@link #getPerimeterPoint(EdgeView edge, Point2D source, Point2D p)} */ public Point2D getPerimeterPoint(Point2D source, Point2D p) { return AbstractCellView.getCenterPoint(this); } // // Special Methods // /** * Returns the intersection of the bounding rectangle and the straight line * between the source and the specified point p. The specified point is * expected not to intersect the bounds. Note: You must override this method * if you use a different renderer. This is because this method relies on * the VertexRenderer interface, which can not be safely assumed for * subclassers. */ public Point2D getPerimeterPoint(EdgeView edge, Point2D source, Point2D p) { if (getRenderer() instanceof VertexRenderer) return ((VertexRenderer) getRenderer()).getPerimeterPoint(this, source, p); return super.getPerimeterPoint(edge, source, p); } /** Array that holds the cursors for the different control points. */ public static transient int[] defaultCursors = new int[] { Cursor.NW_RESIZE_CURSOR, Cursor.N_RESIZE_CURSOR, Cursor.NE_RESIZE_CURSOR, Cursor.W_RESIZE_CURSOR, Cursor.E_RESIZE_CURSOR, Cursor.SW_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR, Cursor.SE_RESIZE_CURSOR }; /** Array that holds the cursors for the different control points. */ public static transient int[] xCursors = new int[] { Cursor.W_RESIZE_CURSOR, 0, Cursor.E_RESIZE_CURSOR, Cursor.W_RESIZE_CURSOR, Cursor.E_RESIZE_CURSOR, Cursor.W_RESIZE_CURSOR, 0, Cursor.E_RESIZE_CURSOR }; /** Array that holds the cursors for the different control points. */ public static transient int[] yCursors = new int[] { Cursor.N_RESIZE_CURSOR, Cursor.N_RESIZE_CURSOR, Cursor.N_RESIZE_CURSOR, 0, 0, Cursor.S_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR }; public static class SizeHandle implements CellHandle, Serializable { /** Reference to graph off screen graphics */ protected transient Graphics offgraphics; protected transient boolean firstDrag = true; protected transient JGraph graph; /* Reference to the temporary view for this handle. */ protected transient VertexView vertex; protected transient CellView[] portViews; protected transient Rectangle2D cachedBounds; /* Reference to the context for the specified view. */ protected transient GraphContext context; protected transient Rectangle2D initialBounds; protected transient CellView[] contextViews; /* Index of the active control point. -1 if none is active. */ protected transient int index = -1; /* Array of control points represented as rectangles. */ protected transient Rectangle2D[] r = new Rectangle2D[8]; protected boolean firstOverlayInvocation = true; /** Array that holds the cursors for the different control points. */ public transient int[] cursors = null; /** * True if the cell is being edited. */ protected boolean editing = false; public SizeHandle(VertexView vertexview, GraphContext ctx) { graph = ctx.getGraph(); vertex = vertexview; editing = graph.getEditingCell() == vertex.getCell(); int sizeableAxis = GraphConstants.getSizeableAxis(vertex .getAllAttributes()); if (sizeableAxis == GraphConstants.X_AXIS) cursors = xCursors; else if (sizeableAxis == GraphConstants.Y_AXIS) cursors = yCursors; else cursors = defaultCursors; // PortView Preview portViews = ctx.createTemporaryPortViews(); initialBounds = (Rectangle2D) vertex.getBounds().clone(); context = ctx; for (int i = 0; i < r.length; i++) r[i] = new Rectangle2D.Double(); invalidate(); } public boolean isConstrainedSizeEvent(MouseEvent e) { GraphUI ui = graph.getUI(); if (ui instanceof BasicGraphUI) return ((BasicGraphUI) ui).isConstrainedMoveEvent(e); return false; } public void paint(Graphics g) { invalidate(); g.setColor((editing) ? graph.getLockedHandleColor() : graph .getHandleColor()); for (int i = 0; i < r.length; i++) { if (cursors[i] != 0) g .fill3DRect((int) r[i].getX(), (int) r[i].getY(), (int) r[i].getWidth(), (int) r[i] .getHeight(), true); } if (!graph.isXorEnabled()) { firstOverlayInvocation = false; overlay(g); } } protected void initOffscreen() { if (!graph.isXorEnabled()) { return; } try { offgraphics = graph.getOffgraphics(); } catch (Exception e) { offgraphics = null; } catch (Error e) { offgraphics = null; } } public void overlay(Graphics g) { if (!firstOverlayInvocation) { if (cachedBounds != null) { g.setColor(Color.black); Rectangle2D tmp = graph.toScreen((Rectangle2D) cachedBounds .clone()); g.drawRect((int) tmp.getX(), (int) tmp.getY(), (int) tmp .getWidth() - 2, (int) tmp.getHeight() - 2); } else if (!initialBounds.equals(vertex.getBounds())) { Graphics2D g2 = (Graphics2D) g; AffineTransform oldTransform = g2.getTransform(); g2.scale(graph.getScale(), graph.getScale()); graph.getUI() .paintCell(g, vertex, vertex.getBounds(), true); if (contextViews != null) { for (int i = 0; i < contextViews.length; i++) { graph.getUI().paintCell(g, contextViews[i], contextViews[i].getBounds(), true); } } if (!graph.isPortsScaled()) g2.setTransform(oldTransform); if (portViews != null && graph.isPortsVisible()) graph.getUI().paintPorts(g, portViews); g2.setTransform(oldTransform); } } firstOverlayInvocation = false; } /** * Invoked when the mouse pointer has been moved on a component (with no * buttons down). */ public void mouseMoved(MouseEvent event) { if (vertex != null) { for (int i = 0; i < r.length; i++) { if (r[i].contains(event.getPoint())) { graph.setCursor(new Cursor(cursors[i])); event.consume(); return; } } } } /** Process mouse pressed event. */ public void mousePressed(MouseEvent event) { if (!graph.isSizeable()) return; for (int i = 0; i < r.length; i++) { if (r[i].contains(event.getPoint()) && cursors[i] != 0) { Set set = new HashSet(); set.add(vertex.getCell()); contextViews = context.createTemporaryContextViews(set); Object[] all = AbstractCellView .getDescendantViews(new CellView[] { vertex }); if (all.length >= org.jgraph.plaf.basic.BasicGraphUI.MAXHANDLES) cachedBounds = (Rectangle2D) initialBounds.clone(); event.consume(); index = i; return; } } } /** Process mouse dragged event. */ public void mouseDragged(MouseEvent event) { if (firstDrag && graph.isDoubleBuffered() && cachedBounds == null) { initOffscreen(); firstDrag = false; } Rectangle2D dirty = null; Graphics g = (offgraphics != null) ? offgraphics : graph .getGraphics(); if (index == -1) return; if (offgraphics != null || !graph.isXorEnabled()) { dirty = graph .toScreen((Rectangle2D) vertex.getBounds().clone()); Rectangle2D t = graph.toScreen(AbstractCellView .getBounds(contextViews)); if (t != null) dirty.add(t); } Rectangle2D newBounds = computeBounds(event); if (graph.isXorEnabled()) { g.setColor(graph.getForeground()); g.setXORMode(graph.getBackground().darker()); overlay(g); } else { firstOverlayInvocation = false; } if (cachedBounds != null) cachedBounds = newBounds; else { // Reset old Bounds CellView[] all = AbstractCellView .getDescendantViews(new CellView[] { vertex }); for (int i = 0; i < all.length; i++) { CellView orig = graph.getGraphLayoutCache().getMapping( all[i].getCell(), false); if (orig != null) { AttributeMap origAttr = (AttributeMap) orig .getAllAttributes().clone(); all[i].changeAttributes(graph.getGraphLayoutCache(), origAttr); all[i].refresh(graph.getGraphLayoutCache(), context, false); } } vertex.setBounds(newBounds); if (vertex != null) graph.getGraphLayoutCache().update(vertex); if (contextViews != null) graph.getGraphLayoutCache().update(contextViews); } if (graph.isXorEnabled()) { overlay(g); } if (offgraphics != null || !graph.isXorEnabled()) { dirty.add(graph.toScreen((Rectangle2D) vertex.getBounds() .clone())); Rectangle2D t = graph.toScreen(AbstractCellView .getBounds(contextViews)); if (t != null) dirty.add(t); int border = PortView.SIZE + 10; if (graph.isPortsScaled()) border = (int) (graph.getScale() * border); int border2 = border / 2; dirty.setFrame(dirty.getX() - border2, dirty.getY() - border2, dirty.getWidth() + border, dirty.getHeight() + border); double sx1 = Math.max(0, dirty.getX()); double sy1 = Math.max(0, dirty.getY()); double sx2 = sx1 + dirty.getWidth(); double sy2 = sy1 + dirty.getHeight(); if (offgraphics != null) { graph.drawImage((int) sx1, (int) sy1, (int) sx2, (int) sy2, (int) sx1, (int) sy1, (int) sx2, (int) sy2); } else { graph.repaint((int) dirty.getX(), (int) dirty.getY(), (int) dirty.getWidth(), (int) dirty.getHeight()); } } } protected Rectangle2D computeBounds(MouseEvent event) { double left = initialBounds.getX(); double right = initialBounds.getX() + initialBounds.getWidth() - 1; double top = initialBounds.getY(); double bottom = initialBounds.getY() + initialBounds.getHeight() - 1; Point2D p = graph.fromScreen(graph.snap((Point2D) event.getPoint() .clone())); // Not into negative coordinates p.setLocation(Math.max(0, p.getX()), Math.max(0, p.getY())); // Bottom row if (index > 4) bottom = p.getY(); // Top row else if (index < 3) top = p.getY(); // Left col if (index == 0 || index == 3 || index == 5) left = p.getX(); // Right col else if (index == 2 || index == 4 || index == 7) right = p.getX(); double width = right - left; double height = bottom - top; if (isConstrainedSizeEvent(event) || GraphConstants.isConstrained(vertex.getAllAttributes())) { if (index == 3 || index == 4 || index == 5) height = width; else if (index == 1 || index == 6 || index == 2 || index == 7) width = height; else { height = width; top = bottom - height; } } if (width < 0) { // Flip over left side left += width; width = Math.abs(width); } if (height < 0) { // Flip over top side top += height; height = Math.abs(height); } return new Rectangle2D.Double(left, top, width + 1, height + 1); } // Dispatch the edit event public void mouseReleased(MouseEvent e) { if (index != -1) { cachedBounds = computeBounds(e); vertex.setBounds(cachedBounds); CellView[] views = AbstractCellView .getDescendantViews(new CellView[] { vertex }); Map attributes = GraphConstants.createAttributes(views, null); graph.getGraphLayoutCache().edit(attributes, null, null, null); } e.consume(); cachedBounds = null; initialBounds = null; firstDrag = true; } protected void invalidate() { // Retrieve current bounds and set local vars Rectangle2D tmp = graph.getCellBounds(vertex.getCell()); if (tmp != null) { tmp = (Rectangle2D) tmp.clone(); graph.toScreen(tmp); int handlesize = graph.getHandleSize(); int s2 = 2 * handlesize; double left = tmp.getX() - handlesize; double top = tmp.getY() - handlesize; double w2 = tmp.getX() + (tmp.getWidth() / 2) - handlesize; double h2 = tmp.getY() + (tmp.getHeight() / 2) - handlesize; double right = tmp.getX() + tmp.getWidth() - handlesize; double bottom = tmp.getY() + tmp.getHeight() - handlesize; // Update control point positions r[0].setFrame(left, top, s2, s2); r[1].setFrame(w2, top, s2, s2); r[2].setFrame(right, top, s2, s2); r[3].setFrame(left, h2, s2, s2); r[4].setFrame(right, h2, s2, s2); r[5].setFrame(left, bottom, s2, s2); r[6].setFrame(w2, bottom, s2, s2); r[7].setFrame(right, bottom, s2, s2); } } } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/ExecutableChange.java0000644000175000017500000000137311256667036026000 0ustar gregoagregoa/* * $Id: ExecutableChange.java,v 1.1 2006/10/27 15:33:08 david Exp $ * Copyright (c) 2001-2005, Gaudenz Alder * * All rights reserved. * * See LICENSE file for license details. If you are unable to locate * this file please contact info (at) jgraph (dot) com. */ package org.jgraph.graph; import javax.swing.undo.AbstractUndoableEdit; /** * The interface executable changes must adhere to */ public abstract class ExecutableChange extends AbstractUndoableEdit { /* * (non-Javadoc) * * @see javax.swing.undo.UndoableEdit#undo() */ public void undo() { execute(); } /* * (non-Javadoc) * * @see javax.swing.undo.UndoableEdit#redo() */ public void redo() { execute(); } /** * */ public abstract void execute(); } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/CellMapper.java0000644000175000017500000000161711256667036024636 0ustar gregoagregoa/* * @(#)CellMapper.java 1.0 03-JUL-04 * * Copyright (c) 2001-2005 Gaudenz Alder * */ package org.jgraph.graph; /** * Defines the requirements for objects that may be used as a cell mapper. A * cell mapper is able to return the view of a cell, given a reference to that * cell object. It is basically a cell to cell view mapping */ public interface CellMapper { /** * Returns the view that is associated with cell. * * @param create * whether a new view should created if a view does not already * exist */ CellView getMapping(Object cell, boolean create); /** * Inserts the association between cell and view. * * @param cell * the cell that constitutes the model element * @param view * the view that constitutes the view element */ void putMapping(Object cell, CellView view); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/DefaultRealEditor.java0000644000175000017500000000306511256667036026150 0ustar gregoagregoa/* * @(#)DefaultCellEditor.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.Component; import javax.swing.DefaultCellEditor; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JTextField; import org.jgraph.JGraph; /** * The default editor for graph cells. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class DefaultRealEditor extends DefaultCellEditor implements GraphCellEditor { // // Constructors // /** * Constructs a DefaultCellEditor that uses a text field. * * @param textField * a JTextField object used as the editor */ public DefaultRealEditor(final JTextField textField) { super(textField); setClickCountToStart(1); } /** * Constructs a DefaultCellEditor object that uses a check box. * * @param checkBox * a JCheckBox object */ public DefaultRealEditor(final JCheckBox checkBox) { super(checkBox); } /** * Constructs a DefaultCellEditor object that uses a combo box. * * @param comboBox * a JComboBox object */ public DefaultRealEditor(final JComboBox comboBox) { super(comboBox); } // // GraphCellEditor Interface // public Component getGraphCellEditorComponent(JGraph graph, Object value, boolean isSelected) { String stringValue = graph.convertValueToString(value); delegate.setValue(stringValue); if (editorComponent instanceof JTextField) ((JTextField) editorComponent).selectAll(); return editorComponent; } } // End of class JCellEditor libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/CellHandle.java0000644000175000017500000000267611256667036024613 0ustar gregoagregoa/* * @(#)CellHandle.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.Graphics; import java.awt.event.MouseEvent; /** * Defines the requirements for objects that may be used as handles. * Handles are used to interactively manipulate a cell's appearance. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public interface CellHandle { /** * Paint the handle on the given graphics object once. * * @param g the graphics object to paint the handle on */ void paint(Graphics g); /** * Paint the handle on the given graphics object during mouse * operations. * * @param g the graphics object to paint the handle on */ void overlay(Graphics g); /** * Messaged when the mouse is moved. * * @param event the mouse event to be processed */ void mouseMoved(MouseEvent event); /** * Messaged when a mouse button is pressed. * * @param event the mouse event to be processed */ void mousePressed(MouseEvent event); /** * Messaged when the user drags the selection. * The Controller is responsible to determine whether the mouse is * inside the parent graph or not. * * @param event the drag event to be processed */ void mouseDragged(MouseEvent event); /** * Messaged when the drag operation has * terminated with a drop. * * @param event the drop event to be processed */ void mouseReleased(MouseEvent event); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/AttributeMap.java0000644000175000017500000004233011256667036025210 0ustar gregoagregoa/* * @(#)AttributeMap 1.0 03-JUL-04 * * Copyright (c) 2001-2005 Gaudenz Alder * * See LICENSE file in distribution for licensing details of this source file */ package org.jgraph.graph; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * A map specifically for the storage of attributes of graph cells. The main * advantage of the AttributeMap is that it allows to override cell view * behaviour for scaling, translation, diffing, and cloning on a per instance * basis without having to change the GraphConstants class */ public class AttributeMap extends Hashtable implements Cloneable { /** * Shared empty attribute map to return instead of null in applyMap. */ public static transient AttributeMap emptyAttributeMap = new AttributeMap(0) { public Object clone() { return this; } }; /** * Creates a new attribute map with an initial capacity of 8. */ public AttributeMap() { super(8); } /** * Creates a new attribute map with the specified initial capacity * * @param initialCapacity * the initial capacity of the new map */ public AttributeMap(int initialCapacity) { super(initialCapacity); } /** * Constructs a new, empty hashtable with the specified initial capacity and * the specified load factor. * * @param initialCapacity * the initial capacity of the hashtable. * @param loadCapacity * the load factor of the hashtable. */ public AttributeMap(int initialCapacity, float loadCapacity) { super(initialCapacity, loadCapacity); } /** * Constructs a new AttributeMap with the same mappings as the given Map. * * @param map * the input map to copy */ public AttributeMap(Map map) { super(map); } /** * Creates a point of suitable type for this attribute map * * @return a new point */ public Point2D createPoint() { return new SerializablePoint2D(); } /** * Creates a point of suitable type for this attribute map with the same * values as the point passed in * * @param p * the point whose values the new point are to be based on * @return a new copy of the point passed in */ public Point2D createPoint(Point2D p) { if (p != null) { return createPoint(p.getX(), p.getY()); } return null; } /** * Creates a point of suitable type for this attribute map with the same * values as those passed in * * @param x * the x-coordinate position of the new point * @param y * the y-coordinate position of the new point * @return a new point at the coordinates passed in */ public Point2D createPoint(double x, double y) { return new SerializablePoint2D(x, y); } /** * Creates a rectangle of suitable type for this attribute map * * @return a new rectangle */ public Rectangle2D createRect() { return new SerializableRectangle2D(); } /** * Creates a rectangle of suitable type for this attribute map with the same * values as those passed in * * @param x * the x-coordinate position of the new rectangle * @param y * the y-coordinate position of the new rectangle * @param w * the width of the new rectangle * @param h * the height of the new rectangle * @return a new rectangle at the coordinates and of the dimensions passed * in */ public Rectangle2D createRect(double x, double y, double w, double h) { return new SerializableRectangle2D(x, y, w, h); } /** * Creates a rectangle of suitable type for this attribute map at the * position of the point passed in * * @param pt * the position of the new rectangle * @return a new rectangle the specified coordinates of zero size */ public Rectangle2D createRect(Point2D pt) { return createRect(pt, 0); } /** * Creates a rectangle of suitable type for this attribute map at the * position of the point passed in with lengths size * * @param pt * the position of the new rectangle * @param size * the length of both sides of the rectangle * @return a new rectangle the specified position and dimensions */ public Rectangle2D createRect(Point2D pt, double size) { if (pt != null) { return createRect(pt.getX(), pt.getY(), size, size); } return null; } /** * Clones the rectangle passed in * * @param rect * the rectangle to clone * * @return a copy of the rectangle passed in */ public Rectangle2D createRect(Rectangle2D rect) { if (rect != null) { return createRect(rect.getX(), rect.getY(), rect.getWidth(), rect .getHeight()); } return null; } /** * Creates a rectangle of suitable type for this attribute map * * @param x * the x-coordinate position of the new rectangle * @param y * the y-coordinate position of the new rectangle * @param w * the width of the new rectangle * @param h * the height of the new rectangle * @param grow1 * the amount both dimensions are to be increased by and the * position coorindates of the rectangle are to be decreased by * @param grow2 * the additional amount by which both dimensions are to be * increased by * @return a new rectangle at the coordinates and of the dimensions passed * in */ public Rectangle2D createRect(double x, double y, double w, double h, double grow1, double grow2) { return createRect(x - grow1, y - grow1, w + grow1 + grow2, h + grow1 + grow2); } /** * Creates a clone of the rectangle passed in and manipulates it by * grow1 and grow2 * * @param grow1 * the amount both dimensions are to be increased by and the * position coorindates of the rectangle are to be decreased by * @param grow2 * the additional amount by which both dimensions are to be * increased by * @return a new rectangle at the coordinates and of the dimensions passed * in */ public Rectangle2D createRect(Rectangle2D rect, double grow1, double grow2) { if (rect != null) { return createRect(rect.getX(), rect.getY(), rect.getWidth(), rect .getHeight(), grow1, grow2); } return null; } /** * Apply the change to this views attributes. * change must be a Map previously obtained * from this object. * * @param change * the change to apply * @return a map that may be used to undo the change to target. */ public AttributeMap applyMap(Map change) { AttributeMap undo = new AttributeMap(); if (change != null) { // Handle Remove All if (GraphConstants.isRemoveAll(change)) { undo.putAll(this); clear(); } // Handle Remove Individual Object[] remove = GraphConstants.getRemoveAttributes(change); if (remove != null) { // don't store command for (int i = 0; i < remove.length; i++) { Object oldValue = remove(remove[i]); if (oldValue != null) undo.put(remove[i], oldValue); } } // Attributes that were empty are added to removeattibutes. // Performance and transient memory peak are reduced by lazily // instantiating the set. Set removeAttributes = null; Iterator it = change.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); Object key = entry.getKey(); if (!key.equals(GraphConstants.REMOVEALL) && !key.equals(GraphConstants.REMOVEATTRIBUTES) && !key.equals(GraphConstants.VALUE)) { Object oldValue = applyValue(key, entry.getValue()); if (oldValue == null) { if (removeAttributes == null) { removeAttributes = new HashSet(); } removeAttributes.add(key); } else { undo.put(key, oldValue); } } } if (removeAttributes != null && !removeAttributes.isEmpty()) { GraphConstants.setRemoveAttributes(undo, removeAttributes .toArray()); } } return undo; } /** * Apply the key to value * * @param key * the map key whose value is to be altered * @param value * the new value to be applied to the specified key * @return the old value. */ public Object applyValue(Object key, Object value) { // In all other cases we put the new value into the // map. If we encounter a list (of points) or rectangle // these will be cloned before insertion. Cloning includes // replacing the rectangle/points with serializable objects. if (value instanceof Rectangle2D) value = createRect((Rectangle2D) value); if (value instanceof Point2D) value = createPoint((Point2D) value); if (value instanceof Point2D[]) value = clonePoints((Point2D[]) value); if (value instanceof List) // FIXME: PointList interface? value = clonePoints((List) value); return put(key, value); } /** * Returns a list where all instances of PortView are replaced by their * correspnding Point instance. * * @param points * the points to be cloned * @return the cloned points */ public Point2D[] clonePoints(Point2D[] points) { List pts = clonePoints(points, true); Point2D[] newPoints = new Point2D[pts.size()]; pts.toArray(newPoints); return newPoints; } /** * Returns a list where all instances of PortView are replaced by their * correspnding Point instance. * * @param points * the points to be cloned * @return the cloned points */ public List clonePoints(List points) { return clonePoints(points.toArray(), true); } /** * Returns a list where all instances of PortView are replaced by their * correspnding Point instance. */ public List clonePoints(Object[] points, boolean convertPortViews) { // TODO: Change the list in-place? ArrayList newList = new ArrayList(points.length); for (int i = 0; i < points.length; i++) { // Clone Point Object point = points[i]; if (point instanceof PortView && convertPortViews) point = createPoint(((PortView) point).getLocation()); else if (point instanceof Point2D) point = createPoint((Point2D) point); newList.add(point); } return newList; } /** * Translates the maps in c using * translate(Map, int, int). */ public static void translate(Collection c, double dx, double dy) { Iterator it = c.iterator(); while (it.hasNext()) { Object map = it.next(); if (map instanceof AttributeMap) ((AttributeMap) map).translate(dx, dy); } } /** * Translates map by the given amount. */ public void translate(double dx, double dy) { // Translate Bounds if (GraphConstants.isMoveable(this)) { Rectangle2D bounds = GraphConstants.getBounds(this); if (bounds != null) { int moveableAxis = GraphConstants.getMoveableAxis(this); if (moveableAxis == GraphConstants.X_AXIS) dy = 0; else if (moveableAxis == GraphConstants.Y_AXIS) dx = 0; bounds.setFrame(bounds.getX() + dx, bounds.getY() + dy, bounds .getWidth(), bounds.getHeight()); } // Translate Points List points = GraphConstants.getPoints(this); if (points != null) { for (int i = 0; i < points.size(); i++) { Object obj = points.get(i); if (obj instanceof Point2D) { Point2D pt = (Point2D) obj; pt.setLocation(pt.getX() + dx, pt.getY() + dy); } } } } } /** * Scales map by the given amount. */ public void scale(double sx, double sy, Point2D origin) { // Scale Bounds Rectangle2D bounds = GraphConstants.getBounds(this); if (bounds != null) { Point2D p = createPoint(bounds.getX(), bounds.getY()); Point2D loc = (Point2D) p.clone(); p.setLocation(origin.getX() + Math.round((p.getX() - origin.getX()) * sx), origin .getY() + Math.round((p.getY() - origin.getY()) * sy)); if (!p.equals(loc)) // Scale Location translate(p.getX() - loc.getX(), p.getY() - loc.getY()); int sizeableAxis = GraphConstants.getSizeableAxis(this); if (sizeableAxis == GraphConstants.X_AXIS) sy = 1; else if (sizeableAxis == GraphConstants.Y_AXIS) sx = 1; double w = Math.max(1, Math.round(bounds.getWidth() * sx)); double h = Math.max(1, Math.round(bounds.getHeight() * sy)); // Scale Bounds bounds.setFrame(bounds.getX(), bounds.getY(), w, h); } // Scale Points List points = GraphConstants.getPoints(this); if (points != null) { Iterator it = points.iterator(); while (it.hasNext()) { Object obj = it.next(); if (obj instanceof Point2D) { // Scale Point Point2D loc = (Point2D) obj; Point2D p = (Point2D) loc.clone(); p.setLocation(origin.getX() + Math.round((p.getX() - origin.getX()) * sx), origin.getY() + Math.round((p.getY() - origin.getY()) * sy)); // Move Point loc.setLocation(p); } } } } /** * Returns a new map that contains all (key, value)-pairs of * newState where either key is not used or value is * different for key in oldState. In other words, this * method removes the common entries from oldState and newState, and returns * the "difference" between the two. * * This method never returns null. */ public Map diff(Map newState) { Map diff = new Hashtable(); Iterator it = newState.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); Object key = entry.getKey(); Object newValue = entry.getValue(); Object oldValue = get(key); if (oldValue == null || !oldValue.equals(newValue)) diff.put(key, newValue); } return diff; } /** * Returns a clone of map, from keys to values. If the map * contains bounds or points, these are cloned as well. References to * PortViews are replaces by points.
* Note: Extend this method to clone custom user objects. */ public Object clone() { // TODO, is cloning the hash table excessive? return cloneEntries((AttributeMap) super.clone()); } /** * Clones special object entried in the given map. */ public AttributeMap cloneEntries(AttributeMap newMap) { // Clone Bounds Rectangle2D bounds = GraphConstants.getBounds(newMap); if (bounds != null) GraphConstants.setBounds(newMap, (Rectangle2D) (bounds.clone())); // Clone List Of Points List points = GraphConstants.getPoints(newMap); if (points != null) GraphConstants.setPoints(newMap, clonePoints(points)); // Clone extra label positions Point2D[] positions = GraphConstants.getExtraLabelPositions(newMap); if (positions != null) GraphConstants.setExtraLabelPositions(newMap, clonePoints(positions)); // Clone Edge Label Point2D label = GraphConstants.getLabelPosition(newMap); if (label != null) GraphConstants.setLabelPosition(newMap, (Point2D) label.clone()); return newMap; } public static class SerializablePoint2D extends Point2D.Double implements Serializable { public SerializablePoint2D() { super(); } public SerializablePoint2D(double x, double y) { super(x, y); } public void setX(double x) { setLocation(x, getY()); } public void setY(double y) { setLocation(getX(), y); } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(new java.lang.Double(getX())); out.writeObject(new java.lang.Double(getY())); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); java.lang.Double x = (java.lang.Double) in.readObject(); java.lang.Double y = (java.lang.Double) in.readObject(); setLocation(x.doubleValue(), y.doubleValue()); } } public static class SerializableRectangle2D extends Rectangle2D.Double implements Serializable { public SerializableRectangle2D() { super(); } public SerializableRectangle2D(double x, double y, double width, double height) { super(x, y, width, height); } public void setX(double x) { setFrame(x, getY(), getWidth(), getHeight()); } public void setY(double y) { setFrame(getX(), y, getWidth(), getHeight()); } public void setWidth(double width) { setFrame(getX(), getY(), width, getHeight()); } public void setHeight(double height) { setFrame(getX(), getY(), getWidth(), height); } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(new java.lang.Double(getX())); out.writeObject(new java.lang.Double(getY())); out.writeObject(new java.lang.Double(getWidth())); out.writeObject(new java.lang.Double(getHeight())); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); java.lang.Double x = (java.lang.Double) in.readObject(); java.lang.Double y = (java.lang.Double) in.readObject(); java.lang.Double width = (java.lang.Double) in.readObject(); java.lang.Double height = (java.lang.Double) in.readObject(); setFrame(x.doubleValue(), y.doubleValue(), width.doubleValue(), height.doubleValue()); } } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/GraphContext.java0000644000175000017500000001772011256667036025222 0ustar gregoagregoa/* * @(#)GraphContext.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.util.ArrayList; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.jgraph.JGraph; /* * This object is used for interactive operations such as move and copy. It * offers two basic operations: 1. Create temporary views for a set of cells, * and 2. Create temporary views for a set of edges that are connected to a set * of cells.

Note:The views are consistent subgraphs. * Consequently, if an edge points to a selected and an unselected port, the * selected port is replaced by its temporary view, but the unselected port is * not. * * @version 1.0 1/1/02 @author Gaudenz Alder */ public class GraphContext implements CellMapper { /** * Switch to enable the preview of edge groups, that is, edges that 1 or * more children, as part of the context cells. */ public static boolean PREVIEW_EDGE_GROUPS = false; /** Reference to the parent graph. */ protected JGraph graph; /** Reference to the graphs GraphLayoutCache. */ protected transient GraphLayoutCache graphLayoutCache; /** Reference to the cells. */ protected Object[] cells; /** Set of all cells including all descendants. */ protected Set allCells, cellSet; /** Number of all descendants without ports. */ protected int cellCount; /** Map of (cell, view) pairs including ports. */ protected Map views = new Hashtable(); /** * Constructs a graph context for cells with respect to the * connections defined in the model, and the views in the view of * graph. */ public GraphContext(JGraph graph, Object[] cells) { GraphModel model = graph.getModel(); allCells = new HashSet(DefaultGraphModel.getDescendants(model, cells)); graphLayoutCache = graph.getGraphLayoutCache(); this.graph = graph; this.cells = cells; // Count Visible Non-Port Cells cellSet = new HashSet(); Iterator it = allCells.iterator(); while (it.hasNext()) { Object cell = it.next(); if (graphLayoutCache.isVisible(cell)) { cellSet.add(cell); if (!model.isPort(cell)) cellCount++; } } } /** * Returns true if this object contains no cells. */ public boolean isEmpty() { return (cells == null || cells.length == 0); } /** * Returns the number of all objects (cells and children) in this object. */ public int getDescendantCount() { return cellCount; } /** * Returns the graph that was passed to the constructor. */ public JGraph getGraph() { return graph; } /** * Returns the array that was passed to the constructor. */ public Object[] getCells() { return cells; } /** * Returns true if node or one of its * ancestors is contained in this object and visible in the original graph. */ public boolean contains(Object node) { return cellSet.contains(node); } /** * Returns an new consistent array of views for cells. */ public CellView[] createTemporaryCellViews() { CellView[] cellViews = new CellView[cells.length]; for (int i = 0; i < cells.length; i++) // Get View For Cell cellViews[i] = getMapping(cells[i], true); return cellViews; } /** * Returns an new consistent array of views for the ports. */ public CellView[] createTemporaryPortViews() { GraphModel model = graph.getModel(); ArrayList result = new ArrayList(); Iterator it = allCells.iterator(); while (it.hasNext()) { Object cand = it.next(); if (model.isPort(cand) && graph.getGraphLayoutCache().isVisible(cand)) result.add(getMapping(cand, true)); } // List -> CellView[] Conversion CellView[] array = new CellView[result.size()]; result.toArray(array); return array; } /** * Returns an new consistent array of views for the edges that are connected * to and not contained in cells. */ public CellView[] createTemporaryContextViews() { return createTemporaryContextViews(cellSet); } /** * Returns an new consistent array of views for the edges that are connected * to and not contained in cellSet. */ public CellView[] createTemporaryContextViews(Set cellSet) { Object[] cells = cellSet.toArray(); // Retrieve Edges From Model (recursively for edges connected // to edges connected to edges...) List result = new ArrayList(); Set delta = DefaultGraphModel.getEdges(graph.getModel(), cells); do { // Iterate over Edges Iterator it = delta.iterator(); while (it.hasNext()) { Object obj = it.next(); CellView edge = graphLayoutCache.getMapping(obj, false); // If Edge not in cellset, add its view is visible in graphview if (!cellSet.contains(obj) && graphLayoutCache.isVisible(obj) && edge != null && (PREVIEW_EDGE_GROUPS || edge.isLeaf())) { // Note: Do not use getMapping, it ignores the create flag CellView preview = createMapping(obj); result.add(preview); // Create temporary children which refer to this // preview and adopt children in the preview CellView[] children = preview.getChildViews(); for (int i=0; i CellView[] Conversion CellView[] array = new CellView[result.size()]; result.toArray(array); return array; } /** * Returns the CellView that is mapped to cell * in the graph context. New views are created based on whether cell is * contained in the context. The create-flag is ignored. */ public CellView getMapping(Object cell, boolean create) { if (cell != null) { CellView view = (CellView) views.get(cell); if (view != null) return view; else if (contains(cell) || (graph.getModel().isPort(cell) && create && graph .getGraphLayoutCache().isVisible(cell))) return createMapping(cell); else return graphLayoutCache.getMapping(cell, false); } return null; } public CellView createMapping(Object cell) { CellView view = graphLayoutCache.getFactory().createView( graph.getModel(), cell); putMapping(cell, view); view.refresh(graph.getGraphLayoutCache(), this, true); // Create Dependent Views // Fetch Attributes From Original View CellView src = graphLayoutCache.getMapping(cell, false); if (src != null) { view.changeAttributes(graphLayoutCache, (AttributeMap) src.getAttributes().clone()); // Inserts portviews into points list view.refresh(graph.getGraphLayoutCache(), this, false); } return view; } /** * Disconnects the edges in cells from the sources and * targets that are not in this context and returns a ConnectionSet that * defines the disconnection. */ public ConnectionSet disconnect(CellView[] cells) { ConnectionSet cs = new ConnectionSet(); for (int i = 0; i < cells.length; i++) { if (cells[i] instanceof EdgeView) { EdgeView view = (EdgeView) cells[i]; CellView port = view.getSource(); if (GraphConstants.isDisconnectable(view.getAllAttributes())) { if (port != null && GraphConstants.isDisconnectable(port .getParentView().getAllAttributes()) && !contains(port.getCell())) { view.setSource(null); cs.disconnect(view.getCell(), true); } port = view.getTarget(); if (port != null && GraphConstants.isDisconnectable(port .getParentView().getAllAttributes()) && !contains(port.getCell())) { view.setTarget(null); cs.disconnect(view.getCell(), false); } } } } return cs; } /** * Associates cell with view in the graph * context. */ public void putMapping(Object cell, CellView view) { views.put(cell, view); } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/CellViewFactory.java0000644000175000017500000000105111256667036025644 0ustar gregoagregoa/* * @(#)CellViewFactory.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; /** * Defines the requirements for objects that may be used as a * cell view factory. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public interface CellViewFactory { /** * Constructs a view for the specified cell and associates it * with the specified object using the specified CellMapper. * * @param cell reference to the object in the model */ CellView createView(GraphModel model, Object cell); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/DefaultGraphSelectionModel.java0000644000175000017500000004272011256667036030007 0ustar gregoagregoa/* * @(#)DefaultGraphSelectionModel.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.beans.PropertyChangeListener; import java.io.Serializable; import java.util.ArrayList; import java.util.EventListener; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import java.util.Vector; import javax.swing.event.EventListenerList; import javax.swing.event.SwingPropertyChangeSupport; import org.jgraph.JGraph; import org.jgraph.event.GraphSelectionEvent; import org.jgraph.event.GraphSelectionListener; /** * Default implementation of GraphSelectionModel. Listeners are notified * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class DefaultGraphSelectionModel implements GraphSelectionModel, Cloneable, Serializable { /** Property name for selectionMode. */ public static final String SELECTION_MODE_PROPERTY = "selectionMode"; /** Value that represents selected state in cellStates. */ public static final int SELECTED = -1; /** Object value that represents the unselected state in cellStates. */ public static final Integer UNSELECTED = new Integer(0); /** Reference to the parent graph. Used to find parents and childs. */ protected JGraph graph; /** Used to message registered listeners. */ protected SwingPropertyChangeSupport changeSupport; /** Event listener list. */ protected EventListenerList listenerList = new EventListenerList(); /** * Mode for the selection, will be either SINGLE_TREE_SELECTION, * CONTIGUOUS_TREE_SELECTION or DISCONTIGUOUS_TREE_SELECTION. */ protected int selectionMode; /** Boolean that indicates if the model allows stepping-into groups. */ protected boolean childrenSelectable = true; /** Maps the cells to their selection state. */ protected Map cellStates = new Hashtable(); /** List that contains the selected items. */ protected Set selection = new LinkedHashSet(); /** Constructs a DefaultGraphSelectionModel for the specified graph. */ public DefaultGraphSelectionModel(JGraph graph) { this.graph = graph; } /** * Sets the selection mode, which must be one of SINGLE_TREE_SELECTION, */ public void setSelectionMode(int mode) { int oldMode = selectionMode; selectionMode = mode; if (selectionMode != GraphSelectionModel.MULTIPLE_GRAPH_SELECTION && selectionMode != GraphSelectionModel.SINGLE_GRAPH_SELECTION) selectionMode = GraphSelectionModel.MULTIPLE_GRAPH_SELECTION; if (oldMode != selectionMode && changeSupport != null) changeSupport.firePropertyChange(SELECTION_MODE_PROPERTY, new Integer(oldMode), new Integer(selectionMode)); } /** * Returns the selection mode, one of SINGLE_TREE_SELECTION, * DISCONTIGUOUS_TREE_SELECTION or * CONTIGUOUS_TREE_SELECTION. */ public int getSelectionMode() { return selectionMode; } /** * Sets if the selection model allows the selection of children. */ public void setChildrenSelectable(boolean flag) { childrenSelectable = flag; } /** * Returns true if the selection model allows the selection of children. */ public boolean isChildrenSelectable() { return childrenSelectable; } /** * Hook for subclassers for fine-grained control over stepping-into cells. * This implementation returns childrenSelectable&& * isCellSelected. */ protected boolean isChildrenSelectable(Object cell) { AttributeMap attr = graph.getModel().getAttributes(cell); if (attr != null && childrenSelectable) return GraphConstants.isChildrenSelectable(attr); return childrenSelectable; } /** * Selects the specified cell. * * @param cell * the cell to select */ public void setSelectionCell(Object cell) { if (cell == null) setSelectionCells(null); else setSelectionCells(new Object[] { cell }); } /** * Sets the selection to cells. If this represents a change * the GraphSelectionListeners are notified. Potentially paths will be held * by this object; in other words don't change any of the objects in the * array once passed in. * * @param cells * new selection */ public void setSelectionCells(Object[] cells) { if (cells != null) { if (selectionMode == GraphSelectionModel.SINGLE_GRAPH_SELECTION && cells.length > 0) cells = new Object[] { cells[cells.length - 1] }; cellStates.clear(); Vector change = new Vector(); Set newSelection = new LinkedHashSet(); for (int i = 0; i < cells.length; i++) { if (cells[i] != null) { selection.remove(cells[i]); change.addElement(new CellPlaceHolder(cells[i], !selection .remove(cells[i]))); select(newSelection, cells[i]); Object parent = graph.getModel().getParent(cells[i]); if (parent != null) change.addElement(new CellPlaceHolder(parent, false)); } } Iterator it = selection.iterator(); while (it.hasNext()) { Object cell = it.next(); while (cell != null) { change.addElement(new CellPlaceHolder(cell, false)); cell = graph.getModel().getParent(cell); } } selection = newSelection; if (change.size() > 0) { notifyCellChange(change); } } } /** * Adds the specified cell to the current selection * * @param cell * the cell to add to the current selection */ public void addSelectionCell(Object cell) { if (cell != null) addSelectionCells(new Object[] { cell }); } /** * Adds cells to the current selection. * * @param cells * the cells to be added to the current selection */ public void addSelectionCells(Object[] cells) { if (cells != null) { if (selectionMode == GraphSelectionModel.SINGLE_GRAPH_SELECTION) setSelectionCells(cells); else { Vector change = new Vector(); for (int i = 0; i < cells.length; i++) { if (cells[i] != null) { boolean newness = select(selection, cells[i]); if (newness) { change.addElement(new CellPlaceHolder(cells[i], true)); Object parent = graph.getModel() .getParent(cells[i]); if (parent != null) change.addElement(new CellPlaceHolder(parent, false)); } } } if (change.size() > 0) notifyCellChange(change); } } } /** * Removes the specified cell from the selection. * * @param cell * the cell to remove from the current selection */ public void removeSelectionCell(Object cell) { if (cell != null) removeSelectionCells(new Object[] { cell }); } /** * Removes the specified cells from the selection. * * @param cells * the cells to remove from the current selection */ public void removeSelectionCells(Object[] cells) { if (cells != null) { Vector change = new Vector(); for (int i = 0; i < cells.length; i++) { if (cells[i] != null) { boolean removed = deselect(cells[i]); if (removed) { change.addElement(new CellPlaceHolder(cells[i], false)); Object parent = graph.getModel().getParent(cells[i]); if (parent != null) change .addElement(new CellPlaceHolder(parent, false)); } } } if (change.size() > 0) notifyCellChange(change); } } /** * Returns the cells that are currently selectable. The array is ordered so * that the top-most cell appears first.
*/ public Object[] getSelectables() { if (isChildrenSelectable()) { List result = new ArrayList(); // Roots Are Always Selectable Stack s = new Stack(); GraphModel model = graph.getModel(); for (int i = 0; i < model.getRootCount(); i++) s.add(model.getRootAt(i)); while (!s.isEmpty()) { Object cell = s.pop(); AttributeMap attrs = graph.getAttributes(cell); if (!model.isPort(cell) && (attrs == null || GraphConstants.isSelectable(attrs))) result.add(cell); if (isChildrenSelectable(cell)) { for (int i = 0; i < model.getChildCount(cell); i++) s.add(model.getChild(cell, i)); } } return result.toArray(); } return graph.getRoots(); } /** * Returns the first cell in the selection. This is useful if there if only * one item currently selected. */ public Object getSelectionCell() { if (selection != null && selection.size() > 0) return selection.toArray()[0]; return null; } /** * Returns the cells in the selection. This will return null (or an empty * array) if nothing is currently selected. */ public Object[] getSelectionCells() { if (selection != null) return selection.toArray(); return null; } /** * Returns the number of paths that are selected. */ public int getSelectionCount() { return (selection == null) ? 0 : selection.size(); } /** * Returns true if the cell, cell, is in the current * selection. */ public boolean isCellSelected(Object cell) { int count = getSelectedChildCount(cell); return (count == SELECTED); } /** * Returns true if the cell, cell, has selected children. */ public boolean isChildrenSelected(Object cell) { int count = getSelectedChildCount(cell); return (count > 0); } /** * Returns true if the selection is currently empty. */ public boolean isSelectionEmpty() { return (selection.isEmpty()); } /** * Empties the current selection. If this represents a change in the current * selection, the selection listeners are notified. */ public void clearSelection() { if (selection != null) { Vector change = new Vector(); Iterator it = cellStates.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); Object cell = entry.getKey(); while (cell != null) { change.addElement(new CellPlaceHolder(cell, false)); cell = graph.getModel().getParent(cell); } } selection.clear(); cellStates.clear(); if (change.size() > 0) notifyCellChange(change); } } // // Internal Datastructures // /** * Returns the number of selected childs for cell. */ protected int getSelectedChildCount(Object cell) { if (cell != null) { Integer state = (Integer) cellStates.get(cell); if (state == null) { state = UNSELECTED; cellStates.put(cell, state); } return state.intValue(); } return 0; } /** * Sets the number of selected childs for cell to * count. */ protected void setSelectedChildCount(Object cell, int count) { Integer i = new Integer(count); cellStates.put(cell, i); } /** * Selects a single cell and updates all datastructures. No listeners are * notified. Override this method to control individual cell selection. */ protected boolean select(Set set, Object cell) { AttributeMap attrs = graph.getAttributes(cell); if (!isCellSelected(cell) && graph.getGraphLayoutCache().isVisible(cell) && (attrs == null || GraphConstants.isSelectable(attrs))) { GraphModel model = graph.getModel(); // Deselect and Update All Parents Object parent = model.getParent(cell); while (parent != null) { int count = getSelectedChildCount(parent); // Deselect Selected Parents if (count == SELECTED) count = 0; // Increase Child Count count++; setSelectedChildCount(parent, count); // Remove From Selection selection.remove(parent); // Next Parent parent = model.getParent(parent); } // Deselect All Children Object[] tmp = new Object[] { cell }; List childs = DefaultGraphModel.getDescendants(model, tmp); // Remove Current Cell From Flat-View // TODO check performance of next line childs.remove(cell); Iterator it = childs.iterator(); while (it.hasNext()) { Object child = it.next(); if (child != null && !model.isPort(child)) { // Remove Child From Selection selection.remove(child); // Remove Child State cellStates.remove(child); } } // Set Selected State for Current setSelectedChildCount(cell, SELECTED); // Add Current To HashSet and Return return set.add(cell); } return false; } /** * Deselects a single cell and updates all datastructures. No listeners are * notified. */ protected boolean deselect(Object cell) { if (isCellSelected(cell)) { // Update All Parents Object parent = graph.getModel().getParent(cell); boolean firstParent = true; int change = -1; while (parent != null && change != 0) { int count = getSelectedChildCount(parent); count += change; // Select First Parent If No More Children if (count == 0 && firstParent) { change = 0; count = SELECTED; selection.add(parent); } // Update Selection Count setSelectedChildCount(parent, count); // Next Parent parent = graph.getModel().getParent(parent); firstParent = false; } // Remove State of Current Cell cellStates.remove(cell); // Remove Current from Selection and Return return selection.remove(cell); } return false; } // // Listeners // /** * Adds x to the list of listeners that are notified each time the set of * selected TreePaths changes. * * @param x * the new listener to be added */ public void addGraphSelectionListener(GraphSelectionListener x) { listenerList.add(GraphSelectionListener.class, x); } /** * Removes x from the list of listeners that are notified each time the set * of selected TreePaths changes. * * @param x * the listener to remove */ public void removeGraphSelectionListener(GraphSelectionListener x) { listenerList.remove(GraphSelectionListener.class, x); } /** * Notifies all listeners that are registered for tree selection events on * this object. * * @see #addGraphSelectionListener * @see EventListenerList */ protected void fireValueChanged(GraphSelectionEvent e) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // TreeSelectionEvent e = null; // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == GraphSelectionListener.class) { // Lazily create the event: // if (e == null) // e = new ListSelectionEvent(this, firstIndex, lastIndex); ((GraphSelectionListener) listeners[i + 1]).valueChanged(e); } } } /** * Returns an array of all the listeners of the given type that were added * to this model. * * @return all of the objects receiving listenerType * notifications from this model * * @since 1.3 */ public EventListener[] getListeners(Class listenerType) { return listenerList.getListeners(listenerType); } /** * Adds a PropertyChangeListener to the listener list. The listener is * registered for all properties. *

* A PropertyChangeEvent will get fired when the selection mode changes. * * @param listener * the PropertyChangeListener to be added */ public synchronized void addPropertyChangeListener( PropertyChangeListener listener) { if (changeSupport == null) { changeSupport = new SwingPropertyChangeSupport(this); } changeSupport.addPropertyChangeListener(listener); } /** * Removes a PropertyChangeListener from the listener list. This removes a * PropertyChangeListener that was registered for all properties. * * @param listener * the PropertyChangeListener to be removed */ public synchronized void removePropertyChangeListener( PropertyChangeListener listener) { if (changeSupport == null) { return; } changeSupport.removePropertyChangeListener(listener); } /** * Notifies listeners of a change in path. changePaths should * contain instances of PathPlaceHolder. */ protected void notifyCellChange(Vector changedCells) { int cCellCount = changedCells.size(); boolean[] newness = new boolean[cCellCount]; Object[] cells = new Object[cCellCount]; CellPlaceHolder placeholder; for (int counter = 0; counter < cCellCount; counter++) { placeholder = (CellPlaceHolder) changedCells.elementAt(counter); newness[counter] = placeholder.isNew; cells[counter] = placeholder.cell; } GraphSelectionEvent event = new GraphSelectionEvent(this, cells, newness); fireValueChanged(event); } /** * Returns a clone of this object with the same selection. This method does * not duplicate selection listeners and property listeners. * * @exception CloneNotSupportedException * never thrown by instances of this class */ public Object clone() throws CloneNotSupportedException { DefaultGraphSelectionModel clone = (DefaultGraphSelectionModel) super .clone(); clone.changeSupport = null; if (selection != null) clone.selection = new LinkedHashSet(selection); clone.listenerList = new EventListenerList(); return clone; } /** * Holds a path and whether or not it is new. */ protected class CellPlaceHolder { protected boolean isNew; protected Object cell; protected CellPlaceHolder(Object cell, boolean isNew) { this.cell = cell; this.isNew = isNew; } /** * Returns the cell. * * @return Object */ public Object getCell() { return cell; } /** * Returns the isNew. * * @return boolean */ public boolean isNew() { return isNew; } /** * Sets the cell. * * @param cell * The cell to set */ public void setCell(Object cell) { this.cell = cell; } /** * Sets the isNew. * * @param isNew * The isNew to set */ public void setNew(boolean isNew) { this.isNew = isNew; } } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/GraphCell.java0000644000175000017500000000124611256667036024451 0ustar gregoagregoa/* * @(#)GraphCell.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.util.Map; /** * Defines the requirements for objects that appear as * GraphCells. This is the base interface for all GraphCells. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public interface GraphCell { /** * Returns the attributes of the cell. */ AttributeMap getAttributes(); /** * Changes the attributes of the cell. * * @deprecated Use getAttributes().applyMap */ Map changeAttributes(Map change); /** * Sets the attributes */ public void setAttributes(AttributeMap map); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/ConnectionSet.java0000644000175000017500000002176511256667036025373 0ustar gregoagregoa/* * @(#)ConnectionSet.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.io.Serializable; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * An object that represents a set of connections. Connections are equal, if * equals returns true. Connections that are added later replace earlier * connections. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class ConnectionSet implements Serializable { /** Contents of the connection set. */ protected Set connections = new HashSet(); /** Set of changed edges for the connection set. */ protected Set edges = new HashSet(); /** * Returns a connection set that represents the connection or disconnection * of cells in model based on * disconnect. */ public static ConnectionSet create(GraphModel m, Object[] cells, boolean disconnect) { ConnectionSet cs = new ConnectionSet(); for (int i = 0; i < cells.length; i++) { Object cell = cells[i]; // Edge if (m.isEdge(cell)) { if (disconnect) cs.disconnect(cell); else cs.connect(cell, m.getSource(cell), m.getTarget(cell)); } // Port Iterator it = m.edges(cell); while (it.hasNext()) { // Edge At Port Object edge = it.next(); if (m.getSource(edge) == cell) connect(cs, edge, cell, true, disconnect); else if (m.getTarget(edge) == cell) connect(cs, edge, cell, false, disconnect); } } return cs; } /** * Constructs an empty ConnectionSet. */ public ConnectionSet() { } /** * Constructs a ConnectionSet with one Connection. */ public ConnectionSet(Object edge, Object port, boolean source) { connect(edge, port, source); } /** * Constructs a connection set containing the specified connections and * updates the set of changed edges. */ public ConnectionSet(Set connections) { setConnections(connections); Iterator it = connections.iterator(); while (it.hasNext()) { Connection conn = (Connection) it.next(); edges.add(conn.getEdge()); } } /** * Constructs a ConnectionSet with two Connections (to the source and target * port of the edge). */ public ConnectionSet(Object edge, Object source, Object target) { connect(edge, source, target); } /** * Connect or disconnect edge from source and * target in cs based on * disconnect. */ protected static void connect(ConnectionSet cs, Object edge, Object port, boolean source, boolean disconnect) { if (disconnect) cs.disconnect(edge, source); else cs.connect(edge, port, source); } /** * Adds the connections in views to the connection set. */ public void addConnections(CellView[] views) { for (int i = 0; i < views.length; i++) { if (views[i] instanceof EdgeView) { EdgeView edgeView = (EdgeView) views[i]; Object edge = edgeView.getCell(); CellView sourceView = edgeView.getSource(); CellView targetView = edgeView.getTarget(); Object source = null; if (sourceView != null) source = sourceView.getCell(); Object target = null; if (targetView != null) target = targetView.getCell(); connect(edge, source, target); } } } /** * Connect edge to source and * target in the connection set. The previous connections * between edge and its source and target are replaced in the * set. */ public void connect(Object edge, Object source, Object target) { connect(edge, source, true); connect(edge, target, false); } /** * Connect edge to port passed in. The * source indicates if port is the source of * edge object. The previous connections between * edge and its source or target in the set is replaced. */ public void connect(Object edge, Object port, boolean source) { Connection c = new Connection(edge, port, source); connections.remove(c); connections.add(c); edges.add(edge); } /** * Disconnect edge from source and * target in the connection set. The previous connections * between edge and its source and target are replaced in the * set. */ public void disconnect(Object edge) { disconnect(edge, true); disconnect(edge, false); } /** * Disconnect edge from port. * source indicates if port is the source of * edge. The previous connections between edge * and its source or target in the set is replaced. */ public void disconnect(Object edge, boolean source) { connections.add(new Connection(edge, null, source)); edges.add(edge); } /** * Returns true if the connection set is empty. */ public boolean isEmpty() { return connections.isEmpty(); } /** * Returns the number of (edge, port)-pairs. */ public int size() { return connections.size(); } /** * Returns an Iterator for the connections in this set. */ public Iterator connections() { return connections.iterator(); } /** * Returns a Set for the edges in this connection set. * @deprecated Use getEdges */ public Set getChangedEdges() { return edges; } /** * Returns the source or target of the specified edge in this connection set * or null if the connection set contains no corresponding entry for the * edge. */ public Object getPort(Object edge, boolean source) { if (edges.contains(edge)) { Iterator it = connections.iterator(); while (it.hasNext()) { Connection c = (Connection) it.next(); if (c.getEdge() == edge && c.isSource() == source) return c.getPort(); } } return null; } /** * Creates a new connection set based on this connection set, where the * edges, and ports are mapped using map. If a port is not * found, the old port is used. If both, the edge and the port are not in * map, the entry is ignored. *

* Note: Consequently, unselected edges are only * reconnected at the first "paste" after a "cut", because in this case the * ConnectionSet is not cloned. */ public ConnectionSet clone(Map map) { ConnectionSet cs = new ConnectionSet(); Iterator it = connections(); while (it.hasNext()) { // Shortcut Vars Connection c = (Connection) it.next(); Object edge = map.get(c.getEdge()); Object port = c.getPort(); if (port != null) port = map.get(port); // New Port if (edge != null && port != null) cs.connect(edge, port, c.isSource()); // Old Port else if (edge != null) cs.connect(edge, c.getPort(), c.isSource()); } return cs; } /** * Object that represents the connection between an edge and a port. */ public static class Connection implements Serializable { /** The edge that will be connected to the port. */ protected Object edge; /** The port that will be connected to the edge. */ protected Object port; /** Indicates if port is the source of edge. */ protected boolean isSource; public Connection() { // Empty Constructor } /** * Constructs a new source or target connection between * edge and port based on the value of * source */ public Connection(Object edge, Object port, boolean isSource) { this.edge = edge; this.port = port; this.isSource = isSource; } /** * Returns the edge of the connection. */ public Object getEdge() { return edge; } /** * Returns the port of the connection. */ public Object getPort() { return port; } /** * Returns true if port is the source of * edge. */ public boolean isSource() { return isSource; } /** * Two connections are equal if they represent the source or target of * the same edge. That is, if *

* c1.edge == c2.edge && c1.isSource == c2.isSource. */ public boolean equals(Object obj) { if (obj instanceof Connection) { Connection other = (Connection) obj; return (other.getEdge().equals(edge) && other.isSource() == isSource); } return false; } /** * Ensure equality of hashCode wrt equals(). */ public int hashCode() { return edge.hashCode(); } /** * @param object */ public void setEdge(Object object) { edge = object; } /** * @param b */ public void setSource(boolean b) { isSource = b; } /** * @param object */ public void setPort(Object object) { port = object; } } /** * @return the set of connections */ public Set getConnections() { return connections; } /** * @return the edges making us this connection set */ public Set getEdges() { return edges; } /** * @param set */ public void setConnections(Set set) { connections = set; } /** * @param set */ public void setEdges(Set set) { edges = set; } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/DefaultGraphCellEditor.java0000644000175000017500000003206711256667036027132 0ustar gregoagregoa/* * @(#)DefaultGraphCellEditor.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.EventObject; import java.util.Vector; import javax.swing.Icon; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.event.CellEditorListener; import javax.swing.plaf.FontUIResource; import org.jgraph.JGraph; import org.jgraph.event.GraphSelectionEvent; import org.jgraph.event.GraphSelectionListener; public class DefaultGraphCellEditor implements ActionListener, GraphCellEditor, GraphSelectionListener, Serializable { /** Editor handling the editing. */ protected GraphCellEditor realEditor; /** Editing container, will contain the editorComponent. */ protected Container editingContainer; /** Component used in editing, obtained from the editingContainer. */ transient protected Component editingComponent; /** * Internal Note, maybe isCellEditable return true. * This is set in configure based on the path being edited and the * selected selected path. */ protected boolean canEdit; /** Used in editing. Indicates position to place editingComponent. */ protected transient int offsetX; protected transient int offsetY; /** JTree instance listening too. */ protected transient JGraph graph; /** last path that was selected. */ protected transient Object lastCell; /** True if the border selection color should be drawn. */ protected Color borderSelectionColor; /** Icon to use when editing. */ protected transient Icon editingIcon; /** Font to paint with, null indicates font of renderer is to be used. */ protected Font font; /** * Constructs a DefaultTreeCellEditor object for a JGraph using the * specified renderer and a default editor. (Use this constructor * for normal editing.) */ public DefaultGraphCellEditor() { this(null); } /** * Constructs a DefaultTreeCellEditor object for a JTree using the * specified renderer and the specified editor. (Use this constructor * for specialized editing.) * * @param editor a TreeCellEditor object */ public DefaultGraphCellEditor(GraphCellEditor editor) { realEditor = editor; if (realEditor == null) realEditor = createGraphCellEditor(); editingContainer = createContainer(); setBorderSelectionColor( UIManager.getColor("Tree.editorBorderSelectionColor")); } /** * Sets the color to use for the border. */ public void setBorderSelectionColor(Color newColor) { borderSelectionColor = newColor; } /** * Returns the color the border is drawn. */ public Color getBorderSelectionColor() { return borderSelectionColor; } /** * Sets the font to edit with. null indicates the renderers * font should be used. This will NOT override any font you have set in * the editor the receiver was instantied with. If null for an editor was * passed in a default editor will be created that will pick up this font. * * @param font the editing Font * @see #getFont */ public void setFont(Font font) { this.font = font; } /** * Gets the font used for editing. * * @return the editing Font * @see #setFont */ public Font getFont() { return font; } // // TreeCellEditor // /** * Configures the editor. Passed onto the realEditor. */ public Component getGraphCellEditorComponent( JGraph graph, Object cell, boolean isSelected) { setGraph(graph); editingComponent = realEditor.getGraphCellEditorComponent(graph, cell, isSelected); determineOffset(graph, cell, isSelected); canEdit = (lastCell != null && cell != null && lastCell.equals(cell)); CellView view = graph.getGraphLayoutCache().getMapping(cell, false); if (view != null) setFont(GraphConstants.getFont(view.getAllAttributes())); editingContainer.setFont(font); return editingContainer; } /** * Returns the value currently being edited. */ public Object getCellEditorValue() { return realEditor.getCellEditorValue(); } /** * If the realEditor returns true to this message, prepareForEditing * is messaged and true is returned. */ public boolean isCellEditable(EventObject event) { boolean retValue = false; if (!realEditor.isCellEditable(event)) return false; if (canEditImmediately(event)) retValue = true; if (retValue) prepareForEditing(); return retValue; } /** * Messages the realEditor for the return value. */ public boolean shouldSelectCell(EventObject event) { return realEditor.shouldSelectCell(event); } /** * If the realEditor will allow editing to stop, the realEditor is * removed and true is returned, otherwise false is returned. */ public boolean stopCellEditing() { if (realEditor.stopCellEditing()) { if (editingComponent != null) editingContainer.remove(editingComponent); editingComponent = null; return true; } return false; } /** * Messages cancelCellEditing to the realEditor and removes it from this * instance. */ public void cancelCellEditing() { realEditor.cancelCellEditing(); if (editingComponent != null) editingContainer.remove(editingComponent); editingComponent = null; } /** * Adds the CellEditorListener. */ public void addCellEditorListener(CellEditorListener l) { realEditor.addCellEditorListener(l); } /** * Removes the previously added CellEditorListener l. */ public void removeCellEditorListener(CellEditorListener l) { realEditor.removeCellEditorListener(l); } // // TreeSelectionListener // /** * Resets lastPath. */ public void valueChanged(GraphSelectionEvent e) { if (graph != null) { if (graph.getSelectionCount() == 1) lastCell = graph.getSelectionCell(); else lastCell = null; } } // // ActionListener (for Timer). // /** * Messaged when the timer fires, this will start the editing * session. */ public void actionPerformed(ActionEvent e) { if (graph != null) graph.startEditingAtCell(lastCell); } // // Local methods // /** * Sets the tree currently editing for. This is needed to add * a selection listener. */ protected void setGraph(JGraph newGraph) { if (graph != newGraph) { if (graph != null) graph.removeGraphSelectionListener(this); graph = newGraph; if (graph != null) graph.addGraphSelectionListener(this); } } /** * Returns true if event is a MouseEvent and the click * count is 1. */ protected boolean shouldStartEditingTimer(EventObject event) { if ((event instanceof MouseEvent) && SwingUtilities.isLeftMouseButton((MouseEvent) event)) { MouseEvent me = (MouseEvent) event; return ( me.getClickCount() == 1 && inHitRegion(me.getX(), me.getY())); } return false; } /** * Returns true if event is null, or it is a MouseEvent * with a click count > 2 and inHitRegion returns true. */ protected boolean canEditImmediately(EventObject event) { if ((event instanceof MouseEvent) && SwingUtilities.isLeftMouseButton((MouseEvent) event)) { MouseEvent me = (MouseEvent) event; return inHitRegion(me.getX(), me.getY()); } return (event == null); } /** * Should return true if the passed in location is a valid mouse location * to start editing from. This is implemented to return false if * x is <= the width of the icon and icon gap displayed * by the renderer. In other words this returns true if the user * clicks over the text part displayed by the renderer, and false * otherwise. */ protected boolean inHitRegion(double x, double y) { // This is only ever called when within the cell bounds, // so this is ok for the default case. return true; } protected void determineOffset( JGraph graph, Object value, boolean isSelected) { editingIcon = null; offsetX = graph.getHandleSize(); offsetY = graph.getHandleSize(); } /** * Invoked just before editing is to start. Will add the * editingComponent to the * editingContainer. */ protected void prepareForEditing() { editingContainer.add(editingComponent); } /** * Creates the container to manage placement of editingComponent. */ protected Container createContainer() { return new EditorContainer(); } /** * This is invoked if a TreeCellEditor is not supplied in the constructor. * It returns a TextField editor. */ protected GraphCellEditor createGraphCellEditor() { Border aBorder = UIManager.getBorder("Tree.editorBorder"); DefaultRealEditor editor = new DefaultRealEditor(new DefaultTextField(aBorder)) { public boolean shouldSelectCell(EventObject event) { boolean retValue = super.shouldSelectCell(event); getComponent().requestFocus(); return retValue; } }; // One click to edit. editor.setClickCountToStart(1); return editor; } // Serialization support. private void writeObject(ObjectOutputStream s) throws IOException { Vector values = new Vector(); s.defaultWriteObject(); // Save the realEditor, if its Serializable. if (realEditor instanceof Serializable) { values.addElement("realEditor"); values.addElement(realEditor); } s.writeObject(values); } private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); Vector values = (Vector) s.readObject(); int indexCounter = 0; int maxCounter = values.size(); if (indexCounter < maxCounter && values.elementAt(indexCounter).equals("realEditor")) { realEditor = (GraphCellEditor) values.elementAt(++indexCounter); indexCounter++; } } /** * TextField used when no editor is supplied. This textfield locks into * the border it is constructed with. It also prefers its parents * font over its font. And if the renderer is not null and no font * has been specified the preferred height is that of the renderer. */ public class DefaultTextField extends JTextField { /** Border to use. */ protected Border border; /** * Constructs a DefaultTreeCellEditor$DefaultTextField object. * * @param border a Border object */ public DefaultTextField(Border border) { this.border = border; } /** * Overrides JComponent.getBorder to * returns the current border. */ public Border getBorder() { return border; } // implements java.awt.MenuContainer public Font getFont() { Font font = super.getFont(); // Prefer the parent containers font if our font is a // FontUIResource if (font instanceof FontUIResource) { Container parent = getParent(); if (parent != null && parent.getFont() != null) font = parent.getFont(); } return font; } } /** * Container responsible for placing the editingComponent. */ public class EditorContainer extends Container { /** * Constructs an EditorContainer object. */ public EditorContainer() { setLayout(null); } /** * Overrides Container.paint to paint the node's * icon and use the selection color for the background. */ public void paint(Graphics g) { Dimension size = getSize(); // Then the icon. if (editingIcon != null) { int yLoc = 0; int xLoc = 0; editingIcon.paintIcon(this, g, xLoc, yLoc); } // Border selection color Color background = getBorderSelectionColor(); if (background != null) { g.setColor(background); g.drawRect(0, 0, size.width - 1, size.height - 1); } super.paint(g); } /** * Lays out this Container. If editing, the editor will be placed at * offset in the x direction and 0 for y. */ public void doLayout() { if (editingComponent != null) { Dimension cSize = getSize(); int h = (int) editingComponent.getPreferredSize().getHeight(); int minw = 45; int w = (int) editingComponent.getPreferredSize().getWidth()+5; int maxw = (int) editingComponent.getMaximumSize().getWidth(); if (editingContainer.getParent() != null && maxw > editingContainer.getParent().getWidth()) w = cSize.width - offsetX; else w = Math.max(minw, Math.min(w, maxw)); editingComponent.setBounds( offsetX, offsetY, w, h); } } /** * Returns the preferred size for the Container. This will be * the preferred size of the editor offset by offset. */ public Dimension getPreferredSize() { if (editingComponent != null) { Dimension pSize = editingComponent.getPreferredSize(); pSize.width += offsetX + 2; pSize.height += offsetY + 2; // Make sure width is at least 50. // and height at least 20 int iwidth = 50; if (editingIcon != null) { iwidth = Math.max(editingIcon.getIconWidth(), iwidth); } pSize.height = Math.max(pSize.height, 24); // Offset 4 pSize.width = Math.max(pSize.width+5, iwidth); return pSize; } return new Dimension(0, 0); } } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/Port.java0000644000175000017500000000136311256667036023534 0ustar gregoagregoa/* * @(#)Port.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.util.Iterator; /** * Defines the requirements for an object that * represents a port in a graph model. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public interface Port extends GraphCell { /** * Returns an iterator of the edges connected * to the port. */ Iterator edges(); /** * Adds edge to the list of ports. */ boolean addEdge(Object edge); /** * Removes edge from the list of ports. */ boolean removeEdge(Object edge); /** * Returns the anchor of the port. */ Port getAnchor(); /** * Sets the anchor of the port. */ void setAnchor(Port port); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/EdgeRenderer.java0000644000175000017500000010361511256667036025146 0ustar gregoagregoa/* * $Id: EdgeRenderer.java,v 1.25 2009/04/06 19:44:25 david Exp $ * * Copyright (c) 2001-2007 Gaudenz Alder * * See LICENSE file in distribution for licensing details of this source file */ package org.jgraph.graph; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.Serializable; import java.lang.ref.WeakReference; import java.util.Map; import javax.swing.JComponent; import javax.swing.UIManager; import org.jgraph.JGraph; import org.jgraph.util.Bezier; import org.jgraph.util.Spline2D; /** * This renderer displays entries that implement the CellView interface. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class EdgeRenderer extends JComponent implements CellViewRenderer, Serializable { /** Switch for exact label hit detection on rotated labels. Default is false. */ public static boolean HIT_LABEL_EXACT = false; /** Static Graphics used for Font Metrics */ protected static transient Graphics fontGraphics; // Headless environment does not allow graphics static { try { fontGraphics = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB) .getGraphics(); } catch (Error e) { // No font graphics fontGraphics = null; } } /** When zooming a graph the font size jumps at certain zoom levels rather than * scaling smoothly. Sometimes the zoom on the font is more than the component zoom * and cropping occurs. This buffer allows for the maximum occurance of this */ public static double LABELWIDTHBUFFER = 1.1; /** A switch for painting the extra labels */ public boolean simpleExtraLabels = true; /** Override this if you want the extra labels to appear in a special fontJ */ public Font extraLabelFont = null; /** Reference to the font metrics of the above */ protected transient FontMetrics metrics; /** Cache the current graph for drawing */ protected transient WeakReference graph; /** Cache the current edgeview for drawing */ protected transient EdgeView view; /** Painting attributes of the current edgeview */ protected transient int beginDeco, endDeco, beginSize, endSize, lineStyle; /** Width of the current edge view */ protected transient float lineWidth; /** Cached value of whether the label is to be displayed */ transient protected boolean labelsEnabled; /** * Boolean attributes of the current edgeview. Fill flags are checked for * valid decorations. */ protected transient boolean labelBorder, beginFill, endFill, focus, selected, preview, opaque, childrenSelected, labelTransformEnabled, isMoveBelowZero; /** * Color attributes of the current edgeview. This components foreground is * set to the edgecolor, the fontColor is in an extra variable. If the * fontColor is null, the current foreground is used. The default background * instead is used for text and is not visible if the label is not visible * or if opaque is true. */ protected transient Color borderColor, defaultForeground, defaultBackground, fontColor; /** Contains the current dash pattern. Null means no pattern. */ protected transient float[] lineDash; /** Contains the current dash offset. Null means no offset. */ protected transient float dashOffset = 0.0f; /** The gradient color of the edge */ protected transient Color gradientColor = null; /** The color of the graph grid */ protected transient Color gridColor = null; /** The color of the second available handle */ protected transient Color lockedHandleColor = null; /** The color of highlighted cells */ protected transient Color highlightColor = null; /** Cached bezier curve */ protected transient Bezier bezier; /** Cached spline curve */ protected transient Spline2D spline; /** * Constructs a renderer that may be used to render edges. */ public EdgeRenderer() { defaultForeground = UIManager.getColor("Tree.textForeground"); defaultBackground = UIManager.getColor("Tree.textBackground"); } /** * Sets view to work with, caching necessary values until the next call of * this method or until some other methods with explicitly specified * different view */ void setView(CellView value) { if (value instanceof EdgeView) { view = (EdgeView) value; installAttributes(view); } else { view = null; } } /** * Configure and return the renderer based on the passed in components. The * value is typically set from messaging the graph with * convertValueToString. * * @param graph * the graph that that defines the rendering context. * @param view * the cell view that should be rendered. * @param sel * whether the object is selected. * @param focus * whether the object has the focus. * @param preview * whether we are drawing a preview. * @return the component used to render the value. */ public Component getRendererComponent(JGraph graph, CellView view, boolean sel, boolean focus, boolean preview) { if (view instanceof EdgeView && graph != null) { this.gridColor = graph.getGridColor(); this.lockedHandleColor = graph.getLockedHandleColor(); this.highlightColor = graph.getHighlightColor(); this.isMoveBelowZero = graph.isMoveBelowZero(); this.graph = new WeakReference(graph); this.focus = focus; this.selected = sel; this.preview = preview; this.childrenSelected = graph.getSelectionModel() .isChildrenSelected(view.getCell()); setView(view); return this; } return null; } /** * Returns true if the edge shape intersects the given rectangle. */ public boolean intersects(JGraph graph, CellView value, Rectangle rect) { if (value instanceof EdgeView && graph != null && value != null) { setView(value); // If we have two control points, we can get rid of hit // detection on do an intersection test on the two diagonals // of rect and the line between the two points Graphics2D g2 = (Graphics2D) graph.getGraphics(); EdgeView edgeView = (EdgeView) value; if (g2 == null || edgeView.getPointCount() == 2) { Point2D p0 = edgeView.getPoint(0); Point2D p1 = edgeView.getPoint(1); if (rect.intersectsLine(p0.getX(), p0.getY(), p1.getX(), p1 .getY())) return true; } else if(g2 != null) { if (g2.hit(rect, view.getShape(), true)) return true; } Rectangle2D r = getLabelBounds(graph, view); if (r != null && r.intersects(rect)) { boolean hits = true; // Performs exact hit detection on rotated labels if (HIT_LABEL_EXACT) { AffineTransform tx = g2.getTransform(); try { String lab = graph.convertValueToString(view); Point2D tmpPt = getLabelPosition(view); Dimension size = getLabelSize(view, lab); Rectangle2D tmp = new Rectangle((int) tmpPt .getX(), (int) tmpPt.getY(), size.width, size.height); double cx = tmp.getCenterX(); double cy = tmp.getCenterY(); g2.translate(-size.width / 2, -size.height * 0.75 - metrics.getDescent()); boolean applyTransform = isLabelTransform(lab); double angle = 0; if (applyTransform) { angle = getLabelAngle(lab); g2.rotate(angle, cx, cy); } hits = g2.hit(rect, tmp, false); } finally { g2.setTransform(tx); } } if (hits) { return true; } } Object[] labels = GraphConstants.getExtraLabels(view .getAllAttributes()); if (labels != null) { for (int i = 0; i < labels.length; i++) { r = getExtraLabelBounds(graph, view, i); if (r != null && r.intersects(rect)) return true; } } } return false; } /** * Returns the bounds of the edge shape. */ public Rectangle2D getBounds(CellView value) { if (value instanceof EdgeView && value != null) { // No need to call setView as getPaintBounds will view = (EdgeView) value; Rectangle2D r = getPaintBounds(view); JGraph graph = null; if (this.graph != null) { graph = (JGraph)this.graph.get(); } Rectangle2D rect = getLabelBounds(graph, view); if (rect != null) Rectangle2D.union(r, rect, r); Object[] labels = GraphConstants.getExtraLabels(view .getAllAttributes()); if (labels != null) { for (int i = 0; i < labels.length; i++) { rect = getExtraLabelBounds(graph, view, i); if (rect != null) Rectangle2D.union(r, rect, r); } } int b = (int) Math.ceil(lineWidth); r.setFrame(r.getX() - b, r.getY() - b, r.getWidth() + 2 * b, r .getHeight() + 2 * b); return r; } return null; } private boolean isLabelTransformEnabled() { return labelTransformEnabled; } /** * Estimates whether the transform for label should be applied. With the * transform, the label will be painted along the edge. To apply transform, * rotate graphics by the angle returned from {@link #getLabelAngle} * * @return true, if transform can be applied, false otherwise */ private boolean isLabelTransform(String label) { if (!isLabelTransformEnabled()) { return false; } Point2D p = getLabelPosition(view); if (p != null && label != null && label.length() > 0) { int sw = metrics.stringWidth(label); Point2D p1 = view.getPoint(0); Point2D p2 = view.getPoint(view.getPointCount() - 1); double length = Math.sqrt((p2.getX() - p1.getX()) * (p2.getX() - p1.getX()) + (p2.getY() - p1.getY()) * (p2.getY() - p1.getY())); if (!(length <= Double.NaN || length < sw)) { return true; } } return false; } /** * Calculates the angle at which graphics should be rotated to paint label * along the edge. Before calling this method always check that transform * should be applied using {@linkisLabelTransform} * * @return the value of the angle, 0 if the angle is zero or can't be * calculated */ private double getLabelAngle(String label) { Point2D p = getLabelPosition(view); double angle = 0; if (p != null && label != null && label.length() > 0) { int sw = metrics.stringWidth(label); // Note: For control points you may want to choose other // points depending on the segment the label is in. Point2D p1 = view.getPoint(0); Point2D p2 = view.getPoint(view.getPointCount() - 1); // Length of the edge double length = Math.sqrt((p2.getX() - p1.getX()) * (p2.getX() - p1.getX()) + (p2.getY() - p1.getY()) * (p2.getY() - p1.getY())); if (!(length <= Double.NaN || length < sw)) { // Label fits into // edge's length // To calculate projections of edge double cos = (p2.getX() - p1.getX()) / length; double sin = (p2.getY() - p1.getY()) / length; // Determine angle angle = Math.acos(cos); if (sin < 0) { // Second half angle = 2 * Math.PI - angle; } } if (angle > Math.PI / 2 && angle <= Math.PI * 3 / 2) { angle -= Math.PI; } } return angle; } /** * Returns the label bounds of the specified view in the given graph. */ public Rectangle2D getLabelBounds(JGraph paintingContext, EdgeView view) { if (paintingContext == null && graph != null) { JGraph graph = (JGraph)this.graph.get(); paintingContext = graph; } // No need to call setView as getLabelPosition will String label = (paintingContext != null) ? paintingContext .convertValueToString(view) : String.valueOf(view.getCell()); if (label != null) { Point2D p = getLabelPosition(view); Dimension d = getLabelSize(view, label); return getLabelBounds(p, d, label); } else { return null; } } /** * Returns the label bounds of the specified view in the given graph. Note: * The index is the position of the String object for the label in the extra * labels array of the view. */ public Rectangle2D getExtraLabelBounds(JGraph paintingContext, EdgeView view, int index) { if (paintingContext == null && graph != null) { JGraph graph = (JGraph)this.graph.get(); paintingContext = graph; } setView(view); Object[] labels = GraphConstants .getExtraLabels(view.getAllAttributes()); if (labels != null && index < labels.length) { Point2D p = getExtraLabelPosition(this.view, index); Dimension d = getExtraLabelSize(paintingContext, this.view, index); String label = (paintingContext != null) ? paintingContext .convertValueToString(labels[index]) : String .valueOf(labels[index]); return getLabelBounds(p, d, label); } return new Rectangle2D.Double(getX(), getY(), 0, 0); } /** * Returns the label bounds of the specified view in the given graph. */ public Rectangle2D getLabelBounds(Point2D p, Dimension d, String label) { if (label != null && isLabelTransform(label)) { // With transform label is rotated, so we should // rotate the rectangle (sw, sh) and return the // bounding rectangle double angle = getLabelAngle(label); if (angle < 0) angle = -angle; if (angle > Math.PI / 2) angle %= Math.PI / 2; double yside = Math.abs(Math.cos(angle) * d.height + Math.sin(angle) * d.width); double xside = Math.abs(d.width * Math.cos(angle) + d.height * Math.sin(angle)); // Getting maximum is not good, but I don't want to be // drown in calculations if (xside > yside) yside = xside; if (yside > xside) xside = yside; angle = getLabelAngle(label); // Increasing by height is safe, but I think the precise // value is font.descent layed on edge vector and // projected on each axis d.width = (int) xside + d.height; d.height = (int) yside + d.height; } if (p != null && d != null) { double x = Math.max(0, p.getX() - (d.width / 2)); double y = Math.max(0, p.getY() - (d.height / 2)); return new Rectangle2D.Double(x, y, d.width + 1, d.height + 1); } return null; } /** * Returns the label position of the specified view in the given graph. */ public Point2D getLabelPosition(EdgeView view) { setView(view); return getLabelPosition(view.getLabelPosition()); } /** * Returns the label position of the specified view in the given graph. */ public Point2D getExtraLabelPosition(EdgeView view, int index) { setView(view); Point2D[] pts = GraphConstants.getExtraLabelPositions(view .getAllAttributes()); if (pts != null && index < pts.length) return getLabelPosition(pts[index]); return null; } /** * Returns the label position of the specified view in the given graph. */ protected Point2D getLabelPosition(Point2D pos) { Rectangle2D tmp = getPaintBounds(view); int unit = GraphConstants.PERMILLE; Point2D p0 = view.getPoint(0); if (pos != null && tmp != null && p0 != null) { if (!isLabelTransformEnabled()) { return view.getAbsoluteLabelPositionFromRelative(pos); } else { Point2D vector = view.getLabelVector(); double dx = vector.getX(); double dy = vector.getY(); double len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { int pointIndex = view.getFirstPointOfSegment(); if (pointIndex >=0 && pointIndex < view.getPointCount()-1) { p0 = view.getPoint(pointIndex); } double x = p0.getX() + (dx * pos.getX() / unit); double y = p0.getY() + (dy * pos.getX() / unit); x += (-dy * pos.getY() / len); y += (dx * pos.getY() / len); return new Point2D.Double(x, y); } else { return new Point2D.Double(p0.getX() + pos.getX(), p0.getY() + pos.getY()); } } } return null; } /** * Returns the label size of the specified view in the given graph. */ public Dimension getExtraLabelSize(JGraph paintingContext, EdgeView view, int index) { Object[] labels = GraphConstants .getExtraLabels(view.getAllAttributes()); if (labels != null && index < labels.length) { String label = (paintingContext != null) ? paintingContext .convertValueToString(labels[index]) : String .valueOf(labels[index]); return getLabelSize(view, label); } return null; } /** * Returns the label size of the specified view in the given graph. */ public Dimension getLabelSize(EdgeView view, String label) { if (label != null && fontGraphics != null) { fontGraphics.setFont(GraphConstants .getFont(view.getAllAttributes())); metrics = fontGraphics.getFontMetrics(); int sw = (int)(metrics.stringWidth(label) * LABELWIDTHBUFFER); int sh = metrics.getHeight(); return new Dimension(sw, sh); } return null; } /** * Installs the attributes of specified cell in this renderer instance. This * means, retrieve every published key from the cells hashtable and set * global variables or superclass properties accordingly. * * @param view * the cell view to retrieve the attribute values from. */ protected void installAttributes(CellView view) { Map map = view.getAllAttributes(); beginDeco = GraphConstants.getLineBegin(map); beginSize = GraphConstants.getBeginSize(map); beginFill = GraphConstants.isBeginFill(map) && isFillable(beginDeco); endDeco = GraphConstants.getLineEnd(map); endSize = GraphConstants.getEndSize(map); endFill = GraphConstants.isEndFill(map) && isFillable(endDeco); lineWidth = GraphConstants.getLineWidth(map); Edge.Routing routing = GraphConstants.getRouting(map); lineStyle = (routing != null && view instanceof EdgeView) ? routing .getPreferredLineStyle((EdgeView) view) : Edge.Routing.NO_PREFERENCE; if (lineStyle == Edge.Routing.NO_PREFERENCE) lineStyle = GraphConstants.getLineStyle(map); lineDash = GraphConstants.getDashPattern(map); dashOffset = GraphConstants.getDashOffset(map); borderColor = GraphConstants.getBorderColor(map); Color foreground = GraphConstants.getLineColor(map); setForeground((foreground != null) ? foreground : defaultForeground); Color background = GraphConstants.getBackground(map); setBackground((background != null) ? background : defaultBackground); Color gradientColor = GraphConstants.getGradientColor(map); setGradientColor(gradientColor); setOpaque(GraphConstants.isOpaque(map)); setFont(GraphConstants.getFont(map)); Color tmp = GraphConstants.getForeground(map); fontColor = (tmp != null) ? tmp : getForeground(); labelTransformEnabled = GraphConstants.isLabelAlongEdge(map); labelsEnabled = GraphConstants.isLabelEnabled(map); } protected boolean isFillable(int decoration) { return !(decoration == GraphConstants.ARROW_SIMPLE || decoration == GraphConstants.ARROW_LINE || decoration == GraphConstants.ARROW_DOUBLELINE); } /** * Returns the bounds of the edge shape without label */ public Rectangle2D getPaintBounds(EdgeView view) { Rectangle2D rec = null; setView(view); if (view.getShape() != null) rec = view.getShape().getBounds(); else rec = new Rectangle2D.Double(0, 0, 0, 0); return rec; } /** * Paint the renderer. */ public void paint(Graphics g) { if (view.isLeaf()) { Shape edgeShape = view.getShape(); // Sideeffect: beginShape, lineShape, endShape if (edgeShape != null) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); setOpaque(false); super.paint(g); translateGraphics(g); // Hook for pre-painting beforeEdgePaint(g); // Actual edge drawing paintEdge(g); // Drawing of any selection paintSelection(g); // Drawing of labels paintLabels(g); // Hook for post-painting afterEdgePaint(g); } } else { paintSelectionBorder(g); } } /** * Draws the edge labels * @param g the graphics object being painted to */ protected void paintLabels(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setStroke(new BasicStroke(1)); g.setFont((extraLabelFont != null) ? extraLabelFont : getFont()); Object[] labels = GraphConstants .getExtraLabels(view.getAllAttributes()); JGraph graph = (JGraph) this.graph.get(); if (labels != null) { for (int i = 0; i < labels.length; i++) paintLabel(g, graph.convertValueToString(labels[i]), getExtraLabelPosition(view, i), false || !simpleExtraLabels); } if (graph.getEditingCell() != view.getCell()) { g.setFont(getFont()); Object label = graph.convertValueToString(view); if (label != null) { paintLabel(g, label.toString(), getLabelPosition(view), true); } } } /** * Paints the edge itself * @param g the graphics object being painted to */ protected void paintEdge(Graphics g) { g.setColor(getForeground()); if (lineWidth > 0) { Graphics2D g2 = (Graphics2D) g; int c = BasicStroke.CAP_BUTT; int j = BasicStroke.JOIN_MITER; g2.setStroke(new BasicStroke(lineWidth, c, j)); if (gradientColor != null && !preview) { g2.setPaint(new GradientPaint(0, 0, getBackground(), getWidth(), getHeight(), gradientColor, true)); } if (view.beginShape != null) { if (beginFill) g2.fill(view.beginShape); g2.draw(view.beginShape); } if (view.endShape != null) { if (endFill) g2.fill(view.endShape); g2.draw(view.endShape); } if (lineDash != null) // Dash For Line Only g2.setStroke(new BasicStroke(lineWidth, c, j, 10.0f, lineDash, dashOffset)); if (view.lineShape != null) g2.draw(view.lineShape); } } /** * Paints any selection effect applied to the edge * @param g the graphics object being painted to */ protected void paintSelection(Graphics g) { if (selected) { // Paint Selected Graphics2D g2 = (Graphics2D) g; g2.setStroke(GraphConstants.SELECTION_STROKE); g2.setColor(highlightColor); if (view.beginShape != null) g2.draw(view.beginShape); if (view.lineShape != null) g2.draw(view.lineShape); if (view.endShape != null) g2.draw(view.endShape); } } /** * Hook method for painting prior to default painting * @param g the graphics object being painted to */ protected void beforeEdgePaint(Graphics g) { } /** * Hook method for painting after the default painting * @param g the graphics object being painted to */ protected void afterEdgePaint(Graphics g) { } /** * Provided for subclassers to paint a selection border. */ protected void paintSelectionBorder(Graphics g) { ((Graphics2D) g).setStroke(GraphConstants.SELECTION_STROKE); if (childrenSelected) g.setColor(gridColor); else if (focus && selected) g.setColor(lockedHandleColor); else if (selected) g.setColor(highlightColor); if (childrenSelected || selected) { Dimension d = getSize(); g.drawRect(0, 0, d.width - 1, d.height - 1); } } // This if for subclassers that to not want the graphics // to be relative to the top, left corner of this component. // Note: Override this method with an empty implementation // if you want absolute positions for your edges protected void translateGraphics(Graphics g) { g.translate(-getX(), -getY()); } /** * Paint the specified label for the current edgeview. */ protected void paintLabel(Graphics g, String label, Point2D p, boolean mainLabel) { if (labelsEnabled && p != null && label != null && label.length() > 0 && metrics != null) { int sw = metrics.stringWidth(label); int sh = metrics.getHeight(); Graphics2D g2 = (Graphics2D) g; boolean applyTransform = isLabelTransform(label); double angle = 0; int dx = -sw / 2; int offset = isMoveBelowZero || applyTransform ? 0 : Math .min(0, (int) (dx + p.getX())); g2.translate(p.getX() - offset, p.getY()); if (applyTransform) { angle = getLabelAngle(label); g2.rotate(angle); } if (isOpaque() && mainLabel) { g.setColor(getBackground()); g.fillRect(-sw / 2 - 1, -sh / 2 - 1, sw + 2, sh + 2); } if (borderColor != null && mainLabel) { g.setColor(borderColor); g.drawRect(-sw / 2 - 1, -sh / 2 - 1, sw + 2, sh + 2); } int dy = +sh / 4; g.setColor(fontColor); if (applyTransform && borderColor == null && !isOpaque()) { // Shift label perpendicularly by the descent so it // doesn't cross the line. dy = -metrics.getDescent(); } g.drawString(label, dx, dy); if (applyTransform) { // Undo the transform g2.rotate(-angle); } g2.translate(-p.getX() + offset, -p.getY()); } } /** * Returns the shape that represents the current edge in the context of the * current graph. This method sets the global beginShape, lineShape and * endShape variables as a side-effect. */ protected Shape createShape() { int n = view.getPointCount(); if (n > 1) { // Following block may modify static vars as side effect (Flyweight // Design) EdgeView tmp = view; Point2D[] p = null; p = new Point2D[n]; for (int i = 0; i < n; i++) { Point2D pt = tmp.getPoint(i); if (pt == null) return null; // exit p[i] = new Point2D.Double(pt.getX(), pt.getY()); } // End of Side-Effect Block // Undo Possible MT-Side Effects if (view != tmp) { view = tmp; installAttributes(view); } // End of Undo if (view.sharedPath == null) { view.sharedPath = new GeneralPath(GeneralPath.WIND_NON_ZERO, n); } else { view.sharedPath.reset(); } view.beginShape = view.lineShape = view.endShape = null; Point2D p0 = p[0]; Point2D pe = p[n - 1]; Point2D p1 = p[1]; Point2D p2 = p[n - 2]; if (lineStyle == GraphConstants.STYLE_BEZIER && n > 2) { bezier = new Bezier(p); p2 = bezier.getPoint(bezier.getPointCount() - 1); } else if (lineStyle == GraphConstants.STYLE_SPLINE && n > 2) { spline = new Spline2D(p); double[] point = spline.getPoint(0.9875); // Extrapolate p2 away from the end point, pe, to avoid integer // rounding errors becoming too large when creating the line end double scaledX = pe.getX() - ((pe.getX() - point[0]) * 128); double scaledY = pe.getY() - ((pe.getY() - point[1]) * 128); p2.setLocation(scaledX, scaledY); } if (beginDeco != GraphConstants.ARROW_NONE) { view.beginShape = createLineEnd(beginSize, beginDeco, p1, p0); } if (endDeco != GraphConstants.ARROW_NONE) { view.endShape = createLineEnd(endSize, endDeco, p2, pe); } view.sharedPath.moveTo((float) p0.getX(), (float) p0.getY()); /* THIS CODE WAS ADDED BY MARTIN KRUEGER 10/20/2003 */ if (lineStyle == GraphConstants.STYLE_BEZIER && n > 2) { Point2D[] b = bezier.getPoints(); view.sharedPath.quadTo((float) b[0].getX(), (float) b[0].getY(), (float) p1.getX(), (float) p1 .getY()); for (int i = 2; i < n - 1; i++) { Point2D b0 = b[2 * i - 3]; Point2D b1 = b[2 * i - 2]; view.sharedPath.curveTo((float) b0.getX(), (float) b0 .getY(), (float) b1.getX(), (float) b1.getY(), (float) p[i].getX(), (float) p[i].getY()); } view.sharedPath.quadTo((float) b[b.length - 1].getX(), (float) b[b.length - 1].getY(), (float) p[n - 1].getX(), (float) p[n - 1].getY()); } else if (lineStyle == GraphConstants.STYLE_SPLINE && n > 2) { for (double t = 0; t <= 1; t += 0.0125) { double[] xy = spline.getPoint(t); view.sharedPath.lineTo((float) xy[0], (float) xy[1]); } } /* END */ else { for (int i = 1; i < n - 1; i++) view.sharedPath.lineTo((float) p[i].getX(), (float) p[i] .getY()); view.sharedPath.lineTo((float) pe.getX(), (float) pe.getY()); } view.sharedPath.moveTo((float) pe.getX(), (float) pe.getY()); if (view.endShape == null && view.beginShape == null) { // With no end decorations the line shape is the same as the // shared path and memory view.lineShape = view.sharedPath; } else { view.lineShape = (GeneralPath) view.sharedPath.clone(); if (view.endShape != null) view.sharedPath.append(view.endShape, true); if (view.beginShape != null) view.sharedPath.append(view.beginShape, true); } return view.sharedPath; } return null; } /** * Paint the current view's direction. Sets tmpPoint as a side-effect such * that the invoking method can use it to determine the connection point to * this decoration. */ protected Shape createLineEnd(int size, int style, Point2D src, Point2D dst) { if (src == null || dst == null) return null; int d = (int) Math.max(1, dst.distance(src)); int ax = (int) -(size * (dst.getX() - src.getX()) / d); int ay = (int) -(size * (dst.getY() - src.getY()) / d); if (style == GraphConstants.ARROW_DIAMOND) { Polygon poly = new Polygon(); poly.addPoint((int) dst.getX(), (int) dst.getY()); poly.addPoint((int) (dst.getX() + ax / 2 + ay / 3), (int) (dst .getY() + ay / 2 - ax / 3)); Point2D last = (Point2D) dst.clone(); dst.setLocation(dst.getX() + ax, dst.getY() + ay); poly.addPoint((int) dst.getX(), (int) dst.getY()); poly.addPoint((int) (last.getX() + ax / 2 - ay / 3), (int) (last .getY() + ay / 2 + ax / 3)); return poly; } else if (style == GraphConstants.ARROW_TECHNICAL || style == GraphConstants.ARROW_CLASSIC) { Polygon poly = new Polygon(); poly.addPoint((int) dst.getX(), (int) dst.getY()); poly.addPoint((int) (dst.getX() + ax + ay / 2), (int) (dst.getY() + ay - ax / 2)); Point2D last = (Point2D) dst.clone(); if (style == GraphConstants.ARROW_CLASSIC) { dst.setLocation((int) (dst.getX() + ax * 2 / 3), (int) (dst .getY() + ay * 2 / 3)); poly.addPoint((int) dst.getX(), (int) dst.getY()); } else if (style == GraphConstants.ARROW_DIAMOND) { dst.setLocation(dst.getX() + 2 * ax, dst.getY() + 2 * ay); poly.addPoint((int) dst.getX(), (int) dst.getY()); } else dst.setLocation((int) (dst.getX() + ax), (int) (dst.getY() + ay)); poly.addPoint((int) (last.getX() + ax - ay / 2), (int) (last.getY() + ay + ax / 2)); return poly; } else if (style == GraphConstants.ARROW_SIMPLE) { GeneralPath path = new GeneralPath(GeneralPath.WIND_NON_ZERO, 4); path.moveTo((float) (dst.getX() + ax + ay / 2), (float) (dst.getY() + ay - ax / 2)); path.lineTo((float) dst.getX(), (float) dst.getY()); path.lineTo((float) (dst.getX() + ax - ay / 2), (float) (dst.getY() + ay + ax / 2)); return path; } else if (style == GraphConstants.ARROW_CIRCLE) { Ellipse2D ellipse = new Ellipse2D.Float((float) (dst.getX() + ax / 2 - size / 2), (float) (dst.getY() + ay / 2 - size / 2), size, size); dst.setLocation(dst.getX() + ax, dst.getY() + ay); return ellipse; } else if (style == GraphConstants.ARROW_LINE || style == GraphConstants.ARROW_DOUBLELINE) { GeneralPath path = new GeneralPath(GeneralPath.WIND_NON_ZERO, 4); path.moveTo((float) (dst.getX() + ax / 2 + ay / 2), (float) (dst .getY() + ay / 2 - ax / 2)); path.lineTo((float) (dst.getX() + ax / 2 - ay / 2), (float) (dst .getY() + ay / 2 + ax / 2)); if (style == GraphConstants.ARROW_DOUBLELINE) { path.moveTo((float) (dst.getX() + ax / 3 + ay / 2), (float) (dst.getY() + ay / 3 - ax / 2)); path.lineTo((float) (dst.getX() + ax / 3 - ay / 2), (float) (dst.getY() + ay / 3 + ax / 2)); } return path; } return null; } /** * @return Returns the gradientColor. */ public Color getGradientColor() { return gradientColor; } /** * @param gradientColor * The gradientColor to set. */ public void setGradientColor(Color gradientColor) { this.gradientColor = gradientColor; } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void validate() { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void revalidate() { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void repaint(long tm, int x, int y, int width, int height) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void repaint(Rectangle r) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { // Strings get interned... if (propertyName == "text") super.firePropertyChange(propertyName, oldValue, newValue); } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, byte oldValue, byte newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, char oldValue, char newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, short oldValue, short newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, int oldValue, int newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, long oldValue, long newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, float oldValue, float newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, double oldValue, double newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/GraphSelectionModel.java0000644000175000017500000001250311256667036026476 0ustar gregoagregoa/* * @(#)GraphSelectionModel.java 0.1 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.beans.PropertyChangeListener; import org.jgraph.event.GraphSelectionListener; /** * This interface represents the current state of the selection for * the graph component. *

* * A GraphSelectionModel can be configured to allow only one * cell (SINGLE_GRAPH_SELECTION) or a number of * cells (MULTIPLE_GRAPH_SELECTION). * * @version 0.1 1/1/02 * @author Gaudenz Alder */ public interface GraphSelectionModel { /** Selection can only contain one cell at a time. */ public static final int SINGLE_GRAPH_SELECTION = 1; /** Selection can contain any number of items. */ public static final int MULTIPLE_GRAPH_SELECTION = 4; /** * Sets the selection model, which must be either * SINGLE_GRAPH_SELECTION or MULTIPLE_GRAPH_SELECTION. *

* This may change the selection if the current selection is not valid * for the new mode. */ void setSelectionMode(int mode); /** * Sets if the selection model allows the selection * of children. */ void setChildrenSelectable(boolean flag); /** * Returns true if the selection model allows the selection * of children. */ boolean isChildrenSelectable(); /** * Returns the current selection mode, either * SINGLE_GRAPH_SELECTION or * MULTIPLE_GRAPH_SELECTION. */ int getSelectionMode(); /** * Sets the selection to cell. If this represents a change, then * the GraphSelectionListeners are notified. If cell is * null, this has the same effect as invoking clearSelection. * * @param cell new cell to select */ void setSelectionCell(Object cell); /** * Sets the selection to cells. If this represents a change, then * the GraphSelectionListeners are notified. If cells is * null, this has the same effect as invoking clearSelection. * * @param cells new selection */ void setSelectionCells(Object[] cells); /** * Adds cell to the current selection. If cell is not currently * in the selection the GraphSelectionListeners are notified. This has * no effect if cell is null. * * @param cell the new cell to add to the current selection */ void addSelectionCell(Object cell); /** * Adds cells to the current selection. If any of the cells are * not currently in the selection the GraphSelectionListeners * are notified. This has no effect if cells is null. * * @param cells the new cells to add to the current selection */ void addSelectionCells(Object[] cells); /** * Removes cell from the selection. If cell is in the selection * the GraphSelectionListeners are notified. This has no effect if * cell is null. * * @param cell the cell to remove from the selection */ void removeSelectionCell(Object cell); /** * Removes cells from the selection. If any of the cells in * cells are in the selection, the * GraphSelectionListeners are notified. This method has no * effect if cells is null. * * @param cells the cells to remove from the selection */ void removeSelectionCells(Object[] cells); /** * Returns the cells that are currently selectable. */ Object[] getSelectables(); /** * Returns the first cell in the selection. How first is defined is * up to implementors. */ Object getSelectionCell(); /** * Returns the cells in the selection. This will return null (or an * empty array) if nothing is currently selected. */ Object[] getSelectionCells(); /** * Returns the number of cells that are selected. */ int getSelectionCount(); /** * Returns true if the cell, cell, is in the current * selection. */ boolean isCellSelected(Object cell); /** * Returns true if the cell, cell, * has selected children. */ boolean isChildrenSelected(Object cell); /** * Returns true if the selection is currently empty. */ boolean isSelectionEmpty(); /** * Empties the current selection. If this represents a change in the * current selection, the selection listeners are notified. */ void clearSelection(); /** * Adds a PropertyChangeListener to the listener list. * The listener is registered for all properties. *

* A PropertyChangeEvent will get fired when the selection mode * changes. * * @param listener the PropertyChangeListener to be added */ void addPropertyChangeListener(PropertyChangeListener listener); /** * Removes a PropertyChangeListener from the listener list. * This removes a PropertyChangeListener that was registered * for all properties. * * @param listener the PropertyChangeListener to be removed */ void removePropertyChangeListener(PropertyChangeListener listener); /** * Adds x to the list of listeners that are notified each time the * set of selected Objects changes. * * @param x the new listener to be added */ void addGraphSelectionListener(GraphSelectionListener x); /** * Removes x from the list of listeners that are notified each time * the set of selected Objects changes. * * @param x the listener to remove */ void removeGraphSelectionListener(GraphSelectionListener x); } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/BasicMarqueeHandler.java0000644000175000017500000001715211256667036026452 0ustar gregoagregoa/* * @(#)BasicMarqueeHandler.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.Color; import java.awt.Cursor; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import org.jgraph.JGraph; /** * A simple implementation of a marquee handler for JGraph. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class BasicMarqueeHandler { /* Restore previous cursor after operation. */ protected transient Cursor previousCursor = null; /* The rectangle that defines the current marquee selection. */ protected Rectangle2D marqueeBounds; /* The start start and current point of the marquee session. */ protected Point2D startPoint, currentPoint; /* Return true if this handler should be preferred over other handlers. */ public boolean isForceMarqueeEvent(MouseEvent event) { return event.isAltDown(); } /** * Stops the current marquee selection. */ public void mouseReleased(MouseEvent e) { try { if (e != null && marqueeBounds != null) { if (!(e.getSource() instanceof JGraph)) throw new IllegalArgumentException("MarqueeHandler cannot " + "handle event from unknown source: " + e); JGraph graph = (JGraph) e.getSource(); Rectangle2D bounds = graph .fromScreen((Rectangle2D) marqueeBounds.clone()); handleMarqueeEvent(e, graph, bounds); graph.setCursor(previousCursor); Rectangle dirty = new Rectangle((int) marqueeBounds.getX(), (int) marqueeBounds.getY(), (int) marqueeBounds .getWidth() + 1, (int) marqueeBounds .getHeight() + 1); dirty.width++; dirty.height++; graph.repaint(dirty); } } finally { currentPoint = null; startPoint = null; marqueeBounds = null; previousCursor = null; } } /** * Hook for subclassers. Current implementation checks if graph selection is * enabled. This is called from mouseReleased to execute the marquee * selection. */ public void handleMarqueeEvent(MouseEvent e, JGraph graph, Rectangle2D bounds) { CellView[] views = graph.getGraphLayoutCache().getRoots(bounds); ArrayList list = new ArrayList(); for (int i = 0; i < views.length; i++) // above returns intersection, we want containment if (bounds.contains(views[i].getBounds())) list.add(views[i].getCell()); Object[] cells = list.toArray(); graph.getUI().selectCellsForEvent(graph, cells, e); } /** * Includes the specified startPoint in the marquee selection. Calls * overlay. */ public void mouseDragged(MouseEvent e) { if (startPoint != null) { if (!(e.getSource() instanceof JGraph)) throw new IllegalArgumentException( "MarqueeHandler cannot handle event from unknown source: " + e); JGraph graph = (JGraph) e.getSource(); Graphics g = graph.getGraphics(); // Xor the current display Color bg = graph.getBackground(); Color fg = graph.getMarqueeColor(); if (graph.isXorEnabled()) { g.setColor(fg); g.setXORMode(bg); overlay(graph, g, true); } Rectangle2D dirty = (Rectangle2D) marqueeBounds.clone(); processMouseDraggedEvent(e); // Repaint if (graph.isXorEnabled()) { g.setColor(bg); g.setXORMode(fg); overlay(graph, g, false); } else { dirty.add(marqueeBounds); graph.repaint((int) dirty.getX()-1, (int) dirty.getY()-1, (int) dirty.getWidth()+2, (int) dirty.getHeight()+2); } } } /** * Called from mouse dragged to update the marquee state during a repaint. */ protected void processMouseDraggedEvent(MouseEvent e) { if (startPoint != null) { currentPoint = e.getPoint(); marqueeBounds = new Rectangle2D.Double(startPoint.getX(), startPoint.getY(), 0, 0); marqueeBounds.add(currentPoint); } } /** * Called after the component was repainted (after autoscroll). This is used * to indicate that the graphics is no more dirty. */ public void paint(JGraph graph, Graphics g) { if (marqueeBounds != null) { if (graph.isXorEnabled()) { Color bg = graph.getBackground(); Color fg = graph.getMarqueeColor(); g.setColor(fg); g.setXORMode(bg); } overlay(graph, g, false); } } /** * Draw the current state of the handler. This is called twice by the * overlay method and also by the paint method. The caller's intention is * that the overlay method draws exactly the current state of the handler so * that it may be used for XOR paint. The drag method calls overlay, changes * the state, and calls overlay again to use this. However, since it is not * always possible to clear the screen with an exact repaint the caller * passes a flag to indicate if the graphics object should be cleared with * this call (eg. if a subsequent call follows). * * @param g */ public void overlay(JGraph graph, Graphics g, boolean clear) { if (marqueeBounds != null) { if (!graph.isXorEnabled()) { g.setColor(graph.getMarqueeColor()); } g.drawRect((int) marqueeBounds.getX(), (int) marqueeBounds.getY(), (int) marqueeBounds.getWidth(), (int) marqueeBounds .getHeight()); } } /** * Start the marquee at the specified startPoint. This invokes * expandMarqueeToPoint to initialize marquee selection. */ public void mousePressed(MouseEvent e) { startPoint = e.getPoint(); marqueeBounds = new Rectangle2D.Double(startPoint.getX(), startPoint .getY(), 0, 0); if (!(e.getSource() instanceof JGraph)) throw new IllegalArgumentException( "MarqueeHandler cannot handle event from unknown source: " + e); JGraph graph = (JGraph) e.getSource(); if (isMarqueeTriggerEvent(e, graph)) { previousCursor = graph.getCursor(); graph.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); } } /** * Hook for subclassers. Current implementation checks if graph selection is * enabled. This is called from mousePressed before initiating the marquee * selection. */ public boolean isMarqueeTriggerEvent(MouseEvent e, JGraph graph) { return graph.isSelectionEnabled(); } /** * Empty. */ public void mouseMoved(MouseEvent e) { // do nothing } /** * Returns the currentPoint. * * @return Point */ public Point2D getCurrentPoint() { return currentPoint; } /** * Returns the marqueeBounds. * * @return Rectangle */ public Rectangle2D getMarqueeBounds() { return marqueeBounds; } /** * Returns the previousCursor. * * @return Cursor */ public Cursor getPreviousCursor() { return previousCursor; } /** * Returns the startPoint. * * @return Point */ public Point2D getStartPoint() { return startPoint; } /** * Sets the currentPoint. * * @param currentPoint * The currentPoint to set */ public void setCurrentPoint(Point2D currentPoint) { this.currentPoint = currentPoint; } /** * Sets the marqueeBounds. * * @param marqueeBounds * The marqueeBounds to set */ public void setMarqueeBounds(Rectangle2D marqueeBounds) { this.marqueeBounds = marqueeBounds; } /** * Sets the previousCursor. * * @param previousCursor * The previousCursor to set */ public void setPreviousCursor(Cursor previousCursor) { this.previousCursor = previousCursor; } /** * Sets the startPoint. * * @param startPoint * The startPoint to set */ public void setStartPoint(Point2D startPoint) { this.startPoint = startPoint; } /** * @return Returns the source of the event as a graph. */ public static JGraph getGraphForEvent(MouseEvent event) { if (event.getSource() instanceof JGraph) return (JGraph) event.getSource(); return null; } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/package.html0000644000175000017500000000050411256667036024222 0ustar gregoagregoa Graphs are made up of a number of classes and interfaces defined in their own package - the jgraph.graph package. The jgraph.graph package provides support classes that include the graph model, graph cells, graph cell editors, drivers,
controllers, and renderers. libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/GraphLayoutCache.java0000644000175000017500000025726211256667036026006 0ustar gregoagregoa/* * $Id: GraphLayoutCache.java,v 1.42 2009/08/12 11:39:37 david Exp $ * * Copyright (c) 2001-2009 JGraph Ltd * */ package org.jgraph.graph; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import java.util.WeakHashMap; import javax.swing.event.EventListenerList; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.CompoundEdit; import javax.swing.undo.UndoableEdit; import org.jgraph.event.GraphLayoutCacheEvent; import org.jgraph.event.GraphLayoutCacheListener; import org.jgraph.event.GraphModelEvent; import org.jgraph.util.RectUtils; /** * An object that defines the view of a graphmodel. This object maps between * model cells and views and provides a set of methods to change these views. * The view may also contain its own set of attributes and is therefore an * extension of an Observable, which may be observed by the GraphUI. It uses the * model to send its changes to the command history. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class GraphLayoutCache implements CellMapper, Serializable { /** * True if the cells should be auto-sized when their values change. Default * is false. */ protected boolean autoSizeOnValueChange = false; /** * Boolean indicating whether existing connections should me made visible if * their sources or targets are made visible, given the opposite end of the * edge is already visible or made visible, too. Default is true. */ protected boolean showsExistingConnections = true; /** * Boolean indicating whether connections should be made visible when * reconnected and their source and target port is visible. Default is true. */ protected boolean showsChangedConnections = true; /** * Boolean indicating whether edited cells should be made visible if they * are changed via * {@link #edit(Map, ConnectionSet, ParentMap, UndoableEdit[])}. Default is * false. */ protected boolean showsInvisibleEditedCells = false; /** * Boolean indicating whether inserted should be made visible if they are * inserted via * {@link #insert(Object[], Map, ConnectionSet, ParentMap, UndoableEdit[])}. * Default is true. */ protected boolean showsInsertedCells = true; /** * Boolean indicating whether inserted edges should me made visible if their * sources or targets are already visible. Default is true. */ protected boolean showsInsertedConnections = true; /** * Boolean indicating whether existing connections should be hidden if their * source or target and no parent of the ports is visible, either by hiding * the cell or by changing the source or target of the edge to a hidden * cell. Default is true. */ protected boolean hidesExistingConnections = true; /** * Boolean indicating whether existing connections should be hidden if their * source or target port is removed from the model. Default is false. */ protected boolean hidesDanglingConnections = false; /** * Boolean indicating whether cellviews should be remembered once visible in * this GraphLayoutCache. Default is true. */ protected boolean remembersCellViews = true; /** * Boolean indicating whether inserted cells should automatically be * selected. Default is true. This is ignored if the cache is partial. Note: * Despite the name of this field the implementation is located in the * BasicGraphUI.GraphModelHandler.graphChanged method. */ protected boolean selectsAllInsertedCells = false; /** * Boolean indicating whether cells that are inserted using the local insert * method should automatically be selected. Default is true. This is ignored * if the cache is not partial and selectsAllInsertedCells is true, in which * case the cells will be selected through another mechanism. Note: Despite * the name of this field the implementation is located in the * BasicGraphUI.GraphLayoutCacheObserver.changed method. */ protected boolean selectsLocalInsertedCells = false; /** * Boolean indicating whether children should be moved to the parent group's * origin on expand. Default is true. */ protected boolean movesChildrenOnExpand = true; /** * Boolean indicating whether parents should be moved to the child area * origin on collapse. Default is true. */ protected boolean movesParentsOnCollapse = true; /** * Boolean indicating whether parents should always be resized to the child * area on collapse. If false the size is only initially updated if it has * not yet been assigned. Default is false. */ protected boolean resizesParentsOnCollapse = false; /** * Specified the initial x- and y-scaling factor for initial collapsed group * bounds. Default is 1.0, ie. no scaling. */ protected double collapseXScale = 1.0, collapseYScale = 1.0; /** * Boolean indicating whether edges should be reconneted to visible parents * on collapse/expand. Default is false. * * @deprecated edges are moved to parent view and back automatically */ protected boolean reconnectsEdgesToVisibleParent = false; /** * The list of listeners that listen to the GraphLayoutCache. */ protected EventListenerList listenerList = new EventListenerList(); /** * Reference to the graphModel */ protected GraphModel graphModel; /** * Maps cells to views. */ protected Map mapping = new Hashtable(); /** * Maps cells to views. The hidden mapping is used to remembed cell views * that are hidden, based on the remembersCellViews setting. hiddenMapping * must use weak keys for the cells since when cells are removed * hiddenMapping is not updated. */ protected transient Map hiddenMapping = new WeakHashMap(); /** * Factory to create the views. */ protected CellViewFactory factory = null; /** * The set of visible cells. */ protected Set visibleSet = new HashSet(); /** * Ordered list of roots for the view. */ protected List roots = new ArrayList(); /** * Cached array of all ports for the view. */ protected PortView[] ports; /** * Only portions of the model are visible. */ protected boolean partial = false; /** * Controls if all attributes are local. If this is false then the * createLocalEdit will check the localAttributes set to see if a specific * attribute is local, otherwise it will assume that all attributes are * local. This allows to make all attributes local without actually knowing * them. Default is false. */ protected boolean allAttributesLocal = false; /** * A set containing all attribute keys that are stored in the cell views, in * other words, the view-local attributes. */ protected Set localAttributes = new HashSet(); /** * Constructs a graph layout cache. */ public GraphLayoutCache() { this(new DefaultGraphModel(), new DefaultCellViewFactory()); } /** * Constructs a view for the specified model that uses factory * to create its views. * * @param model * the model that constitues the data source */ public GraphLayoutCache(GraphModel model, CellViewFactory factory) { this(model, factory, false); } /** * Constructs a view for the specified model that uses factory * to create its views. * * @param model * the model that constitues the data source */ public GraphLayoutCache(GraphModel model, CellViewFactory factory, boolean partial) { this(model, factory, null, null, partial); } /** * Constructs a view for the specified model that uses factory * to create its views. * * @param model * the model that constitues the data source */ public GraphLayoutCache(GraphModel model, CellViewFactory factory, CellView[] cellViews, CellView[] hiddenCellViews, boolean partial) { this.factory = factory; this.partial = partial; if (cellViews != null) { graphModel = model; for (int i = 0; i < cellViews.length; i++) { if (cellViews[i] != null) { putMapping(cellViews[i].getCell(), cellViews[i]); if (partial) visibleSet.add(cellViews[i].getCell()); } } insertViews(cellViews); // Notify observers for autosizing? } else { setModel(model); } if (hiddenCellViews != null) { for (int i = 0; i < hiddenCellViews.length; i++) hiddenMapping.put(hiddenCellViews[i].getCell(), hiddenCellViews[i]); } } // // GraphLayoutCacheListeners // /** * Adds a listener for the GraphLayoutCacheEvent posted after the graph * layout cache changes. * * @see #removeGraphLayoutCacheListener * @param l * the listener to add */ public void addGraphLayoutCacheListener(GraphLayoutCacheListener l) { listenerList.add(GraphLayoutCacheListener.class, l); } /** * Removes a listener previously added with addGraphLayoutCacheListener() * . * * @see #addGraphLayoutCacheListener * @param l * the listener to remove */ public void removeGraphLayoutCacheListener(GraphLayoutCacheListener l) { listenerList.remove(GraphLayoutCacheListener.class, l); } /** * Invoke this method after you've changed how the cells are to be * represented in the graph. */ public void cellViewsChanged(final CellView[] cellViews) { if (cellViews != null) { fireGraphLayoutCacheChanged(this, new GraphLayoutCacheEvent.GraphLayoutCacheChange() { public Object[] getInserted() { return null; } public Object[] getRemoved() { return null; } public Map getPreviousAttributes() { return null; } public Object getSource() { return this; } public Object[] getChanged() { return cellViews; } public Map getAttributes() { return null; } public Object[] getContext() { return null; } public Rectangle2D getDirtyRegion() { return null; } public void setDirtyRegion(Rectangle2D dirty) {} }); } } /* * Notify all listeners that have registered interest for notification on * this event type. The event instance is lazily created using the * parameters passed into the fire method. * * @see EventListenerList */ protected void fireGraphLayoutCacheChanged(Object source, GraphLayoutCacheEvent.GraphLayoutCacheChange edit) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); GraphLayoutCacheEvent e = null; // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == GraphLayoutCacheListener.class) { // Lazily create the event: if (e == null) e = new GraphLayoutCacheEvent(source, edit); ((GraphLayoutCacheListener) listeners[i + 1]) .graphLayoutCacheChanged(e); } } } /** * Return an array of all GraphLayoutCacheListener that were added to this * model. */ public GraphLayoutCacheListener[] getGraphLayoutCacheListeners() { return (GraphLayoutCacheListener[]) listenerList .getListeners(GraphLayoutCacheListener.class); } // // Accessors // /** * Sets the factory that creates the cell views. */ public void setFactory(CellViewFactory factory) { this.factory = factory; } /** * Returns the factory that was passed to the constructor. */ public CellViewFactory getFactory() { return factory; } /** * Sets the current model. */ public void setModel(GraphModel model) { roots.clear(); mapping.clear(); hiddenMapping.clear(); visibleSet.clear(); graphModel = model; if (!isPartial()) { Object[] cells = DefaultGraphModel.getRoots(getModel()); CellView[] cellViews = getMapping(cells, true); insertViews(cellViews); } // Update PortView Cache and Notify Observers update(); } /** * Sets the current model. */ public void update() { updatePorts(); cellViewsChanged(getRoots()); } /** * @return Returns an unordered array of all visible cellviews. */ public CellView[] getCellViews() { Collection coll = mapping.values(); CellView[] result = new CellView[coll.size()]; coll.toArray(result); return result; } /** * Returns the bounding box for the specified cell views. */ public static Rectangle2D getBounds(CellView[] views) { if (views != null && views.length > 0) { Rectangle2D r = (views[0] != null) ? views[0].getBounds() : null; Rectangle2D ret = (r != null) ? (Rectangle2D) r.clone() : null; for (int i = 1; i < views.length; i++) { r = (views[i] != null) ? views[i].getBounds() : null; if (r != null) { if (ret == null) ret = (r != null) ? (Rectangle2D) r.clone() : null; else Rectangle2D.union(ret, r, ret); } } return ret; } return null; } /** * A helper method to return various arrays of cells that are visible in * this cache. For example, to get all selected vertices in a graph, do * graph.getSelectionCells(graph.getGraphLayoutCache().getCells(false, true, false, false)); */ public Object[] getCells(boolean groups, boolean vertices, boolean ports, boolean edges) { CellView[] views = getCellViews(); List result = new ArrayList(views.length); GraphModel model = getModel(); for (int i = 0; i < views.length; i++) { Object cell = views[i].getCell(); boolean isEdge = model.isEdge(cell); if ((ports || !model.isPort(cell))) { if (((((ports || vertices) && !isEdge) || (edges && isEdge)) && views[i] .isLeaf()) || (groups && !views[i].isLeaf())) result.add(views[i].getCell()); } } return result.toArray(); } /** * Returns a nested map of (cell, map) pairs that represent all attributes * of all cell views in this view. * * @see #getCellViews */ public Map createNestedMap() { CellView[] cellViews = getCellViews(); Map nested = new Hashtable(); for (int i = 0; i < cellViews.length; i++) { nested.put(cellViews[i].getCell(), new Hashtable((Map) cellViews[i] .getAllAttributes().clone())); } return nested; } /** * @return Returns an unordered array of all hidden cellviews. */ public CellView[] getHiddenCellViews() { Collection coll = hiddenMapping.values(); CellView[] result = new CellView[coll.size()]; coll.toArray(result); return result; } /** * Remaps all existing views using the CellViewFactory * and replaces the respective root views. */ public synchronized void reload() { List newRoots = new ArrayList(); Map oldMapping = new Hashtable(mapping); mapping.clear(); Iterator it = oldMapping.keySet().iterator(); Set rootsSet = new HashSet(roots); while (it.hasNext()) { Object cell = it.next(); CellView oldView = (CellView) oldMapping.get(cell); CellView newView = getMapping(cell, true); newView.changeAttributes(this, oldView.getAttributes()); // newView.refresh(getModel(), this, false); if (rootsSet.contains(oldView)) newRoots.add(newView); } // replace hidden hiddenMapping.clear(); roots = newRoots; } /** * Returns the current model. */ public GraphModel getModel() { return graphModel; } /** * Returns the roots of the view. */ public CellView[] getRoots() { CellView[] views = new CellView[roots.size()]; roots.toArray(views); return views; } /** * Return all root cells that intersect the given rectangle. */ public CellView[] getRoots(Rectangle2D clip) { java.util.List result = new ArrayList(); CellView[] views = getRoots(); for (int i = 0; i < views.length; i++) if (views[i].getBounds().intersects(clip)) result.add(views[i]); views = new CellView[result.size()]; result.toArray(views); return views; } /** * Returns a an array with the visible cells in cells. */ public Object[] getVisibleCells(Object[] cells) { if (cells != null) { List result = new ArrayList(cells.length); for (int i = 0; i < cells.length; i++) if (isVisible(cells[i])) result.add(cells[i]); return result.toArray(); } return null; } /** * Returns the ports of the view. */ public PortView[] getPorts() { return ports; } /** * Updates the cached array of ports. */ protected void updatePorts() { Object[] roots = DefaultGraphModel.getRoots(graphModel); List list = DefaultGraphModel.getDescendants(graphModel, roots); if (list != null) { ArrayList result = new ArrayList(); Iterator it = list.iterator(); while (it.hasNext()) { Object cell = it.next(); if (graphModel.isPort(cell)) { CellView portView = getMapping(cell, false); if (portView != null) { result.add(portView); portView.refresh(this, this, false); } } } ports = new PortView[result.size()]; result.toArray(ports); } } public void refresh(CellView[] views, boolean create) { if (views != null) for (int i = 0; i < views.length; i++) refresh(views[i], create); } public void refresh(CellView view, boolean create) { if (view != null) { view.refresh(this, this, create); CellView[] children = view.getChildViews(); for (int i = 0; i < children.length; i++) refresh(children[i], create); } } public void update(CellView[] views) { if (views != null) for (int i = 0; i < views.length; i++) update(views[i]); } public void update(CellView view) { if (view != null) { view.update(this); CellView[] children = view.getChildViews(); for (int i = 0; i < children.length; i++) update(children[i]); } } // // Update View based on Model Change // /** * Called from BasicGraphUI.ModelHandler to update the view based on the * specified GraphModelEvent. */ public void graphChanged(GraphModelEvent.GraphModelChange change) { // Get Old Attributes From GraphModelChange (Undo) -- used to remap // removed cells CellView[] views = change.getViews(this); if (views != null) { // Only ex-visible views are piggybacked for (int i = 0; i < views.length; i++) if (views[i] != null) { // Do not use putMapping because cells are invisible mapping.put(views[i].getCell(), views[i]); } // Ensure visible state setVisibleImpl(getCells(views), true); } // Fetch View Order Of Changed Cells (Before any changes) Object[] changed = change.getChanged(); // Fetch Views to Insert before Removal (Special case: two step process, // see setModel) getMapping(change.getInserted(), true); // Remove and Hide Roots views = removeCells(change.getRemoved()); // Store Removed Attributes In GraphModelChange (Undo) change.putViews(this, views); // Insert New Roots // insertViews(insertViews); // Hide edges with invisible source or target if (isPartial()) { // Then show showCellsForChange(change); // First hide hideCellsForChange(change); } // Refresh Changed Cells if (changed != null && changed.length > 0) { // Restore All Cells in Model Order (Replace Roots) for (int i = 0; i < changed.length; i++) { CellView view = getMapping(changed[i], false); if (view != null) { view.refresh(this, this, true); // Update child edges in groups (routing) update(view); } } } reloadRoots(); // Refresh Context of Changed Cells (=Connected Edges) refresh(getMapping(getContext(change), false), false); updatePorts(); } /** * Completely reloads all roots from the model in the order returned by * DefaultGraphModel.getAll. This uses the current visibleSet and mapping to * fetch the cell views for the cells. */ protected void reloadRoots() { // Reorder roots Object[] orderedCells = DefaultGraphModel.getAll(graphModel); List newRoots = new ArrayList(); for (int i = 0; i < orderedCells.length; i++) { CellView view = getMapping(orderedCells[i], false); if (view != null) { view.refresh(this, this, true); if (view.getParentView() == null) { newRoots.add(view); } } } roots = newRoots; } /** * Hook for subclassers to augment the context for a graphChange. This means * you can add additional cells that should be refreshed on a special change * event. eg. parallel edges when one is removed or added. */ protected Object[] getContext(GraphModelEvent.GraphModelChange change) { return change.getContext(); } protected void hideCellsForChange(GraphModelEvent.GraphModelChange change) { // Hide visible edges between invisible vertices // 1. Remove attached edges of removed cells // 2. Remove edges who's source or target has changed to // invisible. Object[] tmp = change.getRemoved(); Set removed = new HashSet(); if (tmp != null) for (int i = 0; i < tmp.length; i++) removed.add(tmp[i]); if (hidesDanglingConnections || hidesExistingConnections) { Object[] changed = change.getChanged(); for (int i = 0; i < changed.length; i++) { CellView view = getMapping(changed[i], false); if (view instanceof EdgeView) { EdgeView edge = (EdgeView) view; Object oldSource = (edge.getSource() == null) ? null : edge .getSource().getCell(); Object oldTarget = (edge.getTarget() == null) ? null : edge .getTarget().getCell(); Object newSource = graphModel.getSource(changed[i]); Object newTarget = graphModel.getTarget(changed[i]); boolean hideExisting = (hidesExistingConnections && ((newSource != null && !hasVisibleParent( newSource, null)) || (newTarget != null && !hasVisibleParent( newTarget, null)))); if ((hidesDanglingConnections && (removed .contains(oldSource) || removed.contains(oldTarget))) || hideExisting) { setVisibleImpl(new Object[] { changed[i] }, false); } } } } } /** * Checks if the port or one of its parents is visible. */ protected boolean hasVisibleParent(Object cell, Set invisible) { boolean isVisible = false; do { isVisible = (invisible == null || !invisible.contains(cell)) ? isVisible(cell) : false; cell = getModel().getParent(cell); } while (cell != null && !isVisible); return isVisible; } protected void showCellsForChange(GraphModelEvent.GraphModelChange change) { Object[] inserted = change.getInserted(); if (inserted != null && showsInsertedConnections) { for (int i = 0; i < inserted.length; i++) { if (!isVisible(inserted[i])) { Object source = graphModel.getSource(inserted[i]); Object target = graphModel.getTarget(inserted[i]); if ((source != null || target != null) && (isVisible(source) && isVisible(target))) setVisibleImpl(new Object[] { inserted[i] }, true); } } } if (change.getConnectionSet() != null) { Set changedSet = change.getConnectionSet().getChangedEdges(); if (changedSet != null && showsChangedConnections) { Object[] changed = changedSet.toArray(); for (int i = 0; i < changed.length; i++) { if (!isVisible(changed[i])) { Object source = graphModel.getSource(changed[i]); Object target = graphModel.getTarget(changed[i]); if ((source != null || target != null) && (isVisible(source) && isVisible(target)) && !isVisible(changed[i])) setVisibleImpl(new Object[] { changed[i] }, true); } } } } } /** * Adds the specified model root cells to the view. Do not add a view that * is already in roots. */ public void insertViews(CellView[] views) { if (views != null) { refresh(views, true); for (int i = 0; i < views.length; i++) { if (views[i] != null && getMapping(views[i].getCell(), false) != null) { CellView parentView = views[i].getParentView(); Object parent = (parentView != null) ? parentView.getCell() : null; if (!graphModel.isPort(views[i].getCell()) && parent == null) { roots.add(views[i]); } } } } } /** * Removes the specified model root cells from the view by removing the * mapping between the cell and its view and makes the cells invisible. */ public CellView[] removeCells(Object[] cells) { if (cells != null && cells.length > 0) { CellView[] views = new CellView[cells.length]; // Store views to be removed from roots in an intermediate // set for performance reasons Set removedRoots = null; for (int i = 0; i < cells.length; i++) { views[i] = removeMapping(cells[i]); if (views[i] != null) { views[i].removeFromParent(); if (removedRoots == null) { removedRoots = new HashSet(); } removedRoots.add(views[i]); visibleSet.remove(views[i].getCell()); } } if (removedRoots !=null && removedRoots.size() > 0) { // If any roots have been removed, reform the roots // lists appropriately, keeping the order the same int newRootsSize = roots.size() - removedRoots.size(); if (newRootsSize < 8) { newRootsSize = 8; } List newRoots = new ArrayList(newRootsSize); Iterator iter = roots.iterator(); while (iter.hasNext()) { Object cell = iter.next(); if (!removedRoots.contains(cell)) { newRoots.add(cell); } } roots = newRoots; } return views; } return null; } // // Cell Mapping // /** * Takes an array of views and returns the array of the corresponding cells * by using getCell for each view. */ public Object[] getCells(CellView[] views) { if (views != null) { Object[] cells = new Object[views.length]; for (int i = 0; i < views.length; i++) if (views[i] != null) cells[i] = views[i].getCell(); return cells; } return null; } /** * Returns the view for the specified cell. If create is true and no view is * found then a view is created using createView(Object). */ public CellView getMapping(Object cell, boolean create) { if (cell == null) return null; CellView view = (CellView) mapping.get(cell); if (view == null && create && isVisible(cell)) { view = (CellView) hiddenMapping.get(cell); if (view != null) { putMapping(cell, view); hiddenMapping.remove(cell); } else { view = factory.createView(graphModel, cell); putMapping(cell, view); view.refresh(this, this, true); // Create Dependent // Views view.update(this); } } return view; } /** * Returns the views for the specified array of cells without creating these * views on the fly. */ public CellView[] getMapping(Object[] cells) { return getMapping(cells, false); } /** * Returns the views for the specified array of cells. Returned array may * contain null pointers if the respective cell is not mapped in this view * and create is false. */ public CellView[] getMapping(Object[] cells, boolean create) { if (cells != null) { CellView[] result = new CellView[cells.length]; for (int i = 0; i < cells.length; i++) result[i] = getMapping(cells[i], create); return result; } return null; } /** * Associates the specified model cell with the specified view. */ public void putMapping(Object cell, CellView view) { if (cell != null && view != null) mapping.put(cell, view); } /** * Removes the association for the specified model cell and returns the view * that was previously associated with the cell. Updates the portlist if * necessary. */ public CellView removeMapping(Object cell) { if (cell != null) { CellView view = (CellView) mapping.remove(cell); return view; } return null; } /** * Whether or not the specified cell is visible. * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. * null is always visible * * @param cell the whose visibility to determine * @return whether or not the cell is visible */ public boolean isVisible(Object cell) { return !isPartial() || visibleSet.contains(cell) || cell == null; } /** * Returns the set of visible sets in this view. * @return the set of visible sets in this view. */ public Set getVisibleSet() { return new HashSet(visibleSet); } /** * Applies the specified set of cells as being those visible * @param visible the set of visible cells */ public void setVisibleSet(Set visible) { visibleSet = visible; } /** * Makes the specified cell visible or invisible depending on the flag * passed in. Note the cell really is a cell, not a cell view. * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. * * @param cell * the cell whose visibility is to be changed * @param visible * true if cell is to be made visible */ public void setVisible(Object cell, boolean visible) { setVisible(new Object[] { cell }, visible); } /** * Makes the specified cells visible or invisible depending on the flag * passed in. Note the cells really are cells, not cell views. * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. * * @param cells * the cells whose visibility is to be changed * @param visible * true if the cells are to be made visible */ public void setVisible(Object[] cells, boolean visible) { if (visible) setVisible(cells, null); else setVisible(null, cells); } /** * Changes the visibility state of the cells passed in. Note that the arrays * must contain cells, not cell views. * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. * * @param visible * cells to be made visible * @param invisible * cells to be made invisible */ public void setVisible(Object[] visible, Object[] invisible) { setVisible(visible, invisible, null); } /** * Changes the visibility state of the cells passed in. Note that the arrays * must contain cells, not cell views. * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. * * @param visible * cells to be made visible * @param invisible * cells to be made invisible * @param cs * a ConnectionSet describing the new state of * edge connections in the graph */ public void setVisible(Object[] visible, Object[] invisible, ConnectionSet cs) { setVisible(visible, invisible, null, cs); } /** * Changes the visibility state of the cells passed in. Note that the arrays * must contain cells, not cell views. * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. * * @param visible * cells to be made visible * @param invisible * cells to be made invisible * @param attributes * a nested attribute map of cells/attribute maps * @param cs * a ConnectionSet describing the new state of * edge connections in the graph */ public void setVisible(Object[] visible, Object[] invisible, Map attributes, ConnectionSet cs) { GraphLayoutCacheEdit edit = new GraphLayoutCacheEdit(null, attributes, visible, invisible); edit.end(); graphModel.edit(attributes, cs, null, new UndoableEdit[] { edit }); } // This is used to augment the array passed to the setVisible method. protected Object[] addVisibleDependencies(Object[] cells, boolean visible) { if (cells != null) { if (visible) { // Make ports and source and target vertex visible Set all = new HashSet(); for (int i = 0; i < cells.length; i++) { all.add(cells[i]); // Add ports all.addAll(getPorts(cells[i])); // Add source vertex and ports Collection coll = getParentPorts(graphModel .getSource(cells[i])); if (coll != null) all.addAll(coll); // Add target vertex and ports coll = getParentPorts(graphModel.getTarget(cells[i])); if (coll != null) all.addAll(coll); } if (showsExistingConnections) { Set tmp = DefaultGraphModel.getEdges(getModel(), cells); Iterator it = tmp.iterator(); while (it.hasNext()) { Object obj = it.next(); Object source = graphModel.getSource(obj); Object target = graphModel.getTarget(obj); if ((isVisible(source) || all.contains(source)) && (isVisible(target) || all.contains(target))) all.add(obj); } } all.removeAll(visibleSet); all.remove(null); return all.toArray(); } else { if (hidesExistingConnections) { Set all = new HashSet(); for (int i = 0; i < cells.length; i++) { all.addAll(getPorts(cells[i])); all.add(cells[i]); } Iterator it = DefaultGraphModel.getEdges(graphModel, cells) .iterator(); while (it.hasNext()) { Object edge = it.next(); Object newSource = graphModel.getSource(edge); Object newTarget = graphModel.getTarget(edge); // Note: At this time the cells are not yet hidden if ((newSource != null && !hasVisibleParent(newSource, all)) || (newTarget != null && !hasVisibleParent( newTarget, all))) { all.add(edge); } } all.remove(null); return all.toArray(); } } } return cells; } /** * The actual implementation of changing cells' visibility state. This * method does not deal with creating the undo or updating the * GraphLayoutCache correctly. The setVisible methods in this * class are intended to be the main public way to change visiblilty. * However, if you do not require the undo to be formed, this method is much * quicker, just note that you must call updatePorts if this * method returns true. * * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. * * @param cells * @param visible * @return whether or not the ports needed updating in the calling method */ public boolean setVisibleImpl(Object[] cells, boolean visible) { cells = addVisibleDependencies(cells, visible); if (cells != null && isPartial()) { boolean updatePorts = false; // Update Visible Set CellView[] views = new CellView[cells.length]; if (!visible) { views = removeCells(cells); } // Set used for model roots contains call for performance Set modelRoots = null; for (int i = 0; i < cells.length; i++) { if (cells[i] != null) { if (visible) { visibleSet.add(cells[i]); views[i] = getMapping(cells[i], true); } else { if (views[i] != null) { if (modelRoots == null) { modelRoots = new HashSet(DefaultGraphModel .getRootsAsCollection(getModel())); } if (modelRoots.contains(views[i].getCell()) && remembersCellViews) { hiddenMapping.put(views[i].getCell(), views[i]); } updatePorts = true; } } } } // Make Cell Views Visible (if not already in place) if (visible) { Set parentSet = new HashSet(); Set removedRoots = null; for (int i = 0; i < views.length; i++) { if (views[i] != null) { CellView view = views[i]; // Remove all children from roots CellView[] children = AbstractCellView .getDescendantViews(new CellView[] { view }); for (int j = 0; j < children.length; j++) { if (removedRoots == null) { removedRoots = new HashSet(); } removedRoots.add(children[j]); } view.refresh(this, this, false); // Link cellView into graphLayoutCache CellView parentView = view.getParentView(); if (parentView != null) parentSet.add(parentView); updatePorts = true; } } if (removedRoots !=null && removedRoots.size() > 0) { // If any roots have been removed, reform the roots // lists appropriately, keeping the order the same List newRoots = new ArrayList(); Iterator iter = roots.iterator(); while (iter.hasNext()) { Object cell = iter.next(); if (!removedRoots.contains(cell)) { newRoots.add(cell); } } roots = newRoots; } CellView[] parentViews = new CellView[parentSet.size()]; parentSet.toArray(parentViews); refresh(parentViews, true); } return updatePorts; } return false; } protected Collection getParentPorts(Object cell) { // does nothing if a parent is already visible Object parent = graphModel.getParent(cell); while (parent != null) { if (isVisible(parent)) return null; parent = graphModel.getParent(parent); } // Else returns the parent and all ports parent = graphModel.getParent(cell); Collection collection = getPorts(parent); collection.add(parent); return collection; } protected Collection getPorts(Object cell) { LinkedList list = new LinkedList(); for (int i = 0; i < graphModel.getChildCount(cell); i++) { Object child = graphModel.getChild(cell, i); if (graphModel.isPort(child)) list.add(child); } return list; } // // Change Support // public boolean isPartial() { return partial; } /** * Required for XML persistence * * @return whether or not the cache is partial */ public boolean getPartial() { return isPartial(); } /** * Messaged when the user has altered the value for the item identified by * cell to newValue. If newValue signifies a truly new value the model * should post a graphCellsChanged event. This calls * augmentNestedMapForValueChange. */ public void valueForCellChanged(Object cell, Object newValue) { Map nested = null; if (isAutoSizeOnValueChange()) { CellView view = getMapping(cell, false); if (view != null) { AttributeMap attrs = view.getAllAttributes(); Rectangle2D bounds = GraphConstants.getBounds(attrs); Rectangle2D dummyBounds = null; // Force the model to store the old bounds if (bounds != null) { dummyBounds = attrs.createRect(bounds.getX(), bounds.getY(), 0, 0); } else { dummyBounds = attrs.createRect(0, 0, 0, 0); } nested = GraphConstants.createAttributes(new Object[] { cell }, new Object[] { GraphConstants.RESIZE, GraphConstants.BOUNDS }, new Object[] { Boolean.TRUE, dummyBounds }); } } else { nested = new Hashtable(); nested.put(cell, new Hashtable()); } augmentNestedMapForValueChange(nested, cell, newValue); edit(nested, null, null, null); } /** * Hook for subclassers to add more stuff for value changes. Currently this * adds the new value to the change. */ protected void augmentNestedMapForValueChange(Map nested, Object cell, Object newValue) { Map attrs = (Map) nested.get(cell); if (attrs != null) GraphConstants.setValue(attrs, newValue); } /** * Inserts the cells and connections into the model, and * absorbs the local attributes. This implementation sets the inserted cells * visible and selects the new roots depending on graph.selectNewCells. */ public void insert(Object[] roots, Map attributes, ConnectionSet cs, ParentMap pm, UndoableEdit[] e) { Object[] visible = null; if (isPartial() && showsInsertedCells) { List tmp = DefaultGraphModel.getDescendants(graphModel, roots); tmp.removeAll(visibleSet); if (!tmp.isEmpty()) visible = tmp.toArray(); } // Absorb local attributes GraphLayoutCacheEdit edit = createLocalEdit(roots, attributes, visible, null); if (edit != null) e = augment(e, edit); graphModel.insert(roots, attributes, cs, pm, e); } /** * Inserts the cloned cells from the clone map and clones the passed-in * arguments according to the clone map before insertion and returns the * clones in order of the cells. This example shows how to clone the current * selection and get a reference to the clones: * *

	 * Object[] cells = graph.getDescendants(graph.order(graph.getSelectionCells()));
	 * ConnectionSet cs = ConnectionSet.create(graphModel, cells, false);
	 * ParentMap pm = ParentMap.create(graphModel, cells, false, true);
	 * cells = graphLayoutCache.insertClones(cells, graph.cloneCells(cells),
	 * 		attributes, cs, pm, 0, 0);
	 * 
*/ public Object[] insertClones(Object[] cells, Map clones, Map nested, ConnectionSet cs, ParentMap pm, double dx, double dy) { if (cells != null) { if (cs != null) cs = cs.clone(clones); if (pm != null) pm = pm.clone(clones); if (nested != null) { nested = GraphConstants.replaceKeys(clones, nested); AttributeMap.translate(nested.values(), dx, dy); } // Replace cells in order Object[] newCells = new Object[cells.length]; for (int i = 0; i < cells.length; i++) newCells[i] = clones.get(cells[i]); // Insert into cache/model insert(newCells, nested, cs, pm, null); return newCells; } return null; } /** * Inserts the specified vertex into the graph model. This method does in * fact nothing, it calls insert edge with the vertex and the source and * target port set to null. This example shows how to add a vertex with a * port and a black border: * *
	 * DefaultGraphCell vertex = new DefaultGraphCell("Hello, world!");
	 * Map attrs = vertex.getAttributes();
	 * GraphConstants.setOpaque(attrs, false);
	 * GraphConstants.setBorderColor(attrs, Color.black);
	 * DefaultPort port = new DefaultPort();
	 * vertex.add(port);
	 * port.setParent(vertex);
	 * graph.getGraphLayoutCache().insert(vertex);
	 * 
* * @param cell * inserts the specified cell in the cache */ public void insert(Object cell) { insert(new Object[] { cell }); } /** * Inserts the specified edge into the graph model. This method does in fact * nothing, it calls insert with a default connection set. * * @param edge * the edge to be inserted * @param source * the source port this edge is connected to * @param target * the target port this edge is connected to */ public void insertEdge(Object edge, Object source, Object target) { insert(new Object[] { edge }, new Hashtable(), new ConnectionSet(edge, source, target), new ParentMap()); } /** * Inserts the specified cells into the graph model. This method is a * general implementation of cell insertion. If the source and target port * are null, then no connection set is created. The method uses the * attributes from the specified edge and the egdge's children to construct * the insert call. This example shows how to insert an edge with a special * arrow between two known vertices: * *
	 * Object source = graph.getDefaultPortForCell(sourceVertex).getCell();
	 * Object target = graph.getDefaultPortForCell(targetVertex).getCell();
	 * DefaultEdge edge = new DefaultEdge("Hello, world!");
	 * edge.setSource(source);
	 * edge.setTarget(target);
	 * Map attrs = edge.getAttributes();
	 * GraphConstants.setLineEnd(attrs, GraphConstants.ARROW_TECHNICAL);
	 * graph.getGraphLayoutCache().insert(edge);
	 * 
*/ public void insert(Object[] cells) { insert(cells, new Hashtable(), new ConnectionSet(), new ParentMap()); } /** * Variant of the insert method that allows to pass a default connection set * and parent map and nested map. */ public void insert(Object[] cells, Map nested, ConnectionSet cs, ParentMap pm) { if (cells != null) { if (nested == null) nested = new Hashtable(); if (cs == null) cs = new ConnectionSet(); if (pm == null) pm = new ParentMap(); for (int i = 0; i < cells.length; i++) { // Using the children of the vertex we construct the parent map. int childCount = getModel().getChildCount(cells[i]); for (int j = 0; j < childCount; j++) { Object child = getModel().getChild(cells[i], j); pm.addEntry(child, cells[i]); // And add their attributes to the nested map AttributeMap attrs = getModel().getAttributes(child); if (attrs != null) nested.put(child, attrs); } // A nested map with the vertex as key // and its attributes as the value // is required for the model. Map attrsTmp = (Map) nested.get(cells[i]); Map attrs = getModel().getAttributes(cells[i]); if (attrsTmp != null) attrs.putAll(attrsTmp); nested.put(cells[i], attrs); // Check if we have parameters for a connection set. Object sourcePort = getModel().getSource(cells[i]); if (sourcePort != null) cs.connect(cells[i], sourcePort, true); Object targetPort = getModel().getTarget(cells[i]); if (targetPort != null) cs.connect(cells[i], targetPort, false); } // Create an array with the parent and its children. cells = DefaultGraphModel.getDescendants(getModel(), cells) .toArray(); // Finally call the insert method on the parent class. insert(cells, nested, cs, pm, null); } } /** * Inserts the specified cell as a parent of children. Note: All cells that * are not yet in the model will be inserted. This example shows how to * group the current selection and pass the group default bounds in case it * is later collapsed: * *
	 * DefaultGraphCell group = new DefaultGraphCell("Hello, world!");
	 * Object[] cells = DefaultGraphModel.order(graph.getModel(), graph
	 * 		.getSelectionCells());
	 * Rectangle2D bounds = graph.getCellBounds(cells);
	 * if (bounds != null) {
	 * 	bounds = new Rectangle2D.Double(bounds.getX() + bounds.getWidth() / 4,
	 * 			bounds.getY() + bounds.getHeight() / 4, bounds.getWidth() / 2,
	 * 			bounds.getHeight() / 2);
	 * 	GraphConstants.setBounds(group.getAttributes(), bounds);
	 * }
	 * graph.getGraphLayoutCache().insertGroup(group, cells);
	 * 
*/ public void insertGroup(Object group, Object[] children) { if (group != null && children != null && children.length > 0) { Map nested = new Hashtable(); // List to store all children that are not in the model List newCells = new ArrayList(children.length + 1); // Plus the group cell at pos 0 if (!getModel().contains(group)) { newCells.add(group); } // Create a parent map for the group and the children, and // store the children's attributes in the nested map. // Note: This implementation assumes that the children have // not yet been added to the group object. Therefore, // the insert method will only collect the group // attributes, but will ignore the child attributes. ParentMap pm = new ParentMap(); for (int i = 0; i < children.length; i++) { pm.addEntry(children[i], group); if (!getModel().contains(children[i])) { newCells.add(children[i]); AttributeMap attrs = getModel().getAttributes(children[i]); if (attrs != null) nested.put(children[i], attrs); } } if (newCells.isEmpty()) edit(nested, null, pm, null); else insert(newCells.toArray(), nested, null, pm); } } /** * Removes cells from the model. */ public void remove(Object[] cells) { graphModel.remove(cells); } /** * Removes cells from the model, including all children and connected edges * if children or edges is true, respectively. * * @param cells * The cells to remove. * @param descendants * Whether to remove all descendants as well. * @param edges * Whether to remove all connected edges as well. */ public void remove(Object[] cells, boolean descendants, boolean edges) { if (cells != null && cells.length > 0) { if (edges) { Object[] tmp = DefaultGraphModel.getEdges(getModel(), cells) .toArray(); Object[] newCells = new Object[cells.length + tmp.length]; System.arraycopy(cells, 0, newCells, 0, cells.length); System.arraycopy(tmp, 0, newCells, cells.length, tmp.length); cells = newCells; } if (descendants) cells = DefaultGraphModel.getDescendants(getModel(), cells) .toArray(); remove(cells); } } /** * Hides the specified cells with all children if descandants * is true. * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. */ public void hideCells(Object[] cells, boolean descandants) { if (cells != null && cells.length > 0) { if (descandants) cells = DefaultGraphModel.getDescendants(getModel(), cells) .toArray(); setVisible(cells, false); } } /** * Shows the specified cells with all children if descandants * is true. * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. */ public void showCells(Object[] cells, boolean descandants) { if (cells != null && cells.length > 0) { if (descandants) cells = DefaultGraphModel.getDescendants(getModel(), cells) .toArray(); setVisible(cells, true); } } /** * Ungroups all groups in cells and returns the children that are not ports. * Note: This replaces the parents with their group cells in the group * structure. */ public Object[] ungroup(Object[] cells) { if (cells != null && cells.length > 0) { ArrayList toRemove = new ArrayList(); ArrayList children = new ArrayList(); boolean groupExists = false; for (int i = 0; i < cells.length; i++) { boolean childExists = false; ArrayList tempPortList = new ArrayList(); for (int j = 0; j < getModel().getChildCount(cells[i]); j++) { Object child = getModel().getChild(cells[i], j); if (!getModel().isPort(child)) { children.add(child); childExists = true; } else { tempPortList.add(child); } } if (childExists) { toRemove.addAll(tempPortList); toRemove.add(cells[i]); groupExists = true; } } if (groupExists) remove(toRemove.toArray()); return children.toArray(); } return null; } /** * Toggles the collapsed state of the specified cells. * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. * * @param cells * The cells to toggle the collapsed state for. * @param collapseOnly * Whether cells should only be collapsed. * @param expandOnly * Whether cells should only be expanded. * */ public void toggleCollapsedState(Object[] cells, boolean collapseOnly, boolean expandOnly) { List toExpand = new ArrayList(); List toCollapse = new ArrayList(); for (int i = 0; i < cells.length; i++) { Object cell = cells[i]; CellView view = getMapping(cell, false); if (view != null) { // Adds to list of expansion cells if it is a leaf in the layout // cache and we do not only want to collapse. if (view.isLeaf() && !collapseOnly) toExpand.add(view.getCell()); // Else adds to list of to-be-collapsed cells if it is not a // leaf in the layout cache we do not only want to expand. else if (!view.isLeaf() && !expandOnly) toCollapse.add(view.getCell()); } } if (!toCollapse.isEmpty() || !toExpand.isEmpty()) setCollapsedState(toCollapse.toArray(), toExpand.toArray()); } /** * Collapses all groups by hiding all their descendants. * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. * * @param groups */ public void collapse(Object[] groups) { setCollapsedState(groups, null); } /** * Expands all groups by showing all children. (Note: This does not show all * descandants, but only the first generation of children.) * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. */ public void expand(Object[] cells) { setCollapsedState(null, cells); } /** * Collapses and/or expands the specified cell(s) * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. * * @param collapse * the cells to be collapsed * @param expand * the cells to be expanded */ public void setCollapsedState(Object[] collapse, Object[] expand) { // Get all descandants for the groups ConnectionSet cs = new ConnectionSet(); // Collapse cells List toHide = DefaultGraphModel.getDescendants(getModel(), collapse); if (collapse != null) { // Remove the groups themselfes for (int i = 0; i < collapse.length; i++) { toHide.remove(collapse[i]); cellWillCollapse(collapse[i]); } // Remove the ports (will be hidden automatically) for (int i = 0; i < collapse.length; i++) { int childCount = getModel().getChildCount(collapse[i]); if (childCount > 0) { for (int j = 0; j < childCount; j++) { Object child = getModel().getChild(collapse[i], j); if (getModel().isPort(child)) { toHide.remove(child); } } } } } // Expand cells Set toShow = new HashSet(); if (expand != null) { for (int i = 0; i < expand.length; i++) { int childCount = getModel().getChildCount(expand[i]); for (int j = 0; j < childCount; j++) { toShow.add(getModel().getChild(expand[i], j)); } } } setVisible(toShow.toArray(), (toHide != null) ? toHide.toArray() : null, cs); } /** * Hook for subclassers to return the first or last visible port to replace * the current source or target port of the edge. This is called when groups * are collapsed for the edges that cross the group, ie. go from a child * cell to a cell which is outside the group. This implementation returns * the first port of the parent group if source is true, otherwise it * returns the last port of the parent group. */ protected Object getParentPort(Object edge, boolean source) { // Contains the parent of the parent vertex, eg. the group Object parent = getModel().getParent( (source) ? DefaultGraphModel.getSourceVertex(getModel(), edge) : DefaultGraphModel.getTargetVertex(getModel(), edge)); // Finds a port in the group int c = getModel().getChildCount(parent); for (int i = (source) ? c - 1 : 0; i < getModel().getChildCount(parent) && i >= 0; i += (source) ? -1 : +1) { Object child = getModel().getChild(parent, i); if (getModel().isPort(child)) { return child; } } return null; } /** * Hook for subclassers to return the port to be used for edges that have * been connected to the group. This is called from expand. This returns the * first port of the first or last vertex depending on source. */ protected Object getChildPort(Object edge, boolean source) { GraphModel model = getModel(); // Contains the parent of the port, eg. the group Object parent = (source) ? DefaultGraphModel.getSourceVertex(model, edge) : DefaultGraphModel.getTargetVertex(model, edge); // Finds a vertex in the group int c = model.getChildCount(parent); for (int i = (source) ? c - 1 : 0; i < c && i >= 0; i += (source) ? -1 : +1) { Object child = model.getChild(parent, i); if (!model.isEdge(child) && !model.isPort(child)) { // Finds a port in the vertex for (int j = 0; j < model.getChildCount(child); j++) { Object port = model.getChild(child, j); if (model.isPort(port)) { return port; } } } } return null; } /** * Applies the propertyMap and the connection changes to the * model. The initial edits that triggered the call are * considered to be part of this transaction. Notifies the model- and undo * listeners of the change. Note: The passed in attributes may contain * PortViews. */ public void edit(Map attributes, ConnectionSet cs, ParentMap pm, UndoableEdit[] e) { if (attributes != null || cs != null || pm != null || e != null) { Object[] visible = null; if (isPartial() && showsInvisibleEditedCells) { Set tmp = new HashSet(); if (attributes != null) tmp.addAll(attributes.keySet()); if (cs != null) tmp.addAll(cs.getChangedEdges()); if (pm != null) tmp.addAll(pm.getChangedNodes()); tmp.removeAll(visibleSet); if (!tmp.isEmpty()) visible = tmp.toArray(); } GraphLayoutCacheEdit edit = createLocalEdit(null, attributes, visible, null); if (edit != null) e = augment(e, edit); // Pass to model graphModel.edit(attributes, cs, pm, e); } } /** * A shortcut method that takes a nested map and passes it to the edit * method. */ public void edit(Map attributes) { edit(attributes, null, null, null); } /** * Applies the attributes to all cells by * creating a map that contains the attributes for each cell and passing it * to edit on this layout cache. Example: * *
	 * Map attrs = new java.util.Hashtable();
	 * GraphConstants.setBackground(attrs, Color.RED);
	 * graph.getGraphLayoutCache().edit(graph.getSelectionCells(), attrs);
	 * 
*/ public void edit(Object[] cells, Map attributes) { if (attributes != null && cells != null && cells.length > 0) { Map nested = new Hashtable(); for (int i = 0; i < cells.length; i++) nested.put(cells[i], attributes); edit(nested, null, null, null); } } /** * Applies the attributes to a single cell by * creating a map that contains the attributes for this cell and passing it * to edit on this layout cache. Example: * *
	 * Map attrs = new java.util.Hashtable();
	 * GraphConstants.setBackground(attrs, Color.RED);
	 * graph.getGraphLayoutCache().editCell(graph.getSelectionCell(), attrs);
	 * 
*/ public void editCell(Object cell, Map attributes) { if (attributes != null && cell != null) { edit(new Object[] { cell }, attributes); } } protected UndoableEdit[] augment(UndoableEdit[] e, UndoableEdit edit) { if (edit != null) { int size = (e != null) ? e.length + 1 : 1; UndoableEdit[] result = new UndoableEdit[size]; if (e != null) System.arraycopy(e, 0, result, 0, size - 2); result[size - 1] = edit; return result; } return e; } /** * Sends cells to back. Note: This expects an array of cells! */ public void toBack(Object[] cells) { if (cells != null && cells.length > 0) { graphModel.toBack(cells); } } /** * Brings cells to front. Note: This expects an array of * cells! */ public void toFront(Object[] cells) { if (cells != null && cells.length > 0) { graphModel.toFront(cells); } } /** * Creates a local edit for the specified change. A local operation contains * all visibility changes, as well as all changes to attributes that are * local, and all control attributes.
* Note: You must use cells as keys for the nested map, not cell views. */ protected GraphLayoutCacheEdit createLocalEdit(Object[] inserted, Map nested, Object[] visible, Object[] invisible) { // Create an edit if there are any view-local attributes set if ((nested != null && !nested.isEmpty()) && (!localAttributes.isEmpty() || isAllAttributesLocal())) { // Move or Copy Local Attributes to Local View Map globalMap = new Hashtable(); Map localMap = new Hashtable(); Map localAttr; Iterator it = nested.entrySet().iterator(); while (it.hasNext()) { localAttr = new Hashtable(); Map.Entry entry = (Map.Entry) it.next(); // (cell, Hashtable) Object cell = entry.getKey(); Map attr = (Map) entry.getValue(); // Create Difference of Existing and New Attributes CellView tmpView = getMapping(cell, false); if (tmpView != null) attr = tmpView.getAllAttributes().diff(attr); // End of Diff Iterator it2 = attr.entrySet().iterator(); while (it2.hasNext()) { Map.Entry entry2 = (Map.Entry) it2.next(); // (key, value) Object key = entry2.getKey(); Object value = entry2.getValue(); boolean isControlAttribute = isControlAttribute(cell, key, value); if (isAllAttributesLocal() || isControlAttribute || isLocalAttribute(cell, key, value)) { localAttr.put(key, value); if (!isControlAttribute) it2.remove(); } } if (!localAttr.isEmpty()) localMap.put(cell, localAttr); if (!attr.isEmpty()) globalMap.put(cell, attr); } nested.clear(); nested.putAll(globalMap); if (visible != null || invisible != null || !localMap.isEmpty()) { GraphLayoutCacheEdit edit = new GraphLayoutCacheEdit(inserted, new Hashtable(localMap), visible, invisible); edit.end(); return edit; } } else if (visible != null || invisible != null) { GraphLayoutCacheEdit edit = new GraphLayoutCacheEdit(inserted, null, visible, invisible); edit.end(); return edit; } return null; } /** * Returns true if the set of local attributes contains key */ protected boolean isLocalAttribute(Object cell, Object key, Object value) { return localAttributes.contains(key); } /** * Returns true if key is a control attribute */ protected boolean isControlAttribute(Object cell, Object key, Object value) { return GraphConstants.REMOVEALL.equals(key) || GraphConstants.REMOVEATTRIBUTES.equals(key); } /** * Handles the removal of view local attributes. Since these attributes are * only being stored in the view, the option is provided to copy the values * for that key into the model. Without this, those values are lost. * * @param key * the key of the view local attribute * @param addToModel * whether or not to move the attribute values to the graph model * @param override * whether or not to override the key's value in the model cell's * attribute map if it exists * @return whether or not the operation completed sucessfully */ public boolean removeViewLocalAttribute(Object key, boolean addToModel, boolean override) { if (localAttributes.contains(key)) { if (addToModel) { // Iterate through all views copying this attribute to the // cell. copyRemovedViewValue(key, addToModel, override, mapping .values()); copyRemovedViewValue(key, addToModel, override, hiddenMapping .values()); } localAttributes.remove(key); return true; } return false; } /** * Helper method to copy removed view local attributes to model cell's * * @param key * the key of the view local attribute * @param addToModel * whether or not to move the attribute values to the graph model * @param override * whether or not to override the key's value in the model cell's * attribute map if it exists * @param coll * the current collection being analysed */ private void copyRemovedViewValue(Object key, boolean addToModel, boolean override, Collection coll) { Iterator iter = coll.iterator(); while (iter.hasNext()) { CellView cellView = (CellView) iter.next(); Map attributes = cellView.getAttributes(); if (attributes.containsKey(key)) { if (addToModel) { Object cell = cellView.getCell(); Map cellAttributes = graphModel.getAttributes(cell); if (cellAttributes != null) { boolean cellContainsKey = cellAttributes .containsKey(key); // Write the model cell's attribute map key // if overriding is enabled or if key doesn't // exist if (!override || !cellContainsKey) { Object value = attributes.get(key); cellAttributes.put(key, value); } } } attributes.remove(key); } } } /** * An implementation of GraphLayoutCacheChange. */ public class GraphLayoutCacheEdit extends CompoundEdit implements GraphLayoutCacheEvent.GraphLayoutCacheChange { protected Object[] cells, previousCells = null; protected CellView[] context, hidden; protected Map attributes, previousAttributes; protected Object[] visible, invisible; /** * The dirty region associated with this event prior to the change */ protected Rectangle2D dirtyRegion = null; // Remember which cells have changed for finding their context protected Set changedCells = new HashSet(); /** * Constructs a GraphViewEdit. This modifies the attributes of the * specified views and may be used to notify UndoListeners. * * @param nested * the map that defines the new attributes */ public GraphLayoutCacheEdit(Map nested) { this(null, nested, null, null); attributes = nested; } /** * Constructs a GraphViewEdit. This modifies the attributes of the * specified views and may be used to notify UndoListeners. This should * also take an array of removed cell views, but it is not possible to * add further UndoableEdits to an already executed CompoundEdit, such * as a GraphModel change. Thus, to handle implicit changes -- rather * than piggybacking on the model's event -- the CompoundEdit's addEdit * method should be extended to accept and instantly execute sub- * sequent edits (implicit changes to the view, such as removing a * mapping, hiding a view or the like). * * @param inserted * an array of inserted cells * @param attributes * the map that defines the new attributes * @param visible * an array defining which cells are visible * @param invisible * an array defining which cells are invisible */ public GraphLayoutCacheEdit(Object[] inserted, Map attributes, Object[] visible, Object[] invisible) { super(); this.attributes = attributes; this.previousAttributes = attributes; this.cells = inserted; this.visible = visible; this.invisible = invisible; } public Object getSource() { return GraphLayoutCache.this; } public boolean isSignificant() { return true; } /** * Returns the cell views that have changed. */ public Object[] getChanged() { return changedCells.toArray(); } /** * Returns the cells that habe been made visible. */ public Object[] getInserted() { return invisible; } /** * Returns the cells that have changed. */ public Object[] getRemoved() { return visible; } /** * Returns the views that have not changed explicitly, but implicitly * because one of their dependent cells has changed. */ public Object[] getContext() { return context; } /** * Returns a map of (cell view, attribute) pairs. */ public Map getAttributes() { return attributes; } /** * Returns a map of (cell view, attribute) pairs. */ public Map getPreviousAttributes() { return previousAttributes; } public Rectangle2D getDirtyRegion() { return dirtyRegion; } public void setDirtyRegion(Rectangle2D dirty) { this.dirtyRegion = dirty; } /** * Redoes a change. * * @exception CannotRedoException * if the change cannot be redone */ public void redo() throws CannotRedoException { super.redo(); execute(); } /** * Undoes a change. * * @exception CannotUndoException * if the change cannot be undone */ public void undo() throws CannotUndoException { super.undo(); execute(); } /** * Execute this edit such that the next invocation to this method will * invert the last execution. */ public void execute() { GraphModel model = getModel(); changedCells.clear(); // Remember or restore hidden cells if (hidden != null) for (int i = 0; i < hidden.length; i++) if (hidden[i] != null) mapping.put(hidden[i].getCell(), hidden[i]); if (invisible != null && invisible.length >0) { CellView[] invisibleViews = new CellView[invisible.length]; invisibleViews = getMapping(invisible, true); Rectangle2D changedBounds = getBounds(invisibleViews); dirtyRegion = RectUtils.union(dirtyRegion, changedBounds); } if (!remembersCellViews) // already remembered hidden = getMapping(invisible); // Handle visibility boolean updatePorts = setVisibleImpl(visible, true) | setVisibleImpl(invisible, false); if (visible != null) { for (int i = 0; i < visible.length; i++) { changedCells.add(visible[i]); // Only calls if not inserted if (cells == null) cellExpanded(visible[i]); } } if (invisible != null) for (int i = 0; i < invisible.length; i++) changedCells.add(invisible[i]); // Swap arrays Object[] tmp = visible; visible = invisible; invisible = tmp; // Handle attributes if (attributes != null) { previousAttributes = attributes; changedCells.addAll(attributes.keySet()); } if (updatePorts) updatePorts(); // Add ancestor cells to changed cells Set parentSet = new HashSet(); Iterator it = changedCells.iterator(); while (it.hasNext()) { Object parent = model.getParent(it.next()); while (parent != null) { parentSet.add(parent); parent = model.getParent(parent); } } changedCells.addAll(parentSet); Set ctx = DefaultGraphModel.getEdges(getModel(), changedCells .toArray()); context = getMapping(ctx.toArray()); Set allChangedCells = new HashSet(changedCells); allChangedCells.addAll(ctx); CellView[] allChangedCellViews = getMapping(allChangedCells.toArray()); Rectangle2D changedBounds = getBounds(allChangedCellViews); dirtyRegion = RectUtils.union(dirtyRegion, changedBounds); if (attributes != null) { attributes = handleAttributes(attributes); } // Refresh all changed cells refresh(getMapping(changedCells.toArray(), false), false); // Updates the connected edges. Make sure that changedCells // contains no edges, as these will be removed from the result. refresh(context, false); tmp = cells; cells = previousCells; previousCells = tmp; reloadRoots(); fireGraphLayoutCacheChanged(GraphLayoutCache.this, this); } } /** * Called when a child has been made visible by expanding its parent. This * implementation translates the child so that it reflects the offset of the * parent group since the child was last visible (see * {@link #movesChildrenOnExpand}). */ protected void cellExpanded(Object cell) { GraphModel model = getModel(); // Moves the child to the group origin if it is not a port if (movesChildrenOnExpand && !model.isPort(cell)) { CellView view = getMapping(cell, false); if (view != null) { CellView parent = getMapping(model.getParent(cell), false); if (parent != null) { if (DefaultGraphModel.isVertex(model, parent)) { // Computes the offset of the parent group Rectangle2D src = GraphConstants.getBounds(parent .getAllAttributes()); Rectangle2D rect = parent.getBounds(); if (rect != null && src != null) { double dx = src.getX() - rect.getX(); double dy = src.getY() - rect.getY(); // Gets the attributes from the cell view or // cell and translates the bounds or points AttributeMap attrs = view.getAttributes(); if (!attrs.contains(GraphConstants.BOUNDS)) attrs = model.getAttributes(view.getCell()); attrs.translate(dx, dy); } } } } } } protected void cellWillCollapse(Object cell) { GraphModel model = getModel(); if (movesParentsOnCollapse) { CellView view = getMapping(cell, false); if (view != null && !view.isLeaf()) { // Uses view-local attribute if available AttributeMap attrs = view.getAttributes(); if (!attrs.contains(GraphConstants.BOUNDS) && !localAttributes.contains(GraphConstants.BOUNDS)) attrs = model.getAttributes(cell); // Moves the group to the origin of the children Rectangle2D src = GraphConstants.getBounds(attrs); Rectangle2D b = view.getBounds(); // FIXME: What if the group is exactly at "defaultBounds"? if (resizesParentsOnCollapse || src == null || src.equals(VertexView.defaultBounds)) { src = attrs.createRect(b.getX(), b.getY(), b.getWidth() * collapseXScale, b.getHeight() * collapseYScale); attrs.applyValue(GraphConstants.BOUNDS, src); } else { src.setFrame(b.getX(), b.getY(), src.getWidth(), src .getHeight()); } } } } /** * Attention: Undo will not work for routing-change if ROUTING and POINTS * are stored in different locations. This happens if the model holds the * routing attribute and the routing changes from unrouted to routed. In * this case the points in the view are already routed according to the new * scheme when written to the command history (-> no undo). */ protected Map handleAttributes(Map attributes) { Map undo = new Hashtable(); CellView[] views = new CellView[attributes.size()]; Iterator it = attributes.entrySet().iterator(); int i = 0; while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); CellView cv = getMapping(entry.getKey(), false); views[i] = cv; i += 1; if (cv != null && cv.getAttributes() != null) { Map deltaNew = (Map) entry.getValue(); // System.out.println("state=" + cv.getAttributes()); // System.out.println("change=" + deltaNew); Map deltaOld = cv.getAttributes().applyMap(deltaNew); cv.refresh(this, this, false); // System.out.println("state'=" + cv.getAttributes()); // System.out.println("change'=" + deltaOld); undo.put(cv.getCell(), deltaOld); } } // Re-route all child edges update(views); return undo; } // // Static Methods // /** * Translates the specified views by the given amount. * * @param views * an array of cell view to each be translated * @param dx * the amount to translate the views in the x-axis * @param dy * the amount to translate the views in the x-axis */ public static void translateViews(CellView[] views, double dx, double dy) { for (int i = 0; i < views.length; i++) { if (views[i] instanceof AbstractCellView) { ((AbstractCellView) views[i]).translate(dx, dy); } } } /** * Returns a collection of cells that are connected to the specified cell by * edges. Any cells specified in the exclude set will be ignored. * * @param cell * The cell from which the neighbours will be determined * @param exclude * The set of cells to ignore when searching * @param directed * whether or not direction of edges should be taken into account * @param visibleCells * whether or not to only consider visible cells * @return Returns the list of neighbours for cell */ public List getNeighbours(Object cell, Set exclude, boolean directed, boolean visibleCells) { // Traverse Graph GraphModel model = getModel(); Object[] fanout = (directed) ? DefaultGraphModel.getOutgoingEdges( model, cell) : DefaultGraphModel.getEdges(model, new Object[] { cell }).toArray(); List neighbours = new ArrayList(fanout.length); Set localExclude = new HashSet(fanout.length + 8, (float) 0.75); for (int i = 0; i < fanout.length; i++) { // if only visible cells are being processed, check that this // edge is visible before looking for neighbours with it if (!visibleCells || isVisible(fanout[i])) { Object neighbour = DefaultGraphModel.getOpposite(model, fanout[i], cell); if (neighbour != null && (exclude == null || !exclude.contains(neighbour)) && !localExclude.contains(neighbour) && (!visibleCells || isVisible(neighbour))) { localExclude.add(neighbour); neighbours.add(neighbour); } } } return neighbours; } /** * Returns the outgoing edges for cell. Cell should be a port or a vertex. * * @param cell * The cell from which the outgoing edges will be determined * @param exclude * The set of edges to ignore when searching * @param visibleCells * whether or not only visible cells should be processed * @param selfLoops * whether or not to include self loops in the returned list * @return Returns the list of outgoing edges for cell */ public List getOutgoingEdges(Object cell, Set exclude, boolean visibleCells, boolean selfLoops) { return getEdges(cell, exclude, visibleCells, selfLoops, false); } /** * Returns the incoming edges for cell. Cell should be a port or a vertex. * * @param cell * The cell from which the incoming edges will be determined * @param exclude * The set of edges to ignore when searching * @param visibleCells * whether or not only visible cells should be processed * @param selfLoops * whether or not to include self loops in the returned list * @return Returns the list of incoming edges for cell */ public List getIncomingEdges(Object cell, Set exclude, boolean visibleCells, boolean selfLoops) { return getEdges(cell, exclude, visibleCells, selfLoops, true); } /** * Returns the incoming or outgoing edges for cell. Cell should be a port or * a vertex. * * @param cell * The cell from which the edges will be determined * @param exclude * The set of edges to ignore when searching * @param visibleCells * whether or not only visible cells should be processed * @param selfLoops * whether or not to include self loops in the returned list * @param incoming * true if incoming edges are to be obtained, * false if outgoing edges are to be obtained * @return Returns the list of incoming or outgoing edges for * cell */ protected List getEdges(Object cell, Set exclude, boolean visibleCells, boolean selfLoops, boolean incoming) { GraphModel model = getModel(); Object[] edges = DefaultGraphModel.getEdges(model, cell, incoming); List edgeList = new ArrayList(edges.length); Set localExclude = new HashSet(edges.length); for (int i = 0; i < edges.length; i++) { // Check that the edge is neiter in the passed in exclude set or // the local exclude set. Also, if visibleCells is true check // the edge is visible in the cache. if ((exclude == null || !exclude.contains(edges[i])) && !localExclude.contains(edges[i]) && (!visibleCells || isVisible(edges[i]))) { // Add the edge to the list if all edges, including self loops // are allowed. If self loops are not allowed, ensure the // source and target of the edge are different if (selfLoops == true || model.getSource(edges[i]) != model .getTarget(edges[i])) { edgeList.add(edges[i]); } localExclude.add(edges[i]); } } return edgeList; } /** * Returns all views, shortcut to getAllDescendants(getRoots()) */ public CellView[] getAllViews() { return getAllDescendants(getRoots()); } /** * Returns all views, including descendants that have a parent in * views, especially the PortViews. Note: Iterative * Implementation using model.getChild and getMapping on this cell mapper. */ public CellView[] getAllDescendants(CellView[] views) { Stack stack = new Stack(); for (int i = 0; i < views.length; i++) if (views[i] != null) stack.add(views[i]); ArrayList result = new ArrayList(); while (!stack.isEmpty()) { CellView tmp = (CellView) stack.pop(); Object[] children = tmp.getChildViews(); for (int i = 0; i < children.length; i++) stack.add(children[i]); result.add(tmp); // Add Port Views for (int i = 0; i < graphModel.getChildCount(tmp.getCell()); i++) { Object child = graphModel.getChild(tmp.getCell(), i); if (graphModel.isPort(child)) { CellView view = getMapping(child, false); if (view != null) stack.add(view); } } } CellView[] ret = new CellView[result.size()]; result.toArray(ret); return ret; } /** * Returns the hiddenMapping. * * @return Map */ public Map getHiddenMapping() { return hiddenMapping; } /** * Sets the showsExistingConnections * * @param showsExistingConnections */ public void setShowsExistingConnections(boolean showsExistingConnections) { this.showsExistingConnections = showsExistingConnections; } /** * Returns the showsExistingConnections. * * @return boolean */ public boolean isShowsExistingConnections() { return showsExistingConnections; } /** * Sets the showsInsertedConnections * * @param showsInsertedConnections */ public void setShowsInsertedConnections(boolean showsInsertedConnections) { this.showsInsertedConnections = showsInsertedConnections; } /** * Returns the showsInsertedConnections. * * @return boolean */ public boolean isShowsInsertedConnections() { return showsInsertedConnections; } /** * Sets the hidesExistingConnections * * @param hidesExistingConnections */ public void setHidesExistingConnections(boolean hidesExistingConnections) { this.hidesExistingConnections = hidesExistingConnections; } /** * Returns the hidesExistingConnections. * * @return boolean */ public boolean isHidesExistingConnections() { return hidesExistingConnections; } /** * Sets the hidesDanglingConnections * * @param hidesDanglingConnections */ public void setHidesDanglingConnections(boolean hidesDanglingConnections) { this.hidesDanglingConnections = hidesDanglingConnections; } /** * Returns the hidesDanglingConnections. * * @return boolean */ public boolean isHidesDanglingConnections() { return hidesDanglingConnections; } /** * Sets the rememberCellViews. * * @param rememberCellViews * The rememberCellViews to set */ public void setRemembersCellViews(boolean rememberCellViews) { this.remembersCellViews = rememberCellViews; } /** * Returns the remembersCellViews. * * @return boolean */ public boolean isRemembersCellViews() { return remembersCellViews; } /** * Sets the hiddenSet. * * NOTE: Your GraphLayoutCache must be partial (set * partial to true in the constructor) * in order to use the visibility functionality of expand/collapse, * setVisible, etc. * * @param hiddenSet * The hiddenSet to set */ public void setHiddenSet(Map hiddenSet) { this.hiddenMapping = hiddenSet; } /** * @return Returns the localAttributes. */ public Set getLocalAttributes() { return localAttributes; } /** * @param localAttributes * The localAttributes to set. */ public void setLocalAttributes(Set localAttributes) { this.localAttributes = localAttributes; } /** * @return Returns the askLocalAttribute. */ public boolean isAllAttributesLocal() { return allAttributesLocal; } /** * @param allAttributesLocal * The allAttributesLocal to set. */ public void setAllAttributesLocal(boolean allAttributesLocal) { this.allAttributesLocal = allAttributesLocal; } /** * Returns true if cells should be auto-sized when their values change * * @return true if cells should be auto-sized when their values change */ public boolean isAutoSizeOnValueChange() { return autoSizeOnValueChange; } /** * Determines whether cells should be auto-sized when their values change. * Fires a property change event if the new setting is different from the * existing setting. * * @param flag * a boolean value, true if cells should be auto-sized when their * values change */ public void setAutoSizeOnValueChange(boolean flag) { this.autoSizeOnValueChange = flag; } /** * @return Returns the selectsAllInsertedCells. */ public boolean isSelectsAllInsertedCells() { return selectsAllInsertedCells; } /** * @param selectsAllInsertedCells * The selectsAllInsertedCells to set. */ public void setSelectsAllInsertedCells(boolean selectsAllInsertedCells) { this.selectsAllInsertedCells = selectsAllInsertedCells; } /** * @return Returns the selectsLocalInsertedCells. */ public boolean isSelectsLocalInsertedCells() { return selectsLocalInsertedCells; } /** * @param selectsLocalInsertedCells * The selectsLocalInsertedCells to set. */ public void setSelectsLocalInsertedCells(boolean selectsLocalInsertedCells) { this.selectsLocalInsertedCells = selectsLocalInsertedCells; } /** * @return Returns the reconnectsEdgesToVisibleParent. * @deprecated edges are moved to parent view and back automatically */ public boolean isReconnectsEdgesToVisibleParent() { return reconnectsEdgesToVisibleParent; } /** * @param reconnectsEdgesToVisibleParent * The reconnectsEdgesToVisibleParent to set. * @deprecated edges are moved to parent view and back automatically */ public void setReconnectsEdgesToVisibleParent( boolean reconnectsEdgesToVisibleParent) { this.reconnectsEdgesToVisibleParent = reconnectsEdgesToVisibleParent; } /** * @return Returns the showsChangedConnections. */ public boolean isShowsChangedConnections() { return showsChangedConnections; } /** * @param showsChangedConnections * The showsChangedConnections to set. */ public void setShowsChangedConnections(boolean showsChangedConnections) { this.showsChangedConnections = showsChangedConnections; } /** * @return Returns the moveChildrenOnExpand. */ public boolean isMovesChildrenOnExpand() { return movesChildrenOnExpand; } /** * @param moveChildrenOnExpand * The moveChildrenOnExpand to set. */ public void setMovesChildrenOnExpand(boolean moveChildrenOnExpand) { this.movesChildrenOnExpand = moveChildrenOnExpand; } public boolean isShowsInvisibleEditedCells() { return showsInvisibleEditedCells; } public void setShowsInvisibleEditedCells(boolean showsInvisibleEditedCells) { this.showsInvisibleEditedCells = showsInvisibleEditedCells; } /** * @return Returns the collapseXScale. */ public double getCollapseXScale() { return collapseXScale; } /** * @param collapseXScale * The collapseXScale to set. */ public void setCollapseXScale(double collapseXScale) { this.collapseXScale = collapseXScale; } /** * @return Returns the collapseYScale. */ public double getCollapseYScale() { return collapseYScale; } /** * @param collapseYScale * The collapseYScale to set. */ public void setCollapseYScale(double collapseYScale) { this.collapseYScale = collapseYScale; } /** * @return Returns the movesParentsOnCollapse. */ public boolean isMovesParentsOnCollapse() { return movesParentsOnCollapse; } /** * @param movesParentsOnCollapse * The movesParentsOnCollapse to set. */ public void setMovesParentsOnCollapse(boolean movesParentsOnCollapse) { this.movesParentsOnCollapse = movesParentsOnCollapse; } /** * @return Returns the resizesParentsOnCollapse. */ public boolean isResizesParentsOnCollapse() { return resizesParentsOnCollapse; } /** * @param resizesParentsOnCollapse * The resizesParentsOnCollapse to set. */ public void setResizesParentsOnCollapse(boolean resizesParentsOnCollapse) { this.resizesParentsOnCollapse = resizesParentsOnCollapse; } /** * Serialization support. */ private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); // Write out the hidden mapping Map map = new Hashtable(hiddenMapping); s.writeObject(map); } /** * Serialization support. */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); // Read the hidden mapping Map map = (Map) s.readObject(); hiddenMapping = new WeakHashMap(map); } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/GraphCellEditor.java0000644000175000017500000000226611256667036025623 0ustar gregoagregoa/* * @(#)GraphCellEditor.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.Component; import javax.swing.CellEditor; import org.jgraph.JGraph; /** * Adds to CellEditor the extensions necessary to configure an editor * in a graph. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public interface GraphCellEditor extends CellEditor { /** * Sets an initial value for the editor. This will cause * the editor to stopEditing and lose any partially edited value * if the editor is editing when this method is called.

* * Returns the component that should be added to the client's * Component hierarchy. Once installed in the client's hierarchy * this component will then be able to draw and receive user input. * * @param graph the JGraph that is asking the editor to edit * This parameter can be null. * @param value the value of the cell to be edited. * @param isSelected true if the cell is to be rendered with * selection highlighting * @return the component for editing */ Component getGraphCellEditorComponent( JGraph graph, Object value, boolean isSelected); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/Edge.java0000644000175000017500000000224411256667036023453 0ustar gregoagregoa/* * @(#)Edge.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.io.Serializable; import java.util.List; /** * Defines the requirements for an object that represents an Edge in a * GraphModel. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public interface Edge extends GraphCell { /** * Returns the source of the edge. */ Object getSource(); /** * Returns the target of the edge. */ Object getTarget(); /** * Sets the source of the edge. */ void setSource(Object port); /** * Returns the target of edge. */ void setTarget(Object port); // // Routing // public static interface Routing extends Serializable { public static final int NO_PREFERENCE = -1; /** * Returns the points to be used for the edge. * @param cache TODO * @param edge * The edge view to route the points for. */ public List route(GraphLayoutCache cache, EdgeView edge); /** * Returns the preferred line style for this routing. A return value of * {@link #NO_PREFERENCE} means no preference. */ public int getPreferredLineStyle(EdgeView edge); } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/DefaultGraphCell.java0000644000175000017500000001064111256667036025755 0ustar gregoagregoa/* * @(#)AbstractGraphCell.java 1.0 03-JUL-04 * * Copyright (c) 2001-2006 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.geom.Point2D; import java.util.Collections; import java.util.List; import java.util.Map; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.MutableTreeNode; /** * The default implementation for the GraphCell interface. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class DefaultGraphCell extends DefaultMutableTreeNode implements GraphCell, Cloneable { /** Hashtable for properties. Initially empty */ protected AttributeMap attributes = null; /** * Creates an empty cell. */ public DefaultGraphCell() { this(null); } /** * Creates a graph cell and initializes it with the specified user object. * * @param userObject an Object provided by the user that constitutes * the cell's data */ public DefaultGraphCell(Object userObject) { this(userObject, null); } /** * Constructs a cell that holds a reference to the specified user object * and contains the specified array of children and sets default values * for the bounds attribute. * * @param userObject reference to the user object * @param storageMap the storage attribute map for this cell */ public DefaultGraphCell(Object userObject, AttributeMap storageMap) { this(userObject, storageMap, null); } /** * Creates a graph cell and initializes it with the specified user object. * The GraphCell allows children only if specified. * * @param userObject an Object provided by the user that constitutes * the cell's data * @param storageMap the storage attribute map for this cell * @param children array of children */ public DefaultGraphCell(Object userObject, AttributeMap storageMap, MutableTreeNode[] children) { super(userObject, true); setAttributes(storageMap); if (children != null) for (int i = 0; i < children.length; i++) add(children[i]); } /** * Provides access to the children list to change ordering. * This method returns a Collections.EMPTY_LIST * if the list of childrenpoints to null. */ public List getChildren() { if (children == null) return Collections.EMPTY_LIST; return children; } /** * Returns the properies of the cell. */ public AttributeMap getAttributes() { return attributes; } /** * Changes the attributes of the cell. * * @deprecated Use getAttributes().applyMap */ public Map changeAttributes(Map change) { return getAttributes().applyMap(change); } /** * Sets the attributes. * @param attributes The attributes to set */ public void setAttributes(AttributeMap attributes) { if (attributes == null) attributes = new AttributeMap(); this.attributes = attributes; } /** * Utility method to create a port for this cell. This method adds * a floating port. * @return the port created */ public Object addPort() { return addPort(null); } /** * Utility method to create a port for this cell. The method adds a port * at a fixed relative offset within the cell. If the offset is null * then a floating port is added. * @param offset the offset of the port within the cell * @return the port created */ public Object addPort(Point2D offset) { return addPort(offset, null); } /** * Utility method to create a port for this cell. The method adds a port * at a fixed relative offset within the cell. If the offset is null * then a floating port is added. * @param offset the offset of the port within the cell * @param userObject the user object of the port cell * @return the port created */ public Object addPort(Point2D offset, Object userObject) { DefaultPort port = new DefaultPort(userObject); if (offset == null) { add(port); } else { GraphConstants.setOffset(port.getAttributes(), offset); add(port); } return port; } /** * Create a clone of the cell. This method uses the superclass * implementation (which does not clone the children), then * uses clone on the attribute map. This method does not * clone the user object. You should override the * cloneUserObject in the graph model to implement cloning * of custom user objects. * * @return Object a clone of this object. */ public Object clone() { DefaultGraphCell c = (DefaultGraphCell) super.clone(); c.attributes = (AttributeMap) attributes.clone(); return c; } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/VertexRenderer.java0000644000175000017500000002725311256667036025562 0ustar gregoagregoa/* * @(#)VertexRenderer.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.Stroke; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.UIManager; import org.jgraph.JGraph; /** * This renderer displays entries that implement the CellView interface and * supports the following attributes. If the cell view is not a leaf, this * object is only visible if it is selected. *

  • GraphConstants.BOUNDS GraphConstants.ICON GraphConstants.FONT * GraphConstants.OPAQUE GraphConstants.BORDER GraphConstants.BORDERCOLOR * GraphConstants.LINEWIDTH GraphConstants.FOREGROUND GraphConstants.BACKGROUND * GraphConstants.VERTICAL_ALIGNMENT GraphConstants.HORIZONTAL_ALIGNMENT * GraphConstants.VERTICAL_TEXT_POSITION GraphConstants.HORIZONTAL_TEXT_POSITION *
  • * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class VertexRenderer extends JLabel implements CellViewRenderer, Serializable { /** Cache the current shape for drawing. */ transient protected VertexView view; /** Cached hasFocus and selected value. */ transient protected boolean hasFocus, selected, preview, childrenSelected; /** Cached default foreground and default background. */ transient protected Color defaultForeground, defaultBackground, bordercolor; /** Cached borderwidth. */ transient protected int borderWidth; /** Cached value of the double buffered state */ transient protected boolean isDoubleBuffered = false; /** Cached value of whether the label is to be displayed */ transient protected boolean labelEnabled; /** * Caches values of the colors to be used for painting the cell. The * values for gridColor, highlightColor and lockedHandleColor are updated * with the respective values from JGraph in getRendererComponent each * time a vertex is rendered. To render the selection border, the * highlightColor or the lockedHandleColor are used depending on the * focused state of the vertex. The gridColor is used to draw the * selection border if any child cells are selected. To change these * color values, please use the respective setters in JGraph. */ transient protected Color gradientColor = null, gridColor = Color.black, highlightColor = Color.black, lockedHandleColor = Color.black; /** * Constructs a renderer that may be used to render vertices. */ public VertexRenderer() { defaultForeground = UIManager.getColor("Tree.textForeground"); defaultBackground = UIManager.getColor("Tree.textBackground"); } /** * Configure and return the renderer component based on the passed in cell. * The value is typically set from messaging the graph with * convertValueToString. We recommend you check the value's * class and throw an illegal argument exception if it's not correct. * * @param graph * the graph that that defines the rendering context. * @param view * the cell view that should be rendered. * @param sel * whether the object is selected. * @param focus * whether the object has the focus. * @param preview * whether we are drawing a preview. * @return the component used to render the value. */ public Component getRendererComponent(JGraph graph, CellView view, boolean sel, boolean focus, boolean preview) { gridColor = graph.getGridColor(); highlightColor = graph.getHighlightColor(); lockedHandleColor = graph.getLockedHandleColor(); isDoubleBuffered = graph.isDoubleBuffered(); if (view instanceof VertexView) { this.view = (VertexView) view; setComponentOrientation(graph.getComponentOrientation()); if (graph.getEditingCell() != view.getCell()) { Object label = graph.convertValueToString(view); if (label != null) setText(label.toString()); else setText(null); } else setText(null); this.hasFocus = focus; this.childrenSelected = graph.getSelectionModel() .isChildrenSelected(view.getCell()); this.selected = sel; this.preview = preview; if (this.view.isLeaf() || GraphConstants.isGroupOpaque(view.getAllAttributes())) installAttributes(view); else resetAttributes(); return this; } return null; } /** * Hook for subclassers that is invoked when the installAttributes is not * called to reset all attributes to the defaults.
    * Subclassers must invoke the superclass implementation. * */ protected void resetAttributes() { setText(null); setBorder(null); setOpaque(false); setGradientColor(null); setIcon(null); } /** * Install the attributes of specified cell in this renderer instance. This * means, retrieve every published key from the cells hashtable and set * global variables or superclass properties accordingly. * * @param view * the cell view to retrieve the attribute values from. */ protected void installAttributes(CellView view) { Map map = view.getAllAttributes(); setIcon(GraphConstants.getIcon(map)); setOpaque(GraphConstants.isOpaque(map)); setBorder(GraphConstants.getBorder(map)); setVerticalAlignment(GraphConstants.getVerticalAlignment(map)); setHorizontalAlignment(GraphConstants.getHorizontalAlignment(map)); setVerticalTextPosition(GraphConstants.getVerticalTextPosition(map)); setHorizontalTextPosition(GraphConstants.getHorizontalTextPosition(map)); bordercolor = GraphConstants.getBorderColor(map); borderWidth = Math.max(1, Math.round(GraphConstants.getLineWidth(map))); if (getBorder() == null && bordercolor != null) setBorder(BorderFactory.createLineBorder(bordercolor, borderWidth)); Color foreground = GraphConstants.getForeground(map); setForeground((foreground != null) ? foreground : defaultForeground); Color gradientColor = GraphConstants.getGradientColor(map); setGradientColor(gradientColor); Color background = GraphConstants.getBackground(map); setBackground((background != null) ? background : defaultBackground); setFont(GraphConstants.getFont(map)); labelEnabled = GraphConstants.isLabelEnabled(map); } /** * Paint the renderer. Overrides superclass paint to add specific painting. */ public void paint(Graphics g) { try { if (gradientColor != null && !preview && isOpaque()) { setOpaque(false); Graphics2D g2d = (Graphics2D) g; g2d.setPaint(new GradientPaint(0, 0, getBackground(), getWidth(), getHeight(), gradientColor, true)); g2d.fillRect(0, 0, getWidth(), getHeight()); } super.paint(g); paintSelectionBorder(g); } catch (IllegalArgumentException e) { // JDK Bug: Zero length string passed to TextLayout constructor } } /** * Provided for subclassers to paint a selection border. */ protected void paintSelectionBorder(Graphics g) { Graphics2D g2 = (Graphics2D) g; Stroke previousStroke = g2.getStroke(); g2.setStroke(GraphConstants.SELECTION_STROKE); if (childrenSelected || selected) { if (childrenSelected) g.setColor(gridColor); else if (hasFocus && selected) g.setColor(lockedHandleColor); else if (selected) g.setColor(highlightColor); Dimension d = getSize(); g.drawRect(0, 0, d.width - 1, d.height - 1); } g2.setStroke(previousStroke); } /** * Returns the intersection of the bounding rectangle and the straight line * between the source and the specified point p. The specified point is * expected not to intersect the bounds. */ public Point2D getPerimeterPoint(VertexView view, Point2D source, Point2D p) { Rectangle2D bounds = view.getBounds(); double x = bounds.getX(); double y = bounds.getY(); double width = bounds.getWidth(); double height = bounds.getHeight(); double xCenter = x + width / 2; double yCenter = y + height / 2; double dx = p.getX() - xCenter; // Compute Angle double dy = p.getY() - yCenter; double alpha = Math.atan2(dy, dx); double xout = 0, yout = 0; double pi = Math.PI; double pi2 = Math.PI / 2.0; double beta = pi2 - alpha; double t = Math.atan2(height, width); if (alpha < -pi + t || alpha > pi - t) { // Left edge xout = x; yout = yCenter - width * Math.tan(alpha) / 2; } else if (alpha < -t) { // Top Edge yout = y; xout = xCenter - height * Math.tan(beta) / 2; } else if (alpha < t) { // Right Edge xout = x + width; yout = yCenter + width * Math.tan(alpha) / 2; } else { // Bottom Edge yout = y + height; xout = xCenter + height * Math.tan(beta) / 2; } return new Point2D.Double(xout, yout); } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void validate() { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void revalidate() { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void repaint(long tm, int x, int y, int width, int height) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void repaint(Rectangle r) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { // Strings get interned... if (propertyName == "text") super.firePropertyChange(propertyName, oldValue, newValue); } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, byte oldValue, byte newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, char oldValue, char newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, short oldValue, short newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, int oldValue, int newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, long oldValue, long newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, float oldValue, float newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, double oldValue, double newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } /** * @return Returns the gradientColor. */ public Color getGradientColor() { return gradientColor; } /** * @param gradientColor * The gradientColor to set. */ public void setGradientColor(Color gradientColor) { this.gradientColor = gradientColor; } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/PortRenderer.java0000644000175000017500000001340111256667036025217 0ustar gregoagregoa/* * @(#)PortRenderer.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; import java.io.Serializable; import javax.swing.JComponent; import javax.swing.UIManager; import org.jgraph.JGraph; /** * This renderer displays entries that implement the CellView interface and * supports the following attributes: *
  • GraphConstants.OFFSET GraphConstants.ABSOLUTE
  • convertValueToString. * * @param graph * the graph that that defines the rendering context. * @param view * the cell view that should be rendered. * @param sel * whether the object is selected. * @param focus * whether the object has the focus. * @param preview * whether we are drawing a preview. * @return the component used to render the value. */ public Component getRendererComponent(JGraph graph, CellView view, boolean sel, boolean focus, boolean preview) { // Check type if (view instanceof PortView && graph != null) { graphBackground = graph.getBackground(); this.view = (PortView) view; this.hasFocus = focus; this.selected = sel; this.preview = preview; this.xorEnabled = graph.isXorEnabled(); return this; } return null; } /** * Paint the renderer. Overrides superclass paint to add specific painting. * Note: The preview flag is interpreted as "highlight" in this context. * (This is used to highlight the port if the mouse is over it.) */ public void paint(Graphics g) { Dimension d = getSize(); if (xorEnabled) { g.setColor(graphBackground); g.setXORMode(graphBackground); } super.paint(g); if (preview) { g.fill3DRect(0, 0, d.width, d.height, true); } else { g.fillRect(0, 0, d.width, d.height); } boolean offset = (GraphConstants.getOffset(view.getAllAttributes()) != null); g.setColor(getForeground()); if (!offset) g.fillRect(1, 1, d.width - 2, d.height - 2); else if (!preview) g.drawRect(1, 1, d.width - 3, d.height - 3); } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void validate() { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void revalidate() { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void repaint(long tm, int x, int y, int width, int height) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void repaint(Rectangle r) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { // Strings get interned... if (propertyName == "text") super.firePropertyChange(propertyName, oldValue, newValue); } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, byte oldValue, byte newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, char oldValue, char newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, short oldValue, short newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, int oldValue, int newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, long oldValue, long newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, float oldValue, float newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, double oldValue, double newValue) { } /** * Overridden for performance reasons. See the Implementation Note for more information. */ public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/PortView.java0000644000175000017500000001324411256667036024370 0ustar gregoagregoa/* * @(#)PortView.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; /** * The default implementation of a port view. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class PortView extends AbstractCellView { /** Default size for all ports is 6. */ public static transient int SIZE = 6; /** Renderer for the class. */ public static transient PortRenderer renderer = new PortRenderer(); /** * Controls if port magic should be allowed. Default is true. This is an * easy switch to disable port magic for all instances of graphs. */ public static boolean allowPortMagic = true; /** Cache of the last valid parent. //FIX: Better solution? */ protected transient CellView lastParent; /** * Constructs an empty portview. */ public PortView() { super(); } /** * Constructs a view that holds a reference to the specified cell, anchor * and parent vertex. * * @param cell * reference to the cell in the model */ public PortView(Object cell) { super(cell); } // // CellView interface // /** * This method ensures a non-null value. If the super method returns null * then the last valid parent is returned. Note: If a vertex is removed, all * ports will be replaced in connected edges. The ports are replaced by the * center point of the last valid vertex view. */ public CellView getParentView() { CellView parent = super.getParentView(); if (parent == null) parent = lastParent; else lastParent = parent; return parent; } /** * Returns the bounds for the port view. */ public Rectangle2D getBounds() { Point2D loc = getLocation(); double x = 0; double y = 0; if (loc != null) { x = loc.getX(); y = loc.getY(); } Rectangle2D bounds = new Rectangle2D.Double(x, y, 0, 0); bounds.setFrame(bounds.getX() - getPortSize() / 2, bounds.getY() - getPortSize() / 2, getPortSize(), getPortSize()); return bounds; } /** * Returns a renderer for the class. */ public CellViewRenderer getRenderer() { return renderer; } /** * Returns null. */ public CellHandle getHandle(GraphContext context) { return null; } // // Special Methods // /** * Shortcut method to getLocation(null, null) */ public Point2D getLocation() { return getLocation(null, null); } /** * For backwards compatibility. */ public Point2D getLocation(EdgeView edge) { return getLocation(edge, null); } /** * Returns the point that the port represents with respect to * edge and point, which is the nearest point * to this port view on the edge. edge and point * may be null. */ public Point2D getLocation(EdgeView edge, Point2D nearest) { CellView vertex = getParentView(); Point2D pos = null; if (vertex != null) { PortView anchor = null; // FIXME: (PortView) // mapper.getMapping(modelAnchor, false); // // Use refresh to get anchor view Point2D offset = GraphConstants.getOffset(allAttributes); // If No Edge Return Center if (edge == null && offset == null) pos = getCenterPoint(vertex); // Apply Offset if (offset != null) { double x = offset.getX(); double y = offset.getY(); Rectangle2D r = vertex.getBounds(); // Absolute Offset boolean isAbsoluteX = GraphConstants.isAbsoluteX(allAttributes); boolean isAbsoluteY = GraphConstants.isAbsoluteY(allAttributes); if (!isAbsoluteX) { x = x * (r.getWidth() - 1) / GraphConstants.PERMILLE; } if (!isAbsoluteY) { y = y * (r.getHeight() - 1) / GraphConstants.PERMILLE; } // Offset from Anchor pos = (anchor != null) ? anchor.getLocation(edge, nearest) : new Point2D.Double(r.getX(), r.getY()); pos = new Point2D.Double(pos.getX() + x, pos.getY() + y); } else if (edge != null) { // Floating Port if (nearest == null) { // If "Dangling" Port Return Center return getCenterPoint(vertex); } pos = vertex.getPerimeterPoint(edge, pos, nearest); if (shouldInvokePortMagic(edge)) { if (nearest != null) { Rectangle2D r = vertex.getBounds(); if (nearest.getX() >= r.getX() && nearest.getX() <= r.getX() + r.getWidth()) { pos.setLocation(nearest.getX(), pos.getY()); } else if (nearest.getY() >= r.getY() && nearest.getY() <= r.getY() + r.getHeight()) { // vertical pos.setLocation(pos.getX(), nearest.getY()); } if (nearest.getX() < r.getX()) pos.setLocation(r.getX(), pos.getY()); else if (nearest.getX() > r.getX() + r.getWidth()) pos .setLocation(r.getX() + r.getWidth(), pos .getY()); if (nearest.getY() < r.getY()) pos.setLocation(pos.getX(), r.getY()); else if (nearest.getY() > r.getY() + r.getHeight()) pos.setLocation(pos.getX(), r.getY() + r.getHeight()); } } } } return pos; } /** * Subclassers can override this to decide whether or not "port magic" * should appear on a given edge. (Port magic means the port tries to make * the edge horizontal or vertical if the closest control point lies within * the bounds of the parent vertex.) */ protected boolean shouldInvokePortMagic(EdgeView edge) { return allowPortMagic && !(getParentView() instanceof EdgeView) && edge.getPointCount() > 2 && GraphConstants.getLineStyle(edge.getAllAttributes()) == GraphConstants.STYLE_ORTHOGONAL; } /** * @return the port size */ public int getPortSize() { return PortView.SIZE; } /** * @param size the port size to set */ public void setPortSize(int size) { PortView.SIZE = size; } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/GraphTransferable.java0000644000175000017500000001064611256667036026206 0ustar gregoagregoa/* * @(#)GraphTransferable.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.ClipboardOwner; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.Map; import org.jgraph.plaf.basic.BasicGraphTransferable; /** * An object that represents the clipboard contents for a graph selection. * The object has three representations: *

    * 1. Richer: The cells, view attributes and connections for this selection are * stored as separate datastructures, which can be inserted using * the GraphModel.insert() method. * 2. HTML: If one cell is selected, the userObject is returned as HTML. * 3. Plain: The userObject of the selected cell is returned as plain text. * * @author Gaudenz Alder * @version 1.0 1/1/02 * */ public class GraphTransferable extends BasicGraphTransferable implements Serializable, ClipboardOwner { /** Local Machine Reference Data Flavor. */ public static DataFlavor dataFlavor; /** Selected cells. */ protected Object[] cells; /** Object that describes the connection between cells. */ protected ConnectionSet cs; /** Object that describes the group structure between cells. */ protected ParentMap pm; /** (Cell, Map) entries that hold the view attributes for the cells. */ protected Map attributeMap; /** Rectangle that defines the former bounds of the views. */ protected Rectangle2D bounds; /** * Constructs a new transferable selection for cells, * csand attrMap. */ public GraphTransferable( Object[] cells, Map attrMap, Rectangle2D bounds, ConnectionSet cs, ParentMap pm) { attributeMap = attrMap; this.bounds = bounds; this.cells = cells; this.cs = cs; this.pm = pm; } /** * Returns the cells that represent the selection. */ public Object[] getCells() { return cells; } /** * Returns the connections between cells (and possibly * other, unselected cells). */ public ConnectionSet getConnectionSet() { return cs; } public ParentMap getParentMap() { return pm; } /** * Returns a map of (GraphCell, Map)-pairs that represent the * view attributes for the respecive cells. */ public Map getAttributeMap() { return attributeMap; } public Rectangle2D getBounds() { return bounds; } // from ClipboardOwner public void lostOwnership(Clipboard clip, Transferable contents) { // do nothing } // --- Richer ---------------------------------------------------------- /** * Returns the jvm-localreference flavors of the transferable. */ public DataFlavor[] getRicherFlavors() { return new DataFlavor[] { dataFlavor }; } /** * Fetch the data in a jvm-localreference format. */ public Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException { if (flavor.equals(dataFlavor)) return this; else throw new UnsupportedFlavorException(flavor); } // --- Plain ---------------------------------------------------------- /** * Returns true if the transferable support a text/plain format. */ public boolean isPlainSupported() { return (cells != null && cells.length == 1); } /** * Fetch the data in a text/plain format. */ public String getPlainData() { if (cells[0] instanceof DefaultGraphCell) { Object obj = ((DefaultGraphCell) cells[0]).getUserObject(); if (obj != null) return obj.toString(); } return cells[0].toString(); } // --- HTML --------------------------------------------------------- /** * Returns true if the transferable support a text/html format. */ public boolean isHTMLSupported() { return isPlainSupported(); } /** * Fetch the data in a text/html format. */ public String getHTMLData() { StringBuffer buf = new StringBuffer(); buf.append("

    "); buf.append(getPlainData()); buf.append("

    "); return buf.toString(); } /* Local Machine Reference Data Flavor. */ static { DataFlavor localDataFlavor; try { localDataFlavor = new DataFlavor( DataFlavor.javaJVMLocalObjectMimeType + "; class=org.jgraph.graph.GraphTransferable"); } catch (ClassNotFoundException cnfe) { localDataFlavor = null; } dataFlavor = localDataFlavor; } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/GraphConstants.java0000644000175000017500000013726711256667036025563 0ustar gregoagregoa/* * @(#)GraphConstants.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Stroke; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.UIManager; import javax.swing.border.Border; /** * A collection of well known or common attribute keys and methods to apply to * an Map to get/set the properties in a typesafe manner. The following * attributes and methods need special attention: removeAttributes, removeAll * and value. RemoveAttributes and RemoveAll are not stored in a map, but remove * the specified entries. The value entry of a propertyMap is always in sync * with the userObject of a GraphCell. The isMoveable, isAutoSize and isSizeable * are used indepedently (see * http://sourceforge.net/forum/forum.php?thread_id=770111&forum_id=140880) * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class GraphConstants { /** * Default font is derived from * UIManager.getDefaults().getFont("Label.font") */ public static Font DEFAULTFONT = null; /** * GetFont in UIDefaults requires a valid framebuffer, so we implement a * fallback to a static default setting. */ static { try { DEFAULTFONT = UIManager.getDefaults().getFont("Label.font"); } catch (InternalError e) { // No default font } } /** Default decoration size. Value is 0. */ public static int DEFAULTDECORATIONSIZE = 10; /** Default inset size. Value is 0. */ public static int DEFAULTINSET = 0; /** 100 percent unit for relative positioning. Current value is 1000. */ public static final int PERMILLE = 1000; /** Global Stroke To Highlight Selection */ static protected float[] dash = { 5f, 5f }; static public Stroke SELECTION_STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f); /** * Represents no decoration for the beginning or ending of edges. * * @see #setLineBegin(Map, int) * @see #getLineBegin(Map) * @see #setLineEnd(Map, int) * @see #getLineEnd(Map) */ public static final int ARROW_NONE = 0; /** * Represents a classic arrow decoration for the beginning or ending of * edges. * * @see #setLineBegin(Map,int) * @see #getLineBegin(Map) * @see #setLineEnd(Map, int) * @see #getLineEnd(Map) */ public static final int ARROW_CLASSIC = 1; /** * Represents a technical arrow decoration for the beginning or ending of * edges. * * @see #setLineBegin(Map, int) * @see #getLineBegin(Map) * @see #setLineEnd(Map, int) * @see #getLineEnd(Map) */ public static final int ARROW_TECHNICAL = 2; /** * Represents a simple arrow decoration for the beginning or ending of * edges. * * @see #setLineBegin(Map,int) * @see #getLineBegin(Map) * @see #setLineEnd(Map, int) * @see #getLineEnd(Map) */ public static final int ARROW_SIMPLE = 4; /** * Represents a circle decoration for the beginning or ending of edges. * * @see #setLineBegin(Map, int) * @see #getLineBegin(Map) * @see #setLineEnd(Map, int) * @see #getLineEnd(Map) */ public static final int ARROW_CIRCLE = 5; /** * Represents a line decoration for the beginning or ending of edges. * * @see #setLineBegin(Map, int) * @see #getLineBegin(Map) * @see #setLineEnd(Map, int) * @see #getLineEnd(Map) */ public static final int ARROW_LINE = 7; /** * Represents a double line decoration for the beginning or ending of edges. * * @see #setLineBegin(Map, int) * @see #getLineBegin(Map) * @see #setLineEnd(Map, int) * @see #getLineEnd(Map) */ public static final int ARROW_DOUBLELINE = 8; /** * Represents a diamond decoration for the beginning or ending of edges. * * @see #setLineBegin(Map, int) * @see #getLineBegin(Map) * @see #setLineEnd(Map, int) * @see #getLineEnd(Map) */ public static final int ARROW_DIAMOND = 9; /** Represents an orthogonal line style */ public static final int STYLE_ORTHOGONAL = 11; /** Represents a bezier line style */ public static final int STYLE_BEZIER = 12; /** Represents an spline line style */ public static final int STYLE_SPLINE = 13; /** Represents an bezier line style */ public static final int X_AXIS = 1; /** Represents an bezier line style */ public static final int Y_AXIS = 2; /** * The default routing that deals with loops. This is returned by * {@link #getRouting(Map)} if there is no routing assigned. */ public static Edge.Routing ROUTING_DEFAULT = new DefaultEdge.LoopRouting(); /** * A simple routing. */ public static final Edge.Routing ROUTING_SIMPLE = new DefaultEdge.DefaultRouting(); /** * Key for the sizeableAxis attribute. This attribute * contains an Integer value representing which axis of a cell may be * resized/scaled Constants defined in this class. */ public final static String SIZEABLEAXIS = "sizeableAxis"; /** * Key for the moveableAxis attribute. This attribute * contains an Integer value indicating along which axis a cell may be * moved. Constants defined in this class. */ public final static String MOVEABLEAXIS = "moveableAxis"; /** * Key for the replaceAttributes attribute. This special * attribute contains a Boolean instance indicating whether a map of * attributes should replace the attributes of the receiving view. */ public final static String REPLACEATTRIBUTES = "replaceAttributes"; /** * Key for the removeAttributes attribute. This special * attribute contains a list of attribute-keys which should be removed at * the receiving views. */ public final static String REMOVEATTRIBUTES = "removeAttributes"; /** * Key for the removeAll attribute. This causes the receivers * attributes to be replaced by the the map that contains this attribute. */ public final static String REMOVEALL = "removeAll"; /** * Key for the icon attribute. Use instances of Icon as * values for this key. */ public final static String ICON = "icon"; /** * Key for the font attribute. Use instances of Font as * values for this key. */ public final static String FONT = "font"; /** * Key for the opaque attribute. Use instances of Boolean as * values for this key. */ public final static String OPAQUE = "opaque"; /** * Key for the groupOpaque attribute. Use instances of * Boolean as values for this key. */ public final static String GROUPOPAQUE = "groupOpaque"; /** * Key for the border attribute. Use instances of Border as * values for this key. Optionally, you can set the global instance of * LineBorder. */ public final static String BORDER = "border"; /** * Key for the linecolor attribute. Use instances of Color as * values for this key. */ public final static String LINECOLOR = "linecolor"; /** * Key for the bordercolor attribute. Use instances of Color * as values for this key. */ public final static String BORDERCOLOR = "bordercolor"; /** * Key for the linewidth attribute. Use instances of Float as * values for this key. */ public final static String LINEWIDTH = "linewidth"; /** * Key for the foreground attribute. Use instances of Color * as values for this key. */ public final static String FOREGROUND = "foregroundColor"; /** * Key for the background attribute. Use instances of Color * as values for this key. */ public final static String BACKGROUND = "backgroundColor"; /** * Key for the gradient attribute. Use instances of Color as * values for this key. */ public final static String GRADIENTCOLOR = "gradientColor"; /** * Key for the verticalAlignment attribute. Use instances of * Integer as values for this key. Constants defined in JLabel class. This * key value pair is for placment of label on vertex cell. Valid values * would be JLabel.TOP, JLabel.CENTER, JLABEL.BOTTOM */ public final static String VERTICAL_ALIGNMENT = "verticalAlignment"; /** * Key for the horizontalAlignment attribute. Use instances * of Integer as values for this key. Constants defined in JLabel class. * This Key/Value pair is for placment of label on vertex cell. Valid values * would be JLabel.LEFT, JLabel.CENTER, JLabel.RIGHT */ public final static String HORIZONTAL_ALIGNMENT = "horizontalAlignment"; /** * Key for the verticalTextPosition attribute. Use instances * of Integer as values for this key. Constants defined in JLabel class. * This Key/Value pair affects text postion of label on vertex cell relative * to image, the value is of type SwingConstant. */ public final static String VERTICAL_TEXT_POSITION = "verticalTextPosition"; /** * Key for the horizontalTextPosition attribute. Use * instances of Integer as values for this key. Constants defined in JLabel * class. This Key/Value pair affects text postion of label on vertex cell * relative to image, the value is of type SwingConstant. */ public final static String HORIZONTAL_TEXT_POSITION = "horizontalTextPosition"; /** * Key for the dashPattern attribute. Use instances of * float[] as values for this key. */ public final static String DASHPATTERN = "dashPattern"; /** * Key for the dashOffset attribute. Use instances of float * as values for this key. */ public final static String DASHOFFSET = "dashOffset"; /** * Key for the lineStyle attribute. Use instances of Integer * as values for this key. Constants defined in this class. */ public final static String LINESTYLE = "lineStyle"; /** * Key for the lineBegin attribute. Use instances of Integer * as values for this key. Constants defined in this class. */ public final static String LINEBEGIN = "lineBegin"; /** * Key for the lineEnd attribute. Use instances of Integer as * values for this key. Constants defined in this class. */ public final static String LINEEND = "lineEnd"; /** * Key for the beginSize attribute. Use instances of Integer * as values for this key. */ public final static String BEGINSIZE = "beginSize"; /** * Key for the endSize attribute. Use instances of Integer as * values for this key. */ public final static String ENDSIZE = "endSize"; /** * Key for the beginFill attribute. Use instances of Integer * as values for this key. */ public final static String BEGINFILL = "beginFill"; /** * Key for the endFill attribute. Use instances of Integer as * values for this key. */ public final static String ENDFILL = "endFill"; /** * Key for the value attribute. You can use any Object as a * value for this key. */ public static final String VALUE = "value"; /** * Key for the editable attribute. Use instances of Boolean * as values for this key. */ public static final String EDITABLE = "editable"; /** * Key for the moveable attribute. Use instances of Boolean * as values for this key. */ public static final String MOVEABLE = "moveable"; /** * Key for the sizeable attribute. Use instances of Boolean * as values for this key. */ public static final String SIZEABLE = "sizeable"; /** * Key for the autosize attribute. Use instances of Boolean * as values for this key. */ public static final String AUTOSIZE = "autosize"; /** * Key for the resize attribute. Use instances of Boolean as * values for this key. */ public static final String RESIZE = "resize"; /** * Key for the inset attribute. Use instances of Integer as * values for this key. */ public static final String INSET = "inset"; /** * Key for the constrained attribute. Use instances of * Boolean as values for this key. */ public static final String CONSTRAINED = "constrained"; /** * Key for the selectable attribute. Use instances of Boolean * as values for this key. */ public static final String SELECTABLE = "selectable"; /** * Key for the childrenSelectable attribute. Use instances of * Boolean as values for this key. */ public static final String CHILDRENSELECTABLE = "childrenSelectable"; /** * Key for the childrenSelectable attribute. Use instances of * Boolean as values for this key. */ public static final String MOVEHIDDENCHILDREN = "childrenSelectable"; /** * Key for the bendable attribute. Use instances of Boolean * as values for this key. */ public static final String BENDABLE = "bendable"; /** * Key for the connectable attribute. Use instances of * Boolean as values for this key. */ public static final String CONNECTABLE = "connectable"; /** * Key for the disconnectable attribute. Use instances of * Boolean as values for this key. */ public static final String DISCONNECTABLE = "disconnectable"; /** * Key for the bounds attribute. Use instances of Rectangle * as values for this key. */ public static final String BOUNDS = "bounds"; /** * Key for the points attribute. Use instances of List as * values for this key. The list should contain Point instances. */ public static final String POINTS = "points"; /** * Key for the routing attribute. Use instances of * EdgeView.EdgeRouter as values for this key. */ public static final String ROUTING = "routing"; /** * Key for the labelposition attribute. Use instances of * Point as values for this key. This Key/Value pair is for position of text * relative to edge cell, and has no effect on label in vertex cell. */ public static final String LABELPOSITION = "labelposition"; /** * Key for the extraLabels attribute. Use arrays of Objects * as values for this key, where the toString() methods of the objects * provide the label text value. */ public static final String EXTRALABELS = "extraLabels"; /** * Key for the extraLabelPositions attribute. Use arrays of * Points as values for this key. */ public static final String EXTRALABELPOSITIONS = "extraLabelPositions"; /** * Key for the labelAlongEdge attribute. Use instances of * Boolean as values for this key. */ public static final String LABELALONGEDGE = "labelAlongEdge"; /** * Key for the absoluteX attribute. Use instances of Boolean * as values for this key. */ public static final String ABSOLUTEX = "absoluteX"; /** * Key for the absoluteY attribute. Use instances of Boolean * as values for this key. */ public static final String ABSOLUTEY = "absoluteY"; /** * Key for the offset attribute. Use instances of Point as * values for this key. */ public static final String OFFSET = "offset"; /** * Key for the size attribute. Use instances of Dimension as * values for this key. */ public static final String SIZE = "size"; /** * Key for the link attribute. Use instances of String as * values for this key. */ public final static String LINK = "link"; /** * Key for the labelEnabled attribute. Use instances of Boolean as * values for this key. */ public final static String LABELENABLED = "labelEnabled"; /** * Key for the labelEnabled attribute. Use instances of Boolean as * values for this key. */ public final static String EXACTSEGMENTRELATIVE = "labelEnabled"; /** * Overloaded method that passes arguments on as arrays */ public static Map createAttributes(Object cell, Object key, Object value) { return createAttributes(new Object[] { cell }, new Object[] { key }, new Object[] { value }); } /** * Overloaded method that passes arguments on as arrays */ public static Map createAttributes(Object[] cells, Object key, Object value) { return createAttributes(cells, new Object[] { key }, new Object[] { value }); } /** * Returns a new (nested) map, from cells to attribute maps. The attributes * are populated with the (key, value)-pairs specified by the two given * arrays. The keys and values parameters must * match in size. */ public static Map createAttributes(Object[] cells, Object[] keys, Object[] values) { if (keys != null && values != null && keys.length != values.length) throw new IllegalArgumentException( "Keys and values must have same length"); Map nested = new Hashtable(); for (int i = 0; i < cells.length; i++) { if (cells[i] != null) { Map attributes = new Hashtable(); for (int j = 0; j < keys.length; j++) if (keys[j] != null && values[j] != null) attributes.put(keys[j], values[j]); nested.put(cells[i], attributes); } } return nested; } /** * Returns a new map, from cells to property maps. The elements * may be instances of CellView, in which case the cell * view's corresponding cell is used as a key, and its attributes are used * as a property map. In any other case, the element is * considered as a cell and looked-up in the cell mapper to find the * corresponding view. If a view is found, its attributes are cloned and * used as a property map, along with the cell as a key. *

    * Note: This method returns a map of maps! This is * different from the createMap method, which creates a map, from keys to * values. This method returns a map, from cells to maps, which in turn map * from keys to values. */ public static Map createAttributes(Object[] elements, CellMapper cm) { Map attributes = new Hashtable(); for (int i = 0; i < elements.length; i++) { CellView view = null; Object key = elements[i]; if (key instanceof CellView) { view = (CellView) key; key = view.getCell(); } else if (cm != null) // else is assumed by clients! view = cm.getMapping(key, false); if (view != null) attributes.put(key, view.getAllAttributes().clone()); // FIXME: // clone // required? } return attributes; } // Returns a nested map of cell, Map pairs where the map reflects // the attributes returned by the model for this cell. public static Map createAttributesFromModel(Object[] elements, GraphModel model) { Map attributes = new Hashtable(); for (int i = 0; i < elements.length; i++) { AttributeMap attr = model.getAttributes(elements[i]); if (attr != null && attr.size() > 0) attributes.put(elements[i], attr.clone()); } return attributes; } /** * Replace the keys in map using keyMapmap must itself * be a map, and is cloned using cloneMap. */ public static Map replaceKeys(Map keyMap, Map map) { Map newMap = new Hashtable(); Iterator it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); if (entry.getValue() instanceof Map) { Object newKey = keyMap.get(entry.getKey()); if (newKey != null) { AttributeMap val = (AttributeMap) ((AttributeMap) entry .getValue()).clone(); newMap.put(newKey, val); } } } return newMap; } /** * Merges the specified nested maps by adding all attributes in the change * to the attributes for the cell in target, if the cell does not exist in * target, then a new entry is added with all attributes from change. * Returns the modified target map. * * Note: This method removes entries from change and adds * entries to target in-place! * * @return a map including all attributes from change merged with target */ public static Map merge(Map change, Map target) { if (change != null && target != null) { change = new Hashtable(change); Iterator it = target.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); Map map = (Map) entry.getValue(); Map additional = (Map) change.remove(entry.getKey()); if (additional != null) map.putAll(additional); } // Adds remaining attributes target.putAll(change); } return target; } /** * Sets the value attribute in the specified map to the specified font * value. * * @param map * The map to store the font attribute in. * @param font * The value to set the font attribute to. */ public static void setFont(Map map, Font font) { map.put(FONT, font); } /** * Returns the font for the specified attribute map. Uses default font if no * font is specified in the attribute map. */ public static Font getFont(Map map) { Font font = (Font) map.get(FONT); if (font == null) font = DEFAULTFONT; return font; } /** * Sets specific attributes are being marked for removal @ param map The * transport map indicating the attribute removal @ param value The * attributes to be removed */ public static final void setRemoveAttributes(Map map, Object[] value) { map.put(REMOVEATTRIBUTES, value); } /** * Returns the attribute marked as for removal in the specified map. */ public static final Object[] getRemoveAttributes(Map map) { return (Object[]) map.get(REMOVEATTRIBUTES); } /** * Sets the moveableaxis attribute in the specified map to the specified * value. */ public static final void setMoveableAxis(Map map, int axis) { map.put(MOVEABLEAXIS, new Integer(axis)); } /** * Returns the moveableaxis attribute from the specified map. */ public static final int getMoveableAxis(Map map) { Integer intObj = (Integer) map.get(MOVEABLEAXIS); if (intObj != null) return intObj.intValue(); return 0; } /** * Sets the sizeableAxis attribute in the specified map to the specified * value. */ public static final void setSizeableAxis(Map map, int axis) { map.put(SIZEABLEAXIS, new Integer(axis)); } /** * Returns the sizeableAxis attribute from the specified map. */ public static final int getSizeableAxis(Map map) { Integer intObj = (Integer) map.get(SIZEABLEAXIS); if (intObj != null) return intObj.intValue(); return 0; } /** * Sets the icon attribute in the specified map to the specified value. */ public static final void setIcon(Map map, Icon value) { map.put(ICON, value); } /** * Returns the icon attribute from the specified map. */ public static final Icon getIcon(Map map) { return (Icon) map.get(ICON); } /** * Sets the opaque attribute in the specified map to the specified value. */ public static final void setOpaque(Map map, boolean flag) { map.put(OPAQUE, new Boolean(flag)); } /** * Returns the opaque attribute from the specified map. */ public static final boolean isOpaque(Map map) { Boolean bool = (Boolean) map.get(OPAQUE); if (bool != null) return bool.booleanValue(); return false; } /** * Sets the opaque attribute in the specified map to the specified value. */ public static final void setGroupOpaque(Map map, boolean flag) { map.put(GROUPOPAQUE, new Boolean(flag)); } /** * Returns the opaque attribute from the specified map. */ public static final boolean isGroupOpaque(Map map) { Boolean bool = (Boolean) map.get(GROUPOPAQUE); if (bool != null) return bool.booleanValue(); return false; } /** * Sets the border attribute in the specified map to the specified value. */ public static final void setBorder(Map map, Border value) { map.put(BORDER, value); } /** * Returns the border attribute from the specified map. */ public static final Border getBorder(Map map) { return (Border) map.get(BORDER); } /** * Sets the linecolor attribute in the specified map to the specified value. */ public static final void setLineColor(Map map, Color value) { map.put(LINECOLOR, value); } /** * Returns the linecolor attribute from the specified map. */ public static final Color getLineColor(Map map) { return (Color) map.get(LINECOLOR); } /** * Sets the bordercolor attribute in the specified map to the specified * value. */ public static final void setBorderColor(Map map, Color value) { map.put(BORDERCOLOR, value); } /** * Returns the bordercolor attribute from the specified map. */ public static final Color getBorderColor(Map map) { return (Color) map.get(BORDERCOLOR); } /** * Sets the linewidth attribute in the specified map to the specified value. */ public static final void setLineWidth(Map map, float width) { map.put(LINEWIDTH, new Float(width)); } /** * Returns the linewidth attribute from the specified map. */ public static final float getLineWidth(Map map) { Float floatObj = (Float) map.get(LINEWIDTH); if (floatObj != null) return floatObj.floatValue(); return 1; } /** * Sets the foregrund attribute in the specified map to the specified value. */ public static final void setForeground(Map map, Color value) { map.put(FOREGROUND, value); } /** * Returns the foreground attribute from the specified map. */ public static final Color getForeground(Map map) { return (Color) map.get(FOREGROUND); } /** * Sets the background attribute in the specified map to the specified * value. */ public static final void setBackground(Map map, Color value) { map.put(BACKGROUND, value); } /** * Returns the background attribute from the specified map. */ public static final Color getBackground(Map map) { return (Color) map.get(BACKGROUND); } /** * Sets the gradientcolor attribute in the specified map to the specified * value. */ public static final void setGradientColor(Map map, Color value) { map.put(GRADIENTCOLOR, value); } /** * Returns the gradientcolor attribute from the specified map. */ public static final Color getGradientColor(Map map) { return (Color) map.get(GRADIENTCOLOR); } /** * This method set the position of the label in vertex cells, and takes * aruguments of type SwingConstants (for JLabel) i.e. JLabel.TOP etc. * Default value is JLabel.CENTER. This method has no effect on edge cells. */ public static final void setVerticalAlignment(Map map, int alignment) { map.put(VERTICAL_ALIGNMENT, new Integer(alignment)); } /** * Returns the verticalalignment attribute from the specified map. */ public static final int getVerticalAlignment(Map map) { Integer intObj = (Integer) map.get(VERTICAL_ALIGNMENT); if (intObj != null) return intObj.intValue(); return JLabel.CENTER; } /** * This method set the position of the label in vertex cells, and takes * aruguments of type SwingConstants (for JLabel) i.e. JLabel.LEFT etc. * Default is JLabel.CENTER. This method has no effect on edge cells. */ public static final void setHorizontalAlignment(Map map, int alignment) { map.put(HORIZONTAL_ALIGNMENT, new Integer(alignment)); } /** * Returns the horizontalalignment attribute from the specified map. */ public static final int getHorizontalAlignment(Map map) { Integer intObj = (Integer) map.get(HORIZONTAL_ALIGNMENT); if (intObj != null) return intObj.intValue(); return JLabel.CENTER; } /** * This method set the text of a label on vertext cell relative to image. * See JLabel api. Default value is JLabel.BOTTOM. */ public static final void setVerticalTextPosition(Map map, int textPosition) { map.put(VERTICAL_TEXT_POSITION, new Integer(textPosition)); } /** * Returns the verticaltextposition attribute from the specified map. */ public static final int getVerticalTextPosition(Map map) { Integer intObj = (Integer) map.get(VERTICAL_TEXT_POSITION); if (intObj != null) return intObj.intValue(); return JLabel.BOTTOM; } /** * This method set the text of a label on vertext cell relative to image. * See JLabel api. Default value is JLabel.CENTER. */ public static final void setHorizontalTextPosition(Map map, int textPosition) { map.put(HORIZONTAL_TEXT_POSITION, new Integer(textPosition)); } /** * Returns the horizontaltextposition attribute from the specified map. */ public static final int getHorizontalTextPosition(Map map) { Integer intObj = (Integer) map.get(HORIZONTAL_TEXT_POSITION); if (intObj != null) return intObj.intValue(); return JLabel.CENTER; } /** * Sets the dashpattern attribute in the specified map to the specified * value. */ public static final void setDashPattern(Map map, float[] value) { map.put(DASHPATTERN, value); } /** * Returns the dashpattern attribute from the specified map. */ public static final float[] getDashPattern(Map map) { return (float[]) map.get(DASHPATTERN); } /** * Sets the dashoffset attribute in the specified map to the specified * value. */ public static final void setDashOffset(Map map, float value) { map.put(DASHOFFSET, new Float(value)); } /** * Returns the dashoffset attribute from the specified map. */ public static final float getDashOffset(Map map) { Float floatObj = (Float) map.get(DASHOFFSET); if (floatObj != null) return floatObj.floatValue(); return 1; } /** * Sets the linestyle attribute in the specified map to the specified value. */ public static final void setLineStyle(Map map, int style) { map.put(LINESTYLE, new Integer(style)); } /** * Returns the linestyle attribute from the specified map. */ public static final int getLineStyle(Map map) { Integer intObj = (Integer) map.get(LINESTYLE); if (intObj != null) return intObj.intValue(); return STYLE_ORTHOGONAL; } /** * Sets the beginsize attribute in the specified map to the specified value. */ public static final void setBeginSize(Map map, int style) { map.put(BEGINSIZE, new Integer(style)); } /** * Returns the beginsize attribute from the specified map. */ public static final int getBeginSize(Map map) { Integer intObj = (Integer) map.get(BEGINSIZE); if (intObj != null) return intObj.intValue(); return DEFAULTDECORATIONSIZE; } /** * Sets the endsize attribute in the specified map to the specified value. */ public static final void setEndSize(Map map, int style) { map.put(ENDSIZE, new Integer(style)); } /** * Returns the endsize attribute from the specified map. */ public static final int getEndSize(Map map) { Integer intObj = (Integer) map.get(ENDSIZE); if (intObj != null) return intObj.intValue(); return DEFAULTDECORATIONSIZE; } /** * Sets the LINEBEGIN attribute in the specified map to the specified value. * This attribute indicates what sort of decoration should be applied to the * beginning of edges when they are rendered. * * @see #ARROW_NONE * @see #ARROW_CLASSIC * @see #ARROW_TECHNICAL * @see #ARROW_SIMPLE * @see #ARROW_CIRCLE * @see #ARROW_LINE * @see #ARROW_DOUBLELINE * @see #ARROW_DIAMOND */ public static final void setLineBegin(Map map, int style) { map.put(LINEBEGIN, new Integer(style)); } /** * Returns the LINEBEGIN attribute from the specified map. This attribute * indicates what sort of decoration should be applied to the beginning of * edges when they are rendered. * * @see #ARROW_NONE * @see #ARROW_CLASSIC * @see #ARROW_TECHNICAL * @see #ARROW_SIMPLE * @see #ARROW_CIRCLE * @see #ARROW_LINE * @see #ARROW_DOUBLELINE * @see #ARROW_DIAMOND */ public static final int getLineBegin(Map map) { Integer intObj = (Integer) map.get(LINEBEGIN); if (intObj != null) return intObj.intValue(); return ARROW_NONE; } /** * Sets the LINEEND attribute in the specified map to the specified value. * This attribute indicates what sort of decoration should be applied to the * ends of edges when they are rendered. * * @see #ARROW_NONE * @see #ARROW_CLASSIC * @see #ARROW_TECHNICAL * @see #ARROW_SIMPLE * @see #ARROW_CIRCLE * @see #ARROW_LINE * @see #ARROW_DOUBLELINE * @see #ARROW_DIAMOND */ public static final void setLineEnd(Map map, int style) { map.put(LINEEND, new Integer(style)); } /** * Returns the LINEEND attribute from the specified map. This attribute * indicates what sort of decoration should be applied to the ends of edges * when they are rendered. * * @see #ARROW_NONE * @see #ARROW_CLASSIC * @see #ARROW_TECHNICAL * @see #ARROW_SIMPLE * @see #ARROW_CIRCLE * @see #ARROW_LINE * @see #ARROW_DOUBLELINE * @see #ARROW_DIAMOND */ public static final int getLineEnd(Map map) { Integer intObj = (Integer) map.get(LINEEND); if (intObj != null) return intObj.intValue(); return ARROW_NONE; } /** * Sets the value attribute in the specified map to the specified value. */ public static final void setValue(Map map, Object value) { map.put(VALUE, value); } /** * Returns the value attribute from the specified map. Only * use for transport maps, not storage map since the value * is removed from the final storage map * * @see GraphModel#getValue(Object) */ public static final Object getValue(Map map) { return map.get(VALUE); } /** * Sets the label position attribute in the specified map to the specified * value. */ public static final void setLabelPosition(Map map, Point2D position) { map.put(LABELPOSITION, position); } /** * Returns the label position attribute from the specified map. The point is * interpreted by the EdgeRenderer as follows:
    * x coordinate: the percentual position on the length of the edge in * direction of the edge
    * y coordinate: the absolute offset, orthogonally to the edge */ public static final Point2D getLabelPosition(Map map) { return (Point2D) map.get(LABELPOSITION); } /** * Sets the array of additional labels. The objects's toString methods are * used to determine the actual label string. Extra labels only work with * edges currently. */ public static final void setExtraLabels(Map map, Object[] definitions) { map.put(EXTRALABELS, definitions); } /** * Returns the additional label objects from the specified map.Extra labels * only work with edges currently. */ public static final Object[] getExtraLabels(Map map) { return (Object[]) map.get(EXTRALABELS); } /** * Sets the labelpositions attribute in the specified map to the specified * value. The Point instance at a particular index corresponds to the object * at the same index in the extralabels array attribute. Extra labels only * work with edges currently. * * @see #getLabelPosition(Map) */ public static final void setExtraLabelPositions(Map map, Point2D[] positions) { map.put(EXTRALABELPOSITIONS, positions); } /** * Returns the extralabelpositions attribute from the specified map. Extra * labels only work with edges currently. */ public static final Point2D[] getExtraLabelPositions(Map map) { return (Point2D[]) map.get(EXTRALABELPOSITIONS); } /** * Sets if the label should be painted along the edge. */ public static final void setLabelAlongEdge(Map map, boolean flag) { map.put(LABELALONGEDGE, new Boolean(flag)); } /** * Returns the true if the label should be painted along the edge. Defaults * to false. */ public static final boolean isLabelAlongEdge(Map map) { Boolean bool = (Boolean) map.get(LABELALONGEDGE); if (bool != null) return bool.booleanValue(); return false; } /** * Sets the editable attribute in the specified map to the specified value. */ public static final void setEditable(Map map, boolean flag) { map.put(EDITABLE, new Boolean(flag)); } /** * Returns the editable attribute from the specified map. */ public static final boolean isEditable(Map map) { Boolean bool = (Boolean) map.get(EDITABLE); if (bool != null) return bool.booleanValue(); return true; } /** * Sets the moveable attribute in the specified map to the specified value. */ public static final void setMoveable(Map map, boolean flag) { map.put(MOVEABLE, new Boolean(flag)); } /** * Returns the moveable attribute from the specified map. */ public static final boolean isMoveable(Map map) { Boolean bool = (Boolean) map.get(MOVEABLE); if (bool != null) return bool.booleanValue(); return true; } /** * Sets the sizeable attribute in the specified map to the specified value. */ public static final void setSizeable(Map map, boolean flag) { map.put(SIZEABLE, new Boolean(flag)); } /** * Returns the sizeable attribute from the specified map. */ public static final boolean isSizeable(Map map) { Boolean bool = (Boolean) map.get(SIZEABLE); if (bool != null) return bool.booleanValue(); return true; } /** * Sets the autosize attribute in the specified map to the specified value. */ public static final void setAutoSize(Map map, boolean flag) { map.put(AUTOSIZE, new Boolean(flag)); } /** * Returns the autosize attribute from the specified map. */ public static final boolean isAutoSize(Map map) { Boolean bool = (Boolean) map.get(AUTOSIZE); if (bool != null) return bool.booleanValue(); return false; } /** * Sets the resize attribute in the specified map to the specified value. */ public static final void setResize(Map map, boolean flag) { map.put(RESIZE, new Boolean(flag)); } /** * Returns the resize attribute from the specified map. */ public static final boolean isResize(Map map) { Boolean bool = (Boolean) map.get(RESIZE); if (bool != null) return bool.booleanValue(); return false; } /** * Sets the constrained attribute in the specified map to the specified * value. */ public static final void setConstrained(Map map, boolean flag) { map.put(CONSTRAINED, new Boolean(flag)); } /** * Returns the constrained attribute from the specified map. */ public static final boolean isConstrained(Map map) { Boolean bool = (Boolean) map.get(CONSTRAINED); if (bool != null) return bool.booleanValue(); return false; } /** * Sets the selectable attribute in the specified map to the specified * value. This detemines whether or not a cell may be selected. Vertices and * edges may be selectable, not ports. */ public static final void setSelectable(Map map, boolean flag) { map.put(SELECTABLE, new Boolean(flag)); } /** * Returns the selectable attribute from the specified map. * * @see #setSelectable(Map, boolean) */ public static final boolean isSelectable(Map map) { Boolean bool = (Boolean) map.get(SELECTABLE); if (bool != null) return bool.booleanValue(); return true; } /** * Sets the childrenselectable attribute in the specified map to the * specified value. */ public static final void setChildrenSelectable(Map map, boolean flag) { map.put(CHILDRENSELECTABLE, new Boolean(flag)); } /** * Returns the childrenselectable attribute from the specified map. */ public static final boolean isChildrenSelectable(Map map) { Boolean bool = (Boolean) map.get(CHILDRENSELECTABLE); if (bool != null) return bool.booleanValue(); return true; } /** * Sets the bendable attribute in the specified map to the specified value. */ public static final void setBendable(Map map, boolean flag) { map.put(BENDABLE, new Boolean(flag)); } /** * Returns the bendable attribute from the specified map. */ public static final boolean isBendable(Map map) { Boolean bool = (Boolean) map.get(BENDABLE); if (bool != null) return bool.booleanValue(); return true; } /** * Sets the connectable attribute in the specified map to the specified * value. */ public static final void setConnectable(Map map, boolean flag) { map.put(CONNECTABLE, new Boolean(flag)); } /** * Returns the connectable attribute from the specified map. */ public static final boolean isConnectable(Map map) { Boolean bool = (Boolean) map.get(CONNECTABLE); if (bool != null) return bool.booleanValue(); return true; } /** * Sets the disconnectable attribute in the specified map to the specified * value. */ public static final void setDisconnectable(Map map, boolean flag) { map.put(DISCONNECTABLE, new Boolean(flag)); } /** * Returns the disconnectable attribute from the specified map. */ public static final boolean isDisconnectable(Map map) { Boolean bool = (Boolean) map.get(DISCONNECTABLE); if (bool != null) return bool.booleanValue(); return true; } /** * Sets the points attribute in the specified map to the specified value. * Points are literally the points where an edge is connected by two lines. * The two defaults points are the start and end of the edge. For example, * for an simple orthogonal edge there will be two extra points indicating * where the two right-angled bends are. This attribute can only be applied * to an edge. */ public static final void setPoints(Map map, java.util.List list) { map.put(POINTS, list); } /** * Returns the points attribute from the specified map. * * @see #setPoints(Map, java.util.List) */ public static final java.util.List getPoints(Map map) { return (java.util.List) map.get(POINTS); } /** * Sets the routing attribute in the specified map to the specified value. */ public static final void setRouting(Map map, Edge.Routing routing) { map.put(ROUTING, routing); } /** * Returns the routing attribute from the specified map. */ public static final Edge.Routing getRouting(Map map) { Edge.Routing routing = (Edge.Routing) map.get(ROUTING); if (routing == null) routing = ROUTING_DEFAULT; return routing; } /** * Sets the bounds attribute in the specified map to the specified value. */ public static final void setBounds(Map map, Rectangle2D bounds) { map.put(BOUNDS, bounds); } /** * Returns the bounds attribute from the specified map. Note: The CellView * interface offers a getBounds method! */ public static final Rectangle2D getBounds(Map map) { return (Rectangle2D) map.get(BOUNDS); } /** * Sets the inset attribute in the specified map to the specified value. */ public static final void setInset(Map map, int width) { map.put(INSET, new Integer(width)); } /** * Returns the inset attribute from the specified map. Note: The CellView * interface offers a getBounds method! */ public static final int getInset(Map map) { Integer intObj = (Integer) map.get(INSET); if (intObj != null) return intObj.intValue(); return DEFAULTINSET; } /** * Sets the size attribute in the specified map to the specified value. * Not currently used in JGraph. Use setBounds to set vertex bounds instead. */ public static final void setSize(Map map, Dimension size) { map.put(SIZE, size); } /** * Returns the size attribute from the specified map. Not currently used * in JGraph. Use getBounds to set vertex bounds instead. */ public static final Dimension getSize(Map map) { return (Dimension) map.get(SIZE); } /** * Sets the offset attribute in the specified map to the specified value. * The offset is the position of a port relative to its origin, note this * attribute is only for ports. */ public static final void setOffset(Map map, Point2D offset) { map.put(OFFSET, offset); } /** * Returns the offset attribute from the specified map. The offset is the * position of a port relative to its origin, note this attribute is only * for ports. */ public static final Point2D getOffset(Map map) { return (Point2D) map.get(OFFSET); } /** * Sets the beginfill attribute in the specified map to the specified value. */ public static final void setBeginFill(Map map, boolean flag) { map.put(BEGINFILL, new Boolean(flag)); } /** * Returns the beginfill attribute from the specified map. */ public static final boolean isBeginFill(Map map) { Boolean bool = (Boolean) map.get(BEGINFILL); if (bool != null) return bool.booleanValue(); return false; } /** * Sets the endfill attribute in the specified map to the specified value. */ public static final void setEndFill(Map map, boolean flag) { map.put(ENDFILL, new Boolean(flag)); } /** * Returns the endfill attribute from the specified map. */ public static final boolean isEndFill(Map map) { Boolean bool = (Boolean) map.get(ENDFILL); if (bool != null) return bool.booleanValue(); return false; } /** * Sets the absolute attributes in the specified map to the specified value. */ public static final void setAbsolute(Map map, boolean flag) { setAbsoluteX(map, flag); setAbsoluteY(map, flag); } /** * Sets the absolutey attribute in the specified map to the specified value. */ public static final void setAbsoluteY(Map map, boolean flag) { map.put(ABSOLUTEY, new Boolean(flag)); } /** * Returns the absolutey attribute from the specified map. */ public static final boolean isAbsoluteY(Map map) { Boolean bool = (Boolean) map.get(ABSOLUTEY); if (bool != null) return bool.booleanValue(); return false; } /** * Sets the absolutex attribute in the specified map to the specified value. */ public static final void setAbsoluteX(Map map, boolean flag) { map.put(ABSOLUTEX, new Boolean(flag)); } /** * Returns the absolutex attribute from the specified map. */ public static final boolean isAbsoluteX(Map map) { Boolean bool = (Boolean) map.get(ABSOLUTEX); if (bool != null) return bool.booleanValue(); return false; } /** * Sets the removeall attribute in the specified map to the specified value. */ public static final void setRemoveAll(Map map, boolean flag) { map.put(REMOVEALL, new Boolean(flag)); } /** * Returns the removeall attribute from the specified map. */ public static final boolean isRemoveAll(Map map) { Boolean bool = (Boolean) map.get(REMOVEALL); if (bool != null) return bool.booleanValue(); return false; } /** * @return Returns the ROUTING_SIMPLE. */ public static Edge.Routing getROUTING_SIMPLE() { return ROUTING_SIMPLE; } /** * @return Returns the ROUTING_SIMPLE. */ public static Edge.Routing getROUTING_DEFAULT() { return ROUTING_DEFAULT; } /** * Sets the link attribute in the specified map to the specified value. * * @param map * The map to store the link attribute in. * @param link * The value to set the link attribute to. */ public static void setLink(Map map, String link) { map.put(LINK, link); } /** * Returns the link for the specified attribute map. */ public static String getLink(Map map) { String link = (String) map.get(LINK); return link; } /** * Sets the label enabled attribute in the specified map to the specified * value. * * @param map * The map to store the label enabled attribute in. * @param flag * The value to set the label enabled attribute to. */ public static void setLabelEnabled(Map map, boolean flag) { map.put(LABELENABLED, new Boolean(flag)); } /** * Returns whether the label is enabled for the specified cell. * * @param map * the attribute map for the cell being tested for this * condition. * @return whether or not labels should be displayed on this cell */ public static boolean isLabelEnabled(Map map) { Boolean bool = (Boolean) map.get(LABELENABLED); if (bool != null) return bool.booleanValue(); return true; } /** * Sets the exact segment attribute in the specified map to the specified * value. * * @param map * The map to store the exact segment attribute in. * @param flag * The value to set the exact segment attribute to. */ public static void setExactSegmentLabel(Map map, boolean flag) { map.put(EXACTSEGMENTRELATIVE, new Boolean(flag)); } /** * Returns whether the exact segment is worked out for placement of labels * along edges for the specified cell. * * @param map * the attribute map for the cell being tested for this * condition. * @return whether or not exact segments should be calculated on this cell */ public static boolean isExactSegmentLabel(Map map) { Boolean bool = (Boolean) map.get(EXACTSEGMENTRELATIVE); if (bool != null) return bool.booleanValue(); return false; } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/DefaultEdge.java0000644000175000017500000001505311256667036024762 0ustar gregoagregoa/* * @(#)DefaultEdge.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.List; /** * A simple implementation for an edge. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class DefaultEdge extends DefaultGraphCell implements Edge { /** Source and target of the edge. */ protected Object source, target; /** * Constructs an empty edge. */ public DefaultEdge() { this(null); } /** * Constructs an edge that holds a reference to the specified user object. * * @param userObject * reference to the user object */ public DefaultEdge(Object userObject) { this(userObject, null); } /** * Constructs an edge that holds a reference to the specified user object * and sets default values for points and the label position. * * @param userObject * reference to the user object */ public DefaultEdge(Object userObject, AttributeMap storageMap) { super(userObject, storageMap); } /** * Returns the source of the edge. */ public Object getSource() { return source; } /** * Returns the target of the edge. */ public Object getTarget() { return target; } /** * Sets the source of the edge. */ public void setSource(Object port) { source = port; } /** * Returns the target of edge. */ public void setTarget(Object port) { target = port; } /** * Create a clone of the cell. The cloning of the user object is deferred to * the cloneUserObject() method. The source and target references are set to * null. * * @return Object a clone of this object. */ public Object clone() { DefaultEdge edge = (DefaultEdge) super.clone(); edge.source = null; edge.target = null; return edge; } // // Default Routing // public static class LoopRouting implements Edge.Routing { public List route(GraphLayoutCache cache, EdgeView edge) { if (edge.isLoop()) { return routeLoop(cache, edge); } return routeEdge(cache, edge); } protected List routeLoop(GraphLayoutCache cache, EdgeView edge) { List newPoints = new ArrayList(); newPoints.add(edge.getSource()); CellView sourceParent = (edge.getSource() != null) ? edge .getSource().getParentView() : edge.getSourceParentView(); if (sourceParent != null) { Point2D from = AbstractCellView.getCenterPoint(sourceParent); Rectangle2D rect = sourceParent.getBounds(); double width = rect.getWidth(); double height2 = rect.getHeight() / 2; double loopWidth = Math.min(20, Math.max(10, width / 8)); double loopHeight = Math.min(30, Math.max(12, Math.max( loopWidth + 4, height2 / 2))); newPoints.add(edge.getAttributes().createPoint( from.getX() - loopWidth, from.getY() - height2 - loopHeight * 1.2)); newPoints.add(edge.getAttributes().createPoint(from.getX(), from.getY() - height2 - 1.5 * loopHeight)); newPoints.add(edge.getAttributes().createPoint( from.getX() + loopWidth, from.getY() - height2 - loopHeight * 1.2)); newPoints.add(edge.getTarget()); return newPoints; } return null; } protected List routeEdge(GraphLayoutCache cache, EdgeView edge) { return null; } public int getPreferredLineStyle(EdgeView edge) { if (edge.isLoop()) { return getLoopStyle(); } return getEdgeStyle(); } protected int getLoopStyle() { return GraphConstants.STYLE_BEZIER; } protected int getEdgeStyle() { return NO_PREFERENCE; } } public static class DefaultRouting extends LoopRouting { protected List routeEdge(GraphLayoutCache cache, EdgeView edge) { List newPoints = new ArrayList(); int n = edge.getPointCount(); Point2D from = edge.getPoint(0); newPoints.add(from); if (edge.getSource() instanceof PortView) { newPoints.set(0, edge.getSource()); from = ((PortView) edge.getSource()).getLocation(); } else if (edge.getSource() != null) { Rectangle2D b = edge.getSource().getBounds(); from = edge.getAttributes().createPoint(b.getCenterX(), b.getCenterY()); } Point2D to = edge.getPoint(n - 1); CellView target = edge.getTarget(); if (target instanceof PortView) to = ((PortView) target).getLocation(); else if (target != null) { Rectangle2D b = target.getBounds(); to = edge.getAttributes().createPoint(b.getCenterX(), b.getCenterY()); } if (from != null && to != null) { Point2D[] routed; double dx = Math.abs(from.getX() - to.getX()); double dy = Math.abs(from.getY() - to.getY()); double x2 = from.getX() + ((to.getX() - from.getX()) / 2); double y2 = from.getY() + ((to.getY() - from.getY()) / 2); routed = new Point2D[2]; Rectangle2D targetBounds = null; Rectangle2D sourceBounds = null; if ((edge.getTarget() != null && edge.getTarget() .getParentView() != null) && (edge.getSource() != null && edge.getSource() .getParentView() != null)) { targetBounds = edge.getTarget().getParentView().getBounds(); sourceBounds = edge.getSource().getParentView().getBounds(); } if (targetBounds != null && sourceBounds != null) { if (dx > dy) { routed[0] = edge.getAttributes().createPoint(x2, from.getY()); routed[1] = edge.getAttributes().createPoint(x2, to.getY()); if (targetBounds.contains(routed[0]) || (sourceBounds.contains(routed[0])) || targetBounds.contains(routed[1]) || (sourceBounds.contains(routed[1]))) { routed[0] = edge.getAttributes().createPoint( from.getX(), y2); routed[1] = edge.getAttributes().createPoint( to.getX(), y2); } } else { routed[0] = edge.getAttributes().createPoint( from.getX(), y2); routed[1] = edge.getAttributes().createPoint(to.getX(), y2); if (targetBounds.contains(routed[0]) || (sourceBounds.contains(routed[0])) || targetBounds.contains(routed[1]) || (sourceBounds.contains(routed[1]))) { routed[0] = edge.getAttributes().createPoint(x2, from.getY()); routed[1] = edge.getAttributes().createPoint(x2, to.getY()); } } // Set/Add Points for (int i = 0; i < routed.length; i++) { if (!targetBounds.contains(routed[i]) && (!sourceBounds.contains(routed[i]))) { newPoints.add(routed[i]); } } } // Add target point if (target != null) newPoints.add(target); else newPoints.add(to); return newPoints; } return null; } } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/GraphUndoManager.java0000644000175000017500000001014011256667036025763 0ustar gregoagregoa/* * @(#)GraphUndoManager.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoManager; import javax.swing.undo.UndoableEdit; import org.jgraph.event.GraphLayoutCacheEvent; /** * An UndoManager that may be shared among multiple GraphLayoutCache's. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class GraphUndoManager extends UndoManager { /** * Overridden to preserve usual semantics: returns true if an undo * operation would be successful now for the given view, false otherwise */ public synchronized boolean canUndo(Object source) { if (isInProgress()) { UndoableEdit edit = editToBeUndone(source); return edit != null && edit.canUndo(); } else { return super.canUndo(); } } /** * Overridden to preserve usual semantics: returns true if a redo * operation would be successful now for the given view, false otherwise */ public synchronized boolean canRedo(Object source) { if (isInProgress()) { UndoableEdit edit = editToBeRedone(source); return edit != null && edit.canRedo(); } else { return super.canRedo(); } } /** * If this UndoManager is inProgress, undo the last significant * UndoableEdit wrt to source, and all insignificant edits back to * it. Updates indexOfNextAdd accordingly. * *

    If not inProgress, indexOfNextAdd is ignored and super's routine is * called.

    * * @see UndoManager#undo */ public void undo(Object source) { if (source == null || !isInProgress()) super.undo(); else { UndoableEdit edit = editToBeUndone(source); //System.out.println("undoTo edit="+edit); if (edit == null) throw new CannotUndoException(); undoTo(edit); } } protected UndoableEdit editToBeUndone(Object source) { UndoableEdit edit = null; Object src = null; do { edit = nextEditToBeUndone(edit); if (edit instanceof GraphLayoutCacheEvent.GraphLayoutCacheChange) src = ((GraphLayoutCacheEvent.GraphLayoutCacheChange) edit).getSource(); if (!(src instanceof GraphLayoutCache)) src = null; } while (edit != null && src != null && src != source); return edit; } /** * Returns the the next significant edit wrt to current * to be undone if undo is called. May return null. */ protected UndoableEdit nextEditToBeUndone(UndoableEdit current) { if (current == null) return editToBeUndone(); else { int index = edits.indexOf(current) - 1; if (index >= 0) return (UndoableEdit) edits.get(index); } return null; } /** * If this UndoManager is inProgress, * redoes the last significant UndoableEdit with * respect to source or after, and all insignificant * edits up to it. Updates indexOfNextAdd accordingly. * *

    If not inProgress, indexOfNextAdd * is ignored and super's routine is called.

    */ public void redo(Object source) { if (source == null || !isInProgress()) super.redo(); else { UndoableEdit edit = editToBeRedone(source); //System.out.println("redoTo edit="+edit); if (edit == null) throw new CannotRedoException(); redoTo(edit); } } protected UndoableEdit editToBeRedone(Object source) { UndoableEdit edit = nextEditToBeRedone(null); UndoableEdit last = null; Object src = null; do { last = edit; edit = nextEditToBeRedone(edit); if (edit instanceof GraphLayoutCacheEvent.GraphLayoutCacheChange) src = ((GraphLayoutCacheEvent.GraphLayoutCacheChange) edit).getSource(); if (!(src instanceof GraphLayoutCache)) src = null; } while (edit != null && src != null && src != source); return last; } /** * Returns the the next significant edit wrt to current * to be redone if redo is called. May return null. */ protected UndoableEdit nextEditToBeRedone(UndoableEdit current) { if (current == null) return editToBeRedone(); else { int index = edits.indexOf(current) + 1; if (index < edits.size()) return (UndoableEdit) edits.get(index); } return null; } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/EdgeView.java0000644000175000017500000014721311256667036024314 0ustar gregoagregoa/* * @(#)EdgeView.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.border.BevelBorder; import org.jgraph.JGraph; import org.jgraph.plaf.GraphUI; import org.jgraph.plaf.basic.BasicGraphUI; /** * The default implementation of an edge view. The getEdgeRenderer method * assumes a renderer of type EdgeRenderer. If you provide a custom renderer to * a subclass, you must also override the methods that call this method, namely: * getShape, getLabelBounds, getExtraLabelBounds, intersects and getBounds. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class EdgeView extends AbstractCellView { /** Renderer for the class. */ public static transient EdgeRenderer renderer = new EdgeRenderer(); /** List of points of the edge. May contain ports. */ protected List points; /** Cached source and target portview of the edge. */ protected CellView source, target; protected CellView sourceParentView, targetParentView; /** Cached label position of the edge. */ protected Point2D labelPosition; protected Point2D[] extraLabelPositions; protected transient Point2D labelVector = null; /** Drawing attributes that are created on the fly */ public transient Shape beginShape, endShape, lineShape; /** Shared-path tune-up. */ public transient GeneralPath sharedPath = null; protected transient Rectangle2D cachedBounds = null; /** Whether or not pre 5.12.3.3 disconnectable behaviour is to be used. * This allowed an edge to reconnect to another vertex ever when * isDisconnectable was false for the edge. Set to false * with isDisconnectable set to false for the edge forbids * any disconnection. Default is true. */ public static boolean LEGACY_DISCONNECTABLE = true; /** * Constructs an empty edge view. */ public EdgeView() { super(); } /** * Constructs an edge view for the specified model object. * * @param cell * reference to the model object */ public EdgeView(Object cell) { super(cell); } // // Data Source // /** * Overrides the parent method to udpate the cached points, source and * target port. If the source or target is removed, a point is inserted into * the array of points. */ public void refresh(GraphLayoutCache cache, CellMapper mapper, boolean createDependentViews) { // Makes sure the manual control points are passed to // the router instead of the cached control points after // changes to the edge (normally manual point changes). points = null; super.refresh(cache, mapper, createDependentViews); // Re-sync source- and targetportviews GraphModel model = cache.getModel(); Object modelSource = model.getSource(cell); Object modelTarget = model.getTarget(cell); setSource(mapper.getMapping(modelSource, createDependentViews)); setTarget(mapper.getMapping(modelTarget, createDependentViews)); if (modelSource != null && getSource() == null) sourceParentView = getVisibleParent(model, mapper, modelSource); else sourceParentView = null; if (modelTarget != null && getTarget() == null) targetParentView = getVisibleParent(model, mapper, modelTarget); else targetParentView = null; } protected CellView getVisibleParent(GraphModel model, CellMapper mapper, Object port) { CellView view = null; do { view = mapper.getMapping(port, false); port = model.getParent(port); } while (view == null && port != null); return view; } /** * Update attributes and recurse children. */ public void update(GraphLayoutCache cache) { super.update(cache); // Save the reference to the points so they can be changed // in-place by use of setPoint, setSource, setTarget methods. List controlPoints = GraphConstants.getPoints(allAttributes); if (controlPoints == null) { controlPoints = new ArrayList(4); controlPoints.add(allAttributes.createPoint(10, 10)); controlPoints.add(allAttributes.createPoint(20, 20)); GraphConstants.setPoints(allAttributes, controlPoints); } // Uses the manual control points while the edge is being routed. // Otherwise uses the cached points (eg. for preview). if (points == null) points = controlPoints; Edge.Routing routing = GraphConstants.getRouting(allAttributes); List routedPoints = null; // Passes the current cached points to the router if (routing != null) routedPoints = routing.route(cache, this); // Shadows the manual control points with the // routed control points points = (routedPoints != null && !routedPoints.isEmpty()) ? routedPoints : controlPoints; // Overrides manual point locations with the real port views if (points == controlPoints) { if (source != null) setSource(source); if (target != null) setTarget(target); } // Checks and caches label positions checkDefaultLabelPosition(); Point2D[] positions = GraphConstants .getExtraLabelPositions(allAttributes); if (positions != null) { extraLabelPositions = new Point2D[positions.length]; for (int i = 0; i < positions.length; i++) extraLabelPositions[i] = positions[i]; } else extraLabelPositions = null; // Clear cached shapes beginShape = null; endShape = null; lineShape = null; invalidate(); } /** * Hook for subclassers to avoid default label positions. */ protected void checkDefaultLabelPosition() { labelPosition = GraphConstants.getLabelPosition(allAttributes); String label = String.valueOf(getCell()); if (labelPosition == null && label != null && label.length() > 0) { int center = GraphConstants.PERMILLE / 2; labelPosition = new Point(center, 0); GraphConstants.setLabelPosition(allAttributes, labelPosition); } } /** * Resets the cached values of the edge view */ protected void invalidate() { labelVector = null; sharedPath = null; cachedBounds = null; } /** * Returns the shape of the view according to the last rendering state */ public Shape getShape() { if (sharedPath != null) return sharedPath; else { return sharedPath = (GeneralPath) getEdgeRenderer().createShape(); } } // // View Methods // /** * Returns true if this view intersects the given rectangle. */ public boolean intersects(JGraph graph, Rectangle2D rect) { boolean intersects = super.intersects(graph, rect); if (!isLeaf()) { return intersects; } else if (intersects) { Rectangle r = new Rectangle((int) rect.getX(), (int) rect.getY(), (int) rect.getWidth(), (int) rect.getHeight()); return getEdgeRenderer().intersects(graph, this, r); } return false; } /** * Returns the location for this edgeview. */ public Rectangle2D getBounds() { Rectangle2D rect = super.getBounds(); if (rect == null) { if (cachedBounds == null) { cachedBounds = getEdgeRenderer().getBounds(this); } rect = cachedBounds; } return rect; } /** * Returns the local renderer. Do not access the renderer field directly. * Use this method instead. Note: This method is package private. */ EdgeRenderer getEdgeRenderer() { return (EdgeRenderer) getRenderer(); } /** * Returns a renderer for the class. */ public CellViewRenderer getRenderer() { return renderer; } /** * Returns a cell handle for the view. */ public CellHandle getHandle(GraphContext context) { return new EdgeHandle(this, context); } // // Cached Values // /** * Returns the CellView that represents the source of the edge. */ public CellView getSource() { return source; } public CellView getSourceParentView() { return sourceParentView; } /** * Sets the sourceView of the edge. */ public void setSource(CellView sourceView) { sourceParentView = null; source = sourceView; if (source != null) points.set(0, source); else points.set(0, getPoint(0)); invalidate(); } /** * Returns the CellView that represents the target of the edge. */ public CellView getTarget() { return target; } public CellView getTargetParentView() { return targetParentView; } /** * Sets the targetView of the edge. */ public void setTarget(CellView targetView) { target = targetView; targetParentView = null; int n = points.size() - 1; if (target != null) points.set(n, target); else points.set(n, getPoint(n)); invalidate(); } /** * Returns a point that describes the position of the label. */ public Point2D getExtraLabelPosition(int index) { return extraLabelPositions[index]; } /** * Returns a point that describes the position of the label. */ public Point2D getLabelPosition() { return labelPosition; } /** * Sets the description of the label position. */ public void setLabelPosition(Point2D pos) { labelPosition.setLocation(pos); invalidate(); } /** * Sets the description of the label position. */ public void setExtraLabelPosition(int index, Point2D pos) { extraLabelPositions[index].setLocation(pos); invalidate(); } // // Points // /** * Returns true if the edge is a loop. */ public boolean isLoop() { return (getSource() != null && getSource() == getTarget()) || (sourceParentView != null && sourceParentView == targetParentView) || (sourceParentView != null && getTarget() != null && getTarget() .getParentView() == sourceParentView) || (targetParentView != null && getSource() != null && getSource() .getParentView() == targetParentView); } /** * Returns the points. * * @return List */ public List getPoints() { return points; } /** * Returns the number of point for this edge. */ public int getPointCount() { if (points != null) { return points.size(); } else { return 0; } } /** * Returns the cached points for this edge. */ public Point2D getPoint(int index) { Object obj = points.get(index); if (index == 0 && sourceParentView != null) { return sourceParentView.getPerimeterPoint(this, getCenterPoint(sourceParentView), getNearestPoint(index == 0)); } else if (index == getPointCount() - 1 && targetParentView != null) { return targetParentView.getPerimeterPoint(this, getCenterPoint(targetParentView), getNearestPoint(index == 0)); } else if (obj instanceof PortView) // Port Location Seen From This Edge return ((PortView) obj).getLocation(this, getNearestPoint(index == 0)); else if (obj instanceof CellView) { // Should not happen Rectangle2D r = ((CellView) obj).getBounds(); return new Point2D.Double(r.getX(), r.getY()); } else if (obj instanceof Point2D) // Regular Point return (Point2D) obj; return null; } /** * Returns the nearest point wrt to the source or target. This method * returns the next or previous point or port in the points list, eg. if * source is true it returns the location of the point or port at index 1 * without calling the getLocation method on any ports.
    * Likewise, the method returns the location at index getPointCount()-2 if * source is false. */ protected Point2D getNearestPoint(boolean source) { if (getPointCount() == 2) { if (source && target instanceof PortView && GraphConstants.getOffset(target.getAllAttributes()) != null) { return ((PortView) target).getLocation(this); } if (!source && this.source instanceof PortView && GraphConstants.getOffset(this.source.getAllAttributes()) != null) { return ((PortView) this.source).getLocation(this); } if (source && targetParentView != null && targetParentView.isLeaf()) return getCenterPoint(targetParentView); else if (!source && sourceParentView != null && sourceParentView.isLeaf()) return getCenterPoint(sourceParentView); } return getPointLocation((source) ? 1 : getPointCount() - 2); } /** * Returns the point of edge at index. Avoids * calling getLocation on any ports of edge. *
    * This is used from within getPoint to pass the nearest point to the * portview to find it's location. This uses the center point of the parent * view to determine the port view's location to avoid infinite recursion. */ protected Point2D getPointLocation(int index) { Object obj = points.get(index); if (obj instanceof Point2D) return (Point2D) obj; else if (obj instanceof PortView) { CellView vertex = ((CellView) obj).getParentView(); if (vertex != null) return getCenterPoint(vertex); } return null; } /** * Sets the point at index to p. */ public void setPoint(int index, Point2D p) { points.set(index, p); invalidate(); } /** * Adds p at position index. */ public void addPoint(int index, Point2D p) { points.add(index, p); invalidate(); } /** * Removes the point at position index. */ public void removePoint(int index) { points.remove(index); invalidate(); } /** * Adds an extra label. */ public void addExtraLabel(Point2D location, Object label) { Object[] extraLabels = GraphConstants .getExtraLabels(getAllAttributes()); Point2D[] positions = GraphConstants .getExtraLabelPositions(getAllAttributes()); // Inserts a new extra label if (extraLabels == null) { extraLabels = new Object[1]; positions = new Point2D[1]; } else { Object[] tmp = new Object[extraLabels.length + 1]; System.arraycopy(extraLabels, 0, tmp, 0, extraLabels.length); extraLabels = tmp; Point2D[] pts = new Point2D[positions.length + 1]; System.arraycopy(positions, 0, pts, 0, positions.length); positions = pts; } int newIndex = extraLabels.length - 1; extraLabels[newIndex] = label; positions[newIndex] = location; GraphConstants.setExtraLabels(getAllAttributes(), extraLabels); GraphConstants.setExtraLabelPositions(getAllAttributes(), positions); } /** * Removes the point at position index. */ public void removeExtraLabel(int index) { Object[] labels = GraphConstants.getExtraLabels(getAllAttributes()); Point2D[] pts = GraphConstants .getExtraLabelPositions(getAllAttributes()); if (labels == null || labels.length > 1) { Object[] newLabels = new Object[labels.length - 1]; Point2D[] newPts = new Point2D[pts.length - 1]; System.arraycopy(labels, 0, newLabels, 0, index); if (index < newLabels.length) System.arraycopy(labels, index + 1, newLabels, index, newLabels.length - index); System.arraycopy(pts, 0, newPts, 0, index); if (index < newPts.length) System.arraycopy(pts, index + 1, newPts, index, newPts.length - index); GraphConstants.setExtraLabels(getAllAttributes(), newLabels); GraphConstants.setExtraLabelPositions(getAllAttributes(), newPts); } else { // TODO: Remove via REMOVEATTRIBUTES GraphConstants.setExtraLabels(getAllAttributes(), new Object[0]); GraphConstants.setExtraLabelPositions(getAllAttributes(), new Point2D[0]); } } /** * Utility method that returns the first point of the pair that forms the * segment that is relativeX along the edge as a proportion * * @return the index of the first point. A value of -1 indicate to use the * first and last points */ public int getFirstPointOfSegment() { boolean exactSegment = GraphConstants .isExactSegmentLabel(allAttributes); double dx = 0; double dy = 0; int n = getPointCount(); if (exactSegment) { // Determine the vector based on the actual edge segment the // label lies on Point2D lastPoint = getPoint(0); double totalLength = 0; for (int i = 1; i < n; i++) { Point2D currentPoint = getPoint(i); dx = currentPoint.getX() - lastPoint.getX(); dy = currentPoint.getY() - lastPoint.getY(); totalLength += Math.sqrt(dx * dx + dy * dy); lastPoint = currentPoint; } double relativeX = getLabelPosition().getX()/(double)GraphConstants.PERMILLE; double labelXPositionDistance = relativeX * totalLength; totalLength = 0; lastPoint = getPoint(0); if (relativeX <= 0.0 || relativeX >= 1.0) { return -1; } else { for (int i = 1; i < n; i++) { Point2D currentPoint = getPoint(i); dx = currentPoint.getX() - lastPoint.getX(); dy = currentPoint.getY() - lastPoint.getY(); totalLength += Math.sqrt(dx * dx + dy * dy); if (totalLength > labelXPositionDistance) { return i-1; } } } } else { return -1; } return -1; } /** * Hook to return the vector that is taken as the base vector to compute * relative label positions. Normally, the vector goes from the first to the * last point on the edge, unless these points are equal, in which case the * average distance of all points to the source point is used. */ public Point2D getLabelVector() { if (labelVector == null) { Point2D p0 = getPoint(0); double dx = 0; double dy = 0; // Finds an average distance int n = getPointCount(); if (isLoop()) { for (int i = 1; i < n; i++) { Point2D point = getPoint(i); dx += point.getX() - p0.getX(); dy += point.getY() - p0.getY(); } n /= 2; dx /= n; dy /= n; labelVector = new Point2D.Double(dx, dy); } else { boolean exactSegment = GraphConstants .isExactSegmentLabel(allAttributes); if (exactSegment) { // Determine the vector based on the actual edge segment the // label lies on Point2D lastPoint = getPoint(0); double totalLength = 0; for (int i = 1; i < n; i++) { Point2D currentPoint = getPoint(i); dx = currentPoint.getX() - lastPoint.getX(); dy = currentPoint.getY() - lastPoint.getY(); totalLength += Math.sqrt(dx * dx + dy * dy); lastPoint = currentPoint; } double relativeX = getLabelPosition().getX()/(double)GraphConstants.PERMILLE; double labelXPositionDistance = relativeX * totalLength; totalLength = 0; lastPoint = getPoint(0); if (relativeX <= 0.0 || relativeX >= 1.0) { exactSegment = false; } else { for (int i = 1; i < n; i++) { Point2D currentPoint = getPoint(i); dx = currentPoint.getX() - lastPoint.getX(); dy = currentPoint.getY() - lastPoint.getY(); totalLength += Math.sqrt(dx * dx + dy * dy); if (totalLength > labelXPositionDistance) { labelVector = new Point2D.Double(dx, dy); break; } lastPoint = currentPoint; } } } if (!exactSegment || labelVector == null) { Point2D point = getPoint(n - 1); dx = point.getX() - p0.getX(); dy = point.getY() - p0.getY(); labelVector = new Point2D.Double(dx, dy); } } } return labelVector; } /** * Returns the absolute position of the main label * @return the absolute position of the main label */ protected Point2D getAbsoluteLabelPosition() { Point2D result = getAbsoluteLabelPositionFromRelative(GraphConstants.getLabelPosition(getAllAttributes())); return result; } /** * Returns the absolute position of the specified extra label * @param index the index of the extra label * @return the absolute position of the specified extra label */ protected Point2D getAbsoluteExtraLabelPosition(int index) { Point2D[] positions = GraphConstants .getExtraLabelPositions(getAllAttributes()); if (positions != null && positions.length > index) { Point2D result = getAbsoluteLabelPositionFromRelative(positions[index]); return result; } return null; } /** * Converts relative label position to absolute and allows for * any label offset. * @param geometry the relative label position * @return the absolute label position including any offset */ protected Point2D getAbsoluteLabelPositionFromRelative(Point2D geometry) { Point2D result = convertRelativeLabelPositionToAbsolute(geometry); if (result != null) { double offsetX = 0; double offsetY = 0; Point2D offset = GraphConstants.getOffset(getAllAttributes()); if (offset != null) { offsetX = offset.getX(); offsetY = offset.getY(); } double x = result.getX() + offsetX; double y = result.getY() + offsetY; return new Point2D.Double(x, y); } return null; } /** * Converts an relative label position (x is distance along edge and y is * distance above/below edge vector) into an absolute co-ordination point * @param geometry the relative label position * @return the absolute label position */ protected Point2D convertRelativeLabelPositionToAbsolute(Point2D geometry) { Point2D pt = getPoint(0); if (pt != null) { double length = 0; int pointCount = getPointCount(); double[] segments = new double[pointCount]; // Find the total length of the segments and also store the length // of each segment for (int i = 1; i < pointCount; i++) { Point2D tmp = getPoint(i); if (tmp != null) { double dx = pt.getX() - tmp.getX(); double dy = pt.getY() - tmp.getY(); double segment = Math.sqrt(dx * dx + dy * dy); segments[i - 1] = segment; length += segment; pt = tmp; } } // Change x to be a value between 0 and 1 indicating how far // along the edge the label is double x = geometry.getX()/GraphConstants.PERMILLE; double y = geometry.getY(); // dist is the distance along the edge the label is double dist = x * length; length = 0; int index = 1; double segment = segments[0]; // Find the length up to the start of the segment the label is // on (length) and retrieve the length of that segment (segment) while (dist > length + segment && index < pointCount - 1) { length += segment; segment = segments[index++]; } // factor is the proportion along this segment the label lies at double factor = (dist - length) / segment; Point2D p0 = getPoint(index - 1); Point2D pe = getPoint(index); if (p0 != null && pe != null) { // The x and y offsets of the label from the start point // of the segment double dx = pe.getX() - p0.getX(); double dy = pe.getY() - p0.getY(); // The normal vectors of double nx = dy / segment; double ny = dx / segment; // The x position is the start x of the segment + the factor of // the x offset between the start and end of the segment + the // x component of the y (height) offset contributed along the // normal vector. x = p0.getX() + dx * factor - nx * y; // The x position is the start y of the segment + the factor of // the y offset between the start and end of the segment + the // y component of the y (height) offset contributed along the // normal vector. y = p0.getY() + dy * factor + ny * y; return new Point2D.Double(x, y); } } return null; } // // Routing // public static double getLength(CellView view) { double cost = 1; if (view instanceof EdgeView) { EdgeView edge = (EdgeView) view; Point2D last = null, current = null; for (int i = 0; i < edge.getPointCount(); i++) { current = edge.getPoint(i); if (last != null) cost += last.distance(current); last = current; } } return cost; } // // Handle // // This implementation uses the point instance to make the change. No index // is used for the current point because routing could change the index // during // the move operation. public static class EdgeHandle implements CellHandle, Serializable { protected JGraph graph; /* Pointer to the edge and its clone. */ protected EdgeView edge, orig; /* * Boolean indicating whether the source, target or label is being * edited. */ protected boolean label = false, source = false, target = false; /** * Holds the index of the current (editing) label or point. */ protected int currentLabel = -1, currentIndex = -1; /* Pointer to the currently selected point. */ protected Point2D currentPoint; /* Array of control points represented as rectangles. */ protected transient Rectangle2D[] r; /* A control point for the label position. */ protected transient Rectangle2D loc; protected transient Rectangle2D[] extraLabelLocations; protected boolean firstOverlayCall = true; protected boolean isEdgeConnectable = true; protected EdgeView relevantEdge = null; /** * True if the cell is being edited. */ protected boolean editing = false; /** * Holds the initial location of the label. */ protected Point2D initialLabelLocation = null; /** * Indicates whether the edge has been modified during the last mouse * pressed and dragged operations. */ protected boolean edgeModified = false; /** * Component that is used for highlighting cells if * the graph does not allow XOR painting. */ protected JComponent highlight = new JPanel(); public EdgeHandle(EdgeView edge, GraphContext ctx) { this.graph = ctx.getGraph(); this.edge = edge; editing = graph.getEditingCell() == edge.getCell(); loc = new Rectangle(); Object[] labels = GraphConstants.getExtraLabels(edge .getAllAttributes()); if (labels != null) { extraLabelLocations = new Rectangle[labels.length]; for (int i = 0; i < extraLabelLocations.length; i++) extraLabelLocations[i] = new Rectangle(); } orig = (EdgeView) graph.getGraphLayoutCache().getMapping( edge.getCell(), false); reloadPoints(orig); isEdgeConnectable = GraphConstants.isConnectable(edge .getAllAttributes()); // Configures the panel for highlighting ports highlight = createHighlight(); } /** * Creates the component that is used for highlighting cells if * the graph does not allow XOR painting. */ protected JComponent createHighlight() { JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); panel.setVisible(false); panel.setOpaque(false); return panel; } protected void reloadPoints(EdgeView edge) { relevantEdge = edge; r = new Rectangle[edge.getPointCount()]; for (int i = 0; i < r.length; i++) r[i] = new Rectangle(); invalidate(); } // Update and paint control points public void paint(Graphics g) { invalidate(); if (!edge.isLeaf()) return; for (int i = 0; i < r.length; i++) { if (isEdgeConnectable && !editing) g.setColor(graph.getHandleColor()); else g.setColor(graph.getLockedHandleColor()); g.fill3DRect((int) r[i].getX(), (int) r[i].getY(), (int) r[i] .getWidth(), (int) r[i].getHeight(), true); CellView port = null; if (i == 0 && edge.getSource() != null) port = edge.getSource(); else if (i == r.length - 1 && edge.getTarget() != null) port = edge.getTarget(); if (port != null || (i == 0 && edge.getSourceParentView() != null) || (i == r.length - 1 && edge.getTargetParentView() != null)) { g.setColor(graph.getLockedHandleColor()); Point2D tmp = (port != null) ? GraphConstants .getOffset(port.getAllAttributes()) : null; if (tmp != null) { g.drawLine((int) r[i].getX() + 1, (int) r[i].getY() + 1, (int) (r[i].getX() + r[i].getWidth()) - 3, (int) (r[i].getY() + r[i].getHeight()) - 3); g.drawLine((int) r[i].getX() + 1, (int) (r[i].getY() + r[i].getHeight()) - 3, (int) (r[i].getX() + r[i].getWidth()) - 3, (int) r[i].getY() + 1); } else g.drawRect((int) r[i].getX() + 2, (int) r[i].getY() + 2, (int) r[i].getWidth() - 5, (int) r[i] .getHeight() - 5); } } if (!graph.isXorEnabled()) { firstOverlayCall = false; overlay(g); } } /** * Highlights the given cell view or removes the highlight if * no cell view is specified. * * @param graph * @param cellView */ protected void highlight(JGraph graph, CellView cellView) { if (cellView != null) { highlight.setBounds(getHighlightBounds(graph, cellView)); if (highlight.getParent() == null) { graph.add(highlight); highlight.setVisible(true); } } else { if (highlight.getParent() != null) { highlight.setVisible(false); highlight.getParent().remove(highlight); } } } /** * Returns the bounds to be used to highlight the given cell view. * * @param graph * @param cellView * @return */ protected Rectangle getHighlightBounds(JGraph graph, CellView cellView) { boolean offset = (GraphConstants.getOffset(cellView.getAllAttributes()) != null); Rectangle2D r = (offset) ? cellView.getBounds() : cellView .getParentView().getBounds(); r = graph.toScreen((Rectangle2D) r.clone()); int s = 3; return new Rectangle((int) (r.getX() - s), (int) (r.getY() - s), (int) (r.getWidth() + 2 * s), (int) (r.getHeight() + 2 * s)); } public void overlay(Graphics g) { if (edge != null && !firstOverlayCall && edge.isLeaf()) { // g.setColor(graph.getBackground()); // JDK 1.3 g.setColor(graph.getForeground()); if (graph.isXorEnabled()) { g.setXORMode(graph.getBackground().darker()); } Graphics2D g2 = (Graphics2D) g; AffineTransform oldTransform = g2.getTransform(); g2.scale(graph.getScale(), graph.getScale()); graph.getUI().paintCell(g, edge, edge.getBounds(), true); g2.setTransform(oldTransform); if (graph.isXorEnabled()) { if (isSourceEditing() && edge.getSource() != null) paintPort(g, edge.getSource()); else if (isTargetEditing() && edge.getTarget() != null) paintPort(g, edge.getTarget()); } } if (!graph.isXorEnabled()) { if (isSourceEditing()) highlight(graph, edge.getSource()); else if (isTargetEditing()) highlight(graph, edge.getTarget()); } firstOverlayCall = false; } protected void paintPort(Graphics g, CellView p) { boolean offset = (GraphConstants.getOffset(p.getAllAttributes()) != null); Rectangle2D r = (offset) ? p.getBounds() : p.getParentView() .getBounds(); r = graph.toScreen((Rectangle2D) r.clone()); int s = 3; r.setFrame(r.getX() - s, r.getY() - s, r.getWidth() + 2 * s, r .getHeight() + 2 * s); graph.getUI().paintCell(g, p, r, true); } protected boolean snap(boolean source, Point2D point) { boolean connect = graph.isConnectable() && isEdgeConnectable; Object port = graph.getPortForLocation(point.getX(), point.getY()); if (port != null && graph.getModel().getParent(port) == edge.getCell()) port = null; if (port != null && connect) { CellView portView = graph.getGraphLayoutCache().getMapping( port, false); Rectangle2D dirty = edge.getBounds(); dirty.add(portView.getParentView().getBounds()); if (GraphConstants.isConnectable(portView.getParentView() .getAllAttributes())) { Object cell = edge.getCell(); if (source && graph.getModel().acceptsSource(cell, port)) { if (edge.getSource() != portView) { edgeModified = true; if (graph.isXorEnabled()) { overlay(graph.getGraphics()); } edge.setSource(portView); edge.update(graph.getGraphLayoutCache()); if (graph.isXorEnabled()) { overlay(graph.getGraphics()); } else { dirty.add(edge.getBounds()); graph.repaint((int) dirty.getX(), (int) dirty .getY(), (int) dirty.getWidth(), (int) dirty.getHeight()); } } return true; } else if (!source && graph.getModel().acceptsTarget(cell, port)) { if (edge.getTarget() != portView) { edgeModified = true; if (graph.isXorEnabled()) { overlay(graph.getGraphics()); } edge.setTarget(portView); edge.update(graph.getGraphLayoutCache()); if (graph.isXorEnabled()) { overlay(graph.getGraphics()); } else { dirty.add(edge.getBounds()); graph.repaint((int) dirty.getX(), (int) dirty .getY(), (int) dirty.getWidth(), (int) dirty.getHeight()); } } return true; } } } return false; } public boolean isConstrainedMoveEvent(MouseEvent e) { GraphUI ui = graph.getUI(); if (ui instanceof BasicGraphUI) return ((BasicGraphUI) ui).isConstrainedMoveEvent(e); return false; } /** * Returning true signifies a mouse event adds a new point to an edge. */ public boolean isAddPointEvent(MouseEvent event) { return event.isPopupTrigger() || SwingUtilities.isRightMouseButton(event); } /** * Returning true signifies a mouse event removes a given point. */ public boolean isRemovePointEvent(MouseEvent event) { return event.isPopupTrigger() || SwingUtilities.isRightMouseButton(event); } protected boolean isSourceEditing() { return source; } protected boolean isTargetEditing() { return target; } /* * Returns true if either the source, target, label or a point is being * edited. */ protected boolean isEditing() { return source || target || label || currentLabel >= 0 || currentPoint != null; } /** * Invoked when the mouse pointer has been moved on a component (with no * buttons down). */ public void mouseMoved(MouseEvent event) { for (int i = 0; i < r.length; i++) if (r[i].contains(event.getPoint())) { graph.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); event.consume(); return; } if (loc.contains(event.getPoint()) && graph.isMoveable() && GraphConstants.isMoveable(edge.getAllAttributes())) { graph.setCursor(new Cursor(Cursor.HAND_CURSOR)); event.consume(); } if (extraLabelLocations != null && graph.isMoveable() && GraphConstants.isMoveable(edge.getAllAttributes())) { for (int i = 0; i < extraLabelLocations.length; i++) { if (extraLabelLocations[i].contains(event.getPoint())) { graph.setCursor(new Cursor(Cursor.HAND_CURSOR)); event.consume(); } } } } // Handle mouse pressed event. public void mousePressed(MouseEvent event) { /* INV: currentPoint = null; source = target = label = false; */ if (!edge.isLeaf()) return; boolean bendable = graph.isBendable() && GraphConstants.isBendable(edge.getAllAttributes()); int x = event.getX(); int y = event.getY(); // Detect hit on control point int index = 0; for (index = 0; index < r.length; index++) { if (r[index].contains(x, y)) { if (EdgeView.LEGACY_DISCONNECTABLE) { currentPoint = edge.getPoint(index); currentIndex = index; source = index == 0; target = index == r.length - 1; break; } else { if ((index > 0 && index < r.length - 1) || GraphConstants.isDisconnectable(edge .getAllAttributes())) { currentPoint = edge.getPoint(index); currentIndex = index; source = index == 0; target = index == r.length - 1; break; } else { event.consume(); } } } } // Detect hit on label if (!isEditing() && graph.isMoveable() && GraphConstants.isMoveable(edge.getAllAttributes()) && loc != null && loc.contains(x, y) && !isAddPointEvent(event) && !isRemovePointEvent(event) && graph.getEdgeLabelsMovable()) { initialLabelLocation = (Point2D) edge.getLabelPosition() .clone(); label = true; } // Detect hit on extra labels else if (extraLabelLocations != null && !isEditing() && graph.isMoveable() && graph.getEdgeLabelsMovable() && GraphConstants.isMoveable(edge.getAllAttributes())) { for (int i = 0; i < extraLabelLocations.length; i++) { if (extraLabelLocations[i] != null && extraLabelLocations[i].contains(x, y)) { currentLabel = i; initialLabelLocation = (Point2D) edge .getExtraLabelPosition(currentLabel).clone(); if (isRemovePointEvent(event)) { edge.removeExtraLabel(i); edgeModified = true; mouseReleased(event); } break; } } } // Remove Point if (isRemovePointEvent(event) && currentPoint != null && !source && !target && bendable && (edge.getSource() == null || currentIndex > 0) && (edge.getTarget() == null || currentIndex < edge .getPointCount() - 1)) { edge.removePoint(index); edgeModified = true; mouseReleased(event); // Add Point } else if (isAddPointEvent(event) && !isEditing() && bendable) { int s = graph.getHandleSize(); Rectangle2D rect = graph.fromScreen(new Rectangle(x - s, y - s, 2 * s, 2 * s)); if (edge.intersects(graph, rect)) { Point2D point = graph.fromScreen(graph.snap(new Point(event .getPoint()))); double min = Double.MAX_VALUE, dist = 0; for (int i = 0; i < edge.getPointCount() - 1; i++) { Point2D p = edge.getPoint(i); Point2D p1 = edge.getPoint(i + 1); dist = new Line2D.Double(p, p1).ptSegDistSq(point); if (dist < min) { min = dist; index = i + 1; } } edge.addPoint(index, point); edgeModified = true; currentPoint = point; reloadPoints(edge); paint(graph.getGraphics()); } } if (isEditing()) event.consume(); } public void mouseDragged(MouseEvent event) { Rectangle2D dirty = edge.getBounds(); Point2D p = graph.fromScreen(new Point(event.getPoint())); // Move Label if (label || currentLabel >= 0) { Rectangle2D r = edge.getBounds(); if (r != null) { edgeModified = true; if (graph.isXorEnabled()) { overlay(graph.getGraphics()); } if (!GraphConstants.isLabelAlongEdge(edge.getAllAttributes())) { p = getRelativeLabelPosition(edge, p); } else { double x = p.getX(); double y = p.getY(); Point2D p0 = edge.getPoint(0); double p0x = p0.getX(); double p0y = p0.getY(); Point2D vector = edge.getLabelVector(); double dx = vector.getX(); double dy = vector.getY(); double pex = p0.getX() + dx; double pey = p0.getY() + dy; double len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { double u = GraphConstants.PERMILLE; double posy = len * (-y * dx + p0y * dx + x * dy - p0x * dy) / (-pey * dy + p0y * dy - dx * pex + dx * p0x); double posx = u * (-y * pey + y * p0y + p0y * pey - p0y * p0y - pex * x + pex * p0x + p0x * x - p0x * p0x) / (-pey * dy + p0y * dy - dx * pex + dx * p0x); p = new Point2D.Double(posx, posy); } else { p = new Point2D.Double(x - p0.getX(), y - p0.getY()); } } if (label) { edge.setLabelPosition(p); } else { edge.setExtraLabelPosition(currentLabel, p); } edge.update(graph.getGraphLayoutCache()); if (graph.isXorEnabled()) { overlay(graph.getGraphics()); } else { graph.repaint((int) dirty.getX() - 1, (int) dirty.getY() - 1, (int) dirty.getWidth() + 2, (int) dirty.getHeight() + 2); } } } else if (isEditing() && currentPoint != null) { boolean disconnectable = (!source && !target) || (graph.isDisconnectable() && GraphConstants .isDisconnectable(orig.getAllAttributes())); if (source) disconnectable = disconnectable && ((orig.getSource() == null && orig .getSourceParentView() == null) || (orig.getSource() != null && GraphConstants .isDisconnectable(orig.getSource() .getParentView() .getAllAttributes())) || (orig .getSourceParentView() != null && GraphConstants .isDisconnectable(orig .getSourceParentView() .getAllAttributes()))); if (target) disconnectable = disconnectable && ((orig.getTarget() == null && orig .getTargetParentView() == null) || (orig.getTarget() != null && GraphConstants .isDisconnectable(orig.getTarget() .getParentView() .getAllAttributes())) || (orig .getTargetParentView() != null && GraphConstants .isDisconnectable(orig .getTargetParentView() .getAllAttributes()))); // Find Source/Target Port if (!((source && snap(true, event.getPoint())) || (target && snap( false, event.getPoint()))) && disconnectable) { // Else Use Point boolean acceptSource = source && (graph.getModel().acceptsSource(edge.getCell(), null) || graph.isPreviewInvalidNullPorts()); boolean acceptTarget = target && (graph.getModel().acceptsTarget(edge.getCell(), null) || graph.isPreviewInvalidNullPorts()); if (acceptSource || acceptTarget || !(source || target)) { edgeModified = true; if (edge.getSource() != null) { dirty.add(edge.getSource().getParentView() .getBounds()); } if (edge.getTarget() != null) { dirty.add(edge.getTarget().getParentView() .getBounds()); } if (graph.isXorEnabled()) { overlay(graph.getGraphics()); } p = graph.fromScreen(graph.snap(new Point(event .getPoint()))); // Constrained movement if (isConstrainedMoveEvent(event) && currentIndex >= 0) { // Reset Initial Positions EdgeView orig = (EdgeView) graph .getGraphLayoutCache().getMapping( edge.getCell(), false); Point2D origPoint = orig.getPoint(currentIndex); double totDx = p.getX() - origPoint.getX(); double totDy = p.getY() - origPoint.getY(); if (Math.abs(totDx) < Math.abs(totDy)) p.setLocation(origPoint.getX(), p.getY()); else p.setLocation(p.getX(), origPoint.getY()); } // Do not move into negative space p.setLocation(Math.max(0, p.getX()), Math.max(0, p .getY())); currentPoint.setLocation(p); if (source) { edge.setPoint(0, p); edge.setSource(null); } else if (target) { edge.setPoint(edge.getPointCount() - 1, p); edge.setTarget(null); } edge.update(graph.getGraphLayoutCache()); dirty.add(edge.getBounds()); if (graph.isXorEnabled()) { overlay(graph.getGraphics()); } else { if (edge.getSource() != null) { dirty.add(edge.getSource().getParentView() .getBounds()); } if (edge.getTarget() != null) { dirty.add(edge.getTarget().getParentView() .getBounds()); } dirty = graph.toScreen((Rectangle2D) dirty.clone()); graph.repaint((int) dirty.getX(), (int) dirty .getY(), (int) dirty.getWidth(), (int) dirty.getHeight()); } } } else if (!graph.isXorEnabled()) { dirty.add(edge.getBounds()); dirty = graph.toScreen((Rectangle2D) dirty.clone()); graph.repaint((int) dirty.getX(), (int) dirty .getY(), (int) dirty.getWidth(), (int) dirty.getHeight()); } } } protected Point2D getRelativeLabelPosition(EdgeView edge, Point2D p) { int pointCount = edge.getPointCount(); double totalLength = 0; double[] segments = new double[pointCount]; Point2D p0 = edge.getPoint(0); Point2D pt = p0; // Calculate the total length of the edge for (int i = 1; i < pointCount; i++) { Point2D tmp = edge.getPoint(i); if (tmp != null) { double dx = pt.getX() - tmp.getX(); double dy = pt.getY() - tmp.getY(); double segment = Math.sqrt(dx * dx + dy * dy); segments[i - 1] = segment; totalLength += segment; pt = tmp; } } // Work which line segment the point of the label is closest to Point2D last = edge.getPoint(1); Line2D line = new Line2D.Double(p0, last); double minDist = line.ptSegDistSq(p); int index = 0; double tmp = 0; double length = 0; for (int i = 2; i < pointCount; i++) { tmp += segments[i-2]; line = new Line2D.Double(edge.getPoint(i), last); double dist = line.ptSegDistSq(p); if (dist < minDist) { minDist = dist; index = i-1; length = tmp; } last = edge.getPoint(i); } double seg = segments[index]; pt = edge.getPoint(index); double x2 = pt.getX(); double y2 = pt.getY(); Point2D pt2 = edge.getPoint(index+1); double x1 = pt2.getX(); double y1 = pt2.getY(); double px = p.getX(); double py = p.getY(); double xSegment = x2 - x1; double ySegment = y2 - y1; px -= x1; py -= y1; double projlenSq = 0; px = xSegment - px; py = ySegment - py; double dotprod = px * xSegment + py * ySegment; if (dotprod <= 0.0) { projlenSq = 0; } else { projlenSq = dotprod * dotprod / (xSegment * xSegment + ySegment * ySegment); } double projlen = Math.sqrt(projlenSq); if (projlen > seg) { projlen = seg; } double yDistance = Line2D.ptLineDist(pt2.getX(), pt2.getY(), pt.getX(), pt.getY(), p.getX(), p.getY()); int direction = Line2D.relativeCCW(pt2.getX(), pt2.getY(), pt.getX(), pt.getY(), p.getX(), p.getY()); if (direction == -1) { yDistance = -yDistance; } // Constructs the relative point for the label Point2D result = new Point2D.Double(((((totalLength/2 - length - projlen)/ totalLength)*-2)+1)*GraphConstants.PERMILLE / 2, yDistance); // Use the utility method to find Point2D storedRelativePosition = edge.convertRelativeLabelPositionToAbsolute(result); if (p.equals(storedRelativePosition)) { GraphConstants.setRemoveAttributes(edge.getAllAttributes(), new Object[] {GraphConstants.OFFSET}); edge.getAllAttributes().remove(GraphConstants.OFFSET); } else { Point2D off = new Point2D.Double(p.getX() - storedRelativePosition.getX(), p.getY() - storedRelativePosition.getY()); GraphConstants.setOffset(edge.getAllAttributes(), off); } return result; } // Handle mouse released event public void mouseReleased(MouseEvent e) { highlight(graph, null); // removes the highlight boolean clone = e.isControlDown() && graph.isCloneable(); GraphModel model = graph.getModel(); Object source = (edge.getSource() != null) ? edge.getSource() .getCell() : null; Object target = (edge.getTarget() != null) ? edge.getTarget() .getCell() : null; if (edgeModified && model.acceptsSource(edge.getCell(), source) && model.acceptsTarget(edge.getCell(), target)) { // Creates an extra label if the label was cloned if (clone && initialLabelLocation != null) { // Resets the dragging label position and adds a new label // instead. Note: label locations are modified in-place // which is why we need to clone at beginning. Object value = null; Point2D location = null; Object[] extraLabels = GraphConstants.getExtraLabels(edge .getAllAttributes()); if (label) { location = (Point2D) edge.getLabelPosition().clone(); value = graph.convertValueToString(orig); edge.setLabelPosition(initialLabelLocation); } else { location = (Point2D) edge.getExtraLabelPosition( currentLabel).clone(); value = extraLabels[currentLabel]; edge.setExtraLabelPosition(currentLabel, initialLabelLocation); } edge.addExtraLabel(location, value); edge.update(graph.getGraphLayoutCache()); clone = false; } // Creates the data required for the edit/insert call ConnectionSet cs = createConnectionSet(edge, clone); Map nested = GraphConstants.createAttributes( new CellView[] { edge }, null); // The cached points may be different from what's // in the attribute map if the edge is routed. Map tmp = (Map) nested.get(edge.getCell()); List controlPoints = GraphConstants.getPoints(tmp); List currentPoints = edge.getPoints(); // Checks if we're dealing with a routing algorithm // and if we are, replaces only the source and target // in the control point list. if (controlPoints != currentPoints) { controlPoints.set(0, edge.getPoint(0)); controlPoints.set(controlPoints.size() - 1, edge .getPoint(edge.getPointCount() - 1)); } if (clone) { Map cellMap = graph.cloneCells(graph .getDescendants(new Object[] { edge.getCell() })); processNestedMap(nested, true); nested = GraphConstants.replaceKeys(cellMap, nested); cs = cs.clone(cellMap); Object[] cells = cellMap.values().toArray(); graph.getGraphLayoutCache().insert(cells, nested, cs, null, null); } else { processNestedMap(nested, false); graph.getGraphLayoutCache().edit(nested, cs, null, null); } } else { if (graph.isXorEnabled()) { overlay(graph.getGraphics()); } else { Rectangle2D dirty = edge.getBounds(); graph.repaint((int) dirty.getX(), (int) dirty.getY(), (int) dirty.getWidth(), (int) dirty.getHeight()); } edge.refresh(graph.getGraphLayoutCache(), graph.getGraphLayoutCache(), false); } initialLabelLocation = null; currentPoint = null; this.edgeModified = false; this.label = false; this.source = false; this.target = false; currentLabel = -1; currentIndex = -1; firstOverlayCall = true; e.consume(); } protected void processNestedMap(Map nested, boolean clone) { // subclassers can override this to modify the attributes } protected ConnectionSet createConnectionSet(EdgeView view, boolean verbose) { Object edge = view.getCell(); GraphModel model = graph.getModel(); ConnectionSet cs = new ConnectionSet(); Object sourcePort = null, targetPort = null; if (view.getSource() != null) sourcePort = view.getSource().getCell(); else if (view.getSourceParentView() != null) sourcePort = model.getSource(edge); if (view.getTarget() != null) targetPort = view.getTarget().getCell(); else if (view.getTargetParentView() != null) targetPort = model.getTarget(edge); if (view.getTarget() != null) targetPort = view.getTarget().getCell(); if (verbose || (sourcePort != model.getSource(edge) && source)) cs.connect(edge, sourcePort, true); if (verbose || (targetPort != model.getTarget(edge) && target)) cs.connect(edge, targetPort, false); return cs; } // Update control points protected void invalidate() { EdgeView e = relevantEdge; int handlesize = graph.getHandleSize(); EdgeRenderer er = (EdgeRenderer) edge.getRenderer(); Point2D labelPosition = er.getLabelPosition(e); Point2D p = null; if (labelPosition != null) { p = (Point2D)labelPosition.clone(); graph.toScreen(p); } Dimension d = er.getLabelSize(e, graph.convertValueToString(e)); if (p != null && d != null) { Point2D s = graph.toScreen(new Point2D.Double(d.width, d.height)); loc.setFrame(p.getX() - s.getX() / 2, p.getY() - s.getY() / 2, s.getX(), s.getY()); } for (int i = 0; i < r.length; i++) { p = e.getPoint(i); p = graph.toScreen(new Point2D.Double(p.getX(), p.getY())); r[i].setFrame(p.getX() - handlesize, p.getY() - handlesize, 2 * handlesize, 2 * handlesize); } if (extraLabelLocations != null) { for (int i = 0; i < extraLabelLocations.length; i++) { p = er.getExtraLabelPosition(e, i); if (p != null) { p = graph.toScreen((Point2D) p.clone()); d = er.getExtraLabelSize(graph, e, i); if (d != null) { Point2D s = graph.toScreen(new Point2D.Double( d.width, d.height)); extraLabelLocations[i].setFrame(p.getX() - s.getX() / 2, p.getY() - s.getY() / 2, s.getX(), s .getY()); } } } } } } public Point2D getPerimeterPoint(EdgeView edge, Point2D source, Point2D p) { if (getPointCount() > 2) return getPoint(getPointCount() / 2); Point2D p0 = getPoint(0); Point2D pe = getPoint(getPointCount() - 1); return new Point2D.Double((pe.getX() + p0.getX()) / 2, (pe.getY() + p0 .getY()) / 2); } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/DefaultPort.java0000644000175000017500000000470711256667036025046 0ustar gregoagregoa/* * Copyright (c) 2001-2006 Gaudenz Alder * * See LICENSE file in distribution for licensing details of this source file */ package org.jgraph.graph; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /** * A simple implementation for a port. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class DefaultPort extends DefaultGraphCell implements Port { /** Edges that are connected to the port */ // TODO After Java 1.3 is EOL, this could be changed into a LinkHashSet // to retain ordering protected HashSet edges = new HashSet(4, 0.75f); /** Reference to the anchor of this port */ protected Port anchor; /** * Constructs an empty port. */ public DefaultPort() { this(null, null); } /** * Constructs a port that holds a reference to the specified user object. * * @param userObject reference to the user object */ public DefaultPort(Object userObject) { this(userObject, null); } /** * Constructs a port that holds a reference to the specified user object * and a reference to the specified anchor. * * @param userObject reference to the user object * @param anchor reference to a graphcell that constitutes the anchor */ public DefaultPort(Object userObject, Port anchor) { super(userObject); setAllowsChildren(false); this.anchor = anchor; } /** * Returns an iterator of the edges connected * to the port. */ public Iterator edges() { return edges.iterator(); } /** * Adds edge to the list of edges. */ public boolean addEdge(Object edge) { return edges.add(edge); } /** * Removes edge from the list of edges. */ public boolean removeEdge(Object edge) { return edges.remove(edge); } /** * Returns the collection of edges connected to this port. */ public Set getEdges() { return new HashSet(edges); } /** * Sets the collection of edges connected to this port. */ public void setEdges(Set edges) { this.edges = new HashSet(edges); } /** * Returns the anchor of this port. */ public Port getAnchor() { return anchor; } /** * Sets the anchor of this port. */ public void setAnchor(Port port) { anchor = port; } /** * Create a clone of the cell. The cloning of the * user object is deferred to the cloneUserObject() * method. * * @return Object a clone of this object. */ public Object clone() { DefaultPort c = (DefaultPort) super.clone(); c.edges = new HashSet(); return c; } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/DefaultGraphModel.java0000644000175000017500000016405311256667036026145 0ustar gregoagregoa/* * $Id: DefaultGraphModel.java,v 1.25 2009/06/12 13:58:33 david Exp $ * * Copyright (c) 2001-2009 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import javax.swing.event.EventListenerList; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreeNode; import javax.swing.undo.AbstractUndoableEdit; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.CompoundEdit; import javax.swing.undo.UndoableEdit; import javax.swing.undo.UndoableEditSupport; import org.jgraph.event.GraphModelEvent; import org.jgraph.event.GraphModelListener; /** * The default implementation of a graph model. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class DefaultGraphModel extends UndoableEditSupport implements Serializable, GraphModel { /** * The list of listeners that listen to the model. */ protected transient EventListenerList listenerList = new EventListenerList(); /** * Default instance of an empty iterator. */ protected transient Iterator emptyIterator = new EmptyIterator(); /** * Set that contains all root cells of this model. */ protected List roots = null; /** * Indicates whether isLeaf is based on a node's allowsChildren value. */ protected boolean asksAllowsChildren = false; /** * Whether or not to remove group cells from the model when all of their * children are removed */ protected boolean removeEmptyGroups = true; /** * The model's own attributes as a map. Defaults to an empty Hashtable. */ protected AttributeMap attributes = null; /** * Counter for the depth of nested transactions. Each call to beginUpdate * increments this counter and each call to endUpdate decrements it. When * the counter reaches 0, the transaction is closed and applied to the * model. */ protected transient int updateLevel = 0; /** * Stores nested transaction added cells */ protected transient Set transAddedCells = null; /** * Stores nested transaction removed cells */ protected transient Set transRemovedCells = null; /** * Stores nested transaction transport attribute maps */ protected transient Map transEditAttrs = null; /** * Stores nested transaction connection sets */ protected transient ConnectionSet transEditCS = null; /** * Stores nested transaction parent maps */ protected transient ParentMap transEditPM = null; /** * Constructs a model that is not an attribute store. */ public DefaultGraphModel() { this(null, null); } /** * Constructs a model that is not an attribute store. */ public DefaultGraphModel(List roots, AttributeMap attributes) { if (roots != null) this.roots = roots; else this.roots = new ArrayList(); if (attributes != null) this.attributes = attributes; else this.attributes = new AttributeMap(); } /** * Constructs a model using the specified information to construct the * cells, attributes and connection data. */ public DefaultGraphModel(List roots, AttributeMap attributes, ConnectionSet cs) { this(roots, attributes); handleConnectionSet(cs); } public List getRoots() { return roots; } // // Graph Model // /** * Returns the number of roots in the model. Returns 0 if the model is * empty. * * @return the number of roots in the model */ public int getRootCount() { return roots.size(); } /** * Returns the root at index index in the model. This should not * return null if index is a valid index for the model (that is * index >= 0 && index < getRootCount()). * * @return the root of at index index */ public Object getRootAt(int index) { return roots.get(index); } /** * Returns the index of root in the model. If root is * null, returns -1. * * @param root * a root in the model, obtained from this data source * @return the index of the root in the model, or -1 if the parent is * null */ public int getIndexOfRoot(Object root) { return roots.indexOf(root); } /** * Returns true if node or one of its * ancestors is in the model. * * @return true if node is in the model */ public boolean contains(Object node) { Object parentNode = null; while ((parentNode = getParent(node)) != null) node = parentNode; return roots.contains(node); } /** * Returns a Map that represents the attributes for the * specified cell. This attributes have precedence over each view's * attributes, regardless of isAttributeStore. * * @return attributes of node as a Map */ public AttributeMap getAttributes(Object node) { if (node instanceof GraphCell) return ((GraphCell) node).getAttributes(); else if (node == null) return attributes; return null; } /** * @return Returns the user object of the given cell. This implementation * checks if the cell is a default mutable tree node and returns * it's user object. */ public Object getValue(Object cell) { if (cell instanceof DefaultMutableTreeNode) return ((DefaultMutableTreeNode) cell).getUserObject(); return null; } /** * Returns the graph model's attribute. Shortcut to * getAttributes(null). * * @return attributes of node as a Map */ public Map getAttributes() { return getAttributes(null); } // // Graph Structure // /** * Returns the source of edge. edge must be an * object previously obtained from this data source. * * @return Object that represents the source of edge */ public Object getSource(Object edge) { if (edge instanceof Edge) return ((Edge) edge).getSource(); return null; } /** * Returns the target of edge. edge must be an * object previously obtained from this data source. * * @return Object that represents the target of edge */ public Object getTarget(Object edge) { if (edge instanceof Edge) return ((Edge) edge).getTarget(); return null; } /** * Returns true if port is a valid source for * edge. edge and port must be objects * previously obtained from this data source. * * @return true if port is a valid source for * edge. */ public boolean acceptsSource(Object edge, Object port) { return true; } /** * Returns true if port is a valid target for * edge. edge and port must be objects * previously obtained from this data source. * * @return true if port is a valid target for * edge. */ public boolean acceptsTarget(Object edge, Object port) { return true; } /** * Returns an iterator of the edges connected to port. * port must be a object previously obtained from this data source. * This method never returns null. * * @param port * a port in the graph, obtained from this data source * @return Iterator that represents the connected edges */ public Iterator edges(Object port) { if (port instanceof Port) return ((Port) port).edges(); return emptyIterator; } /** * Returns true if edge is a valid edge. * * @return true if edge is a valid edge. */ public boolean isEdge(Object edge) { return edge instanceof Edge; } /** * Returns true if port is a valid port, * possibly supporting edge connection. * * @return true if port is a valid port. */ public boolean isPort(Object port) { return port instanceof Port; } /** * A shortcut method to create a connection set that represents the * connections in this model. Useful for encoding to avoid writing redundant * connection data stored in the cells. */ public ConnectionSet getConnectionSet() { return ConnectionSet .create(this, DefaultGraphModel.getAll(this), false); } // // Group Structure // /** * Returns a map of (cell, clone)-pairs for all cells. In * the new array, all references are replaced with references to the cloned * cells (ie parent or anchor). This method does only include children which * are in cells. Use JGraph.getDescendants to get a complete * list of all children. */ public Map cloneCells(Object[] cells) { Map map = new Hashtable(); // Add Cells to Queue for (int i = 0; i < cells.length; i++) map.put(cells[i], cloneCell(cells[i])); // Replace Parent and Anchors Iterator it = map.entrySet().iterator(); Object obj, cell, parent; while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); obj = entry.getValue(); cell = entry.getKey(); // Replaces the cloned cell's parent with the parent's clone parent = getParent(cell); if (parent != null) parent = map.get(parent); if (parent != null) ((DefaultMutableTreeNode) parent) .add((DefaultMutableTreeNode) obj); // Replaces the anchors for ports if (obj instanceof Port) { Object anchor = ((Port) obj).getAnchor(); if (anchor != null) ((Port) obj).setAnchor((Port) map.get(anchor)); } } return map; } /** * Sets the parent of the specified cell. */ protected void setParent(Object child, Object parent) { if (child instanceof DefaultMutableTreeNode && parent instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) parent; parentNode.add((DefaultMutableTreeNode) child); } } /** * Creates a shallow copy of the cell including a copy of the user object. * Subclassers can override the cloneUserObject to provide a custom user * object cloning mechanism. */ protected Object cloneCell(Object cellObj) { if (cellObj instanceof DefaultGraphCell) { // Clones the cell DefaultGraphCell cell = (DefaultGraphCell) cellObj; DefaultGraphCell clone = (DefaultGraphCell) cell.clone(); // Clones the user object clone.setUserObject(cloneUserObject(cell.getUserObject())); return clone; } return cellObj; } /** * Clones the user object. Helper method that is invoked from cloneCells. * You must use cloneCells (or cloneCell for single cells) to get a deep * copy of a clone. Subclassers must override this and valueForCellChanged * to implement custom user objects. This implementation returns * object. */ protected Object cloneUserObject(Object userObject) { return userObject; } /** * Returns the parent of child in the model. child must be a * node previously obtained from this data source. This returns null if * child is a root in the model. * * @param child * a node in the graph, obtained from this data source * @return the parent of child */ public Object getParent(Object child) { if (child != null && child instanceof TreeNode) return ((TreeNode) child).getParent(); return null; } /** * Returns the index of child in parent. If either the parent or child is * null, returns -1. * * @param parent * a note in the tree, obtained from this data source * @param child * the node we are interested in * @return the index of the child in the parent, or -1 if either the parent * or the child is null */ public int getIndexOfChild(Object parent, Object child) { if (parent == null || child == null) return -1; return ((TreeNode) parent).getIndex((TreeNode) child); } /** * Returns the child of parent at index index in the * parent's child array. parent must be a node previously obtained * from this data source. This should not return null if index is a * valid index for parent (that is index >= 0 && index * < getChildCount( parent )). * * @param parent * a node in the tree, obtained from this data source * @return the child of parent at index index */ public Object getChild(Object parent, int index) { if (parent instanceof TreeNode) return ((TreeNode) parent).getChildAt(index); return null; } /** * Returns the number of children of parent . Returns 0 if the node * is a leaf or if it has no children. parent must be a node * previously obtained from this data source. * * @param parent * a node in the tree, obtained from this data source * @return the number of children of the node parent */ public int getChildCount(Object parent) { if (parent instanceof TreeNode) return ((TreeNode) parent).getChildCount(); return 0; } /** * Returns whether the specified node is a leaf node. The way the test is * performed depends on the. * * @param node * the node to check * @return true if the node is a leaf node */ public boolean isLeaf(Object node) { if (asksAllowsChildren && node instanceof TreeNode) return !((TreeNode) node).getAllowsChildren(); return ((TreeNode) node).isLeaf(); } // // Change Support // /** * Inserts the roots and connections into the model. Notifies * the model- and undo listeners of the change. The passed-in edits are * executed if they implement the * GraphModelEvent.ExecutableGraphChange interface in * ascending array-order, after execution of the model change. Note: The * passed-in propertyMap may contain PortView s which must be * turned into Point s when stored in the model. */ public void insert(Object[] roots, Map attributes, ConnectionSet cs, ParentMap pm, UndoableEdit[] edits) { if (updateLevel > 0) { // Store the insert in the current transaction updateTransaction(roots, null, attributes, cs, pm); } else { // Implement the insert immediately GraphModelEdit edit = createEdit(roots, null, attributes, cs, pm, edits); if (edit != null) { edit.execute(); // fires graphChangeEvent if (edits != null) { for (int i = 0; i < edits.length; i++) if (edits[i] instanceof GraphLayoutCache.GraphLayoutCacheEdit) ((GraphLayoutCache.GraphLayoutCacheEdit) edits[i]) .execute(); } postEdit(edit); // fires undoableedithappened } } } /** * Removes cells from the model. Notifies the model- and undo * listeners of the change. */ public void remove(Object[] roots) { if (updateLevel > 0) { // Store the insert in the current transaction updateTransaction(null, roots, null, null, null); } else { GraphModelEdit edit = createRemoveEdit(roots); if (edit != null) { edit.execute(); postEdit(edit); } } } /** * Shortcut to the new edit method which allows inserts and removes to go * along with an edit. */ public void edit(Map attributes, ConnectionSet cs, ParentMap pm, UndoableEdit[] edits) { edit(null, null, attributes, cs, pm, edits); } /** * Applies attributes and the connection changes to the * model. The initial edits that triggered the call are * considered to be part of this transaction. The passed-in edits are * executed if they implement the * GraphModelEvent.ExecutableGraphChange interface in * ascending array-order, after execution of the model change. Notifies the * model- and undo listeners of the change. Note: If only * edits is non-null, the edits are directly passed to the * UndoableEditListeners. Note: The passed-in propertyMap may contains * PortViews which must be turned into Points when stored in the model. */ public void edit(Object[] inserted, Object[] removed, Map attributes, ConnectionSet cs, ParentMap pm, UndoableEdit[] edits) { if (updateLevel > 0) { // Store the insert in the current transaction updateTransaction(inserted, removed, attributes, cs, pm); } else { if ((inserted == null || inserted.length == 0) && (removed == null || removed.length == 0) && (attributes == null || attributes.isEmpty()) && (cs == null || cs.isEmpty()) && pm == null && edits != null && edits.length == 1) { if (edits[0] instanceof GraphLayoutCache.GraphLayoutCacheEdit) ((GraphLayoutCache.GraphLayoutCacheEdit) edits[0]).execute(); postEdit(edits[0]); // UndoableEdit Relay } else { GraphModelEdit edit = createEdit(inserted, removed, attributes, cs, pm, edits); if (edit != null) { edit.execute(); if (edits != null) { for (int i = 0; i < edits.length; i++) if (edits[i] instanceof GraphLayoutCache.GraphLayoutCacheEdit) ((GraphLayoutCache.GraphLayoutCacheEdit) edits[i]) .execute(); } postEdit(edit); } } } } /* * Unused, placeholder for JGraph 6 API */ public synchronized void execute(ExecutableChange change) { } /* * Read section entitled "Complex Transactions" in the user manual * chapter 2 for how to use the update level */ public int getUpdateLevel() { return updateLevel; } /* * Read section entitled "Complex Transactions" in the user manual * chapter 2 for how to use the update level */ public void beginUpdate() { updateLevel++; if (updateLevel == 1) { transEditAttrs = new Hashtable(); transEditCS = new ConnectionSet(); transEditPM = new ParentMap(); transAddedCells = new HashSet(); transRemovedCells = new HashSet(); } } /* * Read section entitled "Complex Transactions" in the user manual * chapter 2 for how to use the update level */ public void endUpdate() { updateLevel--; if (updateLevel == 0) { // Dispatch the built up transaction GraphModelEdit edit = createEdit(transAddedCells.toArray(), transRemovedCells.toArray(), transEditAttrs, transEditCS, transEditPM, null); if (edit != null) { edit.execute(); // fires graphChangeEvent postEdit(edit); // fires undoableedithappened } } } /** * Updates the current state of the various transaction data * @param inserted inserted cell to be added to the transaction * @param removed removed cells to be removed from the transaction * @param attributes nested attribute maps to apply to the transaction * @param cs connection sets to add to the transaction * @param pm parent maps to add to the transaction */ protected void updateTransaction(Object[] inserted, Object[] removed, Map attributes, ConnectionSet cs, ParentMap pm) { // Inserts if (inserted != null && inserted.length > 0) { for (int i = 0; i < inserted.length; i++) { if (transRemovedCells.contains(inserted[i])) { // Does not make sense to remove then insert a cell // in same transaction, operations cancel out transRemovedCells.remove(inserted[i]); } else { transAddedCells.add(inserted[i]); } } } // Removes if (removed != null && removed.length > 0) { for (int i = 0; i < removed.length; i++) { if (transAddedCells.contains(removed[i])) { // Does not make sense to insert then remove a cell // in same transaction, operations cancel out transAddedCells.remove(removed[i]); } else { transRemovedCells.add(removed[i]); } } } // Attributes if (attributes != null) { GraphConstants.merge(attributes, transEditAttrs); } // Connection sets if (cs != null) { Set connections = transEditCS.getConnections(); connections.addAll(cs.getConnections()); transEditCS.setConnections(connections); Set edges = transEditCS.getEdges(); edges.addAll(cs.getEdges()); transEditCS.setEdges(edges); } // Parent maps if (pm != null) { Iterator entries = pm.entries(); while (entries.hasNext()) { ParentMap.Entry entry = (ParentMap.Entry)entries.next(); transEditPM.addEntry(entry.getChild(), entry.getParent()); } } } /** * Sends cells to back. */ public void toBack(Object[] cells) { GraphModelLayerEdit edit = createLayerEdit(cells, GraphModelLayerEdit.BACK); if (edit != null) { edit.execute(); postEdit(edit); } } /** * Brings cells to front. */ public void toFront(Object[] cells) { GraphModelLayerEdit edit = createLayerEdit(cells, GraphModelLayerEdit.FRONT); if (edit != null) { edit.execute(); postEdit(edit); } } protected GraphModelLayerEdit createLayerEdit(Object[] cells, int layer) { return new GraphModelLayerEdit(cells, layer); } // // Edit Creation // /** * Returns an edit that represents an insert. */ // protected GraphModelEdit createInsertEdit(Object[] cells, Map // attributeMap, // ConnectionSet cs, ParentMap pm, UndoableEdit[] edits) { // return createEdit(cells, null, attributeMap, cs, pm, edits); // } /** * Returns an edit that represents a remove. */ protected GraphModelEdit createRemoveEdit(Object[] cells) { // Remove from GraphStructure ConnectionSet cs = ConnectionSet.create(this, cells, true); // Remove from Group Structure ParentMap pm = ParentMap.create(this, cells, true, false); // Construct Edit GraphModelEdit edit = createEdit(null, cells, null, cs, pm, null); if (edit != null) edit.end(); return edit; } protected GraphModelEdit createEdit(Object[] inserted, Object[] removed, Map attributes, ConnectionSet cs, ParentMap pm, UndoableEdit[] edits) { GraphModelEdit edit = new GraphModelEdit(inserted, removed, attributes, cs, pm); if (edit != null) { if (edits != null) for (int i = 0; i < edits.length; i++) edit.addEdit(edits[i]); edit.end(); } return edit; } // // Change Handling // /** * Inserts cells into the model. Returns the cells that were * inserted (including descendants). */ protected Object[] handleInsert(Object[] cells) { Object[] inserted = null; if (cells != null) { for (int i = 0; i < cells.length; i++) // Add to Roots if no parent if (getParent(cells[i]) == null) roots.add(cells[i]); // Return *all* inserted cells inserted = getDescendants(this, cells).toArray(); } return inserted; } /** * Removes cells from the model. Returns the cells that were * removed as roots. */ protected Object[] handleRemove(Object[] cells) { Set removedRoots = new HashSet(); if (cells != null && cells.length > 0) { Set rootsSet = new HashSet(roots); for (int i = 0; i < cells.length; i++){ if (getParent(cells[i]) == null && rootsSet.contains(cells[i])) { removedRoots.add(cells[i]); } } if (removedRoots.size() > 0) { // If any roots have been removed, reform the roots // lists appropriately, keeping the order the same int newRootsSize = roots.size() - removedRoots.size(); if (newRootsSize < 8) { newRootsSize = 8; } List newRoots = new ArrayList(newRootsSize); Iterator iter = roots.iterator(); while (iter.hasNext()) { Object cell = iter.next(); if (!removedRoots.contains(cell)) { newRoots.add(cell); } } roots = newRoots; } } return removedRoots.toArray(); } /** * Applies cells to the model. Returns a parent map that may * be used to undo this change. */ protected ParentMap handleParentMap(ParentMap parentMap) { if (parentMap != null) { ParentMap undo = new ParentMap(); HashSet rootsSet = null; HashSet rootsToBeRemoved = null; Iterator it = parentMap.entries(); while (it.hasNext()) { ParentMap.Entry entry = (ParentMap.Entry) it.next(); Object child = entry.getChild(); Object parent = entry.getParent(); undo.addEntry(child, getParent(child)); if (parent == null) { if (child instanceof MutableTreeNode) { ((MutableTreeNode) child).removeFromParent(); } } else { if (parent instanceof DefaultMutableTreeNode && child instanceof MutableTreeNode) { ((DefaultMutableTreeNode) parent) .add((MutableTreeNode) child); } } if (rootsSet == null) { rootsSet = new HashSet(roots); } boolean isRoot = rootsSet.contains(child); if (parent == null && !isRoot) { rootsSet.add(child); roots.add(child); } else if (parent != null && isRoot) { if (rootsToBeRemoved == null) { rootsToBeRemoved = new HashSet(); } rootsSet.remove(child); rootsToBeRemoved.add(child); } } if (rootsToBeRemoved != null && rootsToBeRemoved.size() > 0) { // If any roots have been removed, reform the roots // lists appropriately, keeping the order the same int newRootsSize = roots.size() - rootsToBeRemoved.size(); if (newRootsSize < 8) { newRootsSize = 8; } List newRoots = new ArrayList(newRootsSize); Iterator iter = roots.iterator(); while (iter.hasNext()) { Object cell = iter.next(); if (!rootsToBeRemoved.contains(cell)) { newRoots.add(cell); } } roots = newRoots; } return undo; } return null; } /** * Applies attributes to the cells specified as keys. Returns * the attributes to undo the change. */ protected Map handleAttributes(Map attributes) { if (attributes != null) { Hashtable undo = new Hashtable(attributes.size()); Iterator it = attributes.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); Object cell = entry.getKey(); Map deltaNew = (Map) entry.getValue(); // System.out.println("deltaNew="+deltaNew); // System.out.println("stateOld="+getAttributes(cell)); // Handle New Values Map deltaOld = null; AttributeMap attr = getAttributes(cell); if (attr != null) { deltaOld = attr.applyMap(deltaNew); // System.out.println("stateNew="+getAttributes(cell)); // System.out.println("deltaOld="+deltaOld); undo.put(cell, deltaOld); } else { // Make room for the value deltaOld = new Hashtable(2); } // Handle new values Object newValue = deltaNew.get(GraphConstants.VALUE); if (newValue != null) { Object oldValue = valueForCellChanged(cell, newValue); if (oldValue != null) GraphConstants.setValue(deltaOld, oldValue); // TODO: Userobject of null is probably invalid else GraphConstants.setRemoveAttributes(deltaOld, new Object[] { GraphConstants.VALUE }); } else { // Special case to handle removal of value attribute Object[] remove = GraphConstants .getRemoveAttributes(deltaNew); if (remove != null && remove.length > 0) { for (int i = 0; i < remove.length; i++) { if (remove[i] == GraphConstants.VALUE) { Object oldValue = valueForCellChanged(cell, null); if (oldValue != null) { GraphConstants.setValue(deltaOld, oldValue); } } } } } } return undo; } return null; } /** * Applies the new value to the specified cell. Unfortunately for cloning * the user object you must still override the attribute map and provide a * custom cloneUserObject method. This is because the cloning of a cell is * local to the cell, which in turn has a reference to its attribute map. * * @param cell * @param newValue * @return the old value for the cell, if any */ public Object valueForCellChanged(Object cell, Object newValue) { if (cell instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) cell; Object oldValue = node.getUserObject(); node.setUserObject(newValue); return oldValue; } return null; } // // Connection Set Handling // /** * Applies connectionSet to the model. Returns a connection * set that may be used to undo this change. */ protected ConnectionSet handleConnectionSet(ConnectionSet cs) { if (cs != null) { ConnectionSet csundo = new ConnectionSet(); Iterator it = cs.connections(); while (it.hasNext()) { ConnectionSet.Connection c = (ConnectionSet.Connection) it .next(); Object edge = c.getEdge(); if (c.isSource()) csundo.connect(edge, getSource(edge), true); else csundo.connect(edge, getTarget(edge), false); handleConnection(c, false); } // When removing edges it is possible that an edge is // removed in a later step which has been added in a // previous connection establishment (set semantic). // Therefore, we first need to remove all old connections // and then add all new connections in two steps. it = cs.connections(); while (it.hasNext()) handleConnection((ConnectionSet.Connection) it.next(), true); return csundo; } return null; } /** * Inserts the specified connection into the model. */ protected void handleConnection(ConnectionSet.Connection c, boolean establish) { Object edge = c.getEdge(); Object port = (establish) ? c.getPort() : (c.isSource()) ? getSource(edge) : getTarget(edge); connect(edge, port, c.isSource(), establish); } /** * Connects or disconnects the edge and port in this model based on * remove. Subclassers should override this to update * connectivity datastructures. */ protected void connect(Object edge, Object port, boolean isSource, boolean insert) { if (port instanceof Port) if (insert) ((Port) port).addEdge(edge); // Only removes if opposite is not // connected to same port else if ((isSource) ? getTarget(edge) != port : getSource(edge) != port) ((Port) port).removeEdge(edge); if (!insert) port = null; if (edge instanceof Edge) { if (isSource) ((Edge) edge).setSource(port); else ((Edge) edge).setTarget(port); } } // // GraphModelListeners // /** * Adds a listener for the GraphModelEvent posted after the graph changes. * * @see #removeGraphModelListener * @param l * the listener to add */ public void addGraphModelListener(GraphModelListener l) { listenerList.add(GraphModelListener.class, l); } /** * Removes a listener previously added with addGraphModelListener() . * * @see #addGraphModelListener * @param l * the listener to remove */ public void removeGraphModelListener(GraphModelListener l) { listenerList.remove(GraphModelListener.class, l); } /** * Invoke this method after you've changed how the cells are to be * represented in the graph. */ public void cellsChanged(final Object[] cells) { if (cells != null) { fireGraphChanged(this, new GraphModelEvent.GraphModelChange() { public Object[] getInserted() { return null; } public Object[] getRemoved() { return null; } public Map getPreviousAttributes() { return null; } public ConnectionSet getConnectionSet() { return null; } public ConnectionSet getPreviousConnectionSet() { return null; } public ParentMap getParentMap() { return null; } public ParentMap getPreviousParentMap() { return null; } public void putViews(GraphLayoutCache view, CellView[] cellViews) { } public CellView[] getViews(GraphLayoutCache view) { return null; } public Object getSource() { return this; } public Object[] getChanged() { return cells; } public Map getAttributes() { return null; } public Object[] getContext() { return null; } public Rectangle2D getDirtyRegion() { return null; } public void setDirtyRegion(Rectangle2D dirty) { } }); } } /* * Notify all listeners that have registered interest for notification on * this event type. The event instance is lazily created using the * parameters passed into the fire method. * * @see EventListenerList */ protected void fireGraphChanged(Object source, GraphModelEvent.GraphModelChange edit) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); GraphModelEvent e = null; // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == GraphModelListener.class) { // Lazily create the event: if (e == null) e = new GraphModelEvent(source, edit); ((GraphModelListener) listeners[i + 1]).graphChanged(e); } } } /** * Return an array of all GraphModelListeners that were added to this model. */ public GraphModelListener[] getGraphModelListeners() { return (GraphModelListener[]) listenerList .getListeners(GraphModelListener.class); } // // GraphModelEdit // /** * An implementation of GraphModelChange that can be added to the model * event. */ public class GraphModelEdit extends CompoundEdit implements GraphModelEvent.GraphModelChange { /* Cells that were inserted/removed/changed during the last execution. */ protected Object[] insert, changed, remove, context; /* Cells that were inserted/removed/changed during the last execution. */ protected Object[] inserted, removed; /* * Property map for the next execution. Attribute Map is passed to the * views on inserts. */ protected Map attributes, previousAttributes; /* Parent map for the next execution. */ protected ParentMap parentMap, previousParentMap; /** The dirty region of the change prior to it happening */ protected Rectangle2D dirtyRegion = null; /* ConnectionSet for the next execution. */ protected ConnectionSet connectionSet, previousConnectionSet; /* Piggybacked undo from the views. */ protected Map cellViews = new Hashtable(); /** * Constructs an edit record. * * @param inserted * a set of roots that were inserted * @param removed * a set of elements that were removed * @param attributes * the attribute changes made by the edit * @param connectionSet * the set of changed connections * @param parentMap * the map of changed parents */ public GraphModelEdit(Object[] inserted, Object[] removed, Map attributes, ConnectionSet connectionSet, ParentMap parentMap) { super(); this.insert = inserted; this.remove = removed; this.connectionSet = connectionSet; this.attributes = attributes; this.parentMap = parentMap; previousAttributes = null; previousConnectionSet = connectionSet; previousParentMap = parentMap; // Remove Empty Parents if (parentMap != null) { // Compute Empty Group Map childCount = new Hashtable(); Iterator it = parentMap.entries(); while (it.hasNext()) { ParentMap.Entry entry = (ParentMap.Entry) it.next(); Object child = entry.getChild(); if (!isPort(child)) { Object oldParent = getParent(child); Object newParent = entry.getParent(); if (oldParent != newParent) { changeChildCount(childCount, oldParent, -1); changeChildCount(childCount, newParent, 1); } } } handleEmptyGroups(filterParents(childCount, 0)); } } public Object[] filterParents(Map childCount, int children) { ArrayList list = new ArrayList(); Iterator it = childCount.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); if (entry.getValue() instanceof Integer) { if (((Integer) entry.getValue()).intValue() == children) list.add(entry.getKey()); } } return list.toArray(); } protected void changeChildCount(Map childCount, Object parent, int change) { if (parent != null) { Integer count = (Integer) childCount.get(parent); if (count == null) { count = new Integer(getChildCount(parent)); } int newValue = count.intValue() + change; childCount.put(parent, new Integer(newValue)); } } /** * Adds the groups that become empty to the cells that will be removed. * (Auto remove empty cells.) Removed cells will be re-inserted on undo, * and the parent- child relations will be restored. */ protected void handleEmptyGroups(Object[] groups) { if (removeEmptyGroups) { if (groups != null && groups.length > 0) { if (remove == null) remove = new Object[] {}; Object[] tmp = new Object[remove.length + groups.length]; System.arraycopy(remove, 0, tmp, 0, remove.length); System.arraycopy(groups, 0, tmp, remove.length, groups.length); remove = tmp; } } } public boolean isSignificant() { return true; } /** * Returns the source of this change. This can either be a view or a * model, if this change is a GraphModelChange. */ public Object getSource() { return DefaultGraphModel.this; } /** * Returns the cells that have changed. This includes the cells that * have been changed through a call to getAttributes and the edges that * have been changed with the ConnectionSet. */ public Object[] getChanged() { return changed; } /** * Returns the objects that have not changed explicitly, but implicitly * because one of their dependent cells has changed. */ public Object[] getContext() { return context; } /** * Returns the cells that were inserted. */ public Object[] getInserted() { return inserted; } /** * Returns the cells that were inserted. */ public Object[] getRemoved() { return removed; } /** * Returns a map that contains (object, map) pairs of the attributes * that have been stored in the model. */ public Map getPreviousAttributes() { return previousAttributes; } /** * Returns a map of (object, view attributes). The objects are model * objects which need to be mapped to views. */ public Map getAttributes() { return attributes; } /** * Returns the connectionSet. * * @return ConnectionSet */ public ConnectionSet getConnectionSet() { return connectionSet; } public ConnectionSet getPreviousConnectionSet() { return previousConnectionSet; } /** * Returns the parentMap. * * @return ParentMap */ public ParentMap getParentMap() { return parentMap; } public ParentMap getPreviousParentMap() { return previousParentMap; } public Rectangle2D getDirtyRegion() { return dirtyRegion; } public void setDirtyRegion(Rectangle2D dirty) { this.dirtyRegion = dirty; } /** * Redoes a change. * * @exception CannotRedoException * if the change cannot be redone */ public void redo() throws CannotRedoException { super.redo(); execute(); } /** * Undoes a change. * * @exception CannotUndoException * if the change cannot be undone */ public void undo() throws CannotUndoException { super.undo(); execute(); } /** * Execute this edit such that the next invocation to this method will * invert the last execution. */ public void execute() { // dirtyRegion = null; // Compute Changed Cells Set tmp = new HashSet(); if (attributes != null) tmp.addAll(attributes.keySet()); if (parentMap != null) tmp.addAll(parentMap.getChangedNodes()); // Note: One must also include the previous parents! if (connectionSet != null) tmp.addAll(connectionSet.getChangedEdges()); if (remove != null) { for (int i = 0; i < remove.length; i++) tmp.remove(remove[i]); } changed = tmp.toArray(); // Context cells Set ctx = getEdges(DefaultGraphModel.this, changed); context = ctx.toArray(); // Do Execute inserted = insert; removed = remove; remove = handleInsert(inserted); previousParentMap = parentMap; parentMap = handleParentMap(parentMap); // Adds previous parents if (parentMap != null) tmp.addAll(parentMap.getChangedNodes()); previousConnectionSet = connectionSet; connectionSet = handleConnectionSet(connectionSet); insert = handleRemove(removed); previousAttributes = attributes; attributes = handleAttributes(attributes); changed = tmp.toArray(); // Fire Event fireGraphChanged(DefaultGraphModel.this, this); } public void putViews(GraphLayoutCache view, CellView[] views) { if (view != null && views != null) cellViews.put(view, views); } public CellView[] getViews(GraphLayoutCache view) { return (CellView[]) cellViews.get(view); } public String toString() { String s = new String(); if (inserted != null) { s += "Inserted:\n"; for (int i = 0; i < inserted.length; i++) s += " " + inserted[i] + "\n"; } else s += "None inserted\n"; if (removed != null) { s += "Removed:\n"; for (int i = 0; i < removed.length; i++) s += " " + removed[i] + "\n"; } else s += "None removed\n"; if (changed != null && changed.length > 0) { s += "Changed:\n"; for (int i = 0; i < changed.length; i++) s += " " + changed[i] + "\n"; } else s += "None changed\n"; if (parentMap != null) s += parentMap.toString(); else s += "No parent map\n"; return s; } } /** * An implementation of GraphViewChange. */ public class GraphModelLayerEdit extends AbstractUndoableEdit implements GraphModelEvent.GraphModelChange { public static final int FRONT = -1, BACK = -2; protected Object changeSource; protected transient Object[] cells; protected transient int[] next, prev; protected int layer; // The cell that change are the parents, because they need to // reload their childs for reordering! protected Object[] changed; /** * Constructs a GraphModelEdit. This modifies the order of the cells in * the model. */ public GraphModelLayerEdit(Object[] cells, int layer) { this.cells = cells; this.layer = layer; next = new int[cells.length]; prev = new int[cells.length]; updateNext(); // Compute array of changed cells (roots or parents of cells) Set par = new HashSet(); for (int i = 0; i < cells.length; i++) { Object cell = DefaultGraphModel.this.getParent(cells[i]); if (cell == null) cell = cells[i]; par.add(cell); } changed = par.toArray(); } protected void updateNext() { for (int i = 0; i < next.length; i++) next[i] = layer; } /** * Returns the source of this change. This can either be a view or a * model, if this change is a GraphModelChange. */ public Object getSource() { return DefaultGraphModel.this; } /** * Returns the cells that have changed. */ public Object[] getChanged() { return changed; } /** * Returns the cells that have changed. */ public Object[] getInserted() { return null; } /** * Returns the cells that have changed. */ public Object[] getRemoved() { return null; } /** * Returns null. */ public Object[] getContext() { return null; } /** * Returns null. */ public Map getAttributes() { return null; } /** * Returns null. */ public Map getPreviousAttributes() { return null; } public ConnectionSet getConnectionSet() { return null; } public ConnectionSet getPreviousConnectionSet() { return null; } /** * Returns null. */ public ParentMap getParentMap() { return null; } public ParentMap getPreviousParentMap() { return null; } public Rectangle2D getDirtyRegion() { return null; } public void setDirtyRegion(Rectangle2D dirty) { } /** * Allows a GraphLayoutCache to add and execute and * UndoableEdit in this change. This does also work if the parent edit * has already been executed, in which case the to be added edit will be * executed immediately, after addition. This is used to handle changes * to the view that are triggered by certain changes of the model. Such * implicit edits may be associated with the view so that they may be * undone and redone correctly, and are stored in the model's global * history together with the parent event as one unit. */ public void addImplicitEdit(UndoableEdit edit) { // ignore } /** * Returns the views that have not changed explicitly, but implicitly * because one of their dependent cells has changed. */ public CellView[] getViews(GraphLayoutCache view) { return null; } /** * Returns the views that have not changed explicitly, but implicitly * because one of their dependent cells has changed. */ public void putViews(GraphLayoutCache view, CellView[] cellViews) { // ignore } /** * Redoes a change. * * @exception CannotRedoException * if the change cannot be redone */ public void redo() throws CannotRedoException { super.redo(); updateNext(); execute(); } /** * Undoes a change. * * @exception CannotUndoException * if the change cannot be undone */ public void undo() throws CannotUndoException { super.undo(); execute(); } /** * Execute this edit such that the next invocation to this method will * invert the last execution. */ public void execute() { for (int i = 0; i < cells.length; i++) { List list = getParentList(cells[i]); if (list != null) { prev[i] = list.indexOf(cells[i]); if (prev[i] >= 0) { list.remove(prev[i]); int n = next[i]; if (n == FRONT) n = list.size(); else if (n == BACK) n = 0; list.add(n, cells[i]); next[i] = prev[i]; } } } updateListeners(); } protected void updateListeners() { fireGraphChanged(DefaultGraphModel.this, this); } /** * Returns the list that exclusively contains view. */ protected List getParentList(Object cell) { List list = null; if (cell instanceof DefaultMutableTreeNode) { Object parent = ((DefaultMutableTreeNode) cell).getParent(); if (parent instanceof DefaultGraphCell) list = ((DefaultGraphCell) parent).getChildren(); else list = roots; } return list; } } // // Static Methods // /** * Returns a deep clone of the specified cell, including all children. */ public static Object cloneCell(GraphModel model, Object cell) { Map clones = model.cloneCells(getDescendants(model, new Object[] { cell }).toArray()); return clones.get(cell); } /** * Returns a deep clone of the specified cells, including all children. */ public static Object[] cloneCell(GraphModel model, Object[] cells) { Map clones = model.cloneCells(getDescendants(model, cells).toArray()); for (int i = 0; i < cells.length; i++) { cells[i] = clones.get(cells[i]); } return cells; } /** * Helper methods that connects the source of edge to * port in model. */ public static void setSourcePort(GraphModel model, Object edge, Object port) { model.edit(null, new ConnectionSet(edge, port, true), null, null); } /** * Helper methods that connects the source of edge to * port in model. */ public static void setTargetPort(GraphModel model, Object edge, Object port) { model.edit(null, new ConnectionSet(edge, port, false), null, null); } /** * Returns the source vertex of the edge by calling getParent on getSource * on the specified model. */ public static Object getSourceVertex(GraphModel model, Object edge) { if (model != null) return model.getParent(model.getSource(edge)); return null; } /** * Returns the target vertex of the edge by calling getParent on getTarget * on the specified model. */ public static Object getTargetVertex(GraphModel model, Object edge) { if (model != null) return model.getParent(model.getTarget(edge)); return null; } /** * @return Returns the user object of the given cell. This implementation * checks if the cell is a default mutable tree node and returns * it's user object. * * @deprecated Use {@link GraphModel#getValue(Object)} instead. */ public static Object getUserObject(Object cell) { if (cell instanceof DefaultMutableTreeNode) return ((DefaultMutableTreeNode) cell).getUserObject(); return null; } /** * Checks whether the cell has at least one child which is not a port. This * implementation operates on the model, not taking into account visibility * of cells. It returns true for groups regardless of their folded state. * * @param cell * the cell to check for being a group * @return Returns true if the cell contains at least one cell which is not * a port */ public static boolean isGroup(GraphModel model, Object cell) { for (int i = 0; i < model.getChildCount(cell); i++) { if (!model.isPort(model.getChild(cell, i))) return true; } return false; } /** * Returns all cells of the model in an array. * * @see #getDescendants(GraphModel, Object[]) * * @return Returns all cells in the model including all descandants. */ public static Object[] getAll(GraphModel model) { return getDescendants(model, getRoots(model)).toArray(); } /** * Returns the roots of the specified model as an array. This implementation * uses the GraphModel interface in the general case, but if the model is a * DefaultGraphModel the performance can be improved to * linear time. */ public static Object[] getRoots(GraphModel model) { Object[] cells = null; if (model != null) { // If model is DefaultGraphModel, we can do a linear time getRoots if (model instanceof DefaultGraphModel) { cells = ((DefaultGraphModel) model).getRoots().toArray(); } else { cells = new Object[model.getRootCount()]; for (int i = 0; i < cells.length; i++) { cells[i] = model.getRootAt(i); } } } return cells; } /** * Returns the roots of the specified model as a collection. This implementation * uses the GraphModel interface in the general case, but if the model is a * DefaultGraphModel the performance can be improved to * linear time. */ public static Collection getRootsAsCollection(GraphModel model) { Collection cells = null; if (model != null) { // If model is DefaultGraphModel, we can do a linear time getRoots if (model instanceof DefaultGraphModel) { cells = ((DefaultGraphModel) model).getRoots(); } else { cells = new LinkedHashSet(model.getRootCount()); for (int i = 0; i < cells.size(); i++) { cells.add(model.getRootAt(i)); } } } return cells; } /** * Returns the roots in cells by checking if their parent is * null. This implementation only uses the GraphModel * interface. This method never returns null. */ public static Object[] getRoots(GraphModel model, Object[] cells) { List roots = new ArrayList(); if (cells != null) { for (int i = 0; i < cells.length; i++) { if (model.getParent(cells[i]) == null) { roots.add(cells[i]); } } } return roots.toArray(); } /** * @return Returns the roots of cells, eg. an array that contains no cell * having an ancestor in cells. */ public static Object[] getTopmostCells(GraphModel model, Object[] cells) { Set cellSet = new HashSet(); for (int i = 0; i < cells.length; i++) cellSet.add(cells[i]); List parents = new ArrayList(); for (int i = 0; i < cells.length; i++) { if (!hasAncestorIn(model, cellSet, cells[i])) parents.add(cells[i]); } return parents.toArray(); } /** * Returns true if the specified child has an ancestor in parents. */ public static boolean hasAncestorIn(GraphModel model, Set parents, Object child) { Object parent = model.getParent(child); while (parent != null) { if (parents.contains(parent)) return true; parent = model.getParent(parent); } return false; } /** * Flattens the given array of root cells by adding the roots and their * descandants. The resulting set contains all cells, which means it * contains branches and leafs. Note: This is an iterative * implementation. No recursion used.
    * Note: This returns a linked list, for frequent read operations you should * turn this into an array, or at least an array list. */ public static List getDescendants(GraphModel model, Object[] cells) { if (cells != null) { Stack stack = new Stack(); for (int i = cells.length - 1; i >= 0; i--) stack.add(cells[i]); LinkedList result = new LinkedList(); while (!stack.isEmpty()) { Object tmp = stack.pop(); for (int i = model.getChildCount(tmp) - 1; i >= 0; i--) stack.add(model.getChild(tmp, i)); if (tmp != null) result.add(tmp); } return result; } return null; } /** * Orders cells so that they reflect the model order. */ public static Object[] order(GraphModel model, Object[] cells) { if (cells != null) { Set cellSet = new HashSet(); for (int i = 0; i < cells.length; i++) cellSet.add(cells[i]); Stack stack = new Stack(); for (int i = model.getRootCount() - 1; i >= 0; i--) stack.add(model.getRootAt(i)); LinkedList result = new LinkedList(); while (!stack.isEmpty()) { Object tmp = stack.pop(); for (int i = model.getChildCount(tmp) - 1; i >= 0; i--) stack.add(model.getChild(tmp, i)); if (cellSet.remove(tmp)) result.add(tmp); } return result.toArray(); } return null; } /** * Returns the set of all connected edges to cells or their * descendants. The passed-in cells are never returned as part of the result * set. This can be used on vertices, edges and ports. */ public static Set getEdges(GraphModel model, Object[] cells) { Set result = new LinkedHashSet(); if (cells != null) { // We know the minimum initial capacity of this set is cells.length // We assume the cell has one port at a minimum int setSize = ((int)(cells.length * 1.33) + 1); Set allCells = new HashSet(setSize, 0.75f); for (int i = 0; i < cells.length; i++) { allCells.add(cells[i]); } // Include descendants List descendants = getDescendants(model, cells); // Iterate through the list rather than adding all to preserve order Iterator desIter = descendants.iterator(); while (desIter.hasNext()) { allCells.add(desIter.next()); } if (allCells != null) { Iterator it = allCells.iterator(); while (it.hasNext()) { Iterator edges = model.edges(it.next()); while (edges.hasNext()) result.add(edges.next()); } for (int i = 0; i < cells.length; i++) result.remove(cells[i]); } } return result; } /** * @return Returns the opposite port or vertex in edge. */ public static Object getOpposite(GraphModel model, Object edge, Object cell) { boolean isPort = model.isPort(cell); Object source = (isPort) ? model.getSource(edge) : getSourceVertex( model, edge); if (cell == source) return (isPort) ? model.getTarget(edge) : getTargetVertex(model, edge); else return source; } /** * Returns true if the given vertices are conntected by a single edge in * this document. */ public static boolean containsEdgeBetween(GraphModel model, Object v1, Object v2) { Object[] edges = getEdgesBetween(model, v1, v2, false); return (edges != null && edges.length > 0); } /** * Returns the edges between two specified ports or two specified vertices. * If directed is true then cell1 must be the source of the * returned edges. This method never returns null. If there are no edges * between the specified cells, then an array of length 0 is returned. */ public static Object[] getEdgesBetween(GraphModel model, Object cell1, Object cell2, boolean directed) { boolean isPort1 = model.isPort(cell1); boolean isPort2 = model.isPort(cell2); ArrayList result = new ArrayList(); Set edges = DefaultGraphModel.getEdges(model, new Object[] { cell1 }); Iterator it = edges.iterator(); while (it.hasNext()) { Object edge = it.next(); // TODO: Handle edge groups Object source = (isPort1) ? model.getSource(edge) : getSourceVertex(model, edge); Object target = (isPort2) ? model.getTarget(edge) : getTargetVertex(model, edge); if ((source == cell1 && target == cell2) || (!directed && source == cell2 && target == cell1)) result.add(edge); } return result.toArray(); } /** * Returns the outgoing edges for cell. Cell should be a port or a vertex. */ public static Object[] getOutgoingEdges(GraphModel model, Object cell) { return getEdges(model, cell, false); } /** * Returns the incoming edges for cell. Cell should be a port or a vertex. */ public static Object[] getIncomingEdges(GraphModel model, Object cell) { return getEdges(model, cell, true); } /** * Returns the incoming or outgoing edges for cell. Cell should be a port or * a vertex. */ public static Object[] getEdges(GraphModel model, Object cell, boolean incoming) { Set edges = DefaultGraphModel.getEdges(model, new Object[] { cell }); // Base initial capacity on size of set, it can't be any larger ArrayList result = new ArrayList(edges.size()); Iterator it = edges.iterator(); while (it.hasNext()) { Object edge = it.next(); // TODO: Handle edge groups Object port = (incoming) ? model.getTarget(edge) : model .getSource(edge); Object parent = model.getParent(port); if (port == cell || parent == cell) result.add(edge); } return result.toArray(); } /** * Returns true if vertex is a valid vertex. * * @return true if vertex is a valid vertex. */ public static boolean isVertex(GraphModel model, Object vertex) { return (vertex != null && !model.isEdge(vertex) && !model.isPort(vertex)); } // Serialization support private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); listenerList = new EventListenerList(); emptyIterator = new EmptyIterator(); } public static class EmptyIterator implements Iterator, Serializable { public boolean hasNext() { return false; } public Object next() { return null; } public void remove() { // nop } } /** * @return the removeEmptyGroups */ public boolean isRemoveEmptyGroups() { return removeEmptyGroups; } /** * @param removeEmptyGroups the removeEmptyGroups to set */ public void setRemoveEmptyGroups(boolean removeEmptyGroups) { this.removeEmptyGroups = removeEmptyGroups; } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/DefaultCellViewFactory.java0000644000175000017500000000445411256667036027163 0ustar gregoagregoa/* * @(#)DefaultCellViewFactory.java 1.0 29-NOV-04 * * Copyright (c) 2001-2005 Gaudenz Alder * */ package org.jgraph.graph; import java.io.Serializable; /** * The default implementation of a cell view factory that returns the default * views for vertices, edges and ports. */ public class DefaultCellViewFactory implements CellViewFactory, Serializable { /** * Constructs a view for the specified cell and associates it with the * specified object using the specified CellMapper. This calls refresh on * the created CellView to create all dependent views. *

    * Note: The mapping needs to be available before the views of child cells * and ports are created. * Note: This method must return new instances! * * @param cell * reference to the object in the model */ public CellView createView(GraphModel model, Object cell) { CellView view = null; if (model.isPort(cell)) view = createPortView(cell); else if (model.isEdge(cell)) view = createEdgeView(cell); else view = createVertexView(cell); return view; } /** * Constructs a VertexView view for the specified object. */ protected VertexView createVertexView(Object cell) { return new VertexView(cell); } /** * Constructs an EdgeView view for the specified object. */ protected EdgeView createEdgeView(Object cell) { if (cell instanceof Edge) return createEdgeView((Edge) cell); else return new EdgeView(cell); } /** * Constructs a PortView view for the specified object. */ protected PortView createPortView(Object cell) { if (cell instanceof Port) return createPortView((Port) cell); else return new PortView(cell); } /** * Constructs an EdgeView view for the specified object. * * @deprecated replaced by {@link #createEdgeView(Object)}since * JGraph no longer exposes dependecies on GraphCell subclasses * (Port, Edge) */ protected EdgeView createEdgeView(Edge cell) { return new EdgeView(cell); } /** * Constructs a PortView view for the specified object. * * @deprecated replaced by {@link #createPortView(Object)}since * JGraph no longer exposes dependecies on GraphCell subclasses * (Port, Edge) */ protected PortView createPortView(Port cell) { return new PortView(cell); } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/ParentMap.java0000644000175000017500000001255311256667036024502 0ustar gregoagregoa/* * @(#)ParentMap.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * An object that describes relations between childs and parents. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class ParentMap implements Serializable { /** Contents of the parent map. */ protected ArrayList entries = new ArrayList(); /** * Set of changed changedNodes for the parent map. Includes childs and * parents. */ protected Set changedNodes = new HashSet(); /** Maps parents to integers with the future number of childs. */ protected Map childCount = new Hashtable(); /** * Constructs a ParentMap object. */ public ParentMap() { // empty } /** * Constructs a ParentMap object. */ public ParentMap(Object[] children, Object parent) { addEntries(children, parent); } /** * Returns a parent map that represents the insertion or removal of * cells in model based on remove. * Unselected childs of selected nodes are moved to the first unselected * parent of that node. *

    * Note: Consequently, cells "move up" one level when * their parent is removed. Note: onlyUsePassedInCells can * be used to indicate if only cells from the passed-in cell array are * allowed parents. This is only used if remove is not true. */ public static ParentMap create(GraphModel m, Object[] c, boolean remove, boolean onlyUsePassedInCells) { Set cellSet = new HashSet(); for (int i = 0; i < c.length; i++) { // Do not add null cells otherwise the while loop lower down runs // in an infinite loop if (c[i] != null) { cellSet.add(c[i]); } } ParentMap parentMap = new ParentMap(); for (int i = 0; i < c.length; i++) { // Collect Parent Information Object parent = m.getParent(c[i]); if (parent != null && (!onlyUsePassedInCells || (!remove && cellSet .contains(parent)))) parentMap.addEntry(c[i], (remove) ? null : parent); if (remove) { // Move Orphans to First Unselected Parent while (cellSet.contains(parent)) { // Make sure there are no nulls in the cell set or the // while will get stuck in a loop parent = m.getParent(parent); } for (int j = 0; j < m.getChildCount(c[i]); j++) { Object child = m.getChild(c[i], j); if (!cellSet.contains(child)) parentMap.addEntry(child, parent); } } } return parentMap; } /** * Add a new entry for this child, parent pair to the parent map. The child * and parent are added to the set of changed nodes. Note: The previous * parent is changed on execution of this parent map and must be added by * the GraphModel and reflected by the GraphChange.getChanged method. TODO: * In general, the GraphModel should be in charge of computing the set of * changed cells. */ public void addEntry(Object child, Object parent) { if (child != null) { entries.add(new Entry(child, parent)); // Update Changed Nodes changedNodes.add(child); if (parent != null) changedNodes.add(parent); } } /** * Adds all child parent pairs using addEntry. */ public void addEntries(Object[] children, Object parent) { for (int i = 0; i < children.length; i++) addEntry(children[i], parent); } /** * Returns the number of entries. */ public int size() { return entries.size(); } /** * Returns an Iterator for the entries in the map. */ public Iterator entries() { return entries.iterator(); } /** * Returns a Set for the nodes, childs and parents, in this * parent map. */ public Set getChangedNodes() { return changedNodes; } /** * Creates a new parent map based on this parent map, where the child and * parents are mapped using map. If one the cells is not in * map, then the original cell is used instead. *

    */ public ParentMap clone(Map map) { ParentMap pm = new ParentMap(); Iterator it = entries(); while (it.hasNext()) { Entry e = (Entry) it.next(); Object child = map.get(e.getChild()); Object parent = map.get(e.getParent()); if (child == null) child = e.getChild(); if (parent == null) parent = e.getParent(); if (child != null && parent != null) pm.addEntry(child, parent); } return pm; } /** * Object that represents the relation between a child an a parent. */ public class Entry implements Serializable { /** Child and parent of the relation this entry describes. */ protected Object child, parent; /** * Constructs a new relation between child and * parent. */ public Entry(Object child, Object parent) { this.child = child; this.parent = parent; } /** * Returns the child of the relation. */ public Object getChild() { return child; } /** * Returns the parent of the relation. */ public Object getParent() { return parent; } } public String toString() { String s = super.toString() + "\n"; Iterator it = entries(); while (it.hasNext()) { Entry entry = (Entry) it.next(); s += " child=" + entry.getChild() + " parent=" + entry.getParent() + "\n"; } return s; } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/GraphModel.java0000644000175000017500000002047311256667036024635 0ustar gregoagregoa/* * @(#)GraphModel.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.util.Iterator; import java.util.Map; import javax.swing.event.UndoableEditListener; import javax.swing.undo.UndoableEdit; import org.jgraph.event.GraphModelListener; /** * The interface that defines a suitable data model for a JGraph. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public interface GraphModel { // // Roots // /** * Returns the number of roots in the model. Returns 0 if the model is * empty. * * @return the number of roots in the model */ int getRootCount(); /** * Returns the root at index index in the model. This should not * return null if index is a valid index for the model (that is * index >= 0 && index < getRootCount()). * * @return the root of at index index */ Object getRootAt(int index); /** * Returns the index of root in the model. If root is * null, returns -1. * * @param root * a root in the model, obtained from this data source * @return the index of the root in the model, or -1 if the parent is * null */ int getIndexOfRoot(Object root); /** * Returns true if node or one of its * ancestors is in the model. * * @return true if node is in the model */ boolean contains(Object node); /** * Returns a AttributeMap that represents the properties for * the specified cell. * * @return properties of node as a Map */ AttributeMap getAttributes(Object node); /** * Returns the user object for the specified cell. * * @return userobject of node */ Object getValue(Object node); // // Graph Structure // /** * Returns the source of edge. edge must be an * object previously obtained from this data source. * * @return Object that represents the source of edge */ Object getSource(Object edge); /** * Returns the target of edge. edge must be an * object previously obtained from this data source. * * @return Object that represents the target of edge */ Object getTarget(Object edge); /** * Returns true if port is a valid source for * edge. edge and port must be objects * previously obtained from this data source. * * @return true if port is a valid source for * edge. */ boolean acceptsSource(Object edge, Object port); /** * Returns true if port is a valid target for * edge. edge and port must be objects * previously obtained from this data source. * * @return true if port is a valid target for * edge. */ boolean acceptsTarget(Object edge, Object port); /** * Returns an iterator of the edges connected to port. * port must be a object previously obtained from this data source. * This method never returns null. * * @param port * a port in the graph, obtained from this data source * @return Iterator that represents the connected edges */ Iterator edges(Object port); /** * Returns true if edge is a valid edge. * * @return true if edge is a valid edge. */ boolean isEdge(Object edge); /** * Returns true if port is a valid port, * possibly supporting edge connection. * * @return true if port is a valid port. */ boolean isPort(Object port); // // Group structure // /** * Returns the parent of child in the model. child must be a * node previously obtained from this data source. This returns null if * child is a root in the model. * * @param child * a node in the graph, obtained from this data source * @return the parent of child */ Object getParent(Object child); /** * Returns the index of child in parent. If either the parent or child is * null, returns -1. * * @param parent * a note in the tree, obtained from this data source * @param child * the node we are interested in * @return the index of the child in the parent, or -1 if either the parent * or the child is null */ int getIndexOfChild(Object parent, Object child); /** * Returns the child of parent at index index in the * parent's child array. parent must be a node previously obtained * from this data source. This should not return null if index is a * valid index for parent (that is index >= 0 && index * < getChildCount( parent )). * * @param parent * a node in the tree, obtained from this data source * @return the child of parent at index index */ Object getChild(Object parent, int index); /** * Returns the number of children of parent . Returns 0 if the node * is a leaf or if it has no children. parent must be a node * previously obtained from this data source. * * @param parent * a node in the tree, obtained from this data source * @return the number of children of the node parent */ int getChildCount(Object parent); /** * Returns whether the specified node is a leaf node. The way the test is * performed depends on the askAllowsChildren setting. * * @param node * the node to check * @return true if the node is a leaf node */ boolean isLeaf(Object node); // // Change Support // /** * Inserts the cells and connections into the model, and * passes attributes to the views. Notifies the model- and * undo listeners of the change. */ void insert(Object[] roots, Map attributes, ConnectionSet cs, ParentMap pm, UndoableEdit[] e); /** * Removes cells from the model. Notifies the model- and undo * listeners of the change. */ void remove(Object[] roots); /** * Applies the propertyMap and the connection changes to the * model. The initial edits that triggered the call are * considered to be part of this transaction. Notifies the model- and undo * listeners of the change. Note: If only * edits is non-null, the edits are directly passed to the * UndoableEditListeners. */ void edit(Map attributes, ConnectionSet cs, ParentMap pm, UndoableEdit[] e); /** * Indicates the start of one level of an executable change */ public void beginUpdate(); /** * Indicates the end of the current level of an executable change */ public void endUpdate(); /** * Executes the specified executable change on this graph model * @param change the change to be executed */ public void execute(ExecutableChange change); /** * Returns a map of (cell, clone)-pairs for all cells and * their children. Special care should be taken to replace references * between cells. */ Map cloneCells(Object[] cells); /** * Messaged when the value of the cell has changed, eg from within the edit * method. */ Object valueForCellChanged(Object cell, Object newValue); // // Layering // /** * Sends cells to back. */ void toBack(Object[] cells); /** * Brings cells to front. */ void toFront(Object[] cells); // // Listeners // /** * Adds a listener for the GraphModelEvent posted after the model changes. */ void addGraphModelListener(GraphModelListener l); /** * Removes a listener previously added with addGraphModelListener() . */ void removeGraphModelListener(GraphModelListener l); /** * Adds an undo listener for notification of any changes. Undo/Redo * operations performed on the UndoableEdit will cause the * appropriate ModelEvent to be fired to keep the view(s) in sync with the * model. */ void addUndoableEditListener(UndoableEditListener listener); /** * Removes an undo listener. */ void removeUndoableEditListener(UndoableEditListener listener); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/AbstractCellView.java0000644000175000017500000004204411256667036026007 0ustar gregoagregoa/* * @(#)AbstractCellView.java 1.0 03-JUL-04 * * Copyright (c) 2001-2005 Gaudenz Alder * * See LICENSE file in distribution for licensing details of this source file */ package org.jgraph.graph; import java.awt.Component; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import java.util.Stack; import javax.swing.tree.DefaultMutableTreeNode; import org.jgraph.JGraph; /** * The abstract base class for all cell views. * * @version 1.0 1/3/02 * @author Gaudenz Alder */ public abstract class AbstractCellView implements CellView, Serializable { /** Editor for the cell. */ public static transient GraphCellEditor cellEditor; // Headless environment may have no default fonts installed static { try { cellEditor = new DefaultGraphCellEditor(); } catch (Error e) { // No cell editor } } /** Reference to the cell for this view */ protected Object cell = null; /** Cached parent view */ protected CellView parent = null; /** Cached child views. Default is a ArrayList with allocation size 0. */ protected java.util.List childViews = new ArrayList(0); /** * Contains the complete set of attributes, including the cell's attributes. * The values in this map are overriden by the corresponding values in * attributes. */ protected AttributeMap allAttributes = createAttributeMap(); /** * Hashtable for attributes. Value in this map override the values in * allAttributes. */ protected AttributeMap attributes = allAttributes; /** Cached bounds of all children if vertex is a group */ protected transient Rectangle2D groupBounds = VertexView.defaultBounds; /** * Constructs an empty abstract cell view. You should set a cell on this * view using setCell before doing anything. Optionally you can also set a * different attribute map using setAttributeMap. Note: To change the * attribute map you should now use the changeAttributes method. */ public AbstractCellView() { } /** * Constructs a view for the specified model object, and invokes update on * the new instance. * * @param cell * reference to the model object */ public AbstractCellView(Object cell) { setCell(cell); } /** * Hook for subclassers to avoid creating an empty AttributeMap during * construction of the instance. Override this and return null if you want * to avoid creation of an attribute map at construction time. */ protected AttributeMap createAttributeMap() { return new AttributeMap(); } /** * Returns the model object that this view represents. * * @return the model object that this view represents */ public Object getCell() { return cell; } /** * Sets the model object that this view represents to the specified cell * * @param cell * the model object this view will represent */ public void setCell(Object cell) { this.cell = cell; } /** * Create child views and reload properties for this view. Invokes update * first. * * @param cache * the graph model to be used * @param mapper * the cell mapper to be used * @param createDependentViews * whether or not to create a view if one does not already exist */ public void refresh(GraphLayoutCache cache, CellMapper mapper, boolean createDependentViews) { // Re-read global attributes GraphModel model = cache.getModel(); allAttributes = getCellAttributes(model); // Cache Parent View if (mapper != null && model != null) { // Create parent only if it's visible in the graph Object par = model.getParent(cell); CellView tmp = mapper.getMapping(par, createDependentViews); if (tmp != parent) removeFromParent(); parent = tmp; } // Cache Cell Attributes in View update(cache); // Re-load Child Views childViews.clear(); for (int i = 0; i < model.getChildCount(cell); i++) { Object child = model.getChild(cell, i); CellView view = mapper.getMapping(child, createDependentViews); if (!model.isPort(child) && view != null) childViews.add(view); } } /** * Hook for subclassers to avoid cloning the cell's attributes. Return * model.getAttributes(cell) to avoid cloning. */ protected AttributeMap getCellAttributes(GraphModel model) { return (AttributeMap) model.getAttributes(cell).clone(); } /** * Update attributes for this view and indicate to the parent this child has * been updated */ public void update(GraphLayoutCache cache) { mergeAttributes(); // Notify Parent groupBounds = null; childUpdated(); } /** * Implements the merging of the cell's attributes, initially stored in * allAttributes, and the location attributes. The result should be stored * in allAttributes. This hook is for subclassers to change the merging * strategy. */ protected void mergeAttributes() { allAttributes.putAll(attributes); } /** * Indicates to parent, if any, that this child has been updated. */ public void childUpdated() { if (parent != null) parent.childUpdated(); groupBounds = null; } // // Graph Structure // /** * Returns the parent view for this view. * * @return the parent view for this view */ public CellView getParentView() { return parent; } /** * Returns the child views of this view. * * @return the child views of this view */ public CellView[] getChildViews() { CellView[] array = new CellView[childViews.size()]; childViews.toArray(array); return array; } /** * Returns all views, including descendants that have a parent in * views without the PortViews. Note: Iterative * Implementation using view.getChildViews. This returns the array in * inverse order, ie with the top most cell view at index 0. * * @param views * the cell views whose descendants are to be returned * @return the specified views and all their descendant views */ public static CellView[] getDescendantViews(CellView[] views) { Stack stack = new Stack(); for (int i = 0; i < views.length; i++) stack.add(views[i]); ArrayList result = new ArrayList(); while (!stack.isEmpty()) { CellView tmp = (CellView) stack.pop(); Object[] children = tmp.getChildViews(); for (int i = 0; i < children.length; i++) stack.add(children[i]); result.add(tmp); } CellView[] ret = new CellView[result.size()]; result.toArray(ret); return ret; } /** * Removes this view from the list of children of the parent. */ public void removeFromParent() { if (parent instanceof AbstractCellView) { java.util.List list = ((AbstractCellView) parent).childViews; // TODO performance could be quadratic list.remove(this); } } /** * Returns true if the view is a leaf. * * @return true if the view is a leaf */ public boolean isLeaf() { return childViews.isEmpty(); } // // View Attributes // /** * Return the attributes of the view. * * @return the attributes of this view */ public AttributeMap getAttributes() { return attributes; } /** * Sets the attributes of this view to the specified value * * @param attributes * the new attributes to set */ public void setAttributes(AttributeMap attributes) { this.attributes = attributes; } /** * Returns the attributes of the view combined with the attributes of the * corresponding cell. The view's attributes override the cell's attributes * with the same key. */ public AttributeMap getAllAttributes() { return allAttributes; } /** * Applies change to the attributes of the view and calls * update. * * @param change * a map of attribute changes to apply * @return the undo map that reverses this change */ public Map changeAttributes(GraphLayoutCache cache, Map change) { if (change != null) { Map undo = attributes.applyMap(change); update(cache); return undo; } return null; } // // View Methods // /** * Returns the cached bounds for the group if isleaf is false */ public Rectangle2D getBounds() { if (!isLeaf()) { if (groupBounds == null) updateGroupBounds(); return groupBounds; } return null; } /** * Returns the bounding box for the specified views. * * @param views * the views for whom the bounding box is to be determined * @return the bounding box of the specified views */ public static Rectangle2D getBounds(CellView[] views) { if (views != null && views.length > 0) { Rectangle2D ret = null; for (int i = 0; i < views.length; i++) { if (views[i] != null) { Rectangle2D r = views[i].getBounds(); if (r != null) { if (ret == null) ret = new Rectangle2D.Double(r.getX(), r.getY(), r .getWidth(), r.getHeight()); else Rectangle2D.union(ret, r, ret); } } } return ret; } return null; } /** * Sets the bounds of this view. Calls translateView and * scaleView. * * @param bounds * the new bounds for this cell view */ public void setBounds(Rectangle2D bounds) { Rectangle2D oldBounds = getBounds(); if (oldBounds == null) oldBounds = new Rectangle2D.Double(); Point2D p0 = new Point2D.Double(oldBounds.getX(), oldBounds.getY()); Point2D pe = new Point2D.Double(bounds.getX(), bounds.getY()); Rectangle2D localBounds = new Rectangle2D.Double(bounds.getX(), bounds .getY(), bounds.getWidth(), bounds.getHeight()); if (GraphConstants.isMoveable(getAllAttributes()) && !pe.equals(p0)) translate(pe.getX() - p0.getX(), pe.getY() - p0.getY()); else localBounds.setFrame(localBounds.getX(), localBounds.getY(), bounds .getWidth() - pe.getX() + p0.getX(), bounds.getHeight() - pe.getY() + p0.getY()); double lbw = localBounds.getWidth(), lbh = localBounds.getHeight(); double obw = oldBounds.getWidth(), obh = oldBounds.getHeight(); if ((lbw != obw || lbh != obh) && obw > 0 && obh > 0) scale(lbw / obw, lbh / obh, pe); } /** * Updates the bounds of this view and its children * */ protected void updateGroupBounds() { // Note: Prevent infinite recursion by removing // child edges that point to their parent. CellView[] childViews = getChildViews(); LinkedList result = new LinkedList(); for (int i = 0; i < childViews.length; i++) if (includeInGroupBounds(childViews[i])) result.add(childViews[i]); childViews = new CellView[result.size()]; result.toArray(childViews); Rectangle2D r = getBounds(childViews); int groupBorder = GraphConstants.getInset(getAllAttributes()); if (r != null) r.setFrame(r.getX() - groupBorder, r.getY() - groupBorder, r .getWidth() + 2 * groupBorder, r.getHeight() + 2 * groupBorder); groupBounds = r; } /** * This is used to exclude certain cell views from the group bounds * computation. This implementation returns false for edges that connect to * one of their ancestor groups (eg. parent). * * @param view * the cell view to be included in the group bounds or not * @return whether or not to include the specified cell in the group bounds */ protected boolean includeInGroupBounds(CellView view) { if (view instanceof EdgeView && getCell() instanceof DefaultMutableTreeNode) { EdgeView edgeView = (EdgeView) view; if (edgeView.getCell() instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode edge = (DefaultMutableTreeNode) edgeView .getCell(); Object src = null; if (edgeView.getSource() != null && edgeView.getSource().getParentView() != null) src = edgeView.getSource().getParentView().getCell(); else if (edgeView.getSourceParentView() != null) src = edgeView.getSourceParentView().getCell(); if (src instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode source = (DefaultMutableTreeNode) src; if (source.isNodeDescendant(edge)) return false; } Object tgt = null; if (edgeView.getTarget() != null && edgeView.getTarget().getParentView() != null) tgt = edgeView.getTarget().getParentView().getCell(); else if (edgeView.getTargetParentView() != null) tgt = edgeView.getTargetParentView().getCell(); if (tgt instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode target = (DefaultMutableTreeNode) tgt; if (target.isNodeDescendant(edge)) { return false; } } } } return true; } /** * Translates view (group) by dx, dy. * * @param dx * the x-coordinate amount to translate by * @param dy * the y-coordinate amount to translate by */ public void translate(double dx, double dy) { if (isLeaf()) getAllAttributes().translate(dx, dy); else { int moveableAxis = GraphConstants .getMoveableAxis(getAllAttributes()); if (moveableAxis == GraphConstants.X_AXIS) dy = 0; else if (moveableAxis == GraphConstants.Y_AXIS) dx = 0; Iterator it = childViews.iterator(); while (it.hasNext()) { Object view = it.next(); if (view instanceof AbstractCellView) { AbstractCellView child = (AbstractCellView) view; child.translate(dx, dy); } } } } /** * Scale view (group) by sx, sy. * * @param sx * the multiple by which the x coordinate position of the cell * view is to be scaled * @param sy * the multiple by which the y coordinate position of the cell * view is to be scaled * @param origin * the origin point from which the scaling will calculate */ public void scale(double sx, double sy, Point2D origin) { if (isLeaf()) getAttributes().scale(sx, sy, origin); else { int sizeableAxis = GraphConstants .getSizeableAxis(getAllAttributes()); if (sizeableAxis == GraphConstants.X_AXIS) sy = 1; else if (sizeableAxis == GraphConstants.Y_AXIS) sx = 1; Iterator it = childViews.iterator(); while (it.hasNext()) { Object view = it.next(); if (view instanceof AbstractCellView) { AbstractCellView child = (AbstractCellView) view; Map attrs = child.getAttributes(); if (GraphConstants.isSizeable(attrs) || GraphConstants.isAutoSize(attrs)) child.scale(sx, sy, origin); } } } } /** * Returns true if the view intersects the given rectangle. * * @param graph * the JGraph instance of the view * @param rect * the rectangle within which intersection is being checked for * * @return whether or not the rectangle specified intersects the view */ public boolean intersects(JGraph graph, Rectangle2D rect) { if (isLeaf() || GraphConstants.isGroupOpaque(getAllAttributes())) { Rectangle2D bounds = getBounds(); if (bounds != null) return bounds.intersects(rect); } else { // Check If Children Intersect Iterator it = childViews.iterator(); while (it.hasNext()) if (((CellView) it.next()).intersects(graph, rect)) return true; } return false; } // // View Editors // /** * Returns a renderer component, configured for the view. The method used to * obtain the renderer instance must install the necessary attributes from * this view * * @param graph * the JGraph instance of the view * @param selected * whether or not this view is selected * @param focus * whether or not this view is the focus * @param preview * whether or not it is a preview of the view * * @return the renderer component for this view with this views attributes * installed */ public Component getRendererComponent(JGraph graph, boolean selected, boolean focus, boolean preview) { CellViewRenderer cvr = getRenderer(); if (cvr != null) return cvr.getRendererComponent(graph, this, selected, focus, preview); return null; } /** * Obtains the renderer instance for this view * * @return the renderer instance for this view */ public abstract CellViewRenderer getRenderer(); /** * Returns a cell handle for the view. * * @param context * the context of this cell view (cells indirectly affected by * it) * @return the cell handle for this view */ public abstract CellHandle getHandle(GraphContext context); /** * Returns a cell editor for the view. * * @return the cell editor for this view */ public GraphCellEditor getEditor() { return cellEditor; } public static Point2D getCenterPoint(CellView vertex) { Rectangle2D r = vertex.getBounds(); if (r != null) return new Point2D.Double(r.getCenterX(), r.getCenterY()); return null; } /** * Returns the intersection of the bounding rectangle and the straight line * between the source and the specified point p. The specified point is * expected not to intersect the bounds. Note: You must override this method * if you use a different renderer. This is because this method relies on * the VertexRenderer interface, which can not be safely assumed for * subclassers. */ public Point2D getPerimeterPoint(EdgeView edge, Point2D source, Point2D p) { return getCenterPoint(this); } } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/GraphTransferHandler.java0000644000175000017500000002724411256667036026662 0ustar gregoagregoa/* * @(#)GraphTransferHandler.java 1.0 31-DEC-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import javax.swing.JComponent; import javax.swing.TransferHandler; import org.jgraph.JGraph; /** * @author Gaudenz Alder * * Default datatransfer handler. */ public class GraphTransferHandler extends TransferHandler { /** * Controls if all inserts should be handled as external drops even if all * cells are already in the graph model. This is useful if the enclosing * component does not allow moving. */ protected boolean alwaysReceiveAsCopyAction = false; /* Pointer to the last inserted array of cells. */ protected Object out, in; /* How many times the last transferable was inserted. */ protected int inCount = 0; public boolean canImport(JComponent comp, DataFlavor[] flavors) { for (int i = 0; i < flavors.length; i++) if (flavors[i] == GraphTransferable.dataFlavor) return true; return false; } /* Public entry point to create a Transferable. */ public Transferable createTransferableForGraph(JGraph graph) { return createTransferable(graph); } protected Transferable createTransferable(JComponent c) { if (c instanceof JGraph) { JGraph graph = (JGraph) c; if (!graph.isSelectionEmpty()) { return createTransferable(graph, graph.getSelectionCells()); } } return null; } protected Transferable createTransferable(JGraph graph, Object[] cells) { Object[] flat = graph.getDescendants(graph.order(cells)); ParentMap pm = ParentMap.create(graph.getModel(), flat, false, true); ConnectionSet cs = ConnectionSet.create(graph.getModel(), flat, false); Map viewAttributes = GraphConstants.createAttributes(flat, graph .getGraphLayoutCache()); Rectangle2D bounds = graph.getCellBounds(graph.getSelectionCells()); bounds = new AttributeMap.SerializableRectangle2D(bounds.getX(), bounds .getY(), bounds.getWidth(), bounds.getHeight()); out = flat; return create(graph, flat, viewAttributes, bounds, cs, pm); } protected GraphTransferable create(JGraph graph, Object[] cells, Map viewAttributes, Rectangle2D bounds, ConnectionSet cs, ParentMap pm) { return new GraphTransferable(cells, viewAttributes, bounds, cs, pm); } protected void exportDone(JComponent comp, Transferable data, int action) { if (comp instanceof JGraph && data instanceof GraphTransferable) { JGraph graph = (JGraph) comp; if (action == TransferHandler.MOVE) { Object[] cells = ((GraphTransferable) data).getCells(); graph.getGraphLayoutCache().remove(cells); } graph.getUI().updateHandle(); graph.getUI().setInsertionLocation(null); } } public int getSourceActions(JComponent c) { return COPY_OR_MOVE; } // NOTE: 1. We abuse return value to signal removal to the sender. // 2. We always clone cells when transferred between two models // This is because they contain parts of the model's data. // 3. Transfer is passed to importDataImpl for unsupported // dataflavors (becaue method may return false, see 1.) public boolean importData(JComponent comp, Transferable t) { try { if (comp instanceof JGraph) { JGraph graph = (JGraph) comp; GraphModel model = graph.getModel(); GraphLayoutCache cache = graph.getGraphLayoutCache(); if (t.isDataFlavorSupported(GraphTransferable.dataFlavor) && graph.isEnabled()) { // May be null Point p = graph.getUI().getInsertionLocation(); // Get Local Machine Flavor Object obj = t .getTransferData(GraphTransferable.dataFlavor); GraphTransferable gt = (GraphTransferable) obj; // Get Transferred Cells Object[] cells = gt.getCells(); // Check if all cells are in the model boolean allInModel = true; for (int i = 0; i < cells.length && allInModel; i++) allInModel = allInModel && model.contains(cells[i]); // Count repetitive inserts if (in == cells) inCount++; else inCount = (allInModel) ? 1 : 0; in = cells; // Delegate to handle if (p != null && in == out && graph.getUI().getHandle() != null) { int mod = (graph.getUI().getDropAction() == TransferHandler.COPY) ? InputEvent.CTRL_MASK : 0; graph.getUI().getHandle().mouseReleased( new MouseEvent(comp, 0, 0, mod, p.x, p.y, 1, false)); return false; } // Get more Transfer Data Rectangle2D bounds = gt.getBounds(); Map nested = gt.getAttributeMap(); ConnectionSet cs = gt.getConnectionSet(); ParentMap pm = gt.getParentMap(); // Move across models or via clipboard always clones if (!allInModel || p == null || alwaysReceiveAsCopyAction || graph.getUI().getDropAction() == TransferHandler.COPY) { // Translate cells double dx = 0, dy = 0; // Cloned via Drag and Drop if (nested != null) { if (p != null && bounds != null) { Point2D insert = graph.fromScreen(graph .snap((Point2D) p.clone())); dx = insert.getX() - bounds.getX(); dy = insert.getY() - bounds.getY(); // Cloned via Clipboard } else { Point2D insertPoint = getInsertionOffset(graph, inCount, bounds); if (insertPoint != null) { dx = insertPoint.getX(); dy = insertPoint.getY(); } } } handleExternalDrop(graph, cells, nested, cs, pm, dx, dy); // Signal sender to remove only if moved between // different models return (graph.getUI().getDropAction() == TransferHandler.MOVE && !allInModel); } // We are dealing with a move across multiple views // of the same model else { // Moved via Drag and Drop if (p != null) { // Scale insertion location Point2D insert = graph.fromScreen(graph .snap(new Point(p))); // Compute translation vector and translate all // attribute maps. if (bounds != null && nested != null) { double dx = insert.getX() - bounds.getX(); double dy = insert.getY() - bounds.getY(); AttributeMap.translate(nested.values(), dx, dy); } else if (bounds == null) { // Prevents overwriting view-local // attributes // for known cells. Note: This is because // if bounds is null, the caller wants // to signal that the bounds were // not available, which is typically the // case if no graph layout cache // is at hand. To avoid overriding the // local attributes such as the bounds // with the default bounds from the model, // we remove all attributes that travel // along with the transferable. (Since // all cells are already in the model // no information is lost by doing this.) double gs2 = 2 * graph.getGridSize(); nested = new Hashtable(); Map emptyMap = new Hashtable(); for (int i = 0; i < cells.length; i++) { // This also gives us the chance to // provide useful default location and // resize if there are no useful bounds // that travel along with the cells. if (!model.isEdge(cells[i]) && !model.isPort(cells[i])) { // Check if there are useful bounds // defined in the model, otherwise // resize, // because the view does not yet exist. Rectangle2D tmp = graph .getCellBounds(cells[i]); if (tmp == null) tmp = GraphConstants .getBounds(model .getAttributes(cells[i])); // Clone the rectangle to force a // repaint if (tmp != null) tmp = (Rectangle2D) tmp.clone(); Hashtable attrs = new Hashtable(); Object parent = model .getParent(cells[i]); if (tmp == null) { tmp = new Rectangle2D.Double(p .getX(), p.getY(), gs2 / 2, gs2); GraphConstants.setResize(attrs, true); // Shift p.setLocation(p.getX() + gs2, p .getY() + gs2); graph.snap(p); // If parent processed then childs // are already located } else if (parent == null || !nested .keySet() .contains( model .getParent(cells[i]))) { CellView view = graph .getGraphLayoutCache() .getMapping(cells[i], false); if (view != null && !view.isLeaf()) { double dx = p.getX() - tmp.getX(); double dy = p.getY() - tmp.getY(); GraphLayoutCache .translateViews( new CellView[] { view }, dx, dy); } else { tmp.setFrame(p.getX(), p.getY(), tmp .getWidth(), tmp.getHeight()); } // Shift p.setLocation(p.getX() + gs2, p .getY() + gs2); graph.snap(p); } GraphConstants.setBounds(attrs, tmp); nested.put(cells[i], attrs); } else { nested.put(cells[i], emptyMap); } } } // Edit cells (and make visible) cache.edit(nested, null, null, null); } // Select topmost cells in group-structure graph.setSelectionCells(DefaultGraphModel .getTopmostCells(model, cells)); // Don't remove at sender return false; } } else return importDataImpl(comp, t); } } catch (Exception exception) { // System.err.println("Cannot import: " + // exception.getMessage()); exception.printStackTrace(); } return false; } /** * Hook method to determine offset of cells cloned via the clipboard * @param graph the graph the insertion is occurring on * @param inCount the number of time the insert has been applied * @param bounds the bounds of the transferred graph * @return the offset from the cloned cell(s) */ protected Point2D getInsertionOffset(JGraph graph, int inCount, Rectangle2D bounds) { Point2D result = null; if (graph != null) { result = new Point2D.Double(inCount * graph.getGridSize(), inCount * graph.getGridSize()); } return result; } protected void handleExternalDrop(JGraph graph, Object[] cells, Map nested, ConnectionSet cs, ParentMap pm, double dx, double dy) { // Removes all connections for which the port is neither // passed in the parent map nor already in the model. Iterator it = cs.connections(); while (it.hasNext()) { ConnectionSet.Connection conn = (ConnectionSet.Connection) it .next(); if (!pm.getChangedNodes().contains(conn.getPort()) && !graph.getModel().contains(conn.getPort())) { it.remove(); } } Map clones = graph.cloneCells(cells); graph.getGraphLayoutCache().insertClones(cells, clones, nested, cs, pm, dx, dy); } // For subclassers if above does not handle the insertion protected boolean importDataImpl(JComponent comp, Transferable t) { return false; } /** * @return Returns the alwaysReceiveAsCopyAction. */ public boolean isAlwaysReceiveAsCopyAction() { return alwaysReceiveAsCopyAction; } /** * @param alwaysReceiveAsCopyAction * The alwaysReceiveAsCopyAction to set. */ public void setAlwaysReceiveAsCopyAction(boolean alwaysReceiveAsCopyAction) { this.alwaysReceiveAsCopyAction = alwaysReceiveAsCopyAction; } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/CellViewRenderer.java0000644000175000017500000000213011256667036026002 0ustar gregoagregoa/* * @(#)CellViewRenderer.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.Component; import org.jgraph.JGraph; /** * Defines the requirements for objects that may be used as a * cell view renderer. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public interface CellViewRenderer { /** * Configure and return the renderer based on the passed in * components. The value is typically set from messaging the * graph with convertValueToString. * We recommend you check the value's class and throw an * illegal argument exception if it's not correct. * * @param graph the graph that that defines the rendering context. * @param view the view that should be rendered. * @param sel whether the object is selected. * @param focus whether the object has the focus. * @param preview whether we are drawing a preview. * @return the component used to render the value. */ Component getRendererComponent( JGraph graph, CellView view, boolean sel, boolean focus, boolean preview); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/graph/CellView.java0000644000175000017500000000525411256667036024325 0ustar gregoagregoa/* * @(#)CellView.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.graph; import java.awt.Component; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Map; import org.jgraph.JGraph; /** * Defines the requirements for an object that * represents a view for a model cell. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public interface CellView { // // Data Source // /** * Returns the model object that this view represents. */ Object getCell(); /** * Refresh this view based on the model cell. This is * messaged when the model cell has changed. */ void refresh(GraphLayoutCache cache, CellMapper mapper, boolean createDependentViews); /** * Update this view's attributes. This is messaged whenever refresh is * messaged, and additionally when the context of the cell has changed, * and during live-preview changes to the view. * @param cache TODO */ void update(GraphLayoutCache cache); void childUpdated(); // // Group Structure // /** * Returns the parent of view of this view. */ CellView getParentView(); /** * Returns the child views of this view. */ CellView[] getChildViews(); /** * Removes this view from the list of childs of the parent. */ void removeFromParent(); /** * Returns true if the view is a leaf. */ boolean isLeaf(); // // View Methods // /** * Returns the bounds for the view. */ Rectangle2D getBounds(); /** * Returns true if the view intersects the given rectangle. */ boolean intersects(JGraph g, Rectangle2D rect); /** * Returns the intersection of the bounding rectangle and the straight line * between the source and the specified point p. The specified point is * expected not to intersect the bounds. Note: You must override this method * if you use a different renderer. This is because this method relies on * the VertexRenderer interface, which can not be safely assumed for * subclassers. */ Point2D getPerimeterPoint(EdgeView edge, Point2D source, Point2D p); /** * Apply the specified map of attributes on the view. */ Map changeAttributes(GraphLayoutCache cache, Map map); /** * Returns all attributes of the view as a map. */ AttributeMap getAttributes(); AttributeMap getAllAttributes(); // // Renderer, Editor and Handle // /** * Returns a renderer component, configured for the view. */ Component getRendererComponent( JGraph graph, boolean selected, boolean focus, boolean preview); /** * Returns a cell handle for the view. */ CellHandle getHandle(GraphContext context); /** * Returns a cell editor for the view. */ GraphCellEditor getEditor(); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/plaf/0000755000175000017500000000000011256667036021563 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/plaf/package.html0000644000175000017500000000014411256667036024043 0ustar gregoagregoa Contains the GraphUI class which extends the Swing ComponentUI class. libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/plaf/basic/0000755000175000017500000000000011256667036022644 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/plaf/basic/package.html0000644000175000017500000000014411256667036025124 0ustar gregoagregoa Contains the BasicGraphUI, which is GraphUI's default implementation. libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/plaf/basic/BasicGraphDropTargetListener.java0000644000175000017500000001550711256667036031224 0ustar gregoagregoa/* * @(#)BasicGraphDropTargetListener 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.plaf.basic; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.dnd.DropTargetContext; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JComponent; import javax.swing.Scrollable; import javax.swing.SwingConstants; import javax.swing.Timer; import javax.swing.plaf.UIResource; public class BasicGraphDropTargetListener implements DropTargetListener, UIResource, ActionListener { /** * construct a DropTargetAutoScroller */ public BasicGraphDropTargetListener() { } /** * called to save the state of a component in case it needs to * be restored because a drop is not performed. */ protected void saveComponentState(JComponent c) { } /** * called to restore the state of a component in case a drop * is not performed. */ protected void restoreComponentState(JComponent c) { } /** * called to restore the state of a component in case a drop * is performed. */ protected void restoreComponentStateForDrop(JComponent c) { } /** * called to set the insertion location to match the current * mouse pointer coordinates. */ protected void updateInsertionLocation(JComponent c, Point p) { } /** * Update the geometry of the autoscroll region. The geometry is * maintained as a pair of rectangles. The region can cause * a scroll if the pointer sits inside it for the duration of the * timer. The region that causes the timer countdown is the area * between the two rectangles. *

    * This is implemented to use the visible area of the component * as the outer rectangle and the insets are based upon the * Scrollable information (if any). If the Scrollable is * scrollable along an axis, the step increment is used as * the autoscroll inset. If the component is not scrollable, * the insets will be zero (i.e. autoscroll will not happen). */ void updateAutoscrollRegion(JComponent c) { // compute the outer Rectangle visible = c.getVisibleRect(); outer.setBounds(visible.x, visible.y, visible.width, visible.height); // compute the insets // TBD - the thing with the scrollable Insets i = new Insets(0, 0, 0, 0); if (c instanceof Scrollable) { Scrollable s = (Scrollable) c; i.left = s.getScrollableUnitIncrement( visible, SwingConstants.HORIZONTAL, 1); i.top = s.getScrollableUnitIncrement( visible, SwingConstants.VERTICAL, 1); i.right = s.getScrollableUnitIncrement( visible, SwingConstants.HORIZONTAL, -1); i.bottom = s.getScrollableUnitIncrement( visible, SwingConstants.VERTICAL, -1); } // set the inner from the insets inner.setBounds( visible.x + i.left, visible.y + i.top, visible.width - (i.left + i.right), visible.height - (i.top + i.bottom)); } /** * Perform an autoscroll operation. This is implemented to scroll by the * unit increment of the Scrollable using scrollRectToVisible. If the * cursor is in a corner of the autoscroll region, more than one axis will * scroll. */ void autoscroll(JComponent c, Point pos) { if (c instanceof org.jgraph.JGraph) BasicGraphUI.autoscroll((org.jgraph.JGraph) c, pos); } /** * Initializes the internal properties if they haven't been already * inited. This is done lazily to avoid loading of desktop properties. */ private void initPropertiesIfNecessary() { if (timer == null) { Toolkit t = Toolkit.getDefaultToolkit(); Integer initial = new Integer(100); Integer interval = new Integer(100); try { initial = (Integer) t.getDesktopProperty( "DnD.Autoscroll.initialDelay"); } catch (Exception e) { // ignore } try { interval = (Integer) t.getDesktopProperty("DnD.Autoscroll.interval"); } catch (Exception e) { // ignore } timer = new Timer(interval.intValue(), this); timer.setCoalesce(true); timer.setInitialDelay(initial.intValue()); try { hysteresis = ((Integer) t .getDesktopProperty("DnD.Autoscroll.cursorHysteresis")) .intValue(); } catch (Exception e) { // ignore } } } static JComponent getComponent(DropTargetEvent e) { DropTargetContext context = e.getDropTargetContext(); return (JComponent) context.getComponent(); } // --- ActionListener methods -------------------------------------- /** * The timer fired, perform autoscroll if the pointer is within the * autoscroll region. *

    * @param e the ActionEvent */ public synchronized void actionPerformed(ActionEvent e) { updateAutoscrollRegion(component); if (outer.contains(lastPosition) && !inner.contains(lastPosition)) { autoscroll(component, lastPosition); } } // --- DropTargetListener methods ----------------------------------- public void dragEnter(DropTargetDragEvent e) { component = getComponent(e); javax.swing.TransferHandler th = ((JComponent) component).getTransferHandler(); canImport = th.canImport(component, e.getCurrentDataFlavors()); if (canImport) { saveComponentState(component); lastPosition = e.getLocation(); updateAutoscrollRegion(component); initPropertiesIfNecessary(); } } public void dragOver(DropTargetDragEvent e) { if (canImport) { Point p = e.getLocation(); updateInsertionLocation(component, p); // check autoscroll synchronized (this) { if (Math.abs(p.x - lastPosition.x) > hysteresis || Math.abs(p.y - lastPosition.y) > hysteresis) { // no autoscroll if (timer.isRunning()) timer.stop(); } else { if (!timer.isRunning()) timer.start(); } lastPosition = p; } } } public void dragExit(DropTargetEvent e) { if (canImport) { restoreComponentState(component); } cleanup(); } public void drop(DropTargetDropEvent e) { if (canImport) { restoreComponentStateForDrop(component); } cleanup(); } public void dropActionChanged(DropTargetDragEvent e) { } /** * Cleans up internal state after the drop has finished (either succeeded * or failed). */ private void cleanup() { if (timer != null) { timer.stop(); } component = null; lastPosition = null; } // --- fields -------------------------------------------------- private Timer timer; private Point lastPosition; private Rectangle outer = new Rectangle(); private Rectangle inner = new Rectangle(); private int hysteresis = 10; private boolean canImport; /** * The current component. The value is cached from the drop events and used * by the timer. When a drag exits or a drop occurs, this value is cleared. */ private JComponent component; } libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/plaf/basic/BasicGraphUI.java0000644000175000017500000027535111256667036025765 0ustar gregoagregoa/* * @(#)BasicGraphUI.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.plaf.basic; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.HeadlessException; import java.awt.Image; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.InputEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.AffineTransform; import java.awt.geom.Dimension2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.VolatileImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.Serializable; import java.util.Map; import java.util.TooManyListenersException; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.CellRendererPane; import javax.swing.ImageIcon; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JViewport; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.TransferHandler; import javax.swing.UIManager; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.MouseInputListener; import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import org.jgraph.JGraph; import org.jgraph.event.GraphLayoutCacheEvent; import org.jgraph.event.GraphLayoutCacheListener; import org.jgraph.event.GraphModelEvent; import org.jgraph.event.GraphModelListener; import org.jgraph.event.GraphSelectionEvent; import org.jgraph.event.GraphSelectionListener; import org.jgraph.graph.AbstractCellView; import org.jgraph.graph.AttributeMap; import org.jgraph.graph.BasicMarqueeHandler; import org.jgraph.graph.CellHandle; import org.jgraph.graph.CellView; import org.jgraph.graph.CellViewRenderer; import org.jgraph.graph.ConnectionSet; import org.jgraph.graph.DefaultGraphModel; import org.jgraph.graph.EdgeRenderer; import org.jgraph.graph.EdgeView; import org.jgraph.graph.GraphCell; import org.jgraph.graph.GraphCellEditor; import org.jgraph.graph.GraphConstants; import org.jgraph.graph.GraphContext; import org.jgraph.graph.GraphLayoutCache; import org.jgraph.graph.GraphModel; import org.jgraph.graph.GraphSelectionModel; import org.jgraph.graph.GraphTransferHandler; import org.jgraph.graph.ParentMap; import org.jgraph.graph.PortView; import org.jgraph.plaf.GraphUI; import org.jgraph.util.RectUtils; /** * The basic L&F for a graph data structure. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public class BasicGraphUI extends GraphUI implements Serializable { /** * Controls live-preview in dragEnabled mode. This is used to disable * live-preview in dragEnabled mode on Java 1.4.0 to workaround a bug that * cause the VM to hang during concurrent DnD and repaints. Is this still * required? */ public static final boolean DNDPREVIEW = System.getProperty("java.version") .compareTo("1.4.0") < 0 || System.getProperty("java.version").compareTo("1.4.0") > 0; /** Border in pixels to scroll if marquee or dragging are active. */ public static int SCROLLBORDER = 18; /** Multiplicator for width and height when autoscrolling (=stepsize). */ public static float SCROLLSTEP = 0.05f; /** The maximum number of cells to paint when dragging. */ public static int MAXCELLS = 20; /** The maximum number of handles to paint individually. */ public static int MAXHANDLES = 20; /** Maximum number of cells to compute clipping bounds for. */ public static int MAXCLIPCELLS = 20; /** Minimum preferred size. */ protected Dimension preferredMinSize; /** Component that we're going to be drawing into. */ protected JGraph graph; /** Reference to the graph's view (geometric pattern). */ protected GraphLayoutCache graphLayoutCache; /** Current editor for the graph. */ protected GraphCellEditor cellEditor; /** * Set to false when editing and shouldSelectCell() returns true meaning the * node should be selected before editing, used in completeEditing. */ protected boolean stopEditingInCompleteEditing; /** Used to paint the CellRenderer. */ protected CellRendererPane rendererPane; /** Size needed to completely display all the cells. */ protected Dimension preferredSize; /** Is the preferredSize valid? */ protected boolean validCachedPreferredSize; /** Used to determine what to display. */ protected GraphModel graphModel; /** Model maintaining the selection. */ protected GraphSelectionModel graphSelectionModel; /** Handle that we are going to use. */ protected CellHandle handle; /** Marquee that we are going to use. */ protected BasicMarqueeHandler marquee; // Following 4 ivars are only valid when editing. /** * When editing, this will be the Component that is doing the actual * editing. */ protected Component editingComponent; /** The focused cell under the mousepointer and the last focused cell. */ protected CellView focus, lastFocus; /** Path that is being edited. */ protected Object editingCell; /** Set to true if the editor has a different size than the renderer. */ protected boolean editorHasDifferentSize; /** Needed to exchange information between Transfer- and MouseListener. */ protected Point insertionLocation; /** * Needed to exchange information between DropTargetHandler and * TransferHandler. */ protected int dropAction = TransferHandler.NONE; /** * If ture, a the view under mousepointer will be snapped to the grid lines * during a drag operation. If snap-to-grid mode is disabled, views are * moved by a snap increment. */ protected boolean snapSelectedView = false; // Cached listeners /** Listens for JGraph property changes and updates display. */ protected PropertyChangeListener propertyChangeListener; /** Listens for Mouse events. */ protected MouseListener mouseListener; /** Listens for KeyListener events. */ protected KeyListener keyListener; /** Listens for Component events. */ protected ComponentListener componentListener; /** Listens for CellEditor events. */ protected CellEditorListener cellEditorListener = createCellEditorListener(); /** Updates the display when the selection changes. */ protected GraphSelectionListener graphSelectionListener; /** Is responsible for updating the view based on model events. */ protected GraphModelListener graphModelListener; /** Updates the display when the view has changed. */ protected GraphLayoutCacheListener graphLayoutCacheListener; /** The default TransferHandler. */ protected TransferHandler defaultTransferHandler; /** The default DropTargetListener. */ protected GraphDropTargetListener defaultDropTargetListener; /** The drop target where the default listener was last installed. */ protected DropTarget dropTarget = null; public static ComponentUI createUI(JComponent x) { return new BasicGraphUI(); } public BasicGraphUI() { super(); } // // Methods for configuring the behavior of the graph. None of them // push the value to the JGraph instance. You should really only // call these methods on the JGraph instance. // /** * Sets the GraphModel. This invokes updateSize. */ protected void setModel(GraphModel model) { cancelEditing(graph); if (graphModel != null && graphModelListener != null) graphModel.removeGraphModelListener(graphModelListener); graphModel = model; if (graphModel != null && graphModelListener != null) graphModel.addGraphModelListener(graphModelListener); if (graphModel != null) // jmv : to avoid NullPointerException updateSize(); } /** * Sets the GraphLayoutCache (geometric pattern). This invokes * updateSize. */ protected void setGraphLayoutCache(GraphLayoutCache cache) { cancelEditing(graph); if (graphLayoutCache != null && graphLayoutCacheListener != null) graphLayoutCache .removeGraphLayoutCacheListener(graphLayoutCacheListener); graphLayoutCache = cache; if (graphLayoutCache != null && graphLayoutCacheListener != null) graphLayoutCache .addGraphLayoutCacheListener(graphLayoutCacheListener); updateSize(); } /** * Sets the marquee handler. */ protected void setMarquee(BasicMarqueeHandler marqueeHandler) { marquee = marqueeHandler; } /** * Resets the selection model. The appropriate listeners are installed on * the model. */ protected void setSelectionModel(GraphSelectionModel newLSM) { cancelEditing(graph); if (graphSelectionListener != null && graphSelectionModel != null) graphSelectionModel .removeGraphSelectionListener(graphSelectionListener); graphSelectionModel = newLSM; if (graphSelectionModel != null && graphSelectionListener != null) graphSelectionModel .addGraphSelectionListener(graphSelectionListener); if (graph != null) graph.repaint(); } // // GraphUI methods // /** * */ /** * Returns the handle that is currently active, or null, if no handle is * currently active. Typically, the returned objects are instances of the * RootHandle inner class. */ public CellHandle getHandle() { return handle; } /** * Returns the current drop action. */ public int getDropAction() { return dropAction; } /** * Returns the cell that has the focus. */ protected Object getFocusedCell() { if (focus != null) return focus.getCell(); return null; } /** Get the preferred Size for a cell view. */ public Dimension2D getPreferredSize(JGraph graph, CellView view) { // Either label or icon if (view != null) { Object cell = view.getCell(); String valueStr = graph.convertValueToString(cell); boolean label = (valueStr != null && valueStr.length() > 0); boolean icon = GraphConstants.getIcon(view.getAllAttributes()) != null; if (label || icon) { boolean focus = (getFocusedCell() == cell) && graph.hasFocus(); // Only ever removed when UI changes, this is OK! Component component = view.getRendererComponent(graph, focus, false, false); if (component != null) { graph.add(component); component.validate(); Dimension d = component.getPreferredSize(); int inset = 2 * GraphConstants.getInset(view .getAllAttributes()); d.width += inset; d.height += inset; return d; } } if (view.getBounds() == null) { if (graphLayoutCache != null) { view.update(null); } else if (graph.getGraphLayoutCache() != null) { view.update(graph.getGraphLayoutCache()); } else { view.update(null); } } Rectangle2D bounds = view.getBounds(); return new Dimension((int) bounds.getWidth(), (int) bounds .getHeight()); } return null; } // // Insertion Location // // Used to track the location of the mousepointer during Drag-and-Drop. // /** * Returns the current location of the Drag-and-Drop activity. */ public Point getInsertionLocation() { return insertionLocation; } /** * Sets the current location for Drag-and-Drop activity. Should be set to * null after a drop. Used from within DropTargetListener. */ public void setInsertionLocation(Point p) { insertionLocation = p; } // // Selection // /** * From GraphUI interface. */ public void selectCellsForEvent(JGraph graph, Object[] cells, MouseEvent event) { selectCellsForEvent(cells, event); } /** * Messaged to update the selection based on a MouseEvent for a group of * cells. If the event is a toggle selection event, the cells are either * selected, or deselected. Otherwise the cells are selected. */ public void selectCellsForEvent(Object[] cells, MouseEvent event) { if (cells == null || !graph.isSelectionEnabled()) return; // Toggle selection if (isToggleSelectionEvent(event)) { for (int i = 0; i < cells.length; i++) toggleSelectionCellForEvent(cells[i], event); // Select cells } else if (isAddToSelectionEvent(event)) graph.addSelectionCells(cells); else graph.setSelectionCells(cells); } /** * Messaged to update the selection based on a MouseEvent over a particular * cell. If the event is a toggle selection event, the cell is either * selected, or deselected. Otherwise the cell is selected. */ public void selectCellForEvent(Object cell, MouseEvent event) { if (graph.isSelectionEnabled()) { // Toggle selection if (isToggleSelectionEvent(event)) toggleSelectionCellForEvent(cell, event); // Select cell else if (isAddToSelectionEvent(event)) graph.addSelectionCell(cell); else graph.setSelectionCell(cell); } } /** * Messaged to update the selection based on a toggle selection event, which * means the cell's selection state is inverted. */ protected void toggleSelectionCellForEvent(Object cell, MouseEvent event) { if (graph.isCellSelected(cell)) graph.removeSelectionCell(cell); else graph.addSelectionCell(cell); } /** * Returning true signifies that cells are added to the selection. */ public boolean isAddToSelectionEvent(MouseEvent e) { return e.isShiftDown(); } /** * Returning true signifies a mouse event on the cell should toggle the * selection of only the cell under mouse. */ public boolean isToggleSelectionEvent(MouseEvent e) { switch (Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) { case InputEvent.CTRL_MASK: return e.isControlDown(); case InputEvent.ALT_MASK: return e.isAltDown(); case InputEvent.META_MASK: return e.isMetaDown(); default: return false; } } /** * Returning true signifies the marquee handler has precedence over other * handlers, and is receiving subsequent mouse events. */ public boolean isForceMarqueeEvent(MouseEvent event) { if (marquee != null) return marquee.isForceMarqueeEvent(event); return false; } /** * Returning true signifies a move should only be applied to one direction. */ public boolean isConstrainedMoveEvent(MouseEvent event) { if (event != null) return event.isShiftDown(); return false; } // // Editing // /** * Returns true if the graph is being edited. The item that is being edited * can be returned by getEditingPath(). */ public boolean isEditing(JGraph graph) { return (editingComponent != null); } /** * Stops the current editing session. This has no effect if the graph isn't * being edited. Returns true if the editor allows the editing session to * stop. */ public boolean stopEditing(JGraph graph) { if (editingComponent != null && cellEditor.stopCellEditing()) { completeEditing(false, false, true); return true; } return false; } /** * Cancels all current editing sessions. */ public void cancelEditing(JGraph graph) { if (editingComponent != null) completeEditing(false, true, false); // Escape key is handled by the KeyHandler.keyPressed inner class method } /** * Selects the cell and tries to edit it. Editing will fail if the * CellEditor won't allow it for the selected item. */ public void startEditingAtCell(JGraph graph, Object cell) { graph.scrollCellToVisible(cell); if (cell != null) startEditing(cell, null); } /** * Returns the element that is being edited. */ public Object getEditingCell(JGraph graph) { return editingCell; } // // Install methods // public void installUI(JComponent c) { if (c == null) throw new NullPointerException( "null component passed to BasicGraphUI.installUI()"); graph = (JGraph) c; marquee = graph.getMarqueeHandler(); prepareForUIInstall(); // Boilerplate install block installDefaults(); installListeners(); installKeyboardActions(); installComponents(); completeUIInstall(); } /** * Invoked after the graph instance variable has been set, * but before any defaults/listeners have been installed. */ protected void prepareForUIInstall() { // Data member initializations stopEditingInCompleteEditing = true; preferredSize = new Dimension(); setGraphLayoutCache(graph.getGraphLayoutCache()); setModel(graph.getModel()); } /** * Invoked from installUI after all the defaults/listeners have been * installed. */ protected void completeUIInstall() { // Custom install code setSelectionModel(graph.getSelectionModel()); updateSize(); } /** * Invoked as part from the boilerplate install block. This sets the look * and feel specific variables in JGraph. */ protected void installDefaults() { if (graph.getBackground() == null || graph.getBackground() instanceof UIResource) { graph.setBackground(UIManager.getColor("Tree.background")); } if (graph.getFont() == null || graph.getFont() instanceof UIResource) { // UIManager.getFont not supported in headless environment try { graph.setFont(UIManager.getFont("Tree.font")); } catch (Error e) { // No default font } } // Set JGraph's laf-specific colors if (JGraph.IS_MAC) { graph.setMarqueeColor(UIManager .getColor("MenuItem.selectionBackground")); } else { graph.setMarqueeColor(UIManager.getColor("Table.gridColor")); } graph .setHandleColor(UIManager .getColor("MenuItem.selectionBackground")); graph.setLockedHandleColor(UIManager.getColor("MenuItem.background")); graph.setGridColor(UIManager.getColor("Tree.selectionBackground")); graph.setOpaque(true); } /** * Invoked as part from the boilerplate install block. This installs the * listeners from BasicGraphUI in the graph. */ protected void installListeners() { // Install Local Handlers TransferHandler th = graph.getTransferHandler(); if (th == null || th instanceof UIResource) { defaultTransferHandler = createTransferHandler(); // Not supported in headless environment try { graph.setTransferHandler(defaultTransferHandler); } catch (Error e) { // No default font } } if (graphLayoutCache != null) { graphLayoutCacheListener = createGraphLayoutCacheListener(); graphLayoutCache .addGraphLayoutCacheListener(graphLayoutCacheListener); } dropTarget = graph.getDropTarget(); try { if (dropTarget != null) { defaultDropTargetListener = new GraphDropTargetListener(); dropTarget.addDropTargetListener(defaultDropTargetListener); } } catch (TooManyListenersException tmle) { // should not happen... swing drop target is multicast } // Install Listeners if ((propertyChangeListener = createPropertyChangeListener()) != null) graph.addPropertyChangeListener(propertyChangeListener); if ((mouseListener = createMouseListener()) != null) { graph.addMouseListener(mouseListener); if (mouseListener instanceof MouseMotionListener) { graph .addMouseMotionListener((MouseMotionListener) mouseListener); } } if ((keyListener = createKeyListener()) != null) { graph.addKeyListener(keyListener); } if ((graphModelListener = createGraphModelListener()) != null && graphModel != null) graphModel.addGraphModelListener(graphModelListener); if ((graphSelectionListener = createGraphSelectionListener()) != null && graphSelectionModel != null) graphSelectionModel .addGraphSelectionListener(graphSelectionListener); } /** * Invoked as part from the boilerplate install block. */ protected void installKeyboardActions() { InputMap km = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); SwingUtilities.replaceUIInputMap(graph, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km); km = getInputMap(JComponent.WHEN_FOCUSED); SwingUtilities.replaceUIInputMap(graph, JComponent.WHEN_FOCUSED, km); SwingUtilities.replaceUIActionMap(graph, createActionMap()); } /** * Return JTree's input map. */ InputMap getInputMap(int condition) { if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) return (InputMap) UIManager.get("Tree.ancestorInputMap"); else if (condition == JComponent.WHEN_FOCUSED) return (InputMap) UIManager.get("Tree.focusInputMap"); return null; } /** * Return the mapping between JTree's input map and JGraph's actions. */ ActionMap createActionMap() { // 1: Up, 2: Right, 3: Down, 4: Left ActionMap map = new ActionMapUIResource(); map .put("selectPrevious", new GraphIncrementAction(1, "selectPrevious")); map.put("selectPreviousChangeLead", new GraphIncrementAction(1, "selectPreviousLead")); map.put("selectPreviousExtendSelection", new GraphIncrementAction(1, "selectPreviousExtendSelection")); map.put("selectParent", new GraphIncrementAction(4, "selectParent")); map.put("selectParentChangeLead", new GraphIncrementAction(4, "selectParentChangeLead")); map.put("selectNext", new GraphIncrementAction(3, "selectNext")); map.put("selectNextChangeLead", new GraphIncrementAction(3, "selectNextLead")); map.put("selectNextExtendSelection", new GraphIncrementAction(3, "selectNextExtendSelection")); map.put("selectChild", new GraphIncrementAction(2, "selectChild")); map.put("selectChildChangeLead", new GraphIncrementAction(2, "selectChildChangeLead")); map.put("cancel", new GraphCancelEditingAction("cancel")); map.put("startEditing", new GraphEditAction("startEditing")); map.put("selectAll", new GraphSelectAllAction("selectAll", true)); map.put("clearSelection", new GraphSelectAllAction("clearSelection", false)); return map; } /** * Intalls the subcomponents of the graph, which is the renderer pane. */ protected void installComponents() { if ((rendererPane = createCellRendererPane()) != null) graph.add(rendererPane); } // // Create methods. // /** * Creates an instance of TransferHandler. Used for subclassers to provide * different TransferHandler. */ protected TransferHandler createTransferHandler() { return new GraphTransferHandler(); } /** * Creates a listener that is responsible to update the UI based on how the * graph's bounds properties change. */ protected PropertyChangeListener createPropertyChangeListener() { return new PropertyChangeHandler(); } /** * Creates the listener responsible for calling the correct handlers based * on mouse events, and to select invidual cells. */ protected MouseListener createMouseListener() { return new MouseHandler(); } /** * Creates the listener reponsible for getting key events from the graph. */ protected KeyListener createKeyListener() { return new KeyHandler(); } /** * Creates the listener that updates the display based on selection change * methods. */ protected GraphSelectionListener createGraphSelectionListener() { return new GraphSelectionHandler(); } /** * Creates a listener to handle events from the current editor. */ protected CellEditorListener createCellEditorListener() { return new CellEditorHandler(); } /** * Creates and returns a new ComponentHandler. */ protected ComponentListener createComponentListener() { return new ComponentHandler(); } /** * Returns the renderer pane that renderer components are placed in. */ protected CellRendererPane createCellRendererPane() { return new CellRendererPane(); } /** * Returns a listener that can update the graph when the view changes. */ protected GraphLayoutCacheListener createGraphLayoutCacheListener() { return new GraphLayoutCacheHandler(); } /** * Returns a listener that can update the graph when the model changes. */ protected GraphModelListener createGraphModelListener() { return new GraphModelHandler(); } // // Uninstall methods // public void uninstallUI(JComponent c) { cancelEditing(graph); uninstallListeners(); uninstallKeyboardActions(); uninstallComponents(); completeUIUninstall(); } protected void completeUIUninstall() { graphLayoutCache = null; rendererPane = null; componentListener = null; propertyChangeListener = null; keyListener = null; setSelectionModel(null); graph = null; graphModel = null; graphSelectionModel = null; graphSelectionListener = null; } protected void uninstallListeners() { // Uninstall Handlers TransferHandler th = graph.getTransferHandler(); if (th == defaultTransferHandler) graph.setTransferHandler(null); if (graphLayoutCacheListener != null) graphLayoutCache .removeGraphLayoutCacheListener(graphLayoutCacheListener); if (dropTarget != null && defaultDropTargetListener != null) dropTarget.removeDropTargetListener(defaultDropTargetListener); if (componentListener != null) graph.removeComponentListener(componentListener); if (propertyChangeListener != null) graph.removePropertyChangeListener(propertyChangeListener); if (mouseListener != null) { graph.removeMouseListener(mouseListener); if (mouseListener instanceof MouseMotionListener) graph .removeMouseMotionListener((MouseMotionListener) mouseListener); } if (keyListener != null) graph.removeKeyListener(keyListener); if (graphModel != null && graphModelListener != null) graphModel.removeGraphModelListener(graphModelListener); if (graphSelectionListener != null && graphSelectionModel != null) graphSelectionModel .removeGraphSelectionListener(graphSelectionListener); } protected void uninstallKeyboardActions() { SwingUtilities.replaceUIActionMap(graph, null); SwingUtilities.replaceUIInputMap(graph, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); SwingUtilities.replaceUIInputMap(graph, JComponent.WHEN_FOCUSED, null); } /** * Uninstalls the renderer pane. */ protected void uninstallComponents() { if (rendererPane != null) graph.remove(rendererPane); } // // Painting routines. // /** * Main painting routine. */ public void paint(Graphics g, JComponent c) { if (graph != c) throw new InternalError("BasicGraphUI cannot paint " + c.toString() + "; " + graph + " was expected."); Rectangle2D clipBounds = g.getClipBounds(); if (clipBounds != null) { clipBounds = (Rectangle2D) clipBounds.clone(); } if (graph.isDoubleBuffered()) { Graphics offGraphics = graph.getOffgraphics(); Image offscreen = graph.getOffscreen(); if (offGraphics == null || offscreen == null) { drawGraph(g, clipBounds); paintOverlay(g); return; } if (offscreen instanceof VolatileImage) { int volatileContentsLostCount = 0; do { offGraphics = graph.getOffgraphics(); volatileContentsLostCount++; if (volatileContentsLostCount > 10) { // Assume a problem with the volatile buffering // and move to standard buffered images graph.setVolatileOffscreen(false); } } while (((VolatileImage) offscreen).contentsLost()); } g.drawImage(graph.getOffscreen(), 0, 0, graph); // Paints the handle and marquee regardless of the double buffering // and XOR state so that no artifacts for the marquee appear after // scroll //if (!graph.isXorEnabled()) { // Paint Handle if (handle != null) { handle.paint(g); } // Paint Marquee if (marquee != null) { marquee.paint(graph, g); } //} paintOverlay(g); offGraphics.setClip(null); } else { // Not double buffered drawGraph(g, clipBounds); } } /** * Hook method to paints the overlay * * @param g * the graphics object to paint the overlay to */ protected void paintOverlay(Graphics g) { } /** * Draws the graph to the specified graphics object within the specified * clip bounds, if any * * @param g * the graphics object to draw the graph to * @param clipBounds * the bounds within graph cells must intersect to be redrawn */ public void drawGraph(Graphics g, Rectangle2D clipBounds) { // if (g.getClip() != null && clipBounds != null && !(g.getClip().equals(clipBounds))) { g.setClip(clipBounds); // } Rectangle2D realClipBounds = null; if (clipBounds != null) { realClipBounds = graph.fromScreen(new Rectangle2D.Double( clipBounds.getX(), clipBounds.getY(), clipBounds .getWidth(), clipBounds.getHeight())); } // Paint Background (Typically Grid) paintBackground(g); Graphics2D g2 = (Graphics2D) g; // Remember current affine transform AffineTransform at = g2.getTransform(); // Use anti aliasing if (graph.isAntiAliased()) g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Use Swing's scaling double scale = graph.getScale(); g2.scale(scale, scale); // Paint cells paintCells(g, realClipBounds); // Reset affine transform and antialias g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); // Paint Foreground (Typically Ports) if (!graph.isPortsScaled()) g2.setTransform(at); paintForeground(g); g2.setTransform(at); // Paint Handle if (handle != null) { handle.paint(g); } // Paint Marquee if (marquee != null) { marquee.paint(graph, g); } // Empty out the renderer pane, allowing renderers to be gc'ed. if (rendererPane != null) { rendererPane.removeAll(); } } /** * Hook method to allow subclassers to alter just the cell painting * functionality * @param g the graphics object to paint to * @param realClipBounds the bounds of the region being repainted */ protected void paintCells(Graphics g, Rectangle2D realClipBounds) { CellView[] views = graphLayoutCache.getRoots(); for (int i = 0; i < views.length; i++) { Rectangle2D bounds = views[i].getBounds(); if (bounds != null) { if (realClipBounds == null) { paintCell(g, views[i], bounds, false); } else if (bounds.intersects(realClipBounds)) { paintCell(g, views[i], bounds, false); } } } } /** * Paints the renderer of view to g at * bounds. Recursive implementation that paints the children * first. *

    * The reciever should NOT modify clipBounds, or * insets. The preview flag is passed to the * renderer, and is not used here. */ public void paintCell(Graphics g, CellView view, Rectangle2D bounds, boolean preview) { // First Paint View if (view != null && bounds != null) { boolean bfocus = (view == this.focus); boolean sel = graph.isCellSelected(view.getCell()); Component component = view.getRendererComponent(graph, sel, bfocus, preview); rendererPane.paintComponent(g, component, graph, (int) bounds .getX(), (int) bounds.getY(), (int) bounds.getWidth(), (int) bounds.getHeight(), true); } // Then Paint Children if (!view.isLeaf()) { CellView[] children = view.getChildViews(); for (int i = 0; i < children.length; i++) paintCell(g, children[i], children[i].getBounds(), preview); } } // // Background // /** * Paint the background of this graph. Calls paintGrid. */ protected void paintBackground(Graphics g) { Rectangle clip = g.getClipBounds(); paintBackgroundImage(g, clip); if (graph.isGridVisible()) { paintGrid(graph.getGridSize(), g, clip); } } /** * Hook for subclassers to paint the background image. * * @param g * The graphics object to paint the image on. * @param clip * The clipping region to draw into */ protected void paintBackgroundImage(Graphics g, Rectangle clip) { Component component = graph.getBackgroundComponent(); if (component != null) { paintBackgroundComponent(g, component, clip); } ImageIcon icon = graph.getBackgroundImage(); if (icon == null) { return; } Image backgroundImage = icon.getImage(); if (backgroundImage == null) { return; } Graphics2D g2 = (Graphics2D) g; AffineTransform transform = null; if (graph.isBackgroundScaled()) { transform = g2.getTransform(); g2.scale(graph.getScale(), graph.getScale()); } g2.drawImage(backgroundImage, 0, 0, graph); if (transform != null) { g2.setTransform(transform); } } /** * Requests that the component responsible for painting the background paint * itself * * @param g * The graphics object to paint the image on. * @param component * the component to be painted onto the background image */ protected void paintBackgroundComponent(Graphics g, Component component) { try { g.setPaintMode(); Dimension dim = component.getPreferredSize(); rendererPane.paintComponent(g, component, graph, 0, 0, (int) dim .getWidth(), (int) dim.getHeight(), true); } catch (Exception e) { } catch (Error e) { } } /** * Requests that the component responsible for painting the background paint * itself * * @param g * The graphics object to paint the image on. * @param component * the component to be painted onto the background image * @param clip * The clipping region to draw into */ protected void paintBackgroundComponent(Graphics g, Component component, Rectangle clip) { try { g.setPaintMode(); Dimension dim = component.getPreferredSize(); rendererPane.paintComponent(g, component, graph, 0, 0, (int) dim .getWidth(), (int) dim.getHeight(), true); } catch (Exception e) { } catch (Error e) { } } /** * Paint the grid. */ protected void paintGrid(double gs, Graphics g, Rectangle2D clipBounds) { if (clipBounds == null) { Rectangle2D graphBounds = graph.getBounds(); clipBounds = new Rectangle2D.Double(0, 0, graphBounds.getWidth(), graphBounds.getHeight()); } double xl = clipBounds.getX(); double yt = clipBounds.getY(); double xr = xl + clipBounds.getWidth(); double yb = yt + clipBounds.getHeight(); double sgs = Math.max(2, gs * graph.getScale()); int xs = (int) (Math.floor(xl / sgs) * sgs); int xe = (int) (Math.ceil(xr / sgs) * sgs); int ys = (int) (Math.floor(yt / sgs) * sgs); int ye = (int) (Math.ceil(yb / sgs) * sgs); g.setColor(graph.getGridColor()); switch (graph.getGridMode()) { case JGraph.CROSS_GRID_MODE: { int cs = (sgs > 16.0) ? 2 : ((sgs < 8.0) ? 0 : 1); for (double x = xs; x <= xe; x += sgs) { for (double y = ys; y <= ye; y += sgs) { int ix = (int) Math.round(x); int iy = (int) Math.round(y); g.drawLine(ix - cs, iy, ix + cs, iy); g.drawLine(ix, iy - cs, ix, iy + cs); } } } break; case JGraph.LINE_GRID_MODE: { xe += (int) Math.ceil(sgs); ye += (int) Math.ceil(sgs); for (double x = xs; x <= xe; x += sgs) { int ix = (int) Math.round(x); g.drawLine(ix, ys, ix, ye); } for (double y = ys; y <= ye; y += sgs) { int iy = (int) Math.round(y); g.drawLine(xs, iy, xe, iy); } } break; case JGraph.DOT_GRID_MODE: default: for (double x = xs; x <= xe; x += sgs) { for (double y = ys; y <= ye; y += sgs) { int ix = (int) Math.round(x); int iy = (int) Math.round(y); g.drawLine(ix, iy, ix, iy); } } break; } } // // Foreground // /** * Paint the foreground of this graph. Calls paintPorts. */ protected void paintForeground(Graphics g) { if (graph.isPortsVisible() && graph.isPortsOnTop()) paintPorts(g, graphLayoutCache.getPorts()); } /** * Paint ports. */ public void paintPorts(Graphics g, CellView[] ports) { if (ports != null) { Rectangle r = g.getClipBounds(); for (int i = 0; i < ports.length; i++) { if (ports[i] != null) { Rectangle2D tmp = ports[i].getBounds(); Rectangle2D bounds = new Rectangle2D.Double(tmp.getX(), tmp .getY(), tmp.getWidth(), tmp.getHeight()); Point2D center = new Point2D.Double(bounds.getCenterX(), bounds.getCenterY()); if (!graph.isPortsScaled()) center = graph.toScreen(center); bounds.setFrame(center.getX() - bounds.getWidth() / 2, center.getY() - bounds.getHeight() / 2, bounds .getWidth(), bounds.getHeight()); if (r == null || bounds.intersects(r)) paintCell(g, ports[i], bounds, false); } } } } // // Various local methods // /** * Update the handle using createHandle. */ public void updateHandle() { if (graphLayoutCache != null) { Object[] cells = graphLayoutCache.getVisibleCells(graph .getSelectionCells()); if (cells != null && cells.length > 0) handle = createHandle(createContext(graph, cells)); else handle = null; } } protected GraphContext createContext(JGraph graph, Object[] cells) { return new GraphContext(graph, cells); } /** * Constructs the "root handle" for context. * * @param context * reference to the context of the current selection. */ public CellHandle createHandle(GraphContext context) { if (context != null && !context.isEmpty() && graph.isEnabled()) { try { return new RootHandle(context); } catch (HeadlessException e) { // Assume because of running on a server } catch (RuntimeException e) { throw e; } } return null; } /** * Messages the Graph with graphDidChange. */ public void updateSize() { validCachedPreferredSize = false; graph.graphDidChange(); updateHandle(); } /** * Updates the preferredSize instance variable, which is * returned from getPreferredSize(). */ protected void updateCachedPreferredSize() { // FIXME: Renderer for the views might have an old state Rectangle2D size = AbstractCellView.getBounds(graphLayoutCache .getRoots()); if (size == null) size = new Rectangle2D.Double(); Point2D psize = new Point2D.Double(size.getX() + size.getWidth(), size .getY() + size.getHeight()); Dimension d = graph.getMinimumSize(); Point2D min = (d != null) ? graph .toScreen(new Point(d.width, d.height)) : new Point(0, 0); Point2D scaled = graph.toScreen(psize); preferredSize = new Dimension( (int) Math.max(min.getX(), scaled.getX()), (int) Math.max(min .getY(), scaled.getY())); // Allow for background image ImageIcon image = graph.getBackgroundImage(); if (image != null) { int height = image.getIconHeight(); int width = image.getIconWidth(); Point2D imageSize = graph.toScreen(new Point(width, height)); preferredSize = new Dimension((int) Math.max(preferredSize .getWidth(), imageSize.getX()), (int) Math.max( preferredSize.getHeight(), imageSize.getY())); } Insets in = graph.getInsets(); if (in != null) { preferredSize.setSize( preferredSize.getWidth() + in.left + in.right, preferredSize.getHeight() + in.top + in.bottom); } validCachedPreferredSize = true; } /** * Sets the preferred minimum size. */ public void setPreferredMinSize(Dimension newSize) { preferredMinSize = newSize; } /** * Returns the minimum preferred size. */ public Dimension getPreferredMinSize() { if (preferredMinSize == null) return null; return new Dimension(preferredMinSize); } /** * Returns the preferred size to properly display the graph. */ public Dimension getPreferredSize(JComponent c) { Dimension pSize = this.getPreferredMinSize(); if (!validCachedPreferredSize) updateCachedPreferredSize(); if (graph != null) { if (pSize != null) return new Dimension( Math.max(pSize.width, preferredSize.width), Math.max( pSize.height, preferredSize.height)); return new Dimension(preferredSize.width, preferredSize.height); } else if (pSize != null) return pSize; else return new Dimension(0, 0); } /** * Returns the minimum size for this component. Which will be the min * preferred size or 0, 0. */ public Dimension getMinimumSize(JComponent c) { if (this.getPreferredMinSize() != null) return this.getPreferredMinSize(); return new Dimension(0, 0); } /** * Returns the maximum size for this component, which will be the preferred * size if the instance is currently in a JGraph, or 0, 0. */ public Dimension getMaximumSize(JComponent c) { if (graph != null) return getPreferredSize(graph); if (this.getPreferredMinSize() != null) return this.getPreferredMinSize(); return new Dimension(0, 0); } /** * Messages to stop the editing session. If the UI the receiver is providing * the look and feel for returns true from * getInvokesStopCellEditing, stopCellEditing will invoked * on the current editor. Then completeEditing will be messaged with false, * true, false to cancel any lingering editing. */ protected void completeEditing() { /* If should invoke stopCellEditing, try that */ if (graph.getInvokesStopCellEditing() && stopEditingInCompleteEditing && editingComponent != null) { cellEditor.stopCellEditing(); } /* * Invoke cancelCellEditing, this will do nothing if stopCellEditing was * succesful. */ completeEditing(false, true, false); } /** * Stops the editing session. If messageStop is true the editor is messaged * with stopEditing, if messageCancel is true the editor is messaged with * cancelEditing. If messageGraph is true the graphModel is messaged with * valueForCellChanged. */ protected void completeEditing(boolean messageStop, boolean messageCancel, boolean messageGraph) { if (stopEditingInCompleteEditing && editingComponent != null) { Component oldComponent = editingComponent; Object oldCell = editingCell; GraphCellEditor oldEditor = cellEditor; boolean requestFocus = (graph != null && (graph.hasFocus() || SwingUtilities .findFocusOwner(editingComponent) != null)); editingCell = null; editingComponent = null; if (messageStop) oldEditor.stopCellEditing(); else if (messageCancel) oldEditor.cancelCellEditing(); graph.remove(oldComponent); if (requestFocus) graph.requestFocus(); if (messageGraph) { Object newValue = oldEditor.getCellEditorValue(); graphLayoutCache.valueForCellChanged(oldCell, newValue); } updateSize(); // Remove Editor Listener if (oldEditor != null && cellEditorListener != null) oldEditor.removeCellEditorListener(cellEditorListener); cellEditor = null; } } /** * Will start editing for cell if there is a cellEditor and shouldSelectCell * returns true. *

    * This assumes that cell is valid and visible. */ protected boolean startEditing(Object cell, MouseEvent event) { completeEditing(); if (graph.isCellEditable(cell)) { CellView tmp = graphLayoutCache.getMapping(cell, false); cellEditor = tmp.getEditor(); editingComponent = cellEditor.getGraphCellEditorComponent(graph, cell, graph.isCellSelected(cell)); if (cellEditor.isCellEditable(event)) { Rectangle2D cellBounds = graph.getCellBounds(cell); editingCell = cell; Dimension2D editorSize = editingComponent.getPreferredSize(); graph.add(editingComponent); Point2D p = getEditorLocation(cell, editorSize, graph .toScreen(new Point2D.Double(cellBounds.getX(), cellBounds.getY()))); editingComponent.setBounds((int) p.getX(), (int) p.getY(), (int) editorSize.getWidth(), (int) editorSize .getHeight()); editingCell = cell; editingComponent.validate(); // Add Editor Listener if (cellEditorListener == null) cellEditorListener = createCellEditorListener(); if (cellEditor != null && cellEditorListener != null) cellEditor.addCellEditorListener(cellEditorListener); Rectangle2D visRect = graph.getVisibleRect(); graph.paintImmediately((int) p.getX(), (int) p.getY(), (int) (visRect.getWidth() + visRect.getX() - cellBounds .getX()), (int) editorSize.getHeight()); if (cellEditor.shouldSelectCell(event) && graph.isSelectionEnabled()) { stopEditingInCompleteEditing = false; try { graph.setSelectionCell(cell); } catch (Exception e) { System.err.println("Editing exception: " + e); } stopEditingInCompleteEditing = true; } if (event instanceof MouseEvent) { /* * Find the component that will get forwarded all the mouse * events until mouseReleased. */ Point componentPoint = SwingUtilities.convertPoint(graph, new Point(event.getX(), event.getY()), editingComponent); /* * Create an instance of BasicTreeMouseListener to handle * passing the mouse/motion events to the necessary * component. */ // We really want similiar behavior to getMouseEventTarget, // but it is package private. Component activeComponent = SwingUtilities .getDeepestComponentAt(editingComponent, componentPoint.x, componentPoint.y); if (activeComponent != null) { new MouseInputHandler(graph, activeComponent, event); } } return true; } else editingComponent = null; } return false; } /** * Subclassers may override this to provide a better location for the * in-place editing of edges (if you do not inherit from the EdgeRenderer * class). */ protected Point2D getEditorLocation(Object cell, Dimension2D editorSize, Point2D pt) { // Edges have different editor position and size CellView view = graphLayoutCache.getMapping(cell, false); if (view instanceof EdgeView) { EdgeView edgeView = (EdgeView) view; CellViewRenderer renderer = edgeView.getRenderer(); if (renderer instanceof EdgeRenderer) { Point2D tmp = ((EdgeRenderer) renderer) .getLabelPosition(edgeView); if (tmp != null) pt = tmp; else pt = AbstractCellView.getCenterPoint(edgeView); pt.setLocation(Math.max(0, pt.getX() - editorSize.getWidth() / 2), Math.max(0, pt.getY() - editorSize.getHeight() / 2)); } graph.toScreen(pt); } return pt; } /** * Scroll the graph for an event at p. */ public static void autoscroll(JGraph graph, Point p) { Rectangle view = graph.getBounds(); if (graph.getParent() instanceof JViewport) view = ((JViewport) graph.getParent()).getViewRect(); if (view.contains(p)) { Point target = new Point(p); int dx = (int) (graph.getWidth() * SCROLLSTEP); int dy = (int) (graph.getHeight() * SCROLLSTEP); if (target.x - view.x < SCROLLBORDER) target.x -= dx; if (target.y - view.y < SCROLLBORDER) target.y -= dy; if (view.x + view.width - target.x < SCROLLBORDER) target.x += dx; if (view.y + view.height - target.y < SCROLLBORDER) target.y += dy; graph.scrollPointToVisible(target); } } /** * Updates the preferred size when scrolling (if necessary). */ public class ComponentHandler extends ComponentAdapter implements ActionListener { /** * Timer used when inside a scrollpane and the scrollbar is adjusting. */ protected Timer timer; /** ScrollBar that is being adjusted. */ protected JScrollBar scrollBar; public void componentMoved(ComponentEvent e) { if (timer == null) { JScrollPane scrollPane = getScrollPane(); if (scrollPane == null) updateSize(); else { scrollBar = scrollPane.getVerticalScrollBar(); if (scrollBar == null || !scrollBar.getValueIsAdjusting()) { // Try the horizontal scrollbar. if ((scrollBar = scrollPane.getHorizontalScrollBar()) != null && scrollBar.getValueIsAdjusting()) startTimer(); else updateSize(); } else startTimer(); } } } /** * Creates, if necessary, and starts a Timer to check if need to resize * the bounds. */ protected void startTimer() { if (timer == null) { timer = new Timer(200, this); timer.setRepeats(true); } timer.start(); } /** * Returns the JScrollPane housing the JGraph, or null if one isn't * found. */ protected JScrollPane getScrollPane() { Component c = graph.getParent(); while (c != null && !(c instanceof JScrollPane)) c = c.getParent(); if (c instanceof JScrollPane) return (JScrollPane) c; return null; } /** * Public as a result of Timer. If the scrollBar is null, or not * adjusting, this stops the timer and updates the sizing. */ public void actionPerformed(ActionEvent ae) { if (scrollBar == null || !scrollBar.getValueIsAdjusting()) { if (timer != null) timer.stop(); updateSize(); timer = null; scrollBar = null; } } } // End of BasicGraphUI.ComponentHandler /** * Listens for changes in the graph model and updates the view accordingly. */ public class GraphModelHandler implements GraphModelListener, Serializable { public void graphChanged(GraphModelEvent e) { Object[] removed = e.getChange().getRemoved(); // Remove from selection & focus if (removed != null && removed.length > 0) { // Update Focus If Necessary if (focus != null) { Object focusedCell = focus.getCell(); for (int i = 0; i < removed.length; i++) { if (removed[i] == focusedCell) { lastFocus = focus; focus = null; break; } } } // Remove from selection graph.getSelectionModel().removeSelectionCells(removed); } Rectangle2D oldDirty = null; Rectangle2D dirtyRegion = e.getChange().getDirtyRegion(); if (dirtyRegion == null) { oldDirty = graph.getClipRectangle(e.getChange()); } if (graphLayoutCache != null) { graphLayoutCache.graphChanged(e.getChange()); } // Get arrays Object[] inserted = e.getChange().getInserted(); Object[] changed = e.getChange().getChanged(); // Insert if (inserted != null && inserted.length > 0) { // Update focus to first inserted cell for (int i = 0; i < inserted.length; i++) graph.updateAutoSize(graphLayoutCache.getMapping( inserted[i], false)); } // Change (update size) if (changed != null && changed.length > 0) { for (int i = 0; i < changed.length; i++) graph.updateAutoSize(graphLayoutCache.getMapping( changed[i], false)); } if (dirtyRegion == null) { Rectangle2D newDirtyRegion = graph.getClipRectangle(e.getChange()); dirtyRegion = RectUtils.union(oldDirty, newDirtyRegion); e.getChange().setDirtyRegion(dirtyRegion); } if (dirtyRegion != null) { graph.addOffscreenDirty(dirtyRegion); } // Select if not partial if (!graphLayoutCache.isPartial() && graphLayoutCache.isSelectsAllInsertedCells() && graph.isEnabled()) { Object[] roots = DefaultGraphModel.getRoots(graphModel, inserted); if (roots != null && roots.length > 0) { lastFocus = focus; focus = graphLayoutCache.getMapping(roots[0], false); graph.setSelectionCells(roots); } } updateSize(); } } // End of BasicGraphUI.GraphModelHandler /** * Listens for changes in the graph view and updates the size accordingly. */ public class GraphLayoutCacheHandler implements GraphLayoutCacheListener, Serializable { /* * (non-Javadoc) * * @see org.jgraph.event.GraphLayoutCacheListener#graphLayoutCacheChanged(org.jgraph.event.GraphLayoutCacheEvent) */ public void graphLayoutCacheChanged(GraphLayoutCacheEvent e) { Object[] changed = e.getChange().getChanged(); if (changed != null && changed.length > 0) { for (int i = 0; i < changed.length; i++) { graph.updateAutoSize(graphLayoutCache.getMapping( changed[i], false)); } } Rectangle2D oldDirtyRegion = e.getChange().getDirtyRegion(); graph.addOffscreenDirty(oldDirtyRegion); Rectangle2D newDirtyRegion = graph.getClipRectangle(e.getChange()); graph.addOffscreenDirty(newDirtyRegion); Object[] inserted = e.getChange().getInserted(); if (inserted != null && inserted.length > 0 && graphLayoutCache.isSelectsLocalInsertedCells() && !(graphLayoutCache.isSelectsAllInsertedCells() && !graphLayoutCache .isPartial()) && graph.isEnabled()) { Object[] roots = DefaultGraphModel.getRoots(graphModel, inserted); if (roots != null && roots.length > 0) { lastFocus = focus; focus = graphLayoutCache.getMapping(roots[0], false); graph.setSelectionCells(roots); } } updateSize(); } } // End of BasicGraphUI.GraphLayoutCacheHandler /** * Listens for changes in the selection model and updates the display * accordingly. */ public class GraphSelectionHandler implements GraphSelectionListener, Serializable { /** * Messaged when the selection changes in the graph we're displaying * for. Stops editing, updates handles and displays the changed cells. */ public void valueChanged(GraphSelectionEvent event) { // cancelEditing(graph); updateHandle(); Object[] cells = event.getCells(); if (cells != null && cells.length <= MAXCLIPCELLS) { Rectangle2D r = graph.toScreen(graph.getCellBounds(cells)); // Includes dirty region of focused cell if (focus != null) { if (r != null) Rectangle2D.union(r, focus.getBounds(), r); else r = focus.getBounds(); } // And last focused cell if (lastFocus != null) { if (r != null) Rectangle2D.union(r, lastFocus.getBounds(), r); else r = lastFocus.getBounds(); } if (r != null) { Rectangle2D unscaledDirty = graph.fromScreen((Rectangle2D)r.clone()); graph.addOffscreenDirty(unscaledDirty); int hsize = (int) graph.getHandleSize() + 1; updateHandle(); Rectangle dirtyRegion = new Rectangle((int)(r.getX() - hsize), (int)(r.getY() - hsize), (int)(r.getWidth() + 2 * hsize), (int)(r.getHeight() + 2 * hsize)); graph.repaint(dirtyRegion); } } else { Rectangle dirtyRegion = new Rectangle(graph.getSize()); graph.addOffscreenDirty(dirtyRegion); graph.repaint(); } } } /** * Listener responsible for getting cell editing events and updating the * graph accordingly. */ public class CellEditorHandler implements CellEditorListener, Serializable { /** Messaged when editing has stopped in the graph. */ public void editingStopped(ChangeEvent e) { completeEditing(false, false, true); } /** Messaged when editing has been canceled in the graph. */ public void editingCanceled(ChangeEvent e) { completeEditing(false, false, false); } } // BasicGraphUI.CellEditorHandler /** * This is used to get mutliple key down events to appropriately generate * events. */ public class KeyHandler extends KeyAdapter implements Serializable { /** Key code that is being generated for. */ protected Action repeatKeyAction; /** Set to true while keyPressed is active. */ protected boolean isKeyDown; public void keyPressed(KeyEvent e) { if (graph != null && graph.hasFocus() && graph.isEnabled()) { KeyStroke keyStroke = KeyStroke.getKeyStroke(e.getKeyCode(), e .getModifiers()); if (graph.getConditionForKeyStroke(keyStroke) == JComponent.WHEN_FOCUSED) { ActionListener listener = graph .getActionForKeyStroke(keyStroke); if (listener instanceof Action) repeatKeyAction = (Action) listener; else repeatKeyAction = null; } else { repeatKeyAction = null; if (keyStroke.getKeyCode() == KeyEvent.VK_ESCAPE) { // System.out.println("Calling release on " + marquee); if (marquee != null) marquee.mouseReleased(null); if (mouseListener != null) mouseListener.mouseReleased(null); updateHandle(); graph.repaint(); } } if (isKeyDown && repeatKeyAction != null) { repeatKeyAction.actionPerformed(new ActionEvent(graph, ActionEvent.ACTION_PERFORMED, "")); e.consume(); } else isKeyDown = true; } } public void keyReleased(KeyEvent e) { isKeyDown = false; } } // End of BasicGraphUI.KeyHandler /** * TreeMouseListener is responsible for updating the selection based on * mouse events. */ public class MouseHandler extends MouseAdapter implements MouseMotionListener, Serializable { /* The cell under the mousepointer. */ protected CellView cell; /* The object that handles mouse operations. */ protected Object handler; protected transient Cursor previousCursor = null; /** * Invoked when a mouse button has been pressed on a component. */ public void mousePressed(MouseEvent e) { handler = null; if (!e.isConsumed() && graph.isEnabled()) { graph.requestFocus(); int s = graph.getTolerance(); Rectangle2D r = graph.fromScreen(new Rectangle2D.Double(e .getX() - s, e.getY() - s, 2 * s, 2 * s)); lastFocus = focus; focus = (focus != null && focus.intersects(graph, r)) ? focus : null; cell = graph.getNextSelectableViewAt(focus, e.getX(), e.getY()); if (focus == null) focus = cell; completeEditing(); boolean isForceMarquee = isForceMarqueeEvent(e); boolean isEditable = graph.isGroupsEditable() || (focus != null && focus.isLeaf()); if (!isForceMarquee) { if (e.getClickCount() == graph.getEditClickCount() && focus != null && isEditable && focus.getParentView() == null && graph.isCellEditable(focus.getCell()) && handleEditTrigger(cell.getCell(), e)) { e.consume(); cell = null; } else if (!isToggleSelectionEvent(e)) { if (handle != null) { handle.mousePressed(e); handler = handle; } // Immediate Selection if (!e.isConsumed() && cell != null && !graph.isCellSelected(cell.getCell())) { selectCellForEvent(cell.getCell(), e); focus = cell; if (handle != null) { handle.mousePressed(e); handler = handle; } e.consume(); cell = null; } } } // Marquee Selection if (!e.isConsumed() && marquee != null && (!isToggleSelectionEvent(e) || focus == null || isForceMarquee)) { marquee.mousePressed(e); handler = marquee; } } } /** * Handles edit trigger by starting the edit and return true if the * editing has already started. * * @param cell * the cell being edited * @param e * the mouse event triggering the edit * @return true if the editing has already started */ protected boolean handleEditTrigger(Object cell, MouseEvent e) { graph.scrollCellToVisible(cell); if (cell != null) startEditing(cell, e); return graph.isEditing(); } public void mouseDragged(MouseEvent e) { autoscroll(graph, e.getPoint()); if (graph.isEnabled()) { if (handler != null && handler == marquee) marquee.mouseDragged(e); else if (handler == null && !isEditing(graph) && focus != null) { if (!graph.isCellSelected(focus.getCell())) { selectCellForEvent(focus.getCell(), e); cell = null; } if (handle != null) handle.mousePressed(e); handler = handle; } if (handle != null && handler == handle) handle.mouseDragged(e); } } /** * Invoked when the mouse pointer has been moved on a component (with no * buttons down). */ public void mouseMoved(MouseEvent e) { if (previousCursor == null) { previousCursor = graph.getCursor(); } if (graph != null && graph.isEnabled()) { if (marquee != null) marquee.mouseMoved(e); if (handle != null) handle.mouseMoved(e); if (!e.isConsumed() && previousCursor != null) { Cursor currentCursor = graph.getCursor(); if (currentCursor != previousCursor) { graph.setCursor(previousCursor); } previousCursor = null; } } } // Event may be null when called to cancel the current operation. public void mouseReleased(MouseEvent e) { try { if (e != null && !e.isConsumed() && graph != null && graph.isEnabled()) { if (handler == marquee && marquee != null) marquee.mouseReleased(e); else if (handler == handle && handle != null) handle.mouseReleased(e); if (isDescendant(cell, focus) && e.getModifiers() != 0) { // Do not switch to parent if Special Selection cell = focus; } if (!e.isConsumed() && cell != null) { Object tmp = cell.getCell(); boolean wasSelected = graph.isCellSelected(tmp); if (!e.isPopupTrigger() || !wasSelected) { selectCellForEvent(tmp, e); focus = cell; postProcessSelection(e, tmp, wasSelected); } } } } finally { handler = null; cell = null; } } /** * Invoked after a cell has been selected in the mouseReleased method. * This can be used to do something interesting if the cell was already * selected, in which case this implementation selects the parent. * Override if you want different behaviour, such as start editing. */ protected void postProcessSelection(MouseEvent e, Object cell, boolean wasSelected) { if (wasSelected && graph.isCellSelected(cell) && e.getModifiers() != 0) { Object parent = cell; Object nextParent = null; while (((nextParent = graphModel.getParent(parent)) != null) && graphLayoutCache.isVisible(nextParent)) parent = nextParent; selectCellForEvent(parent, e); lastFocus = focus; focus = graphLayoutCache.getMapping(parent, false); } } protected boolean isDescendant(CellView parentView, CellView childView) { if (parentView == null || childView == null) { return false; } Object parent = parentView.getCell(); Object child = childView.getCell(); Object ancestor = child; do { if (ancestor == parent) return true; } while ((ancestor = graphModel.getParent(ancestor)) != null); return false; } } // End of BasicGraphUI.MouseHandler public class RootHandle implements CellHandle, Serializable { // x and y offset from the mouse press event to the left/top corner of a // view that is returned by a findViewForPoint(). // These are used only when the isSnapSelectedView mode is enabled. protected transient double _mouseToViewDelta_x = 0; protected transient double _mouseToViewDelta_y = 0; protected transient boolean firstDrag = true; /* Temporary views for the cells. */ protected transient CellView[] views; protected transient CellView[] contextViews; protected transient CellView[] portViews; protected transient CellView targetGroup, ignoreTargetGroup; /* Bounds of the cells. Non-null if too many cells. */ protected transient Rectangle2D cachedBounds; /* Initial top left corner of the selection */ protected transient Point2D initialLocation; /* Child handles. Null if too many handles. */ protected transient CellHandle[] handles; /* The point where the mouse was pressed. */ protected transient Point2D start = null, last, snapStart, snapLast; /** Reference to graph off screen graphics */ protected transient Graphics offgraphics; /** * Indicates whether this handle is currently moving cells. Start may be * non-null and isMoving false while the minimum movement has not been * reached. */ protected boolean isMoving = false; /** * Indicates whether this handle has started drag and drop. Note: * isDragging => isMoving. */ protected boolean isDragging = false; /** The handle that consumed the last mousePressedEvent. Initially null. */ protected transient CellHandle activeHandle = null; /* The current selection context, responsible for cloning the cells. */ protected transient GraphContext context; /* * True after the graph was repainted to block xor-ed painting of * background. */ protected boolean isContextVisible = true; protected boolean blockPaint = false; protected Point2D current; /* Defines the Disconnection if DisconnectOnMove is True */ protected transient ConnectionSet disconnect = null; /** * Creates a root handle which contains handles for the given cells. The * root handle and all its childs point to the specified JGraph * instance. The root handle is responsible for dragging the selection. */ public RootHandle(GraphContext ctx) { this.context = ctx; if (!ctx.isEmpty()) { // Temporary cells views = ctx.createTemporaryCellViews(); Rectangle2D tmpBounds = graph.toScreen(graph.getCellBounds(ctx .getCells())); if (ctx.getDescendantCount() < MAXCELLS) { contextViews = ctx.createTemporaryContextViews(); initialLocation = graph.toScreen(getInitialLocation(ctx .getCells())); } else cachedBounds = tmpBounds; if (initialLocation == null && tmpBounds != null) { initialLocation = new Point2D.Double(tmpBounds.getX(), tmpBounds.getY()); } // Sub-Handles Object[] cells = ctx.getCells(); if (cells.length < MAXHANDLES) { handles = new CellHandle[views.length]; for (int i = 0; i < views.length; i++) handles[i] = views[i].getHandle(ctx); // PortView Preview portViews = ctx.createTemporaryPortViews(); } } } /** * Returns the initial location, which is the top left corner of the * selection, ignoring all connected endpoints of edges. */ protected Point2D getInitialLocation(Object[] cells) { if (cells != null && cells.length > 0) { Rectangle2D ret = null; for (int i = 0; i < cells.length; i++) { if (graphModel != null && graphModel.isEdge(cells[i])) { CellView cellView = graphLayoutCache.getMapping( cells[i], false); if (cellView instanceof EdgeView) { EdgeView edgeView = (EdgeView) cellView; if (edgeView.getSource() == null) { Point2D pt = edgeView.getPoint(0); if (pt != null) { if (ret == null) ret = new Rectangle2D.Double(pt.getX(), pt.getY(), 0, 0); else Rectangle2D.union(ret, new Rectangle2D.Double(pt .getX(), pt.getY(), 0, 0), ret); } } if (edgeView.getTarget() == null) { Point2D pt = edgeView.getPoint(edgeView .getPointCount() - 1); if (pt != null) { if (ret == null) ret = new Rectangle2D.Double(pt.getX(), pt.getY(), 0, 0); else Rectangle2D.union(ret, new Rectangle2D.Double(pt .getX(), pt.getY(), 0, 0), ret); } } } } else { Rectangle2D r = graph.getCellBounds(cells[i]); if (r != null) { if (ret == null) ret = (Rectangle2D) r.clone(); Rectangle2D.union(ret, r, ret); } } } if (ret != null) return new Point2D.Double(ret.getX(), ret.getY()); } return null; } /* Returns the context of this root handle. */ public GraphContext getContext() { return context; } /* Paint the handles. Use overlay to paint the current state. */ public void paint(Graphics g) { if (handles != null && handles.length < MAXHANDLES) for (int i = 0; i < handles.length; i++) if (handles[i] != null) handles[i].paint(g); blockPaint = true; if (!graph.isXorEnabled() && current != null) { double dx = current.getX() - start.getX(); double dy = current.getY() - start.getY(); if (dx != 0 || dy != 0) { overlay(g); } } else { blockPaint = true; } } public void overlay(Graphics g) { if (isDragging && !DNDPREVIEW) // BUG IN 1.4.0 (FREEZE) return; if (cachedBounds != null) { // Paint Cached Bounds g.setColor(Color.black); g.drawRect((int) cachedBounds.getX(), (int) cachedBounds.getY(), (int) cachedBounds .getWidth() - 2, (int) cachedBounds.getHeight() - 2); } else { Graphics2D g2 = (Graphics2D) g; AffineTransform oldTransform = g2.getTransform(); g2.scale(graph.getScale(), graph.getScale()); if (views != null) { // Paint Temporary Views for (int i = 0; i < views.length; i++) paintCell(g, views[i], views[i].getBounds(), true); } // Paint temporary context if (contextViews != null && isContextVisible) { for (int i = 0; i < contextViews.length; i++) { paintCell(g, contextViews[i], contextViews[i] .getBounds(), true); } } if (!graph.isPortsScaled()) g2.setTransform(oldTransform); if (portViews != null && graph.isPortsVisible()) paintPorts(g, portViews); g2.setTransform(oldTransform); } // Paints the target group to move into if (targetGroup != null) { Rectangle2D b = graph.toScreen((Rectangle2D) targetGroup .getBounds().clone()); g.setColor(graph.getHandleColor()); g.fillRect((int) b.getX() - 1, (int) b.getY() - 1, (int) b .getWidth() + 2, (int) b.getHeight() + 2); g.setColor(graph.getMarqueeColor()); g.draw3DRect((int) b.getX() - 2, (int) b.getY() - 2, (int) b .getWidth() + 3, (int) b.getHeight() + 3, true); } } /** * Invoked when the mouse pointer has been moved on a component (with no * buttons down). */ public void mouseMoved(MouseEvent event) { if (!event.isConsumed() && handles != null) { for (int i = handles.length - 1; i >= 0 && !event.isConsumed(); i--) if (handles[i] != null) handles[i].mouseMoved(event); } } public void mousePressed(MouseEvent event) { if (!event.isConsumed() && graph.isMoveable()) { if (handles != null) { // Find Handle for (int i = handles.length - 1; i >= 0; i--) { if (handles[i] != null) { handles[i].mousePressed(event); if (event.isConsumed()) { activeHandle = handles[i]; return; } } } } if (views != null) { // Start Move if over cell Point2D screenPoint = event.getPoint(); Point2D pt = graph .fromScreen((Point2D) screenPoint.clone()); CellView view = findViewForPoint(pt); if (view != null) { if (snapSelectedView) { Rectangle2D bounds = view.getBounds(); start = graph.toScreen(new Point2D.Double(bounds .getX(), bounds.getY())); snapStart = graph.snap((Point2D) start.clone()); _mouseToViewDelta_x = screenPoint.getX() - start.getX(); _mouseToViewDelta_y = screenPoint.getY() - start.getY(); } else { // this is the original RootHandle's mode. snapStart = graph.snap((Point2D) screenPoint .clone()); _mouseToViewDelta_x = snapStart.getX() - screenPoint.getX(); _mouseToViewDelta_y = snapStart.getY() - screenPoint.getY(); start = (Point2D) snapStart.clone(); } last = (Point2D) start.clone(); snapLast = (Point2D) snapStart.clone(); isContextVisible = contextViews != null && contextViews.length < MAXCELLS && (!event.isControlDown() || !graph .isCloneable()); event.consume(); } } // Analyze for common parent if (graph.isMoveIntoGroups() || graph.isMoveOutOfGroups()) { Object[] cells = context.getCells(); Object ignoreGroup = graph.getModel().getParent(cells[0]); for (int i = 1; i < cells.length; i++) { Object tmp = graph.getModel().getParent(cells[i]); if (ignoreGroup != tmp) { ignoreGroup = null; break; } } if (ignoreGroup != null) ignoreTargetGroup = graph.getGraphLayoutCache() .getMapping(ignoreGroup, false); } } } /** * Hook for subclassers to return a different view for a mouse click at * pt. For example, this can be used to return a leaf * cell instead of a group. */ protected CellView findViewForPoint(Point2D pt) { double snap = graph.getTolerance(); Rectangle2D r = new Rectangle2D.Double(pt.getX() - snap, pt.getY() - snap, 2 * snap, 2 * snap); for (int i = 0; i < views.length; i++) if (views[i].intersects(graph, r)) return views[i]; return null; } /** * Used for move into group to find the target group. */ protected CellView findUnselectedInnermostGroup(double x, double y) { Object[] cells = graph.getDescendants(graph.getRoots()); for (int i = cells.length - 1; i >= 0; i--) { CellView view = graph.getGraphLayoutCache().getMapping( cells[i], false); if (view != null && !view.isLeaf() && !context.contains(view.getCell()) && view.getBounds().contains(x, y)) return view; } return null; } protected void startDragging(MouseEvent event) { isDragging = true; if (graph.isDragEnabled()) { int action = (event.isControlDown() && graph.isCloneable()) ? TransferHandler.COPY : TransferHandler.MOVE; TransferHandler th = graph.getTransferHandler(); setInsertionLocation(event.getPoint()); try { th.exportAsDrag(graph, event, action); } catch (Exception ex) { // Ignore } } } /** * @return Returns the parent graph scrollpane for the specified graph. */ public Component getFirstOpaqueParent(Component component) { if (component != null) { Component parent = component; while (parent != null) { if (parent.isOpaque() && !(parent instanceof JViewport)) return parent; parent = parent.getParent(); } } return component; } protected void initOffscreen() { if (!graph.isXorEnabled()) { return; } try { offgraphics = graph.getOffgraphics(); } catch (Exception e) { offgraphics = null; } catch (Error e) { offgraphics = null; } } /** Process mouse dragged event. */ public void mouseDragged(MouseEvent event) { boolean constrained = isConstrainedMoveEvent(event); boolean spread = false; Rectangle2D dirty = null; if (firstDrag && graph.isDoubleBuffered() && cachedBounds == null) { initOffscreen(); firstDrag = false; } if (event != null && !event.isConsumed()) { if (activeHandle != null) // Paint Active Handle activeHandle.mouseDragged(event); // Invoke Mouse Dragged else if (start != null) { // Move Cells Graphics g = (offgraphics != null) ? offgraphics : graph .getGraphics(); Point ep = event.getPoint(); Point2D point = new Point2D.Double(ep.getX() - _mouseToViewDelta_x, ep.getY() - _mouseToViewDelta_y); Point2D snapCurrent = graph.snap(point); current = snapCurrent; int thresh = graph.getMinimumMove(); double dx = current.getX() - start.getX(); double dy = current.getY() - start.getY(); if (isMoving || Math.abs(dx) > thresh || Math.abs(dy) > thresh) { boolean overlayed = false; isMoving = true; if (disconnect == null && graph.isDisconnectOnMove()) disconnect = context.disconnect(graphLayoutCache .getAllDescendants(views)); // Constrained movement double totDx = current.getX() - start.getX(); double totDy = current.getY() - start.getY(); dx = current.getX() - last.getX(); dy = current.getY() - last.getY(); Point2D constrainedPosition = constrainDrag(event, totDx, totDy, dx, dy); if (constrainedPosition != null) { dx = constrainedPosition.getX(); dy = constrainedPosition.getY(); } double scale = graph.getScale(); dx = dx / scale; dy = dy / scale; // Start Drag and Drop if (graph.isDragEnabled() && !isDragging) startDragging(event); if (dx != 0 || dy != 0) { if (offgraphics != null || !graph.isXorEnabled()) { dirty = graph.toScreen(AbstractCellView .getBounds(views)); Rectangle2D t = graph.toScreen(AbstractCellView .getBounds(contextViews)); if (t != null) dirty.add(t); } if (graph.isXorEnabled()) { g.setColor(graph.getForeground()); // use 'darker' to force XOR to distinguish // between // existing background elements during drag // http://sourceforge.net/tracker/index.php?func=detail&aid=677743&group_id=43118&atid=435210 g.setXORMode(graph.getBackground().darker()); } if (!snapLast.equals(snapStart) && (offgraphics != null || !blockPaint)) { if (graph.isXorEnabled()) { overlay(g); } overlayed = true; } isContextVisible = (!event.isControlDown() || !graph .isCloneable()) && contextViews != null && (contextViews.length < MAXCELLS); blockPaint = false; if (constrained && cachedBounds == null) { // Reset Initial Positions CellView[] all = graphLayoutCache .getAllDescendants(views); for (int i = 0; i < all.length; i++) { CellView orig = graphLayoutCache .getMapping(all[i].getCell(), false); AttributeMap attr = orig.getAllAttributes(); all[i].changeAttributes(graph .getGraphLayoutCache(), (AttributeMap) attr.clone()); all[i].refresh(graph.getGraphLayoutCache(), context, false); } } if (cachedBounds != null) { if (dirty != null) { dirty.add(cachedBounds); } cachedBounds.setFrame(cachedBounds.getX() + dx * scale, cachedBounds.getY() + dy * scale, cachedBounds.getWidth(), cachedBounds.getHeight()); if (dirty != null) { dirty.add(cachedBounds); } } else { // Translate GraphLayoutCache.translateViews(views, dx, dy); if (views != null) graphLayoutCache.update(views); if (contextViews != null) graphLayoutCache.update(contextViews); } // Change preferred size of graph if (graph.isAutoResizeGraph() && (event.getX() > graph.getWidth() - SCROLLBORDER || event.getY() > graph .getHeight() - SCROLLBORDER)) { int SPREADSTEP = 25; Rectangle view = null; if (graph.getParent() instanceof JViewport) view = ((JViewport) graph.getParent()) .getViewRect(); if (view != null) { if (view.contains(event.getPoint())) { if (view.x + view.width - event.getPoint().x < SCROLLBORDER) { preferredSize.width = Math.max( preferredSize.width, (int) view.getWidth()) + SPREADSTEP; spread = true; } if (view.y + view.height - event.getPoint().y < SCROLLBORDER) { preferredSize.height = Math.max( preferredSize.height, (int) view.getHeight()) + SPREADSTEP; spread = true; } if (spread) { graph.revalidate(); autoscroll(graph, event.getPoint()); if (graph.isDoubleBuffered()) initOffscreen(); } } } } // Move into groups Rectangle2D ignoredRegion = (ignoreTargetGroup != null) ? (Rectangle2D) ignoreTargetGroup .getBounds().clone() : null; if (targetGroup != null) { Rectangle2D tmp = graph .toScreen((Rectangle2D) targetGroup .getBounds().clone()); if (dirty != null) dirty.add(tmp); else dirty = tmp; } targetGroup = null; if (graph.isMoveIntoGroups() && (ignoredRegion == null || !ignoredRegion .intersects(AbstractCellView .getBounds(views)))) { targetGroup = (event.isControlDown()) ? null : findUnselectedInnermostGroup( snapCurrent.getX() / scale, snapCurrent.getY() / scale); if (targetGroup == ignoreTargetGroup) targetGroup = null; } if (!snapCurrent.equals(snapStart) && (offgraphics != null || !blockPaint) && !spread) { if (graph.isXorEnabled()) { overlay(g); } overlayed = true; } if (constrained) last = (Point2D) start.clone(); last.setLocation(last.getX() + dx * scale, last .getY() + dy * scale); // It is better to translate last by a // scaled dx/dy // instead of making it to be the // current (as in prev version), // so that the view would be catching up with a // mouse pointer snapLast = snapCurrent; if (overlayed && (offgraphics != null || !graph .isXorEnabled())) { if (dirty == null) { dirty = new Rectangle2D.Double(); } dirty.add(graph.toScreen(AbstractCellView .getBounds(views))); Rectangle2D t = graph.toScreen(AbstractCellView .getBounds(contextViews)); if (t != null) dirty.add(t); // TODO: Should use real ports if portsVisible // and check if ports are scaled int border = PortView.SIZE + 4; if (graph.isPortsScaled()) border = (int) (graph.getScale() * border); int border2 = border / 2; dirty.setFrame(dirty.getX() - border2, dirty .getY() - border2, dirty.getWidth() + border, dirty.getHeight() + border); double sx1 = Math.max(0, dirty.getX()); double sy1 = Math.max(0, dirty.getY()); double sx2 = sx1 + dirty.getWidth(); double sy2 = sy1 + dirty.getHeight(); if (isDragging && !DNDPREVIEW) // BUG IN 1.4.0 // (FREEZE) return; if (offgraphics != null) { graph.drawImage((int) sx1, (int) sy1, (int) sx2, (int) sy2, (int) sx1, (int) sy1, (int) sx2, (int) sy2); } else { graph.repaint((int) dirty.getX(), (int) dirty.getY(), (int) dirty .getWidth() + 1, (int) dirty.getHeight() + 1); } } } } // end if (isMoving or ...) } // end if (start != null) } else if (event == null) graph.repaint(); } /** * Hook method to constrain a mouse drag * * @param event * @param totDx * @param totDy * @param dx * @param dy * @return a point describing any position constraining applied */ protected Point2D constrainDrag(MouseEvent event, double totDx, double totDy, double dx, double dy) { boolean constrained = isConstrainedMoveEvent(event); if (constrained && cachedBounds == null) { if (Math.abs(totDx) < Math.abs(totDy)) { dx = 0; dy = totDy; } else { dx = totDx; dy = 0; } } else { if (!graph.isMoveBelowZero() && last != null && initialLocation != null && start != null) { if (initialLocation.getX() + totDx < 0) { // TODO isn't dx always just 0? dx = start.getX() - last.getX() - initialLocation.getX(); } if (initialLocation.getY() + totDy < 0) { // TODO isn't dy always just 0? dy = start.getY() - last.getY() - initialLocation.getY(); } } if (!graph.isMoveBeyondGraphBounds() && last != null && initialLocation != null && start != null) { Rectangle2D graphBounds = graph.getBounds(); Rectangle2D viewBounds = AbstractCellView.getBounds(views); if (initialLocation.getX() + totDx + viewBounds.getWidth() > graphBounds .getWidth()) dx = 0; if (initialLocation.getY() + totDy + viewBounds.getHeight() > graphBounds .getHeight()) dy = 0; } } return new Point2D.Double(dx, dy); } public void mouseReleased(MouseEvent event) { try { if (event != null && !event.isConsumed()) { if (activeHandle != null) { activeHandle.mouseReleased(event); activeHandle = null; } else if (isMoving && !event.getPoint().equals(start)) { if (cachedBounds != null) { Point ep = event.getPoint(); Point2D point = new Point2D.Double(ep.getX() - _mouseToViewDelta_x, ep.getY() - _mouseToViewDelta_y); Point2D snapCurrent = graph.snap(point); double dx = snapCurrent.getX() - start.getX(); double dy = snapCurrent.getY() - start.getY(); if (!graph.isMoveBelowZero() && initialLocation.getX() + dx < 0) dx = -1 * initialLocation.getX(); if (!graph.isMoveBelowZero() && initialLocation.getY() + dy < 0) dy = -1 * initialLocation.getY(); Point2D tmp = graph.fromScreen(new Point2D.Double( dx, dy)); GraphLayoutCache.translateViews(views, tmp.getX(), tmp.getY()); } CellView[] all = graphLayoutCache .getAllDescendants(views); Map attributes = GraphConstants.createAttributes(all, null); if (event.isControlDown() && graph.isCloneable()) { // Clone // Cells Object[] cells = graph.getDescendants(graph .order(context.getCells())); // Include properties from hidden cells Map hiddenMapping = graphLayoutCache .getHiddenMapping(); for (int i = 0; i < cells.length; i++) { Object witness = attributes.get(cells[i]); if (witness == null) { CellView view = (CellView) hiddenMapping .get(cells[i]); if (view != null && !graphModel.isPort(view .getCell())) { // TODO: Clone required? Same in // GraphConstants. AttributeMap attrs = (AttributeMap) view .getAllAttributes().clone(); // Maybe translate? // attrs.translate(dx, dy); attributes.put(cells[i], attrs.clone()); } } } ConnectionSet cs = ConnectionSet.create(graphModel, cells, false); ParentMap pm = ParentMap.create(graphModel, cells, false, true); cells = graphLayoutCache.insertClones(cells, graph .cloneCells(cells), attributes, cs, pm, 0, 0); } else if (graph.isMoveable()) { // Move Cells ParentMap pm = null; // Moves into group if (targetGroup != null) { pm = new ParentMap(context.getCells(), targetGroup.getCell()); } else if (graph.isMoveOutOfGroups() && (ignoreTargetGroup != null && !ignoreTargetGroup .getBounds().intersects( AbstractCellView .getBounds(views)))) { pm = new ParentMap(context.getCells(), null); } graph.getGraphLayoutCache().edit(attributes, disconnect, pm, null); } event.consume(); } } } catch (Exception e) { e.printStackTrace(); } finally { ignoreTargetGroup = null; targetGroup = null; isDragging = false; disconnect = null; firstDrag = true; current = null; start = null; } } } /** * PropertyChangeListener for the graph. Updates the appropriate variable * and takes the appropriate actions, based on what changes. */ public class PropertyChangeHandler implements PropertyChangeListener, Serializable { public void propertyChange(PropertyChangeEvent event) { if (event.getSource() == graph) { String changeName = event.getPropertyName(); if (changeName.equals("minimumSize")) updateCachedPreferredSize(); else if (changeName.equals(JGraph.GRAPH_MODEL_PROPERTY)) setModel((GraphModel) event.getNewValue()); else if (changeName.equals(JGraph.GRAPH_LAYOUT_CACHE_PROPERTY)) { setGraphLayoutCache((GraphLayoutCache) event.getNewValue()); graph.repaint(); } else if (changeName.equals(JGraph.MARQUEE_HANDLER_PROPERTY)) setMarquee((BasicMarqueeHandler) event.getNewValue()); else if (changeName.equals("transferHandler")) { if (dropTarget != null) dropTarget .removeDropTargetListener(defaultDropTargetListener); dropTarget = graph.getDropTarget(); try { if (dropTarget != null) dropTarget .addDropTargetListener(defaultDropTargetListener); } catch (TooManyListenersException tmle) { // should not happen... swing drop target is multicast } } else if (changeName.equals(JGraph.EDITABLE_PROPERTY)) { boolean editable = ((Boolean) event.getNewValue()) .booleanValue(); if (!editable && isEditing(graph)) cancelEditing(graph); } else if (changeName.equals(JGraph.SELECTION_MODEL_PROPERTY)) setSelectionModel(graph.getSelectionModel()); else if (changeName.equals(JGraph.GRID_VISIBLE_PROPERTY) || changeName.equals(JGraph.GRID_SIZE_PROPERTY) || changeName.equals(JGraph.GRID_COLOR_PROPERTY) || changeName.equals(JGraph.HANDLE_COLOR_PROPERTY) || changeName .equals(JGraph.LOCKED_HANDLE_COLOR_PROPERTY) || changeName.equals(JGraph.HANDLE_SIZE_PROPERTY) || changeName.equals(JGraph.PORTS_VISIBLE_PROPERTY) || changeName.equals(JGraph.ANTIALIASED_PROPERTY)) graph.repaint(); else if (changeName.equals(JGraph.SCALE_PROPERTY)) { updateSize(); } else if (changeName.equals(JGraph.PROPERTY_BACKGROUNDIMAGE)) { updateSize(); } else if (changeName.equals("font")) { completeEditing(); updateSize(); } else if (changeName.equals("componentOrientation")) { if (graph != null) graph.graphDidChange(); } } } } // End of BasicGraphUI.PropertyChangeHandler /** * GraphIncrementAction is used to handle up/down actions. */ public class GraphIncrementAction extends AbstractAction { /** Specifies the direction to adjust the selection by. */ protected int direction; private GraphIncrementAction(int direction, String name) { this.direction = direction; } public void actionPerformed(ActionEvent e) { if (graph != null) { int step = 70; Rectangle rect = graph.getVisibleRect(); if (direction == 1) rect.translate(0, -step); // up else if (direction == 2) rect.translate(step, 0); // right else if (direction == 3) rect.translate(0, step); // down else if (direction == 4) rect.translate(-step, 0); // left graph.scrollRectToVisible(rect); } } public boolean isEnabled() { return (graph != null && graph.isEnabled()); } } // End of class BasicGraphUI.GraphIncrementAction /** * ActionListener that invokes cancelEditing when action performed. */ private class GraphCancelEditingAction extends AbstractAction { public GraphCancelEditingAction(String name) { } public void actionPerformed(ActionEvent e) { if (graph != null) cancelEditing(graph); } public boolean isEnabled() { return (graph != null && graph.isEnabled() && graph.isEditing()); } } // End of class BasicGraphUI.GraphCancelEditingAction /** * ActionListener invoked to start editing on the focused cell. */ private class GraphEditAction extends AbstractAction { public GraphEditAction(String name) { } public void actionPerformed(ActionEvent ae) { if (isEnabled()) { if (getFocusedCell() instanceof GraphCell) { graph.startEditingAtCell(getFocusedCell()); } } } public boolean isEnabled() { return (graph != null && graph.isEnabled()); } } // End of BasicGraphUI.GraphEditAction /** * Action to select everything in the graph. */ private class GraphSelectAllAction extends AbstractAction { private boolean selectAll; public GraphSelectAllAction(String name, boolean selectAll) { this.selectAll = selectAll; } public void actionPerformed(ActionEvent ae) { if (graph != null) { if (selectAll) { graph.setSelectionCells(graph.getGraphLayoutCache() .getVisibleCells(graph.getRoots())); } else graph.clearSelection(); } } public boolean isEnabled() { return (graph != null && graph.isEnabled()); } } // End of BasicGraphUI.GraphSelectAllAction /** * MouseInputHandler handles passing all mouse events, including mouse * motion events, until the mouse is released to the destination it is * constructed with. It is assumed all the events are currently target at * source. */ public class MouseInputHandler extends Object implements MouseInputListener { /** Source that events are coming from. */ protected Component source; /** Destination that receives all events. */ protected Component destination; public MouseInputHandler(Component source, Component destination, MouseEvent event) { this.source = source; this.destination = destination; this.source.addMouseListener(this); this.source.addMouseMotionListener(this); /* Dispatch the editing event */ destination.dispatchEvent(SwingUtilities.convertMouseEvent(source, event, destination)); } public void mouseClicked(MouseEvent e) { if (destination != null) destination.dispatchEvent(SwingUtilities.convertMouseEvent( source, e, destination)); } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { if (destination != null) destination.dispatchEvent(SwingUtilities.convertMouseEvent( source, e, destination)); removeFromSource(); } public void mouseEntered(MouseEvent e) { if (!SwingUtilities.isLeftMouseButton(e)) { removeFromSource(); } } public void mouseExited(MouseEvent e) { if (!SwingUtilities.isLeftMouseButton(e)) { removeFromSource(); } // insertionLocation = null; } public void mouseDragged(MouseEvent e) { if (destination != null) destination.dispatchEvent(SwingUtilities.convertMouseEvent( source, e, destination)); } public void mouseMoved(MouseEvent e) { removeFromSource(); } protected void removeFromSource() { if (source != null) { source.removeMouseListener(this); source.removeMouseMotionListener(this); } source = destination = null; } } // End of class BasicGraphUI.MouseInputHandler /** * Graph Drop Target Listener */ public class GraphDropTargetListener extends BasicGraphDropTargetListener implements Serializable { /** * called to save the state of a component in case it needs to be * restored because a drop is not performed. */ protected void saveComponentState(JComponent comp) { } /** * called to restore the state of a component because a drop was not * performed. */ protected void restoreComponentState(JComponent comp) { if (handle != null) handle.mouseDragged(null); } /** * called to set the insertion location to match the current mouse * pointer coordinates. */ protected void updateInsertionLocation(JComponent comp, Point p) { setInsertionLocation(p); if (handle != null) { // How to fetch the shift state? int mod = (dropAction == TransferHandler.COPY) ? InputEvent.CTRL_MASK : 0; handle.mouseDragged(new MouseEvent(comp, 0, 0, mod, p.x, p.y, 1, false)); } } public void dragEnter(DropTargetDragEvent e) { dropAction = e.getDropAction(); super.dragEnter(e); } public void dropActionChanged(DropTargetDragEvent e) { dropAction = e.getDropAction(); super.dropActionChanged(e); } } // End of BasicGraphUI.GraphDropTargetListener /** * @return true if snapSelectedView mode is enabled during the drag * operation. If it is enabled, the view, that is returned by the * findViewForPoint(Point pt), will be snapped to the grid lines. *
    * By default, findViewForPoint() returns the first view from the * GraphContext whose bounds intersect with snap proximity of a * mouse pointer. If snap-to-grid mode is disabled, views are moved * by a snap increment. */ public boolean isSnapSelectedView() { return snapSelectedView; } /** * Sets the mode of the snapSelectedView drag operation. * * @param snapSelectedView * specifies if the snap-to-grid mode should be applied during a * drag operation. If it is enabled, the view, that is returned * by the findViewForPoint(Point pt), will be snapped to the grid * lines.
    * By default, findViewForPoint() returns the first view from the * GraphContext whose bounds intersect with snap proximity of a * mouse pointer. If snap-to-grid mode is disabled, views are * moved by a snap increment. */ public void setSnapSelectedView(boolean snapSelectedView) { this.snapSelectedView = snapSelectedView; } } // End of class BasicGraphUI libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/plaf/basic/BasicGraphTransferable.java0000644000175000017500000001765211256667036030056 0ustar gregoagregoa/* * @(#)BasicGraphTransferable 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.plaf.basic; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.Serializable; import java.io.StringBufferInputStream; import java.io.StringReader; import javax.swing.plaf.UIResource; public class BasicGraphTransferable implements Transferable, UIResource, Serializable { private static DataFlavor[] htmlFlavors; private static DataFlavor[] stringFlavors; private static DataFlavor[] plainFlavors; static { try { htmlFlavors = new DataFlavor[3]; htmlFlavors[0] = new DataFlavor("text/html;class=java.lang.String"); htmlFlavors[1] = new DataFlavor("text/html;class=java.io.Reader"); htmlFlavors[2] = new DataFlavor("text/html;charset=unicode;class=java.io.InputStream"); plainFlavors = new DataFlavor[3]; plainFlavors[0] = new DataFlavor("text/plain;class=java.lang.String"); plainFlavors[1] = new DataFlavor("text/plain;class=java.io.Reader"); plainFlavors[2] = new DataFlavor("text/plain;charset=unicode;class=java.io.InputStream"); stringFlavors = new DataFlavor[2]; stringFlavors[0] = new DataFlavor( DataFlavor.javaJVMLocalObjectMimeType + ";class=java.lang.String"); stringFlavors[1] = DataFlavor.stringFlavor; } catch (ClassNotFoundException cle) { System.err.println( "error initializing javax.swing.plaf.basic.BasicTranserable"); } } /** * Returns an array of DataFlavor objects indicating the flavors the data * can be provided in. The array should be ordered according to preference * for providing the data (from most richly descriptive to least descriptive). * @return an array of data flavors in which this data can be transferred */ public DataFlavor[] getTransferDataFlavors() { DataFlavor[] richerFlavors = getRicherFlavors(); int nRicher = (richerFlavors != null) ? richerFlavors.length : 0; int nHTML = (isHTMLSupported()) ? htmlFlavors.length : 0; int nPlain = (isPlainSupported()) ? plainFlavors.length : 0; int nString = (isPlainSupported()) ? stringFlavors.length : 0; int nFlavors = nRicher + nHTML + nPlain + nString; DataFlavor[] flavors = new DataFlavor[nFlavors]; // fill in the array int nDone = 0; if (nRicher > 0) { System.arraycopy(richerFlavors, 0, flavors, nDone, nRicher); nDone += nRicher; } if (nHTML > 0) { System.arraycopy(htmlFlavors, 0, flavors, nDone, nHTML); nDone += nHTML; } if (nPlain > 0) { System.arraycopy(plainFlavors, 0, flavors, nDone, nPlain); nDone += nPlain; } if (nString > 0) { System.arraycopy(stringFlavors, 0, flavors, nDone, nString); nDone += nString; } return flavors; } /** * Returns whether or not the specified data flavor is supported for * this object. * @param flavor the requested flavor for the data * @return boolean indicating whether or not the data flavor is supported */ public boolean isDataFlavorSupported(DataFlavor flavor) { DataFlavor[] flavors = getTransferDataFlavors(); for (int i = 0; i < flavors.length; i++) { if (flavors[i].equals(flavor)) { return true; } } return false; } /** * Returns an object which represents the data to be transferred. The class * of the object returned is defined by the representation class of the flavor. * * @param flavor the requested flavor for the data * @see DataFlavor#getRepresentationClass * @exception IOException if the data is no longer available * in the requested flavor. * @exception UnsupportedFlavorException if the requested data flavor is * not supported. */ public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (isRicherFlavor(flavor)) { return getRicherData(flavor); } else if (isHTMLFlavor(flavor)) { String data = getHTMLData(); data = (data == null) ? "" : data; if (String.class.equals(flavor.getRepresentationClass())) { return data; } else if (Reader.class.equals(flavor.getRepresentationClass())) { return new StringReader(data); } else if ( InputStream.class.equals(flavor.getRepresentationClass())) { return new StringBufferInputStream(data); } // fall through to unsupported } else if (isPlainFlavor(flavor)) { String data = getPlainData(); data = (data == null) ? "" : data; if (String.class.equals(flavor.getRepresentationClass())) { return data; } else if (Reader.class.equals(flavor.getRepresentationClass())) { return new StringReader(data); } else if ( InputStream.class.equals(flavor.getRepresentationClass())) { return new StringBufferInputStream(data); } // fall through to unsupported } else if (isStringFlavor(flavor)) { String data = getPlainData(); data = (data == null) ? "" : data; return data; } throw new UnsupportedFlavorException(flavor); } // --- richer subclass flavors ---------------------------------------------- protected boolean isRicherFlavor(DataFlavor flavor) { DataFlavor[] richerFlavors = getRicherFlavors(); int nFlavors = (richerFlavors != null) ? richerFlavors.length : 0; for (int i = 0; i < nFlavors; i++) { if (richerFlavors[i].equals(flavor)) { return true; } } return false; } /** * Some subclasses will have flavors that are more descriptive than HTML * or plain text. If this method returns a non-null value, it will be * placed at the start of the array of supported flavors. */ protected DataFlavor[] getRicherFlavors() { return null; } protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException { return null; } // --- html flavors ---------------------------------------------------------- /** * Returns whether or not the specified data flavor is an HTML flavor that * is supported. * @param flavor the requested flavor for the data * @return boolean indicating whether or not the data flavor is supported */ protected boolean isHTMLFlavor(DataFlavor flavor) { DataFlavor[] flavors = htmlFlavors; for (int i = 0; i < flavors.length; i++) { if (flavors[i].equals(flavor)) { return true; } } return false; } /** * Whether the HTML flavors are offered. If so, the method * getHTMLData should be implemented to provide something reasonable. */ protected boolean isHTMLSupported() { return false; } /** * Fetch the data in a text/html format */ protected String getHTMLData() { return null; } // --- plain text flavors ---------------------------------------------------- /** * Returns whether or not the specified data flavor is an plain flavor that * is supported. * @param flavor the requested flavor for the data * @return boolean indicating whether or not the data flavor is supported */ protected boolean isPlainFlavor(DataFlavor flavor) { DataFlavor[] flavors = plainFlavors; for (int i = 0; i < flavors.length; i++) { if (flavors[i].equals(flavor)) { return true; } } return false; } /** * Whether the plain text flavors are offered. If so, the method * getPlainData should be implemented to provide something reasonable. */ protected boolean isPlainSupported() { return false; } /** * Fetch the data in a text/plain format. */ protected String getPlainData() { return null; } // --- string flavorss -------------------------------------------------------- /** * Returns whether or not the specified data flavor is a String flavor that * is supported. * @param flavor the requested flavor for the data * @return boolean indicating whether or not the data flavor is supported */ protected boolean isStringFlavor(DataFlavor flavor) { DataFlavor[] flavors = stringFlavors; for (int i = 0; i < flavors.length; i++) { if (flavors[i].equals(flavor)) { return true; } } return false; } }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/plaf/GraphUI.java0000644000175000017500000000560511256667036023733 0ustar gregoagregoa/* * @(#)GraphUI.java 1.0 03-JUL-04 * * Copyright (c) 2001-2004 Gaudenz Alder * */ package org.jgraph.plaf; import java.awt.Graphics; import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; import javax.swing.plaf.ComponentUI; import org.jgraph.JGraph; import org.jgraph.graph.CellHandle; import org.jgraph.graph.CellView; /** * Pluggable look and feel interface for JGraph. * * @version 1.0 1/1/02 * @author Gaudenz Alder */ public abstract class GraphUI extends ComponentUI { /** * Paints the renderer of view to g * at bounds. */ public abstract void paintCell( Graphics g, CellView view, Rectangle2D bounds, boolean preview); /** * Paints the renderers of portViews to g. */ public abstract void paintPorts(Graphics g, CellView[] portViews); /** * Messaged to update the selection based on a MouseEvent for a group of * cells. If the event is a toggle selection event, the cells are either * selected, or deselected. Otherwise the cells are selected. */ public abstract void selectCellsForEvent( JGraph graph, Object[] cells, MouseEvent event); /** * Returns the preferred size for view. */ public abstract Dimension2D getPreferredSize(JGraph graph, CellView view); /** * Returns the CellHandle that is currently active, * or null if no handle is active. */ public abstract CellHandle getHandle(); /** * Returns true if the graph is being edited. The item that is being * edited can be returned by getEditingCell(). */ public abstract boolean isEditing(JGraph graph); /** * Stops the current editing session. This has no effect if the * graph isn't being edited. Returns true if the editor allows the * editing session to stop. */ public abstract boolean stopEditing(JGraph graph); /** * Cancels the current editing session. This has no effect if the * graph isn't being edited. Returns true if the editor allows the * editing session to stop. */ public abstract void cancelEditing(JGraph graph); /** * Selects the cell and tries to edit it. Editing will * fail if the CellEditor won't allow it for the selected item. */ public abstract void startEditingAtCell(JGraph graph, Object cell); /** * Returns the cell that is being edited. */ public abstract Object getEditingCell(JGraph graph); /** * Sets the current location for Drag-and-Drop activity. Should be set to * null after a drop. */ public abstract void setInsertionLocation(Point p); /** * Returns the insertion location for DnD operations. */ public abstract Point getInsertionLocation(); /** * Updates the handle. */ public abstract void updateHandle(); /** * Returns the current drop action. */ public abstract int getDropAction(); }libjgraph-java-5.12.4.2+dfsg.orig/src/org/jgraph/JGraph.java0000644000175000017500000030201411256667036022657 0ustar gregoagregoa/* * $Id: JGraph.java,v 1.96 2009/09/24 13:54:11 david Exp $ * * Copyright (c) 2001-2009 JGraph Ltd * */ package org.jgraph; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.event.MouseEvent; import java.awt.geom.Dimension2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.VolatileImage; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Vector; import javax.accessibility.Accessible; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JViewport; import javax.swing.Scrollable; import javax.swing.SwingConstants; import org.jgraph.event.GraphSelectionEvent; import org.jgraph.event.GraphSelectionListener; import org.jgraph.event.GraphLayoutCacheEvent.GraphLayoutCacheChange; import org.jgraph.graph.AbstractCellView; import org.jgraph.graph.AttributeMap; import org.jgraph.graph.BasicMarqueeHandler; import org.jgraph.graph.CellView; import org.jgraph.graph.ConnectionSet; import org.jgraph.graph.DefaultCellViewFactory; import org.jgraph.graph.DefaultEdge; import org.jgraph.graph.DefaultGraphCell; import org.jgraph.graph.DefaultGraphModel; import org.jgraph.graph.DefaultGraphSelectionModel; import org.jgraph.graph.GraphConstants; import org.jgraph.graph.GraphLayoutCache; import org.jgraph.graph.GraphModel; import org.jgraph.graph.GraphSelectionModel; import org.jgraph.graph.PortView; import org.jgraph.plaf.GraphUI; import org.jgraph.plaf.basic.BasicGraphUI; /** * A control that displays a network of related objects using the well-known * paradigm of a graph. *

    * A JGraph object doesn't actually contain your data; it simply provides a view * of the data. Like any non-trivial Swing component, the graph gets data by * querying its data model. *

    * JGraph displays its data by drawing individual elements. Each element * displayed by the graph contains exactly one item of data, which is called a * cell. A cell may either be a vertex or an edge. Vertices may have neighbours * or not, and edges may have source and target vertices or not, depending on * whether they are connected. *

    * Creating a Graph *

    * The following code creates a JGraph object: *

    * JGraph graph = new JGraph();
    * ...
    * JScrollPane graphLayoutCache = new JScrollPane(graph) *

    * The code creates an instance of JGraph and puts it in a scroll pane. JGraphs * constructor is called with no arguments in this example, which causes the * constructor to create a sample model. *

    * Editing *

    * Outmoved, cloned, resized, and shaped, or connected/disconnected to or from * other cells. *

    * Keyboard Bindings *

    * JGraph defines the following set of keyboard bindings: *

    *

      *
    • Alt-Click forces marquee selection if over a cell. *
    • Shift- or Ctrl-Select extends or toggles the selection. *
    • Shift-Drag constrains the offset to one direction. *
    • Ctrl-Drag clones the selection. *
    • Doubleclick/F2 starts editing a cell. *
    * You can change the number of clicks that triggers editing using * setEditClickCount(). *

    * Customization *

    * There are a number of additional methods that customize JGraph. For example, * setMinimumMove() defines the minimum amount of pixels before a move operation * is initiated. setSnapSize() defines the maximum distance for a cell to be * selected. setFloatEnabled() enables/disables port floating. *

    * With setDisconnectOnMove() you can indicate if the selected subgraph should * be disconnected from the unselected rest when a move operation is initiated. * setDragEnabled() enables/disables the use of Drag And Drop, and * setDropEnabled() sets if the graph accepts Drops from external sources. *

    * Customizing a graphs display *

    * JGraph performs some look-and-feel specific painting. You can customize this * painting in a limited way. For example, you can modify the grid using * setGridColor() and setGridSize(), and you can change the handle colors using * setHandleColor() and setLockedHandleColor(). *

    * If you want finer control over the rendering, you can subclass one of the * default renderers, and extend its paint()-method. A renderer is a * Component-extension that paints a cell based on its attributes. Thus, neither * the JGraph nor its look-and-feel-specific implementation actually contain the * code that paints the cell. Instead, the graph uses the cell renderers * painting code. *

    * Selection *

    * Apart from the single-cell and marquee-selection, JGraphs selection model * also allows to "step-into" groups, and select children. This feature can be * disabled using the setAllowsChildSelection() method of the selection model * instance. *

    * If you are interested in knowing when the selection changes implement the * GraphSelectionListener interface and add the instance using * the method addGraphSelectionListener. * valueChanged will be invoked when the selection changes, that * is if the user clicks twice on the same vertex valueChanged * will only be invoked once. *

    * Change Notification *

    * For detection of double-clicks or when a user clicks on a cell, regardless of * whether or not it was selected, I recommend you implement a MouseListener and * use getFirstCellForLocation. *

    * Undo Support *

    * To enable Undo-Support, a GraphUndoManager must be added using * addGraphSelectionListener. The GraphUndoManager is an * extension of Swing's GraphUndoManager that maintains a command * history in the context of multiple views. In this setup, a cell may have a * set of attributes in each view attached to the model. *

    * For example, consider a position that is stored separately in each view. If a * node is inserted, the change will be visible in all attached views, resulting * in a new node that pops-up at the initial position. If the node is * subsequently moved, say, in view1, this does not constitute a change in * view2. If view2 does an "undo", the move and the insertion must be * undone, whereas an "undo" in view1 will only undo the previous move * operation. *

    * Like all JComponent classes, you can use * {@link javax.swing.InputMap}and {@link javax.swing.ActionMap}to associate * an {@link javax.swing.Action}object with a {@link javax.swing.KeyStroke}and * execute the action under specified conditions. * * @author Gaudenz Alder * @version 2.1 16/03/03 * */ public class JGraph extends JComponent implements Scrollable, Accessible, Serializable { public static final String VERSION = "JGraph (v5.12.4.2)"; public static final int DOT_GRID_MODE = 0; public static final int CROSS_GRID_MODE = 1; public static final int LINE_GRID_MODE = 2; // Turn off XOR painting on MACs since it doesn't work public static boolean IS_MAC = false; static { try { String osName = System.getProperty("os.name"); if (osName != null) { IS_MAC = osName.toLowerCase().startsWith("mac os x"); } String javaVersion = System.getProperty("java.version"); if (javaVersion.startsWith("1.4") || javaVersion.startsWith("1.5")) { // TODO different double buffering for 1.6 JVM? } } catch (Exception e) { // ignore } } /** * @see #getUIClassID * @see #readObject */ private static final String uiClassID = "GraphUI"; /** Creates a new event and passes it off the selectionListeners. */ protected transient GraphSelectionRedirector selectionRedirector; // // Bound Properties // /** * The model that defines the graph displayed by this object. Bound * property. */ transient protected GraphModel graphModel; /** * The view that defines the display properties of the model. Bound * property. */ transient protected GraphLayoutCache graphLayoutCache; /** Models the set of selected objects in this graph. Bound property. */ transient protected GraphSelectionModel selectionModel; /** Handler for marquee selection. */ transient protected BasicMarqueeHandler marquee; /** Off screen image for double buffering */ protected transient Image offscreen; /** The bounds of the offscreen buffer */ protected transient Rectangle2D offscreenBounds; /** The offset of the offscreen buffer */ protected transient Point2D offscreenOffset; /** Graphics object of off screen image */ protected transient Graphics offgraphics; /** Whether or not the current background image is correct */ protected transient Rectangle2D offscreenDirty = null; protected transient boolean wholeOffscreenDirty = false; protected transient double wholeOffscreenDirtyProportion = 0.8; /** * The buffer around the offscreen graphics object that provides the * specified distance of scrolling before the buffer has to be recreated. * Increasing the value means fewer buffer allocations but more * memory usage for the current buffer */ protected transient int offscreenBuffer = 300; /** * Whether or not to try to use a volatile offscreen buffer for double * buffering. Volatile */ protected boolean volatileOffscreen = false; /** Stores whether the last double buffer allocation worked or not */ protected boolean lastBufferAllocated = true; /** Holds the background image. */ protected ImageIcon backgroundImage; /** A Component responsible for drawing the background image, if any */ protected Component backgroundComponent; /** Whether or not the background image is scaled on zooming */ protected boolean backgroundScaled = true; /** Scale of the graph. Default is 1. Bound property. */ protected double scale = 1.0; /** True if the graph is anti-aliased. Default is false. Bound property. */ protected boolean antiAliased = false; /** True if the graph allows editing the value of a cell. Bound property. */ protected boolean editable = true; /** True if the graph allows editing of non-leaf cells. Bound property. */ protected boolean groupsEditable = false; /** * True if the graph allows selection of cells. Note: You must also disable * selectNewCells if you disable this. Bound property. */ protected boolean selectionEnabled = true; /** * True if the graph allows invalid null ports during previews (aka flip * back edges). Default is true. */ protected boolean previewInvalidNullPorts = true; /** True if the grid is visible. Bound property. */ protected boolean gridVisible = false; /** The size of the grid in points. Default is 10. Bound property. */ protected double gridSize = 10; /** The style of the grid. Use one of the _GRID_MODE constants. */ protected int gridMode = DOT_GRID_MODE; /** True if the ports are visible. Bound property. */ protected boolean portsVisible = false; /** True if the ports are scaled. Bound property. */ protected boolean portsScaled = true; /** True if port are painted above all other cells. */ protected boolean portsOnTop = true; /** True if the graph allows to move cells below zero. */ protected boolean moveBelowZero = false; /** True if the graph allows to move cells beyond the graph bounds */ protected boolean moveBeyondGraphBounds = true; /** True if the labels on edges may be moved. */ protected boolean edgeLabelsMovable = true; /** * True if the graph should be auto resized when cells are moved below the * bottom right corner. Default is true. */ protected boolean autoResizeGraph = true; // // Look-And-Feel dependent // /** Highlight Color. This color is used to draw the selection border of * unfocused cells. Changes when the Look-and-Feel changes. */ protected Color highlightColor = Color.green; /** * Color of the handles and locked handles. Changes when the Look-and-Feel * changes. This color is also used to draw the selection border * of focused cells. */ protected Color handleColor, lockedHandleColor; /** Color of the marquee. Changes when the Look-and-Feel changes. */ protected Color marqueeColor; /** The color of the grid. This color is used to draw the selection border * for cells with selected children. Changes when the Look-and-Feel changes. */ protected Color gridColor; // // Datatransfer // /** * True if Drag-and-Drop should be used for move operations. Default is * false due to a JDK bug. */ protected boolean dragEnabled = false; /** * True if the graph accepts transfers from other components (graphs). This * also affects the clipboard. Default is true. */ protected boolean dropEnabled = true; /** * True if the graph accepts transfers from other components (graphs). This * also affects the clipboard. Default is true. */ protected boolean xorEnabled = !IS_MAC; // // Unbound Properties // /** Number of clicks for editing to start. Default is 2 clicks. */ protected int editClickCount = 2; /** True if the graph allows interactions. Default is true. */ protected boolean enabled = true; /** True if the snap method should be active (snap to grid). */ protected boolean gridEnabled = false; /** Size of a handle. Default is 3 pixels. */ protected int handleSize = 3; /** Maximum distance between a cell and the mousepointer. Default is 4. */ protected int tolerance = 4; /** Minimum amount of pixels to start a move transaction. Default is 5. */ protected int minimumMove = 5; /** * True if getPortViewAt should return the default port if no other port is * found. Default is false. */ protected boolean isJumpToDefaultPort = false; /** * Specifies if cells should be added to a group when moved over the group's * area. Default is false. */ protected boolean isMoveIntoGroups = false; /** * Specifies if cells should be removed from groups when removed from the * group area. Default is false. */ protected boolean isMoveOutOfGroups = false; /** * True if selected edges are disconnected from unselected vertices on move. * Default is false. */ protected boolean disconnectOnMove = false; /** True if the graph allows move operations. Default is true. */ protected boolean moveable = true; /** True if the graph allows "ctrl-drag" operations. Default is false. */ protected boolean cloneable = false; /** True if the graph allows cells to be resized. Default is true. */ protected boolean sizeable = true; /** * True if the graph allows points to be modified/added/removed. Default is * true. */ protected boolean bendable = true; /** * True if the graph allows new connections to be established. Default is * true. */ protected boolean connectable = true; /** * True if the graph allows existing connections to be removed. Default is * true. */ protected boolean disconnectable = true; /** * If true, when editing is to be stopped by way of selection changing, data * in graph changing or other means stopCellEditing is * invoked, and changes are saved. If false, cancelCellEditing * is invoked, and changes are discarded. */ protected boolean invokesStopCellEditing; // // Bound propery names // /** * Bound property name for graphModel. */ public final static String GRAPH_MODEL_PROPERTY = "model"; /** * Bound property name for graphModel. */ public final static String GRAPH_LAYOUT_CACHE_PROPERTY = "view"; /** * Bound property name for graphModel. */ public final static String MARQUEE_HANDLER_PROPERTY = "marquee"; /** * Bound property name for editable. */ public final static String EDITABLE_PROPERTY = "editable"; /** * Bound property name for selectionEnabled. */ public final static String SELECTIONENABLED_PROPERTY = "selectionEnabled"; /** * Bound property name for scale. */ public final static String SCALE_PROPERTY = "scale"; /** * Bound property name for antiAliased. */ public final static String ANTIALIASED_PROPERTY = "antiAliased"; /** * Bound property name for gridSize. */ public final static String GRID_SIZE_PROPERTY = "gridSize"; /** * Bound property name for gridVisible. */ public final static String GRID_VISIBLE_PROPERTY = "gridVisible"; /** * Bound property name for gridColor. */ public final static String GRID_COLOR_PROPERTY = "gridColor"; /** * Bound property name for gridColor. */ public final static String HANDLE_COLOR_PROPERTY = "handleColor"; /** * Bound property name for gridColor. */ public final static String HANDLE_SIZE_PROPERTY = "handleSize"; /** * Bound property name for gridColor. */ public final static String LOCKED_HANDLE_COLOR_PROPERTY = "lockedHandleColor"; /** * Bound property name for gridVisible. */ public final static String PORTS_VISIBLE_PROPERTY = "portsVisible"; /** * Bound property name for portsScaled. */ public final static String PORTS_SCALED_PROPERTY = "portsScaled"; /** * Bound property name for selectionModel. */ public final static String SELECTION_MODEL_PROPERTY = "selectionModel"; /** * Bound property name for messagesStopCellEditing. */ public final static String INVOKES_STOP_CELL_EDITING_PROPERTY = "invokesStopCellEditing"; /** * Bound property name for backgroundImage. */ public final static String PROPERTY_BACKGROUNDIMAGE = "backgroundImage"; /** * Creates and returns a sample GraphModel. Used primarily * for beanbuilders to show something interesting. */ public static void addSampleData(GraphModel model) { ConnectionSet cs = new ConnectionSet(); Map attributes = new Hashtable(); // Styles For Implement/Extend/Aggregation AttributeMap implementStyle = new AttributeMap(); GraphConstants.setLineBegin(implementStyle, GraphConstants.ARROW_TECHNICAL); GraphConstants.setBeginSize(implementStyle, 10); GraphConstants.setDashPattern(implementStyle, new float[] { 3, 3 }); if (GraphConstants.DEFAULTFONT != null) { GraphConstants.setFont(implementStyle, GraphConstants.DEFAULTFONT .deriveFont(10)); } AttributeMap extendStyle = new AttributeMap(); GraphConstants .setLineBegin(extendStyle, GraphConstants.ARROW_TECHNICAL); GraphConstants.setBeginFill(extendStyle, true); GraphConstants.setBeginSize(extendStyle, 10); if (GraphConstants.DEFAULTFONT != null) { GraphConstants.setFont(extendStyle, GraphConstants.DEFAULTFONT .deriveFont(10)); } AttributeMap aggregateStyle = new AttributeMap(); GraphConstants.setLineBegin(aggregateStyle, GraphConstants.ARROW_DIAMOND); GraphConstants.setBeginFill(aggregateStyle, true); GraphConstants.setBeginSize(aggregateStyle, 6); GraphConstants.setLineEnd(aggregateStyle, GraphConstants.ARROW_SIMPLE); GraphConstants.setEndSize(aggregateStyle, 8); GraphConstants.setLabelPosition(aggregateStyle, new Point2D.Double(500, 0)); if (GraphConstants.DEFAULTFONT != null) { GraphConstants.setFont(aggregateStyle, GraphConstants.DEFAULTFONT .deriveFont(10)); } // // The Swing MVC Pattern // // Model Column DefaultGraphCell gm = new DefaultGraphCell("GraphModel"); attributes.put(gm, createBounds(new AttributeMap(), 20, 100, Color.blue)); gm.addPort(null, "GraphModel/Center"); DefaultGraphCell dgm = new DefaultGraphCell("DefaultGraphModel"); attributes.put(dgm, createBounds(new AttributeMap(), 20, 180, Color.blue)); dgm.addPort(null, "DefaultGraphModel/Center"); DefaultEdge dgmImplementsGm = new DefaultEdge("implements"); cs.connect(dgmImplementsGm, gm.getChildAt(0), dgm.getChildAt(0)); attributes.put(dgmImplementsGm, implementStyle); DefaultGraphCell modelGroup = new DefaultGraphCell("ModelGroup"); modelGroup.add(gm); modelGroup.add(dgm); modelGroup.add(dgmImplementsGm); // JComponent Column DefaultGraphCell jc = new DefaultGraphCell("JComponent"); attributes.put(jc, createBounds(new AttributeMap(), 180, 20, Color.green)); jc.addPort(null, "JComponent/Center"); DefaultGraphCell jg = new DefaultGraphCell("JGraph"); attributes.put(jg, createBounds(new AttributeMap(), 180, 100, Color.green)); jg.addPort(null, "JGraph/Center"); DefaultEdge jgExtendsJc = new DefaultEdge("extends"); cs.connect(jgExtendsJc, jc.getChildAt(0), jg.getChildAt(0)); attributes.put(jgExtendsJc, extendStyle); // UI Column DefaultGraphCell cu = new DefaultGraphCell("ComponentUI"); attributes .put(cu, createBounds(new AttributeMap(), 340, 20, Color.red)); cu.addPort(null, "ComponentUI/Center"); DefaultGraphCell gu = new DefaultGraphCell("GraphUI"); attributes.put(gu, createBounds(new AttributeMap(), 340, 100, Color.red)); gu.addPort(null, "GraphUI/Center"); DefaultGraphCell dgu = new DefaultGraphCell("BasicGraphUI"); attributes.put(dgu, createBounds(new AttributeMap(), 340, 180, Color.red)); dgu.addPort(null, "BasicGraphUI/Center"); DefaultEdge guExtendsCu = new DefaultEdge("extends"); cs.connect(guExtendsCu, cu.getChildAt(0), gu.getChildAt(0)); attributes.put(guExtendsCu, extendStyle); DefaultEdge dguImplementsDu = new DefaultEdge("implements"); cs.connect(dguImplementsDu, gu.getChildAt(0), dgu.getChildAt(0)); attributes.put(dguImplementsDu, implementStyle); DefaultGraphCell uiGroup = new DefaultGraphCell("UIGroup"); uiGroup.add(cu); uiGroup.add(gu); uiGroup.add(dgu); uiGroup.add(dguImplementsDu); uiGroup.add(guExtendsCu); // Aggregations DefaultEdge jgAggregatesGm = new DefaultEdge("model"); cs.connect(jgAggregatesGm, jg.getChildAt(0), gm.getChildAt(0)); attributes.put(jgAggregatesGm, aggregateStyle); DefaultEdge jcAggregatesCu = new DefaultEdge("ui"); cs.connect(jcAggregatesCu, jc.getChildAt(0), cu.getChildAt(0)); attributes.put(jcAggregatesCu, aggregateStyle); // Insert Cells into model Object[] cells = new Object[] { jgAggregatesGm, jcAggregatesCu, modelGroup, jc, jg, jgExtendsJc, uiGroup }; model.insert(cells, attributes, cs, null, null); } /** * Returns an attributeMap for the specified position and color. */ public static Map createBounds(AttributeMap map, int x, int y, Color c) { GraphConstants.setBounds(map, map.createRect(x, y, 90, 30)); GraphConstants.setBorder(map, BorderFactory.createRaisedBevelBorder()); GraphConstants.setBackground(map, c.darker().darker()); GraphConstants .setGradientColor(map, c.brighter().brighter().brighter()); GraphConstants.setForeground(map, Color.white); if (GraphConstants.DEFAULTFONT != null) { GraphConstants.setFont(map, GraphConstants.DEFAULTFONT.deriveFont( Font.BOLD, 12)); } GraphConstants.setOpaque(map, true); return map; } /** * Returns a JGraph with a sample model. */ public JGraph() { this((GraphModel) null); } /** * Returns an instance of JGraph which displays the the * specified data model. * * @param model * the GraphModel to use as the data model */ public JGraph(GraphModel model) { this(model, (GraphLayoutCache) null); } /** * Returns an instance of JGraph which displays the data * model using the specified view. * * @param cache * the GraphLayoutCache to use as the view */ public JGraph(GraphLayoutCache cache) { this((cache != null) ? cache.getModel() : null, cache); } /** * Returns an instance of JGraph which displays the specified * data model using the specified view. * * @param model * the GraphModel to use as the data model * @param cache * the GraphLayoutCache to use as the cache */ public JGraph(GraphModel model, GraphLayoutCache cache) { this(model, cache, new BasicMarqueeHandler()); } /** * Returns an instance of JGraph which displays the specified * data model and assigns the specified marquee handler * * @param model * the GraphModel to use as the data model * @param mh * the BasicMarqueeHandler to use as the marquee * handler */ public JGraph(GraphModel model, BasicMarqueeHandler mh) { this(model, null, mh); } /** * Returns an instance of JGraph which displays the specified * data model using the specified view and assigns the specified marquee * handler * * @param model * the GraphModel to use as the data model * @param layoutCache * the GraphLayoutCache to use as the cache * @param mh * the BasicMarqueeHandler to use as the marquee * handler */ public JGraph(GraphModel model, GraphLayoutCache layoutCache, BasicMarqueeHandler mh) { setDoubleBuffered(true); selectionModel = new DefaultGraphSelectionModel(this); setLayout(null); marquee = mh; if (model == null) { model = new DefaultGraphModel(); setModel(model); addSampleData(model); } else setModel(model); if (layoutCache == null) layoutCache = new GraphLayoutCache(model, new DefaultCellViewFactory()); setGraphLayoutCache(layoutCache); updateUI(); } // // UI-delegate (GraphUI) // /** * Returns the L&F object that renders this component. * * @return the GraphUI object that renders this component */ public GraphUI getUI() { return (GraphUI) ui; } /** * Sets the L&F object that renders this component. * * @param ui * the GraphUI L&F object * @see javax.swing.UIDefaults#getUI(JComponent) * */ public void setUI(GraphUI ui) { if ((GraphUI) this.ui != ui) { super.setUI(ui); } } /** * Notification from the UIManager that the L&F has changed. * Replaces the current UI object with the latest version from the * UIManager. Subclassers can override this to support * different GraphUIs. * * @see JComponent#updateUI * */ public void updateUI() { setUI(new org.jgraph.plaf.basic.BasicGraphUI()); invalidate(); } /** * Returns the name of the L&F class that renders this component. * * @return the string "GraphUI" * @see JComponent#getUIClassID * */ public String getUIClassID() { return uiClassID; } // // Content // /** * Returns all root cells (cells that have no parent) that the model * contains. */ public Object[] getRoots() { return DefaultGraphModel.getRoots(graphModel); } /** * Returns all cells that intersect the given rectangle. */ public Object[] getRoots(Rectangle clip) { CellView[] views = graphLayoutCache.getRoots(clip); Object[] cells = new Object[views.length]; for (int i = 0; i < views.length; i++) cells[i] = views[i].getCell(); return cells; } /** * Returns all cells including all descendants in the passed * in order of cells. */ public Object[] getDescendants(Object[] cells) { return DefaultGraphModel.getDescendants(getModel(), cells).toArray(); } /** * Returns all cells including all descendants ordered using * the current layering data stored by the model. */ public Object[] order(Object[] cells) { return DefaultGraphModel.order(getModel(), cells); } /** * Returns a map of (cell, clone)-pairs for all cells and * their children. Special care is taken to replace the anchor references * between ports. (Iterative implementation.) */ public Map cloneCells(Object[] cells) { return graphModel.cloneCells(cells); } /** * Returns the topmost cell view at the specified location using the view's * bounds on non-leafs to check for containment. If reverse is true this * will return the innermost view. */ public CellView getTopmostViewAt(double x, double y, boolean reverse, boolean leafsOnly) { Rectangle2D r = new Rectangle2D.Double(x, y, 1, 1); Object[] cells = getDescendants(getRoots()); for (int i = (reverse) ? cells.length - 1 : 0; i >= 0 && i < cells.length; i += (reverse) ? -1 : +1) { CellView view = getGraphLayoutCache().getMapping(cells[i], false); if (view != null && (!leafsOnly || view.isLeaf()) && ((view.isLeaf() && view.intersects(this, r)) || (!view .isLeaf() && view.getBounds().contains(x, y)))) return view; } return null; } /** * Returns the topmost cell at the specified location. * * @param x * an integer giving the number of pixels horizontally from the * left edge of the display area, minus any left margin * @param y * an integer giving the number of pixels vertically from the top * of the display area, minus any top margin * @return the topmost cell at the specified location */ public Object getFirstCellForLocation(double x, double y) { return getNextCellForLocation(null, x, y); } /** * Returns the cell at the specified location that is "behind" the * current cell. Returns the topmost cell if there are no * more cells behind current. Note: This does only return * visible cells. */ public Object getNextCellForLocation(Object current, double x, double y) { CellView cur = graphLayoutCache.getMapping(current, false); CellView cell = getNextViewAt(cur, x, y); if (cell != null) return cell.getCell(); return null; } /** * Returns the bounding rectangle of the specified cell. */ public Rectangle2D getCellBounds(Object cell) { CellView view = graphLayoutCache.getMapping(cell, false); if (view != null) return view.getBounds(); return null; } /** * Returns the bounding rectangle of the specified cells. */ public Rectangle2D getCellBounds(Object[] cells) { if (cells != null && cells.length > 0) { Rectangle2D r = getCellBounds(cells[0]); Rectangle2D ret = (r != null) ? (Rectangle2D) r.clone() : null; for (int i = 1; i < cells.length; i++) { r = getCellBounds(cells[i]); if (r != null) { if (ret == null) ret = (r != null) ? (Rectangle2D) r.clone() : null; else Rectangle2D.union(ret, r, ret); } } return ret; } return null; } /** * Returns the next view at the specified location wrt. current. * This is used to iterate overlapping cells, and cells that are grouped. * The current selection affects this method.
    * Note: This returns the next selectable view.
    * Note: Arguments are not expected to be scaled (they are scaled in here). */ public CellView getNextViewAt(CellView current, double x, double y) { return getNextViewAt(current, x, y, false); } /** * Returns the next view at the specified location wrt. current. * This is used to iterate overlapping cells, and cells that are grouped. * The current selection affects this method.
    * Note: This returns the next selectable view.
    * Note: Arguments are not expected to be scaled (they are scaled in here). */ public CellView getNextViewAt(CellView current, double x, double y, boolean leafsOnly) { CellView[] cells = AbstractCellView .getDescendantViews(getGraphLayoutCache().getRoots()); return getNextViewAt(cells, current, x, y, leafsOnly); } /** * Note: Arguments are not expected to be scaled (they are scaled in here). */ public CellView getNextSelectableViewAt(CellView current, double x, double y) { CellView[] selectables = getGraphLayoutCache().getMapping( getSelectionModel().getSelectables(), false); return getNextViewAt(selectables, current, x, y); } /** * Returns the next view at the specified location wrt. c in * the specified array of views. The views must be in order, as returned, * for example, by GraphLayoutCache.order(Object[]). */ public CellView getNextViewAt(CellView[] cells, CellView c, double x, double y) { return getNextViewAt(cells, c, x, y, false); } /** * Returns the next view at the specified location wrt. c in * the specified array of views. The views must be in order, as returned, * for example, by GraphLayoutCache.order(Object[]). */ public CellView getNextViewAt(CellView[] cells, CellView c, double x, double y, boolean leafsOnly) { if (cells != null) { Rectangle2D r = fromScreen(new Rectangle2D.Double(x - tolerance, y - tolerance, 2 * tolerance, 2 * tolerance)); // Ensure the tolerance is at least 1.0 (can go below in high zoom // in case). if (r.getWidth() < 1.0) { r.setFrame(r.getX(), r.getY(), 1.0, r.getHeight()); } if (r.getHeight() < 1.0) { r.setFrame(r.getX(), r.getY(), r.getWidth(), 1.0); } // Iterate through cells and switch to active // if current is traversed. Cache first cell. CellView first = null; boolean active = (c == null); for (int i = 0; i < cells.length; i++) { if (cells[i] != null && (!leafsOnly || cells[i].isLeaf()) && cells[i].intersects(this, r)) { // TODO: This behaviour is specific to selection and // should be parametrized (it only returns a group with // selected children if no other portview is available) if (active && !selectionModel.isChildrenSelected(cells[i] .getCell())) { return cells[i]; } else if (first == null) first = cells[i]; active = active | (cells[i] == c); } } return first; } return null; } /** * Returns the next view at the specified location wrt. c in * the specified array of views. The views must be in order, as returned, * for example, by GraphLayoutCache.order(Object[]). */ public CellView getLeafViewAt(double x, double y) { return getNextViewAt(null, x, y, true); } /** * Convenience method to return the port at the specified location. */ public Object getPortForLocation(double x, double y) { PortView view = getPortViewAt(x, y, tolerance); if (view != null) return view.getCell(); return null; } /** * Returns the portview at the specified location.
    * Note: Arguments are not expected to be scaled (they are scaled in here). */ public PortView getPortViewAt(double x, double y) { return getPortViewAt(x, y, tolerance); } /** * Returns the portview at the specified location.
    * Note: Arguments are not expected to be scaled (they are scaled in here). */ public PortView getPortViewAt(double x, double y, int tolerance) { double sx = x / scale; double sy = y / scale; Rectangle2D r = new Rectangle2D.Double(sx - tolerance, sy - tolerance, 2 * tolerance, 2 * tolerance); PortView[] ports = graphLayoutCache.getPorts(); if (ports != null) { for (int i = ports.length - 1; i >= 0; i--) if (ports[i] != null && ports[i].intersects(this, r)) return ports[i]; if (isJumpToDefaultPort()) { CellView cellView = getNextViewAt(null, x, y, true); // Finds a non-edge cell under the mousepointer if (cellView != null && graphModel.isEdge(cellView.getCell())) { CellView nextView = getNextViewAt(cellView, x, y, true); while (nextView != cellView && graphModel.isEdge(nextView.getCell())) { nextView = getNextViewAt(nextView, x, y, true); } cellView = nextView; } if (cellView != null) { PortView defaultPort = getDefaultPortForCell(cellView .getCell()); return defaultPort; } } } return null; } /** * Returns the default portview for the specified cell. The default * implementation returns the first floating port (ie. the first port that * does not define an offset) or the port, if there is only one * port. * * @param cell * the cell whose port is to be obtained * @return the port view of the specified cell */ public PortView getDefaultPortForCell(Object cell) { if (cell != null && !getModel().isEdge(cell)) { int childCount = getModel().getChildCount(cell); for (int i = 0; i < childCount; i++) { Object childCell = getModel().getChild(cell, i); CellView child = getGraphLayoutCache().getMapping(childCell, false); if (child instanceof PortView) { Point2D offset = GraphConstants.getOffset(child .getAllAttributes()); if (offset == null || childCount == 1) return (PortView) child; } } } return null; } /** * Converts the specified value to string. If the value is an instance of * CellView then the corresponding value or cell is used. */ public String convertValueToString(Object value) { if (value instanceof CellView) value = ((CellView) value).getCell(); return String.valueOf(value); } // // Grid and Scale // /** * Returns the given point applied to the grid. * * @param p * a point in screen coordinates. * @return the same point applied to the grid. */ public Point2D snap(Point2D p) { if (gridEnabled && p != null) { double sgs = gridSize * getScale(); p.setLocation(Math.round(Math.round(p.getX() / sgs) * sgs), Math .round(Math.round(p.getY() / sgs) * sgs)); } return p; } /** * Returns the given rectangle applied to the grid. * * @param r * a rectangle in screen coordinates. * @return the same rectangle applied to the grid. */ public Rectangle2D snap(Rectangle2D r) { if (gridEnabled && r != null) { double sgs = gridSize * getScale(); r.setFrame(Math.round(Math.round(r.getX() / sgs) * sgs), Math .round(Math.round(r.getY() / sgs) * sgs), 1 + Math .round(Math.round(r.getWidth() / sgs) * sgs), 1 + Math .round(Math.round(r.getHeight() / sgs) * sgs)); } return r; } /** * Returns the given dimension applied to the grid. * * @param d * a dimension in screen coordinates to snap to. * @return the same dimension applied to the grid. */ public Dimension2D snap(Dimension2D d) { if (gridEnabled && d != null) { double sgs = gridSize * getScale(); d.setSize(1 + Math.round(Math.round(d.getWidth() / sgs) * sgs), 1 + Math.round(Math.round(d.getHeight() / sgs) * sgs)); } return d; } /** * Upscale the given point in place, using the given instance. * * @param p * the point to be upscaled * @return the upscaled point instance */ public Point2D toScreen(Point2D p) { if (p == null) return null; p.setLocation(Math.round(p.getX() * scale), Math .round(p.getY() * scale)); return p; } /** * Downscale the given point in place, using the given instance. * * @param p * the point to be downscaled * @return the downscaled point instance */ public Point2D fromScreen(Point2D p) { if (p == null) return null; p.setLocation(Math.round(p.getX() / scale), Math .round(p.getY() / scale)); return p; } /** * Upscale the given rectangle in place, using the given instance. * * @param rect * the rectangle to be upscaled * @return the upscaled rectangle instance */ public Rectangle2D toScreen(Rectangle2D rect) { if (rect == null) return null; rect.setFrame(rect.getX() * scale, rect.getY() * scale, rect.getWidth() * scale, rect.getHeight() * scale); return rect; } /** * Downscale the given rectangle in place, using the given instance. * * @param rect * the rectangle to be downscaled * @return the down-scaled rectangle instance */ public Rectangle2D fromScreen(Rectangle2D rect) { if (rect == null) return null; rect.setFrame(rect.getX() / scale, rect.getY() / scale, rect.getWidth() / scale, rect.getHeight() / scale); return rect; } /** * Computes and updates the size for view. */ public void updateAutoSize(CellView view) { if (view != null && !isEditing()) { Rectangle2D bounds = (view.getAttributes() != null) ? GraphConstants .getBounds(view.getAttributes()) : null; AttributeMap attrs = getModel().getAttributes(view.getCell()); if (bounds == null) bounds = GraphConstants.getBounds(attrs); if (bounds != null) { boolean autosize = GraphConstants.isAutoSize(view .getAllAttributes()); boolean resize = GraphConstants.isResize(view .getAllAttributes()); if (autosize || resize) { Dimension2D d = getUI().getPreferredSize(this, view); bounds.setFrame(bounds.getX(), bounds.getY(), d.getWidth(), d.getHeight()); // Remove resize attribute snap(bounds); if (resize) { if (view.getAttributes() != null) view.getAttributes().remove(GraphConstants.RESIZE); attrs.remove(GraphConstants.RESIZE); } view.refresh(getGraphLayoutCache(), getGraphLayoutCache(), false); } } } } /** * Returns the attributes for the specified cell. If the layout cache * returns a view for the cell then this method returns allAttributes, * otherwise the method returns model.getAttributes(cell). */ public AttributeMap getAttributes(Object cell) { AttributeMap attrs; CellView cellView = getGraphLayoutCache().getMapping(cell, false); if (cellView != null) { attrs = cellView.getAllAttributes(); } else { attrs = getModel().getAttributes(cell); } return attrs; } // // Unbound Properties // /** * Returns the number of clicks for editing to start. */ public int getEditClickCount() { return editClickCount; } /** * Sets the number of clicks for editing to start. */ public void setEditClickCount(int count) { editClickCount = count; } /** * Returns true if the graph accepts drops/pastes from external sources. */ public boolean isDropEnabled() { return dropEnabled; } /** * Sets if the graph accepts drops/pastes from external sources. */ public void setDropEnabled(boolean flag) { dropEnabled = flag; } /** * Returns true if the graph accepts drops/pastes from external sources. */ public boolean isXorEnabled() { return (xorEnabled && isOpaque()); } /** * Sets if the graph accepts drops/pastes from external sources. */ public void setXorEnabled(boolean flag) { xorEnabled = flag; } /** * Returns true if the graph uses Drag-and-Drop to move cells. */ public boolean isDragEnabled() { return dragEnabled; } /** * Sets if the graph uses Drag-and-Drop to move cells. */ public void setDragEnabled(boolean flag) { dragEnabled = flag; } /* * Returns true if the graph allows movement of cells. */ public boolean isMoveable() { return moveable; } /** * Sets if the graph allows movement of cells. */ public void setMoveable(boolean flag) { moveable = flag; } /** * Returns true if the graph allows adding/removing/modifying points. */ public boolean isBendable() { return bendable; } /** * Sets if the graph allows adding/removing/modifying points. */ public void setBendable(boolean flag) { bendable = flag; } /** * Returns true if the graph allows new connections to be established. */ public boolean isConnectable() { return connectable; } /** * Setse if the graph allows new connections to be established. */ public void setConnectable(boolean flag) { connectable = flag; } /** * Returns true if the graph allows existing connections to be removed. */ public boolean isDisconnectable() { return disconnectable; } /** * Sets if the graph allows existing connections to be removed. */ public void setDisconnectable(boolean flag) { disconnectable = flag; } /** * Returns true if cells are cloned on CTRL-Drag operations. */ public boolean isCloneable() { return cloneable; } /** * Sets if cells are cloned on CTRL-Drag operations. */ public void setCloneable(boolean flag) { cloneable = flag; } /** * Returns true if the graph allows cells to be resized. */ public boolean isSizeable() { return sizeable; } /** * Sets if the graph allows cells to be resized. */ public void setSizeable(boolean flag) { sizeable = flag; } /** * Sets if selected edges should be disconnected from unselected vertices * when they are moved. */ public void setDisconnectOnMove(boolean flag) { disconnectOnMove = flag; } /** * Returns true if selected edges should be disconnected from unselected * vertices when they are moved. */ public boolean isDisconnectOnMove() { return disconnectOnMove && disconnectable; } /** * Sets if getPortViewAt should return the default port if no other port is * found. */ public void setJumpToDefaultPort(boolean flag) { isJumpToDefaultPort = flag; } /** * Returns true if getPortViewAt should return the default port if no other * port is found. */ public boolean isJumpToDefaultPort() { return isJumpToDefaultPort; } /** * Specifies if cells should be added to groups when moved over the group's * area. */ public void setMoveIntoGroups(boolean flag) { isMoveIntoGroups = flag; } /** * Returns true if cells should be added to groups when moved over the * group's area. */ public boolean isMoveIntoGroups() { return isMoveIntoGroups; } /** * Specifies if cells should be removed from groups when removed from the * group's area. */ public void setMoveOutOfGroups(boolean flag) { isMoveOutOfGroups = flag; } /** * Returns true if cells should be removed from groups when removed from the * group's area. */ public boolean isMoveOutOfGroups() { return isMoveOutOfGroups; } /** * Returns true if the grid is active. * * @see #snap(Point2D) * */ public boolean isGridEnabled() { return gridEnabled; } /** * If set to true, the grid will be active. * * @see #snap(Point2D) * */ public void setGridEnabled(boolean flag) { gridEnabled = flag; } /** * Returns true if the graph allows to move cells below zero. */ public boolean isMoveBelowZero() { return moveBelowZero; } /** * Sets if the graph should auto resize when cells are being moved below the * bottom right corner. */ public void setMoveBelowZero(boolean moveBelowZero) { this.moveBelowZero = moveBelowZero; } /** * @return the moveBeyondGraphBounds */ public boolean isMoveBeyondGraphBounds() { return moveBeyondGraphBounds; } /** * @param moveBeyondGraphBounds the moveBeyondGraphBounds to set */ public void setMoveBeyondGraphBounds(boolean moveBeyondGraphBounds) { this.moveBeyondGraphBounds = moveBeyondGraphBounds; } /** * Returns true if edge labels may be dragged and dropped. * * @return whether edge labels may be dragged and dropped */ public boolean getEdgeLabelsMovable() { return edgeLabelsMovable; } /** * Set if edge labels may be moved with the mouse or not. * * @param edgeLabelsMovable * true if edge labels may be dragged */ public void setEdgeLabelsMovable(boolean edgeLabelsMovable) { this.edgeLabelsMovable = edgeLabelsMovable; } /** * Returns true if the graph should be automatically resized when cells are * being moved below the bottom right corner. Note if the value of * moveBeyondGraphBounds if false auto resizing * is automatically disabled */ public boolean isAutoResizeGraph() { if (!moveBeyondGraphBounds) { return false; } return autoResizeGraph; } /** * Sets whether or not the graph should be automatically resize when cells * are being moved below the bottom right corner */ public void setAutoResizeGraph(boolean autoResizeGraph) { this.autoResizeGraph = autoResizeGraph; } /** * Returns the maximum distance between the mousepointer and a cell to be * selected. */ public int getTolerance() { return tolerance; } /** * Sets the maximum distance between the mousepointer and a cell to be * selected. */ public void setTolerance(int size) { if (size < 1) { size = 1; } tolerance = size; } /** * Returns the size of the handles. */ public int getHandleSize() { return handleSize; } /** * Sets the size of the handles. */ public void setHandleSize(int size) { int oldValue = handleSize; handleSize = size; firePropertyChange(HANDLE_SIZE_PROPERTY, oldValue, size); } /** * Returns the miminum amount of pixels for a move operation. */ public int getMinimumMove() { return minimumMove; } /** * Sets the miminum amount of pixels for a move operation. */ public void setMinimumMove(int pixels) { minimumMove = pixels; } // // Laf-Specific color scheme. These colors are changed // by BasicGraphUI when the laf changes. // /** * Returns the current grid color. */ public Color getGridColor() { return gridColor; } /** * Sets the current grid color. */ public void setGridColor(Color newColor) { Color oldValue = gridColor; gridColor = newColor; firePropertyChange(GRID_COLOR_PROPERTY, oldValue, newColor); } /** * Returns the current handle color. */ public Color getHandleColor() { return handleColor; } /** * Sets the current handle color. */ public void setHandleColor(Color newColor) { Color oldValue = handleColor; handleColor = newColor; firePropertyChange(HANDLE_COLOR_PROPERTY, oldValue, newColor); } /** * Returns the current second handle color. */ public Color getLockedHandleColor() { return lockedHandleColor; } /** * Sets the current second handle color. */ public void setLockedHandleColor(Color newColor) { Color oldValue = lockedHandleColor; lockedHandleColor = newColor; firePropertyChange(LOCKED_HANDLE_COLOR_PROPERTY, oldValue, newColor); } /** * Returns the current marquee color. */ public Color getMarqueeColor() { return marqueeColor; } /** * Sets the current marquee color. */ public void setMarqueeColor(Color newColor) { marqueeColor = newColor; } /** * Returns the current highlight color. */ public Color getHighlightColor() { return highlightColor; } /** * Sets the current selection highlight color. */ public void setHighlightColor(Color newColor) { highlightColor = newColor; } // // Bound properties // /** * Returns the current scale. * * @return the current scale as a double */ public double getScale() { return scale; } /** * Sets the current scale. *

    * Fires a property change for the SCALE_PROPERTY. * * @param newValue * the new scale */ public void setScale(double newValue) { Point2D centerPoint = getCenterPoint(); setScale(newValue, centerPoint); } /** * Sets the current scale and centers the graph to the specified point * * @param newValue * the new scale * @param center * the center of the graph */ public void setScale(double newValue, Point2D center) { if (newValue > 0 && newValue != this.scale) { Rectangle2D view = getViewPortBounds(); double oldValue = this.scale; scale = newValue; boolean zoomIn = true; Rectangle newView = null; clearOffscreen(); if (view != null) { double scaleRatio = newValue / oldValue; int newCenterX = (int) (center.getX() * scaleRatio); int newCenterY = (int) (center.getY() * scaleRatio); int newX = (int) (newCenterX - view.getWidth() / 2.0); int newY = (int) (newCenterY - view.getHeight() / 2.0); newView = new Rectangle(newX, newY, (int) view.getWidth(), (int) view.getHeight()); // When zooming out scroll before revalidation otherwise // revalidation causes one scroll and scrollRectToVisible // another if (scaleRatio < 1.0) { scrollRectToVisible(newView); zoomIn = false; } } firePropertyChange(SCALE_PROPERTY, oldValue, newValue); // When zooming in, do it after the revalidation otherwise // it intermittently moves to the old co-ordinate system if (zoomIn && newView != null) { scrollRectToVisible(newView); } } } /** * Invalidate the offscreen region, do not just delete it, since if the new * region is smaller than the old you may not wish to re-create the buffer */ public void clearOffscreen() { if (offscreen != null) { int h = offscreen.getHeight(this); int w = offscreen.getWidth(this); Rectangle2D dirtyRegion = new Rectangle2D.Double(0, 0, w, h); fromScreen(dirtyRegion); addOffscreenDirty(dirtyRegion); } } /** * Returns the center of the component relative to the parent viewport's * position. */ public Point2D getCenterPoint() { Rectangle2D viewBounds = getViewPortBounds(); if (viewBounds != null) { return new Point2D.Double(viewBounds.getCenterX(), viewBounds .getCenterY()); } viewBounds = getBounds(); return new Point2D.Double(viewBounds.getCenterX(), viewBounds .getCenterY()); } /** * Return the bounds of the parent viewport, if one exists. If one does not * exist, null is returned * * @return the bounds of the parent viewport */ public Rectangle2D getViewPortBounds() { if (getParent() instanceof JViewport) { return ((JViewport) getParent()).getViewRect(); } return null; } /** * Returns the size of the grid in pixels. * * @return the size of the grid as an int */ public double getGridSize() { return gridSize; } /** * Returns the current grid view mode. */ public int getGridMode() { return gridMode; } /** * Sets the size of the grid. *

    * Fires a property change for the GRID_SIZE_PROPERTY. * * @param newSize * the new size of the grid in pixels */ public void setGridSize(double newSize) { double oldValue = this.gridSize; this.gridSize = newSize; firePropertyChange(GRID_SIZE_PROPERTY, oldValue, newSize); } /** * Sets the current grid view mode. * * @param mode * The current grid view mode. Valid values are * DOT_GRID_MODE,CROSS_GRID_MODE, and * LINE_GRID_MODE. */ public void setGridMode(int mode) { if (mode == DOT_GRID_MODE || mode == CROSS_GRID_MODE || mode == LINE_GRID_MODE) { gridMode = mode; repaint(); } } /** * Returns true if the grid will be visible. * * @return true if the grid is visible */ public boolean isGridVisible() { return gridVisible; } /** * If set to true, the grid will be visible. *

    * Fires a property change for the GRID_VISIBLE_PROPERTY. */ public void setGridVisible(boolean flag) { boolean oldValue = gridVisible; gridVisible = flag; // Clear the double buffer if the grid has been enabled if (flag != oldValue) { clearOffscreen(); } firePropertyChange(GRID_VISIBLE_PROPERTY, oldValue, flag); } /** * Returns true if the ports will be visible. * * @return true if the ports are visible */ public boolean isPortsVisible() { return portsVisible; } /** * If set to true, the ports will be visible. *

    * Fires a property change for the PORTS_VISIBLE_PROPERTY. */ public void setPortsVisible(boolean flag) { boolean oldValue = portsVisible; portsVisible = flag; // Clear the double buffer if the grid has been enabled if (flag != oldValue) { clearOffscreen(); } firePropertyChange(PORTS_VISIBLE_PROPERTY, oldValue, flag); } /** * Returns true if the ports will be scaled. * * @return true if the ports are visible */ public boolean isPortsScaled() { return portsScaled; } /** * If set to true, the ports will be scaled. *

    * Fires a property change for the PORTS_SCALED_PROPERTY. */ public void setPortsScaled(boolean flag) { boolean oldValue = portsScaled; portsScaled = flag; firePropertyChange(PORTS_SCALED_PROPERTY, oldValue, flag); } public boolean isPortsOnTop() { return portsOnTop; } public void setPortsOnTop(boolean portsOnTop) { this.portsOnTop = portsOnTop; } /** * Returns true if the graph will be anti aliased. * * @return true if the graph is anti aliased */ public boolean isAntiAliased() { return antiAliased; } /** * Sets antialiasing on or off based on the boolean value. *

    * Fires a property change for the ANTIALIASED_PROPERTY. * * @param newValue * whether to turn antialiasing on or off */ public void setAntiAliased(boolean newValue) { boolean oldValue = this.antiAliased; this.antiAliased = newValue; firePropertyChange(ANTIALIASED_PROPERTY, oldValue, newValue); } /** * Returns true if the graph is editable (if it allows cells to be edited). * * @return true if the graph is editable */ public boolean isEditable() { return editable; } /** * Determines whether the graph is editable. Fires a property change event * if the new setting is different from the existing setting. *

    * Note: Editable determines whether the graph allows editing. This is not * to be confused with enabled, which allows the graph to handle mouse * events (including editing). * * @param flag * a boolean value, true if the graph is editable */ public void setEditable(boolean flag) { boolean oldValue = this.editable; this.editable = flag; firePropertyChange(EDITABLE_PROPERTY, oldValue, flag); } /** * @return the groupsEditable */ public boolean isGroupsEditable() { return groupsEditable; } /** * @param groupsEditable the groupsEditable to set */ public void setGroupsEditable(boolean groupsEditable) { this.groupsEditable = groupsEditable; } /** * Returns true if the cell selection is enabled * * @return true if the cell selection is enabled */ public boolean isSelectionEnabled() { return selectionEnabled; } /** * Determines whether cell selection is enabled. Fires a property change * event if the new setting is different from the existing setting. * * @param flag * a boolean value, true if cell selection is enabled */ public void setSelectionEnabled(boolean flag) { boolean oldValue = this.selectionEnabled; this.selectionEnabled = flag; firePropertyChange(SELECTIONENABLED_PROPERTY, oldValue, flag); } /** * Returns true if graph allows invalid null ports during previews * * @return true if the graph allows invalid null ports during previews */ public boolean isPreviewInvalidNullPorts() { return previewInvalidNullPorts; } /** * Determines whether the graph allows invalid null ports during previews * * @param flag * a boolean value, true if the graph allows invalid null ports * during previews */ public void setPreviewInvalidNullPorts(boolean flag) { this.previewInvalidNullPorts = flag; } /** * Returns the current double buffering graphics object. Checks to see if * the graph bounds has changed since the last time the off screen image was * created and if so, creates a new image. * * @return the off screen graphics */ public Graphics getOffgraphics() { if (!isDoubleBuffered()) { // If double buffering is not enabled return null; } // Get the bounds of the entire graph Rectangle2D graphBounds = getBounds(); // Find the size of the double buffer in the JVM int x = Math .max(0, (int) graphBounds.getX()); int y = Math .max(0, (int) graphBounds.getY()); int width = (int) graphBounds.getWidth(); int height = (int) graphBounds.getHeight(); boolean offScreenNeedsExtending = true; Rectangle2D newOffscreenBuffer = new Rectangle2D.Double(0, 0, width, height); if (offscreenBounds != null) { offScreenNeedsExtending = !(offscreenBounds .contains(newOffscreenBuffer)); if (offScreenNeedsExtending) { width += offscreenBuffer; height += offscreenBuffer; newOffscreenBuffer = new Rectangle2D.Double(0, 0, width, height); } } // Check whether the visible area is completely contained within the // buffer. If not, the buffer need to be re-generated if ((offscreen == null || offgraphics == null || offscreenBounds == null) || offScreenNeedsExtending) { if (offscreen != null) { offscreen.flush(); } if (offgraphics != null) { offgraphics.dispose(); } offscreen = null; offgraphics = null; Runtime runtime = Runtime.getRuntime(); long maxMemory = runtime.maxMemory(); long allocatedMemory = runtime.totalMemory(); long freeMemory = runtime.freeMemory(); long totalFreeMemory = (freeMemory + (maxMemory - allocatedMemory)) / 1024; // Calculate size of buffer required (assuming TYPE_INT_RGB which // stores each pixel in a 32-bit int ) long memoryRequired = width*height*4/1024; if (memoryRequired > totalFreeMemory) { if (lastBufferAllocated) { // If the last attempt to allocate a buffer worked it might // be we need to reclaim the memory before the next one // will work System.gc(); } lastBufferAllocated = false; return null; } if (offscreen == null && volatileOffscreen) { try { offscreen = createVolatileImage(width, height); } catch (OutOfMemoryError e) { offscreen = null; offgraphics = null; } } if (offscreen == null) { // Probably running in headless mode, try to create a buffered // image. createBufferedImage(width, height); } if (offscreen == null) { // TODO assume the graph is too large and only buffer part // of it, might also be faster to calculate in // advance whether they is enough memory to create image // rather than let it try and throw error. lastBufferAllocated = false; return null; } lastBufferAllocated = true; setupOffScreen(x, y, width, height, newOffscreenBuffer); } else if (offscreen instanceof VolatileImage) { int valCode = ((VolatileImage) offscreen) .validate(getGraphicsConfiguration()); if (!volatileOffscreen) { offscreen.flush(); offgraphics.dispose(); offscreen = null; offgraphics = null; createBufferedImage(width,height); setupOffScreen(x, y, width, height, newOffscreenBuffer); } else if (valCode == VolatileImage.IMAGE_INCOMPATIBLE) { offscreen.flush(); offgraphics.dispose(); try { offscreen = createVolatileImage(width, height); } catch (OutOfMemoryError e) { offscreen = null; offgraphics = null; return null; } setupOffScreen(x, y, width, height, newOffscreenBuffer); } else if (valCode == VolatileImage.IMAGE_RESTORED) { addOffscreenDirty(new Rectangle2D.Double(0, 0, getWidth(), getHeight())); } } Rectangle2D offscreenDirty = getOffscreenDirty(); if (offscreenDirty != null) { if (isOpaque()) { offgraphics.setColor(getBackground()); offgraphics.setPaintMode(); } else { ((Graphics2D) offgraphics).setComposite(AlphaComposite.getInstance( AlphaComposite.CLEAR, 0.0f)); } toScreen(offscreenDirty); offscreenDirty.setRect(offscreenDirty.getX() - (getHandleSize() + 1), offscreenDirty.getY() - (getHandleSize() + 1), offscreenDirty.getWidth() + (getHandleSize() + 1) * 2, offscreenDirty.getHeight() + (getHandleSize() + 1) * 2); offgraphics.fillRect((int) offscreenDirty.getX(), (int) offscreenDirty.getY(), (int) offscreenDirty .getWidth(), (int) offscreenDirty.getHeight()); if (!isOpaque()) { ((Graphics2D) offgraphics).setComposite(AlphaComposite.SrcOver); } ((BasicGraphUI) getUI()).drawGraph(offgraphics, offscreenDirty); clearOffscreenDirty(); } return offgraphics; } /** * Utility method to create a standard buffered image * @param width * @param height */ protected void createBufferedImage(int width, int height) { GraphicsConfiguration graphicsConfig = getGraphicsConfiguration(); if (graphicsConfig != null) { try { offscreen = graphicsConfig.createCompatibleImage(width, height, (isOpaque()) ? Transparency.OPAQUE : Transparency.TRANSLUCENT); } catch (OutOfMemoryError e) { offscreen = null; offgraphics = null; } catch (NegativeArraySizeException e) { // Customer reported this exception in DataBufferInt 13/12/2008 offscreen = null; offgraphics = null; // System.out.println("width = " + width); // System.out.println("height = " + height); } } else { try { offscreen = new BufferedImage(width, height, isOpaque() ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB); } catch (OutOfMemoryError e) { offscreen = null; offgraphics = null; } } } /** * Utility method that initialises the offscreen graphics area * @param x * @param y * @param width * @param height * @param newOffscreenBuffer */ protected void setupOffScreen(int x, int y, int width, int height, Rectangle2D newOffscreenBuffer) { offgraphics = offscreen.getGraphics(); if (isOpaque()) { offgraphics.setColor(getBackground()); offgraphics.setPaintMode(); } else { ((Graphics2D) offgraphics).setComposite(AlphaComposite.getInstance( AlphaComposite.CLEAR, 0.0f)); } offgraphics.fillRect(0, 0, width, height); if (!isOpaque()) { ((Graphics2D) offgraphics).setComposite(AlphaComposite.SrcOver); } ((BasicGraphUI)getUI()).drawGraph(offgraphics, null); offscreenBounds = newOffscreenBuffer; offscreenOffset = new Point2D.Double(x, y); // Clear the offscreen, we've just drawn the whole thing clearOffscreenDirty(); } /** * @return the offscreen */ public Image getOffscreen() { return offscreen; } /** * Returns the area that is deemed dirty for the next double buffered redraw * * @return the area that is deemed dirty for the next double buffered redraw */ public Rectangle2D getOffscreenDirty() { return offscreenDirty; } /** * Adds the specified area to the region deemed dirty for the next double * buffered redraw * * @param offscreenDirty * the region to add */ public void addOffscreenDirty(Rectangle2D offscreenDirty) { if (this.offscreenDirty == null && offscreenDirty != null) { this.offscreenDirty = (Rectangle2D) offscreenDirty.clone(); } else if (offscreenDirty != null) { this.offscreenDirty.add(offscreenDirty); } } /** * Clears the region deemed dirty for the next double buffered redraw */ public void clearOffscreenDirty() { offscreenDirty = null; } /** * Schedules the offscreen resources taken by the offscreen buffer to * be reclaimed. Note that this does not force garbage collection */ public void releaseOffscreenResources() { offscreen.flush(); offgraphics.dispose(); offscreen = null; offgraphics = null; } /** * Utility method to draw the off screen buffer * * @param dx1 * the x coordinate of the first corner of the * destination rectangle. * @param dy1 * the y coordinate of the first corner of the * destination rectangle. * @param dx2 * the x coordinate of the second corner of the * destination rectangle. * @param dy2 * the y coordinate of the second corner of the * destination rectangle. * @param sx1 * the x coordinate of the first corner of the source * rectangle. * @param sy1 * the y coordinate of the first corner of the source * rectangle. * @param sx2 * the x coordinate of the second corner of the source * rectangle. * @param sy2 * the y coordinate of the second corner of the source * rectangle. * @return true if the current output representation is * complete; false otherwise. */ public boolean drawImage(int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2) { getOffgraphics(); return getGraphics().drawImage(offscreen, (int) sx1, (int) sy1, (int) sx2, (int) sy2, (int) sx1, (int) sy1, (int) sx2, (int) sy2, this); } public boolean drawImage(Graphics g) { Rectangle rect = getBounds(); return getGraphics().drawImage(offscreen, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, this); } /** * Returns the background image. * * @return Returns the backgroundImage. */ public ImageIcon getBackgroundImage() { return backgroundImage; } /** * Sets the background image. Fires a property change event for * {@link #PROPERTY_BACKGROUNDIMAGE}. * * @param backgroundImage * The backgroundImage to set. */ public void setBackgroundImage(ImageIcon backgroundImage) { ImageIcon oldValue = this.backgroundImage; this.backgroundImage = backgroundImage; clearOffscreen(); firePropertyChange(PROPERTY_BACKGROUNDIMAGE, oldValue, backgroundImage); } /** * Override parent to clear offscreen double buffer */ public void setBackground(Color bg) { clearOffscreen(); super.setBackground(bg); } /** * @return the backgroundScaled */ public boolean isBackgroundScaled() { return backgroundScaled; } /** * @return the offscreenOffset */ public Point2D getOffscreenOffset() { return offscreenOffset; } /** * @param offscreenOffset the offscreenOffset to set */ public void setOffscreenOffset(Point2D offscreenOffset) { this.offscreenOffset = offscreenOffset; } /** * @return the volatileOffscreen */ public boolean isVolatileOffscreen() { return volatileOffscreen; } /** * @param volatileOffscreen the volatileOffscreen to set */ public void setVolatileOffscreen(boolean volatileOffscreen) { this.volatileOffscreen = volatileOffscreen; } /** * @param backgroundScaled * the backgroundScaled to set */ public void setBackgroundScaled(boolean backgroundScaled) { this.backgroundScaled = backgroundScaled; } /** * @return the backgroundComponent */ public Component getBackgroundComponent() { return backgroundComponent; } /** * @param backgroundComponent * the backgroundComponent to set */ public void setBackgroundComponent(Component backgroundComponent) { clearOffscreen(); this.backgroundComponent = backgroundComponent; } /* * Overriden to change painting style for opaque components * @see javax.swing.JComponent#setOpaque(boolean) */ public void setOpaque(boolean opaque) { // Due to problems with XOR painting on transparent backgrounds // switch off XOR for non-opaque components if (!opaque) { setXorEnabled(false); } super.setOpaque(opaque); } /** * Returns the GraphModel that is providing the data. * * @return the model that is providing the data */ public GraphModel getModel() { return graphModel; } /** * Sets the GraphModel that will provide the data. Note: * Updates the current GraphLayoutCache's model using setModel if the * GraphLayoutCache points to a different model. *

    * Fires a property change for the GRAPH_MODEL_PROPERTY. * * @param newModel * the GraphModel that is to provide the data */ public void setModel(GraphModel newModel) { GraphModel oldModel = graphModel; graphModel = newModel; clearOffscreen(); firePropertyChange(GRAPH_MODEL_PROPERTY, oldModel, graphModel); // FIX: Use Listener if (graphLayoutCache != null && graphLayoutCache.getModel() != graphModel) graphLayoutCache.setModel(graphModel); clearSelection(); invalidate(); } /** * Returns the GraphLayoutCache that is providing the * view-data. * * @return the view that is providing the view-data */ public GraphLayoutCache getGraphLayoutCache() { return graphLayoutCache; } /** * Sets the GraphLayoutCache that will provide the view-data. *

    * Note: Updates the graphs's model using using the model from the layout * cache. *

    * Fires a property change for the GRAPH_LAYOUT_CACHE_PROPERTY. * * @param newLayoutCache * the GraphLayoutCache that is to provide the * view-data */ public void setGraphLayoutCache(GraphLayoutCache newLayoutCache) { if (!isSelectionEmpty()) { clearSelection(); } GraphLayoutCache oldLayoutCache = graphLayoutCache; graphLayoutCache = newLayoutCache; clearOffscreen(); firePropertyChange(GRAPH_LAYOUT_CACHE_PROPERTY, oldLayoutCache, graphLayoutCache); if (graphLayoutCache != null && graphLayoutCache.getModel() != getModel()) { setModel(graphLayoutCache.getModel()); } else { // Forces an update of the layout cache internal state (ports field) graphLayoutCache.update(); } invalidate(); } /** * Returns the MarqueeHandler that will handle marquee * selection. */ public BasicMarqueeHandler getMarqueeHandler() { return marquee; } /** * Sets the MarqueeHandler that will handle marquee * selection. * * @param newMarquee * the BasicMarqueeHandler that is to provide * marquee handling */ public void setMarqueeHandler(BasicMarqueeHandler newMarquee) { BasicMarqueeHandler oldMarquee = marquee; marquee = newMarquee; firePropertyChange(MARQUEE_HANDLER_PROPERTY, oldMarquee, newMarquee); invalidate(); } /** * Determines what happens when editing is interrupted by selecting another * cell in the graph, a change in the graph's data, or by some other means. * Setting this property to true causes the changes to be * automatically saved when editing is interrupted. *

    * Fires a property change for the INVOKES_STOP_CELL_EDITING_PROPERTY. * * @param newValue * true means that stopCellEditing is invoked when * editing is interruped, and data is saved; false means that * cancelCellEditing is invoked, and changes are * lost */ public void setInvokesStopCellEditing(boolean newValue) { boolean oldValue = invokesStopCellEditing; invokesStopCellEditing = newValue; firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, oldValue, newValue); } /** * Returns the indicator that tells what happens when editing is * interrupted. * * @return the indicator that tells what happens when editing is interrupted * @see #setInvokesStopCellEditing * */ public boolean getInvokesStopCellEditing() { return invokesStopCellEditing; } /** * Returns true if the graph and the cell are editable. This * is invoked from the UI before editing begins to ensure that the given * cell can be edited. * * @return true if the specified cell is editable * @see #isEditable * */ public boolean isCellEditable(Object cell) { if (cell != null) { CellView view = graphLayoutCache.getMapping(cell, false); if (view != null) { return isEditable() && GraphConstants.isEditable(view.getAllAttributes()); } } return false; } /** * Overrides JComponent'sgetToolTipText * method in order to allow the graph to create a tooltip for the topmost * cell under the mousepointer. This differs from JTree where the renderers * tooltip is used. *

    * NOTE: For JGraph to properly display tooltips of its * renderers, JGraph must be a registered component with the * ToolTipManager. This can be done by invoking * ToolTipManager.sharedInstance().registerComponent(graph). * This is not done automatically! * * @param e * the MouseEvent that initiated the * ToolTip display * @return a string containing the tooltip or null if * event is null */ public String getToolTipText(MouseEvent e) { if (e != null) { Object cell = getFirstCellForLocation(e.getX(), e.getY()); CellView view = getGraphLayoutCache().getMapping(cell, false); if (view != null) { Component c = view.getRendererComponent(this, false, false, false); if (c instanceof JComponent) { Rectangle2D rect = getCellBounds(cell); Point2D where = fromScreen(e.getPoint()); // Pass the event to the renderer in graph coordinates; // the renderer is ignorant of screen scaling e = new MouseEvent(c, e.getID(), e.getWhen(), e .getModifiers(), (int) (where.getX() - rect.getX()), (int) (where .getY() - rect.getY()), e.getClickCount(), e.isPopupTrigger()); return ((JComponent) c).getToolTipText(e); } } } return super.getToolTipText(e); } // // The following are convenience methods that get forwarded to the // current GraphSelectionModel. // /** * Sets the graph's selection model. When a null value is * specified an emtpy selectionModel is used, which does not * allow selections. * * @param selectionModel * the GraphSelectionModel to use, or * null to disable selections * @see GraphSelectionModel * */ public void setSelectionModel(GraphSelectionModel selectionModel) { if (selectionModel == null) selectionModel = EmptySelectionModel.sharedInstance(); GraphSelectionModel oldValue = this.selectionModel; // Remove Redirector From Old Selection Model if (this.selectionModel != null && selectionRedirector != null) this.selectionModel .removeGraphSelectionListener(selectionRedirector); this.selectionModel = selectionModel; // Add Redirector To New Selection Model if (selectionRedirector != null) this.selectionModel.addGraphSelectionListener(selectionRedirector); firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, this.selectionModel); } /** * Returns the model for selections. This should always return a non- * null value. If you don't want to allow anything to be * selected set the selection model to null, which forces an * empty selection model to be used. * * @return the current selection model * @see #setSelectionModel * */ public GraphSelectionModel getSelectionModel() { return selectionModel; } /** * Clears the selection. */ public void clearSelection() { getSelectionModel().clearSelection(); } /** * Returns true if the selection is currently empty. * * @return true if the selection is currently empty */ public boolean isSelectionEmpty() { return getSelectionModel().isSelectionEmpty(); } /** * Adds a listener for GraphSelection events. * * @param tsl * the GraphSelectionListener that will be * notified when a cell is selected or deselected (a "negative * selection") */ public void addGraphSelectionListener(GraphSelectionListener tsl) { listenerList.add(GraphSelectionListener.class, tsl); if (listenerList.getListenerCount(GraphSelectionListener.class) != 0 && selectionRedirector == null) { selectionRedirector = new GraphSelectionRedirector(); selectionModel.addGraphSelectionListener(selectionRedirector); } } /** * Removes a GraphSelection listener. * * @param tsl * the GraphSelectionListener to remove */ public void removeGraphSelectionListener(GraphSelectionListener tsl) { listenerList.remove(GraphSelectionListener.class, tsl); if (listenerList.getListenerCount(GraphSelectionListener.class) == 0 && selectionRedirector != null) { selectionModel.removeGraphSelectionListener(selectionRedirector); selectionRedirector = null; } } /** * Notifies all listeners that have registered interest for notification on * this event type. The event instance is lazily created using the * parameters passed into the fire method. * * @param e * the GraphSelectionEvent generated by the * GraphSelectionModel when a cell is selected or * deselected * @see javax.swing.event.EventListenerList * */ protected void fireValueChanged(GraphSelectionEvent e) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == GraphSelectionListener.class) { ((GraphSelectionListener) listeners[i + 1]).valueChanged(e); } } } /** * Selects the specified cell. * * @param cell * the Object specifying the cell to select */ public void setSelectionCell(Object cell) { getSelectionModel().setSelectionCell(cell); } /** * Selects the specified cells. * * @param cells * an array of objects that specifies the cells to select */ public void setSelectionCells(Object[] cells) { getSelectionModel().setSelectionCells(cells); } /** * Adds the cell identified by the specified Object to the * current selection. * * @param cell * the cell to be added to the selection */ public void addSelectionCell(Object cell) { getSelectionModel().addSelectionCell(cell); } /** * Adds each cell in the array of cells to the current selection. * * @param cells * an array of objects that specifies the cells to add */ public void addSelectionCells(Object[] cells) { getSelectionModel().addSelectionCells(cells); } /** * Removes the cell identified by the specified Object from the current * selection. * * @param cell * the cell to be removed from the selection */ public void removeSelectionCell(Object cell) { getSelectionModel().removeSelectionCell(cell); } /** * Returns the first selected cell. * * @return the Object for the first selected cell, or * null if nothing is currently selected */ public Object getSelectionCell() { return getSelectionModel().getSelectionCell(); } /** * Returns all selected cells. * * @return an array of objects representing the selected cells, or * null if nothing is currently selected */ public Object[] getSelectionCells() { return getSelectionModel().getSelectionCells(); } /** * Returns all selected cells in cells. */ public Object[] getSelectionCells(Object[] cells) { if (cells != null) { List selected = new ArrayList(cells.length); for (int i = 0; i < cells.length; i++) { if (isCellSelected(cells[i])) selected.add(cells[i]); } return selected.toArray(); } return null; } /** * Returns the selection cell at the specified location. * * @return Returns the selection cell for pt. */ public Object getSelectionCellAt(Point2D pt) { pt = fromScreen((Point2D) pt.clone()); Object[] cells = getSelectionCells(); if (cells != null) { for (int i = 0; i < cells.length; i++) if (getCellBounds(cells[i]).contains(pt.getX(), pt.getY())) return cells[i]; } return null; } /** * Returns the number of cells selected. * * @return the number of cells selected */ public int getSelectionCount() { return getSelectionModel().getSelectionCount(); } /** * Returns true if the cell is currently selected. * * @param cell * an object identifying a cell * @return true if the cell is selected */ public boolean isCellSelected(Object cell) { return getSelectionModel().isCellSelected(cell); } /** * Scrolls to the specified cell. Only works when this JGraph * is contained in a JScrollPane. * * @param cell * the object identifying the cell to bring into view */ public void scrollCellToVisible(Object cell) { Rectangle2D bounds = getCellBounds(cell); if (bounds != null) { Rectangle2D b2 = toScreen((Rectangle2D) bounds.clone()); scrollRectToVisible(new Rectangle((int) b2.getX(), (int) b2.getY(), (int) b2.getWidth(), (int) b2.getHeight())); } } /** * Makes sure the specified point is visible. * * @param p * the point that should be visible */ public void scrollPointToVisible(Point2D p) { if (p != null) scrollRectToVisible(new Rectangle((int) p.getX(), (int) p.getY(), 1, 1)); } /** * Returns true if the graph is being edited. The item that is being edited * can be obtained using getEditingCell. * * @return true if the user is currently editing a cell * @see #getSelectionCell * */ public boolean isEditing() { GraphUI graph = getUI(); if (graph != null) return graph.isEditing(this); return false; } /** * Ends the current editing session. (The * DefaultGraphCellEditor object saves any edits that are * currently in progress on a cell. Other implementations may operate * differently.) Has no effect if the tree isn't being edited.

    * Note:
    * To make edit-saves automatic whenever the user changes their position in * the graph, use {@link #setInvokesStopCellEditing}.
    * * @return true if editing was in progress and is now stopped, false if * editing was not in progress */ public boolean stopEditing() { GraphUI graph = getUI(); if (graph != null) return graph.stopEditing(this); return false; } /** * Cancels the current editing session. Has no effect if the graph isn't * being edited. */ public void cancelEditing() { GraphUI graph = getUI(); if (graph != null) graph.cancelEditing(this); } /** * Selects the specified cell and initiates editing. The edit-attempt fails * if the CellEditor does not allow editing for the specified * item. */ public void startEditingAtCell(Object cell) { GraphUI graph = getUI(); if (graph != null) graph.startEditingAtCell(this, cell); } /** * Returns the cell that is currently being edited. * * @return the cell being edited */ public Object getEditingCell() { GraphUI graph = getUI(); if (graph != null) return graph.getEditingCell(this); return null; } /** * Messaged when the graph has changed enough that we need to resize the * bounds, but not enough that we need to remove the cells (e.g cells were * inserted into the graph). You should never have to invoke this, the UI * will invoke this as it needs to. (Note: This is invoked by GraphUI, eg. * after moving.) */ public void graphDidChange() { revalidate(); repaint(); } /** * Repaints the entire graph, regardless of what is marked dirty */ public void refresh() { clearOffscreen(); repaint(); } // /* (non-Javadoc) // * @see javax.swing.JComponent#isOptimizedDrawingEnabled() // */ // @Override // public boolean isOptimizedDrawingEnabled() { // return true; // } /** * Returns a {@link BufferedImage} for the graph using inset as an empty * border around the cells of the graph. If bg is null then a transparent * background is applied to the image, else the background is filled with * the bg color. Therefore, one should only use a null background if the * fileformat support transparency, eg. GIF and PNG. For JPG, you can use * Color.WHITE for example. * * @return Returns an image of the graph. */ public BufferedImage getImage(Color bg, int inset) { // TODO, this method could just use the offscreen if available Object[] cells = getRoots(); Rectangle2D bounds = getCellBounds(cells); if (bounds != null) { toScreen(bounds); GraphicsConfiguration graphicsConfig = getGraphicsConfiguration(); BufferedImage img = null; if (graphicsConfig != null) { img = getGraphicsConfiguration().createCompatibleImage( (int) bounds.getWidth() + 2 * inset, (int) bounds.getHeight() + 2 * inset, (bg != null) ? Transparency.OPAQUE : Transparency.BITMASK); } else { img = new BufferedImage((int) bounds.getWidth() + 2 * inset, (int) bounds.getHeight() + 2 * inset, (bg != null) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB); } Graphics2D graphics = img.createGraphics(); if (bg != null) { graphics.setColor(bg); graphics.fillRect(0, 0, img.getWidth(), img.getHeight()); } else { graphics.setComposite(AlphaComposite.getInstance( AlphaComposite.CLEAR, 0.0f)); graphics.fillRect(0, 0, img.getWidth(), img.getHeight()); graphics.setComposite(AlphaComposite.SrcOver); } graphics.translate((int) (-bounds.getX() + inset), (int) (-bounds .getY() + inset)); print(graphics); graphics.dispose(); return img; } return null; } /** * Calculates the clip * @param change * @return the total region dirty as a result of this change */ public Rectangle2D getClipRectangle(GraphLayoutCacheChange change) { List removed = DefaultGraphModel.getDescendants(getModel(), change.getRemoved()); Rectangle2D removedBounds = (removed != null && !removed.isEmpty()) ? getCellBounds(removed.toArray()) : null; List inserted = DefaultGraphModel.getDescendants(getModel(), change.getInserted()); Rectangle2D insertedBounds = (inserted != null && !inserted.isEmpty()) ? getCellBounds(inserted.toArray()) : null; List changed = DefaultGraphModel.getDescendants(getModel(), change.getChanged()); Rectangle2D changedBounds = (changed != null && !changed.isEmpty()) ? getCellBounds(changed.toArray()) : null; List context = DefaultGraphModel.getDescendants(getModel(), change.getContext()); Rectangle2D contextBounds = (context != null && !context.isEmpty()) ? getCellBounds(context.toArray()) : null; Rectangle2D clip = removedBounds; if (clip == null) { clip = insertedBounds; } else if (insertedBounds != null) { clip.add(insertedBounds); } if (clip == null) { clip = changedBounds; } else if (changedBounds != null) { clip.add(changedBounds); } if (clip == null) { clip = contextBounds; } else if (contextBounds != null) { clip.add(contextBounds); } return clip; } /** * Serialization support. */ private void writeObject(ObjectOutputStream s) throws IOException { Vector values = new Vector(); s.defaultWriteObject(); // Save the cellEditor, if its Serializable. if (graphModel instanceof Serializable) { values.addElement("graphModel"); values.addElement(graphModel); } // Save the graphModel, if its Serializable. values.addElement("graphLayoutCache"); values.addElement(graphLayoutCache); // Save the selectionModel, if its Serializable. if (selectionModel instanceof Serializable) { values.addElement("selectionModel"); values.addElement(selectionModel); } // Save the marquee handler, if its Serializable. if (marquee instanceof Serializable) { values.addElement("marquee"); values.addElement(marquee); } s.writeObject(values); if (getUIClassID().equals(uiClassID)) { /* * byte count = JComponent.getWriteObjCounter(this); * JComponent.setWriteObjCounter(this, --count); */ if (/* count == 0 && */ ui != null) { ui.installUI(this); } } } /** * Serialization support. */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); Vector values = (Vector) s.readObject(); int indexCounter = 0; int maxCounter = values.size(); if (indexCounter < maxCounter && values.elementAt(indexCounter).equals("graphModel")) { graphModel = (GraphModel) values.elementAt(++indexCounter); indexCounter++; } if (indexCounter < maxCounter && values.elementAt(indexCounter).equals("graphLayoutCache")) { graphLayoutCache = (GraphLayoutCache) values .elementAt(++indexCounter); indexCounter++; } if (indexCounter < maxCounter && values.elementAt(indexCounter).equals("selectionModel")) { selectionModel = (GraphSelectionModel) values .elementAt(++indexCounter); indexCounter++; } if (indexCounter < maxCounter && values.elementAt(indexCounter).equals("marquee")) { marquee = (BasicMarqueeHandler) values.elementAt(++indexCounter); indexCounter++; } // Reinstall the redirector. if (listenerList.getListenerCount(GraphSelectionListener.class) != 0) { selectionRedirector = new GraphSelectionRedirector(); selectionModel.addGraphSelectionListener(selectionRedirector); } } /** * EmptySelectionModel is a GraphSelectionModel * that does not allow anything to be selected. *

    * Warning: Serialized objects of this class will not be * compatible with future Swing releases. The current serialization support * is appropriate for short term storage or RMI between applications running * the same version of Swing. A future release of Swing will provide support * for long term persistence. */ public static class EmptySelectionModel extends DefaultGraphSelectionModel { /** Unique shared instance. */ protected static final EmptySelectionModel sharedInstance = new EmptySelectionModel(); /** * A null implementation that constructs an * EmptySelectionModel. */ public EmptySelectionModel() { super(null); } /** Returns a shared instance of an empty selection model. */ static public EmptySelectionModel sharedInstance() { return sharedInstance; } /** A null implementation that selects nothing. */ public void setSelectionCells(Object[] cells) { } /** A null implementation that adds nothing. */ public void addSelectionCells(Object[] cells) { } /** A null implementation that removes nothing. */ public void removeSelectionCells(Object[] cells) { } } /** * Handles creating a new GraphSelectionEvent with the * JGraph as the source and passing it off to all the * listeners. *

    * Warning: Serialized objects of this class will not be * compatible with future Swing releases. The current serialization support * is appropriate for short term storage or RMI between applications running * the same version of Swing. A future release of Swing will provide support * for long term persistence. */ protected class GraphSelectionRedirector implements Serializable, GraphSelectionListener { /** * Invoked by the GraphSelectionModel when the selection * changes. * * @param e * the GraphSelectionEvent generated by the * GraphSelectionModel */ public void valueChanged(GraphSelectionEvent e) { GraphSelectionEvent newE; newE = (GraphSelectionEvent) e.cloneWithSource(JGraph.this); fireValueChanged(newE); } } // End of class JGraph.GraphSelectionRedirector // // Scrollable interface // /** * Returns the preferred display size of a JGraph. The * height is determined from getPreferredWidth. * * @return the graph's preferred size */ public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } /** * Returns the amount to increment when scrolling. The amount is 4. * * @param visibleRect * the view area visible within the viewport * @param orientation * either SwingConstants.VERTICAL or * SwingConstants.HORIZONTAL * @param direction * less than zero to scroll up/left, greater than zero for * down/right * @return the "unit" increment for scrolling in the specified direction * @see javax.swing.JScrollBar#setUnitIncrement(int) * */ public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { if (orientation == SwingConstants.VERTICAL) { return 2; } return 4; } /** * Returns the amount for a block increment, which is the height or width of * visibleRect, based on orientation. * * @param visibleRect * the view area visible within the viewport * @param orientation * either SwingConstants.VERTICAL or * SwingConstants.HORIZONTAL * @param direction * less than zero to scroll up/left, greater than zero for * down/right. * @return the "block" increment for scrolling in the specified direction * @see javax.swing.JScrollBar#setBlockIncrement(int) * */ public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return (orientation == SwingConstants.VERTICAL) ? visibleRect.height : visibleRect.width; } /** * Returns false to indicate that the width of the viewport does not * determine the width of the graph, unless the preferred width of the graph * is smaller than the viewports width. In other words: ensure that the * graph is never smaller than its viewport. * * @return false * @see Scrollable#getScrollableTracksViewportWidth * */ public boolean getScrollableTracksViewportWidth() { if (getParent() instanceof JViewport) { return (((JViewport) getParent()).getWidth() > getPreferredSize().width); } return false; } /** * Returns false to indicate that the height of the viewport does not * determine the height of the graph, unless the preferred height of the * graph is smaller than the viewports height. In other words: ensure that * the graph is never smaller than its viewport. * * @return false * @see Scrollable#getScrollableTracksViewportHeight * */ public boolean getScrollableTracksViewportHeight() { if (getParent() instanceof JViewport) { return (((JViewport) getParent()).getHeight() > getPreferredSize().height); } return false; } /** * Returns a string representation of this JGraph. This * method is intended to be used only for debugging purposes, and the * content and format of the returned string may vary between * implementations. The returned string may be empty but may not be * null. * * @return a string representation of this JGraph. */ protected String paramString() { String editableString = (editable ? "true" : "false"); String invokesStopCellEditingString = (invokesStopCellEditing ? "true" : "false"); return super.paramString() + ",editable=" + editableString + ",invokesStopCellEditing=" + invokesStopCellEditingString; } public static void main(String[] args) { System.out.println(VERSION); } } libjgraph-java-5.12.4.2+dfsg.orig/Installer$2.class0000644000175000017500000000116010421406442021101 0ustar gregoagregoa.$     val$zse LInstaller; Synthetic val$frameLjavax/swing/JFrame;val$jarFileNameLjava/lang/String;4(LInstaller;Ljavax/swing/JFrame;Ljava/lang/String;)VCodeactionPerformed(Ljava/awt/event/ActionEvent;)V  ! "# Installer$2 InnerClassesjava/lang/Objectjava/awt/event/ActionListener()V Installer extractUI)(Ljavax/swing/JFrame;Ljava/lang/String;)V       **+*,*-*** libjgraph-java-5.12.4.2+dfsg.orig/lib/0000755000175000017500000000000011256667036016556 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/lib/jgraph.jar0000644000175000017500000051131011256667036020530 0ustar gregoagregoaPK lx8; META-INF/PK kx8;bu^jMETA-INF/MANIFEST.MFMLK-. K-*ϳR03rCq,HLHU%LyRKRSt*AM u 4K|3+KRs<4yxPK gx8;org/PK gx8; org/jgraph/PK gx8;org/jgraph/event/PK gx8;org/jgraph/graph/PK gx8;org/jgraph/plaf/PK gx8;org/jgraph/plaf/basic/PK gx8;org/jgraph/util/PK gx8;R+org/jgraph/JGraph$EmptySelectionModel.classQN@=(E_JW.YZVJD?2N ̞33{v1 $QСd/k`X N B6CoͰW#$p]%0\z~|oʐuNx Z[DpUYC>/XR%ܖm]bQ@KѰ:aNb*JrLw#$ޠLd *֡APK gx8;;ot0org/jgraph/JGraph$GraphSelectionRedirector.classMK@Mcccjj6⥈(tmDJ ij?Jkݙ}ݙټ==Ŧ ȪSWQ% V-C]k89ΐhݸCa/\0*? u860>oئ љw =r!U *f4a J6f'*#|Zz>C-up \Cg1 QJxFtĭv?m dDKYK-[Ui~^L1'zB/G_i$FK!$+8EQbTEH0%"b~i h3'r;PK gx8;Ԛft5org/jgraph/JGraph.class<xTU2KtH& (JP$$)@,0$d` 3슲^qfb ˮuuW}y39s=ܒ<ÏB/b"TY."]D(.*bs8%Vjs |'.qXENBP^s\"b]KUEFNqE+b*STTU"W@ n?@b?OVă.C:; EUyl݊ /ŗ02+)5DבO@?c_m{G=y)??M?† ŧ>C9/|_!.HdX/ߘA_Em{E%(_HKc_dsCɉ97j.rLUʂ!e#~B>yo)_ t*1-^#D0ʡ{BE*1H6%#uAE$XX|.H%*e_]1-S JoU=MAeLEG#c07 뒎rx' B%eJ3Yd"Dsy.:A\5,@ *"tTeR=5 hDI#'.%-+ E:EgH-(J$9qNCOwW!X` "k/Rߎ,lR'*iJCĐ*.n=U #8EbM kzi6"؄`33,l}V: V.|i' ]BUڂ/rIK[Lmp mtJ!v@p9_"R_9үkpFH}Xs@p5Cp=܈x7!ÅFP%nE07-nǾNw""ׂQ{U ~Z>U#]0\nAԏ X{ ,EB*=_I~`n*=J*c. qIU*='`O.tH~;UzTyUzA^TeEzE^d) Ƃ`%Zmww RG"f>Q,YT@InZԐcjS,IIFU;wǖC(W76ZzU}cu %VIIIVƦDVSJ2jjkJM+|%% d}Ki]81um~E' !+p ٬ο9v7ĂMN,Y@t#{[yh^GXn hk$qoMw,Yfpow[ @kR6V@a)u.- |x x%W6p(ИR[hS8 GP=` IcA !P 뺎iMSuuնAnU'5'>ϬE#I 7Cؗ F2GV;`C&Dk̊l`jЇD`[Co(zvwDm˃ #U<Ȣ%GEM$7+X9+!0/ o\)66:u5P>A; / Dw _ O*mZAw']C.жZYN.wE1KMYDuW `IT8 1ˆvvB)/]{C1 j#P /`c W5ۍ`lƸ[秢u5:[C`Mz^Ee@wusbn> nzEA{Cx]  TB!4,0jK+/dn⥍k6Ы*[5ZXci^_tɲU +jZrjk+YѽM5u5UͰN4`S-2 9) /M+V6_XrXf+-^8} 3"0 DcX7?E(@]l'e46S#x 1ڐ=Z!qS5(P94OP;;\ w Nx>Z0HI+@vhfP!D? HĶTYmi@wmCjm 7-=2 PY1j&KƁ5Q4'-X9uV[Cj Xjh%bkɖ;Вl,.=T1.&J4/BSlahM:%BONsD!Do2#q?QKeN|@5Onry]Ҿ>uuTkв|,U Mp9)Oږ/?j[ Խ ~QZƖ]sZZchQ2 ]*$/Hls7Z4 '(-J]w)͌T7!,=i[M)0eZ!X~wfTT,oǵ$ݱ@hpbBc6̥0 ?ٱh w4-jm3<0f@"]tցjM(GaTh81ڍ2$*Y$rܻhAaO:MiG@hk `GC#PqvY62-=XAu.ޒqTc9YM;j}9~@ys2ot fփc FZquE?\ lt K=T4ݱ"4E8Yɼ\֌3[+rĠrv<0Mu^`$I${iAtaEv(ǿ7A~>&"J:W&rs?ge|IwW GSh:yM8ؙk9v (clԽGs8j4F,#!bRSý@6Rv}՟FFTq*VEϬf9^ Tfs$QOeҎw0nH퍪:AK8Wr=3--OQ;?$)= @x87' zKH*0u`rzzc.fwM"fJ^۝L==PHH2i2 ND˺۝eBlI&B9&9\ԐhE-C>vw'R?؝:EM8\llnY!vHkҟApl>$P 6:R$hBM% &5b%̆I8]vŨpCE&EzKF&'Hդ5o5#cM"|"n֝LzeT>>AJoЧ+jR/4K LEJ )6UniҿC%_چqD­9Gv0IѤJB5c¶=,2+MAp 7"y3f@m&ղZM9Z!ϙM m!9p/4xɃLh3vф90_Q$ܢiB<,YּGvՊSEjeFߢocn&s4Nȅ|Hњ(,6|b;X`duB&|)|cq<^ج䉚 rBTzMn CX mvK䥚8Znf~ @L^ .Hn4~Mv゗UX+)+@Oɧ2 QLWk_^aChr+bG;5!0F?lE^!L4[kr^#zEvi&#zD6뫿&,b]n8L`Q&oA33VzBz5hgc>[MI3iՊ|&'jB7Xp=rق&vby&L&u&o/7l^ φ@e$ߌq%I=)L /h#XPah(!B/(D5';Q_ߚpp&,4D(T*Ƣ|icێC 5 ͚+& {b1I:Sw܇,w_\/0x&|/77i &:DY%2 u:EzS)]M,W[AM2~Mo@SXŇڄk򝢪NѥnQ eb. sm\h= \7jb8%K5:o4qL4P[q\!h2a^Ȼ4y7q6>~q&;!r튼Gp@$ YL ,[~XNi#&?像C\Q,S'(ciS2XB Gtc煅:&I__!+38L4KOlom D5P0yj%/!tkyT/Q26>젶9Y)6Z{8ZgρX=F[캧7x`=uyRكDNh[UP[%;ME_~CNɌ$)қGI]ޣ^N#xvG۝Ї6̮n O]C{WmKFks50\ˎ`Xb^<9 W SְzNoJKdONuH8nOI\ɫk"x$N; ycg0 GƥY^Qtձ4'n9~/z7U]D'[`LJLZRflP fYA؏:v'+g y:85r4ܢYcaQRo>[RGxFu0(Dّ~q `dWqZ2{ io7[h[3Ǣ# vXh,m ;gXC1>B]z(<tdO֓]<^ϏH*7e8č_bFSY/:['ƊS^l}lPjme +±v7)o-(JuɃ?ll7.ke"LQk剥VdK^4]467t%XIswC;=SXs4/E˃ &G/'r"ƾb@3æj {NÞW86alo'a20kaְ5mdk%f0!>. s,rpzD= Ul jJ!v77VGzᄮ=ԋw9ib A[0ƍzNcV,xny5~7H8Kc?dԹ!6 ٿ9 tQ"tȒNbiwI8p|=bݣ$gdtTxFGK+)*C=~vEYflpOY ddBiA}@eSqF!`16z8?~!>f9qł;eNxw9$q3N;Wݥ ˃R;(yX5}gV7S-*WtSߪۃ=ci-NQz]zX2M+N^?B SR=ϤT .&o!5Og4˂mHzBe[ɢk-ۣH h2avJCp 12'ԗw|;E } DK5m`f/Yȕ @gz#7`m c&8{:kP;N|'v 鿗dNpz<˳G2g@QoN͌ 0B^jSԝ}̊l1!-!Ϗ,DZj)6n}P+%Tlŭ oǥ^EWѫٞOid,=H[HH3 ٟf(j)_#N|*O >d:قG߂,}w,}ҿX!KN?b,قO"XA#XOdY^ KwROT%T(g)'PA(sG#*}L ZYkdɏ8Z%-d$Yed$gr +!WHWb ;+٠^9ۉ2×ux>RВOe!23lV8J:;?m79jKƈdAsɸ {%w2B-^'E^az2q!WuI $Rlwz]W3;7)KꢹSL!"붠%iѦǠZW󺘐nSHӐmF7`KQcl3hq=ԛiAoKBϤ&UJ3Zh RgxLLSxst oc}7כfII6`ZLJyeL-Do>Wmoe+Y-7E7^gSjjQի5d;MSZB^2A #>'&YE\Du|2'̍y7@H,N ַ, 'S Rn]jbQ|;ߡ('{L(IDW8K 9(+9H\%qҴ$9"[}ld<7O~K0*u @S ''~'+ɩZONê}R?Y`/n [1׆v,v}g?YuBԅa X z.'QlaE/TlA Tu}r?ل  SY>u 8yG 0w! 8oy |%dɥ>ᲒCd>T:%;Q/=D%-`Wƕ{U8/N~m(wPe2P}k ۩j8F! 9z({C fM3#̲Aqrt{@X1xbtZaL7 'q 0#S&Ky K[HAi^r %M[$Ge:4R2A[A%Sb5xxw(b2Q蒽d`Y",HwwCz>rg⬬=7'wWcRZU㼖D='{9 ҽ. 8`!u%QG\*{"AFBn9>~klj*t(.w<r7 8UE2a980TH'a:|^Gp0+r>Lañ`p2'^`I^_ .Ik%h%ZԒnP"N.?wj&J@J"8ʘC u'qºseW ~ROe~>$Hq&*P^8;@Q*HၠaArltH?%N0Ncz,a|<٩ nx,ZVq4He}C+=c,08_bDs p/?,O,Mk։V8%=nP= YQH6`U=>RlqnذCĉ Dži][0|Wz[-ͩ0q)K[cZD`LOqr#؃ B9 q'HHLHߛ ( G8"DE({d \8'wn PykDۣ2@>Khw3ҳ}мAR>ʫq>'+|^dx3dCʰ"oz3tvy>+NGꄂڅ1h`E2[S82%/Bb^Oby`eJ+ QGI7'/"3%\q.(9@^Yʤ{/y1@ z8LZjK`y(<@xI:P~Z?$LҙeLHa1K0( oSϭͱw.Sm\(fndd+=8A\Wvİ=Q֗7 a qezR{Ľ1µ5WwYDDbsCeL<~b_P_j#&r޼Ațsy}ن ?C8ӎ ?$|c>Xζv01\{A~-ؒ_d_hO~AŖb"{ mɷ[ɷ?% =%[ m[ K7/5LSd=s nl= =p[s 3Ƚ俴m>hž+mUaW={ mؓ7LǷg{ 11\k2ȯ3}gn!4 epޖ˙L.H6Fgr?52Naі-6>V6Q>O|v[^9/$^;my}iӖחחܯxƖL^  mɣ=Q^>\0]y8| .,PlM.=`b= ȂəW@d[ȍ@o4CM'~\*T+$퓇NpnȽrǽ 1|Mc{yGcx9sœdHY= uw|ߋ/8v^6Lz͓ /N,qO9jH5`0{PxA):.$NE.(ץVr>BtX:S%NĦf퀮5tvB3&^s=#.8kᘭQg1jȇ;}Tn$H&PHqp|KQ7-pQ[!c\Ck5#\ Mc8WTU2em0{޳wOڒ1ȟe e Ȓ$ӶZ fm2f,;![fmXA;[A,&KdIY[fei7ȟ^<ʇ=bsTM7sObnN,;tzC4 8/w$VKY6 "T$~Eo3q:LGpa20Z0@켌8/ݘ:> YiaYS̑Hs r )|Uq x tbg|>$1NUWGtsqzepa! 2~'a }@tR9L|h]$[Q N!J7™B%яMdOeVE -F${MpjYNLr)q:z ~( HvIk^;D1N!KF1x(;Mna3t:+."t*FW=:ۆ\/A XWFlSqz}g%/~dQTW>\N~: hW۸s)6l4&Nw͇bޠ-}VL *|`YmnpuAwFG6-4ӅO.Fr;7r]d ~&|n)7}}a^7љlt5k`|~ _34al{|k'QYq&y~07qi(;b6Ĉ[oYɋqrN߄F0e#e0n2&ebfVDt Dt P,M kgQtaQ  < (5m#y/m6Os8kƎ/6ˢ˶X5NWY=lgOŽdkơ4NneRGOmBhEOg\Otlc;+j݅sa.JCDm8iA|%ؖ y d[m\ &4ӛ]Bqg-%\Gqk# Z<5@[UqGGG׷H? $}!츍?9yȎCvg6*֓qXz-y3Ncms %ǖNSZ(Eqq.;DT A6%299(3HYr:3-Y.8g[qg$5\xVRzn8o }%<ߗBn/=_ +uB9E(!\^5݂58 |[+5C:-DN@r$Cւ2FQw>GHxF/ yp V8D)@yTj0s ိJc')ƭȐ38^@eW«"V^qzN2ɫVo$ Q[[ ,Wpy_y -'O _a_-m8Fҽj|5=0L]Y#G{vOGgFz tGwl;ҫ&$tg=Wp *`ѦzEWzŬwNu$q2y\ Aݺ^۲^Wt=ƅ }$47$yf^8 m&^'oئ7\f6, AZRݚ;=No %Zߊ] շ3pтQ,Q}b +(Cٹ^ 8Y.20NDRpX4BT+iXK jMLŢwC(8W3ğP8Bp`iZ( X*FqzaSĩ_SvYG1|&J!GAW lw*cNIyyn~/ ԘVwK׿8}3хxGyPa]~}!Ph%,7Lqt~yt_>;R7IBw#"L"ɏ3Z4Nh翥'^z1Ɇ<}$qxXP_WG$rOA>$r*C)I#nNWw6 3rG)rT3a/úٌM{r7pP?PK gx8;5g} T,org/jgraph/event/GraphLayoutCacheEvent.classJ@IĤRD( D~C.DijA\>xF98SlشÖm; {7x\̔/X#,LT_E4۸\:{=ҁw杼B[M:6GCRF^-ڲI&SaXPK gx8;s>/org/jgraph/event/GraphLayoutCacheListener.class;o>=Nv6vvvFĂ TļFm t,ԲԼ}w4Ů Qk0FMj}2KRRs'e&*- S@3020Y PK gx8;55j.7org/jgraph/event/GraphModelEvent$GraphModelChange.classN@hXkcLH\J1iI7:'0 2^(*Z.'3 o/W`B:qVH9 ܄O=kVc3&g~x""9S&ܧOn UkO39cp>'\uȟL:| ah6ģw pRENs Axc}PK gx8; Zچ)org/jgraph/event/GraphModelListener.class;o>=Nv6vvvFĂ ļFU t,~jYj^;훟 [k12(QY\Z X4[?)+5A,TZ6 Nddad`a`PK gx8; {2lp*org/jgraph/event/GraphSelectionEvent.classuSKOPtC)2=Nv6vvvFĜTļFM tĂ ԲԼ}w;85'5$3?$fȠN@OfqIj^j#@VbY~},,#8X$3Gl"L1##3 d1PK gx8;8/;G 'org/jgraph/graph/AbstractCellView.classWx[gI#J3eɊC$c" h@Й:-B;Rʊ:tѦ-@)eNJsްXrh}:w߳=G+vN &8ꂄypE8[NԤ}@ɝZ^Da99a1qmg\Kt 4xIK%`Vtj`Ǚgg[te>f?RwvYB`(Z[uyl#C"]i17 Z"ECq,q;v6n5#N4iN=: !u.J>|MW ;4Es9Ud.~XIgYWŠn|^ 93b4Ft"j"%FU:&4!oPEItseo~YY-(Zq1[K4-6.SWi!_D+q"6- iš`&'L2i5 N5I6*ZVV|LGmLvEs%b".rE\}qBM 2 !A<eSSAIEtl~ t0E3m 't$i'Fè$^7賊ɛHh2v]hsn8-Jvo"T{H*EUK _3e[$h?

    Dr a*0T=Mfʍ<R%OvQal5Vz|z=GݝVwJgr֪L5RmK`7 P8d $BIo [*!"z#VҺvo'76a8|~ȇQ=p0uUOX.|~(ĥ 8XAHzMq2 [F$O,'i,oÆBjKj%#B4WZ{^xjL My'LYGeI2{0DaC 葇P09 OEpeOM+LC&Pm1,;PjN6YIک7u*,G4,15&ҙmi+#[TVp*Zsy)dv <&ӘN{$>ev fPdl;zb~jW@F;ő791TҨLѾ ~Tf7vߨ7du?$O OT,Xg?;@acM~_sI*T&Vqj* # rN೹iqg,?J, E>tעqa׃?˄P?oqoZ[\[T#w.4 USCXL4-!x'0m֋鿄K5Vw qoÍbjOLu'f,oĊvRo1 `e I~c\+15A>NPHUӜ&V`u'Wُ>=vBa蜝dD~aܜj+rׁp+zQY*>p&+Lrb ֺ~2HuMXOFhlz{M_UmdCK$r?~)KvLd/=B@&S8Z{4a_|zZ0aSeӹIcEOc>T1'=UBnvHPGڞڞj7@; sc7A Jod]5Mvm饔NSՏKͰ꥜~QX2n؋2?2i\.(c_ SLm!OZ>Mn FCq\9MCaGE(Ep%Y""@g"N=Wֺ<#Rr~4Z7|ԒMٶQ漰H%E4Z/\0?@yhDNں ]4:m~E:dp?=sH~#d;oIcg4^"D빃*j~nfttqug-74@p|d%"eN`+`+u1'; X| ZrGPK gx8;3$SO7org/jgraph/graph/AttributeMap$SerializablePoint2D.class}TkOA=+7j_|(5|c]Rv_J?xg[-f;s;LcK,Ê2ienXEFm #25WPҰ>ȶe[r!3NC0sv|O.׏N%Ev*a9?qrTՓmOIW-\mi9vGi/gfkxh`$ <¦ 65<5I YqZK("s/T.ú6Ӧ͞ݕҵj9[N\o˖Yc϶[nNG^2,si6sfL2x >Kۄ{~V.u׎g7 / Ie%H}/HǹbjkwN&}Mga2 z; s*l8-N_QE8b<]S) `_ hlt(y-t"bf`1}7*Ib#N׺ h/%҈s6D41ii$~B;aa D_ߏ,IEcX?Q=l`PK gx8;㹥F;org/jgraph/graph/AttributeMap$SerializableRectangle2D.class}SkS@=KӦ KJAE⛗V R!Lh:F_ʌ8~G9MR=w/ACw%Ћ) %<ӐGAŴf3Q!\{≊*^1$-b.V8+,ԹD&d8bHt֑`>l!yӰ]#\E;ɂTVj]ە$sxJIxHPK gx8;gn #org/jgraph/graph/AttributeMap.classX xTW/7$LG(6Z a+ E#< 03aVU,XVV'IV}i<7 3@wswxX,"*71yp⤊fɽ*j6{wPyM;y>&X ]w1S -߃r}L&}xC8># ><|܃O$aO1ϣ >"&Y&,yH0cSp*xs >y&|X'|ɠ'U/X܋8//1yɗy_Wk쥯3&o 9sQ]vI=i^Y%fϣ!ō=omQIJ}!ǽdu]w;b|~*k.LcBGk q:+~3է\s)UN'ٚ4 F A[wB{2gk:^q [AzPSaM0r9z|$<6{ i3JgȚ&V oߊokP-VmT[]o;3f /ܹs}?O\VF5nèxt [## FǙL0Wc jqW( }^-~k4x570y#7|3[ o`+ţw{Uߧ`A&LN*>#A&e+cٹS2>a0܂Ӭp=d<|?>#2JX>fY+tmDz%Ƶ6KSj=y]fڑfN'[~=̌o&mivn5sKTŴPSSyM_1tsnPm/!eu YvN}.:D<#\iGAkڮ~D3F6KLHFwdֱDTxU궵L18}E1MĤ5.;rumC;&!:8jM@ʬN;KlѦk 'A^s].Z7.P_uudoI-7f(*N)EqjZJKS_,]U9q(DLxظd5Ɇܡ]2ΩΫp-X :A>$z Chv; vܛrY˔44f3?m m7A<{zx-\vQTco^1Vr5qD HFg]\$nz>,5(c17SƧqP` q 3.Y3"+_6/e_|Zq/>\x/N9saQIRyt |y#-[~+^cM$jbnJ>Z -'Q?PK gx8;GC|!org/jgraph/graph/CellHandle.classMn@g0Uԡ"(@)7u1(x ;`v4r0(0DD9+W5+>UjexHr>¢kLl:!?奖R~E>R.ȥey-FۓjiWc֜'Fc_e8jI\Bn`ekˁ#PK gx8;b!org/jgraph/graph/CellMapper.class;o>=NvvvFĂ̼tF IJDļt}(MtĂ }霚Zn _P_?ݚa XA&12L, , @ $PK gx8;:b]org/jgraph/graph/CellView.classSn@BRBA*󴉧#k-|My'sΜ%=vvVv6FĒ԰rF tĂ }"}SRs}sRK51;LfdP)閘\_T n$I L bPK gx8;FY'org/jgraph/graph/CellViewRenderer.class;o>=vvVv6FԒԼԢ"܂ԼFw tĂ }/we,!Ssr2S˭4}KY32(A3'1/]?)+50F&`dad`f`1PK gx8;F0/org/jgraph/graph/ConnectionSet$Connection.class}RNP=7u[/ \U7QHZ)R.`9,lÂ?a]7,@jEnQ?TDz1sϙ9s{-l0܄tlmƺ|iSǖG@u=Wa$Os>܃ aGQ 4dD{~H˚0f+tMgmS˄ϙ4I{|"D`B LTKRhtZ\I.kb-&x1tD猼X <0~tM{1w$nbbOL< ^ Lݟ#gƾ 3д K <]п,ʌI3t,(k+p_F@a;Fc%,M4e?5TEӪ xFQ(ov@;F7kY9ZtkpW7ʅR=mڅJ#/ky, (3E}|װ [PK gx8;G~ $org/jgraph/graph/ConnectionSet.classVsemM핤tm %M(- HD] P}|8 :Z8 0oos6mLws}'_؊Oe Ahc8qN0x';8`i/ЃQgBhhQ28Ǽ%,"0ri`2xD|b)12Ȍ3E+nfB,k<2}VY m(ehޞ蛚ɾ  JN 8$+O-#k]7.do+lz4z2CKD;lIKkfXaLtG#fv(3dI9G$y8Yuѓ]&5՗*JgWh^~Ѱ b5/z-䚜bn"Oj<|tFф$ 7{܅o̿[SJ%}-(IU8%!;hЇT@ $BRĔyU*'pʿ;(̕/%??H7-tkwhLѷ:}MUJUܸ'̣'{d%yt% 7 'i?R\ﺓ+@2"Wt}n$MΨHtJcBKzO7y޿/ c y1rD$ݩ<]qKTP 0!P!5U)Yu][)0A|jKp>!r5)jت% V ަWЪ%׮%Z磳YjM'bXֳaǩ#:jvb ֕ -.C[:OJe]#]r aݐ~{jVǒt$әUMT]/1ó8䚟tθ[cTǦcʹ$hyd6;&i4sSWF4LUZex۪õA h:=jxk{;Egq4PK gx8;-org/jgraph/graph/DefaultCellViewFactory.classn@Iq+ Z -&qZPش&"MȪ+co-TP Bq8x17w̜+h n`QnhGn% D+2<7y*a$nKKݰl@P 6ݦ|U{jz-f}3:jzg2w۪WM8LLlC/ZLBqi "Ď X9ږT7: qW=qt<踌YSI2$/8#gy,#^^Yo?'VwT''B ?vʳ-d:*, G` i[q+1Ol =U CEMQ/)B| ~o-EoH$zAs/R.)WH HA0q.PI@3~!P'hGȰE-TO128Y&2EɓD"ǩ} 8'_PK gx8;R1org/jgraph/graph/DefaultEdge$DefaultRouting.classT]Se~|&y[*[eiQ(Im I"T sI&2NN7V/Rёћ\xrF=g)L݋MX|* 뾟]wɒkh \ɡdnm5|v/RWNMӀs+6fn G]wsfۊnl?Hn9~09 2:#y|pVtݪRѰZچ] hOv*'-(ڍFԴo̜{U/D%w٭X~ud{ 2NT*Ã6쀇[7^}GUўüɱ%H_v|[ pZ|$T<܆Rpw,=\2h,רYLi.G)~E9$O~ ϝiR^xCUǡ]G_Z}"Sp |4kDw .L6aD> 6FyR #U8B_>:1TO5C0Lms,z%fD!ˈbT'lgvr]B2MKok PK gx8;nc0.org/jgraph/graph/DefaultEdge$LoopRouting.class}T[WUNHfe*ڸpBHʥIm"T(:$0C'RTc|Ig.IZ{s7_?"~0i 3(j4滛BQ˲xO.ojEiW?0 s~TYRg\. B nkei>T +XUB?fM=AfZ;З_c`9v%؎Q6}^Mi+>!Jz.'!7jX3^q? -l*f-2=mD3]vĖpQXuj ^t6%一*>:%LrLaa[݂DMTU8TuT9L X*l]M][N͍OI4xϟ(Zޢ>YEŐDwNfO0,l6]G]zDog6-*r3i"8[0S޹T.B\[V qBMQ{ a3/L2ϺYse0* {1}rYBvLfus'&k,Kl绍^'y:UG<+:UC=8"KӝlVg3_K-L,o#}z|8KЍ9H{(/N}e Fgk3LDeOfFrZn A{:™jU?p 'w(ԯ[Jͯ475Ē˨t-g?kI&Mb8CVS;q(W/4ykzU'@s"硧Hdy? ,2dS%цx"~IRF"A[0CHƂ)LGq\A/=A{虾ӳ_?Z}?|~)⳥6Ro0< W3< љ APK gx8;JA/'org/jgraph/graph/DefaultGraphCell.classTYSA$, #E x"b3*b֒,a5 ǻ^Ē*'nؘH>tOO}L7ǧ0Yf1B;ius,γy!fR*F\f=;WX\Up-0 3f 9WR8IO(@Ȕ`/JkE'r`旌m9VvOvΖW|9XXj|n VZl=Jpأ{XyaS,X1*ǚu)kd VN%ֿ]hUڎW$p?Aƪf%^S U:7Vz`;l/XiSZ*gcbf23nA R=3|!>3f\;'GGwC׹z,a"▊#8۸.R &ULaZEN=SuБ`1 YUGPW* wd#i9y\8R*Mke6I |>Epc*JQ:@%]ǡmB3OSakyh+`-wz=̃JK3'~>HRgX:(ڱ8 0_Қ@ (AP4*^F}5=N_Nh&> ŋhH|QyğZ"*Byk?q/M'i4G&wrCK2@@} 쎐#"BzMې-&6Η?~;9q`?QgE< 6org/jgraph/graph/DefaultGraphCellEditor$DefaultTextField.classRJA}D'Gc\DM&(( Jq 32n_(?J#IuU~ϯ7X0@:8UQ騎1qLa:2s~oO^o d sK z/O5~mnVK[O6tΨ9ra;vNbe*蕺둉|_:q|O2"ͯJ+&5aDŽnbjmwZ|s B28&1ϰܦ\T>7rno<]"dH*miy5 5}e.jLz52,3{:S]zF$((O#LZ/`V V CJR0SzvcPRX;X+} YzJ4E0 L}PK gx8;I=org/jgraph/graph/DefaultGraphCellEditor$EditorContainer.classUMwT(RJZ$u)n))m[ ܤ,,bɒs`ٞ:-2 ~Kq&̼7y#l8ILdf\1ak&650Y69|yI?̱C#Nyoqm(w~sAiM]U*z~-^z6j-Ѹi;+ܠ.Bn8+NBjUl a9ndn=f;V7o#M9jxM˾Jߗͩ݋NS-ss8u|t =57V;<($PC &Pgph2 xxcCG-6jsp*7\?bB܀:'}#QpV%[}9FK{k~,תtjxGԨ`Xqkǧʶ6 2FC#Ma}^ssBE" oKE6d%K.FԡID{N$_M }]:z[w,B5= ;w504Bݐ-K֖l7[']Kd .[ IDHӽjqY nd谜6^Z8W ^۵6#-@os>I" ;E8 w30V:PvS~2DR6b8ΫZ3 mc`}ifg33 eTuzf55tfb2ϟ2HkQFg B32 NR/`weHP%Y'Z"'TX m3{Uڪ/Yg`0bF56kXz &H\ӴTD722O`IkI~JPPK gx8;8$ -org/jgraph/graph/DefaultGraphCellEditor.classW |gI& 96$B8feK`CB $ Cv 줻zh=ZSZx6(Z#h}}_lv ?ɏ]w>ÏX%(½4{=/'7 ބ7[x~&o؉|g`<{w ރ3x/ާ8||XL)&i^N39,x0L>ĠfG|%&|T|PЎ&W|q?Œ2קy&>' "%_U5_7d|Sܫ[c)>~䇼#&??3? 1ö]V֌P{4vZ2'ja-ecv1%k+'JD$ѨKw0Mg43#9юjQmŽfvgq[q=nqz)iq@@5<[i՛Y :* 媹ѐ0J5Z|$}?l%bzb@7imX.dkr52 +={v,(4l9Y˱l7Pb=mլ &|x09#qmgB:9'C27 '@43E^*#jMkIن~=vJ䨕2cNָAAuEC9AŲI[wR=4+-t5:낄>f GԮQ2P99^tAWfVk=1l%X5'lgLyҋ@c9:}+1l,4έW)Tqp 9"׵&{vɜg Ɨ[ˋh䪏.:bP͹I;t?9>}vj䶔ms/gK8Mc6#҈P01S0Nw@ wl-mJX_TE?nu~'ҮLv`ٛs>}RQ B \m.WW; M5Zas6J7~ [[IgAzKz`T* ɀKIY2¼;nPwPK gx8;mb*org/jgraph/graph/DefaultGraphModel$1.classTmoP~.|Yp11` $|SǮPҵe4q1?e<:^5sssxDXCɽc"!1TuufCUU|:xC!z\hi| 7L:)޵ο.>yu Mcx]Mm ]. [1lKMRVq->L>NIiTjʉ: L 4Z\0QBrn۷\B AK0p|u#/x ud:vzƸ49Re&|G+!X(?)rMIQ7# ^o44u&vP q6pŮfF7_݄6Ca!`$ԏd"1o06٥x4}kAOj;sh= ưE: H$'ϕ9P{әyI5U(I*e"#tnA͍T[P2 \g>Vv{jZ(_m'k7@ۥ/ kl[Ҳkkn`f6~PK gx8;yM 7org/jgraph/graph/DefaultGraphModel$GraphModelEdit.classX xTWd,/;B:@'3LK-K@,# ̌3oZ[{Sj ZZ@E.պ/պ}_x{%3|_}sYنG~$.`x` caNgp0|=܋CA|aQ >ap?o'e0q ǃ NцO)x(0Nsif _Y>i;LXG|| {k\l }~uFķxmYw||< ~z0bO|)?‡'}>Ƈ߲@"NQLAͻjDZ %۳W0(ôדĘGtFe3~LU,*}4ȧ PoY4RF-G6TXXWiy=c@k6?;r nt>yH06#Ԓ7Czv$,!tXdCxr:b<)}?Y5S g߷^Ԋijl&4:1u#5@g/OeRƕG;9?gszvO& ҆hdH0j¨g8Nds},N&zH:-HˆfM2ԀBܑ!ؚ-Ȃ'czK  mI[hf0oeC-4X2l7Hɬ:2i&[ڜ+OeW#D[f3GEoFY^OfWMc+ Jb&U7*6}E(W4d^~nvOvM;. JË+1: W̕&sko⁉d2{ji==pGOs1ԌCv  y2jkMD];<ۭ%wB 4j8n_Bsw  ͏ v`HgL4ꡏCSDi.aq MVҢ\z ,!5nMU`(b lzĜi2Ma%\6,(uj+S9ӽnu̙J&@62-g@*h8'}^t/l>Id&Z n]OP ji{Pͥ1>C -k ftks1:n,û ָϡ5T^$jJ%C)zwe1j,(ҢgHS.,,jlR t}%(dqgX^rfLNye>]9pEL4bgEV ަm2>CeyO`wX1Lrݰd'AlR}Y.a'=8$!v +! !@rl*>+=òpY‡蕨pR !^9>1>)>9N_J8+ +3uΝrNL ~Ͱb; gyphۘesc0D/ar,?0.S uAv2/j+:1&F=&Sl߃on6'.^Tgp?O6]l&)5G8J0c(zG(^ nZѹ!ş,qΣMs=r'Oa)ǫj ߩ5~x\xܩ ~"ZQ]~/<{xx/7p >n~;Cx#Qݩaon C ~ܭ=^ @/C;~R0ļ'c ~Y  @=y(5xyA hX!< d#<| _C ~WU'>>IV~X;ާyop܏0w{<>?ᇼG>8Oa s~>1uk ~_ď3 <{xϹ=؎;y8÷xY%fݾ/}> |iy |)o0g﬛OV[b6u ԎP=$)#әL6hE4tÊ #cb !F*&OJ3ĀI[WT'Hd6y<W5&ڎ0vrF/]yF #m22+M)1dt)ٓH.HEjV3d|:6Ls"nl_Ap(#u$Xg"}2b՘gsFPfuM#. dbaۓ=Üh$*cFw&m}p S;-L#=1Iֽɐ!)%TFZ J& 5 OIȘ4j|uHh-cr$רOi6>&g-6)'w&ls'Ӟ!m2m禒'YNuRpC =iGwLP^>aZ~N1/@4q.f*}jKfS?)#6r mI D5 J%S#Gq #: 3#>ROOu\g9K~/"<UoI9lLb;˽ao%t<iL]tqCg[:CY V!PGPW/ ҿaj63hqh=(eM48ApN1 qyқ:|1|q&u@]4b!b$Q(IxSG"pZ"Y\ea*e#ZM,G RN. ]MEX$j"xcD98Wi&!uBP'l ^0Je]otT:!t2ҿP-AE:VSE(Pe .;%T\DLD0RT5=U+u$4=}[f]t>閊QlVAqu  L`$Z\`aFs٪S/Gk]dYJ]lgu,޼d@\MȳHN$x-׉庸"6\=we)depR>cvmFk֐nJ ~"T^q޲-NU^;x叉i;y#;dd'kf+N/3Em>.Y oF~OARUTr/ݵC֍gմj5v ̿]6x ~Ҫ{Ξ7bM]>.GWoԙzbL~(Sq:Sq]fלG~A/+yvfbi)]b {,Փy G-|RW//'I|ROOWԓ_;'z0>"A pyuߺP4 Ta%a~vR~-rUPC<@8Bh!OB(6'Dz"*D$YDp]?LZkQi%sPO}Y}>T!U0TS+q%AL,D"0Eؘ @X5uGmI:ŗ.'E^(< ST}cࣝeP.t]UjWi9PD-! c?f}ڨ(TjI%ld3}XKnm0ߨQߦC!aDGuN-2a,t%1XjKI D]:Z9sJtB[iJ ]A[+]d<5V~E3ڻKD8۩J¾ThʷRj Zx^= snFQv(% >i -кK#|6;TjĶ0* HAb[ iz v)9$/CZIfm i3Lx.gc+Y\`Z}B"QU˅Jυ#!5TYxܴP([ü@Uh8tgM㩲"-M~{=.+͕8*ZUo}hpW"YP-E;)d/#* diqwR푣BņVlwM򨃾3e^6xtE >:YFjِiG`{/ԅ'a@OҐ2g㻪M톐\$\KKE!1Е殾l6kÖ\s}\)/"EDdEIϐ(XV=sA [pLuT_pk8zOJ=r'U,^D"dFFv-x(9Zױo4 ¢PQO"q?܏û׹ol?7e/y{$XߓUhzvU )q'~O'|d?sғ+ FdH ){)rR<]7zy-IjZZo-,~른2k}T]@ʰ-?بO07simzOQ9*py[5c_&Ç8l^^(4LrZӫz" G }T96(Xн< e5vrE#Tԛ}|T8"EAaÝ}:.k1k;QM!QTMij)`C ?'^ zEuAǣR(9+i[㾌ʴOg}R.[}/X`:2J< {L(iWv+ @n۹\{Z`uϟG AȤ0(kIxExBJUlK%&uBMXgL_bH(p >HxˬTtRf)aiOcP-Ka#@8Zj$Dp5u`\˺ U/]nHw U+yôpTFH*Hy/0_wmx울`wי準ŪkiNly[9%ιd )iEtmd-f)i皰̟L>UrsKR5 UR8Heз(YS4PIiNI+RJ 2{C}Qp_!p#J"@Bz?-Ўd>[p=>L2qXnPK gx8;{Aorg/jgraph/graph/DefaultGraphSelectionModel$CellPlaceHolder.classQN@=CK+Xy eQZ7 >*P2)VB ?2a,;̹||~8n"c] :r: a׿O 6 Zytlxm˛AA :_j a j@ǂ֋p'Z:1=}3${@sE>YWJSSr$0,A0:AU!fY{(VnmgHˆ̤u X/c8NQeMQ&+X³oSRc[PPK gx8;@4ǰ 1org/jgraph/graph/DefaultGraphSelectionModel.classXg`[g/jlIi lǍBRmP[A\I: ih떦4Ĥ t9{[=dI~aǽ{ιg~ȟ{VҾ>OOb&t^|V|Ut|1K2|Y ܬkv|ߪ·:C? `)TxTOhOegBs ¯e>&w2^?G ]so/2U2CSW-{ DžnVTxL^%Y"V[哩(˩,)=rK[B|̆7[7+1QP[A6!v 1.N7xuAR|BGjx[Z{Afrqr-JNl)R||VKԐ< )˯-'͉׸ć֖o1}ܗoQ.reLje9Ѵ2"[Z(k)s{4'Bd68˝-5\{h3&Rf<KA'XRr{\}oލ~ J"斲wσ!qΖђםVeAȳIٓ̚ ,`> +t㸵̫M'=% ֲ4[amӹvwb &&'ܱBt6e,ID*@ sT,HZ2 huu!"E2L~3'P*9"dL֠6Ziu&?7r9FT x^ɯQo2}9$n MnƘ\06.u0zN2h=[Jk s4S_N֊951iAvڢFL|,/KATs /`ХxAϡ]F]:m2o3h3t9d`8 )ýx;+nc6?`=Eu'7 B=Š+zE|VZӀsAQ̠8dU<v\ȏhsw:e,٠A67Fk V [$?^.tMay5^C]rJ;+!i<Аp`T Ƞ=dz:M$l ' 䁆ŧ#xAIv@z pyy$̩ҋх ] "ÿH&3dDeXDU6&?g$j‘IW6[NYpeԭ/D|w1] ,n)N`rΟS8ǹx}I-ijOo[:E-ŮId3p>N4BEgP- 㨈31ށwEΠ*LSX2l0xmBA I8rjmim#G Hs, ,볃]#EZ 0e|N<vFOi0,е hX>Ŷw:V+ <|3|6-M*Щi\H%Bce|ϖ=1H):C<h{62S͈Cz&_[79F׎H#kKXQOcDkx"x%rǓuH,bېQ&M)M)9n+WtMw㐭'teAmb햦5BFlv*Î3y;&wAOj'ǽggF5}Ӿ w#*5uZ6}|b+J`Jͷ&Mw K|w`KnRaP=sŏ\' \ZK;nv̛ƃ6\R>7puj iˆ$ka41N!.+_fL!wOaT~ w!fq=Ű뱛Ld&M:@ZfFOzn"s=hrGބqt2WA ;v{HfV[޹ YC@!k< =26WPK gx8;xfW"org/jgraph/graph/DefaultPort.class}o@ƿm'[Mz@8;WK[ T}3!c#- X 7҆<;w` +0;@1t|ck\/7-ysG]vc;uhA1YK;HZnMoGf[1J ڶ@Q91ۭ`SΧf&toOU%S* d|whGPc챔O8eiHvL"=UJ%]I*JR&'^UWPTebU\&rBcuE4)oPO5CN'(0ht{N۷]mC|7z6inxC{K-Bx3[8{,pZhqUt!B `fǁt$t#Č J."qR{9,{r!ҟ$UI0N3a (9sYUVXu\*S89QIX\XVNP8%\᜔ԍ 4J2^,!~`ipgoPK gx8;^ (org/jgraph/graph/DefaultRealEditor.classSmOP~.n>P@v/hQYbdV踶&f&~0e=Av^v>v~vFҒ̼tFϼ"bFԒҢTF MIJDļt}k" d#j` /JJ/J,Ї)@:0HԜFY@}@d4c\@Y8PK gx8;,=#org/jgraph/graph/EdgeRenderer.classz |T̛e%30IDbMPlV&/1L vtn`jt PݵV꯭mޟ]~{ߛ%ɄY9{7|_z.xF& 7;|-}.nE;e/]LA|!|T;|NB 'T,8'!^Z.>{@| z}`?π@|8꡽ _?kˤ~}+' b i<p~+Έ/ð]< aAWq|8 5q :8„w\7R_u mkKmvgUoOVys<yO<.A}x)"~`H~ ~)ts/ ~} _yKeدot[/;=z@UI?/w?}_J)%zN\,^Eχ^4x,o)>9'+M3=r yl#lsp>< tKE`Z%p,*\*C;Y ]w!2/sUCb7yYK=mX*/r JU\ &]6{) ٢V/=+`a=+r]+?ZZÎQzBb6 `3p>%tk.yq##[ty^ny |&]K2$ s|Ւ r.#JֶocuSkuWn*Ht 2zk᡾X$%hFe=2+*[mֺy[bCqd#ӌb3ƒyڜD 4}NfNmƙZ`tY]vbIs0b2cw##.^1-`kOH&43`y~s0LkyAsxo<ӿ*3ʈ6AEqeg`xgSMdkjjx4 pTD-\VĠ]3o+!{imA768l&SG9[Xbi7U\|ep8_=d;w )9vgv;xs<.|1 > Q>E9e= Y շN %;Mާ&-^?yy2W06y<fݺ0mmaZZtq_E!K膨~C,  x&Ma.Es.{ i^C,H+ ഛ(2fq!4d@'. .FQ zCxaG%_u9`A!oIC0]Jr@͘Z K6y!ʛ ʷ"lț; $uNCQb0ݻ>C[j5b!6MSCԐ#Đaא3nm/?h+JC y!?,?bȏʏC~\ib!G%WeSCwBu C-f< Iy!·K.Ss'?m# gaTh3_gAмis.]>8u`i%C>3L>==5D{ /`,ۋ@SD)lFC!%_2 Ml7ě 7 Y4SgD C\+3ﰻ~o?W Gh\R:(:lCِ5 ?| @G#C44 e?М'4{ӄ3Bs >YcjI{{Y, VI r{8ܚ +AKwg"LIU/]ѕ^,3Qϓj$UV J&Հ ƈ`l0xP诞x{_hd 6 osA<OȦr\%VHYzMmE`5ng[ϵ(X]xC+战8-jĬwjٿm@.g{^(})5b*ϑ!HݤFmeOo-c?ho N&Y%E,n YؙJy'iK&fv2-&o%SV:iFT#'Z1lJ겞)-UԲ(P5Inޣy1գo&ZTk;3 S%Y[<ҳ:پXk;els/.%:rso59Klz,¹$7{ 6 j{&/Vyo&UYT1G=XjG2}GNE'J˼ﮎ'=} .xji#a(x1T-_u]-ַ^׼i#w:tm>_|2.b4Z3mie.q&HTWuN *Gz'ʟ]Y䭹>3zbEt*< p\XXeOa̟Mڬ]^U69tX/()rSR{YkףۙWm|f ԓ su, ^vc5ڼ}F{iS\i%U+L7Z7&ݢܧ ˩nw0[x/H3œ6#r9wB:Mj WVMw`">^ӂQa 5ж2h 9N( =ԙ#4F]";ESxhĎLVPDi_Q*{zyD]ahWY4 Vx|zIE܆g{p+ԧZй݇''zO(Rm(Qmݖmΰ WL%we l9v;nϳv Tnb]".PmR{ 8Ϳ_[A_NCP=< 2J5HuzYdɑ=#{"8TW0ȿ; ;8} KYB[#j&,Tl^5M=zޜա5z%a*!V\g'5,WbQQ_;.va Iq𸘗IΪTy8^.n|1FwUYou|hέa\ }}(=t-*Zw]Vt;Yj8.=^_L+s*0*omLT9~T>趇 D`8u[Ngf'U:b{SCFީ~G%gþ3%8Vw#[]POAݹEpM;O r.GIzz>Fju*=uZG]}:ii^Z:Cvq1>co~{kyaL<8F/0 z1M?ntgZtkˣ(/ )vdF| 6e23Gul_P4>fۯUs{~,e/bVUvuQ̿t{JmWNZ]xΗUl^%§>k%j!W<_Nt?a3_=hw2GKQėj~voV#m9+XΜ/*rʢ6emgzIT9m\џi,gbl1;mHӯx86M6O==+wN:nRZVU[jdrDH6g\Rn>s=CtqH~E?M8G"~Bu5⑨"?dDMM/MNXDSz+/*sVzM)eg `EAVL2#0:Piؖ%wM*,5E+># a0 93^#gu/֎K.!bӣy(}"*>]7 uS).ӧC"'‘/}@ MazTrzT*嚈k\ sŞC/P{K5_) ǃ&tP͢+cbO*org/jgraph/graph/EdgeView$EdgeHandle.class; x[ѳ'=K^V,%vrٖ )H qHKEXINU3vr(S(BOzQ( m$۲_f{0t'f8L&0\Ej{cMԛO`-d\PÎbV3b3? N`'l)͜Bp2!,rkat 8hFmr: B.uPo V$@'%#BxRE( l"*gsb ia3Nq -tFTg[ !Fz.݄z]ζtz)ꥉ4EB_BȗJܕx9~erv\IOn"zOp-\Mktv :b{٧I73D:=sfzn^?haiN$}'Ξq>v%U5B:ug|zߠ =G!&(!o:v{ʾKHT9#9zߧ-?c֏ ~B /I[~I-6/whӯ ٻ7L%;{#G5W"7' I_:أ`Vd9j:Tj0"hEWºXd'!%(R7G FT/t0P_2IHm٩pdWv: Uw;MHb[^؊$MDB`O4(x"DlOSٕN%?P0Kn%SU;"x–`y--"9H<#ؓ^!G±tP^0>. W&±-Hsk,5宦N$tzKb[o,:N jKX;.I!iⒺuD, %#tk?.Q4S'bN&f/YRaAm+K%[D|,'zJ%e2PzFHW'iX¥ nl+&RZ]'^!M91BSM+})*wRJƖXjMdȝDNx+LsJ?dFߵ#vGco*$d$"|5lMQz58ZtՉTL8R7sHPQڛLH*%eTWKM8d#_|f䬓҉XGWgG0 #i)uɦ x +5+7Lÿ+)o;N?mT\g \/M׸]pG@N]P ^bKx)Ke\t^!x%w >W ^'«x}^Jʧ|318=|j z}AF4/X$s0.:o8~~| ?A' $|ˉB I;n2@}SSo1_IwUg||gu:+ {>C]P$.QMlCK'<(f<#oQo1ZxpK`*jZNP2xV`L[8+6{[41གoGK{%S$I!%x ݉Z绨zPǨjq"ɋWY9ݹb2, o/!)8K[I'zw;.X&Ou6|)ز%I ~' Gs_KD `լ&G)K#b #?Fq< >D8ğ0RlOɭV_k?LJCL&璻#&Io Dk"X%&#Ioâ^)uǿ`ָluN \)yfVJAM$xT%8AfSU[eDfkb7*\B)dls_:eҌR'Yje5:mZytK05;Op"nYNwRnAϓ rRBR=dOXN2m@=fjNLA(Q5Bq=izDl0T?BG.dnhBIH~rH\Ԏu(C D ()d$$vxy0mG,.Lv5ɡ6ZR>F2ؓڒHnSr˶lAX(K-wֶ/.F6+ 4*CU,PW~GҒ>%R#nzhяksJ2M{kd 8I~$@@"Q㍦%’|7`N̩^( Ez)3C9Y ͔&C;\,%)6ih2ˁW=iZ̋6gL[;Qv8dD'H%GZ[6Llܣ/] ZVG@q=0Jh*웱X>ײr܎$'`NARsM:jR2FkFR!ɒ|btn,_<¬L4TN6nfNR?Fm򱳘Ovc_y 45{^՛WQ= Iy u tu jv0jX:9H} zڬ|PzkQ|}4 滧yT }YZj@t;k>&]^0,j >h<)yT8-:N-)Y(j^W`3Ѓ>Mn0a"e+KrYnVnyFaUxdY5cWOWkppg|V! $kl2aQršÈ23sU¢SB ӮeF?ק 8P OvhɆ)#3dCp$TϢw8`i3˥C8<_cGC ZRJYz {uaoW6 r/2 2T7!{asXyiPVZY;{ZϨʕ'ҟ-w5ƋUTj1]$o|;'·veL>ie>tAK"x^8vLXʈZlb}tUU=U}5YPx^U$f 2^ڍO1>N؍lW7swk3(VE_ޤۥQ~DC>Z}:+u<?C{0pWU}#6R|p 7nb9֬iW:qE_ҡuv BE[{= 1_Z|lRՍxyM$(/K(?|pפp0dDX7->;@ߴ,od2+e԰PK gx8;zw;'org/jgraph/graph/EdgeView.classY |[řz,ۑeb;!$,aHEAIldع,=JHrHv9Ж@M/m5G| ]{[ض{tݥ{ueo@Hׯ3L!}CǐZ3pF"NgPN:sF0jtd69c,ǹVizGsFGx6O"ə=M%uvLu48ew2=\|ڌsL" i u~PZڱla˂]˙#ڟ*}`6(rAJi #a <./r']ΠUKbw,e "z{2Ԥ7k߽.[2Ix/s>x sAЁۺzih1teQ$<3{4v"}R# +q}Igr2Oj:Bugw2#'xrr3R'veөVdƂ%34ۺ;>1'1h-K292ӆPm4AʇA 9I$OhL6QVϠ}k,o)> S1> R=GxYnf>S֯~ݢ6ySԼ_R)xZO&`evH0愥3@dvDVݦهvSaQaPGIν<$;gTqx~Iy v_+c038q =H-UD-ޟ|fF3h0uNSgz|n8A>OO+}TogY9f${RaO60e ;˞Go Q| Vx a0Kc"+)~艝]ov <0)ǐ7Oi[hf~,eEh;9Xʞ p|ZR\&92kZQ&^[#D7:JL.SS(XFgӽ;x_y_8U1bt0X׵jmرEfʥ?C%kac5鰠A'[QŁc8* 60,.@5jWJqA+ZfZ AXA!ZZ3 L]Rg[ @P6΁biāULC"xUBi5`' 5M'X'q %X$jڥ`EK-9>UZ94%Ivh /jP7 WaEg@ l-Ɵih 8$O~p%E$sqXh*C-9b Jj`4 a oZWz^ ף>thRu/@ hom|dfjԴPDf!è惪Mj~W@thEkgδ.`F¦-p6iDln0x? L} J?b eY@b7@mpQjxCf)M3ގπ|/–#i6ۨNM rnAL1q+¡s5]EG@_Uv4iK\=bGejTPT 5mN/:! w^=G(^4M`1l\c,N!F~-IC:6 }AM3iѧџ+L7~tc,O#N~;7c*oF~@)W5#LǤ~!Ztb*ls)3H*]LgAb?U_[]4Y%l>*bx,A\5um`^,ܺ \kKy^¼*a*QA_?þ+B, i {tz{o0X< i49gDE8t$ a"O SlJ$PeF/( X)ܸ5za1 Giu aSi9aSP_r p\SL vƧvCoq@\Bu FH"̈_Gt#Z"3 _˓6j(uX(*spGr"9wBO75%ߎ&ĉvkȢZ-YV|iM,͂cZ!y)f yAUZ[?wԫ,h#?nIW .#8/# OAjG( KȟqpeWhJlaY$mA)b?x?ow}xo͆O;lڜ^vӍI.5 U&M#=K>'YHv.q2r>jx:9}a 1>鳀UO= ֊R&=(ԪU+}SguKg(||~Hu@ hEvCp&{e xebvBXb>F#-&(^5f{m:dJ)0{g@vHm.KEv]y ˕wJ.8DBϥEwXIڪU$p[ETjuJ4zmPV#*4IZ:aSkȒ)Xl,e̫Epv4O. ~'|(G'8_"U%pW_h) V 4q:*/@5a+fAG|XIj`@:ɋxvonA,gZ&ೞOj__Ž_gt%\ڼU4*c=TTÍaGjn玡Cx+V)~4у*T2|HꀷR*ޣ{dHūvCPWq4bf՜|>T"9!Rj0}s:wcиt)tT c%pƨbV^u?pB^RVQ+WKDƯ|`~ea_nBFS&6AuD"V 4 Gh8‡bWE\B?/M O_V&bS[uR<,6QÃ)TdVX2N?Ȟc@@h}Y}NoPK gx8;dì org/jgraph/graph/GraphCell.class;o>=v.vnFǒ̤ҒbFE MtĂ } WX` MA'+,Q$3GJZPXZ% j12Ha(qΩ99@G:L?)+5YX4+P$ q0pXPK gx8;J&org/jgraph/graph/GraphCellEditor.class;o>=Nv6vvvFĂ Ԝה̒"܂ԼF tt }/BkIJDļt}(MXby>\5#ff#a b L B-##3= d1PK gx8;Ygn:%org/jgraph/graph/GraphConstants.classZy|kI>3Nq+1I8eɑdvWײY2(ЃҖw($mB(mhi wK('7hw,}>};o̼f_$!dI$A7:u&ެN‷V'ivq;\Nx}{>~|nr#2|EnϏQd8?R V32ܡ2$nYQ^+pD9I;|Up\e8 <(I'9?+_PQ8c |Y&8ϯ(_#M+O*ߐ |KoxZ+~?Q L+ P2=s2F)/;~#uϟxQ(W7.?V?N/$iŷ*%px qD%%T$WJ\C@\^3 U}hg@7RڽjO0o\> n U Aߏ=d&MfG8Y ^ aկPp$>c~ GwzB@0" !lHzpCfˆxT,}89Y/cJs}(2gHnjʕ^g0Ra9Kd̯NC`_0` ¡ y:SCTC&i Ϩ//c*FM0Cl<C_2{LFYOcblNǴuL}Xi~6 zpb`5d: p:Bӣz"{8ɉ3^N ~\p_O0co5ajFP Tuo)7t#hdb(ѽ d9oiry=!B(۫`}{C%r]b%%ƛ!QQV:SO)kૹ j¥Agt!5P #^5`6hN9J/\ UE{z( DtV#ѯ!.D<2UPZ <`OQWyA?Q}2ctgQOO$!q7BOƳ`ߞԴ|*E'cr!KO]_ whW"i5Dx4y:HU̪WcgЅWqjc:7J l/T'KZ/ zI+4l)Ϙ ic AtjaԪg 0;@6Ťi()vPag P8?zR>k鍨kùc}lɘ1&TLCħ/#3%īOUZwӈcLMNy\Tt:%̶MTZ#Z:&zSL7m,dSl_3yPgҌA|n/(i=b:` 8XOi-F v"9=%ɜaISz)^ND,tgcvX*{g0wb}Hml/w#5R,R)6'C/XBcHF%Ra|I=^elXoA&U-F[!*v GxbO9bp)̤X&9874dsh]d˗5-/5\ #/F= Xj"<,\uzxNa4ܮbDڥΩ~t__97y<4/%Z ktly#KhryLB&<]Ԛ399%cYIr=cփ[rKX;_#q[d--jRTꩤ-5JVH+e J:SVfi[:K*]NMfWH|RQJUni*Y impKnEڀTQpUrאעӶNr+,!bZڤ& n)~㨶/b;Jh$kwK<(1P6*%{<)VrVg(f]QXWMYj&YĶ\1x&s3T evVT- Kۆ%jW4_::S*'M^G^]3 G]r, K "A|nzavh|IU*K> KhF˕0J&W(Q,QZ,͊_`5SBUZrRͳD_Yei#٢#Gi79o\#6-5q&zXcz5GώܓFWanȂۣ{_fq۩}t체-mL,&/J504фN0@yn.L|=fɍ-t>V6ozB";՟J!Wj/ڑ+aVhc_Y"N% :_їKdpTAjU#O0.(.ø~*GC?MaD-wcy~)S46,,ko(H]Pc+zF3 px^:(é` `qFF% < Y.tlIhѿ'L*34o1ًTXH7o0qYR!mg-*Rk۫[~FK֐D rZп҄?ןM rc$y#ގqEo+6o;Bn{9>7!?#(9| [91ķYx}4=?M>k߈E|'B|7 ,p {9|&nC|ÛÝ?-rxc>}ގ8/@|?px'9|!ޅ!_a{?n_p/G9܇A%Oqxc!2AďsxW98Bu?('9A#i(px w9<{G4/E}a?q8ޏ'އS$g8<*|ǻ,% ڎogs g8$XI[xgG>RrOoX5W}[QAd|^~,8EOvT cD;F@yzGHޱc ]%՗c2lYѡ?jJ)8)/!{/CD72Ewl[fW'QI6`ɮUt0t9Vm*m*}8o" MgqGW!RSlo*-9N%6" MToGI%Ǿ;6]ϋ,cݎ#'U#]8Jo&5(yyPD#Ewȇ!pE#b|||DD|TD>Z|LD>&&! Ը%L]8ck xR U&KexyS0m'j0coʤYTfE8*Jē'n[:xMKDMP%EMfḧ7ϼhWcbǏ>ޫ*m*Kp>Ȍbf1G~T#Lcapx`{Y'>ۇ3* >Ǜ+T"3<Aޤ?€qF'*s_ H );-"43IU[9;kRAVd5]^Xkk #m z.Hs%in.fz>A7J1kbf5M)# Is2NPE>Ӕ`q+ Kfc|kw{ZKP^QpѝKCbgVmN |"pkܬ^\۬Ϧx2Ai*:Dzכ.(F>|dTWr$%%+h}b#6MȬ2MY͚Rzmt:0m}6Zi+\?6mk)+Xb~.a[OC }@(DP2iBtN OC"(8fp4B'b9 gZ8vj yx3X;{eoEȝ 8 oӏ$ێ54K492SPIȞ͸a_< x YL9s!o"وp느Wŀô,}mkCXƊK"}^F9fRHN{}<u[l0N^oH( z&sOG2&3l y ('?["QـM@\*11ҧN /۲~^Vn{2A -8'rǖg\Gi%I@n6 %u,d$٦]!d@TB@NȓE\d; ްk%mzpGX]yx{IL'reGuSfG[WccR)!LIymh.\/ʃPbHQrt^SD?4f*z"D8h%?r?mW{vU [qilg 3x EYjq,a&GVY`ky tw׎y(y/"@^#-"ɢO qz^cd_PK gx8;&^@)org/jgraph/graph/GraphLayoutCache$1.classRn@=46I mBEC + \*R)/}ں+ۑkW:%|MJЕ<3ٙY7gj`%(@Ǯ==EP\d [ǝ$[_1T =Ma~ΰ7Q&tJ^q(27o_TcXrS yg"aݴ;}>Nc|kꊳd0"%0ɳwRI.u4qn.U=Jԣ&PHiɹ9XAAaWa3lOt|9]$H< F6c.[=aـ[0s8,SeNdxsMM D,g>h{::\Z-W@6]} f?D~zaA;% nְ>lJY`ٻ ʹsMlX]UlJ4ƾVPK gx8;jC<org/jgraph/graph/GraphLayoutCache$GraphLayoutCacheEdit.classW{W$k-9n8E6 Dm*'WvSS; vkYTcsHM(Pph!㖆ۅB|=l|ɧy~3_KWqdqc *dKcPdPb38(36[3X̭2׎Ѷ@ Ţ% a|>ۏ18ǚg,Iޞc=iI\8/ dU|IWU|]A[^U78-hf3͞y{XAGŒ Y\)5o\0bL* Y.ϒb Plgk$NG˙IBr]1uAe x[Ҕ,&qMigLQ) B/ˡQ2+%Ȃvi%@8pB[2kvN˗$Ǧ $m=OtCUpg:t7xH,yQ&$+J{ڬYyr#5Xo: }NkU9bM@`$HhܨJf)V悿Abo#in9ۈ>}}IaWSKLഉ-e&ͪgY3 oPEFqD ax8 ;ZD'vC C,7PMoaRN%n^g|)wsl{W} [H?x?9~^/~Ku, *^x"<([;*('_3"*7zWp53aS&qÐVU}LNȥI iM1) ܊[3 `wbǍ bmX`5S$͔[Bk8NmN[ Vs.KYiUPɥBcAF:xp_ ऒVpu~!bE3 r+1GӑǴd3eR:JA1Ӳ--6wn89aTjҒV=< YzZ64K#'lAz[Q9velc"ϟ^pU)[͚BY66P4=%,Y-pfa KM0e\9ݒz9W{vGеA~:k=Oқw{+ΚaO6΁u61ӮQT* :B/:,xĢf+눤:D/C=:b} vdN|*/!~'@ٝ>] `|t ;vgI݅eGMDe~bܐ^vWd){$PY=/1s\e8wB0>ؕpޡ5fKWq+8y>\:` C\yig}I 9yree^terg9}e쪣 R ?wk81s> NxZa 7~Wtn՜8;; ְiە66o' K6& mɶUQp} BFѵJFQ7AFūfdĵl>\@6d5$} Y52N\=NƜZ$cŠ3W"A.p3zNO8 k㡰^e62}d/;3kPK gx8;DWS*i'org/jgraph/graph/GraphLayoutCache.class\ |TyoB`! a(QQN@`=d GJ=Z^ xT"X`mi>lk}g$o|>{7{g0'F14LcLF4o7#fv1zh*LB34SLCshff*4DDL#`sLcnMSǛƼY`'lDӨz8 hAsJ5NE蝁fq5Ko,Û`3M[dMi %Fi eji  ƹ\L.֢Y>G<4lDs>゠qqi\`Fho栱hAs bѴq[hR V4@IzpwƝvqO]hnӸq?6Ԋ0çEgg1n4y4 zGf< Sx*yb >aD 2E)J8{f:Ѿ.+ٱ4Ѹ%3-^ښdBΎTCeUmɥ[mIF3[R;2wd:Zښ%Fhn7#m[2-Zlm2=Is/̟|KKSҝ5o8TNnKn۔LgºIp 2[[#uж$m)l[ն|g{qȬj[jmMgmd6TҨ5B`񲼗IdLfySs2snjbd;$H:VbMγ~9QwIb{bvBm])4G3Dce)Ye]*<jjt ܖho8%:weƊm+mss#ٴڧB-$ Ɏ S:Ҽo{*hoO;Z˲Dk⎎t˦Ύ:kZלmbL99K.NK6C9k%I+-jֱ%șԖE8,%rjH<̵d#fl!MM;mJ AK@5 Xhm8\5-Asj|S"{>ٹUyj%ק 2U4'; 1>}01Xa p@s#x?-Δ+:ۛБpd$D >^u>sms $/z]tdGI>I m"cHVj cYF%eV|dk*ѤҜ~6kq\X5a(F˪* o4VVH :ݰ̊s*6FLř0eUj\yLP9I#Fă`kG'>os#z!%_'H7 tk7Pkւ,,*mbUdrAHVҲl)ܨy5f;29(6 *h٤Dg6"g4U䰺j G8OYήv<7#9 eB殥&3R1K[Sm8#!e܅UZ$9?4'ji |&Y]Nu#)KNITH;ζf؎T3EFvR!K:#WOkUjlS, ѓAz,e#8wA4X:lacڐd0yQ?>]GjIq+-ۑZNaYt_fG y֠NSZڢ*Nhs-TJa_ \kJ41վkDl]ܽHU G[9jFk[Sk22elʠJ:҉L+Qa]>ҼeSG7毭KN4ϪΎɟ,ͩWmmWȊtz$ 9;h,i$цhfzXCu"^O4Z2a\ 6 P*X%ƒk!Fu Bؖk+c3MúòKoK˻Or`p0':npjbTaZ6.&R6HZ^/U&ס$]PTmxts.&/-#*i*>\o lelNНDBiڜL"-QKJ+2v7t ݦB;(gi' DmSuY_E <%?hRUtSXcـT jhH}J-KnV@t*mgxΦ 2lbRtWr@Y砩COE3 qhf&YhFAECG3ϵh)7/'P(g%2[:#Y$~2db+ X|!?5d,~*?߬."~ŗޥh,~"D[FQSLD~ůk,~ JsM)Tc$&Y|D >ñOx_g)b*g3GV~YSni8KL38)fZb_·ZUTY:~/@M+OXD1[DLDL8S$Zx&ЂbK-~18K,0 8MT9sTK,~x%NZTqXd-q$%b)6t%X.VXL} 8/قT)#e wZb%oF[bXm&@4 knK I_f\_:,x.։r2x}/Jl@sz)Yb# 1~%.b).ł6s͝hB)",g0dCfNx;M Ž-]e.Av78{x@|jAY)C)V, Q"|Vo UL7*<:$0bu.欤"OjؚLq'5M|/ u{Ob T }M&9wlH~SU+%(ȨJ7uTɔ:ufU9 RsTrHkr?D(糾V?.~ ƠSؒ;aOXkf[MQx_+ݰ%6?%ׁ6?=޹W'׊Rزmjn,ɴ-\% s,)(t啖%m =#J!ՙnL"gKb%ʏ]w lއ] UɊtj=MP0+9DG.WWAqYn3k8oO%eW 7K)\(p9YXMN'ZCX+CT~68FVV-?CѸ$" Wz)̫4BZ!=O9am=> mnk؅oonX'w(joM4&I>z)tY>xEuEIt`d >2R+M]v|.K^V߰mD{AܿݨuyUk,9g#F9&ޞ7HLJíйiԲysv&Pm؋ _\zմ/7A!YԱEyp)hݥ7 Z阩 1@X(+ܿkﭪ]!ec{{*X~',Ik _{ч>!\ƀFXw0=el*d+X%NտqwgYJֿտ1׿s\{G9Ϙ|ZO'j{?~w.k)T~]Ogw^e]&(*gUnYڳi20Nc"oAff~):%8c X`H4&`NCc>a,@WdVf!2<̤S"YqD`%6 M)GWf xBozє1f'FF`h*8261Ji=Nԝy͊gsR7F6Es|n|^$lu$zվ/~d/[X=lId !o :=D}xUwYvRrưFXMN-|f|ƠI9)UVsi -sf}q'D{U6DYv~- |N҉wɲ?mwwžc0[!;ȖZxQ,a l1zJ}eي}sߨ1h_ؙ\e|>Mbf/\e_/kiah@- agMa3D{ԁ E=$YVw\y^[eSpa5Ȳ56<!u"(Z\ΞgkWFc=lbLoCzH/KE.j{zκl؋I2;6ygQeH:%v͊b!q ~)lIMcDK~]_ㅚGSTC~z{C~/5Ea3\.0Xur/4v6=] t␧+q;M r~"%8 ̛5+!\ Z)UFx,Z€o,,5\V̞z\Gl ,?ޮ AKt7cB-y=.VKKXö([Kض֘a3lm)uMżgr@:0ǖKV5o3aLxq)-l8[S/=!xȣĖ4JbweHq?)Qr_‹}ڼQ9Tv%XE&l=:Wz%z4v:6 g]|"X.AL3%챗1fu+]+!&փyGƉ;U1fTm%~6]EdZ-|[^dvgþV,1fnV,dOj7$ V{H8.V" HCRT[a4{ؘ8CVbٝ\A@v{iWkޞB[dVH/n!AZST'˺IoiK5d?ה:WI7`̰䘽lADpԙ7O{6 82|ڈ(ޤ҅dz')6^D>`WV\ lRauPJ} ^knM/ DooFV=+ VD6YvwԇҢ{(9ƂBZ$V}D};QgfY7MXԿj,IȃΫ|ܕGyy؛ ) !K/Bw|v քXcvH y\ǯ\4zy>g6R`NPopF{)ďjKGݡh&ӵ"Q2{)wp:9;]$]"DBAȧ4H5( JqdgY7%p"a2=ކFѶUZ~_#]ܲd<(p\k=1賔I9J2WG8vW)U{jT!! ?tN*F I6c2{X]};~d'Js7'9L }t?<ݼ'rAtΫx"/ً-xwHMjLn~>^ ~%e#M*x*䐈^WCN+kKuQYEV$s*g`I~$ \C)(M_C{}ZuLD{T0In@wy`yq{:zŦ3 >fi|a HY x ^u\ӹ0ɧ=F?I& ^^O=]S3>\26)kq=+z&飰0JJ%%ɶ^[va;Jan6AWgQS!e9';m`?ԴOWoRd=C Yq Yĵ.SvY@vPI^,FFcas Ңxa_V'#S%?I˓`[@h0-BT{े/x)T |>.Kr! ’CbQwwoS#I CQ|nqY5lq 0ӧs}} aMz,{-'*PODo!=B 42Yz%~=bmKϢ d A:Uf B끐L]9:X if5)eJ(Ie#Ph7 &a1`6g("~37 @oDfvܝR"Rp6>GN,p=GPm݂U\ޣsR=Qe;m*_q5I$9SL:u< |bnuh#qSKˆ"R7+5 db#Hu{=px m~=5]$;ѹhD(m"z{+졮|LϲK>DstT: u,9 UEϼO\Oie~&}}.ٕFSWQ$U.חE?z}dL$ ǔ>#%Z?$QÜ-:bdǦcwrFDwaS:&΀]a W@7q{( ̴b-'e3-Y#:y^9JMyR?)Dr'̺f/仏ؾlmdpQ饭{UNyɌ:c%%M!s/1ki!ҁ 䇒G%c UN;?kLDEjKQSA $ BO{rc{ɸ\ ߄ qd/A}/p;{! " i`|0g58vze[C8rE\][$RHgIqu4D\NȗGvGٞG4HRf_JՊFr'%(*j"#;]d^R IauN3sؗ_ɧtBc~Q˺siB^=?"Q B\jȓ`>3C2UYNMEU)ќciR'"mK ~9"0| Jn粨yr?Qmk{ryK2E>[k'˜WS8_Fh/Bů8䙑HkWu|}G ssq(I;U{2aoXa e$#̲!K̛8\gO'- IGC֔ҌQ,}5;dC}9՗%E}u4J.#idPtZ𶯒GE :P)$Cɐj$@EQKYր;㪊) |݉ioR0 WGb*embbI"#=^U{9HVDe|ydܼ(M_Bs u`YN/o8S6$-a ]lއ:WA$5p_?]㵰/,nhqM?AM܃^dOɲwVi?>J=MMV^EⰊBv߇冽ooM={/Åvת  >{j hP48w @۰?=h.Y0YFYuy[S@gTAerr߬Yn _|&'6=/ E !隸k'|'1ᦩ_x /:5~Q]*W{Y QēPa ΂hJ7ψ|Wbd2 /n m|(({: a^ m_WcA|e+s,CF~ŠZ0+F|5.w(%X/!"71rSfy,ORߣP@7C磬R,{Gǃ&`lMks~>F+cm_vGtErGg}jeƹGgs]y6{o[w⏷^bF+xm?vG/B I]._ic$+%6%wԦaa_mqGm9膃^95LAW%-_dV F6Ǖ xN>F7'*tExiEXf|+}Xc|댏 ㆫ >gn Ɨ~PK gx8;M:F*org/jgraph/graph/GraphSelectionModel.classuSn@mBíi@bC`m#RPM=JȻ_U[%G39??'؀ 8dǽ{bMi|r̀ 6BHŅ:5$c{=9h qTȠbq%sCk*:JDE'>a(<NTjm-:]U1CG٪xs1M/ *iT)RZ(;ocbI-°{5U7dx#/]s_S,M4&oL2P`F V[3z%C?)Q8i> VkV/yW?"ݍnr{XԠNSSXB=oxhp6 >` nC۶~l][wg𙭟[+m7 C`FPK gx8;[f 2+org/jgraph/graph/GraphTransferHandler.classX xTOf2ys'6A2AL$H0 $83 EZZ/jkJ[0$ZmW[L2I&~%nsY{<n؋ y5xν7ix"26]!!u.[q+5OG;|@|HG9%GDڇ5|Dgerl8&$qܨ ?.>|eBNjxċ >%:j>1DŽ|N:|^D|IJ1t_ _'|<? Աc%2}W=9.@EڰVr9'> ~B/e+kapV;^A ?؉?k]o>O OK32l}N$=4"aF$ZbքX@ `Pޱ2#f{PPf-m F pr5on%[b28sۢ7&Y/Dhyܗl6f2nF=Vk#P,\Մ▙J/#X9Ghw⽍{zeظޞmAp>Nt=M,1ݝ)Ix4:3pq9<׊7rؓV6gshK,\贒97qh[4z/Y$\2kO_V36YN&sgPRp2@rGV\_ӓ8L{NY GMTQ%JLrETfk4p޴îI&݃IϻӊH: tVsf\QxKL$,>A5j2 4_!6 /#,>uȞG3-ŊΤ%-Ut&F^Q%@5rQV*\\:O*:<٬!o`j MLXoPfEEVŊ.5.EԦh-]FʙQLjܢp+nD~ڠh#]h]g&J> mRE-bL+Ej32,i q:htN^hvV4~Hdkڧ(D݊,/P> +C{5(ꧨGUuڶ?d H( JNo]R\#RRb=(1_K&F ECS>JRi?Pt]OX?1٘Fiz͹~껝<"ym\97#|6 cnQ^sn)fV"dEhe fap[RxẅIϻ;9۷P8#M|:s|-pƲOl)~ >3'w/JIΓfyjM气]3`3wmG(bR~v9"ϵLˋU\m_tΠɠm$?F]g{84չEoy+mc_2~ q?6ĝ9 YvyxMJή3_ D֓#2wL?l݋ONc'&`sp@0Vd}pxÉ=9+N5P¼Kј )ki(Mᨍ9=i6I !]:zq1&/:ē}};.Qnt/ fB\-n؞)+>\e SsqNLAB ~A=ـ]_:Qu'"fzO<ɸiqt&KvȌ VO:zK,GYN%bFpwg:^o̕qycT#6seN y45Rxgly6[ S٩c (;>Y59$*4'_bȅSgxLeE!{李kf6Sgb\+9?MW0KN">`-LXsLjd6H%2Wg ~i~%oK\I3G?sBnjO=gg&6ZٷaZ {#5 uv,`SɻD}Xp_?vtGbtGsQ=8u B\ؿalft ߍʆ+^tO7uM弩[O]} n#^ lhHA;J{?Ml11g.=F֠.n?`a%#( zmyeAʻ - !B>9A.p uchFN /Kjn 4zvA! 솓Ia^Ub0_z RXhx7JŵUΚ-۶NE$fZ,lȍbQyf!][AAش,cu]|U>ᯭa|xoo(Ή',{/J뺎ũ5:0D=\1lv_}/{CVhNź Q wa˂>iJ"i 5F;J 5k|7·VM`Q*YT"ePT63,%l4cSY](2KRhJ|PUs~z:8`WsZwR,\fLXQ*Ņ&*.E4l+U]3wMq\TNkUEUDcZ1m)a6>lẀ=J-XiT>Y^[\&c(2*SXwʌq^eo \% UFR|ס,W68w01faJBQlf[q$=J38ǘ31gW.w4WˀW~[\Ɯlò +D*Aš%XņDM[z.!F"oD|+ a:;0m=vQc3\TmeОWu,C3ԼCڶҺfLfflU>NʺUm)[u.)p".FZxW-N8Ek:ӌlŲLQ Ȏ \E7F6V2 ]dǷa5SɪtGo{⒖q5 6xryEܖ.elRgfӻaʎJ5fon$f1 qL WR#z'V!y]t8䧃hB4~hhp#t< HGڳ#)l࡯V0B^Sꎚ]BVS-ԁZ#_ux]? n~A([A1n#9#}O( 1tw]F?5}̸!Kr"3S@>y8/Utgb/Uq3\=)UԂA '>F_&_1AWT19a u -?EGFXs7DsPK gx8;M8}b 'org/jgraph/graph/GraphUndoManager.classTQSWldY#RB4l6nD4jZVV6,:E/>:c%?͒*{# eu"1&e f3\,1 -[;t|\ǕA~թt\{^E4dML}_:EZ^Aœ^KVxiWoߚm9^Ow geyj!պՁ^p]jUVu]ְѬY\V1W ݲ:2[5{Qdap鲫ֲ4hvEMkhN^/p8W>v^h`HTdp[K]ϧO`Gji,΢5Gpϧ1zy[3xrRMiaӚ< ܓa ?}_vc0axAQöL En9c(O{\3G#Cc֐x7z6o1I QQӜhFrWH>ktGLLSezlѿՈqDv8*h(TUz/Dv6UîW]L;A#AHe(o]T$ӆD1 GʒMecJjsR>DBeeGe#y_'D5P9NV#'>PK gx8;NM&org/jgraph/graph/ParentMap$Entry.class}N@ЖR ?uqӸ0hB~INL4|(-ō]33ܹ_:_Q#[l'cbDȠPnw}w(z!;-@ƍ Z{Џx015XIB\Pk_œ#! ȕfkEzXm#F#jM ۷a("/q,b߁1;##"̻j̈́z)`0AO􄑼PSmɧѪ "P8`t 9BJtC.3ry2/o.-}<]T~PK gx8;z org/jgraph/graph/ParentMap.classV[WU\2 iҋ4$XXT[- KtCLlAş}t)Ad.}OĽg&T>{}/~LJ×bRpYC "k h|㪂a ox#U0P-awd-6'Xຆణ,&Yè-2ZXpCi^UXΪ*S` HpW@v03E(Xm7ԢkR ]t!3g:Ykf,?ð`̜/:n@rP@xb|dM[{șe3l\K Q pj˸YGTsf&H̙'/mᢽly8zîU0|Z`E+~&L.Pؾ,+q-iOG^hR`Itfq >p霥yՑ9:>DN *Ht:>FAG0OvxWq>1t!0ByjFViHHV!M6jnn"0RUR#о<,֏A/^ I4ZP5zvPoYقN?DXcu=BrTfZ< S_b(;>NaCBC pؐպEC2ɘ| gՋWWA<UB:B>tC2"^U(*5oч&xd/og;HſGx-7)=pxvr ҏJp+6p$tO$Cn@_VpA_g/ $V>~%:i4^1ֽ{c?%NtA(ےglH@NӒ!z;`;%)2=t}d}M9wr{ljCuN6"Q4 ('˔xϒ&%t6v1wNoNBCPK gx8;O4org/jgraph/graph/Port.class;o>=v^v>v~FԔbF1 MIJDҒ}ϒԢĒ"kFĔWFQ ļt}k(FT"Ǽ"FIEYE2 h,g1Bea@[J12hRw霚`ead`a`e`l `Js2pinJPK gx8; #org/jgraph/graph/PortRenderer.classuUWU-l]('Ek JAV!)Ahs!d7 QsZ=M/}8I`1l̽7۹3ǟ "tq#qxCdSߔ1 b,Y10ϫlzKaFXgPA):{,JY2VedeB(I8;m;Z dUڎ@ɘr;VVB'L٦Q$핍iEaW\pD1ʖܰe pKh/P$);+$t;'p %,WDƠS41ֈ(Ζn0+$FVw)ȎFSc>9nLV G:vA8TZ37iwʍNf׈ccAH*!G $B,ek*n㒊gpIBOFXXa[E'TK7!|v傡ZTgX2ȦWӸYUXl`SEW%lɸbxC2>S9PqŗJ׸>GܣvqUB!իJ3,(I)_sfN Ǡ6vQKy˹[s^QZp߆ #&w&?qQ\4nd7'm HA~ MeZ.e)kdY&ix兑ӫQV Ӽsaɛ"|^=iNQK} mx@o0n" XD7kZ鄧!q{F<s$w4ѬBRAX"'!j~i  %`PKT_qLjej-e2ڵ#um Y#=B:+tCvНhnxnUpr Nٱ N?ƙ'C5_OY}SP[lޒ%]0ba S̵z{סC/(s-suaه1 ] ^ a`׃FPK gx8;8Up org/jgraph/graph/PortView.classVsem5ri4i T))H*zm[,&[ xWƃ\MAf8x&-$&3w߹Ͽ؂+>"Y##p؇#8`DsL ?08'6REǗx'*5&{VKb,b;ipA 09a (xM<'AJJ|F+h SF!3S1s)!rY=M&$rjQɎ)FBwy-1{HMB{85Uc95Nf7e5.KX]ѺLіsf,3Q2Krڶah=+Y^vpPgrp #oj MN)# xeujVRUZ{qʘeimY lX1 ,?_ЬSir8J؆'cn< P)0 SY u=lNrBڵ0 >!u7[x[;OaP]'>_W3aw%|$ |܂n>>s+g-{̷'74Y4 j,79շ=8TVݜ8u Jx~V= ǐiYS+\5a ƺU$_fƣN ag̚z.qSE=]RNur׋`#Gԭ nJ:g)ghzvBsLi}F[ {Lzӥe-d%hRtz]6C6f籬NO^az2 u?=]%!|&H-nS a+{5#4b~zc*zȏSD?At4h8'靬JA9@w2(߃g<xKߕ(_@ M!wY;g[ 26kED$UK{ 4XlO l>"Ew)긛q9^|=HE]oᑁvqA%/aͪ6A^&-!" v,c ..[$4c%M[yi}tQ ݇C }w^F\P6\} S!B~͕5k_.J8tT-Hr`ZBWPx+2^1}o, YhŏcFvD03!J!.̯)'iBupE tPu}VAw[(<juq`ٸWm4wZLM{4ͷo<;cO1-L_PK gx8;Vȳv %org/jgraph/graph/VertexRenderer.classXe˰O e7H"IAc]ИPB`veagDDv~ ٮyyYe}_Ws8cx3^c<>#x(|>O28%/e/f0,1c1$ʓZC7OƈWܳ[DcFZޓRэbYeaS$(.rMiU &4yDc2qiX@J,jD 5bӅcjtL_JX=Jdh˽@9!*UQʢJblUc3,aw'̪*ftxxY UPJbk"a# J^ YӜՁ8tʉUØYḩPf*Xׯ긒P~Y&c,*xi-6 Ք9AUҮZHbO&/E~c^;h r,)JGUC7 po GVh8@uEc3Ӷ%5d` $}JNIww6IGG6I[[6 m:w@m,g ,Up3"`?372!ACQ6Ҧq U{t5S$S\+pxnx:$Ā:AFlx)&^^[< ܊-[M`;av ܆]b=L2>8 ` R ao^!JJդPLx)ƈ@GF9y\ aS5 '\~>t+>f#Pq1p7܅vquiFtrτ n2'V@J%N \gpӻ~r (&< fc v A,S[W!b1ۚ+qy މ&vJu|:)i1>M穱xѰLQRo''j#}rgKze?ytzme2kD0{…]ے;oknCzTYطVEZ*Qe<0VH#j*uTS1@g]TxqXM*ڤ2[aC:[*ɥjF7-ITAwi$/KoJ5@2dh&XvVN{yԺA?HjKmAWh^t9%K冷?7!_;de2PFvixvjK!uNwb%5)oyq¼ӭi4e$iAS1κ8,JGlޗz\Nde<ğe,]VP",_& G)7@fQ:Rܵ2k W3.#'_Shq;pB7ǎ۔J-ƌQЌ_F%c.㓈αdzo,Qd}=-c~)|=pX:LySzGM9oTմfה=VQ1Mwuo;.n@g3^CV*r$FHufriM[1ikVE/׌Q*mM~.3첟 /!G&M94aLԀbCg06qƓ61MlL9 Fd&/ ҨAs`E 'm\ IҼý7Xopy$ …p;!\La_Y;XA)W&PySPOBeS?I%h<)Ul_e>9V`{AiN=ER0kg!?H>xq= vqVYINg8jЇm2 uf`<Ԓ& cG)~Ihd|Fd) g1>{ 8wΛ D.^\9'jfIl6u I\Ar&&w>y~#mMB 9hK+}}Ӛ/M 9]"$BYܕOm:H ۸Ia>T㰝0z oRI݄Z1p'`Gu!ڑ`,,v'a00j-)LAgmAJ&W]Nu \F;]$\V@aRyJ +(u&w8ftyHbtEPUz8T|YDnDC%DȾhhodFB兡 ¡2PBվj_y4ƗWՄ D}|PCt\M*5=@ R+ 7k*fV jN4G}Tq֤ UA=b- /Uf Iw8D+E.Eh"\XWqu aO=pÅ/h6PvQnVn`6a[TB ÖZGQ[\JuQnc A/ibPK gx8;X0{u,org/jgraph/graph/VertexView$SizeHandle.classx=k|e;ÉDLA@ 8aDIΠLYiA[b%)% {ޅlRD}?y *c;]<^<|@ '<<iuS<b*8Eޞ# U!T|}\OP yO)x}Y>g//KL |Eku|wTh2{ ?2|~?Og*~TX 0WyGO Y_I1Y|/&oaJdE-"A+p76MVE<<^BEV0JT7fUIclV8<3܂ޞtgjݗlWpX,nhHaZNmdWZ&Xd[ -.yFGxx xcaIqֽ'§>(W RQM5S5{vRDS1/jZty!w͚B;CO\^iv}jJ ATLq"z1R!;QB'9.C% KT*0ەѼ7AVAdS\{؍dzolYc@2;SAjI67ť[s9Ib`J$)? foD[dɝۊɦtd5%Ll<dzt֔Cɐi4do%*dk57ۚf{M^N%ɋ[7[Df[hKǢT)|>*& E&Ij]a*{.Cw'jh{L-+oВR´`)RX3}"X9dڱ+qV2lmev3*fu^)%6?s2)d&4oM'4*,[,XXI\*͘d? eA6Zx7Y|H98pB4ck5Q s-J5L^_VT峇D$k# z&T0-F˃%m\ټmm֎.ʺDZ3G82K*yK&-("mq뮹_C޻K'ߩm7vʇ4[m_z /*dF/y*7Sl]?JTj2Kz@PK gx8;i ! !org/jgraph/graph/VertexView.classV{SU- dY覶TH(+ }i}m4ٔL?Bۖ8C9M<gdνϹݿwI9,0r*⼻- ,2m>.1xq2XVW 7kGOtBgLw 22o)ot#6n31X񩌼znK\ ^.Ţ(J𛢐0% 3]ɚh. K˸֬Q*dH;ѣ c5$Җ^9G,V2IF߬l}BΚ ,Kƌ }2T'Dj3WZ1պ0o%+o [XVO7ӡa+8j"FR%@s1/HʽFČ E__eYQ1 3\UMUĚ)ҔՌ I. 3* weJ r>FD:opW0b)DDE0QTaCP R9d|K|%kF~Cؠ{8jo䘊i|bz*bBtSTiSPY=NXm.9LJk^3WoXʜ+rEI cdU//' uyacx~F` diNWkg)F)&}94>ާ94G2}{ǸÂv]&O#y =':!Ȼ?.-fv4t&wѵ_g迋mȚ7%|W'H4%DO'ͧ6v{=vwtes n 񜝍RݟG>>3PK gx8;sorg/jgraph/plaf/GraphUI.class]o0_ne]l| )Dm(tm$^*[+$.(ıSڢ ?rr|9T]RIa.h3T: vE2!Uγ3@I8J6,V|,Ť/:z*I^sRԼP0앙9{g^B( feoYy*}j>>7{xDa.NEBx[|]FГcԊF@Sr>E Va Ò:ֈD4v?=+Sted.OJ3]3}eDni,qG:ٙ0rz$4/G3ϳ[g rGny 6gJR~Zܿ>Ös 5V|q]~e7r uvkDF ^a"jK{6칂U54=קl6=o]}q]PK gx8;G8org/jgraph/plaf/basic/BasicGraphDropTargetListener.classW[-ZXV!$嬂#]4EKS8H)z-ǢeGI;S#'JĖN{/F̼+4)|{3ơ<ǟp&TP:I[Ay.}y 1b #d*ިMx3{m*ޮ!w❌KŻke4 cp->x@Ãh>ʻg!s '5| fg9}:w -DgmôMX۰H14yb(O԰VwUR;פs.)؞eOٞrNzrXʤm#yҮcYFVD a6fx>#mxpg4Y,@l&cgp{JƼ~NDC71ǝ42 Z*,<"YBU]n[~ߺ$S2'XDngLrFs323웙=s:CRӖ!|N+1@[uGJ @o[:y`4p3+&4uq\m'<qɉ+rw⪎3xJGU<KҢEΒI.uzݵJkLau ]IgN +l>~CHǏt 9eW?T\k *qC:nį8 #,ZoTV{ы?21"Ÿu:^QWu/>7a6yw^%ƒ3ɣ e-=cRPr5yrtNcvT-M  ɻi;ּoeMkLjip[m퉫ЏCʹibfv$K:y;CXMܦC9);rDck A#jof03`:D*n5ec689c"o%ZseNB*e$6I 9YB|ZeXXyC>=ͱCX4ySZXUN3;JF 5!x yxXʕ/WzJ3R4^im SuF"T,z&ި-`e_gm(Q*GDӺ⨺|~ £G(Z/& P{ϣ~uW_~GoOaGWp0WGCc͌EX< -51gmx1~[f6|6C^?Sk'q5S[PONmm_¶aN}_At1zKDӎv^EjFMOTPM[E k; 9hz~ B8UW]&/[ІvgG+P˯pgV.xz&\ŭpZ: $v[L hяs h<dFh:sO,cArvޑ ^@qZB j*spQJg +W$fֳp }Kb Mӳ2ք-4MGºRvg.?JPY Ʀ]I Eɵar|vG^WBk5[riIPP.3lL3YQg% %EA[vC>qlG'8c'>eأ:n;ﶡ`*S03|~Vu]KPǮb)v/凮gY+2lYvpG,;Hqk KRW*s|<!tat<}n)둜[(r1ٜ)G9yl%X",$!Ḧ12tIH9tEu1![È018Vƴk#jCWpL78[|'ȽnY):}7rO>E IU1gMJHpzt1#PZ^&}:6zXZ89k1]j:"_ %Y2d;1htk /707z&%f81;Y;˰:YW丑u^3Dqj x#=.vVvFʼԒdvNFtĂ 4d}' T1dd``dK-rI,.N-fdJ,KIKOJM.adP"l$&`b`@gd`8PK gx8;Ǹ:org/jgraph/plaf/basic/BasicGraphUI$CellEditorHandler.classRj1=8ڞ\7iGi0v!ꢻ.:&`$  3}U"Џ*rEJё~pu,a*u*M?PK gx8;1 9org/jgraph/plaf/basic/BasicGraphUI$ComponentHandler.classTRPBn(Xh7ܠ*"E\+KI.qF8'PIS7 ̜;͗o>8Xd ☁lƎ8|=:hYXo% .b@%1Wu ihVμ5tĢxfOwƞdp@CkPJeHŮ?,Fu mRN>!]{nYZG"pJaF9lfrJ{quEÑ8MiH漲԰W=Wj[e (E[-jWVuGc =&* 2 EтLFAH H$/ 5ӯ}!n;DafeW:(bolb&|hAb VX6ӽ)DVF =D+Xh^a3F_Yc :5$d,pFeGۃ>S &È-bKݟaN.`km*ڳzl|}G#Q.-:wqLJDq5ƩIk~PK gx8;uTAorg/jgraph/plaf/basic/BasicGraphUI$GraphCancelEditingAction.classRN1=L>x4!HU*DQEŢj9M4cA)|6H<$,G^OB|*އx&Q Lƌ3>^x0l;:-1I[MHl:Umvq?i:4mcmڵ znS*=Y6 (wk{g篯L`MU܈!nbR FcHcL Y#IR &Sp!M75ɐ*NI*9Z֫^Yԗ\ + ,wδLoa4?7 mN]-ayhq ]|_wL>g;zwygV.ru/l33^Z<02ǮNIx+[;SA.WVIЁN4<#5`VhC=+ 5,aAò hXS ב0$Ll|8xt=a^\prQ57coVJpsSWQ& ImS3M87]f[&U֋0x&ݯeMԕYF:~D4hzw2PNch )yzqz 4:eÑd%Mf=L>*^6j+2"ž Bq %47~:sԊF>BiHx>p}6%qF,>z&<O1ZdOՠMm5 8e.oPK gx8;g'18org/jgraph/plaf/basic/BasicGraphUI$GraphEditAction.classSn@=56&P(}ە*B Q@E@3uvZ|6H%H,ذwEʢxq9wΝ?xm.8Xt),6{.cń%l3^Oy~\?M1DLpo9IDgI Ořb~$w#j+J9$8/Nda ĹLf7KJیKv&:aW6<\au=4ɰYq];pÖ#?lT jgcsW^݉v-ipBn%eM5kbVi2AwЋV7HJtQ-ӡcDCM.t,⎆:pO},ۥIJÓ"v|@f#eyld!s&3=CB4*s h ?gi ʟ. x]8R)0\Nvt7N֛Na¼(lYO*DVA&tCݤsNW%N0dUX1ԯJmCĖux{89~Iv1RS ރv%k"y(@"[3$j= 1JThԙ3P/?yp+VըTf鸆]WnjX- 9 1fcS cQ56 QuKTu%㖢 juxqj3Kd{ôMoa<}*C$%ÐYNˉRMj®2\ɧ.%w#s M 9.ǧ(hX⸇eL Gkx/8C5'g((hsx~$ǎS"vJ/DRo`ԼapBo_۴~!FѷI$9D>6BZtwp!a}@5:B" K]\Lx6/zhIJp Z g~_303k]nQĀUX =3pz.~LF)m-#n!^ö[(B_23dlג1rvU8J^J[+~*}?!yO܉PK gx8;R8 :org/jgraph/plaf/basic/BasicGraphUI$GraphModelHandler.classVWW=B;*V%b@ J+~X@ jX됼$L~ us$.XsmI0@iw~o ߅!3G7zBP=WiG819݄M6oNnvoh$I'Rԙ%#E]qLǟog<muz\6z(}eC2i,\I:u].m4\[˭) /-éuڟ,[ۦbԿ3=۠~5Ed;Y-d33XEE~  V ehya[5еl)Zͅy|=\[e{ ]<Ž~9aâeci#Iԁ쨸I'xUatdPБSְ.iaaS}org/jgraph/plaf/basic/BasicGraphUI$GraphSelectionHandler.classUwUnHM -Mk*Ik(M &-Mnݸٔo 3Lqǧ>Ǚl&b[0wfL6 8aF"$"H!& =i" 懬h,X\10a"k4\pCgr^lLN LoT]^Q׍YY^g@j3K{ٴM'SVr˰*S3* $Ֆ=UP*yc/2HL /jXXA^MqKwPpWbk_ė+@D %  cЍ^UΦ~%K]\pp5 û]7$6Y,K| W&y{4%.[YVn2 8h9}e}4C39H;trM2HvpĦY/FL("x^w\ff1+j8ٻ97(`%Nyit?j%rnBUʦa@%TqJM"p/_T %*kִ"^bikB%˱i#]JST\gۿ!\^TmstGgi]󱿛 A򮶍XYj~YѮi,=JsU0yzl(/4sS j\?QYƏ#3x/O L|QtFɩxF >F T9-j!Oԋ9:Ge Xc@g}\pߵL;-[xm6qł.~K0F''H*P4T{Rbuncl{y)87XU!>RU󉆳 rQta\Y'q:Y_(yp:q1$Ԝ.i*C#RxWD3ma"n wʹFc1]]fN3dS5Vh֌f],K5^Rş_2qM1̾ yd!1iW$*g麲p$K5Ěg R8 $윬KA` qBCc4r|9yi:8n`AM[Xp|R9x G9H UXM56CR(m& V9(2_ *uPR^,Le8ܙhz :Lۘ)bN9gUN^?ΰ?Z+~Jd w.\E\MYja*]I[JuFC@DZdWeS:4`B{Up([(_%Ň# ~ 93~O}C|ٷEbmfBG jFjG~6oaXubV?Kq(|sM ldg0G]x$^PY>MNPK gx8;<ؕ4N5org/jgraph/plaf/basic/BasicGraphUI$MouseHandler.classXef6lKڒl4biƤ&$*0L6S6;lJ@"J=͖x$A>@'ymb;sxgg.ÿ#؆kp FyoM9[reDpٷjZd`0/ˎnbN3yy9xK`ŝxKͼvyexwXz>ȶC;1&?L!cۏQ&?1>^OTF+)9 3?2d]$دkJP&xZ3%RxZͤ<%mʚڴnDδ ҪsveR=[mJCeDj^{ү>'5[OzFI4jӹƸ&AL,m,K!VDMk;>uܓSOݱfkFrИfbCҚ`j=dI8Lܤ WիYI-3fl [bZA>&Џ[`'v $Ы _ă $+2 ~+8!%@Ҍ>G0mF`RoĞLF3ic |  |~|C8)-6v(8%0i<"Q<&8/O|O |~(pb-~<#0?f?Bl{5~#[Nz = 0̻8 G'6 _Uo?𢂗%mNw3(&˵q5kt-[˰Raմ~ꔧ*M] u+ad72S뷞<Ž\BCi;b%LtkgaQ aj4e$s)1(j&S6 nttz42aSISh*s]lm Mj}eǍZYr~iN*A_@'٫ngoTIc*l:3)n2̤6T *ߪonF.k#uk@S':$쉴5aYѬVc!$wn*Wا^k}cQߒȕֈwwjAcr6W\e& )X.Y KލBH^u4tuK_>rlմV+̖#<5gn0:Eqm8r\nS4^Rc5cK㐓Ac\it \6j8aICKm@\5JkwN4Z|g@=b섚\ %2x^%xA#5u*Y?* +66p-~N_2qng=@Ǜh C\qRGBN:](jQMA˝gBSBuJ>9 VkcMt~OcezkJy;զyU!{2!Z=.sB-~ {< sAM v@L!6 d*VMʬ@ZƑixq`)p^ LU+;F.a W:YբcW7G5D1 :ЉH4< O bZ3hx=9 i3`A`Ƽ.YzeXقt" rڒeI']0h.Wrͼ(twפd>˦ō:3jgdj|%Uw+*mf=#'a^]]SfT&2騚>QugI]Y-*?2ZjܪlogKyC"S˞2"$pf`ْd!S*F((EzD">$-o "xK_VmFQ~Be$/NIa3ITK׏KIўڪLԒ}F5p!1)n /OP$s#tmH >]B0>oPK gx8;QeW, >org/jgraph/plaf/basic/BasicGraphUI$PropertyChangeHandler.classVsEud3!ɐpD0\$D& DŋN'0;&$}x%UZeăV luO&Cݿ}$'pT Rګ#;/A::򀆻KG)L[CqT&= ܋1)ݯjx(xX /rc:t qB##_GV'u2.ipJÂh 714w`jx0١T~:T\IΝ Uc^4$"'Mωv3O^KpKCeo ϊ k )&O C%֎Em`; ܂[5'tT.]$D)Ӑ|bW?]=P#QXLZͽ:<6U.{AZ'?ٝ.R9{6LV.O`+ivuJ6m}9FG(< EP7a3''V>]g^$I⑈gVjD*z/=;c5؊`Tn i;vкvi_ADw`."؇EcCZ/*/i72EYT RENP]g-FY, HY@]f,m r8oDJtK/O.˳<3,sJ5.W%yfTdie%9Yf[ Jd따2+2Jh%PW*w4pp27LGʃ.q RE*41e7]W\0BIWm/PK gx8;oum73org/jgraph/plaf/basic/BasicGraphUI$RootHandle.classY |T?fd’d"N&hA L&1̼Lhbu)N\K(V.{ֵVkmն}o$>~s=}qN31SJ.*'Y1S 抓17@%,TW bZvAg34]MgE=]p1֖sR9r`M+\u w8|߂VXUHEfV; ąbC뀺`"-c%] /sMan3z~@Wǰۻۂ^!.[q~s1[GQp0jvw%zW| j]Owm)'1)]~:z7{=>Ysyq]9č&ڛ| j틘oǕ;uwѻ'~ ;@xxb2n_æ} v~9 nvq!!+`M ohxB<VP귰|a A!~ <q BzěX%.Bx.~ >s[H<]1xAvGxU˺#1ڠQ}$sTQ_cmdƫ1AD]5wE}=[j ؠBؼH揄Z'M h?74m\2]HT0{p j|5]z]}ᮐ L/HXPՑ`8p[|@Hg3Ƹy Q1D\%qS z:|[taSm皓L)?kl Y` rRƉ!)oA3Gc - Rډ7cs(ߺǢhTGƘpE*,k,m-ZAsq{BΚ;X_P :v[x{φZ(OpʵH6MyT/2b[i|9_cQ嘦` ) rWPCeZs6=9۱,|}+M}^c1t8ˊHT+tuUҚpL(@3k6nq|E'2;TQ[Qʓn~"Cũ`Yxv̛ӽ97*pM@}D3>20SQ۲Ǖfcf}+3G}P0\ӯ6Q]ɗ SF,.7`8ۍRJ>Dg5ɲ͐?"J|rf~dE29ƶ/`;_ު>+z}K 1e#uտг5Uf2`*GzgTM4~$z92A+>86ąYW=Ff DeBzv=R7Xp`X$=N4L Aw)[ijU|Odtr,I;FgV,@ݕ'>wM>X|3 }}@B n!"sYj9 h ]V-P4m!`tYvI> @b(GxZ4j|A23~5M-D^[v7#25.)toUCג*̈́dnFqL,4 3Zy"\Pc l;hu".kIv}j]#~""۞~*ŀ$] ˾o uG߂96i5ڜfЦuqz*J6$Rz-FMcckϭbjtb`{'iG?YxgV.9HHnkSAj&Nϛf2;h1ք4gs0ZZW/["&MFRI5(UC7TpKC 8p|BҀe8Ґ9n *Jk6>Ky]}{.^{|+i F$zȬqOӜ҂{ Nm;;fYv],`IڝQ68uS'f0.%&_U ΉNSKg]\m;*Z?DMf%j|(>t55N~sFJ8)USNsUsY\eI|f{溬 }I1I7R#f~N18 ɕMB]F 頪]kX-˫+/-wq\r/TByn}La8L9hĔ!-"祥 rtTu. u QQ"„2 )4hTL=/;6:wos::JxFkw:3SsˏRtAaJOUݙV8*d g#O pIs>gEsF~HuT:Es,untxxV:*JD5K"ng2i_5#PqsqZ~'ۜz]o+F4oS7ʴiCAeRjuIT'bŮzѫuZҙ\T>S8Ɓmioz M[ji.\*dߛ'cmZ[j#;m|<ͷʤ[?ʔSUCOrGaKRpd=!Q1v_n0q 1_FAQUO2/4Մ2Lblu4*L'̙NڭiJV]w{Q” }A2Zk8LO2"$2i6E{ԷPQ.|!*^Cp\ʅ\$R?#_\tՔKMR9rl dم[JW!q k\z78irSIA7foAp+ۜVd;smt;݁_wcNM+q'9VՔ]X AЇ^!Xk f/j~>MyԮM$ە5u܍'tb:Ivc:I0$aN9^}<L†&RЂZAJUZSY:RuS=DnW zӝשWk4uV?fcG!8ZSks*uj:XڀLE$MHߌj"'R=1u)e[u<`Jĝ_!DЏzxd̮[ 3W!h?V[ k =z ~sABȹ4eځeM?Qb:p=ـ_s"r߄!+t7(z}6:$8ש ЈCyX|tktz!?"EI?k_&{EK*O=rMIg+clA*j ^uz݈&Սuo) J V‚mێ F z$T{A.Xz>PS﷫8=^M}ܦQGtڏ`fǜ4>O8i>iT3gsnP_p//jԗ Uz} zˮ ˎl|);4@S?ԏ(! ?2'FQJ΋x;EK61pgZ+KA/V bp] ABmFEYõdGbf2%ʎo23sAV8b\IB+&^ѻ)M,%MV wC lE7ԯɖY񘅋hjEK92_\X{ƆE,#_DMВiS h- E 1i>@i~[Z')L(`&gjqY$ 5FDc9 !TWKt0#"o{:Jr`7Ԁ~zo0k]Zl3NjYd-dIYjA -^B9}75 iO8X~,R⠯ :aby0cse))l0˪>CrDk@ДoA Y Zh b'[p0{ y;(H /3^ZՋ, q'jJH_V7%CuM &Jg7Qe\z94P01Q5'XA:O Y O0A T.I YXXcWZk #'G(#*eEd:5իq ąY0!eB;l V'*V"o̿ \).H̻.Y9\GBlJҲ;I73 :]`P*&{m SfQ`[EEKҲxA@].@SZ}}<ٲ%9 o Fe \0LK _[# rjFkb2!A?<J,e_Jlh0}׷- ¢ihUy 1$GҶ |R={ @Ǧ7#fQ84J<uvd1BA'ACXhA0 FCŤFN!"cycIJ%͊~H#L>:># 6.Xswc1ؗ~Sr%"zr#< d x@6uZDap ט=J %X)4rُm:~:}^+:}w:Ji/ݥW諚]XDW?4b;]:}O>zN_/t7GSկ|:Kot[~GS(WS?+,ޯ3¨j̆|QYtf0'ՙN? ̴F f:+@?`:G'A;P? /V\:`(+љ06\g#krFl4 =G0&ꊊ {tn$Mv)uV L:}>aA";AnHFCǘ=c:uL,<&yxU ;Y3 ta庒T !(FFP*A{:uZ*uΪXN?_l |²h90Fji:;M١lcBiր#DL0  DApl۫F|Xtv$zWUm lDUW'8 JF9kU:;F+7:tţ묞ͥ5Ooѷ56_g l%y;c`p2ci2B':;5(eNO묉5;rMZg "]3YJR+RhlΖe2T_V w#Z :&2>%QXx0|@v[^t+v"{*_U{[#H G"7[͍ Wl2s(hAGq"L@ ;h8W ҕKf#(5PY܃ Ok,/Q aڥ2{# tTw7 aP+c}$rW=7~|)8Rfjqx@e'8~E+́t$te .bSLCPsyb teuwZYzqhbi8 &sdI,=(,b0.ϰCd`hl΂eD*uNY~ 3=HgQ 0bSg]9L9\WUfȁm^BM6 +~ueΤdLF'ԅ;W.&G|,qouv;WW@!2d(y:; t{G]Df'Y]+3+Xp y)LW@Fv9 uJ2Kg[ؕ:]koXp2[gb:]9Jٍ&ݬnnŠ\S!;J g Ɖ_x6(&jZ\X]xC0yp8.'>,Y 3&ot%u,fhmP6 D1d%@[$Z4< !E[rj3͖Zْ[74' -_$YtjٖM/RqsYi&ofVǘiP4tԟߺ#(HQ-KSJ< K %'76Jv%/j֑|󷦠uIS/0Wg̷^Q ԛxxdZ$,,LDalBi3l&iZI6ĨzrJX>Q5V8M_ALFICs^9|̘OiV Ȧ2@^(N )?KK9֎Y_(j EoA&fVSd.#lJiƝM,s."7>G]ae,yi:EJW (6[*8.tN0aą)ωa84KCC#8~?fվ4qߟ5_BkAI P?eM$ܾD𤪙1 S*y5$#P,P x8Cw:`5|l}ɵKjj44l0h[TÃt,m\V}fkP$`dcEeZ*[ܹ *$Kt75 Oށ`4gQ=|5s4ģ'F`244p/[5rJh sD&IR )ͶqM!gi3MmeGLLE&>/a(M^lnfQ c8Jy=I*>s˱C@y|$MԤ. CK?`45Ġ9i&SMfs#vI](E4hew%e&ݲͲ&*8%; osj}aj|".^C|v@=HH!q9SI4h$P`A]OnQN!#~ԙ-V(a?c:7Ğ# XqYjűF8v򓈎VfyhM<[:7$wzg;`|ȯ>ɑ1Xkڕh<78j~ߪnho1pkÝAyh'ђ7/cm4n%AHȦ ߁#;>JXrhkMņB?\l3ä%TF YF_d,'7;aFjpQ8W-^bX~̕L5'WHɗ8I@~]&N '裖w-#Ae]ʍ]kڑC]#:|}Zr fXBEL&7Bx}ӛ<[dz+O t+wһxnΏK H]$Ӈ#<|Gyq>ӧ<}><}ӗ<}<}ӷ<} B{!WLB!Km?ϠWRJ^z<8!v^NY`kN'$d($z?~&)9<(E48hclhl.~₤a?VZS^(`bs,?I/Nj%1DtcbvCDPAT^»{/e[0 86fӿ-V)WkYRj3ͽdJB%XwQ=P#,/"*aj O`$h(6p¶{*dzqG=𔏜CUi/tF7dn>mpџes $.P{ʑ'(, ˡp$xDL) >OR&jGnŊY\/g"(d*-ďC9W ~["8fjTMPaHvNCe0v.IdSr$[Xvoi&EQd.%tUy4%7\,9=*SPSɕ#ί@ ' + R]u@ sה#ȱ-OC-?Szb-R- yD6(ȿww~0QJn m\"CrZc CH a"`wr.0\f=$EI'H$,]C 1duh}c ,ޛziiqkfe9Cqr&dD'd/9Ǒ{9qr.Kp9/NGzCrp; p2vAN+6|XF]BjdUE䏻El1Q&fpv- 7"CmEfr@x`m,/\a^rY\^^xt-\1Z&*\19Wk$o&Z\1Ls)7nKn[m9b@lU%bnsP&)~1iw' Ben3PFJ d#іw$љ$w#Nd26I8e|&I/2H&(3Iv#=$er&Ik*e}|AQ3f_<+w=n)m=m=n*c=u=JTϓ }=H4hu\LSic)?D.ݏ&2ީaow4Mn^^`߬rnu;o ^}my/N>زrPULJ+p##aG\[17lݠ.CRy Ôeѳ a >"C}| W^y|ьmž/q' _ _77+)ɉhx$ߛ$?l!N7sGd_{ fNCȁ8?qfusJ)V4DZmbMJ6.>&\; E(dSW%`;+7@n|;9XpF6ON1q> 3U78Ml!CAq+AF{6W4x\ i1siՠǣ7 ˈ& 9F#ٲ9PAi8U# Mc@5n39wrHw斓[_r.P}[&{ ROy1̕uq:Fy;$S F#X+80QKcE' jl8sә6L>*LG_վŴpTܺ]x[ɠ.*n%s%ZRYAInRYadh/msdP1v5Dǔ*\65%Ů󯸚 Ο9drxu!Һ=nd록mn{q`dACMB Y/c03qnIv#r5NW4{*w)ڨzN#cYi :,mBhrh&5ux_ZxC0W\yu86#ly{"'mӓ8O.71@_~^`gSp)>.Mj5t1śR 5dL}m۰"d 8]Ï@+Ҷ.2*/E)|$t.^p ѐ8+u;Whԭ0(1CT35X1l]f{hƭqz*:4EscrmEc͕yc'%"<8@iiJ1 ʯ@`n쬠 t^!N76a\ځf(y;\8o䷭4TѺy=ɫluPJ. +V\ٝbq06=Эs g{r͐8?YY᫇nW7j⥄sw+Iarָj^_x7=nzz~.DG/"'qD6bW%(! K!ʷCz 7[}Jt7WA Ր>5oDk!. :A=Su 9PK gx8;ydorg/jgraph/util/Bezier.classSMo@}Y&PB)NF= Y|T )D%KrҔRH8q^z@%D~bIBXgfyz_I# 4Υ1SKR?i$pQbZ@Β'[ۑ׍_g\[ h[>^z׏910xC umG&HnFG>mnD\w@ҲFjw>awEAc NK\‚ED#+f ͠=o9;Q{˹j=BhΣM}0O}>,$& 4q^=Y[ͦN)UEM<<>1{OѫB4nUS{zUh}Ggh -3q\zgF>NWR弾:$S擴@[ %ʩ¤:dYK\SȨIV2 SE-0V9wd IN7$VKS0S4 )VcEk⛼?y&``w.qC hpJGY6z Rs PK gx8;D] s(org/jgraph/util/ParallelEdgeRouter.classWyxu%%ő¾` !:4EJJ; &$PQ+Z#IBhKK)/ ބ:2)8-=.Eޥ|_ݙݕ|HNW19~޼yGsqx ܇ux~pLvn&Y&{d4ĂL~ 01J痼pqo\>VʏL7zZ]濉ɛEƵ~۽k^jw?n;kr7]L{^vI~/<~0,7 o >6?n!w˽k[]`qV}S~ړ)hûFlkVr9=!ѷ#^XEv:aY#/A^f#\֭=F290XW$lMFRU*l5[ڿGRg*{of6OgWc|c4b5(բ=;R9-ppӎ-wO))d:WЯ(aTD ܍Qg &d)|Zg|(8{} .v1(J2܏c26=e<P% aLN27MOyLZ2y Kh32k͛ݽ-`cf J:[b@ҤbG61vօP4jlp& Jtw1{nUuQ-fjdHjb5cg}y-aa"%UZ7X4 Z,e99gSE=0+6^}` S9so2ZS@EnoںMIʭaaV.^Q0YO.I9z t;8s ܖM]ba9;j_ش@R5:J$ո>.=XBޅ&C5Dy4J4zڎBr.taK5n\dK]O4>z Gᾠc 7鑄nh.'ݒz|#ngBB_+q됔Nۜǡa[[{!Jke X$|@wR a4޹m@|8p!wz Q O XΜ4h4&$8滑 bcEQ5ʜeh+O!N(Vӱ*"0Qwh[!c"DQ^D3 x恂L4ڬ@ A5l$"j(a!iCNODh1 C*hʬE,6 "6UfԦ KԅDylJ%ߓDÇPK-jc /J,gˇ1Y]|ӭXmEB]6emVTZtÙj[J%;NQe yaŕ$KRN+$YՐYgYqm{5&&mhW#h;DɸO"nqFhg!Jx +OC!wrN!i %t>|zg66PWnqՕRg=òrZxix[M㥶`rV$=q-۞\/K֋V*{.rwM >w7#p9sa!qLzG'<"WTlfRTe%$~#&"HW`ƱZ+$.[h(ܛ'mJ| |/[ e"k"P>Eh >`C"X9BvD?,~g?X(m7ڲ2OgdDD6q$CG%"Q-KX+"4)yeD;p)e O&x]fz-scXG1["LrFTτ PK gx8;Xorg/jgraph/util/RectUtils.class}R]O@=۝mu\mXBC"`$B2ȤtvģDQ;*OK{νiocفkup74C[9nqvqgh>O~0užbyR o}AcĪȂMyKZ%=+XxǢC<XBGHŰ@sUT%0WqЏK IjwJ~>I>8:Jm(㑬%0|/z09uf+U2Ske\$ 2&k L/UbӔe;b6LYE9;k 1|O1NAAx"bN\D8 Qm[m޶a h32΍X֧F?Jl^.Er;PK gx8;VForg/jgraph/util/Spline.class}ToU&:ζ^o0khܔ"$Wpn[ޔPN (Q.|3'{v@mI8'%(JdAM:^$,[M] K_P^{R2e² W$ݽbb{[g :> ^wϽi(] 4jnZߒ WD n9-'x1Uuފ\S t`[m'pW|fvՔ߾r/ìœ K‚`*b8 *|U9ԘK%^q:ŭ.l7;X0qm?YpAq+ݮ$fntn}DžSqnN79v RT}b36B}6B 0⯃Ds =g1yf̴!/|辇_ l2dDSz)$Ʈ `5ƃ'a7y 1lUGC0h4F}^b+h7+[ Y\C@}U]ЕN 뭪BJ\ cB&\; r\Ȱ'05".ȃ uqH9Cٴ9n[ŦxŒ/}K/(|K%t85^XdTl^\D*"\ ù3/]j?:>l l؇J:cHrxc}34dE; ܵ \AiQ ( *#FqZ PK gx8;0hY\[org/jgraph/util/Spline2D.classuRKoIn{q2؞q< bE28$\V%$v% X;P=Ӷ9- L0NLgttNů8/o(b 0+E/ 1' .+(20!Lި]drNڨ7Z=fz%pn8nPlBQsgKC+o=r\ntq&s_nؔ$\\.z"!خcQPjWXMLghEOTҋәWgRQ%/jHኂpM ǼV_mǪϵu^y\7Z01jYOfիC[w޶mԔ nDFBbL 71$aa88Eq!CƅX'𳆓aM`ծPhE%H VxO,o'oՒ{VL>@ 6<=.5uNZ."yh9Ɍ2:IEb g)|/=org/jgraph/event/GraphLayoutCacheListener.classPK gx8;55j.7>org/jgraph/event/GraphModelEvent$GraphModelChange.classPK gx8;5䰂&?@org/jgraph/event/GraphModelEvent.classPK gx8; Zچ)Aorg/jgraph/event/GraphModelListener.classPK gx8; {2lp*VBorg/jgraph/event/GraphSelectionEvent.classPK gx8; - Eorg/jgraph/event/GraphSelectionListener.classPK gx8;8/;G 'Eorg/jgraph/graph/AbstractCellView.classPK gx8;c+%@Sorg/jgraph/graph/AttributeMap$1.classPK gx8;3$SO7>Torg/jgraph/graph/AttributeMap$SerializablePoint2D.classPK gx8;㹥F;Vorg/jgraph/graph/AttributeMap$SerializableRectangle2D.classPK gx8;gn #Yorg/jgraph/graph/AttributeMap.classPK gx8; y*dorg/jgraph/graph/BasicMarqueeHandler.classPK gx8;GC|!lorg/jgraph/graph/CellHandle.classPK gx8;b!morg/jgraph/graph/CellMapper.classPK gx8;:b]norg/jgraph/graph/CellView.classPK gx8;W&+x&porg/jgraph/graph/CellViewFactory.classPK gx8;FY'qorg/jgraph/graph/CellViewRenderer.classPK gx8;F0/Mrorg/jgraph/graph/ConnectionSet$Connection.classPK gx8;G~ $~torg/jgraph/graph/ConnectionSet.classPK gx8;->zorg/jgraph/graph/DefaultCellViewFactory.classPK gx8;R1|org/jgraph/graph/DefaultEdge$DefaultRouting.classPK gx8;nc0.Հorg/jgraph/graph/DefaultEdge$LoopRouting.classPK gx8;MS"org/jgraph/graph/DefaultEdge.classPK gx8;JA/'نorg/jgraph/graph/DefaultGraphCell.classPK gx8;IGWkZ/_org/jgraph/graph/DefaultGraphCellEditor$1.classPK gx8;j>org/jgraph/graph/DefaultGraphCellEditor$DefaultTextField.classPK gx8;I=org/jgraph/graph/DefaultGraphCellEditor$EditorContainer.classPK gx8;8$ -Forg/jgraph/graph/DefaultGraphCellEditor.classPK gx8;mb*org/jgraph/graph/DefaultGraphModel$1.classPK gx8;v!6Şorg/jgraph/graph/DefaultGraphModel$EmptyIterator.classPK gx8;yM 7.org/jgraph/graph/DefaultGraphModel$GraphModelEdit.classPK gx8;  <org/jgraph/graph/DefaultGraphModel$GraphModelLayerEdit.classPK gx8;,n!GB(org/jgraph/graph/DefaultGraphModel.classPK gx8;{Aorg/jgraph/graph/DefaultGraphSelectionModel$CellPlaceHolder.classPK gx8;@4ǰ 1org/jgraph/graph/DefaultGraphSelectionModel.classPK gx8;xfW"org/jgraph/graph/DefaultPort.classPK gx8;^ (Qorg/jgraph/graph/DefaultRealEditor.classPK gx8; ( #org/jgraph/graph/Edge$Routing.classPK gx8;F}6org/jgraph/graph/Edge.classPK gx8;,=#org/jgraph/graph/EdgeRenderer.classPK gx8;2'زa>*org/jgraph/graph/EdgeView$EdgeHandle.classPK gx8;zw;'org/jgraph/graph/EdgeView.classPK gx8;[J9'-org/jgraph/graph/ExecutableChange.classPK gx8;dì .org/jgraph/graph/GraphCell.classPK gx8;J&/org/jgraph/graph/GraphCellEditor.classPK gx8;Ygn:%0org/jgraph/graph/GraphConstants.classPK gx8;tm#]G+#Dorg/jgraph/graph/GraphContext.classPK gx8;&^@);Morg/jgraph/graph/GraphLayoutCache$1.classPK gx8;jC<pOorg/jgraph/graph/GraphLayoutCache$GraphLayoutCacheEdit.classPK gx8;DWS*i' Worg/jgraph/graph/GraphLayoutCache.classPK gx8;4g`!org/jgraph/graph/GraphModel.classPK gx8;M:F*Korg/jgraph/graph/GraphSelectionModel.classPK gx8;[f 2+forg/jgraph/graph/GraphTransferHandler.classPK gx8;m#o (org/jgraph/graph/GraphTransferable.classPK gx8;M8}b 'korg/jgraph/graph/GraphUndoManager.classPK gx8;NM&org/jgraph/graph/ParentMap$Entry.classPK gx8;z org/jgraph/graph/ParentMap.classPK gx8;O4org/jgraph/graph/Port.classPK gx8; #org/jgraph/graph/PortRenderer.classPK gx8;8Up }org/jgraph/graph/PortView.classPK gx8;Vȳv %Eorg/jgraph/graph/VertexRenderer.classPK gx8;X0{u,"org/jgraph/graph/VertexView$SizeHandle.classPK gx8;i ! !org/jgraph/graph/VertexView.classPK gx8;sorg/jgraph/plaf/GraphUI.classPK gx8;G8org/jgraph/plaf/basic/BasicGraphDropTargetListener.classPK gx8;Y,vi2org/jgraph/plaf/basic/BasicGraphTransferable.classPK gx8;ʁ*org/jgraph/plaf/basic/BasicGraphUI$1.classPK gx8;Ǹ:org/jgraph/plaf/basic/BasicGraphUI$CellEditorHandler.classPK gx8;1 9aorg/jgraph/plaf/basic/BasicGraphUI$ComponentHandler.classPK gx8;uTAorg/jgraph/plaf/basic/BasicGraphUI$GraphCancelEditingAction.classPK gx8;(G`@org/jgraph/plaf/basic/BasicGraphUI$GraphDropTargetListener.classPK gx8;g'18org/jgraph/plaf/basic/BasicGraphUI$GraphEditAction.classPK gx8;غ_=.org/jgraph/plaf/basic/BasicGraphUI$GraphIncrementAction.classPK gx8;<أk@org/jgraph/plaf/basic/BasicGraphUI$GraphLayoutCacheHandler.classPK gx8;R8 :org/jgraph/plaf/basic/BasicGraphUI$GraphModelHandler.classPK gx8;bW8$=Borg/jgraph/plaf/basic/BasicGraphUI$GraphSelectAllAction.classPK gx8;גO >org/jgraph/plaf/basic/BasicGraphUI$GraphSelectionHandler.classPK gx8;x|3org/jgraph/plaf/basic/BasicGraphUI$KeyHandler.classPK gx8;<ؕ4N5org/jgraph/plaf/basic/BasicGraphUI$MouseHandler.classPK gx8;}t:k org/jgraph/plaf/basic/BasicGraphUI$MouseInputHandler.classPK gx8;QeW, >org/jgraph/plaf/basic/BasicGraphUI$PropertyChangeHandler.classPK gx8;oum73Forg/jgraph/plaf/basic/BasicGraphUI$RootHandle.classPK gx8;*f( .org/jgraph/plaf/basic/BasicGraphUI.classPK gx8;yd3Yorg/jgraph/util/Bezier.classPK gx8;D] s([org/jgraph/util/ParallelEdgeRouter.classPK gx8;Xeorg/jgraph/util/RectUtils.classPK gx8;VFgorg/jgraph/util/Spline.classPK gx8;0hY\[7lorg/jgraph/util/Spline2D.classPKff"olibjgraph-java-5.12.4.2+dfsg.orig/examples/0000755000175000017500000000000011256667036017626 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/examples/org/0000755000175000017500000000000011256667036020415 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/0000755000175000017500000000000011256667036021670 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/0000755000175000017500000000000011524541167023315 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/LiveJGraphInternalFrame.java0000644000175000017500000000170211256667036030631 0ustar gregoagregoapackage org.jgraph.example; import javax.swing.JTable; import javax.swing.JScrollPane; import org.jgraph.graph.DefaultGraphCell; import java.awt.BorderLayout; import javax.swing.JPanel; import javax.swing.JInternalFrame; public class LiveJGraphInternalFrame extends JInternalFrame { private DefaultGraphCell _graphCell = null; public LiveJGraphInternalFrame() { setResizable(true); setFrameIcon(null); setClosable(true); setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE); } public void create() { JPanel mainPanel = new JPanel(new BorderLayout()); JScrollPane sp = new JScrollPane(new JTable(2, 3)); mainPanel.add(sp, BorderLayout.CENTER); getContentPane().add(mainPanel); pack(); setSize(320, 320); setVisible(true); } public String toString() { return (""); } public void setGraphCell(DefaultGraphCell graphCell) { _graphCell = graphCell; } public DefaultGraphCell getGraphCell() { return (_graphCell); } }libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/HelloWorld.java0000644000175000017500000000722111256667036026243 0ustar gregoagregoa/* * @(#)HelloWorld.java 3.3 23-APR-04 * * Copyright (c) 2001-2004, Gaudenz Alder All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package org.jgraph.example; import java.awt.Color; import java.awt.geom.Rectangle2D; import javax.swing.BorderFactory; import javax.swing.JFrame; import javax.swing.JScrollPane; import org.jgraph.JGraph; import org.jgraph.graph.DefaultEdge; import org.jgraph.graph.DefaultGraphCell; import org.jgraph.graph.DefaultGraphModel; import org.jgraph.graph.GraphConstants; import org.jgraph.graph.GraphModel; public class HelloWorld { public static void main(String[] args) { // Switch off D3D because of Sun XOR painting bug // See http://www.jgraph.com/forum/viewtopic.php?t=4066 System.setProperty("sun.java2d.d3d", "false"); // Construct Model and Graph GraphModel model = new DefaultGraphModel(); JGraph graph = new JGraph(model); // Control-drag should clone selection graph.setCloneable(true); // Enable edit without final RETURN keystroke graph.setInvokesStopCellEditing(true); // When over a cell, jump to its default port (we only have one, anyway) graph.setJumpToDefaultPort(true); // Insert all three cells in one call, so we need an array to store them DefaultGraphCell[] cells = new DefaultGraphCell[3]; // Create Hello Vertex cells[0] = createVertex("Hello", 20, 20, 40, 20, null, false); // Create World Vertex cells[1] = createVertex("World", 140, 140, 40, 20, Color.ORANGE, true); // Create Edge DefaultEdge edge = new DefaultEdge(); // Fetch the ports from the new vertices, and connect them with the edge edge.setSource(cells[0].getChildAt(0)); edge.setTarget(cells[1].getChildAt(0)); cells[2] = edge; // Set Arrow Style for edge int arrow = GraphConstants.ARROW_CLASSIC; GraphConstants.setLineEnd(edge.getAttributes(), arrow); GraphConstants.setEndFill(edge.getAttributes(), true); // Insert the cells via the cache, so they get selected graph.getGraphLayoutCache().insert(cells); // Show in Frame JFrame frame = new JFrame(); frame.getContentPane().add(new JScrollPane(graph)); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } public static DefaultGraphCell createVertex(String name, double x, double y, double w, double h, Color bg, boolean raised) { // Create vertex with the given name DefaultGraphCell cell = new DefaultGraphCell(name); // Set bounds GraphConstants.setBounds(cell.getAttributes(), new Rectangle2D.Double( x, y, w, h)); // Set fill color if (bg != null) { GraphConstants.setGradientColor(cell.getAttributes(), bg); GraphConstants.setOpaque(cell.getAttributes(), true); } // Set raised border if (raised) GraphConstants.setBorder(cell.getAttributes(), BorderFactory .createRaisedBevelBorder()); else // Set black border GraphConstants.setBorderColor(cell.getAttributes(), Color.black); // Add a Floating Port cell.addPort(); return cell; } }libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/CompoundVertexView.java0000644000175000017500000000771311256667036030013 0ustar gregoagregoapackage org.jgraph.example; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Iterator; import org.jgraph.graph.AbstractCellView; import org.jgraph.graph.CellView; import org.jgraph.graph.GraphConstants; import org.jgraph.graph.GraphLayoutCache; import org.jgraph.graph.VertexView; /** * Vertex view that supports visual vertex nesting to show inclusion edges in a * compound graph. Differs from a selection group view as follows: *

      *
    • Bounds of a compound vertex view with child views are not determined * directly by the child views' bounds, but rather are independently settable * like the bounds of a leaf vertex.
    • *
    • Scaling a parent compound vertex view does not scale its child views, * although translating a parent compound vertex view does translate its child * views.
    • *
    * Like any {@link VertexView}, the bounds of a compound vertex view are forced * to always remain large enough to enclose its child views. Otherwise, no * restrictions are placed on the bounds. * * @author J. Pulley */ public class CompoundVertexView extends VertexView { /** * Initializes a new view for a compound vertex. */ public CompoundVertexView() { } /** * Initializes a new view for a compound vertex with the specified model * object. * * @param cell * model object */ public CompoundVertexView(Object cell) { super(cell); } /** * Retrieve this view's bounds from the view's attributes. * * @return view's bounds */ public Rectangle2D getBounds() { return GraphConstants.getBounds(getAllAttributes()); } /** * Set this view's bounds in the view's attributes. If the new bounds do not * completely enclose any child vertices, the new bounds are set to the * {@link Rectangle2D#createUnion(Rectangle2D) union} of the child vertices' * bounds and the argument. * * @param newBounds * new bounds */ public void setBounds(Rectangle2D newBounds) { GraphConstants.setBounds(getAllAttributes(), newBounds); checkChildBounds(); } /** * Update attributes for this view and indicate to the parent this child has * been updated. */ public void update(GraphLayoutCache cache) { super.update(cache); checkChildBounds(); } /** * Translate this view and all child views by dx, dy. * * @param dx * x-axis translation * @param dy * y-axis translation */ public void translate(double dx, double dy) { getAllAttributes().translate(dx, dy); int moveableAxis = GraphConstants.getMoveableAxis(getAllAttributes()); if (moveableAxis == GraphConstants.X_AXIS) { dy = 0; } else if (moveableAxis == GraphConstants.Y_AXIS) { dx = 0; } Iterator it = childViews.iterator(); while (it.hasNext()) { Object view = it.next(); if (view instanceof AbstractCellView) { AbstractCellView child = (AbstractCellView) view; child.translate(dx, dy); } } } /** * Scale this view by sx and sy, relative to * origin. Child views are not scaled. * * @param sx * x scaling factor * @param sy * y scaling factor */ public void scale(double sx, double sy, Point2D origin) { getAllAttributes().scale(sx, sy, origin); checkChildBounds(); } /** * If this view's current bounds do not completely enclose all child vertex * views, sets this view's bounds to the union of the current bounds and the * childrens' bounds. */ private void checkChildBounds() { if (!isLeaf()) { Rectangle2D bounds = GraphConstants.getBounds(getAllAttributes()); if (bounds == null) { bounds = new Rectangle2D.Double(); } CellView[] childViewArray = (CellView[]) childViews .toArray(new CellView[0]); Rectangle2D childBounds = AbstractCellView .getBounds(childViewArray); if (!bounds.contains(childBounds)) { bounds = bounds.createUnion(childBounds); GraphConstants.setBounds(getAllAttributes(), bounds); } } } } libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/JGraphIconView.java0000644000175000017500000003723111256667036027013 0ustar gregoagregoa/* * @(#)IconExample.java 1.0 28-SEPT-04 * * Copyright (c) 2001-2004, Dean Mao All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. - Redistributions in * binary form must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. - Neither the name of JGraph nor * the names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ package org.jgraph.example; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.EventObject; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.AbstractCellEditor; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JTextArea; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import org.jgraph.JGraph; import org.jgraph.graph.CellView; import org.jgraph.graph.CellViewRenderer; import org.jgraph.graph.DefaultGraphCellEditor; import org.jgraph.graph.GraphCellEditor; import org.jgraph.graph.GraphConstants; import org.jgraph.graph.VertexView; /** * The cell view modifies the center point of the vertex such that it is * relative to the icon rather than the entire vertex. The perimeter * * @author Dean Mao * @created Sep 28, 2004 */ public class JGraphIconView extends VertexView { private transient MyMultiLinedEditor editor = new MyMultiLinedEditor(); protected static transient IconRenderer viewRenderer = new IconRenderer(); public JGraphIconView() { super(); } public JGraphIconView(Object cell) { super(cell); } public GraphCellEditor getEditor() { return editor; } private boolean isMouseOver; /** * The mouse over boolean is here so that we could potentially render the * icon differently when the mouse is hovering above the vertex. * * @return whether or not the mouse is over the vertex */ public boolean isMouseOver() { return isMouseOver; } public void setMouseOver(boolean isMouseOver) { this.isMouseOver = isMouseOver; } /** * The center point of the vertex should be in the middle of the icon * instead of the middle of the icon/description duo. The reason we must * calculate this is so that edges know where to point. We want to make the * edges look like they are pointing to an icon from an icon. */ public Point2D getCenterPoint() { Icon icon = GraphConstants.getIcon(getAllAttributes()); double iconWidth = icon.getIconWidth(); double iconHeight = icon.getIconHeight(); return new Point2D.Double(getBounds().getX() + iconWidth / 2, getBounds().getY() + iconHeight / 2); } /** * We don't want to calculate a perimeter point around the entire vertex. * Only the perimeter around the icon will be calculated. This calculation * is generic for elliptical perimeters. */ public Point2D getPerimeterPoint(Point2D source, Point2D p) { Rectangle2D bounds = this.getBounds(); Icon icon = GraphConstants.getIcon(getAllAttributes()); double iconWidth = icon.getIconWidth(); double iconHeight = icon.getIconHeight(); double x = bounds.getX(); double y = bounds.getY(); double a = iconWidth / 2; double b = iconHeight / 2; double eccentricity = Math.sqrt(1 - ((b / a) * (b / a))); double width = bounds.getWidth(); double height = viewRenderer.getIconDisplay().getPreferredSize() .getHeight(); double xCenter = (x + (width / 2)); double yCenter = (y + (height / 2)); double dx = p.getX() - xCenter; double dy = p.getY() - yCenter; double theta = Math.atan2(dy, dx); double eSquared = eccentricity * eccentricity; double rPrime = a * Math .sqrt((1 - eSquared) / (1 - (eSquared * (Math.cos(theta) * Math .cos(theta))))); double ex = rPrime * Math.cos(theta); double ey = rPrime * Math.sin(theta); return new Point2D.Double(ex + xCenter, ey + yCenter); } public CellViewRenderer getRenderer() { return viewRenderer; } class MyMultiLinedEditor extends MultiLinedEditor { /** * We offset the multi-lined editor by the height of the icon so that * the multi-lined editor appears directly over the description text * that we are editing. */ public Component getGraphCellEditorComponent(JGraph graph, Object cell, boolean isSelected) { Component component = super.getGraphCellEditorComponent(graph, cell, isSelected); Dimension dim = ((IconRenderer) JGraphIconView.this .getRendererComponent(graph, false, false, false)) .getIconDisplay().getPreferredSize(); offsetY = (int) dim.getHeight(); return component; } } /** * This is a combination renderer that displays both an icon and a * description under the icon. The description is html rendered so that it * can be multi-lined. * * @author Dean Mao * @created Sep 28, 2004 */ public static class IconRenderer extends JComponent implements CellViewRenderer { private IconDisplay iconDisplay; private DescriptionTextArea textRenderer; public IconRenderer() { super(); iconDisplay = new IconDisplay(); textRenderer = new DescriptionTextArea("text/html", ""); textRenderer.setOpaque(true); iconDisplay.setOpaque(false); GridBagLayout gbl = new GridBagLayout(); setLayout(gbl); GridBagConstraints defaultRendererConstraint = new GridBagConstraints(); defaultRendererConstraint.gridx = 0; defaultRendererConstraint.gridy = 0; defaultRendererConstraint.gridwidth = 1; defaultRendererConstraint.gridheight = 1; defaultRendererConstraint.weightx = 1; defaultRendererConstraint.weighty = 0; defaultRendererConstraint.fill = GridBagConstraints.HORIZONTAL; // add icon renderer: gbl.setConstraints(iconDisplay, defaultRendererConstraint); this.add(iconDisplay); GridBagConstraints textRendererConstraint = new GridBagConstraints(); textRendererConstraint.gridx = 0; textRendererConstraint.gridy = 1; textRendererConstraint.gridwidth = 1; textRendererConstraint.gridheight = GridBagConstraints.REMAINDER; textRendererConstraint.weightx = 1; textRendererConstraint.weighty = 1; textRendererConstraint.fill = GridBagConstraints.BOTH; textRendererConstraint.insets = new Insets(3, 3, 3, 3); // add description renderer: gbl.setConstraints(textRenderer, textRendererConstraint); this.add(textRenderer); } public Dimension getPreferredSize() { Dimension dim = super.getPreferredSize(); dim.setSize(dim.getWidth() + 40, dim.getHeight()); return dim; } private JGraphIconView view; private boolean isSelected; private boolean isFocused; private boolean isPreview; public java.awt.Component getRendererComponent(JGraph graph, CellView view, boolean sel, boolean focus, boolean preview) { if (view instanceof JGraphIconView) { if (graph.getEditingCell() != view.getCell()) { setBackground(Color.white); } this.view = (JGraphIconView) view; this.isSelected = sel; this.isFocused = focus; this.isPreview = preview; iconDisplay.setIcon(GraphConstants.getIcon(view .getAllAttributes())); textRenderer.setDescription(graph.convertValueToString(view)); return this; } return null; } public JGraphIconView getView() { return this.view; } public boolean isSelected() { return this.isSelected; } public boolean isFocused() { return this.isFocused; } public boolean isPreview() { return this.isPreview; } public IconDisplay getIconDisplay() { return this.iconDisplay; } } /** * This JComponent only displays an icon as the upper part of the * icon/description duo. * * @author Dean Mao * @created Sep 28, 2004 */ public static class IconDisplay extends JLabel { public IconDisplay() { super(); setVerticalAlignment(JLabel.CENTER); setHorizontalAlignment(JLabel.CENTER); setHorizontalTextPosition(JLabel.CENTER); setVerticalTextPosition(JLabel.BOTTOM); setFont(UIManager.getFont("Tree.font")); setForeground(UIManager.getColor("Tree.textForeground")); setBackground(UIManager.getColor("Tree.textBackground")); } public Dimension getMinimumSize() { Dimension dim = super.getMinimumSize(); dim.setSize(dim.getWidth(), dim.getHeight() + 2); return dim; } public Dimension getPreferredSize() { return this.getMinimumSize(); } public void paint(Graphics g) { setBackground(Color.white); setBorder(null); // preview mode is "true" when we are dragging the component in the // graph if (!((IconRenderer) this.getParent()).isPreview()) { // paint the icon only in preview mode. super.paint(g); } else { // This is how we will paint the component when we are in // preview // mode (dragging component around). g.setColor(Color.BLACK); Dimension d = getSize(); g.drawOval((int) (d.width / 2 - 20), (int) (d.height / 2 - 20), 40, 40); } } } /** * This JComponent only displays html renderered text as the lower part of * the icon/description duo. * * @author Dean Mao * @created Sep 28, 2004 */ public static class DescriptionTextArea extends JEditorPane { public DescriptionTextArea(String type, String text) { super(type, text); } private String description; public void setDescription(String description) { this.description = description; setText(this.description); } public void setText(String text) { // display only the description if not blank if (text != null && !text.equals("")) { // make new lines appear as line breaks in the html renderered // text text = text.replaceAll("\n", "
    "); super .setText("
    " + text + "
    "); } else { super.setText(""); } } } public static class MultiLinedEditor extends DefaultGraphCellEditor { public class RealCellEditor extends AbstractCellEditor implements GraphCellEditor { JTextArea editorComponent = new JTextArea(); public RealCellEditor() { editorComponent.setBorder(UIManager .getBorder("Tree.editorBorder")); editorComponent.setLineWrap(true); editorComponent.setWrapStyleWord(true); // substitute a JTextArea's VK_ENTER action with our own that // will stop an edit. editorComponent.getInputMap(JComponent.WHEN_FOCUSED).put( KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "enter"); editorComponent.getInputMap(JComponent.WHEN_FOCUSED).put( KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_DOWN_MASK), "shiftEnter"); editorComponent.getInputMap(JComponent.WHEN_FOCUSED).put( KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_DOWN_MASK), "metaEnter"); editorComponent.getInputMap(JComponent.WHEN_FOCUSED).put( KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "enter"); editorComponent.getActionMap().put("enter", new AbstractAction() { public void actionPerformed(ActionEvent e) { stopCellEditing(); } }); AbstractAction newLineAction = new AbstractAction() { public void actionPerformed(ActionEvent e) { Document doc = editorComponent.getDocument(); try { doc.insertString( editorComponent.getCaretPosition(), "\n", null); } catch (BadLocationException e1) { e1.printStackTrace(); } } }; editorComponent.getActionMap().put("shiftEnter", newLineAction); editorComponent.getActionMap().put("metaEnter", newLineAction); } public Component getGraphCellEditorComponent(JGraph graph, Object value, boolean isSelected) { editorComponent.setText(value.toString()); editorComponent.selectAll(); return editorComponent; } public Object getCellEditorValue() { return editorComponent.getText(); } public boolean stopCellEditing() { // set the size of a vertex to that of an editor. CellView view = graph.getGraphLayoutCache().getMapping( graph.getEditingCell(), false); Map map = view.getAllAttributes(); Rectangle2D cellBounds = GraphConstants.getBounds(map); Rectangle editingBounds = editorComponent.getBounds(); GraphConstants.setBounds(map, new Rectangle((int) cellBounds .getX(), (int) cellBounds.getY(), editingBounds.width, editingBounds.height)); return super.stopCellEditing(); } public boolean shouldSelectCell(EventObject event) { editorComponent.requestFocus(); return super.shouldSelectCell(event); } } public MultiLinedEditor() { super(); } /** * Overriding this in order to set the size of an editor to that of an * edited view. */ public Component getGraphCellEditorComponent(JGraph graph, Object cell, boolean isSelected) { Component component = super.getGraphCellEditorComponent(graph, cell, isSelected); // set the size of an editor to that of a view CellView view = graph.getGraphLayoutCache().getMapping(cell, false); Rectangle2D tmp = view.getBounds(); editingComponent.setBounds((int) tmp.getX(), (int) tmp.getY(), (int) tmp.getWidth(), (int) tmp.getHeight()); // I have to set a font here instead of in the // RealCellEditor.getGraphCellEditorComponent() because // I don't know what cell is being edited when in the // RealCellEditor.getGraphCellEditorComponent(). Font font = GraphConstants.getFont(view.getAllAttributes()); editingComponent.setFont((font != null) ? font : graph.getFont()); return component; } protected GraphCellEditor createGraphCellEditor() { return new MultiLinedEditor.RealCellEditor(); } /** * Overriting this so that I could modify an eiditor container. see * http://sourceforge.net/forum/forum.php?thread_id=781479&forum_id=140880 */ protected Container createContainer() { return new MultiLinedEditor.ModifiedEditorContainer(); } class ModifiedEditorContainer extends EditorContainer { public void doLayout() { super.doLayout(); // substract 2 pixels that were added to the preferred size of // the container for the border. Dimension cSize = getSize(); Dimension dim = editingComponent.getSize(); editingComponent.setSize(dim.width - 2, dim.height); // reset container's size based on a potentially new preferred // size of a real editor. setSize(cSize.width, getPreferredSize().height); } } } }libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/IconExample.java0000644000175000017500000002077011256667036026400 0ustar gregoagregoa/* * @(#)IconExample.java 1.0 28-SEPT-04 * * Copyright (c) 2001-2004, Dean Mao All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. - Redistributions in * binary form must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. - Neither the name of JGraph nor * the names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ package org.jgraph.example; import java.awt.BorderLayout; import java.awt.Graphics; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.net.URL; import java.util.Map; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.event.UndoableEditEvent; import org.jgraph.graph.CellView; import org.jgraph.graph.CellViewRenderer; import org.jgraph.graph.DefaultCellViewFactory; import org.jgraph.graph.DefaultGraphCell; import org.jgraph.graph.EdgeView; import org.jgraph.graph.GraphConstants; import org.jgraph.graph.GraphModel; import org.jgraph.graph.GraphUndoManager; import org.jgraph.graph.Port; import org.jgraph.graph.PortRenderer; import org.jgraph.graph.PortView; /** * @author Dean Mao * * Hint: Use ant example to run this example. Make sure you the * jgraph source distribution located at ../jgraph relative to * this project's root directory (see build.xml). * * @created Sep 28, 2004 */ public class IconExample extends GraphEd { // Shared icon protected ImageIcon jgraphIcon = null; /** * */ public IconExample() { // Use Border Layout getContentPane().setLayout(new BorderLayout()); // Construct the Graph graph = new IconGraph(new MyModel()); // Construct Command History // // Create a GraphUndoManager which also Updates the ToolBar undoManager = new GraphUndoManager() { // Override Superclass public void undoableEditHappened(UndoableEditEvent e) { // First Invoke Superclass super.undoableEditHappened(e); // Then Update Undo/Redo Buttons updateHistoryButtons(); } }; // Add Listeners to Graph // // Register UndoManager with the Model graph.getModel().addUndoableEditListener(undoManager); // Update ToolBar based on Selection Changes graph.getSelectionModel().addGraphSelectionListener(this); // Listen for Delete Keystroke when the Graph has Focus graph.addKeyListener(this); // Construct Panel // // Add a ToolBar getContentPane().add(createToolBar(), BorderLayout.NORTH); // Add the Graph as Center Component getContentPane().add(new JScrollPane(graph), BorderLayout.CENTER); // // Load Icon String iconPath = "org/jgraph/example/resources/jgraph.gif"; URL jgraphUrl = IconExample.class.getClassLoader() .getResource(iconPath); // If Valid URL if (jgraphUrl != null) { // Load Icon jgraphIcon = new ImageIcon(jgraphUrl); } else { throw new RuntimeException( "Can't load without the default icon file! I tried to find: " + iconPath); } } public Map createCellAttributes(Point2D point) { Map map = super.createCellAttributes(point); GraphConstants.setIcon(map, jgraphIcon); return map; } protected DefaultGraphCell createDefaultGraphCell() { return new CustomCell(jgraphIcon, "default\ndescription"); } /** * Define a custom graph that implements the CellViewFactory method, * createView(), so that we can create our custom icon/description vertex. * * @author Dean Mao * @created Sep 28, 2004 */ public class IconGraph extends GraphEd.MyGraph { public IconGraph(GraphModel model) { super(model); getGraphLayoutCache().setAutoSizeOnValueChange(true); getGraphLayoutCache().setFactory(new DefaultCellViewFactory() { public CellView createView(GraphModel model, Object c) { CellView view = null; if (c instanceof CustomCell) { return new JGraphIconView(c); } else if (c instanceof Port) { view = new InvisiblePortView(c); } else { view = super.createView(model, c); } return view; } }); } } /** * CustomCell that allows user to define an icon and a description for the * graph vertex. * * @author Dean Mao * @created Sep 28, 2004 */ public class CustomCell extends DefaultGraphCell { private ImageIcon icon; private String description; public CustomCell(ImageIcon icon, String description) { this.icon = icon; this.description = description; } public String getDescription() { return description; } public ImageIcon getIcon() { return icon; } /** * Sets the description on a cell. This is called from the multi-lined * editor. */ public void setUserObject(Object obj) { if (obj != null && obj instanceof String) { this.description = obj.toString(); } } /** * Return the description of the cell so that it will be the initial * value of the in-graph editor. */ public String toString() { return description; } } /** * This "invisible port" is the same size as the icon on the * icon/description vertex. We do this by navigating up the tree to get the * CellView, then the CustomCell to get the actual icon height/width. Keep * in mind that we are also changing the location of the port such that it * is in the same place as the icon displayed on the screen. * * The port renderer is designed so that it doesn't paint anything. * * @author Dean Mao * @created Sep 28, 2004 */ public class InvisiblePortView extends PortView { public InvisiblePortView(Object cell) { super(cell); } public Rectangle2D getBounds() { Rectangle2D parentBounds = getParentView().getBounds(); double height = ((CustomCell) getParentView().getCell()).getIcon() .getIconHeight(); double width = ((CustomCell) getParentView().getCell()).getIcon() .getIconWidth(); double x = parentBounds.getX() + ((parentBounds.getWidth() - width) / 2); double y = parentBounds.getY() + 5; return new Rectangle2D.Double(x, y, width, height); } public CellViewRenderer getRenderer() { return portRenderer; } public Point2D getLocation(EdgeView edge) { if (edge == null) return new Point2D.Double(this.getBounds().getCenterX(), this .getBounds().getCenterY()); else return super.getLocation(edge); } } protected static InvisiblePortRenderer portRenderer = new InvisiblePortRenderer(); public static class InvisiblePortRenderer extends PortRenderer { public void paint(Graphics g) { if (preview) super.paint(g); // else: null implementation (ie, don't paint anything!!) } } /** * Main method */ public static void main(String[] args) { // Switch off D3D because of Sun XOR painting bug // See http://www.jgraph.com/forum/viewtopic.php?t=4066 System.setProperty("sun.java2d.d3d", "false"); // Construct Frame JFrame frame = new JFrame("IconExample"); // Set Close Operation to Exit frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Add an Editor Panel frame.getContentPane().add(new IconExample()); // Fetch URL to Icon Resource URL jgraphUrl = IconExample.class.getClassLoader().getResource( "org/jgraph/example/resources/jgraph.gif"); // If Valid URL if (jgraphUrl != null) { // Load Icon ImageIcon jgraphIcon = new ImageIcon(jgraphUrl); // Use in Window frame.setIconImage(jgraphIcon.getImage()); } // Set Default Size frame.setSize(520, 390); // Show Frame frame.setVisible(true); } }libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/LiveJGraphDemo.java0000644000175000017500000002026511256667036026773 0ustar gregoagregoapackage org.jgraph.example; import java.util.Hashtable; import java.util.Map; import java.util.HashMap; import org.jgraph.graph.GraphConstants; import org.jgraph.JGraph; import org.jgraph.graph.ConnectionSet; import org.jgraph.graph.DefaultEdge; import org.jgraph.graph.DefaultPort; import org.jgraph.graph.DefaultGraphCell; import org.jgraph.graph.DefaultGraphModel; import javax.swing.event.InternalFrameEvent; import java.beans.PropertyVetoException; import javax.swing.event.InternalFrameAdapter; import javax.swing.*; import java.awt.event.*; import java.awt.BorderLayout; public class LiveJGraphDemo extends JFrame { private JDesktopPane _desktopPane = null; private FrameSelectionListener _fsl = null; private FrameComponentListener _fcl = null; private AddParentInternalFrameAction _apifa = null; private AddChildInternalFrameAction _acifa = null; private DefaultGraphModel _graph = null; private JPanel _canvas = null; private ComponentListener _cl = null; public LiveJGraphDemo() { super("Live JGraph Demo"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); _fsl = new FrameSelectionListener(); _fcl = new FrameComponentListener(); _cl = new CompListener(); _graph = new DefaultGraphModel(); AddRootInternalFrameAction arifa = new AddRootInternalFrameAction(); _apifa = new AddParentInternalFrameAction(); _apifa.setEnabled(false); _acifa = new AddChildInternalFrameAction(); _acifa.setEnabled(false); JPanel mainPanel = new JPanel(new BorderLayout()); _desktopPane = new JDesktopPane(); _desktopPane.addComponentListener(_cl); _canvas = new JPanel(new BorderLayout()); JGraph graphComp = new JGraph(_graph); _canvas.add(graphComp, BorderLayout.CENTER); _desktopPane.add(_canvas, JLayeredPane.FRAME_CONTENT_LAYER); mainPanel.add(_desktopPane, BorderLayout.CENTER); JToolBar toolBar = new JToolBar(); toolBar.setFloatable(false); toolBar.add(arifa); toolBar.add(_apifa); toolBar.add(_acifa); mainPanel.add(toolBar, BorderLayout.NORTH); getContentPane().add(mainPanel); JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu("File"); JMenuItem menuItem = new JMenuItem("Exit"); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); menu.add(menuItem); menuBar.add(menu); menu = new JMenu("Components"); menu.add(arifa); menu.add(_apifa); menu.add(_acifa); menuBar.add(menu); setJMenuBar(menuBar); pack(); setSize(1000, 1000); setVisible(true); } public static void main(String[] args) { // Switch off D3D because of Sun XOR painting bug // See http://www.jgraph.com/forum/viewtopic.php?t=4066 System.setProperty("sun.java2d.d3d", "false"); new LiveJGraphDemo(); } private class AddRootInternalFrameAction extends AbstractAction { public AddRootInternalFrameAction() { super("Add Root Component", new ImageIcon("org/jgraph/example/resources/plus.gif")); putValue(Action.SHORT_DESCRIPTION, "Add Root Component"); } public void actionPerformed(ActionEvent ae) { //Create initial internal frame LiveJGraphInternalFrame internalFrame = new LiveJGraphInternalFrame(); internalFrame.create(); //Add to graph model DefaultGraphCell insertCell = new DefaultGraphCell(internalFrame); internalFrame.setGraphCell(insertCell); Object insertCells[] = new Object[] { insertCell }; _graph.insert(insertCells, null, null, null, null); internalFrame.addInternalFrameListener(_fsl); internalFrame.addComponentListener(_fcl); _desktopPane.add(internalFrame); try { internalFrame.setSelected(true); } catch (PropertyVetoException pve) { } } } private class AddChildInternalFrameAction extends AbstractAction { public AddChildInternalFrameAction() { super("Add Child Component", new ImageIcon("org/jgraph/example/resources/add_down.gif")); putValue(Action.SHORT_DESCRIPTION, "Add Child Component"); } public void actionPerformed(ActionEvent ae) { //Get currently selected internal frame LiveJGraphInternalFrame currentSelectedFrame = (LiveJGraphInternalFrame) _desktopPane .getSelectedFrame(); //Create initial internal frame LiveJGraphInternalFrame internalFrame = new LiveJGraphInternalFrame(); internalFrame.create(); //Add to graph model DefaultGraphCell insertCell = new DefaultGraphCell(internalFrame); internalFrame.setGraphCell(insertCell); Object insertCells[] = new Object[] { insertCell }; _graph.insert(insertCells, null, null, null, null); DefaultGraphCell parentCell = currentSelectedFrame.getGraphCell(); DefaultPort parentPort = new DefaultPort(); parentCell.add(parentPort); DefaultPort childPort = new DefaultPort(); insertCell.add(childPort); DefaultEdge edge = new DefaultEdge(); HashMap map = new HashMap(); Map atts = new Hashtable(); GraphConstants.setLineEnd(atts, GraphConstants.ARROW_CLASSIC); GraphConstants.setEndFill(atts, true); map.put(edge, atts); ConnectionSet cs = new ConnectionSet(edge, parentPort, childPort); Object insertEdges[] = new Object[] { edge }; _graph.insert(insertEdges, map, cs, null, null); internalFrame.addInternalFrameListener(_fsl); internalFrame.addComponentListener(_fcl); _desktopPane.add(internalFrame); try { internalFrame.setSelected(true); } catch (PropertyVetoException pve) { } } } private class AddParentInternalFrameAction extends AbstractAction { public AddParentInternalFrameAction() { super("Add Parent Component", new ImageIcon("org/jgraph/example/resources/add_up.gif")); putValue(Action.SHORT_DESCRIPTION, "Add Parent Component"); } public void actionPerformed(ActionEvent ae) { //Get currently selected internal frame LiveJGraphInternalFrame currentSelectedFrame = (LiveJGraphInternalFrame) _desktopPane .getSelectedFrame(); //Create initial internal frame LiveJGraphInternalFrame internalFrame = new LiveJGraphInternalFrame(); internalFrame.create(); //Add to graph model DefaultGraphCell insertCell = new DefaultGraphCell(internalFrame); internalFrame.setGraphCell(insertCell); Object insertCells[] = new Object[] { insertCell }; _graph.insert(insertCells, null, null, null, null); DefaultGraphCell childCell = currentSelectedFrame.getGraphCell(); DefaultPort childPort = new DefaultPort(); childCell.add(childPort); DefaultPort parentPort = new DefaultPort(); insertCell.add(parentPort); DefaultEdge edge = new DefaultEdge(); HashMap map = new HashMap(); Map atts = new Hashtable(); GraphConstants.setLineEnd(atts, GraphConstants.ARROW_CLASSIC); GraphConstants.setEndFill(atts, true); map.put(edge, atts); ConnectionSet cs = new ConnectionSet(edge, parentPort, childPort); Object insertEdges[] = new Object[] { edge }; _graph.insert(insertEdges, map, cs, null, null); internalFrame.addInternalFrameListener(_fsl); internalFrame.addComponentListener(_fcl); _desktopPane.add(internalFrame); try { internalFrame.setSelected(true); } catch (PropertyVetoException pve) { } } } private class FrameSelectionListener extends InternalFrameAdapter { public void internalFrameActivated(InternalFrameEvent ife) { _apifa.setEnabled(true); _acifa.setEnabled(true); } public void internalFrameDeactivated(InternalFrameEvent ife) { _apifa.setEnabled(false); _acifa.setEnabled(false); } } private class CompListener extends ComponentAdapter { public void componentResized(ComponentEvent ce) { _canvas.setSize(_desktopPane.getSize()); _canvas.updateUI(); } } private class FrameComponentListener extends ComponentAdapter { public void componentResized(ComponentEvent ce) { HashMap map = new HashMap(); Map atts = new Hashtable(); LiveJGraphInternalFrame frame = (LiveJGraphInternalFrame) ce .getComponent(); GraphConstants.setBounds(atts, frame.getBounds()); map.put(frame.getGraphCell(), atts); _graph.edit(map, null, null, null); } public void componentMoved(ComponentEvent ce) { HashMap map = new HashMap(); Map atts = new Hashtable(); LiveJGraphInternalFrame frame = (LiveJGraphInternalFrame) ce .getComponent(); GraphConstants.setBounds(atts, frame.getBounds()); map.put(frame.getGraphCell(), atts); _graph.edit(map, null, null, null); } } }libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/GraphEd.java0000644000175000017500000010532311256667036025504 0ustar gregoagregoa/* * @(#)GraphEd.java 3.3 23-APR-04 * * Copyright (c) 2001-2004, Gaudenz Alder All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package org.jgraph.example; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Cursor; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.net.URL; import java.util.Hashtable; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JApplet; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JToolBar; import javax.swing.SwingUtilities; import javax.swing.border.BevelBorder; import javax.swing.event.UndoableEditEvent; import org.jgraph.JGraph; import org.jgraph.event.GraphModelEvent; import org.jgraph.event.GraphModelListener; import org.jgraph.event.GraphSelectionEvent; import org.jgraph.event.GraphSelectionListener; import org.jgraph.graph.AbstractCellView; import org.jgraph.graph.BasicMarqueeHandler; import org.jgraph.graph.CellHandle; import org.jgraph.graph.CellView; import org.jgraph.graph.DefaultCellViewFactory; import org.jgraph.graph.DefaultEdge; import org.jgraph.graph.DefaultGraphCell; import org.jgraph.graph.DefaultGraphCellEditor; import org.jgraph.graph.DefaultGraphModel; import org.jgraph.graph.Edge; import org.jgraph.graph.EdgeRenderer; import org.jgraph.graph.EdgeView; import org.jgraph.graph.GraphConstants; import org.jgraph.graph.GraphContext; import org.jgraph.graph.GraphLayoutCache; import org.jgraph.graph.GraphModel; import org.jgraph.graph.GraphUndoManager; import org.jgraph.graph.Port; import org.jgraph.graph.PortRenderer; import org.jgraph.graph.PortView; import org.jgraph.graph.VertexRenderer; import org.jgraph.graph.VertexView; public class GraphEd extends JApplet implements GraphSelectionListener, KeyListener { // JGraph instance protected JGraph graph; // Undo Manager protected GraphUndoManager undoManager; // Actions which Change State protected Action undo, redo, remove, group, ungroup, tofront, toback, cut, copy, paste; // cell count that gets put in cell label protected int cellCount = 0; // Status Bar protected StatusBarGraphListener statusBar; // // Main // // Main Method public static void main(String[] args) { // Switch off D3D because of Sun XOR painting bug // See http://www.jgraph.com/forum/viewtopic.php?t=4066 System.setProperty("sun.java2d.d3d", "false"); // Construct Frame JFrame frame = new JFrame("GraphEd"); // Set Close Operation to Exit // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Add an Editor Panel frame.getContentPane().add(new GraphEd()); // Fetch URL to Icon Resource URL jgraphUrl = GraphEd.class.getClassLoader().getResource( "org/jgraph/example/resources/jgraph.gif"); // If Valid URL if (jgraphUrl != null) { // Load Icon ImageIcon jgraphIcon = new ImageIcon(jgraphUrl); // Use in Window frame.setIconImage(jgraphIcon.getImage()); } // Set Default Size frame.setSize(520, 390); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Show Frame frame.setVisible(true); } // // Editor Panel // // Construct an Editor Panel public GraphEd() { // Construct the Graph graph = createGraph(); // Use a Custom Marquee Handler graph.setMarqueeHandler(createMarqueeHandler()); // Construct Command History // // Create a GraphUndoManager which also Updates the ToolBar undoManager = new GraphUndoManager() { // Override Superclass public void undoableEditHappened(UndoableEditEvent e) { // First Invoke Superclass super.undoableEditHappened(e); // Then Update Undo/Redo Buttons updateHistoryButtons(); } }; populateContentPane(); /********************************************************************** * GraphModelChange explaination *********************************************************************/ // Uncomment out the code below if you want to see the model change outputs // the new attributes are obtained from getPreviousAttributes(). The // mode change is the undo edit of the change. That is, if you undo, // that change edit is executed directly. So the previous attributes // are the attributes before the undo and the attributes are the // attributes after the undo. But when listening to a change, // getPreviousAttributes() returns the new attributes and // getAttributes() the old attributes. // graph.getModel().addGraphModelListener(new GraphModelListener() { // public void graphChanged(GraphModelEvent e) { // GraphModelEvent.GraphModelChange c = e.getChange(); // if (c.getRemoved() == null && c.getInserted() == null) { // Map previousAttributes = c.getPreviousAttributes(); // Set keySet = previousAttributes.keySet(); // Iterator iter = keySet.iterator(); // while (iter.hasNext()) { // Object attribute = iter.next(); // System.out.println("Prev Key " + String.valueOf(attribute)); // Object value = c.getPreviousAttributes().get(attribute); // System.out.println("\t" + String.valueOf(value)); // } // Map attributes = c.getAttributes(); // keySet = attributes.keySet(); // iter = keySet.iterator(); // while (iter.hasNext()) { // Object attribute = iter.next(); // System.out.println("Curr Key " + String.valueOf(attribute)); // Object value = c.getAttributes().get(attribute); // System.out.println("\t" + String.valueOf(value)); // } // System.out.println("\n\n"); // } // } // }); installListeners(graph); } public void destroy() { super.destroy(); PortView.renderer = new PortRenderer(); EdgeView.renderer = new EdgeRenderer(); AbstractCellView.cellEditor = new DefaultGraphCellEditor(); VertexView.renderer = new VertexRenderer(); } // Hook for subclassers protected void populateContentPane() { // Use Border Layout getContentPane().setLayout(new BorderLayout()); // Add a ToolBar getContentPane().add(createToolBar(), BorderLayout.NORTH); // Add the Graph as Center Component getContentPane().add(new JScrollPane(graph), BorderLayout.CENTER); statusBar = createStatusBar(); getContentPane().add(statusBar, BorderLayout.SOUTH); } // Hook for subclassers protected JGraph createGraph() { JGraph graph = new MyGraph(new MyModel()); graph.getGraphLayoutCache().setFactory(new DefaultCellViewFactory() { // Override Superclass Method to Return Custom EdgeView protected EdgeView createEdgeView(Object cell) { // Return Custom EdgeView return new EdgeView(cell) { /** * Returns a cell handle for the view. */ public CellHandle getHandle(GraphContext context) { return new MyEdgeHandle(this, context); } }; } }); return graph; } // Hook for subclassers protected void installListeners(JGraph graph) { // Add Listeners to Graph // // Register UndoManager with the Model graph.getModel().addUndoableEditListener(undoManager); // Update ToolBar based on Selection Changes graph.getSelectionModel().addGraphSelectionListener(this); // Listen for Delete Keystroke when the Graph has Focus graph.addKeyListener(this); graph.getModel().addGraphModelListener(statusBar); } // Hook for subclassers protected void uninstallListeners(JGraph graph) { graph.getModel().removeUndoableEditListener(undoManager); graph.getSelectionModel().removeGraphSelectionListener(this); graph.removeKeyListener(this); graph.getModel().removeGraphModelListener(statusBar); } // Hook for subclassers protected BasicMarqueeHandler createMarqueeHandler() { return new MyMarqueeHandler(); } // Insert a new Vertex at point public void insert(Point2D point) { // Construct Vertex with no Label DefaultGraphCell vertex = createDefaultGraphCell(); // Create a Map that holds the attributes for the Vertex vertex.getAttributes().applyMap(createCellAttributes(point)); // Insert the Vertex (including child port and attributes) graph.getGraphLayoutCache().insert(vertex); } // Hook for subclassers public Map createCellAttributes(Point2D point) { Map map = new Hashtable(); // Snap the Point to the Grid if (graph != null) { point = graph.snap((Point2D) point.clone()); } else { point = (Point2D) point.clone(); } // Add a Bounds Attribute to the Map GraphConstants.setBounds(map, new Rectangle2D.Double(point.getX(), point.getY(), 0, 0)); // Make sure the cell is resized on insert GraphConstants.setResize(map, true); // Add a nice looking gradient background GraphConstants.setGradientColor(map, Color.blue); // Add a Border Color Attribute to the Map GraphConstants.setBorderColor(map, Color.black); // Add a White Background GraphConstants.setBackground(map, Color.white); // Make Vertex Opaque GraphConstants.setOpaque(map, true); return map; } // Hook for subclassers protected DefaultGraphCell createDefaultGraphCell() { DefaultGraphCell cell = new DefaultGraphCell("Cell " + new Integer(cellCount++)); // Add one Floating Port cell.addPort(); return cell; } // Insert a new Edge between source and target public void connect(Port source, Port target) { // Construct Edge with no label DefaultEdge edge = createDefaultEdge(); if (graph.getModel().acceptsSource(edge, source) && graph.getModel().acceptsTarget(edge, target)) { // Create a Map thath holds the attributes for the edge edge.getAttributes().applyMap(createEdgeAttributes()); // Insert the Edge and its Attributes graph.getGraphLayoutCache().insertEdge(edge, source, target); } } // Hook for subclassers protected DefaultEdge createDefaultEdge() { return new DefaultEdge(); } // Hook for subclassers public Map createEdgeAttributes() { Map map = new Hashtable(); // Add a Line End Attribute GraphConstants.setLineEnd(map, GraphConstants.ARROW_SIMPLE); // Add a label along edge attribute GraphConstants.setLabelAlongEdge(map, true); return map; } // Create a Group that Contains the Cells public void group(Object[] cells) { // Order Cells by Model Layering cells = graph.order(cells); // If Any Cells in View if (cells != null && cells.length > 0) { DefaultGraphCell group = createGroupCell(); // Insert into model graph.getGraphLayoutCache().insertGroup(group, cells); } } // Hook for subclassers protected DefaultGraphCell createGroupCell() { return new DefaultGraphCell(); } // Returns the total number of cells in a graph protected int getCellCount(JGraph graph) { Object[] cells = graph.getDescendants(graph.getRoots()); return cells.length; } // Ungroup the Groups in Cells and Select the Children public void ungroup(Object[] cells) { graph.getGraphLayoutCache().ungroup(cells); } // Determines if a Cell is a Group public boolean isGroup(Object cell) { // Map the Cell to its View CellView view = graph.getGraphLayoutCache().getMapping(cell, false); if (view != null) return !view.isLeaf(); return false; } // Brings the Specified Cells to Front public void toFront(Object[] c) { graph.getGraphLayoutCache().toFront(c); } // Sends the Specified Cells to Back public void toBack(Object[] c) { graph.getGraphLayoutCache().toBack(c); } // Undo the last Change to the Model or the View public void undo() { try { undoManager.undo(graph.getGraphLayoutCache()); } catch (Exception ex) { System.err.println(ex); } finally { updateHistoryButtons(); } } // Redo the last Change to the Model or the View public void redo() { try { undoManager.redo(graph.getGraphLayoutCache()); } catch (Exception ex) { System.err.println(ex); } finally { updateHistoryButtons(); } } // Update Undo/Redo Button State based on Undo Manager protected void updateHistoryButtons() { // The View Argument Defines the Context undo.setEnabled(undoManager.canUndo(graph.getGraphLayoutCache())); redo.setEnabled(undoManager.canRedo(graph.getGraphLayoutCache())); } // // Listeners // // From GraphSelectionListener Interface public void valueChanged(GraphSelectionEvent e) { // Group Button only Enabled if more than One Cell Selected group.setEnabled(graph.getSelectionCount() > 1); // Update Button States based on Current Selection boolean enabled = !graph.isSelectionEmpty(); remove.setEnabled(enabled); ungroup.setEnabled(enabled); tofront.setEnabled(enabled); toback.setEnabled(enabled); copy.setEnabled(enabled); cut.setEnabled(enabled); } // // KeyListener for Delete KeyStroke // public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } public void keyPressed(KeyEvent e) { // Listen for Delete Key Press if (e.getKeyCode() == KeyEvent.VK_DELETE) // Execute Remove Action on Delete Key Press remove.actionPerformed(null); } // // Custom Graph // // Defines a Graph that uses the Shift-Button (Instead of the Right // Mouse Button, which is Default) to add/remove point to/from an edge. public static class MyGraph extends JGraph { // Construct the Graph using the Model as its Data Source public MyGraph(GraphModel model) { this(model, null); } // Construct the Graph using the Model as its Data Source public MyGraph(GraphModel model, GraphLayoutCache cache) { super(model, cache); // Make Ports Visible by Default setPortsVisible(true); // Use the Grid (but don't make it Visible) setGridEnabled(true); // Set the Grid Size to 10 Pixel setGridSize(6); // Set the Tolerance to 2 Pixel setTolerance(2); // Accept edits if click on background setInvokesStopCellEditing(true); // Allows control-drag setCloneable(true); // Jump to default port on connect setJumpToDefaultPort(true); } } // // Custom Edge Handle // // Defines a EdgeHandle that uses the Shift-Button (Instead of the Right // Mouse Button, which is Default) to add/remove point to/from an edge. public static class MyEdgeHandle extends EdgeView.EdgeHandle { /** * @param edge * @param ctx */ public MyEdgeHandle(EdgeView edge, GraphContext ctx) { super(edge, ctx); } // Override Superclass Method public boolean isAddPointEvent(MouseEvent event) { // Points are Added using Shift-Click return event.isShiftDown(); } // Override Superclass Method public boolean isRemovePointEvent(MouseEvent event) { // Points are Removed using Shift-Click return event.isShiftDown(); } } // // Custom Model // // A Custom Model that does not allow Self-References public static class MyModel extends DefaultGraphModel { // Override Superclass Method public boolean acceptsSource(Object edge, Object port) { // Source only Valid if not Equal Target return (((Edge) edge).getTarget() != port); } // Override Superclass Method public boolean acceptsTarget(Object edge, Object port) { // Target only Valid if not Equal Source return (((Edge) edge).getSource() != port); } } // // Custom MarqueeHandler // MarqueeHandler that Connects Vertices and Displays PopupMenus public class MyMarqueeHandler extends BasicMarqueeHandler { // Holds the Start and the Current Point protected Point2D start, current; // Holds the First and the Current Port protected PortView port, firstPort; /** * Component that is used for highlighting cells if * the graph does not allow XOR painting. */ protected JComponent highlight = new JPanel(); public MyMarqueeHandler() { // Configures the panel for highlighting ports highlight = createHighlight(); } /** * Creates the component that is used for highlighting cells if * the graph does not allow XOR painting. */ protected JComponent createHighlight() { JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); panel.setVisible(false); panel.setOpaque(false); return panel; } // Override to Gain Control (for PopupMenu and ConnectMode) public boolean isForceMarqueeEvent(MouseEvent e) { if (e.isShiftDown()) return false; // If Right Mouse Button we want to Display the PopupMenu if (SwingUtilities.isRightMouseButton(e)) // Return Immediately return true; // Find and Remember Port port = getSourcePortAt(e.getPoint()); // If Port Found and in ConnectMode (=Ports Visible) if (port != null && graph.isPortsVisible()) return true; // Else Call Superclass return super.isForceMarqueeEvent(e); } // Display PopupMenu or Remember Start Location and First Port public void mousePressed(final MouseEvent e) { // If Right Mouse Button if (SwingUtilities.isRightMouseButton(e)) { // Find Cell in Model Coordinates Object cell = graph.getFirstCellForLocation(e.getX(), e.getY()); // Create PopupMenu for the Cell JPopupMenu menu = createPopupMenu(e.getPoint(), cell); // Display PopupMenu menu.show(graph, e.getX(), e.getY()); // Else if in ConnectMode and Remembered Port is Valid } else if (port != null && graph.isPortsVisible()) { // Remember Start Location start = graph.toScreen(port.getLocation()); // Remember First Port firstPort = port; } else { // Call Superclass super.mousePressed(e); } } // Find Port under Mouse and Repaint Connector public void mouseDragged(MouseEvent e) { // If remembered Start Point is Valid if (start != null) { // Fetch Graphics from Graph Graphics g = graph.getGraphics(); // Reset Remembered Port PortView newPort = getTargetPortAt(e.getPoint()); // Do not flicker (repaint only on real changes) if (newPort == null || newPort != port) { // Xor-Paint the old Connector (Hide old Connector) paintConnector(Color.black, graph.getBackground(), g); // If Port was found then Point to Port Location port = newPort; if (port != null) current = graph.toScreen(port.getLocation()); // Else If no Port was found then Point to Mouse Location else current = graph.snap(e.getPoint()); // Xor-Paint the new Connector paintConnector(graph.getBackground(), Color.black, g); } } // Call Superclass super.mouseDragged(e); } public PortView getSourcePortAt(Point2D point) { // Disable jumping graph.setJumpToDefaultPort(false); PortView result; try { // Find a Port View in Model Coordinates and Remember result = graph.getPortViewAt(point.getX(), point.getY()); } finally { graph.setJumpToDefaultPort(true); } return result; } // Find a Cell at point and Return its first Port as a PortView protected PortView getTargetPortAt(Point2D point) { // Find a Port View in Model Coordinates and Remember return graph.getPortViewAt(point.getX(), point.getY()); } // Connect the First Port and the Current Port in the Graph or Repaint public void mouseReleased(MouseEvent e) { highlight(graph, null); // If Valid Event, Current and First Port if (e != null && port != null && firstPort != null && firstPort != port) { // Then Establish Connection connect((Port) firstPort.getCell(), (Port) port.getCell()); e.consume(); // Else Repaint the Graph } else graph.repaint(); // Reset Global Vars firstPort = port = null; start = current = null; // Call Superclass super.mouseReleased(e); } // Show Special Cursor if Over Port public void mouseMoved(MouseEvent e) { // Check Mode and Find Port if (e != null && getSourcePortAt(e.getPoint()) != null && graph.isPortsVisible()) { // Set Cusor on Graph (Automatically Reset) graph.setCursor(new Cursor(Cursor.HAND_CURSOR)); // Consume Event // Note: This is to signal the BasicGraphUI's // MouseHandle to stop further event processing. e.consume(); } else // Call Superclass super.mouseMoved(e); } // Use Xor-Mode on Graphics to Paint Connector protected void paintConnector(Color fg, Color bg, Graphics g) { if (graph.isXorEnabled()) { // Set Foreground g.setColor(fg); // Set Xor-Mode Color g.setXORMode(bg); // Highlight the Current Port paintPort(graph.getGraphics()); drawConnectorLine(g); } else { Rectangle dirty = new Rectangle((int) start.getX(), (int) start.getY(), 1, 1); if (current != null) { dirty.add(current); } dirty.grow(1, 1); graph.repaint(dirty); highlight(graph, port); } } // Overrides parent method to paint connector if // XOR painting is disabled in the graph public void paint(JGraph graph, Graphics g) { super.paint(graph, g); if (!graph.isXorEnabled()) { g.setColor(Color.black); drawConnectorLine(g); } } protected void drawConnectorLine(Graphics g) { if (firstPort != null && start != null && current != null) { // Then Draw A Line From Start to Current Point g.drawLine((int) start.getX(), (int) start.getY(), (int) current.getX(), (int) current.getY()); } } // Use the Preview Flag to Draw a Highlighted Port protected void paintPort(Graphics g) { // If Current Port is Valid if (port != null) { // If Not Floating Port... boolean o = (GraphConstants.getOffset(port.getAllAttributes()) != null); // ...Then use Parent's Bounds Rectangle2D r = (o) ? port.getBounds() : port.getParentView() .getBounds(); // Scale from Model to Screen r = graph.toScreen((Rectangle2D) r.clone()); // Add Space For the Highlight Border r.setFrame(r.getX() - 3, r.getY() - 3, r.getWidth() + 6, r .getHeight() + 6); // Paint Port in Preview (=Highlight) Mode graph.getUI().paintCell(g, port, r, true); } } /** * Highlights the given cell view or removes the highlight if * no cell view is specified. * * @param graph * @param cellView */ protected void highlight(JGraph graph, CellView cellView) { if (cellView != null) { highlight.setBounds(getHighlightBounds(graph, cellView)); if (highlight.getParent() == null) { graph.add(highlight); highlight.setVisible(true); } } else { if (highlight.getParent() != null) { highlight.setVisible(false); highlight.getParent().remove(highlight); } } } /** * Returns the bounds to be used to highlight the given cell view. * * @param graph * @param cellView * @return */ protected Rectangle getHighlightBounds(JGraph graph, CellView cellView) { boolean offset = (GraphConstants.getOffset(cellView.getAllAttributes()) != null); Rectangle2D r = (offset) ? cellView.getBounds() : cellView .getParentView().getBounds(); r = graph.toScreen((Rectangle2D) r.clone()); int s = 3; return new Rectangle((int) (r.getX() - s), (int) (r.getY() - s), (int) (r.getWidth() + 2 * s), (int) (r.getHeight() + 2 * s)); } } // End of Editor.MyMarqueeHandler // // // // // PopupMenu and ToolBar // // // // // // PopupMenu // public JPopupMenu createPopupMenu(final Point pt, final Object cell) { JPopupMenu menu = new JPopupMenu(); if (cell != null) { // Edit menu.add(new AbstractAction("Edit") { public void actionPerformed(ActionEvent e) { graph.startEditingAtCell(cell); } }); } // Remove if (!graph.isSelectionEmpty()) { menu.addSeparator(); menu.add(new AbstractAction("Remove") { public void actionPerformed(ActionEvent e) { remove.actionPerformed(e); } }); } menu.addSeparator(); // Insert menu.add(new AbstractAction("Insert") { public void actionPerformed(ActionEvent ev) { insert(pt); } }); return menu; } // // ToolBar // public JToolBar createToolBar() { JToolBar toolbar = new JToolBar(); toolbar.setFloatable(false); // Insert URL insertUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/insert.gif"); ImageIcon insertIcon = new ImageIcon(insertUrl); toolbar.add(new AbstractAction("", insertIcon) { public void actionPerformed(ActionEvent e) { insert(new Point(10, 10)); } }); // Toggle Connect Mode URL connectUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/connecton.gif"); ImageIcon connectIcon = new ImageIcon(connectUrl); toolbar.add(new AbstractAction("", connectIcon) { public void actionPerformed(ActionEvent e) { graph.setPortsVisible(!graph.isPortsVisible()); URL connectUrl; if (graph.isPortsVisible()) connectUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/connecton.gif"); else connectUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/connectoff.gif"); ImageIcon connectIcon = new ImageIcon(connectUrl); putValue(SMALL_ICON, connectIcon); } }); // Undo toolbar.addSeparator(); URL undoUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/undo.gif"); ImageIcon undoIcon = new ImageIcon(undoUrl); undo = new AbstractAction("", undoIcon) { public void actionPerformed(ActionEvent e) { undo(); } }; undo.setEnabled(false); toolbar.add(undo); // Redo URL redoUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/redo.gif"); ImageIcon redoIcon = new ImageIcon(redoUrl); redo = new AbstractAction("", redoIcon) { public void actionPerformed(ActionEvent e) { redo(); } }; redo.setEnabled(false); toolbar.add(redo); // // Edit Block // toolbar.addSeparator(); Action action; URL url; // Copy action = javax.swing.TransferHandler.getCopyAction(); url = getClass().getClassLoader().getResource( "org/jgraph/example/resources/copy.gif"); toolbar.add(copy = new EventRedirector(action, new ImageIcon(url))); // Paste action = javax.swing.TransferHandler.getPasteAction(); url = getClass().getClassLoader().getResource( "org/jgraph/example/resources/paste.gif"); toolbar.add(paste = new EventRedirector(action, new ImageIcon(url))); // Cut action = javax.swing.TransferHandler.getCutAction(); url = getClass().getClassLoader().getResource( "org/jgraph/example/resources/cut.gif"); toolbar.add(cut = new EventRedirector(action, new ImageIcon(url))); // Remove URL removeUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/delete.gif"); ImageIcon removeIcon = new ImageIcon(removeUrl); remove = new AbstractAction("", removeIcon) { public void actionPerformed(ActionEvent e) { if (!graph.isSelectionEmpty()) { Object[] cells = graph.getSelectionCells(); cells = graph.getDescendants(cells); graph.getModel().remove(cells); } } }; remove.setEnabled(false); toolbar.add(remove); // To Front toolbar.addSeparator(); URL toFrontUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/tofront.gif"); ImageIcon toFrontIcon = new ImageIcon(toFrontUrl); tofront = new AbstractAction("", toFrontIcon) { public void actionPerformed(ActionEvent e) { if (!graph.isSelectionEmpty()) toFront(graph.getSelectionCells()); } }; tofront.setEnabled(false); toolbar.add(tofront); // To Back URL toBackUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/toback.gif"); ImageIcon toBackIcon = new ImageIcon(toBackUrl); toback = new AbstractAction("", toBackIcon) { public void actionPerformed(ActionEvent e) { if (!graph.isSelectionEmpty()) toBack(graph.getSelectionCells()); } }; toback.setEnabled(false); toolbar.add(toback); // Zoom Std toolbar.addSeparator(); URL zoomUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/zoom.gif"); ImageIcon zoomIcon = new ImageIcon(zoomUrl); toolbar.add(new AbstractAction("", zoomIcon) { public void actionPerformed(ActionEvent e) { graph.setScale(1.0); } }); // Zoom In URL zoomInUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/zoomin.gif"); ImageIcon zoomInIcon = new ImageIcon(zoomInUrl); toolbar.add(new AbstractAction("", zoomInIcon) { public void actionPerformed(ActionEvent e) { graph.setScale(2 * graph.getScale()); } }); // Zoom Out URL zoomOutUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/zoomout.gif"); ImageIcon zoomOutIcon = new ImageIcon(zoomOutUrl); toolbar.add(new AbstractAction("", zoomOutIcon) { public void actionPerformed(ActionEvent e) { graph.setScale(graph.getScale() / 2); } }); // Group toolbar.addSeparator(); URL groupUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/group.gif"); ImageIcon groupIcon = new ImageIcon(groupUrl); group = new AbstractAction("", groupIcon) { public void actionPerformed(ActionEvent e) { group(graph.getSelectionCells()); } }; group.setEnabled(false); toolbar.add(group); // Ungroup URL ungroupUrl = getClass().getClassLoader().getResource( "org/jgraph/example/resources/ungroup.gif"); ImageIcon ungroupIcon = new ImageIcon(ungroupUrl); ungroup = new AbstractAction("", ungroupIcon) { public void actionPerformed(ActionEvent e) { ungroup(graph.getSelectionCells()); } }; ungroup.setEnabled(false); toolbar.add(ungroup); return toolbar; } /** * @return Returns the graph. */ public JGraph getGraph() { return graph; } /** * @param graph * The graph to set. */ public void setGraph(JGraph graph) { this.graph = graph; } // This will change the source of the actionevent to graph. public class EventRedirector extends AbstractAction { protected Action action; // Construct the "Wrapper" Action public EventRedirector(Action a, ImageIcon icon) { super("", icon); this.action = a; } // Redirect the Actionevent public void actionPerformed(ActionEvent e) { e = new ActionEvent(graph, e.getID(), e.getActionCommand(), e .getModifiers()); action.actionPerformed(e); } } /** * Create a status bar */ protected StatusBarGraphListener createStatusBar() { return new EdStatusBar(); } /** * * @return a String representing the version of this application */ protected String getVersion() { return JGraph.VERSION; } /** * @return Returns the redo. */ public Action getRedo() { return redo; } /** * @param redo * The redo to set. */ public void setRedo(Action redo) { this.redo = redo; } /** * @return Returns the undo. */ public Action getUndo() { return undo; } /** * @param undo * The undo to set. */ public void setUndo(Action undo) { this.undo = undo; } public class StatusBarGraphListener extends JPanel implements GraphModelListener { /** * Graph Model change event */ public void graphChanged(GraphModelEvent e) { updateStatusBar(); } protected void updateStatusBar(){ } } public class EdStatusBar extends StatusBarGraphListener { /** * */ protected JLabel leftSideStatus; /** * contains the scale for the current graph */ protected JLabel rightSideStatus; /** * Constructor for GPStatusBar. * */ public EdStatusBar() { super(); // Add this as graph model change listener setLayout(new BorderLayout()); leftSideStatus = new JLabel(getVersion()); rightSideStatus = new JLabel("0/0Mb"); leftSideStatus.setBorder(BorderFactory.createLoweredBevelBorder()); rightSideStatus.setBorder(BorderFactory.createLoweredBevelBorder()); add(leftSideStatus, BorderLayout.CENTER); add(rightSideStatus, BorderLayout.EAST); } protected void updateStatusBar() { Runtime runtime = Runtime.getRuntime(); int freeMemory = (int) (runtime.freeMemory() / 1024); int totalMemory = (int) (runtime.totalMemory() / 1024); int usedMemory = (totalMemory - freeMemory); String str = (usedMemory / 1024) + "/" + (totalMemory / 1024) + "Mb"; rightSideStatus.setText(str); } /** * @return Returns the leftSideStatus. */ public JLabel getLeftSideStatus() { return leftSideStatus; } /** * @param leftSideStatus * The leftSideStatus to set. */ public void setLeftSideStatus(JLabel leftSideStatus) { this.leftSideStatus = leftSideStatus; } /** * @return Returns the rightSideStatus. */ public JLabel getRightSideStatus() { return rightSideStatus; } /** * @param rightSideStatus * The rightSideStatus to set. */ public void setRightSideStatus(JLabel rightSideStatus) { this.rightSideStatus = rightSideStatus; } } /** * @return Returns the copy. */ public Action getCopy() { return copy; } /** * @param copy * The copy to set. */ public void setCopy(Action copy) { this.copy = copy; } /** * @return Returns the cut. */ public Action getCut() { return cut; } /** * @param cut * The cut to set. */ public void setCut(Action cut) { this.cut = cut; } /** * @return Returns the paste. */ public Action getPaste() { return paste; } /** * @param paste * The paste to set. */ public void setPaste(Action paste) { this.paste = paste; } /** * @return Returns the toback. */ public Action getToback() { return toback; } /** * @param toback * The toback to set. */ public void setToback(Action toback) { this.toback = toback; } /** * @return Returns the tofront. */ public Action getTofront() { return tofront; } /** * @param tofront * The tofront to set. */ public void setTofront(Action tofront) { this.tofront = tofront; } /** * @return Returns the remove. */ public Action getRemove() { return remove; } /** * @param remove * The remove to set. */ public void setRemove(Action remove) { this.remove = remove; } } libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/0000755000175000017500000000000011256667036025335 5ustar gregoagregoalibjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/undo.gif0000644000175000017500000000031211256667036026765 0ustar gregoagregoaGIF89a!,TR&" ݅4S:bI )˨Z !OCopyright 2000 by Sun Microsystems, Inc. All Rights Reserved. JLF GR Ver 1.0 ;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/paste.gif0000644000175000017500000000045211256667036027141 0ustar gregoagregoaGIF89aϺɶɶ±°̼̻˼õ{{vrfffc11^33!,@T`&dIb2XVaЖ .C*]b K(V4$bhf͇\"W@=ca1ZC!!OCopyright 2000 by Sun Microsystems, Inc. All Rights Reserved. JLF GR Ver 1.0 ;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/delete.gif0000644000175000017500000000032011256667036027261 0ustar gregoagregoaGIF89a̙fff333!,Bhn$>d5fB02m7\7@̷C u@0sROU8vU$!OCopyright 2000 by Sun Microsystems, Inc. All Rights Reserved. JLF GR Ver 1.0 ;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/expand.gif0000644000175000017500000000027611256667036027310 0ustar gregoagregoaGIF89aփ╝!,;%d9Zy٪f+89䗈,41 XJ+JK-B;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/ungroup.gif0000644000175000017500000000016011256667036027520 0ustar gregoagregoaGIF89aM{m{4R00!,58J:ͩBF fjaSe]eyCeﲛ-i)" ;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/tofront.gif0000644000175000017500000000023211256667036027514 0ustar gregoagregoaGIF89a̤튊ܭ!,GI1d@0N bJW9 AXo3"p8|K& ,okԝ;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/add_down.gif0000644000175000017500000000157111256667036027607 0ustar gregoagregoaGIF89aZZc!,V@P $pC2jdpBr )D( HK. x 3C3["K=g4y@@$ ;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/copy.gif0000644000175000017500000000044011256667036026774 0ustar gregoagregoaGIF89a泳̳̲̲̱˰ɬƪĨ¥!,@J 'd9n*Tn%mtmH֠&B]PPvxYJ!Q6 cM S#$|k!!OCopyright 2000 by Sun Microsystems, Inc. All Rights Reserved. JLF GR Ver 1.0 ;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/plus.gif0000644000175000017500000000155211256667036027012 0ustar gregoagregoaGIF89a*U****U***UU*UUUUU*U*U*UԪ****U**********U*******U*U**UU*U*U*U****U*******U*******U**Ԫ*UU*UUUUUU*U**U*UU*U*U*UUUU*UUUUUUUUUUU*UUUUUUU*UUUUUUU*UUUUԪU*U****U***UU*UUUUU*U*U*UԪԪ*UԪ****U***ԪUU*UUUUUԪ*UԪ*UԪ*UԪ*U****U***UU*UUUUU*UԪԪ*ԪUԪԪԪ*UԪ &&&333???LLLYYYfffrrr𠠤!,G  Ч`‡ >D/"hA?dHdɂ&S9eG5b(_̚7ea@;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/port.gif0000644000175000017500000000050611256667036027011 0ustar gregoagregoaGIF89a 1{!cZRRRJBBBBBcc{BkJ9ƽ{{99111Jc))1J1)s1s!k!cZ!ZJJ! ,c@pH,DKD"XH\ @N9%`tBBh%yCw&("D!#%)(E$$'(G"''"HDA;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/zoomout.gif0000644000175000017500000000046011256667036027540 0ustar gregoagregoaGIF89a00a==n}}丸󋌢n= q !,W%dYZei=V$4k-0X B;= A&ki55*ޗ5P(y]tpf%!;!!OCopyright 2000 by Sun Microsystems, Inc. All Rights Reserved. JLF GR Ver 1.0 ;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/cut.gif0000644000175000017500000000121411256667036026615 0ustar gregoagregoaGIF89a00xx򗗗 !,LPI+P͙jUwRY(Ȋ|iz<dp *'t)@ 5ĔFK))2M";٤ !,upH,kȤVў' :1'LBMGLdT\%Gfm2UC:/)&$!}5 TGKL~ T3 K`FDA;𣝛POOwvvݯDDD !,wwn|Somvovzo|v~kw"~{o"#~k{w~D"~wu{uvkDyŰyB!HÇ#!OCopyright 2000 by Sun Microsystems, Inc. All Rights Reserved. JLF GR Ver 1.0 ;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/zoomin.gif0000644000175000017500000000046011256667036027337 0ustar gregoagregoaGIF89a00a==nĿ昙a0—~ !,W %dY`i0@L$kE XMa;1 ki55@*ޔudRM"y]tpf%!;!!OCopyright 2000 by Sun Microsystems, Inc. All Rights Reserved. JLF GR Ver 1.0 ;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/add_up.gif0000644000175000017500000000157111256667036027264 0ustar gregoagregoaGIF89aZZc!,VH@!0l8) 1CJlh1HR\r@(~)J*IɓC8S೦͢C ;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/expandAll.gif0000644000175000017500000000030711256667036027734 0ustar gregoagregoaGIF89a勖ₑד驱ٺ!,D%d9Zy٪f+<6@U$[W1Te F+\bo߾;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/connectoff.gif0000644000175000017500000000161611256667036030154 0ustar gregoagregoaGIF89af3̙f3f3ffffff3f3333f333f3f3̙f3̙̙̙̙f̙3̙ffffff3f3333f333f3̙f3̙̙f3̙f3ff̙ffff3f33̙33f333̙f3ffffff3ffff̙fff3fffffff3ffffffffffff3fff3f3f3f3ff33f3ffffff3f3333f333333̙3f3333333f3333f3f3f3ff3f33f33333333f333333333f333f3̙f3f3ffffff3f3333f333f3==nnnXX~π!,k Ho<„!᷆/̆ ۶>^ĖM`6n:ZM[ɓ-)v2ٴimeHb›%:8dD2ltaS5Zʵ`@;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/collapse.gif0000644000175000017500000000030311256667036027622 0ustar gregoagregoaGIF89aق銖匙⃑!,@UYdijJV 3 ld`($Q -I xɽ(+6^U;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/insert.gif0000644000175000017500000000050111256667036027324 0ustar gregoagregoaGIF89aڴڳڳڳٱװկխӬҪЧͣɡǝÚ}}zzttssff33f!+,@^p)E0l,)q 6D8EJWpX5idԘ X QW}h|e Dj$Zp)k KprydLNPtx{|S~_A;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/connecton.gif0000644000175000017500000000016211256667036030011 0ustar gregoagregoaGIF89a==nnn!,@7 ͹M! #٠*VIR!L7$ i7r1["JX;libjgraph-java-5.12.4.2+dfsg.orig/examples/org/jgraph/example/resources/zoom.gif0000644000175000017500000000045711256667036027016 0ustar gregoagregoaGIF89a00a==n󋌢n= q !,Y %dYN$6L3 l;5