1
28 package demo.view.advanced.ports;
29
30 import demo.view.DemoBase;
31 import demo.view.DemoDefaults;
32 import demo.view.application.DragAndDropDemo;
33 import y.base.Edge;
34 import y.base.Node;
35 import y.base.YList;
36 import y.geom.YPoint;
37 import y.geom.YRectangle;
38 import y.layout.LayoutOrientation;
39 import y.layout.hierarchic.IncrementalHierarchicLayouter;
40 import y.view.Arrow;
41 import y.view.CreateEdgeMode;
42 import y.view.EdgeRealizer;
43 import y.view.EditMode;
44 import y.view.GenericNodeRealizer;
45 import y.view.Graph2D;
46 import y.view.HotSpotMode;
47 import y.view.LineType;
48 import y.view.MovePortMode;
49 import y.view.NodeLabel;
50 import y.view.NodePort;
51 import y.view.NodeScaledPortLocationModel;
52 import y.view.NodeRealizer;
53 import y.view.HitInfo;
54 import y.view.Drawable;
55 import y.view.AbstractCustomNodePainter;
56 import y.view.SmartNodeLabelModel;
57 import y.view.YRenderingHints;
58 import y.view.Graph2DTraversal;
59 import y.view.Graph2DLayoutExecutor;
60
61 import javax.swing.AbstractAction;
62 import javax.swing.Action;
63 import javax.swing.JList;
64 import javax.swing.JScrollPane;
65 import javax.swing.JToolBar;
66 import javax.swing.event.ListSelectionEvent;
67 import javax.swing.event.ListSelectionListener;
68 import java.awt.BorderLayout;
69 import java.awt.Color;
70 import java.awt.EventQueue;
71 import java.awt.Insets;
72 import java.awt.Graphics2D;
73 import java.awt.Rectangle;
74 import java.awt.GradientPaint;
75 import java.awt.Shape;
76 import java.awt.geom.Ellipse2D;
77 import java.awt.geom.Rectangle2D;
78 import java.awt.geom.GeneralPath;
79 import java.awt.geom.Arc2D;
80 import java.awt.geom.AffineTransform;
81 import java.awt.geom.Line2D;
82 import java.awt.event.ActionEvent;
83 import java.awt.event.MouseEvent;
84 import java.util.ArrayList;
85 import java.util.List;
86 import java.util.Locale;
87 import java.util.Map;
88 import java.util.HashMap;
89 import java.util.Iterator;
90
91
102 public class LogicGatesDemo extends DemoBase {
103 private static final String AND_GATE_NODE_CONFIGURATION = "AndGateNodeConfiguration";
104 private static final String NAND_GATE_NODE_CONFIGURATION = "NandGateNodeConfiguration";
105 private static final String NOT_GATE_NODE_CONFIGURATION = "NotGateNodeConfiguration";
106 private static final String PORT_CONFIG_INPUT = "INPUT_PORT";
107 private static final String PORT_CONFIG_OUTPUT = "OUTPUT_PORT";
108 private static final Color PORT_HIGHLIGHT_COLOR = Color.GREEN;
109 private static final Color LINE_COLOR = Color.BLACK;
110 private static final LineType LINE_TYPE = LineType.LINE_2;
111
112 public LogicGatesDemo() {
113 this(null);
114 }
115
116 public LogicGatesDemo( final String helpFilePath ) {
117 loadGraph("resource/LogicGatesDemo.graphml");
119 addHelpPane(helpFilePath);
120 }
121
122 protected JToolBar createToolBar() {
127 final IncrementalHierarchicLayouter ihl = new IncrementalHierarchicLayouter();
128 ihl.setLayoutOrientation(LayoutOrientation.LEFT_TO_RIGHT);
129 ihl.setOrthogonallyRouted(true);
130 final Graph2DLayoutExecutor executor = new Graph2DLayoutExecutor();
131 executor.getNodePortConfigurator().setAutomaticPortConstraintsEnabled(true);
133
134 final Action layoutAction = new AbstractAction(
135 "Layout", SHARED_LAYOUT_ICON) {
136 public void actionPerformed(ActionEvent e) {
137 executor.doLayout(view, ihl);
138 }
139 };
140
141 final JToolBar jtb = super.createToolBar();
142 jtb.addSeparator();
143 jtb.add(createActionControl(layoutAction));
144 return jtb;
145 }
146
147 protected void initialize() {
148 initializePortConfiguration();
149
150 List gatesNRList = new ArrayList();
152 GenericNodeRealizer.Factory gnrFactory = GenericNodeRealizer.getFactory();
153
154 Map andSymbolConfMap = gnrFactory.createDefaultConfigurationMap();
156 AndGateConfiguration andGateHandler = new AndGateConfiguration(true);
157 andSymbolConfMap.put(GenericNodeRealizer.Painter.class, andGateHandler);
158 andSymbolConfMap.put(GenericNodeRealizer.ContainsTest.class, andGateHandler);
159 gnrFactory.addConfiguration(AND_GATE_NODE_CONFIGURATION, andSymbolConfMap);
160 GenericNodeRealizer andRealizer = new GenericNodeRealizer(AND_GATE_NODE_CONFIGURATION);
161 andRealizer.setSize(100, 50);
162 andRealizer.setLineColor(Color.BLACK);
163 andRealizer.setLineType(LINE_TYPE);
164 andRealizer.setFillColor2(Color.WHITE);
165 andRealizer.setFillColor(DemoDefaults.DEFAULT_CONTRAST_COLOR);
166 configureLabelModel(andRealizer.getLabel());
167 addPort(andRealizer, new YPoint(-0.5, -0.25), PORT_CONFIG_INPUT);
168 addPort(andRealizer, new YPoint(-0.5, 0.25), PORT_CONFIG_INPUT);
169 addPort(andRealizer, new YPoint( 0.5, 0.0), PORT_CONFIG_OUTPUT);
170 gatesNRList.add(andRealizer);
171
172 Map nandSymbolConfMap = gnrFactory.createDefaultConfigurationMap();
174 AndGateConfiguration nandGateHandler = new AndGateConfiguration(false);
175 nandSymbolConfMap.put(GenericNodeRealizer.Painter.class, nandGateHandler);
176 nandSymbolConfMap.put(GenericNodeRealizer.ContainsTest.class, nandGateHandler);
177 gnrFactory.addConfiguration(NAND_GATE_NODE_CONFIGURATION, nandSymbolConfMap);
178 GenericNodeRealizer nandRealizer = new GenericNodeRealizer(NAND_GATE_NODE_CONFIGURATION);
179 nandRealizer.setSize(100, 50);
180 nandRealizer.setLineColor(Color.BLACK);
181 nandRealizer.setLineType(LINE_TYPE);
182 nandRealizer.setFillColor2(Color.WHITE);
183 nandRealizer.setFillColor(DemoDefaults.DEFAULT_CONTRAST_COLOR);
184 configureLabelModel(nandRealizer.getLabel());
185 addPort(nandRealizer, new YPoint(-0.5, -0.25), PORT_CONFIG_INPUT);
186 addPort(nandRealizer, new YPoint(-0.5, 0.25), PORT_CONFIG_INPUT);
187 addPort(nandRealizer, new YPoint( 0.5, 0.0), PORT_CONFIG_OUTPUT);
188 gatesNRList.add(nandRealizer);
189
190 Map notSymbolConfMap = gnrFactory.createDefaultConfigurationMap();
192 NotGateConfiguration notGateHandler = new NotGateConfiguration();
193 notSymbolConfMap.put(GenericNodeRealizer.Painter.class, notGateHandler);
194 notSymbolConfMap.put(GenericNodeRealizer.ContainsTest.class, notGateHandler);
195 gnrFactory.addConfiguration(NOT_GATE_NODE_CONFIGURATION, notSymbolConfMap);
196 GenericNodeRealizer notRealizer = new GenericNodeRealizer(NOT_GATE_NODE_CONFIGURATION);
197 notRealizer.getLabel().setInsets(new Insets(0, 0, 0, 15));
198 notRealizer.setSize(100, 50);
199 notRealizer.setLineColor(Color.BLACK);
200 notRealizer.setLineType(LINE_TYPE);
201 notRealizer.setFillColor2(Color.WHITE);
202 notRealizer.setFillColor(DemoDefaults.DEFAULT_CONTRAST_COLOR);
203 configureLabelModel(notRealizer.getLabel());
204 addPort(notRealizer, new YPoint(-0.5, 0.0), PORT_CONFIG_INPUT);
205 addPort(notRealizer, new YPoint( 0.5, 0.0), PORT_CONFIG_OUTPUT);
206 gatesNRList.add(notRealizer);
207
208 final Graph2D graph = this.view.getGraph2D();
209
210 final GenericNodeRealizer[] logicSymbols = new GenericNodeRealizer[gatesNRList.size()];
211 gatesNRList.toArray(logicSymbols);
212
213 EdgeRealizer er = graph.getDefaultEdgeRealizer();
215 er.setLineType(LINE_TYPE);
216 er.setLineColor(LINE_COLOR);
217 er.setArrow(Arrow.NONE);
218
219 DragAndDropDemo.DragAndDropSupport dragAndDropSupport = new DragAndDropDemo.DragAndDropSupport(logicSymbols, view);
221 dragAndDropSupport.configureSnapping(true, 30, 20, true);
222
223 final JList list = dragAndDropSupport.getList();
225 list.addListSelectionListener(new ListSelectionListener() {
226 public void valueChanged(ListSelectionEvent e) {
227 graph.setDefaultNodeRealizer(logicSymbols[list.getSelectedIndex()]);
228 }
229 });
230
231 list.setSelectedIndex(0);
233 JScrollPane scrollPane = new JScrollPane(list);
234
235 contentPane.add(scrollPane, BorderLayout.WEST);
236 }
237
238 private static void configureLabelModel(NodeLabel label) {
239 SmartNodeLabelModel model = new SmartNodeLabelModel();
240 label.setLabelModel(model, model.getDefaultParameter());
241 }
242
243 private static void addPort(GenericNodeRealizer owner, YPoint relativeOffsets, String portConfiguration) {
244 NodePort port = new NodePort();
245 owner.addPort(port);
246 port.setConfiguration(portConfiguration);
247 port.setModelParameter(new NodeScaledPortLocationModel().createScaledParameter(relativeOffsets));
248 }
249
250 private void initializePortConfiguration() {
251 final HashMap portImpls = new HashMap();
252
253 portImpls.put(NodePort.BoundsProvider.class, new NodePort.BoundsProvider() {
256 public YRectangle getBounds(NodePort port) {
257 return getLogicGateHandler(port).getBounds(port);
258 }
259
260 private LogicGateConfiguration getLogicGateHandler(NodePort port) {
261 String configuration = ((GenericNodeRealizer) port.getRealizer()).getConfiguration();
262 GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
263 return (LogicGateConfiguration) factory.getImplementation(configuration, GenericNodeRealizer.ContainsTest.class);
264 }
265 });
266 portImpls.put(NodePort.Painter.class, new NodePort.Painter() {
268 public void paint(NodePort port, Graphics2D gfx) {
269 YRectangle bounds = port.getBounds();
270 gfx.setStroke(LINE_TYPE);
272 gfx.setColor(LINE_COLOR);
273 gfx.draw(new Line2D.Double(bounds.getX(), bounds.getY(), bounds.getX() + bounds.getWidth(), bounds.getY()));
274 }
275 });
276 NodePort.getFactory().addConfiguration(PORT_CONFIG_INPUT, portImpls);
277 NodePort.getFactory().addConfiguration(PORT_CONFIG_OUTPUT, portImpls);
278 }
279
280 protected EditMode createEditMode() {
281 EditMode editMode = new HighlightOutputPortsEditMode();
282 editMode.setMoveNodePortMode(null);
283 editMode.allowMovePorts(true);
284 editMode.setOrthogonalEdgeRouting(true);
285 editMode.assignNodeLabel(false);
286
287 CreateEdgeMode createEdgeMode = new CreateEdgeMode() {
289 protected boolean acceptSourceNodePort(Node node, NodePort port, double x, double y) {
290 return isOutput(port);
292 }
293 protected boolean acceptTargetNodePort(Node node, NodePort port, double x, double y) {
294 return isInput(port);
296 }
297 protected void drawTargetPortIndicator(Graphics2D gfx, final NodePort port) {
298 if (isInput(port)) {
301 highlightPort(gfx, port);
302 }
303 }
304 };
305 createEdgeMode.setIndicatingTargetNode(true);
306 createEdgeMode.setOrthogonalEdgeCreation(true);
307 editMode.setCreateEdgeMode(createEdgeMode);
308
309 HotSpotMode hotSpotMode = new HotSpotMode() {
311 protected boolean isModifierPressed(MouseEvent me) {
312 return true;
313 }
314 };
315 editMode.setHotSpotMode(hotSpotMode);
316
317 final MovePortMode mpm = new MovePortMode() {
319 protected YList getPortCandidates(Node v, Edge e, double gridSpacing) {
320 YList result = new YList();
321 NodeRealizer nr = getGraph2D().getRealizer(v);
322 boolean newTarget = port == port.getOwner().getTargetPort();
324 for (int i = 0; i < nr.portCount(); i++) {
325 final NodePort nodePort = nr.getPort(i);
326 if ((newTarget && isInput(nodePort)) || (!newTarget && isOutput(nodePort))) {
327 result.add(nodePort.getLocation());
328 }
329 }
330 return result;
331 }
332
333 protected void drawPortCandidate(Graphics2D gfx, YPoint p, Node v, Edge e, boolean isSnapActive,
334 boolean isSnapCandidate) {
335 if (isSnapCandidate) {
337 HitInfo info = view.getHitInfoFactory().createHitInfo(p.x, p.y, Graph2DTraversal.NODE_PORTS, true);
338 NodePort hitNodePort = info.getHitNodePort();
339 if (hitNodePort != null && isInput(hitNodePort)) {
340 highlightPort(gfx, hitNodePort);
341 }
342 }
343 }
344 };
345 mpm.setChangeEdgeEnabled(true);
346 mpm.setUsingRealizerPortCandidates(true);
347 mpm.setUsingNodePortCandidates(true);
348 mpm.setSegmentSnappingEnabled(true);
349 editMode.setMovePortMode(mpm);
350
351 return editMode;
352 }
353
354 private boolean isInput(NodePort port) {
355 return port.getConfiguration().equals(PORT_CONFIG_INPUT);
356 }
357
358 private boolean isOutput(NodePort port) {
359 return port.getConfiguration().equals(PORT_CONFIG_OUTPUT);
360 }
361
362 public static void highlightPort(Graphics2D gfx, NodePort port1) {
364 final YRectangle bounds = port1.getBounds();
365 Rectangle2D.Double box = new Rectangle2D.Double(
366 bounds.getX(), bounds.getY() - 2, bounds.getWidth(), bounds.getHeight() + 4);
367 Color oldColor = gfx.getColor();
368 gfx.setColor(PORT_HIGHLIGHT_COLOR);
369 gfx.fill(box);
370 gfx.setColor(oldColor);
371 }
372
373 public static void main(String[] args) {
374 EventQueue.invokeLater(new Runnable() {
375 public void run() {
376 Locale.setDefault(Locale.ENGLISH);
377 initLnF();
378 (new LogicGatesDemo("resource/logicgateshelp.html")).start();
379 }
380 });
381 }
382
383
384 private class HighlightOutputPortsEditMode extends EditMode {
385 private Node previousNode;
386 private ArrayList portHighlightDrawables;
387
388 public HighlightOutputPortsEditMode() {
389 super();
390 portHighlightDrawables = new ArrayList();
391 }
392
393 public void mousePressed(MouseEvent e) {
394 unhighlightPorts();
395 super.mousePressed(e);
396 }
397
398 public void mouseMoved(double x, double y) {
399 super.mouseMoved(x, y);
400 final HitInfo hi = getHitInfo(x, y);
401 if (hi.hasHitNodes()) {
402 final Node node = hi.getHitNode();
404 final Graph2D graph = view.getGraph2D();
405 final NodeRealizer nr = graph.getRealizer(node);
406 if ((node != previousNode) && !nr.isSelected()) {
407 unhighlightPorts();
409 for (int i = 0; i < nr.portCount(); i++) {
410 final NodePort port = nr.getPort(i);
411 if (isOutput(port)) {
412 highlight(port);
413 }
414 }
415 view.updateView();
416 previousNode = node;
417 }
418 } else {
419 previousNode = null;
420 unhighlightPorts();
421 }
422 }
423
424 private void highlight(NodePort port) {
425 PortHighlightDrawable portHighlightDrawable = new PortHighlightDrawable(port);
426 portHighlightDrawables.add(portHighlightDrawable);
427 view.addDrawable(portHighlightDrawable);
428 }
429
430 private void unhighlightPorts() {
431 if (!portHighlightDrawables.isEmpty()) {
432 for (Iterator ports = portHighlightDrawables.iterator(); ports.hasNext();) {
433 PortHighlightDrawable currPortHighlightDrawable = (PortHighlightDrawable) ports.next();
434 view.removeDrawable(currPortHighlightDrawable);
435 }
436 portHighlightDrawables.clear();
437 view.updateView();
438 }
439 }
440 }
441
442
443 private static final class PortHighlightDrawable implements Drawable {
444 private NodePort port;
445
446 public PortHighlightDrawable(NodePort port) {
447 this.port = port;
448 }
449
450 public void paint(Graphics2D gfx) {
451 highlightPort(gfx, port);
452 }
453
454 public Rectangle getBounds() {
455 YRectangle rect = port.getBounds();
456 return new Rectangle((int) rect.getX(), (int) rect.getY(), (int) rect.getWidth(), (int) rect.getHeight());
457 }
458 }
459
460
466 private abstract static class LogicGateConfiguration
467 extends AbstractCustomNodePainter implements GenericNodeRealizer.ContainsTest {
468
469 private static final double REALLY_SMALL_HEIGHT = 0.001;
472
473 private static final Rectangle2D.Double rawBounds = new Rectangle2D.Double(0, 0, 120, 60);
475
476 protected GeneralPath symbol;
478
479 private boolean inverted;
481
482 protected LogicGateConfiguration(boolean inverted) {
483 super();
484 this.inverted = inverted;
485 }
486
487 public boolean isInverted() {
488 return inverted;
489 }
490
491 protected GeneralPath getSymbol() {
492 return symbol;
493 }
494
495 protected Rectangle2D.Double getRawBounds() {
496 return rawBounds;
497 }
498
499 protected void paintNode(NodeRealizer context, Graphics2D graphics, boolean sloppy) {
501 Shape shape = getTransformedSymbol(context);
502 final boolean useSelectionStyle = useSelectionStyle(context, graphics);
503 Color fillColor1 = getFillColor(context, useSelectionStyle);
504 Color fillColor2 = getFillColor2(context, useSelectionStyle);
505 if (fillColor2 != null && useGradientStyle(graphics)) {
506 double x = context.getX();
507 double y = context.getY();
508 double width = context.getWidth();
509 double height = context.getHeight();
510 GradientPaint gp = new GradientPaint(
511 (float) x, (float) y, fillColor2,
512 (float) (x + width * 0.5), (float) (y + height * 0.5), fillColor1);
513 graphics.setPaint(gp);
514 } else {
515 graphics.setColor(fillColor1);
516 }
517 graphics.fill(shape);
518 graphics.setStroke(getLineStroke(context, useSelectionStyle));
519 graphics.setColor(getLineColor(context, useSelectionStyle));
520 graphics.draw(shape);
521 }
522
523 public boolean contains(NodeRealizer context, double x, double y) {
526 if (!context.getBoundingBox().contains(x, y)) {
527 return false;
530 }
531 if (getTransformedSymbol(context).contains(x, y)) {
532 return true;
533 }
534 for (int i = 0; i < context.portCount(); i++) {
536 NodePort port = context.getPort(i);
537 YRectangle bounds = port.getBounds();
538 double tolerance = 4;
539 if (bounds.x - tolerance <= x && x <= bounds.x + bounds.width + tolerance &&
540 bounds.y - tolerance <= y && y <= bounds.y + bounds.height + tolerance) {
541 return true;
542 }
543 }
544 return false;
545 }
546
547 public YRectangle getBounds(NodePort port) {
551 YPoint location = port.getLocation();
552 if (port.getConfiguration().equals(PORT_CONFIG_INPUT)) {
553 double width = getXScale(port.getRealizer()) * getLeftOffset();
554 return new YRectangle(location.getX(), location.getY(), width, REALLY_SMALL_HEIGHT);
555 } else {
556 double width = getXScale(port.getRealizer()) * getRightOffset();
557 return new YRectangle(location.getX() - width, location.getY(), width, REALLY_SMALL_HEIGHT);
558 }
559 }
560
561 static boolean useGradientStyle( final Graphics2D graphics ) {
562 return YRenderingHints.isGradientPaintingEnabled(graphics);
563 }
564
565 static boolean useSelectionStyle(
566 final NodeRealizer context,
567 final Graphics2D gfx
568 ) {
569 return context.isSelected() &&
570 YRenderingHints.isSelectionPaintingEnabled(gfx);
571 }
572
573 private Shape getTransformedSymbol(NodeRealizer context) {
576 AffineTransform transform = new AffineTransform();
577 Rectangle2D.Double bounds = getRawBounds();
578 transform.translate(context.getX() - bounds.getX(), context.getY() - bounds.getY());
579 transform.scale(getXScale(context), getYScale(context));
580 return getSymbol().createTransformedShape(transform);
581 }
582
583 protected double getYScale(NodeRealizer context) {
584 return context.getHeight() / getRawBounds().getHeight();
585 }
586
587 protected double getXScale(NodeRealizer context) {
588 return context.getWidth() / getRawBounds().getWidth();
589 }
590
591 private double getLeftOffset() {
592 return getSymbol().getBounds2D().getX() - getRawBounds().getX();
593 }
594
595 private double getRightOffset() {
596 return getRawBounds().getMaxX() - getSymbol().getBounds2D().getMaxX();
597 }
598 }
599
600 private static class AndGateConfiguration extends LogicGateConfiguration {
601 public AndGateConfiguration(boolean inverted) {
602 super(inverted);
603
604 symbol = new GeneralPath();
605 symbol.moveTo(70f, 0f);
606 symbol.lineTo(20f, 0f);
607 symbol.lineTo(20f, 60f);
608 symbol.lineTo(70f, 60f);
609 symbol.append(new Arc2D.Double(40, 0, 60, 60, 270, 180, Arc2D.OPEN), true);
610
611 if (isInverted()) {
613 Ellipse2D negationSymbol = new Ellipse2D.Double();
614 negationSymbol.setFrame(100, 26, 8, 8);
615 symbol.append(negationSymbol, false);
616 }
617 }
618 }
619
620 private static class NotGateConfiguration extends LogicGateConfiguration {
621 public NotGateConfiguration() {
622 super(true);
623
624 symbol = new GeneralPath();
625 symbol.moveTo(30f, 0f);
626 symbol.lineTo(90f, 30f);
627 symbol.lineTo(30f, 60f);
628 symbol.closePath();
629
630 Ellipse2D negationSymbol = new Ellipse2D.Double();
631 negationSymbol.setFrame(90, 26, 8, 8);
632 symbol.append(negationSymbol, false);
633 }
634 }
635 }
636