1
28 package demo.layout.hierarchic;
29
30 import demo.view.DemoBase;
31 import org.w3c.dom.Element;
32 import y.base.DataProvider;
33 import y.base.Edge;
34 import y.base.EdgeCursor;
35 import y.base.EdgeMap;
36 import y.base.Graph;
37 import y.base.Node;
38 import y.base.NodeCursor;
39 import y.base.NodeMap;
40 import y.io.GraphMLIOHandler;
41 import y.io.graphml.KeyScope;
42 import y.io.graphml.graph2d.PortConstraintInputHandler;
43 import y.io.graphml.graph2d.PortConstraintOutputHandler;
44 import y.io.graphml.input.GraphMLParseException;
45 import y.io.graphml.input.InputHandlerProvider;
46 import y.io.graphml.input.QueryInputHandlersEvent;
47 import y.io.graphml.output.GraphMLWriteException;
48 import y.io.graphml.output.OutputHandlerProvider;
49 import y.io.graphml.output.QueryOutputHandlersEvent;
50 import y.layout.LabelLayoutConstants;
51 import y.layout.PortConstraint;
52 import y.layout.PortConstraintConfigurator;
53 import y.layout.PortConstraintKeys;
54 import y.layout.PreferredPlacementDescriptor;
55 import y.layout.hierarchic.ClassicLayerSequencer;
56 import demo.layout.module.HierarchicLayoutModule;
57 import y.util.GraphCopier;
58 import y.util.Maps;
59 import y.view.Arrow;
60 import y.view.EdgeLabel;
61 import y.view.EdgeRealizer;
62 import y.view.EditMode;
63 import y.view.Graph2D;
64 import y.view.Graph2DClipboard;
65 import y.view.Graph2DSelectionEvent;
66 import y.view.Graph2DSelectionListener;
67 import y.view.PortAssignmentMoveSelectionMode;
68 import y.view.SmartEdgeLabelModel;
69
70 import javax.swing.AbstractAction;
71 import javax.swing.Action;
72 import javax.swing.BorderFactory;
73 import javax.swing.ButtonGroup;
74 import javax.swing.ButtonModel;
75 import javax.swing.JButton;
76 import javax.swing.JCheckBox;
77 import javax.swing.JComboBox;
78 import javax.swing.JPanel;
79 import javax.swing.JRadioButton;
80 import javax.swing.JScrollPane;
81 import javax.swing.JToolBar;
82 import java.awt.BorderLayout;
83 import java.awt.Color;
84 import java.awt.EventQueue;
85 import java.awt.GridBagConstraints;
86 import java.awt.GridBagLayout;
87 import java.awt.event.ActionEvent;
88 import java.awt.event.ActionListener;
89 import java.util.HashMap;
90 import java.util.Locale;
91 import java.util.Map;
92 import java.util.WeakHashMap;
93
94
180
181 public class HierarchicLayouterDemo extends DemoBase {
182 private static final String EDGE_LABELING = "EDGE_LABELING";
183 private static final String HIERARCHIC = "HIERARCHIC";
184 private static final String NONE = "NONE";
185
186 private PortSpec sourceSpec, targetSpec;
187 private EdgeMap sourceGroupIdMap, targetGroupIdMap;
188 private JCheckBox labelBox;
189 private EdgeMap sourcePortMap;
190 private EdgeMap targetPortMap;
191
192 private HierarchicLayoutModule layoutModule;
193
194 private NodeMap groupMap;
195 private PortAssignmentMoveSelectionMode paMode;
196
197 public HierarchicLayouterDemo() {
198 this (null);
199 }
200
201 public HierarchicLayouterDemo(String helpFilePath) {
202 final Graph2D graph = view.getGraph2D();
203 groupMap = graph.createNodeMap();
204 graph.addDataProvider(ClassicLayerSequencer.GROUP_KEY, groupMap);
205 EdgeRealizer defaultER = graph.getDefaultEdgeRealizer();
206 defaultER.setArrow(Arrow.STANDARD);
207
208 sourcePortMap = graph.createEdgeMap();
209 targetPortMap = graph.createEdgeMap();
210 sourceGroupIdMap = graph.createEdgeMap();
211 targetGroupIdMap = graph.createEdgeMap();
212 graph.addDataProvider(PortConstraintKeys.SOURCE_GROUPID_KEY, sourceGroupIdMap);
213 graph.addDataProvider(PortConstraintKeys.TARGET_GROUPID_KEY, targetGroupIdMap);
214 graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, sourcePortMap);
215 graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, targetPortMap);
216
217 paMode.setSpc(sourcePortMap);
218 paMode.setTpc(targetPortMap);
219
220
221 JPanel left = new JPanel(new GridBagLayout());
222 GridBagConstraints gbc = new GridBagConstraints();
223
224 JPanel edgeSpec = new JPanel(new GridBagLayout());
225 edgeSpec.setBorder(BorderFactory.createTitledBorder("Edge Settings"));
226 gbc.fill = GridBagConstraints.BOTH;
227 gbc.anchor = GridBagConstraints.NORTHWEST;
228 gbc.gridx = 0;
229
230 edgeSpec.add(new JButton(new AbstractAction("Parse Ports") {
231 public void actionPerformed(ActionEvent ae) {
232 new PortConstraintConfigurator()
233 .createPortConstraintsFromSketch(graph, sourcePortMap, targetPortMap);
234 }
235 }), gbc);
236
237 gbc.gridx = 1;
238
239 labelBox = new JCheckBox("Label Edges");
240 labelBox.addActionListener(new ActionListener() {
241 public void actionPerformed(ActionEvent ev) {
242 setLabelEdges(labelBox.isSelected());
243 }
244 });
245
246 edgeSpec.add(labelBox);
247
248 sourceSpec = new PortSpec("Source Port", sourcePortMap, sourceGroupIdMap);
249 targetSpec = new PortSpec("Target Port", targetPortMap, targetGroupIdMap);
250 gbc.gridx = 0;
251 gbc.gridy = 1;
252 edgeSpec.add(sourceSpec, gbc);
253 gbc.gridx = 1;
254 edgeSpec.add(targetSpec, gbc);
255 gbc.gridx = 0;
256 gbc.gridy = 0;
257 left.add(edgeSpec, gbc);
258 gbc.gridy = GridBagConstraints.RELATIVE;
259
260 JPanel groupSpec = new JPanel(new GridBagLayout());
261 groupSpec.setBorder(BorderFactory.createTitledBorder("Node Groups"));
262
263 Color[] groupColors = new Color[]{null, Color.blue, Color.yellow, Color.red};
265
266 for (int i = 0; i < groupColors.length; i++) {
267 JButton groupButton = new GroupButton(groupColors[i], i);
268 groupSpec.add(groupButton, gbc);
269 }
270 gbc.weightx = gbc.weighty = 1;
271 groupSpec.add(new JPanel(), gbc);
272 gbc.weightx = gbc.weighty = 0;
273 left.add(groupSpec, gbc);
274
275 gbc.weighty = 1.0d;
276 left.add(new JPanel(), gbc);
277
278 contentPane.add(new JScrollPane(left), BorderLayout.WEST);
279
280 graph.addGraph2DSelectionListener(new Graph2DSelectionListener() {
281 public void onGraph2DSelectionEvent(Graph2DSelectionEvent ev) {
282 if (ev.getSubject() instanceof Edge) {
283 Edge e = (Edge) ev.getSubject();
284 if (ev.getGraph2D().isSelected(e)) {
285 PortConstraint pc = getSPC(e);
286 sourceSpec.setSide(pc.getSide());
287 sourceSpec.setStrong(pc.isStrong());
288 sourceSpec.setGroupId(sourceGroupIdMap.get(e));
289 pc = getTPC(e);
290 targetSpec.setSide(pc.getSide());
291 targetSpec.setStrong(pc.isStrong());
292 targetSpec.setGroupId(targetGroupIdMap.get(e));
293 }
294 }
295 }
296 });
297
298 addHelpPane(helpFilePath);
299
300 layoutModule = new HierarchicLayoutModule();
301 loadGraph("resource/portConstraints.graphml");
302 }
303
304 protected JToolBar createToolBar() {
305 final Action layoutAction = new AbstractAction(
306 "Layout", SHARED_LAYOUT_ICON) {
307 public void actionPerformed(ActionEvent e) {
308 layoutModule.start(view.getGraph2D());
309 }
310 };
311 final Action preferencesAction = new AbstractAction(
312 "Settings...", getIconResource("resource/properties.png")) {
313 public void actionPerformed(ActionEvent e) {
314 OptionSupport.showDialog(layoutModule, view.getGraph2D(), false, view.getFrame());
315 }
316 };
317
318 JToolBar toolBar = super.createToolBar();
319 toolBar.addSeparator();
320 toolBar.add(createActionControl(layoutAction));
321 toolBar.add(createActionControl(preferencesAction));
322 return toolBar;
323 }
324
325
329 protected Graph2DClipboard getClipboard() {
330 final Graph2DClipboard clipboard = super.getClipboard();
331 clipboard.setCopyFactory(new NodeGroupGraphCopyFactory(clipboard.getCopyFactory()));
332 return clipboard;
333 }
334
335
339 protected void assignGroup(Color color, int index) {
340 Graph2D graph = view.getGraph2D();
341 graph.firePreEvent();
342 graph.backupRealizers(graph.selectedNodes());
343 try {
344 for (NodeCursor nc = graph.selectedNodes(); nc.ok(); nc.next()) {
345 Node n = nc.node();
346 if (color == null) {
347 color = graph.getDefaultNodeRealizer().getFillColor();
348 groupMap.set(n, null);
350 } else {
351 groupMap.setInt(n, index);
353 }
354 graph.getRealizer(n).setFillColor(color);
356
357 }
358 graph.updateViews();
359 } finally {
360 graph.firePostEvent();
361 }
362 }
363
364 class GroupButton extends JButton implements ActionListener {
366 Color color;
367 int index;
368
369 GroupButton(Color color, int index) {
370 super("");
371 setText(index > 0 ? "Group " + index : "No Group");
372 this.color = color;
373 this.index = index;
374 setBackground(color);
375 this.addActionListener(this);
376 }
377
378 public void actionPerformed(ActionEvent e) {
379 HierarchicLayouterDemo.this.assignGroup(color, index);
380 }
381 }
382
383
384 PortConstraint getSPC(Edge e) {
385 PortConstraint pc = (e != null) ? (PortConstraint) sourcePortMap.get(e) : null;
386
387 if (pc == null) {
388 pc = PortConstraint.create(PortConstraint.ANY_SIDE);
389 }
390
391 return pc;
392 }
393
394 PortConstraint getTPC(Edge e) {
395 PortConstraint pc = (e != null) ? (PortConstraint) targetPortMap.get(e) : null;
396
397 if (pc == null) {
398 pc = PortConstraint.create(PortConstraint.ANY_SIDE);
399 }
400
401 return pc;
402 }
403
404 void setPC(EdgeMap portMap, EdgeMap groupMap, byte side, boolean strong, Object groupId) {
405 Graph2D graph = view.getGraph2D();
406 PortConstraint pc = PortConstraint.create(side, strong);
407 String preText = pc.toString();
408 if (groupId != null) {
409 preText = preText + '[' + groupId + ']';
410 }
411 for (EdgeCursor ec = graph.selectedEdges(); ec.ok(); ec.next()) {
412 Edge e = ec.edge();
413 portMap.set(e, pc);
414 getSPC(e);
415 getTPC(e);
416 groupMap.set(e, groupId);
417 if (graph.getRealizer(e).labelCount() >= 2) {
418 String text = portMap == sourcePortMap ? "source " + preText : "target" + preText;
419 graph.getRealizer(e).getLabel(portMap == sourcePortMap ? 0 : 1).setText(text);
420 }
421 }
422 graph.updateViews();
423 }
424
425
428 public static void main(String[] args) {
429 EventQueue.invokeLater(new Runnable() {
430 public void run() {
431 Locale.setDefault(Locale.ENGLISH);
432 initLnF();
433 (new HierarchicLayouterDemo("resource/hierarchiclayouterdemohelp.html")).start("Hierarchic Layouter Demo");
434 }
435 });
436 }
437
438
439 protected GraphMLIOHandler createGraphMLIOHandler() {
440 GraphMLIOHandler ioh = super.createGraphMLIOHandler();
441 ioh.getGraphMLHandler().addOutputHandlerProvider(new OutputHandlerProvider() {
442 public void onQueryOutputHandler(QueryOutputHandlersEvent event) throws GraphMLWriteException {
443 boolean isRegistered = false;
444 Graph g = event.getContext().getGraph();
445 Object[] keys = g.getDataProviderKeys();
446 for (int i = 0; i < keys.length; i++) {
447 Object key = keys[i];
448 if(PortConstraintKeys.SOURCE_GROUPID_KEY.equals(key) ||
449 PortConstraintKeys.TARGET_GROUPID_KEY.equals(key) ||
450 PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY.equals(key) ||
451 PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY.equals(key)) {
452 isRegistered = true;
453 break;
454 }
455 }
456 if (isRegistered) {
457 event.addOutputHandler(new PortConstraintOutputHandler(), KeyScope.EDGE);
458 }
459 }
460 });
461
462 ioh.getGraphMLHandler().addInputHandlerProvider(new InputHandlerProvider() {
463 public void onQueryInputHandler(QueryInputHandlersEvent event) throws GraphMLParseException {
464 Element keyDefinition = event.getKeyDefinition();
465 PortConstraintInputHandler handler = new PortConstraintInputHandler();
466
467 if (handler.acceptKey(keyDefinition)) {
468 initDataProvider(PortConstraintKeys.SOURCE_GROUPID_KEY, event.getContext().getGraph());
469 initDataProvider(PortConstraintKeys.TARGET_GROUPID_KEY, event.getContext().getGraph());
470 initDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, event.getContext().getGraph());
471 initDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, event.getContext().getGraph());
472 event.addInputHandler(handler);
473 }
474 }
475
476 private void initDataProvider(Object key, Graph graph) {
477 DataProvider dp = graph.getDataProvider(key);
478 if (dp == null || !(dp instanceof EdgeMap)) {
479 dp = Maps.createEdgeMap(new WeakHashMap());
480 graph.addDataProvider(key, dp);
481 }
482 }
483 });
484 return ioh;
485 }
486
487 class PortSpec extends JPanel {
488 JRadioButton anySideB, northB, southB, eastB, westB;
489 JCheckBox cb;
490 JComboBox box;
491 EdgeMap portMap;
492 EdgeMap groupMap;
493
494 Object[] items = new Object[]{"no Group", new Integer(1), new Integer(2), new Integer(3), new Integer(4),
495 new Integer(5), new Integer(6), new Integer(7)};
496
497 PortSpec(String title, EdgeMap portMap, EdgeMap groupMap) {
498 super(new GridBagLayout());
499 GridBagConstraints gbc = new GridBagConstraints();
500 gbc.fill = GridBagConstraints.BOTH;
501 gbc.gridx = 0;
502 gbc.anchor = GridBagConstraints.NORTHWEST;
503 this.setBorder(BorderFactory.createTitledBorder(title));
504 this.portMap = portMap;
505 this.groupMap = groupMap;
506 final ButtonGroup bg = new ButtonGroup();
507
508
509 anySideB = new JRadioButton("ANY_SIDE");
510 northB = new JRadioButton("NORTH");
511 southB = new JRadioButton("SOUTH");
512 eastB = new JRadioButton("EAST");
513 westB = new JRadioButton("WEST");
514 anySideB.setSelected(true);
515
516 cb = new JCheckBox("Strong");
517
518
519 box = new JComboBox(items);
520
521 ActionListener rl = new ActionListener() {
522 public void actionPerformed(ActionEvent ev) {
523 ButtonModel bm = bg.getSelection();
524 byte side = PortConstraint.ANY_SIDE;
525 if (bm == southB.getModel()) {
526 side = PortConstraint.SOUTH;
527 } else if (bm == northB.getModel()) {
528 side = PortConstraint.NORTH;
529 } else if (bm == eastB.getModel()) {
530 side = PortConstraint.EAST;
531 } else if (bm == westB.getModel()) {
532 side = PortConstraint.WEST;
533 }
534 boolean strong = cb.isSelected();
535 Object groupId = box.getSelectedItem();
536 if (groupId instanceof String) {
537 groupId = null;
538 }
539 setPC(PortSpec.this.portMap, PortSpec.this.groupMap, side, strong, groupId);
540 }
541 };
542
543 addButton(anySideB, bg, rl, gbc);
544 addButton(northB, bg, rl, gbc);
545 addButton(southB, bg, rl, gbc);
546 addButton(eastB, bg, rl, gbc);
547 addButton(westB, bg, rl, gbc);
548 this.add(cb, gbc);
549 this.add(box, gbc);
550 box.addActionListener(rl);
551 cb.addActionListener(rl);
552 }
553
554 void addButton(JRadioButton b, ButtonGroup bg, ActionListener rl, GridBagConstraints gbc) {
555 bg.add(b);
556 this.add(b, gbc);
557 b.addActionListener(rl);
558 }
559
560 void setSide(byte side) {
561 switch (side) {
562 case PortConstraint.ANY_SIDE:
563 anySideB.setSelected(true);
564 break;
565 case PortConstraint.NORTH:
566 northB.setSelected(true);
567 break;
568 case PortConstraint.SOUTH:
569 southB.setSelected(true);
570 break;
571 case PortConstraint.WEST:
572 westB.setSelected(true);
573 break;
574 case PortConstraint.EAST:
575 eastB.setSelected(true);
576 break;
577 }
578 }
579
580 void setStrong(boolean strong) {
581 cb.setSelected(strong);
582 }
583
584 void setGroupId(Object id) {
585 if (id == null) {
586 box.setSelectedIndex(0);
587 } else {
588 box.setSelectedItem(id);
589 }
590 }
591 }
592
593 void setLabelEdges(boolean labelEdges) {
594 Graph2D graph = view.getGraph2D();
595
596 if (labelEdges) {
597 addLabels(graph.getDefaultEdgeRealizer());
598 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
599 addLabels(graph.getRealizer(ec.edge()));
600 }
601 layoutModule.getOptionHandler().set(EDGE_LABELING, HIERARCHIC);
602 } else {
603 removeLabels(graph.getDefaultEdgeRealizer());
604 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
605 removeLabels(graph.getRealizer(ec.edge()));
606 }
607 layoutModule.getOptionHandler().set(EDGE_LABELING, NONE);
608 }
609
610 view.updateView();
611
612 }
613
614
615 void addLabels(EdgeRealizer er) {
616 removeLabels(er);
617
618 addLabel(
619 er, getSPC(er.getEdge()).toString(),
620 SmartEdgeLabelModel.POSITION_SOURCE_CENTER,
621 LabelLayoutConstants.PLACE_AT_SOURCE);
622 addLabel(
623 er, getTPC(er.getEdge()).toString(),
624 SmartEdgeLabelModel.POSITION_TARGET_CENTER,
625 LabelLayoutConstants.PLACE_AT_TARGET);
626 addLabel(
627 er, "Center",
628 SmartEdgeLabelModel.POSITION_CENTER,
629 LabelLayoutConstants.PLACE_AT_CENTER);
630 }
631
632 static void addLabel(
633 final EdgeRealizer er,
634 final String text,
635 final int position,
636 final byte placement
637 ) {
638 final EdgeLabel el = new EdgeLabel(text);
639 final SmartEdgeLabelModel model = new SmartEdgeLabelModel();
640 el.setLabelModel(model, model.createDiscreteModelParameter(position));
641 el.setPreferredPlacementDescriptor(
642 PreferredPlacementDescriptor.newSharedInstance(placement));
643 er.addLabel(el);
644 }
645
646 static void removeLabels(EdgeRealizer er) {
647 for (int i = er.labelCount(); i --> 0;) {
648 er.removeLabel(i);
649 }
650 }
651
652 protected void registerViewModes() {
653 EditMode mode = new EditMode();
654 mode.setMoveSelectionMode(paMode = new PortAssignmentMoveSelectionMode(null, null));
655 view.addViewMode(mode);
656 }
657
658
661 private class NodeGroupGraphCopyFactory implements GraphCopier.CopyFactory {
662 private final GraphCopier.CopyFactory copyFactory;
663 private final HashMap node2group;
664
665 public NodeGroupGraphCopyFactory(GraphCopier.CopyFactory copyFactory) {
666 this.copyFactory = copyFactory;
667 node2group = new HashMap();
668 }
669
670 public Node copyNode(Graph targetGraph, Node originalNode) {
671 return copyFactory.copyNode(targetGraph, originalNode);
672 }
673
674 public Edge copyEdge(Graph targetGraph, Node newSource, Node newTarget, Edge originalEdge) {
675 return copyFactory.copyEdge(targetGraph, newSource, newTarget, originalEdge);
676 }
677
678 public Graph createGraph() {
679 return copyFactory.createGraph();
680 }
681
682 public void preCopyGraphData(Graph sourceGraph, Graph targetGraph) {
683 copyFactory.preCopyGraphData(sourceGraph, targetGraph);
684 }
685
686
690 public void postCopyGraphData(Graph sourceGraph, Graph targetGraph, Map nodeMap, Map edgeMap) {
691 copyFactory.postCopyGraphData(sourceGraph, targetGraph, nodeMap, edgeMap);
692
693 if (sourceGraph == view.getGraph2D()) {
695 node2group.clear();
698 for (NodeCursor nodeCursor = sourceGraph.nodes(); nodeCursor.ok(); nodeCursor.next()) {
699 final Node sourceNode = nodeCursor.node();
700 final Node targetNode = (Node) nodeMap.get(sourceNode);
701 if (targetNode != null) {
702 node2group.put(targetNode, groupMap.get(sourceNode));
703 }
704 }
705 } else {
706 for (NodeCursor nodeCursor = sourceGraph.nodes(); nodeCursor.ok(); nodeCursor.next()) {
709 final Node sourceNode = nodeCursor.node();
710 final Node targetNode = (Node) nodeMap.get(sourceNode);
711 groupMap.set(targetNode, node2group.get(sourceNode));
712 }
713 }
714 }
715 }
716 }
717