1
14 package demo.layout.hierarchic;
15
16 import demo.view.DemoBase;
17 import y.base.Edge;
18 import y.base.EdgeCursor;
19 import y.base.EdgeMap;
20 import y.base.Node;
21 import y.base.NodeCursor;
22 import y.base.NodeMap;
23 import y.base.Graph;
24 import y.base.DataProvider;
25 import y.layout.LabelLayoutConstants;
26 import y.layout.PortConstraint;
27 import y.layout.PortConstraintConfigurator;
28 import y.layout.PortConstraintKeys;
29 import y.layout.hierarchic.ClassicLayerSequencer;
30 import y.module.HierarchicLayoutModule;
31 import y.view.Arrow;
32 import y.view.EdgeLabel;
33 import y.view.EdgeRealizer;
34 import y.view.EditMode;
35 import y.view.Graph2D;
36 import y.view.Graph2DSelectionEvent;
37 import y.view.Graph2DSelectionListener;
38 import y.view.PortAssignmentMoveSelectionMode;
39 import y.io.GraphMLIOHandler;
40 import y.io.graphml.output.OutputHandlerProvider;
41 import y.io.graphml.output.QueryOutputHandlersEvent;
42 import y.io.graphml.output.GraphMLWriteException;
43 import y.io.graphml.graph2d.PortConstraintOutputHandler;
44 import y.io.graphml.graph2d.PortConstraintInputHandler;
45 import y.io.graphml.KeyScope;
46 import y.io.graphml.input.InputHandlerProvider;
47 import y.io.graphml.input.QueryInputHandlersEvent;
48 import y.io.graphml.input.GraphMLParseException;
49 import y.util.Maps;
50
51 import javax.swing.AbstractAction;
52 import javax.swing.Action;
53 import javax.swing.BorderFactory;
54 import javax.swing.ButtonGroup;
55 import javax.swing.ButtonModel;
56 import javax.swing.JButton;
57 import javax.swing.JCheckBox;
58 import javax.swing.JComboBox;
59 import javax.swing.JPanel;
60 import javax.swing.JRadioButton;
61 import javax.swing.JScrollPane;
62 import javax.swing.JToolBar;
63 import java.awt.BorderLayout;
64 import java.awt.Color;
65 import java.awt.GridBagConstraints;
66 import java.awt.GridBagLayout;
67 import java.awt.EventQueue;
68 import java.awt.event.ActionEvent;
69 import java.awt.event.ActionListener;
70 import java.util.Locale;
71 import java.util.WeakHashMap;
72
73 import org.w3c.dom.Element;
74 import y.view.SmartEdgeLabelModel;
75
76
162
163 public class HierarchicLayouterDemo extends DemoBase {
164 private static final String EDGE_LABELING = "EDGE_LABELING";
165 private static final String HIERARCHIC = "HIERARCHIC";
166 private static final String NONE = "NONE";
167
168 private PortSpec sourceSpec, targetSpec;
169 private EdgeMap sourceGroupIdMap, targetGroupIdMap;
170 private JCheckBox labelBox;
171 private EdgeMap sourcePortMap;
172 private EdgeMap targetPortMap;
173
174 private HierarchicLayoutModule layoutModule;
175
176 private NodeMap groupMap;
177 private PortAssignmentMoveSelectionMode paMode;
178
179 public HierarchicLayouterDemo() {
180 final Graph2D graph = view.getGraph2D();
181 groupMap = graph.createNodeMap();
182 graph.addDataProvider(ClassicLayerSequencer.GROUP_KEY, groupMap);
183 EdgeRealizer defaultER = graph.getDefaultEdgeRealizer();
184 defaultER.setArrow(Arrow.STANDARD);
185
186 sourcePortMap = graph.createEdgeMap();
187 targetPortMap = graph.createEdgeMap();
188 sourceGroupIdMap = graph.createEdgeMap();
189 targetGroupIdMap = graph.createEdgeMap();
190 graph.addDataProvider(PortConstraintKeys.SOURCE_GROUPID_KEY, sourceGroupIdMap);
191 graph.addDataProvider(PortConstraintKeys.TARGET_GROUPID_KEY, targetGroupIdMap);
192 graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, sourcePortMap);
193 graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, targetPortMap);
194
195 paMode.setSpc(sourcePortMap);
196 paMode.setTpc(targetPortMap);
197
198
199 JPanel left = new JPanel(new GridBagLayout());
200 GridBagConstraints gbc = new GridBagConstraints();
201 gbc.fill = GridBagConstraints.BOTH;
202 gbc.anchor = GridBagConstraints.NORTHWEST;
203 gbc.gridx = 0;
204
205 left.add(new JButton(new AbstractAction("Parse Ports") {
206 public void actionPerformed(ActionEvent ae) {
207 new PortConstraintConfigurator()
208 .createPortConstraintsFromSketch(graph, sourcePortMap, targetPortMap);
209 }
210 }), gbc);
211
212 gbc.gridx = 1;
213
214 labelBox = new JCheckBox("Label Edges");
215 labelBox.addActionListener(new ActionListener() {
216 public void actionPerformed(ActionEvent ev) {
217 setLabelEdges(labelBox.isSelected());
218 }
219 });
220
221 left.add(labelBox);
222
223 sourceSpec = new PortSpec("Source Port", sourcePortMap, sourceGroupIdMap);
224 targetSpec = new PortSpec("Target Port", targetPortMap, targetGroupIdMap);
225 gbc.gridx = 0;
226 gbc.gridy = 1;
227 left.add(sourceSpec, gbc);
228 gbc.gridx = 1;
229 left.add(targetSpec, gbc);
230 gbc.gridx = 0;
231 gbc.gridy = GridBagConstraints.RELATIVE;
232
233 JPanel groupSpec;
234 groupSpec = new JPanel(new GridBagLayout());
235 groupSpec.setBorder(BorderFactory.createTitledBorder("Node Groups"));
236
237 Color[] groupColors = new Color[]{null, Color.blue, Color.yellow, Color.red};
239
240 for (int i = 0; i < groupColors.length; i++) {
241 JButton groupButton = new GroupButton(groupColors[i], i);
242 groupSpec.add(groupButton, gbc);
243 }
244 gbc.weightx = gbc.weighty = 1;
245 groupSpec.add(new JPanel(), gbc);
246 gbc.weightx = gbc.weighty = 0;
247 gbc.gridwidth = 2;
248 left.add(groupSpec, gbc);
249 gbc.gridwidth = 1;
250
251 gbc.weighty = 1.0d;
252 left.add(new JPanel(), gbc);
253
254 contentPane.add(new JScrollPane(left), BorderLayout.WEST);
255
256 graph.addGraph2DSelectionListener(new Graph2DSelectionListener() {
257 public void onGraph2DSelectionEvent(Graph2DSelectionEvent ev) {
258 if (ev.getSubject() instanceof Edge) {
259 Edge e = (Edge) ev.getSubject();
260 if (ev.getGraph2D().isSelected(e)) {
261 PortConstraint pc = getSPC(e);
262 sourceSpec.setSide(pc.getSide());
263 sourceSpec.setStrong(pc.isStrong());
264 sourceSpec.setGroupId(sourceGroupIdMap.get(e));
265 pc = getTPC(e);
266 targetSpec.setSide(pc.getSide());
267 targetSpec.setStrong(pc.isStrong());
268 targetSpec.setGroupId(targetGroupIdMap.get(e));
269 }
270 }
271 }
272 });
273
274 layoutModule = new HierarchicLayoutModule();
275 loadGraph("resource/portConstraints.graphml");
276 }
277
278 protected JToolBar createToolBar() {
279 final Action layoutAction = new AbstractAction(
280 "Layout", SHARED_LAYOUT_ICON) {
281 public void actionPerformed(ActionEvent e) {
282 layoutModule.start(view.getGraph2D());
283 }
284 };
285 final Action preferencesAction = new AbstractAction(
286 "Settings...", getIconResource("resource/properties.png")) {
287 public void actionPerformed(ActionEvent e) {
288 OptionSupport.showDialog(layoutModule, view.getGraph2D(), false, view.getFrame());
289 }
290 };
291
292 JToolBar toolBar = super.createToolBar();
293 toolBar.addSeparator();
294 toolBar.add(createActionControl(layoutAction));
295 toolBar.add(createActionControl(preferencesAction));
296 return toolBar;
297 }
298
299
303 protected void assignGroup(Color color, int index) {
304 Graph2D graph = view.getGraph2D();
305 for (NodeCursor nc = graph.selectedNodes(); nc.ok(); nc.next()) {
306 Node n = nc.node();
307 if (color == null) {
308 color = graph.getDefaultNodeRealizer().getFillColor();
309 groupMap.set(n, null);
311 } else {
312 groupMap.setInt(n, index);
314 }
315 graph.getRealizer(n).setFillColor(color);
317
318 }
319 graph.updateViews();
320 }
321
322
323 class GroupButton extends JButton implements ActionListener {
325 Color color;
326 int index;
327
328 GroupButton(Color color, int index) {
329 super("");
330 setText(index > 0 ? "Group " + index : "No Group");
331 this.color = color;
332 this.index = index;
333 setBackground(color);
334 this.addActionListener(this);
335 }
336
337 public void actionPerformed(ActionEvent e) {
338 HierarchicLayouterDemo.this.assignGroup(color, index);
339 }
340 }
341
342
343 PortConstraint getSPC(Edge e) {
344 PortConstraint pc = (e != null) ? (PortConstraint) sourcePortMap.get(e) : null;
345
346 if (pc == null) {
347 pc = PortConstraint.create(PortConstraint.ANY_SIDE);
348 }
349
350 return pc;
351 }
352
353 PortConstraint getTPC(Edge e) {
354 PortConstraint pc = (e != null) ? (PortConstraint) targetPortMap.get(e) : null;
355
356 if (pc == null) {
357 pc = PortConstraint.create(PortConstraint.ANY_SIDE);
358 }
359
360 return pc;
361 }
362
363 void setPC(EdgeMap portMap, EdgeMap groupMap, byte side, boolean strong, Object groupId) {
364 Graph2D graph = view.getGraph2D();
365 PortConstraint pc = PortConstraint.create(side, strong);
366 String preText = pc.toString();
367 if (groupId != null) {
368 preText = preText + '[' + groupId + ']';
369 }
370 for (EdgeCursor ec = graph.selectedEdges(); ec.ok(); ec.next()) {
371 Edge e = ec.edge();
372 portMap.set(e, pc);
373 getSPC(e);
374 getTPC(e);
375 groupMap.set(e, groupId);
376 if (graph.getRealizer(e).labelCount() >= 2) {
377 String text = portMap == sourcePortMap ? "source " + preText : "target" + preText;
378 graph.getRealizer(e).getLabel(portMap == sourcePortMap ? 0 : 1).setText(text);
379 }
380 }
381 graph.updateViews();
382 }
383
384
387 public static void main(String[] args) {
388 EventQueue.invokeLater(new Runnable() {
389 public void run() {
390 Locale.setDefault(Locale.ENGLISH);
391 initLnF();
392 (new HierarchicLayouterDemo()).start("Hierarchic Layouter Demo");
393 }
394 });
395 }
396
397
398 protected GraphMLIOHandler createGraphMLIOHandler() {
399 GraphMLIOHandler ioh = super.createGraphMLIOHandler();
400 ioh.getGraphMLHandler().addOutputHandlerProvider(new OutputHandlerProvider() {
401 public void onQueryOutputHandler(QueryOutputHandlersEvent event) throws GraphMLWriteException {
402 boolean isRegistered = false;
403 Graph g = event.getContext().getGraph();
404 Object[] keys = g.getDataProviderKeys();
405 for (int i = 0; i < keys.length; i++) {
406 Object key = keys[i];
407 if(PortConstraintKeys.SOURCE_GROUPID_KEY.equals(key) ||
408 PortConstraintKeys.TARGET_GROUPID_KEY.equals(key) ||
409 PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY.equals(key) ||
410 PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY.equals(key)) {
411 isRegistered = true;
412 break;
413 }
414 }
415 if (isRegistered) {
416 event.addOutputHandler(new PortConstraintOutputHandler(), KeyScope.EDGE);
417 }
418 }
419 });
420
421 ioh.getGraphMLHandler().addInputHandlerProvider(new InputHandlerProvider() {
422 public void onQueryInputHandler(QueryInputHandlersEvent event) throws GraphMLParseException {
423 Element keyDefinition = event.getKeyDefinition();
424 PortConstraintInputHandler handler = new PortConstraintInputHandler();
425
426 if (handler.acceptKey(keyDefinition)) {
427 initDataProvider(PortConstraintKeys.SOURCE_GROUPID_KEY, event.getContext().getGraph());
428 initDataProvider(PortConstraintKeys.TARGET_GROUPID_KEY, event.getContext().getGraph());
429 initDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, event.getContext().getGraph());
430 initDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, event.getContext().getGraph());
431 event.addInputHandler(handler);
432 }
433 }
434
435 private void initDataProvider(Object key, Graph graph) {
436 DataProvider dp = graph.getDataProvider(key);
437 if (dp == null || !(dp instanceof EdgeMap)) {
438 dp = Maps.createEdgeMap(new WeakHashMap());
439 graph.addDataProvider(key, dp);
440 }
441 }
442 });
443 return ioh;
444 }
445
446 class PortSpec extends JPanel {
447 JRadioButton anySideB, northB, southB, eastB, westB;
448 JCheckBox cb;
449 JComboBox box;
450 EdgeMap portMap;
451 EdgeMap groupMap;
452
453 Object[] items = new Object[]{"no Group", new Integer(1), new Integer(2), new Integer(3), new Integer(4),
454 new Integer(5), new Integer(6), new Integer(7)};
455
456 PortSpec(String title, EdgeMap portMap, EdgeMap groupMap) {
457 super(new GridBagLayout());
458 GridBagConstraints gbc = new GridBagConstraints();
459 gbc.fill = GridBagConstraints.BOTH;
460 gbc.gridx = 0;
461 gbc.anchor = GridBagConstraints.NORTHWEST;
462 this.setBorder(BorderFactory.createTitledBorder(title));
463 this.portMap = portMap;
464 this.groupMap = groupMap;
465 final ButtonGroup bg = new ButtonGroup();
466
467
468 anySideB = new JRadioButton("ANY_SIDE");
469 northB = new JRadioButton("NORTH");
470 southB = new JRadioButton("SOUTH");
471 eastB = new JRadioButton("EAST");
472 westB = new JRadioButton("WEST");
473 anySideB.setSelected(true);
474
475 cb = new JCheckBox("Strong");
476
477
478 box = new JComboBox(items);
479
480 ActionListener rl = new ActionListener() {
481 public void actionPerformed(ActionEvent ev) {
482 ButtonModel bm = bg.getSelection();
483 byte side = PortConstraint.ANY_SIDE;
484 if (bm == southB.getModel()) {
485 side = PortConstraint.SOUTH;
486 } else if (bm == northB.getModel()) {
487 side = PortConstraint.NORTH;
488 } else if (bm == eastB.getModel()) {
489 side = PortConstraint.EAST;
490 } else if (bm == westB.getModel()) {
491 side = PortConstraint.WEST;
492 }
493 boolean strong = cb.isSelected();
494 Object groupId = box.getSelectedItem();
495 if (groupId instanceof String) {
496 groupId = null;
497 }
498 setPC(PortSpec.this.portMap, PortSpec.this.groupMap, side, strong, groupId);
499 }
500 };
501
502 addButton(anySideB, bg, rl, gbc);
503 addButton(northB, bg, rl, gbc);
504 addButton(southB, bg, rl, gbc);
505 addButton(eastB, bg, rl, gbc);
506 addButton(westB, bg, rl, gbc);
507 this.add(cb, gbc);
508 this.add(box, gbc);
509 box.addActionListener(rl);
510 cb.addActionListener(rl);
511 }
512
513 void addButton(JRadioButton b, ButtonGroup bg, ActionListener rl, GridBagConstraints gbc) {
514 bg.add(b);
515 this.add(b, gbc);
516 b.addActionListener(rl);
517 }
518
519 void setSide(byte side) {
520 switch (side) {
521 case PortConstraint.ANY_SIDE:
522 anySideB.setSelected(true);
523 break;
524 case PortConstraint.NORTH:
525 northB.setSelected(true);
526 break;
527 case PortConstraint.SOUTH:
528 southB.setSelected(true);
529 break;
530 case PortConstraint.WEST:
531 westB.setSelected(true);
532 break;
533 case PortConstraint.EAST:
534 eastB.setSelected(true);
535 break;
536 }
537 }
538
539 void setStrong(boolean strong) {
540 cb.setSelected(strong);
541 }
542
543 void setGroupId(Object id) {
544 if (id == null) {
545 box.setSelectedIndex(0);
546 } else {
547 box.setSelectedItem(id);
548 }
549 }
550 }
551
552 void setLabelEdges(boolean labelEdges) {
553 Graph2D graph = view.getGraph2D();
554
555 if (labelEdges) {
556 addLabels(graph.getDefaultEdgeRealizer());
557 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
558 addLabels(graph.getRealizer(ec.edge()));
559 }
560 layoutModule.getOptionHandler().set(EDGE_LABELING, HIERARCHIC);
561 } else {
562 removeLabels(graph.getDefaultEdgeRealizer());
563 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
564 removeLabels(graph.getRealizer(ec.edge()));
565 }
566 layoutModule.getOptionHandler().set(EDGE_LABELING, NONE);
567 }
568
569 view.updateView();
570
571 }
572
573
574 void addLabels(EdgeRealizer er) {
575 removeLabels(er);
576
577 EdgeLabel el = new EdgeLabel(getSPC(er.getEdge()).toString());
578 SmartEdgeLabelModel model = new SmartEdgeLabelModel();
579 el.setLabelModel(model);
580 el.setModelParameter(model.createDiscreteModelParameter(SmartEdgeLabelModel.POSITION_SOURCE_CENTER));
581 el.setPreferredPlacement(LabelLayoutConstants.PLACE_AT_SOURCE);
582 er.addLabel(el);
583
584
585 el = new EdgeLabel(getTPC(er.getEdge()).toString());
586 model = new SmartEdgeLabelModel();
587 el.setLabelModel(model);
588 el.setModelParameter(model.createDiscreteModelParameter(SmartEdgeLabelModel.POSITION_TARGET_CENTER));
589 el.setPreferredPlacement(LabelLayoutConstants.PLACE_AT_TARGET);
590 er.addLabel(el);
591
592 el = new EdgeLabel("Center");
593 model = new SmartEdgeLabelModel();
594 el.setLabelModel(model);
595 el.setModelParameter(model.createDiscreteModelParameter(SmartEdgeLabelModel.POSITION_CENTER));
596 el.setPreferredPlacement(LabelLayoutConstants.PLACE_AT_CENTER);
597 er.addLabel(el);
598 }
599
600 void removeLabels(EdgeRealizer er) {
601 for (int i = er.labelCount(); i --> 0;) {
602 er.removeLabel(i);
603 }
604 }
605
606 protected void registerViewModes() {
607 EditMode mode = new EditMode();
608 mode.setMoveSelectionMode(paMode = new PortAssignmentMoveSelectionMode(null, null));
609 view.addViewMode(mode);
610 }
611 }
612