Mercurial > hg > truffle
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 } |