1   
28  package demo.view.application;
29  
30  import demo.view.DemoBase;
31  
32  import y.base.Edge;
33  import y.base.EdgeCursor;
34  import y.base.EdgeList;
35  import y.base.Node;
36  import y.base.NodeCursor;
37  import y.base.NodeList;
38  import y.base.YCursor;
39  import y.geom.YPoint;
40  import y.layout.Layouter;
41  import y.layout.LayoutOrientation;
42  import y.layout.hierarchic.incremental.EdgeLayoutDescriptor;
43  import y.layout.hierarchic.IncrementalHierarchicLayouter;
44  import y.layout.hierarchic.incremental.RoutingStyle;
45  import y.util.GraphCopier;
46  import y.view.EditMode;
47  import y.view.Graph2D;
48  import y.view.Graph2DSelectionEvent;
49  import y.view.Graph2DSelectionListener;
50  import y.view.Graph2DView;
51  import y.view.HitInfo;
52  import y.view.LocalViewCreator.*;
53  import y.view.ViewMode;
54  import y.view.LocalViewCreator;
55  import y.view.ModelViewManager;
56  import y.view.NodeRealizer;
57  import y.view.hierarchy.GroupNodeRealizer;
58  import y.view.hierarchy.HierarchyManager;
59  
60  import java.awt.BorderLayout;
61  import java.awt.CardLayout;
62  import java.awt.Dimension;
63  import java.awt.EventQueue;
64  import java.awt.FlowLayout;
65  import java.awt.GridBagConstraints;
66  import java.awt.GridBagLayout;
67  import java.awt.Insets;
68  import java.awt.geom.Point2D;
69  import java.awt.event.ActionEvent;
70  import java.awt.event.ActionListener;
71  import java.net.URL;
72  import java.util.HashSet;
73  import java.util.Iterator;
74  import java.util.Locale;
75  
76  import javax.swing.AbstractAction;
77  import javax.swing.BorderFactory;
78  import javax.swing.Box;
79  import javax.swing.ButtonGroup;
80  import javax.swing.JPanel;
81  import javax.swing.JCheckBox;
82  import javax.swing.JComponent;
83  import javax.swing.JLabel;
84  import javax.swing.JRadioButton;
85  import javax.swing.JSpinner;
86  import javax.swing.JSplitPane;
87  import javax.swing.SpinnerNumberModel;
88  import javax.swing.Timer;
89  import javax.swing.Action;
90  import javax.swing.border.CompoundBorder;
91  import javax.swing.event.ChangeEvent;
92  import javax.swing.event.ChangeListener;
93  
94  
116 public class LocalViewDemo extends DemoBase {
117   
121   private static final int TIMER_DELAY = 100;
122 
123   
127   private static final byte SELECTION_TRIGGER = 1;
128   
132   private static final byte HOVER_TRIGGER = 2;
133 
134 
135   private final Graph2DView localView;
136 
137     private final Neighborhood neighborhood;
139   private final CommonParentGroup commonParentGroup;
140   private final AncestorGroups ancestorGroups;
141   private final FolderContents folderContents;
142 
143     private final SourceAndTarget sourceAndTarget;
145   private final EdgeGroup edgeGroup;
146 
147     private final SelectedSubgraph selectedSubgraph;
149 
150     private LocalViewCreator currentLocalViewCreator;
152     private AbstractLocalViewCreator nodeLocalViewCreator;
154     private AbstractLocalViewCreator edgeLocalViewCreator;
156 
157 
158       private final SelectionTrigger selectionTrigger;
161       private final HoverTrigger hoverTrigger;
164 
165     private byte triggerType;
167 
168 
169   public LocalViewDemo() {
170     this(null);
171   }
172 
173   public LocalViewDemo( final String helpFilePath ) {
174     final Graph2D graph = view.getGraph2D();
175     (new HierarchyManager(graph)).addHierarchyListener(
176             new GroupNodeRealizer.StateChangeListener());
177 
178 
179                     final GraphCopier.CopyFactory factory = graph.getGraphCopyFactory();
184     final Graph2D localViewGraph = (Graph2D) factory.createGraph();
185 
186     localView = new Graph2DView(localViewGraph);
187     localView.setPreferredSize(new Dimension(320, 240));
188 
189     final Layouter layouter = createLayouter();
190     neighborhood = new Neighborhood(graph, factory, localViewGraph);
191     neighborhood.setLayouter(layouter);
192     commonParentGroup = new CommonParentGroup(graph, factory, localViewGraph);
193     ancestorGroups = new AncestorGroups(graph, factory, localViewGraph);
194     folderContents = new FolderContents(graph, factory, localViewGraph);
195 
196     sourceAndTarget = new SourceAndTarget(graph, factory, localViewGraph);
197     sourceAndTarget.setLayouter(layouter);
198     edgeGroup = new EdgeGroup(graph, factory, localViewGraph);
199     edgeGroup.setLayouter(layouter);
200 
201     selectedSubgraph = new SelectedSubgraph(graph, factory, localViewGraph);
202 
203 
204     setEdgeLocalViewCreator(sourceAndTarget);
205     setNodeLocalViewCreator(neighborhood);
206 
207 
208         loadGraph("resource/LocalViewDemo.graphml");
210 
211 
212             localView.addViewMode(new ViewMode() {
215       public void mouseClicked(double x, double y) {
216                 if (lastClickEvent.getClickCount() == 2) {
218           final HitInfo hitInfo = getHitInfo(x, y);
219           final Graph2DView view = LocalViewDemo.this.view;
220           final Graph2D viewGraph = view.getGraph2D();
221                     if (hitInfo.getHitNode() != null) {
223                         final LocalViewCreator lvc = getCurrentLocalViewCreator();
225             final Node modelNode = lvc.getModelNode(hitInfo.getHitNode());
226             if (modelNode != null) {
227               if (modelNode.getGraph() == viewGraph) {
228                                 viewGraph.unselectAll();
230                 final NodeRealizer nr = viewGraph.getRealizer(modelNode);
231                 Point2D.Double center = new Point2D.Double(nr.getCenterX(), nr.getCenterY());
232                 double minZoom = Math.min(view.getWidth() / (nr.getWidth() + 40),
233                     view.getHeight() / (nr.getHeight() + 40));
234                 view.focusView(Math.min(minZoom, view.getZoom()), center, true);
235                 viewGraph.setSelected(modelNode, true);
236                 viewGraph.updateViews();
237               }
238             }
239           } else if (hitInfo.getHitEdge() != null) {                         final LocalViewCreator lvc = getCurrentLocalViewCreator();
242             final Edge modelEdge = lvc.getModelEdge(hitInfo.getHitEdge());
243             if (modelEdge != null) {
244               if(modelEdge.getGraph() == viewGraph) {
245                                 final NodeRealizer snr = viewGraph.getRealizer(modelEdge.source());
247                 final NodeRealizer tnr = viewGraph.getRealizer(modelEdge.target());
248                 Point2D.Double sCenter = new Point2D.Double(snr.getCenterX(), snr.getCenterY());
249                 Point2D.Double tCenter = new Point2D.Double(tnr.getCenterX(), tnr.getCenterY());
250                 double minZoom =
251                     Math.min(
252                         view.getWidth()/(snr.getWidth() + tnr.getWidth() + Math.abs(sCenter.x - tCenter.x)),
253                         view.getHeight() / (snr.getHeight() + tnr.getHeight() + Math.abs(sCenter.y - tCenter.y)));
254                 view.focusView(Math.min(view.getZoom(), minZoom), new Point2D.Double((sCenter.x + tCenter.x) * 0.5d, (sCenter.y + tCenter.y) * 0.5d), true);
255                 viewGraph.unselectAll();
256                 viewGraph.setSelected(modelEdge, true);
257                 viewGraph.updateViews();
258               }
259             }
260           }
261         }
262       }
263     });
264 
265     localView.setFitContentOnResize(true);
266     
267     selectionTrigger = new SelectionTrigger();
268     hoverTrigger = new HoverTrigger();
269 
270     final JSplitPane localViewAndSettings = new JSplitPane(
271             JSplitPane.VERTICAL_SPLIT, localView, createSettingsComponent());
272     localViewAndSettings.setBorder(BorderFactory.createEmptyBorder());
273     localViewAndSettings.setResizeWeight(1);
274     final JSplitPane center = new JSplitPane(
275             JSplitPane.HORIZONTAL_SPLIT, localViewAndSettings, view);
276     center.setBorder(BorderFactory.createEmptyBorder());
277     center.setResizeWeight(0);
278     
279     contentPane.add(center, BorderLayout.CENTER);
280     addHelpPane(helpFilePath);
281 
282         setTrigger(SELECTION_TRIGGER);
284 
285 
286         if (!graph.isEmpty()) {
288       graph.unselectAll();
289       for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
290         if (nc.node().inDegree() > 0 && nc.node().outDegree() > 0) {
291           graph.setSelected(nc.node(), true);
292           break;
293         }
294       }
295       if (graph.isSelectionEmpty()) {
296         graph.setSelected(graph.firstNode(), true);
297       }
298       graph.updateViews();
299     }
300   }
301 
302   
305   protected EditMode createEditMode() {
306      EditMode editMode = super.createEditMode();
307      editMode.allowEdgeCreation(false);
308      editMode.allowNodeCreation(false);
309      return editMode;
310   }
311 
312   
315   protected boolean isDeletionEnabled() {
316     return false;
317   }
318 
319   
322   protected boolean isUndoRedoEnabled() {
323     return false;
324   }
325 
326   
329   protected boolean isClipboardEnabled() {
330     return false;
331   }
332 
333   
336   protected Action createSaveAction() {
337     return null;
338   }
339 
340   
343   protected void loadGraph( final URL resource ) {
344     if (resource != null && localView != null) {
345       localView.getGraph2D().clear();
346     }
347 
348     super.loadGraph(resource);
349 
350     if (resource != null && localView != null) {
351       localView.updateView();
352     }
353   }
354 
355   
364   private void setTrigger( final byte triggerType ) {
365         switch (this.triggerType) {
367       case SELECTION_TRIGGER:
368         view.getGraph2D().removeGraph2DSelectionListener(selectionTrigger);
369         break;
370       case HOVER_TRIGGER:
371         view.removeViewMode(hoverTrigger);
372         break;
373     }
374 
375     this.triggerType = triggerType;
376 
377         switch (this.triggerType) {
379       case SELECTION_TRIGGER:
380         view.getGraph2D().addGraph2DSelectionListener(selectionTrigger);
381         break;
382       case HOVER_TRIGGER:
383         view.addViewMode(hoverTrigger);
384         break;
385     }
386   }
387 
388 
389   
393   private void setEdgeLocalViewCreator( final AbstractLocalViewCreator elvc ) {
394     final boolean update = currentLocalViewCreator == edgeLocalViewCreator;
395 
396     edgeLocalViewCreator = elvc;
397 
398         updateEdgeViewImpl(update);
400   }
401 
402   
406   private void updateEdgeView() {
407     updateEdgeViewImpl(currentLocalViewCreator == edgeLocalViewCreator);
408   }
409 
410   private void updateEdgeViewImpl( final boolean update ) {
411     if (update) {
412       if (SELECTION_TRIGGER == triggerType) {
413         createEdgeView(view.getGraph2D().selectedEdges());
414       } else {
415         createEdgeView(new EdgeList().edges());
416       }
417     }
418   }
419 
420   
424   private void setNodeLocalViewCreator( final AbstractLocalViewCreator nlvc ) {
425     final boolean update = currentLocalViewCreator == nodeLocalViewCreator;
426 
427     nodeLocalViewCreator = nlvc;
428 
429         updateNodeViewImpl(update);
431   }
432 
433   
437   private void updateNodeView() {
438     updateNodeViewImpl(currentLocalViewCreator == nodeLocalViewCreator);
439   }
440 
441   private void updateNodeViewImpl( final boolean update ) {
442     if (update) {
443       if (SELECTION_TRIGGER == triggerType) {
444         createNodeView(view.getGraph2D().selectedNodes());
445       } else {
446         createNodeView((new NodeList().nodes()));
447       }
448     }
449   }
450 
451   
456   private void createEdgeView( final YCursor edges ) {
457             currentLocalViewCreator = edgeLocalViewCreator;
460 
461             edgeLocalViewCreator.clearFocusEdges();
464     for (; edges.ok(); edges.next()) {
465       edgeLocalViewCreator.addFocusEdge((Edge) edges.current());
466     }
467 
468                                     edgeLocalViewCreator.updateViewGraph();
477   }
478 
479   
484   private void createNodeView( final YCursor nodes ) {
485             currentLocalViewCreator = nodeLocalViewCreator;
488 
489             nodeLocalViewCreator.clearFocusNodes();
492     for (; nodes.ok(); nodes.next()) {
493       nodeLocalViewCreator.addFocusNode((Node) nodes.current());
494     }
495 
496                                     nodeLocalViewCreator.updateViewGraph();
505   }
506 
507   
511   private LocalViewCreator getCurrentLocalViewCreator() {
512     return currentLocalViewCreator;
513   }
514 
515   
521   private JComponent createSettingsComponent() {
522     final JPanel settingsPane = new JPanel(new CardLayout());
523 
524     final Box nodeStrategyButtons = Box.createVerticalBox();
525     nodeStrategyButtons.setBorder(BorderFactory.createTitledBorder("Node Strategies"));
526 
527     final ButtonGroup nodeStrategiesGroup = new ButtonGroup();
528 
529         addStrategy(settingsPane, nodeStrategyButtons, nodeStrategiesGroup, "Neighborhood", createNeighborhoodSettings(), neighborhood, true);
531     addStrategy(settingsPane, nodeStrategyButtons, nodeStrategiesGroup, "Common Parent Group", createCommonParentGroupSettings(), commonParentGroup, true);
532     addStrategy(settingsPane, nodeStrategyButtons, nodeStrategiesGroup, "Ancestor Groups", createAncestorGroupsSettings(), ancestorGroups, true);
533     addStrategy(settingsPane, nodeStrategyButtons, nodeStrategiesGroup, "Folder Contents", createFolderContentsSettings(), folderContents, true);
534     addStrategy(settingsPane, nodeStrategyButtons, nodeStrategiesGroup, "Selected Subgraph", null, selectedSubgraph, true);
535 
536 
537     final ButtonGroup edgeStrategiesGroup = new ButtonGroup();
538     final Box edgeStrategyButtons = Box.createVerticalBox();
539     edgeStrategyButtons.setBorder(BorderFactory.createTitledBorder("Edge Strategies"));
540 
541         addStrategy(settingsPane, edgeStrategyButtons, edgeStrategiesGroup, "Source and Target", createSourceAndTargetSettings(), sourceAndTarget, false);
543     addStrategy(settingsPane, edgeStrategyButtons, edgeStrategiesGroup, "Edge Group", createEdgeGroupSettings(), edgeGroup, false);
544 
545 
546     Box container = Box.createVerticalBox();
547     container.add(createTriggerPane());
548 
549         final GridBagConstraints gbc = new GridBagConstraints();
551     final JPanel strategies = new JPanel(new GridBagLayout());
552     gbc.anchor = GridBagConstraints.NORTHWEST;
553     gbc.fill = GridBagConstraints.HORIZONTAL;
554     gbc.gridx = 0;
555     gbc.weightx = 1;
556     gbc.weighty = 0;
557     strategies.add(nodeStrategyButtons, gbc);
558     strategies.add(edgeStrategyButtons, gbc);
559     gbc.fill = GridBagConstraints.BOTH;
560     gbc.weighty = 1;
561     strategies.add(Box.createGlue(), gbc);
562 
563     Box strategy = Box.createHorizontalBox();
564     strategy.add(strategies);
565     strategy.add(settingsPane);
566 
567     container.add(strategy);
568     return container;
569   }
570 
571   
577     private JComponent createSourceAndTargetSettings() {
578     final JCheckBox hierarchyAware = new JCheckBox("Hierarchy aware");
579     hierarchyAware.addActionListener(new ActionListener() {
580       public void actionPerformed(ActionEvent e) {
581         sourceAndTarget.setHierarchyAware(hierarchyAware.isSelected());
582         updateEdgeView();
583       }
584     });
585 
586     final Box settings = Box.createVerticalBox();
587     settings.add(hierarchyAware);
588     settings.add(Box.createGlue());
589     return settings;
590   }
591 
592   
598   private JComponent createEdgeGroupSettings() {
599     final JCheckBox src = new JCheckBox("Source");
600     src.setSelected(true);
601     final JCheckBox tgt = new JCheckBox("Target");
602     tgt.setSelected(true);
603     final AbstractAction groupTypes = new AbstractAction() {
604       public void actionPerformed( final ActionEvent e ) {
605         src.setEnabled(tgt.isSelected());
606         tgt.setEnabled(src.isSelected());
607         byte types = 0;
608         if (src.isSelected()) {
609           types |= EdgeGroup.GROUP_BY_SOURCE;
610         }
611         if (tgt.isSelected()) {
612           types |= EdgeGroup.GROUP_BY_TARGET;
613         }
614         edgeGroup.setGroupByPolicy(types);
615         updateEdgeView();
616       }
617     };
618     src.addActionListener(groupTypes);
619     tgt.addActionListener(groupTypes);
620 
621     final Box settings = Box.createVerticalBox();
622     settings.add(new JLabel("Group by"));
623     settings.add(src);
624     settings.add(tgt);
625     settings.add(Box.createGlue());
626     return settings;
627   }
628 
629   
635   private JComponent createFolderContentsSettings() {
636     final ButtonGroup includeFoldersGroup = new ButtonGroup();
637     final JRadioButton always = new JRadioButton("Always Include Folders");
638     always.addActionListener(new ActionListener() {
639       public void actionPerformed( final ActionEvent e ) {
640         folderContents.setFolderPolicy(FolderContents.FOLDER_POLICY_ALWAYS);
641         updateNodeView();
642       }
643     });
644     includeFoldersGroup.add(always);
645     final JRadioButton asNeeeded = new JRadioButton("Include Folders As Needed");
646     asNeeeded.setSelected(true);
647     asNeeeded.addActionListener(new ActionListener() {
648       public void actionPerformed( final ActionEvent e ) {
649         folderContents.setFolderPolicy(FolderContents.FOLDER_POLICY_AS_NEEDED);
650         updateNodeView();
651       }
652     });
653     includeFoldersGroup.add(asNeeeded);
654     final JRadioButton never = new JRadioButton("Never Include Folders");
655     never.addActionListener(new ActionListener() {
656       public void actionPerformed( final ActionEvent e ) {
657         folderContents.setFolderPolicy(FolderContents.FOLDER_POLICY_NEVER);
658         updateNodeView();
659       }
660     });
661     includeFoldersGroup.add(never);
662 
663     final Box settings = Box.createVerticalBox();
664     settings.add(always);
665     settings.add(asNeeeded);
666     settings.add(never);
667     settings.add(Box.createGlue());
668     return settings;
669   }
670 
671   
677   private JComponent createAncestorGroupsSettings() {
678     final JCheckBox includeFocusNodes = new JCheckBox("Include Focus Nodes");
679     includeFocusNodes.setSelected(ancestorGroups.isIncludeFocusNodes());
680     includeFocusNodes.addActionListener(new AbstractAction() {
681       public void actionPerformed( final ActionEvent e ) {
682         ancestorGroups.setIncludeFocusNodes(includeFocusNodes.isSelected());
683         updateNodeView();
684       }
685     });
686 
687     final Box settings = Box.createVerticalBox();
688     settings.add(includeFocusNodes);
689     settings.add(Box.createGlue());
690     return settings;
691   }
692 
693   
699   private JComponent createCommonParentGroupSettings() {
700     final JCheckBox includeDescendants = new JCheckBox("Include Descendants");
701     includeDescendants.setSelected(commonParentGroup.isIncludeDescendants());
702     includeDescendants.addActionListener(new AbstractAction() {
703       public void actionPerformed( final ActionEvent e ) {
704         commonParentGroup.setIncludeDescendants(includeDescendants.isSelected());
705         updateNodeView();
706       }
707     });
708 
709     final Box settings = Box.createVerticalBox();
710     settings.add(includeDescendants);
711     settings.add(Box.createGlue());
712     return settings;
713   }
714 
715   
721   private JComponent createNeighborhoodSettings() {
722     final ButtonGroup nodesGroup = new ButtonGroup();
723     final JRadioButton preds = new JRadioButton("Predecessors");
724     preds.addActionListener(new ActionListener() {
725       public void actionPerformed( final ActionEvent e ) {
726         neighborhood.setNeighborhoodType(Neighborhood.NEIGHBORHOOD_TYPE_PREDECESSORS);
727         updateNodeView();
728       }
729     });
730     nodesGroup.add(preds);
731     final JRadioButton succs = new JRadioButton("Successors");
732     succs.addActionListener(new ActionListener() {
733       public void actionPerformed( final ActionEvent e ) {
734         neighborhood.setNeighborhoodType(Neighborhood.NEIGHBORHOOD_TYPE_SUCCESSORS);
735         updateNodeView();
736       }
737     });
738     nodesGroup.add(succs);
739     final JRadioButton predsSuccs = new JRadioButton("Predecessors and Successors");
740     predsSuccs.setSelected(true);
741     predsSuccs.addActionListener(new ActionListener() {
742       public void actionPerformed( final ActionEvent e ) {
743         neighborhood.setNeighborhoodType(Neighborhood.NEIGHBORHOOD_TYPE_PREDECESSORS_AND_SUCCESSORS);
744         updateNodeView();
745       }
746     });
747     nodesGroup.add(predsSuccs);
748     final JRadioButton neighbors = new JRadioButton("All Neighbors");
749     neighbors.addActionListener(new ActionListener() {
750       public void actionPerformed( final ActionEvent e ) {
751         neighborhood.setNeighborhoodType(Neighborhood.NEIGHBORHOOD_TYPE_NEIGHBORS);
752         updateNodeView();
753       }
754     });
755     nodesGroup.add(neighbors);
756 
757     final JSpinner neighborhoodMGD = new JSpinner();
758     neighborhoodMGD.getModel().setValue(new Integer(neighborhood.getMaximumGraphDistance()));
759     ((SpinnerNumberModel) neighborhoodMGD.getModel()).setMinimum(new Integer(0));
760     neighborhoodMGD.addChangeListener(new ChangeListener() {
761       public void stateChanged( final ChangeEvent e ) {
762         final Object value = neighborhoodMGD.getModel().getValue();
763         if (value instanceof Integer) {
764           final int mgd = ((Integer) value).intValue();
765           if (mgd > -1) {
766             neighborhood.setMaximumGraphDistance(mgd);
767             updateNodeView();
768           }
769         }
770       }
771     });
772 
773     final JCheckBox hierarchyAware = new JCheckBox("Hierarchy aware");
774     hierarchyAware.addActionListener(new ActionListener() {
775       public void actionPerformed(ActionEvent e) {
776         neighborhood.setHierarchyAware(hierarchyAware.isSelected());
777         updateNodeView();
778       }
779     });
780 
781     final ButtonGroup edgesGroup = new ButtonGroup();
782     final JRadioButton none = new JRadioButton("None");
783     none.addActionListener(new ActionListener() {
784       public void actionPerformed( final ActionEvent e ) {
785         neighborhood.setEdgePolicy(Neighborhood.EDGE_POLICY_NONE);
786         updateNodeView();
787       }
788     });
789     edgesGroup.add(none);
790     final JRadioButton subgraph = new JRadioButton("Subgraph Edges");
791     subgraph.addActionListener(new ActionListener() {
792       public void actionPerformed( final ActionEvent e ) {
793         neighborhood.setEdgePolicy(Neighborhood.EDGE_POLICY_INDUCED_SUBGRAPH);
794         updateNodeView();
795       }
796     });
797     edgesGroup.add(subgraph);
798     final JRadioButton shortestPaths = new JRadioButton("Shortest Paths");
799     shortestPaths.setSelected(true);
800     shortestPaths.addActionListener(new ActionListener() {
801       public void actionPerformed( final ActionEvent e ) {
802         neighborhood.setEdgePolicy(Neighborhood.EDGE_POLICY_SHORTEST_PATHS);
803         updateNodeView();
804       }
805     });
806     edgesGroup.add(shortestPaths);
807 
808 
809     final Box nodeTypes = Box.createVerticalBox();
810     nodeTypes.add(preds);
811     nodeTypes.add(succs);
812     nodeTypes.add(predsSuccs);
813     nodeTypes.add(neighbors);
814 
815     final GridBagConstraints gbc = new GridBagConstraints();
816     final JPanel includedNodes = new JPanel(new GridBagLayout());
817     includedNodes.setBorder(BorderFactory.createTitledBorder("Included Nodes"));
818     gbc.anchor = GridBagConstraints.NORTHWEST;
819     gbc.fill = GridBagConstraints.HORIZONTAL;
820     gbc.gridx = 0;
821     gbc.gridy = 0;
822     gbc.gridwidth = 2;
823     gbc.gridheight = 1;
824     gbc.weightx = 1;
825     gbc.weighty = 0;
826     includedNodes.add(nodeTypes, gbc);
827     gbc.fill = GridBagConstraints.NONE;
828     gbc.gridy = 1;
829     gbc.gridwidth = 1;
830     gbc.insets = new Insets(5, 0, 0, 0);
831     gbc.weightx = 0;
832     includedNodes.add(new JLabel("Maximum Graph Distance"), gbc);
833     gbc.fill = GridBagConstraints.HORIZONTAL;
834     gbc.gridx = 1;
835     gbc.insets = new Insets(5, 5, 0, 0);
836     gbc.weightx = 1;
837     includedNodes.add(neighborhoodMGD, gbc);
838     gbc.gridwidth = 2;
839     gbc.gridx = 0;
840     gbc.gridy = 2;
841     gbc.insets = new Insets(0, 0, 5, 0);
842     includedNodes.add(hierarchyAware, gbc);
843 
844     final Box includedEdges = Box.createVerticalBox();
845     includedEdges.setBorder(BorderFactory.createTitledBorder("Included Edges"));
846     includedEdges.add(none);
847     includedEdges.add(subgraph);
848     includedEdges.add(shortestPaths);
849 
850     final JPanel controls = new JPanel(new GridBagLayout());
851     gbc.gridx = 0;
852     gbc.gridy = 0;
853     gbc.gridwidth = 1;
854     gbc.gridheight = 1;
855     gbc.weightx = 1;
856     gbc.weighty = 0;
857     controls.add(includedNodes, gbc);
858     gbc.gridy = 1;
859     controls.add(includedEdges, gbc);
860 
861     final JPanel settings = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
862     settings.add(controls);
863     return settings;
864   }
865 
866   
872   private JComponent createTriggerPane() {
873     final ButtonGroup updateGroup = new ButtonGroup();
874     final JRadioButton updateHover = new JRadioButton(new AbstractAction("Mouse Hover") {
875       public void actionPerformed( final ActionEvent e ) {
876         setTrigger(HOVER_TRIGGER);
877       }
878     });
879     updateGroup.add(updateHover);
880     final JRadioButton updateSelection = new JRadioButton(new AbstractAction("Selection Change") {
881       public void actionPerformed( final ActionEvent e ) {
882         setTrigger(SELECTION_TRIGGER);
883       }
884     });
885     updateSelection.setSelected(true);
886     updateGroup.add(updateSelection);
887 
888     final Box updatePane = Box.createHorizontalBox();
889     updatePane.add(Box.createHorizontalStrut(5));
890     updatePane.add(new JLabel("Update Trigger: "));
891     updatePane.add(updateHover);
892     updatePane.add(updateSelection);
893     updatePane.add(Box.createGlue());
894     return updatePane;
895   }
896 
897   
906   private void addStrategy(
907           final JPanel settingsPane,
908           final Box strategyButtons,
909           final ButtonGroup strategiesGroup,
910           final String title,
911           JComponent settings,
912           final AbstractLocalViewCreator lvc,
913           final boolean nodeBased
914   ) {
915     if (settings == null) {
916       settings = new JPanel();
917       settings.add(new JLabel("No Settings"));
918     }
919     final JRadioButton strategyButton = new JRadioButton(new AbstractAction(title) {
920       public void actionPerformed(ActionEvent e) {
921         ((CardLayout) settingsPane.getLayout()).show(settingsPane, title);
922         if (nodeBased) {
923           setNodeLocalViewCreator(lvc);
924         } else {
925           setEdgeLocalViewCreator(lvc);
926         }
927       }
928     });
929 
930     final CompoundBorder border = BorderFactory.createCompoundBorder(
931         BorderFactory.createTitledBorder(title + " Settings"),
932         BorderFactory.createEmptyBorder(5, 5, 5, 5));
933     settings.setBorder(border);
934     settingsPane.add(settings, title);
935 
936     strategiesGroup.add(strategyButton);
937     if (strategiesGroup.getButtonCount() == 1) {
938       strategiesGroup.setSelected(strategyButton.getModel(), true);
939     }
940     strategyButtons.add(strategyButton);
941   }
942 
943   
949   private Layouter createLayouter() {
950                 final IncrementalHierarchicLayouter layouter = new IncrementalHierarchicLayouter();
954     layouter.setLayoutOrientation(LayoutOrientation.BOTTOM_TO_TOP);
955 
956             final EdgeLayoutDescriptor eld = layouter.getEdgeLayoutDescriptor();
959     eld.setRoutingStyle(new RoutingStyle(RoutingStyle.EDGE_STYLE_ORTHOGONAL));
960     eld.setMinimumFirstSegmentLength(25);
961     eld.setMinimumLength(35);
962     return layouter;
963   }
964 
965   public static void main( String[] args ) {
966     EventQueue.invokeLater(new Runnable() {
967       public void run() {
968         Locale.setDefault(Locale.ENGLISH);
969         initLnF();
970         (new LocalViewDemo("resource/localviewhelp.html")).start();
971       }
972     });
973   }
974 
975   
979   private final class SelectionTrigger implements Graph2DSelectionListener {
980     private final Timer timer;
981     private Graph2DSelectionEvent lastEvent;
982 
983     SelectionTrigger() {
984       timer = new Timer(TIMER_DELAY, new ActionListener() {
985         public void actionPerformed( final ActionEvent e ) {
986           if (lastEvent != null) {
987             handleEvent(lastEvent);
988           }
989         }
990 
991         
994         private void handleEvent( final Graph2DSelectionEvent e ) {
995           if (e.isNodeSelection()) {
996             createNodeView(e.getGraph2D().selectedNodes());
997           } else if (e.isEdgeSelection()) {
998             createEdgeView(e.getGraph2D().selectedEdges());
999           }
1000        }
1001      });
1002      timer.setRepeats(false);
1003    }
1004
1005    public void onGraph2DSelectionEvent( final Graph2DSelectionEvent e ) {
1006      if (e.getGraph2D() == getCurrentLocalViewCreator().getModel()) {
1007        if (e.isNodeSelection() || e.isEdgeSelection()) {
1008          lastEvent = e;
1009          timer.restart();
1010        }
1011      }
1012    }
1013  }
1014
1015  
1019  private final class HoverTrigger extends ViewMode {
1020    private final Timer timer;
1021    private YPoint lastPosition;
1022
1023    private HoverTrigger() {
1024      timer = new Timer(TIMER_DELAY, new ActionListener() {
1025        public void actionPerformed( ActionEvent e) {
1026          if (lastPosition != null) {
1027            handleHit(getHitInfo(lastPosition.x, lastPosition.y));
1028          }
1029        }
1030
1031        
1034        private void handleHit( final HitInfo hitInfo ) {
1035          if (hitInfo.hasHitNodes()) {
1036            createNodeView(hitInfo.hitNodes());
1037          } else if (hitInfo.hasHitEdges()) {
1038            createEdgeView(hitInfo.hitEdges());
1039          }
1040        }
1041      });
1042      timer.setRepeats(false);
1043    }
1044
1045    public void mouseMoved(double x, double y) {
1046      super.mouseMoved(x, y);
1047      this.lastPosition = new YPoint(x, y);
1048      timer.restart();
1049    }
1050  }
1051
1052  
1058  private static final class SelectedSubgraph extends AbstractLocalViewCreator {
1059    private final Graph2D model;
1060    private final GraphCopier.CopyFactory factory;
1061    private final Graph2D view;
1062
1063    
1074    SelectedSubgraph(
1075            final Graph2D model,
1076            final GraphCopier.CopyFactory factory,
1077            final Graph2D view
1078    ) {
1079      super(AbstractLocalViewCreator.ELEMENT_TYPE_NODE);
1080      this.model = model;
1081      this.factory = factory;
1082      this.view = view;
1083
1084                  final ModelViewManager mvm = ModelViewManager.getInstance(model);
1087      if (!mvm.isViewGraph(view)) {
1088        mvm.addViewGraph(view, null, false, false);
1089        mvm.setCopyFactory(view, factory);
1090      }
1091    }
1092
1093    
1097    public Graph2D getModel() {
1098      return model;
1099    }
1100
1101    
1105    public Graph2D getViewGraph() {
1106      return view;
1107    }
1108
1109    
1116    public Node getModelNode( final Node view ) {
1117      return getManager().getModelNode(view);
1118    }
1119
1120    
1129    public Node getViewNode( final Node model ) {
1130      return getManager().getViewNode(model, getViewGraph());
1131    }
1132
1133    
1140    public Edge getModelEdge( final Edge view ) {
1141      return getManager().getModelEdge(view);
1142    }
1143
1144    
1153    public Edge getViewEdge( final Edge model ) {
1154      return getManager().getViewEdge(model, getViewGraph());
1155    }
1156
1157    
1163    protected Layouter createDefaultLayouter() {
1164      return null;
1165    }
1166
1167    
1170    protected void buildViewGraph() {
1171      final Graph2D model = getModel();
1172      if (!model.isSelectionEmpty()) {
1173        final HashSet nodes = new HashSet();
1174        final HashSet edges = new HashSet();
1175
1176                for (Iterator it = focusNodes(); it.hasNext();) {
1178          final Node node = (Node) it.next();
1179          if (model.isSelected(node)) {
1180            nodes.add(node);
1181          }
1182        }
1183
1184                for (EdgeCursor ec = model.edges(); ec.ok(); ec.next()) {
1186          final Edge edge = ec.edge();
1187          if (nodes.contains(edge.source()) && nodes.contains(edge.target())) {
1188            edges.add(edge);
1189          }
1190        }
1191
1192                final Graph2D view = getViewGraph();
1194        final ModelViewManager mvm = getManager();
1195
1196                                        final GraphCopier.CopyFactory oldFactory = mvm.getCopyFactory(view);
1201        if (oldFactory != factory) {
1202          mvm.setCopyFactory(view, factory);
1203        }
1204        try {
1205          mvm.synchronizeModelToViewGraph(
1206                  (new NodeList(nodes.iterator())).nodes(),
1207                  (new EdgeList(edges.iterator())).edges(),
1208                  view);
1209        } finally {
1210                    if (oldFactory != factory) {
1212            mvm.setCopyFactory(view, oldFactory);
1213          }
1214        }
1215      }
1216    }
1217
1218    private ModelViewManager getManager() {
1219      return ModelViewManager.getInstance(getModel());
1220    }
1221  }
1222}
1223