comparison visualizer/View/src/com/sun/hotspot/igv/view/scene/DiagramScene.java @ 4512:015fb895586b

Moved visualizer to new directory.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Tue, 07 Feb 2012 22:41:09 +0100
parents
children
comparison
equal deleted inserted replaced
4511:6cb549627941 4512:015fb895586b
1 /*
2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24 package com.sun.hotspot.igv.view.scene;
25
26 import com.oracle.graal.visualizer.editor.DiagramViewModel;
27 import com.oracle.graal.visualizer.sharedactions.ExportSVGCookie;
28 import com.oracle.graal.visualizer.sharedactions.ZoomCookie;
29 import com.sun.hotspot.igv.data.ChangedListener;
30 import com.sun.hotspot.igv.data.Pair;
31 import com.sun.hotspot.igv.data.Properties;
32 import com.sun.hotspot.igv.graph.*;
33 import com.sun.hotspot.igv.hierarchicallayout.HierarchicalLayoutManager;
34 import com.sun.hotspot.igv.layout.LayoutGraph;
35 import com.sun.hotspot.igv.util.ColorIcon;
36 import com.sun.hotspot.igv.util.DoubleClickAction;
37 import com.sun.hotspot.igv.util.PropertiesSheet;
38 import com.sun.hotspot.igv.view.widgets.*;
39 import java.awt.*;
40 import java.awt.event.*;
41 import java.util.List;
42 import java.util.*;
43 import javax.swing.*;
44 import org.netbeans.api.visual.action.*;
45 import org.netbeans.api.visual.animator.SceneAnimator;
46 import org.netbeans.api.visual.layout.LayoutFactory;
47 import org.netbeans.api.visual.model.*;
48 import org.netbeans.api.visual.widget.LayerWidget;
49 import org.netbeans.api.visual.widget.Widget;
50 import org.openide.nodes.AbstractNode;
51 import org.openide.nodes.Children;
52 import org.openide.nodes.Sheet;
53 import org.openide.util.Lookup;
54 import org.openide.util.lookup.AbstractLookup;
55 import org.openide.util.lookup.InstanceContent;
56
57 public class DiagramScene extends ObjectScene implements ExportSVGCookie, ZoomCookie {
58
59 private CustomizablePanWidgetAction panAction;
60 private WidgetAction hoverAction;
61 private WidgetAction selectAction;
62 private Lookup lookup;
63 private InstanceContent content;
64 private Action[] actions;
65 private LayerWidget connectionLayer;
66 private JScrollPane scrollPane;
67 private LayerWidget mainLayer;
68 private LayerWidget blockLayer;
69 private Widget topLeft;
70 private Widget bottomRight;
71 private DiagramViewModel model;
72 private WidgetAction zoomAction;
73 private boolean rebuilding;
74 /**
75 * The alpha level of partially visible figures.
76 */
77 public static final float ALPHA = 0.4f;
78 /**
79 * The offset of the graph to the border of the window showing it.
80 */
81 public static final int BORDER_SIZE = 20;
82 public static final int UNDOREDO_LIMIT = 100;
83 public static final int SCROLL_UNIT_INCREMENT = 80;
84 public static final int SCROLL_BLOCK_INCREMENT = 400;
85 public static final float ZOOM_MAX_FACTOR = 3.0f;
86 public static final float ZOOM_MIN_FACTOR = 0.0f;//0.15f;
87 public static final float ZOOM_INCREMENT = 1.5f;
88 public static final int SLOT_OFFSET = 6;
89 public static final int ANIMATION_LIMIT = 40;
90 private PopupMenuProvider popupMenuProvider = new PopupMenuProvider() {
91
92 @Override
93 public JPopupMenu getPopupMenu(Widget widget, Point localLocation) {
94 return DiagramScene.this.createPopupMenu();
95 }
96 };
97 private RectangularSelectDecorator rectangularSelectDecorator = new RectangularSelectDecorator() {
98
99 @Override
100 public Widget createSelectionWidget() {
101 Widget widget = new Widget(DiagramScene.this);
102 widget.setBorder(BorderFactory.createLineBorder(Color.black, 2));
103 widget.setForeground(Color.red);
104 return widget;
105 }
106 };
107
108 @SuppressWarnings("unchecked")
109 public <T> T getWidget(Object o) {
110 Widget w = this.findWidget(o);
111 return (T) w;
112 }
113
114 @SuppressWarnings("unchecked")
115 public <T> T getWidget(Object o, Class<T> klass) {
116 Widget w = this.findWidget(o);
117 return (T) w;
118 }
119
120 public void zoomOut() {
121 double zoom = getZoomFactor();
122 Point viewPosition = getScrollPane().getViewport().getViewPosition();
123 double newZoom = zoom / DiagramScene.ZOOM_INCREMENT;
124 if (newZoom > DiagramScene.ZOOM_MIN_FACTOR) {
125 setZoomFactor(newZoom);
126 validate();
127 getScrollPane().getViewport().setViewPosition(new Point((int) (viewPosition.x / DiagramScene.ZOOM_INCREMENT), (int) (viewPosition.y / DiagramScene.ZOOM_INCREMENT)));
128 }
129 }
130
131 public void zoomIn() {
132
133 double zoom = getZoomFactor();
134 Point viewPosition = getScrollPane().getViewport().getViewPosition();
135 double newZoom = zoom * DiagramScene.ZOOM_INCREMENT;
136 if (newZoom < DiagramScene.ZOOM_MAX_FACTOR) {
137 setZoomFactor(newZoom);
138 validate();
139 getScrollPane().getViewport().setViewPosition(new Point((int) (viewPosition.x * DiagramScene.ZOOM_INCREMENT), (int) (viewPosition.y * DiagramScene.ZOOM_INCREMENT)));
140 }
141 }
142
143 private void centerFigures(Collection<Figure> list) {
144 gotoFigures(list);
145 }
146
147 private RectangularSelectProvider rectangularSelectProvider = new RectangularSelectProvider() {
148
149 @Override
150 public void performSelection(Rectangle rectangle) {
151 if (rectangle.width < 0) {
152 rectangle.x += rectangle.width;
153 rectangle.width *= -1;
154 }
155
156 if (rectangle.height < 0) {
157 rectangle.y += rectangle.height;
158 rectangle.height *= -1;
159 }
160
161 Set<Object> selectedObjects = new HashSet<>();
162 for (Figure f : getModel().getDiagramToView().getFigures()) {
163 FigureWidget w = getWidget(f);
164 if (w != null) {
165 Rectangle r = new Rectangle(w.getBounds());
166 r.setLocation(w.getLocation());
167
168 if (r.intersects(rectangle)) {
169 selectedObjects.add(f);
170 }
171
172 for (Slot s : f.getSlots()) {
173 SlotWidget sw = getWidget(s);
174 Rectangle r2 = new Rectangle(sw.getBounds());
175 r2.setLocation(sw.convertLocalToScene(new Point(0, 0)));
176
177 if (r2.intersects(rectangle)) {
178 selectedObjects.add(s);
179 }
180 }
181 } else {
182 assert false : "w should not be null here!";
183 }
184 }
185
186 setSelectedObjects(selectedObjects);
187 }
188 };
189 private MouseWheelListener mouseWheelListener = new MouseWheelListener() {
190
191 @Override
192 public void mouseWheelMoved(MouseWheelEvent e) {
193 if (e.isControlDown()) {
194 DiagramScene.this.relayoutWithoutLayout(null);
195 }
196 }
197 };
198
199 public Point getScrollPosition() {
200 return getScrollPane().getViewport().getViewPosition();
201 }
202
203 public void setScrollPosition(Point p) {
204 getScrollPane().getViewport().setViewPosition(p);
205 }
206
207 private JScrollPane createScrollPane() {
208 JComponent comp = this.createView();
209 comp.setDoubleBuffered(true);
210 comp.setBackground(Color.WHITE);
211 comp.setOpaque(true);
212 this.setBackground(Color.WHITE);
213 this.setOpaque(true);
214 JScrollPane result = new JScrollPane(comp);
215 result.setBackground(Color.WHITE);
216 result.getVerticalScrollBar().setUnitIncrement(SCROLL_UNIT_INCREMENT);
217 result.getVerticalScrollBar().setBlockIncrement(SCROLL_BLOCK_INCREMENT);
218 result.getHorizontalScrollBar().setUnitIncrement(SCROLL_UNIT_INCREMENT);
219 result.getHorizontalScrollBar().setBlockIncrement(SCROLL_BLOCK_INCREMENT);
220 return result;
221 }
222 private ObjectSceneListener selectionChangedListener = new ObjectSceneListener() {
223
224 @Override
225 public void objectAdded(ObjectSceneEvent arg0, Object arg1) {
226 }
227
228 @Override
229 public void objectRemoved(ObjectSceneEvent arg0, Object arg1) {
230 }
231
232 @Override
233 public void objectStateChanged(ObjectSceneEvent e, Object o, ObjectState oldState, ObjectState newState) {
234 }
235
236 @Override
237 public void selectionChanged(ObjectSceneEvent e, Set<Object> oldSet, Set<Object> newSet) {
238 DiagramScene scene = (DiagramScene) e.getObjectScene();
239 if (scene.isRebuilding()) {
240 return;
241 }
242
243 content.set(newSet, null);
244
245 Set<Integer> nodeSelection = new HashSet<>();
246 for (Object o : newSet) {
247 if (o instanceof Properties.Provider) {
248 final Properties.Provider provider = (Properties.Provider) o;
249 AbstractNode node = new AbstractNode(Children.LEAF) {
250
251 @Override
252 protected Sheet createSheet() {
253 Sheet s = super.createSheet();
254 PropertiesSheet.initializeSheet(provider.getProperties(), s);
255 return s;
256 }
257 };
258 node.setDisplayName(provider.getProperties().get("name"));
259 content.add(node);
260 }
261
262
263 if (o instanceof Figure) {
264 nodeSelection.addAll(((Figure) o).getSource().getSourceNodesAsSet());
265 } else if (o instanceof Slot) {
266 nodeSelection.addAll(((Slot) o).getSource().getSourceNodesAsSet());
267 }
268 }
269 getModel().setSelectedNodes(nodeSelection);
270 }
271
272 @Override
273 public void highlightingChanged(ObjectSceneEvent e, Set<Object> oldSet, Set<Object> newSet) {
274 Set<Integer> nodeHighlighting = new HashSet<>();
275 for (Object o : newSet) {
276 if (o instanceof Figure) {
277 nodeHighlighting.addAll(((Figure) o).getSource().getSourceNodesAsSet());
278 } else if (o instanceof Slot) {
279 nodeHighlighting.addAll(((Slot) o).getSource().getSourceNodesAsSet());
280 }
281 }
282 // boolean b = highlightedCoordinatorListener.isEnabled();
283 // highlightedCoordinatorListener.setEnabled(false);
284 // SelectionCoordinator.getInstance().setHighlightedObjects(nodeHighlighting);
285 // highlightedCoordinatorListener.setEnabled(b);
286 validate();
287 }
288
289 @Override
290 public void hoverChanged(ObjectSceneEvent e, Object oldObject, Object newObject) {
291 Set<Object> newHighlightedObjects = new HashSet<>(DiagramScene.this.getHighlightedObjects());
292 if (oldObject != null) {
293 newHighlightedObjects.remove(oldObject);
294 }
295 if (newObject != null) {
296 newHighlightedObjects.add(newObject);
297 }
298 DiagramScene.this.setHighlightedObjects(newHighlightedObjects);
299 }
300
301 @Override
302 public void focusChanged(ObjectSceneEvent arg0, Object arg1, Object arg2) {
303 }
304 };
305
306 public void setActions(Action[] actions) {
307 this.actions = actions;
308 }
309
310
311
312 public DiagramScene(DiagramViewModel model) {
313
314 this.model = model;
315 content = new InstanceContent();
316 lookup = new AbstractLookup(content);
317 content.add(this);
318
319 this.setCheckClipping(true);
320
321 scrollPane = createScrollPane();
322
323 hoverAction = createObjectHoverAction();
324
325 // This panAction handles the event only when the left mouse button is
326 // pressed without any modifier keys, otherwise it will not consume it
327 // and the selection action (below) will handle the event
328 panAction = new CustomizablePanWidgetAction(~0, MouseEvent.BUTTON1_DOWN_MASK);
329 this.getActions().addAction(panAction);
330
331 selectAction = createSelectAction();
332 this.getActions().addAction(selectAction);
333
334 blockLayer = new LayerWidget(this);
335 this.addChild(blockLayer);
336
337 mainLayer = new LayerWidget(this);
338 this.addChild(mainLayer);
339
340 topLeft = new Widget(this);
341 topLeft.setPreferredLocation(new Point(-BORDER_SIZE, -BORDER_SIZE));
342 this.addChild(topLeft);
343
344 bottomRight = new Widget(this);
345 bottomRight.setPreferredLocation(new Point(-BORDER_SIZE, -BORDER_SIZE));
346 this.addChild(bottomRight);
347
348 connectionLayer = new LayerWidget(this);
349 this.addChild(connectionLayer);
350
351 LayerWidget selectionLayer = new LayerWidget(this);
352 this.addChild(selectionLayer);
353
354 this.setLayout(LayoutFactory.createAbsoluteLayout());
355
356 this.getInputBindings().setZoomActionModifiers(KeyEvent.CTRL_MASK);
357 zoomAction = ActionFactory.createMouseCenteredZoomAction(1.2);
358 this.getActions().addAction(zoomAction);
359 this.getView().addMouseWheelListener(mouseWheelListener);
360 this.getActions().addAction(ActionFactory.createPopupMenuAction(popupMenuProvider));
361
362 this.getActions().addAction(ActionFactory.createWheelPanAction());
363
364 LayerWidget selectLayer = new LayerWidget(this);
365 this.addChild(selectLayer);
366 this.getActions().addAction(ActionFactory.createRectangularSelectAction(rectangularSelectDecorator, selectLayer, rectangularSelectProvider));
367
368 this.addObjectSceneListener(selectionChangedListener, ObjectSceneEventType.OBJECT_SELECTION_CHANGED, ObjectSceneEventType.OBJECT_HIGHLIGHTING_CHANGED, ObjectSceneEventType.OBJECT_HOVER_CHANGED);
369
370 update();
371 }
372
373 public DiagramViewModel getModel() {
374 return model;
375 }
376
377 public JScrollPane getScrollPane() {
378 return scrollPane;
379 }
380
381 Component getComponent() {
382 return scrollPane;
383 }
384
385 public boolean isAllVisible() {
386 return getModel().getHiddenNodes().isEmpty();
387 }
388
389 public Action createGotoAction(final Figure f) {
390 final DiagramScene diagramScene = this;
391 Action a = new AbstractAction() {
392
393 @Override
394 public void actionPerformed(ActionEvent e) {
395 diagramScene.gotoFigure(f);
396 }
397 };
398
399 a.setEnabled(true);
400 a.putValue(Action.SMALL_ICON, new ColorIcon(f.getColor()));
401 String name = f.getLines()[0];
402
403 name += " (";
404
405 if (!this.getWidget(f, FigureWidget.class).isVisible()) {
406 name += "hidden";
407 }
408 name += ")";
409 a.putValue(Action.NAME, name);
410 return a;
411 }
412
413 private void update() {
414 mainLayer.removeChildren();
415 blockLayer.removeChildren();
416
417 rebuilding = true;
418
419 Collection<Object> objects = new ArrayList<>(this.getObjects());
420 for (Object o : objects) {
421 this.removeObject(o);
422 }
423
424 Diagram d = getModel().getDiagramToView();
425
426 for (Figure f : d.getFigures()) {
427 FigureWidget w = new FigureWidget(f, hoverAction, selectAction, this, mainLayer);
428 w.getActions().addAction(ActionFactory.createPopupMenuAction(w));
429 w.getActions().addAction(selectAction);
430 w.getActions().addAction(hoverAction);
431 w.setVisible(false);
432
433 this.addObject(f, w);
434
435 for (InputSlot s : f.getInputSlots()) {
436 SlotWidget sw = new InputSlotWidget(s, this, w, w);
437 addObject(s, sw);
438 sw.getActions().addAction(new DoubleClickAction(sw));
439 sw.getActions().addAction(hoverAction);
440 sw.getActions().addAction(selectAction);
441 }
442
443 for (OutputSlot s : f.getOutputSlots()) {
444 SlotWidget sw = new OutputSlotWidget(s, this, w, w);
445 addObject(s, sw);
446 sw.getActions().addAction(new DoubleClickAction(sw));
447 sw.getActions().addAction(hoverAction);
448 sw.getActions().addAction(selectAction);
449 }
450 }
451
452 rebuilding = false;
453 this.smallUpdate(true);
454 }
455
456 public boolean isRebuilding() {
457 return rebuilding;
458 }
459
460 private void smallUpdate(boolean relayout) {
461
462 this.updateHiddenNodes(model.getHiddenNodes(), relayout);
463 this.validate();
464 }
465
466 private boolean isVisible(Connection c) {
467 FigureWidget w1 = getWidget(c.getInputSlot().getFigure());
468 FigureWidget w2 = getWidget(c.getOutputSlot().getFigure());
469
470 if (w1.isVisible() && w2.isVisible()) {
471 return true;
472 }
473
474 return false;
475 }
476
477 private void relayout(Set<Widget> oldVisibleWidgets) {
478 System.out.println("relayout called with old visible widgets: " + oldVisibleWidgets);
479
480 Diagram diagram = getModel().getDiagramToView();
481
482 HashSet<Figure> figures = new HashSet<>();
483
484 for (Figure f : diagram.getFigures()) {
485 FigureWidget w = getWidget(f);
486 if (w.isVisible()) {
487 figures.add(f);
488 }
489 }
490
491 HashSet<Connection> edges = new HashSet<>();
492
493 for (Connection c : diagram.getConnections()) {
494 if (isVisible(c)) {
495 edges.add(c);
496 }
497 }
498
499 HierarchicalLayoutManager manager = new HierarchicalLayoutManager(HierarchicalLayoutManager.Combine.SAME_OUTPUTS);
500 manager.setMaxLayerLength(10);
501 manager.doLayout(new LayoutGraph(edges, figures));
502 relayoutWithoutLayout(oldVisibleWidgets);
503 }
504 private Set<Pair<Point, Point>> lineCache = new HashSet<>();
505
506 private void relayoutWithoutLayout(Set<Widget> oldVisibleWidgets) {
507
508 System.out.println("relayout without layout with visible widgets: " + oldVisibleWidgets);
509
510 Diagram diagram = getModel().getDiagramToView();
511
512 int maxX = -BORDER_SIZE;
513 int maxY = -BORDER_SIZE;
514 for (Figure f : diagram.getFigures()) {
515 FigureWidget w = getWidget(f);
516 if (w.isVisible()) {
517 Point p = f.getPosition();
518 Dimension d = f.getSize();
519 maxX = Math.max(maxX, p.x + d.width);
520 maxY = Math.max(maxY, p.y + d.height);
521 }
522 }
523
524 for (Connection c : diagram.getConnections()) {
525 List<Point> points = c.getControlPoints();
526 FigureWidget w1 = getWidget((Figure) c.getTo().getVertex());
527 FigureWidget w2 = getWidget((Figure) c.getFrom().getVertex());
528 if (w1.isVisible() && w2.isVisible()) {
529 for (Point p : points) {
530 if (p != null) {
531 maxX = Math.max(maxX, p.x);
532 maxY = Math.max(maxY, p.y);
533 }
534 }
535 }
536 }
537
538 bottomRight.setPreferredLocation(new Point(maxX + BORDER_SIZE, maxY + BORDER_SIZE));
539 int offx = 0;
540 int offy = 0;
541 int curWidth = maxX + 2 * BORDER_SIZE;
542 int curHeight = maxY + 2 * BORDER_SIZE;
543
544 Rectangle bounds = this.getScrollPane().getBounds();
545 bounds.width /= getZoomFactor();
546 bounds.height /= getZoomFactor();
547 if (curWidth < bounds.width) {
548 offx = (bounds.width - curWidth) / 2;
549 }
550
551 if (curHeight < bounds.height) {
552 offy = (bounds.height - curHeight) / 2;
553 }
554
555 final int offx2 = offx;
556 final int offy2 = offy;
557
558 SceneAnimator animator = this.getSceneAnimator();
559 connectionLayer.removeChildren();
560 int visibleFigureCount = 0;
561 for (Figure f : diagram.getFigures()) {
562 if (getWidget(f, FigureWidget.class).isVisible()) {
563 visibleFigureCount++;
564 }
565 }
566
567
568 Set<Pair<Point, Point>> lastLineCache = lineCache;
569 lineCache = new HashSet<>();
570 for (Figure f : diagram.getFigures()) {
571 for (OutputSlot s : f.getOutputSlots()) {
572 SceneAnimator anim = animator;
573 if (visibleFigureCount > ANIMATION_LIMIT || oldVisibleWidgets == null) {
574 anim = null;
575 }
576 processOutputSlot(lastLineCache, s, s.getConnections(), 0, null, null, offx2, offy2, anim);
577 }
578 }
579
580 for (Figure f : diagram.getFigures()) {
581 FigureWidget w = getWidget(f);
582 if (w.isVisible()) {
583 Point p = f.getPosition();
584 Point p2 = new Point(p.x + offx2, p.y + offy2);
585 if ((visibleFigureCount <= ANIMATION_LIMIT && oldVisibleWidgets != null && oldVisibleWidgets.contains(w))) {
586 animator.animatePreferredLocation(w, p2);
587 } else {
588 w.setPreferredLocation(p2);
589 animator.animatePreferredLocation(w, p2);
590 }
591 }
592 }
593
594 this.validate();
595 }
596 private final Point specialNullPoint = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
597
598 private void processOutputSlot(Set<Pair<Point, Point>> lastLineCache, OutputSlot s, List<Connection> connections, int controlPointIndex, Point lastPoint, LineWidget predecessor, int offx, int offy, SceneAnimator animator) {
599 Map<Point, List<Connection>> pointMap = new HashMap<>(connections.size());
600
601 for (Connection c : connections) {
602
603 if (!isVisible(c)) {
604 continue;
605 }
606
607 List<Point> controlPoints = c.getControlPoints();
608 if (controlPointIndex >= controlPoints.size()) {
609 continue;
610 }
611
612 Point cur = controlPoints.get(controlPointIndex);
613 if (cur == null) {
614 cur = specialNullPoint;
615 } else if (controlPointIndex == 0 && !s.shouldShowName()) {
616 cur = new Point(cur.x, cur.y - SLOT_OFFSET);
617 } else if (controlPointIndex == controlPoints.size() - 1 && !c.getInputSlot().shouldShowName()) {
618 cur = new Point(cur.x, cur.y + SLOT_OFFSET);
619 }
620
621 if (pointMap.containsKey(cur)) {
622 pointMap.get(cur).add(c);
623 } else {
624 List<Connection> newList = new ArrayList<>(2);
625 newList.add(c);
626 pointMap.put(cur, newList);
627 }
628
629 }
630
631 for (Point p : pointMap.keySet()) {
632 List<Connection> connectionList = pointMap.get(p);
633
634 boolean isBold = false;
635 boolean isDashed = true;
636
637 for (Connection c : connectionList) {
638
639 if (c.getStyle() == Connection.ConnectionStyle.BOLD) {
640 isBold = true;
641 }
642
643 if (c.getStyle() != Connection.ConnectionStyle.DASHED) {
644 isDashed = false;
645 }
646 }
647
648 LineWidget newPredecessor = predecessor;
649 if (p == specialNullPoint) {
650 } else if (lastPoint == specialNullPoint) {
651 } else if (lastPoint != null) {
652 Point p1 = new Point(lastPoint.x + offx, lastPoint.y + offy);
653 Point p2 = new Point(p.x + offx, p.y + offy);
654
655 Pair<Point, Point> curPair = new Pair<>(p1, p2);
656 SceneAnimator curAnimator = animator;
657 if (lastLineCache.contains(curPair)) {
658 curAnimator = null;
659 }
660 LineWidget w = new LineWidget(this, s, connectionList, p1, p2, predecessor, curAnimator, isBold, isDashed);
661 lineCache.add(curPair);
662
663 newPredecessor = w;
664 connectionLayer.addChild(w);
665 w.getActions().addAction(hoverAction);
666 }
667
668 processOutputSlot(lastLineCache, s, connectionList, controlPointIndex + 1, p, newPredecessor, offx, offy, animator);
669 }
670 }
671
672 @Override
673 public Lookup getLookup() {
674 return lookup;
675 }
676
677 private void gotoFigures(final Collection<Figure> figures) {
678 Rectangle overall = null;
679 getModel().showFigures(figures);
680 for (Figure f : figures) {
681
682 FigureWidget fw = getWidget(f);
683 if (fw != null) {
684 Rectangle r = fw.getBounds();
685 Point p = fw.getLocation();
686 Rectangle r2 = new Rectangle(p.x, p.y, r.width, r.height);
687
688 if (overall == null) {
689 overall = r2;
690 } else {
691 overall = overall.union(r2);
692 }
693 }
694 }
695 if (overall != null) {
696 centerRectangle(overall);
697 }
698 }
699
700 private Set<Object> idSetToObjectSet(Set<Object> ids) {
701
702 Set<Object> result = new HashSet<>();
703 for (Figure f : getModel().getDiagramToView().getFigures()) {
704 if (DiagramScene.doesIntersect(f.getSource().getSourceNodesAsSet(), ids)) {
705 result.add(f);
706 }
707
708 for (Slot s : f.getSlots()) {
709 if (DiagramScene.doesIntersect(s.getSource().getSourceNodesAsSet(), ids)) {
710 result.add(s);
711 }
712 }
713 }
714 return result;
715 }
716
717 public void gotoSelection(Set<Object> ids) {
718
719 Rectangle overall = null;
720 Set<Integer> hiddenNodes = new HashSet<>(this.getModel().getHiddenNodes());
721 hiddenNodes.removeAll(ids);
722 this.getModel().showNot(hiddenNodes);
723
724 Set<Object> objects = idSetToObjectSet(ids);
725 for (Object o : objects) {
726
727 Widget w = getWidget(o);
728 if (w != null) {
729 Rectangle r = w.getBounds();
730 Point p = w.convertLocalToScene(new Point(0, 0));
731
732 Rectangle r2 = new Rectangle(p.x, p.y, r.width, r.height);
733
734 if (overall == null) {
735 overall = r2;
736 } else {
737 overall = overall.union(r2);
738 }
739 }
740 }
741 if (overall != null) {
742 centerRectangle(overall);
743 }
744
745 setSelectedObjects(objects);
746 }
747
748 private Point calcCenter(Rectangle r) {
749
750 Point center = new Point((int) r.getCenterX(), (int) r.getCenterY());
751 center.x -= getScrollPane().getViewport().getViewRect().width / 2;
752 center.y -= getScrollPane().getViewport().getViewRect().height / 2;
753
754 // Ensure to be within area
755 center.x = Math.max(0, center.x);
756 center.x = Math.min(getScrollPane().getViewport().getViewSize().width - getScrollPane().getViewport().getViewRect().width, center.x);
757 center.y = Math.max(0, center.y);
758 center.y = Math.min(getScrollPane().getViewport().getViewSize().height - getScrollPane().getViewport().getViewRect().height, center.y);
759
760 return center;
761 }
762
763 private void centerRectangle(Rectangle r) {
764
765 if (getScrollPane().getViewport().getViewRect().width == 0 || getScrollPane().getViewport().getViewRect().height == 0) {
766 return;
767 }
768
769 Rectangle r2 = new Rectangle(r.x, r.y, r.width, r.height);
770 r2 = convertSceneToView(r2);
771
772 double factorX = (double) r2.width / (double) getScrollPane().getViewport().getViewRect().width;
773 double factorY = (double) r2.height / (double) getScrollPane().getViewport().getViewRect().height;
774 double factor = Math.max(factorX, factorY);
775 if (factor >= 1.0) {
776 Point p = getScrollPane().getViewport().getViewPosition();
777 setZoomFactor(getZoomFactor() / factor);
778 r2.x /= factor;
779 r2.y /= factor;
780 r2.width /= factor;
781 r2.height /= factor;
782 getScrollPane().getViewport().setViewPosition(calcCenter(r2));
783 } else {
784 getScrollPane().getViewport().setViewPosition(calcCenter(r2));
785 }
786 }
787
788 void setSelection(Collection<Figure> list) {
789 super.setSelectedObjects(new HashSet<>(list));
790 centerFigures(list);
791 }
792
793 private boolean isVisible(Figure f) {
794 for (Integer n : f.getSource().getSourceNodesAsSet()) {
795 if (getModel().getHiddenNodes().contains(n)) {
796 return false;
797 }
798 }
799 return true;
800 }
801
802 public static boolean doesIntersect(Set<?> s1, Set<?> s2) {
803 if (s1.size() > s2.size()) {
804 Set<?> tmp = s1;
805 s1 = s2;
806 s2 = tmp;
807 }
808
809 for (Object o : s1) {
810 if (s2.contains(o)) {
811 return true;
812 }
813 }
814
815 return false;
816 }
817
818 private void updateHiddenNodes(Set<Integer> newHiddenNodes, boolean doRelayout) {
819
820 System.out.println("newHiddenNodes: " + newHiddenNodes);
821
822 Diagram diagram = getModel().getDiagramToView();
823 assert diagram != null;
824
825 Set<Widget> oldVisibleWidgets = new HashSet<>();
826
827 for (Figure f : diagram.getFigures()) {
828 FigureWidget w = getWidget(f);
829 if (w != null && w.isVisible()) {
830 oldVisibleWidgets.add(w);
831 }
832 }
833
834 for (Figure f : diagram.getFigures()) {
835 boolean hiddenAfter = doesIntersect(f.getSource().getSourceNodesAsSet(), newHiddenNodes);
836
837 FigureWidget w = getWidget(f);
838 w.setBoundary(false);
839 if (!hiddenAfter) {
840 // Figure is shown
841 w.setVisible(true);
842 } else {
843 // Figure is hidden
844 w.setVisible(false);
845 }
846 }
847
848 if (getModel().getShowNodeHull()) {
849 List<FigureWidget> boundaries = new ArrayList<>();
850 for (Figure f : diagram.getFigures()) {
851 FigureWidget w = getWidget(f);
852 if (!w.isVisible()) {
853 Set<Figure> set = new HashSet<>(f.getPredecessorSet());
854 set.addAll(f.getSuccessorSet());
855
856 boolean b = false;
857 for (Figure neighbor : set) {
858 FigureWidget neighborWidget = getWidget(neighbor);
859 if (neighborWidget.isVisible()) {
860 b = true;
861 break;
862 }
863 }
864
865 if (b) {
866 w.setBoundary(true);
867 boundaries.add(w);
868 }
869 }
870 }
871
872 for (FigureWidget w : boundaries) {
873 if (w.isBoundary()) {
874 w.setVisible(true);
875 }
876 }
877 }
878
879 if (doRelayout) {
880 relayout(oldVisibleWidgets);
881 }
882 this.validate();
883 }
884
885 private void showFigure(Figure f) {
886 HashSet<Integer> newHiddenNodes = new HashSet<>(getModel().getHiddenNodes());
887 newHiddenNodes.removeAll(f.getSource().getSourceNodesAsSet());
888 updateHiddenNodes(newHiddenNodes, true);
889 }
890
891 public void show(final Figure f) {
892 showFigure(f);
893 }
894
895 public void setSelectedObjects(Object... args) {
896 Set<Object> set = new HashSet<>();
897 for (Object o : args) {
898 set.add(o);
899 }
900 super.setSelectedObjects(set);
901 }
902
903 private void centerWidget(Widget w) {
904 Rectangle r = w.getBounds();
905 Point p = w.getLocation();
906 centerRectangle(new Rectangle(p.x, p.y, r.width, r.height));
907 }
908
909 public void gotoFigure(final Figure f) {
910 if (!isVisible(f)) {
911 showFigure(f);
912 }
913
914 FigureWidget fw = getWidget(f);
915 if (fw != null) {
916 setSelection(new HashSet<>(Arrays.asList(f)));
917 }
918 }
919
920 public JPopupMenu createPopupMenu() {
921 JPopupMenu menu = new JPopupMenu();
922 for (Action a : actions) {
923 if (a == null) {
924 menu.addSeparator();
925 } else {
926 menu.add(a);
927 }
928 }
929 return menu;
930 }
931
932 private final ChangedListener<DiagramViewModel> fullChange = new ChangedListener<DiagramViewModel>() {
933
934 @Override
935 public void changed(DiagramViewModel source) {
936 assert source == model : "Receive only changed event from current model!";
937 assert source != null;
938 update();
939 }
940 };
941 private final ChangedListener<DiagramViewModel> hiddenNodesChange = new ChangedListener<DiagramViewModel>() {
942
943 @Override
944 public void changed(DiagramViewModel source) {
945 assert source == model : "Receive only changed event from current model!";
946 assert source != null;
947 smallUpdate(true);
948 }
949 };
950 private final ChangedListener<DiagramViewModel> selectionChange = new ChangedListener<DiagramViewModel>() {
951
952 @Override
953 public void changed(DiagramViewModel source) {
954 assert source == model : "Receive only changed event from current model!";
955 assert source != null;
956 smallUpdate(false);
957 }
958 };
959
960 @Override
961 public void showAll() {
962 // TODO(tw): Implement.
963 }
964 }