1 /* 2 * Copyright (c) 2008, 2016, 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; 25 26 import com.sun.hotspot.igv.data.ChangedEvent; 27 import com.sun.hotspot.igv.data.ChangedListener; 28 import com.sun.hotspot.igv.data.InputNode; 29 import com.sun.hotspot.igv.data.Properties; 30 import com.sun.hotspot.igv.data.Properties.PropertyMatcher; 31 import com.sun.hotspot.igv.data.services.InputGraphProvider; 32 import com.sun.hotspot.igv.filter.FilterChain; 33 import com.sun.hotspot.igv.filter.FilterChainProvider; 34 import com.sun.hotspot.igv.graph.Diagram; 35 import com.sun.hotspot.igv.graph.Figure; 36 import com.sun.hotspot.igv.graph.services.DiagramProvider; 37 import com.sun.hotspot.igv.svg.BatikSVG; 38 import com.sun.hotspot.igv.util.LookupHistory; 39 import com.sun.hotspot.igv.util.RangeSlider; 40 import com.sun.hotspot.igv.view.actions.*; 41 import java.awt.*; 42 import java.awt.event.HierarchyBoundsListener; 43 import java.awt.event.HierarchyEvent; 44 import java.awt.event.KeyEvent; 45 import java.awt.event.KeyListener; 46 import java.beans.PropertyChangeEvent; 47 import java.beans.PropertyChangeListener; 48 import java.io.*; 49 import java.util.List; 50 import java.util.*; 51 import javax.swing.*; 52 import javax.swing.border.Border; 53 import org.openide.DialogDisplayer; 54 import org.openide.NotifyDescriptor; 55 import org.openide.actions.RedoAction; 56 import org.openide.actions.UndoAction; 57 import org.openide.awt.Toolbar; 58 import org.openide.awt.ToolbarPool; 59 import org.openide.awt.UndoRedo; 60 import org.openide.util.Lookup; 61 import org.openide.util.NbBundle; 62 import org.openide.util.Utilities; 63 import org.openide.util.actions.Presenter; 64 import org.openide.util.lookup.AbstractLookup; 65 import org.openide.util.lookup.InstanceContent; 66 import org.openide.util.lookup.ProxyLookup; 67 import org.openide.windows.Mode; 68 import org.openide.windows.TopComponent; 69 import org.openide.windows.WindowManager; 70 71 /** 72 * 73 * @author Thomas Wuerthinger 74 */ 75 public final class EditorTopComponent extends TopComponent implements PropertyChangeListener { 76 77 private DiagramViewer scene; 78 private InstanceContent content; 79 private InstanceContent graphContent; 80 private EnableBlockLayoutAction blockLayoutAction; 81 private OverviewAction overviewAction; 82 private HideDuplicatesAction hideDuplicatesAction; 83 private PredSuccAction predSuccAction; 84 private SelectionModeAction selectionModeAction; 85 private PanModeAction panModeAction; 86 private boolean notFirstTime; 87 private JComponent satelliteComponent; 88 private JPanel centerPanel; 89 private CardLayout cardLayout; 90 private RangeSlider rangeSlider; 91 private JToggleButton overviewButton; 92 private JToggleButton hideDuplicatesButton; 93 private static final String PREFERRED_ID = "EditorTopComponent"; 94 private static final String SATELLITE_STRING = "satellite"; 95 private static final String SCENE_STRING = "scene"; 96 private DiagramViewModel rangeSliderModel; 97 private ExportCookie exportCookie = new ExportCookie() { 98 99 @Override 100 public void export(File f) { 101 102 Graphics2D svgGenerator = BatikSVG.createGraphicsObject(); 103 104 if (svgGenerator == null) { 105 NotifyDescriptor message = new NotifyDescriptor.Message("For export to SVG files the Batik SVG Toolkit must be intalled.", NotifyDescriptor.ERROR_MESSAGE); 106 DialogDisplayer.getDefault().notifyLater(message); 107 } else { 108 scene.paint(svgGenerator); 109 FileOutputStream os = null; 110 try { 111 os = new FileOutputStream(f); 112 Writer out = new OutputStreamWriter(os, "UTF-8"); 113 BatikSVG.printToStream(svgGenerator, out, true); 114 } catch (FileNotFoundException e) { 115 NotifyDescriptor message = new NotifyDescriptor.Message("For export to SVG files the Batik SVG Toolkit must be intalled.", NotifyDescriptor.ERROR_MESSAGE); 116 DialogDisplayer.getDefault().notifyLater(message); 117 118 } catch (UnsupportedEncodingException e) { 119 } finally { 120 if (os != null) { 121 try { 122 os.close(); 123 } catch (IOException e) { 124 } 125 } 126 } 127 128 } 129 } 130 }; 131 132 private DiagramProvider diagramProvider = new DiagramProvider() { 133 134 @Override 135 public Diagram getDiagram() { 136 return getModel().getDiagramToView(); 137 } 138 139 @Override 140 public ChangedEvent<DiagramProvider> getChangedEvent() { 141 return diagramChangedEvent; 142 } 143 }; 144 145 private ChangedEvent<DiagramProvider> diagramChangedEvent = new ChangedEvent<>(diagramProvider); 146 147 updateDisplayName()148 private void updateDisplayName() { 149 setDisplayName(getDiagram().getName()); 150 setToolTipText(getDiagram().getGraph().getGroup().getName()); 151 } 152 EditorTopComponent(Diagram diagram)153 public EditorTopComponent(Diagram diagram) { 154 155 LookupHistory.init(InputGraphProvider.class); 156 LookupHistory.init(DiagramProvider.class); 157 this.setFocusable(true); 158 FilterChain filterChain = null; 159 FilterChain sequence = null; 160 FilterChainProvider provider = Lookup.getDefault().lookup(FilterChainProvider.class); 161 if (provider == null) { 162 filterChain = new FilterChain(); 163 sequence = new FilterChain(); 164 } else { 165 filterChain = provider.getFilterChain(); 166 sequence = provider.getSequence(); 167 } 168 169 setName(NbBundle.getMessage(EditorTopComponent.class, "CTL_EditorTopComponent")); 170 setToolTipText(NbBundle.getMessage(EditorTopComponent.class, "HINT_EditorTopComponent")); 171 172 Action[] actions = new Action[]{ 173 PrevDiagramAction.get(PrevDiagramAction.class), 174 NextDiagramAction.get(NextDiagramAction.class), 175 null, 176 ExtractAction.get(ExtractAction.class), 177 ShowAllAction.get(HideAction.class), 178 ShowAllAction.get(ShowAllAction.class), 179 null, 180 ZoomInAction.get(ZoomInAction.class), 181 ZoomOutAction.get(ZoomOutAction.class), 182 }; 183 184 185 Action[] actionsWithSelection = new Action[]{ 186 ExtractAction.get(ExtractAction.class), 187 ShowAllAction.get(HideAction.class), 188 null, 189 ExpandPredecessorsAction.get(ExpandPredecessorsAction.class), 190 ExpandSuccessorsAction.get(ExpandSuccessorsAction.class) 191 }; 192 193 initComponents(); 194 195 ToolbarPool.getDefault().setPreferredIconSize(16); 196 Toolbar toolBar = new Toolbar(); 197 Border b = (Border) UIManager.get("Nb.Editor.Toolbar.border"); //NOI18N 198 toolBar.setBorder(b); 199 JPanel container = new JPanel(); 200 this.add(container, BorderLayout.NORTH); 201 container.setLayout(new BorderLayout()); 202 container.add(BorderLayout.NORTH, toolBar); 203 204 rangeSliderModel = new DiagramViewModel(diagram.getGraph().getGroup(), filterChain, sequence); 205 rangeSlider = new RangeSlider(); 206 rangeSlider.setModel(rangeSliderModel); 207 JScrollPane pane = new JScrollPane(rangeSlider, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); 208 container.add(BorderLayout.CENTER, pane); 209 210 scene = new DiagramScene(actions, actionsWithSelection, rangeSliderModel); 211 content = new InstanceContent(); 212 graphContent = new InstanceContent(); 213 this.associateLookup(new ProxyLookup(new Lookup[]{scene.getLookup(), new AbstractLookup(graphContent), new AbstractLookup(content)})); 214 content.add(exportCookie); 215 content.add(rangeSliderModel); 216 content.add(diagramProvider); 217 218 rangeSliderModel.getDiagramChangedEvent().addListener(diagramChangedListener); 219 rangeSliderModel.selectGraph(diagram.getGraph()); 220 rangeSliderModel.getViewPropertiesChangedEvent().addListener(new ChangedListener<DiagramViewModel>() { 221 @Override 222 public void changed(DiagramViewModel source) { 223 hideDuplicatesButton.setSelected(getModel().getHideDuplicates()); 224 hideDuplicatesAction.setState(getModel().getHideDuplicates()); 225 } 226 }); 227 228 229 toolBar.add(NextDiagramAction.get(NextDiagramAction.class)); 230 toolBar.add(PrevDiagramAction.get(PrevDiagramAction.class)); 231 toolBar.addSeparator(); 232 toolBar.add(ExtractAction.get(ExtractAction.class)); 233 toolBar.add(ShowAllAction.get(HideAction.class)); 234 toolBar.add(ShowAllAction.get(ShowAllAction.class)); 235 toolBar.addSeparator(); 236 toolBar.add(ShowAllAction.get(ZoomInAction.class)); 237 toolBar.add(ShowAllAction.get(ZoomOutAction.class)); 238 239 blockLayoutAction = new EnableBlockLayoutAction(); 240 JToggleButton button = new JToggleButton(blockLayoutAction); 241 button.setSelected(false); 242 toolBar.add(button); 243 blockLayoutAction.addPropertyChangeListener(this); 244 245 overviewAction = new OverviewAction(); 246 overviewButton = new JToggleButton(overviewAction); 247 overviewButton.setSelected(false); 248 toolBar.add(overviewButton); 249 overviewAction.addPropertyChangeListener(this); 250 251 predSuccAction = new PredSuccAction(); 252 button = new JToggleButton(predSuccAction); 253 button.setSelected(true); 254 toolBar.add(button); 255 predSuccAction.addPropertyChangeListener(this); 256 257 hideDuplicatesAction = new HideDuplicatesAction(); 258 hideDuplicatesButton = new JToggleButton(hideDuplicatesAction); 259 hideDuplicatesButton.setSelected(false); 260 toolBar.add(hideDuplicatesButton); 261 hideDuplicatesAction.addPropertyChangeListener(this); 262 263 toolBar.addSeparator(); 264 toolBar.add(UndoAction.get(UndoAction.class)); 265 toolBar.add(RedoAction.get(RedoAction.class)); 266 267 toolBar.addSeparator(); 268 ButtonGroup interactionButtons = new ButtonGroup(); 269 270 panModeAction = new PanModeAction(); 271 panModeAction.setSelected(true); 272 button = new JToggleButton(panModeAction); 273 button.setSelected(true); 274 interactionButtons.add(button); 275 toolBar.add(button); 276 panModeAction.addPropertyChangeListener(this); 277 278 selectionModeAction = new SelectionModeAction(); 279 button = new JToggleButton(selectionModeAction); 280 interactionButtons.add(button); 281 toolBar.add(button); 282 selectionModeAction.addPropertyChangeListener(this); 283 284 toolBar.add(Box.createHorizontalGlue()); 285 Action action = Utilities.actionsForPath("QuickSearchShadow").get(0); 286 Component quicksearch = ((Presenter.Toolbar) action).getToolbarPresenter(); 287 try { 288 // (aw) workaround for disappearing search bar due to reparenting one shared component instance. 289 quicksearch = (Component) quicksearch.getClass().getConstructor(KeyStroke.class).newInstance(new Object[]{null}); 290 } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) { 291 } 292 Dimension preferredSize = quicksearch.getPreferredSize(); 293 preferredSize = new Dimension((int) preferredSize.getWidth() * 2, (int) preferredSize.getHeight()); 294 quicksearch.setMinimumSize(preferredSize); // necessary for GTK LAF 295 quicksearch.setPreferredSize(preferredSize); 296 toolBar.add(quicksearch); 297 298 centerPanel = new JPanel(); 299 this.add(centerPanel, BorderLayout.CENTER); 300 cardLayout = new CardLayout(); 301 centerPanel.setLayout(cardLayout); 302 centerPanel.add(SCENE_STRING, scene.getComponent()); 303 centerPanel.setBackground(Color.WHITE); 304 satelliteComponent = scene.createSatelliteView(); 305 satelliteComponent.setSize(200, 200); 306 centerPanel.add(SATELLITE_STRING, satelliteComponent); 307 308 // TODO: Fix the hot key for entering the satellite view 309 this.addKeyListener(keyListener); 310 311 scene.getComponent().addHierarchyBoundsListener(new HierarchyBoundsListener() { 312 313 @Override 314 public void ancestorMoved(HierarchyEvent e) { 315 } 316 317 @Override 318 public void ancestorResized(HierarchyEvent e) { 319 if (!notFirstTime && scene.getComponent().getBounds().width > 0) { 320 notFirstTime = true; 321 SwingUtilities.invokeLater(new Runnable() { 322 323 @Override 324 public void run() { 325 EditorTopComponent.this.scene.initialize(); 326 } 327 }); 328 } 329 } 330 }); 331 332 if (diagram.getGraph().getGroup().getGraphsCount() == 1) { 333 rangeSlider.setVisible(false); 334 } 335 336 updateDisplayName(); 337 } 338 private KeyListener keyListener = new KeyListener() { 339 340 @Override 341 public void keyTyped(KeyEvent e) { 342 } 343 344 @Override 345 public void keyPressed(KeyEvent e) { 346 if (e.getKeyCode() == KeyEvent.VK_S) { 347 EditorTopComponent.this.overviewButton.setSelected(true); 348 EditorTopComponent.this.overviewAction.setState(true); 349 } 350 } 351 352 @Override 353 public void keyReleased(KeyEvent e) { 354 if (e.getKeyCode() == KeyEvent.VK_S) { 355 EditorTopComponent.this.overviewButton.setSelected(false); 356 EditorTopComponent.this.overviewAction.setState(false); 357 } 358 } 359 }; 360 getDiagramModel()361 public DiagramViewModel getDiagramModel() { 362 return rangeSliderModel; 363 } 364 showSatellite()365 private void showSatellite() { 366 cardLayout.show(centerPanel, SATELLITE_STRING); 367 satelliteComponent.requestFocus(); 368 369 } 370 showScene()371 private void showScene() { 372 cardLayout.show(centerPanel, SCENE_STRING); 373 scene.getComponent().requestFocus(); 374 } 375 zoomOut()376 public void zoomOut() { 377 scene.zoomOut(); 378 } 379 zoomIn()380 public void zoomIn() { 381 scene.zoomIn(); 382 } 383 showPrevDiagram()384 public void showPrevDiagram() { 385 int fp = getModel().getFirstPosition(); 386 int sp = getModel().getSecondPosition(); 387 if (fp != 0) { 388 fp--; 389 sp--; 390 getModel().setPositions(fp, sp); 391 } 392 } 393 getModel()394 public DiagramViewModel getModel() { 395 return rangeSliderModel; 396 } 397 getFilterChain()398 public FilterChain getFilterChain() { 399 return getModel().getFilterChain(); 400 } 401 getActive()402 public static EditorTopComponent getActive() { 403 Set<? extends Mode> modes = WindowManager.getDefault().getModes(); 404 for (Mode m : modes) { 405 TopComponent tc = m.getSelectedTopComponent(); 406 if (tc instanceof EditorTopComponent) { 407 return (EditorTopComponent) tc; 408 } 409 } 410 return null; 411 } 412 413 /** This method is called from within the constructor to 414 * initialize the form. 415 * WARNING: Do NOT modify this code. The content of this method is 416 * always regenerated by the Form Editor. 417 */ 418 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents initComponents()419 private void initComponents() { 420 jCheckBox1 = new javax.swing.JCheckBox(); 421 422 org.openide.awt.Mnemonics.setLocalizedText(jCheckBox1, "jCheckBox1"); 423 jCheckBox1.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); 424 jCheckBox1.setMargin(new java.awt.Insets(0, 0, 0, 0)); 425 426 setLayout(new java.awt.BorderLayout()); 427 428 }// </editor-fold>//GEN-END:initComponents 429 // Variables declaration - do not modify//GEN-BEGIN:variables 430 private javax.swing.JCheckBox jCheckBox1; 431 // End of variables declaration//GEN-END:variables 432 433 @Override getPersistenceType()434 public int getPersistenceType() { 435 return TopComponent.PERSISTENCE_NEVER; 436 } 437 438 @Override componentOpened()439 public void componentOpened() { 440 } 441 442 @Override componentClosed()443 public void componentClosed() { 444 rangeSliderModel.close(); 445 } 446 447 @Override preferredID()448 protected String preferredID() { 449 return PREFERRED_ID; 450 } 451 452 private ChangedListener<DiagramViewModel> diagramChangedListener = new ChangedListener<DiagramViewModel>() { 453 454 @Override 455 public void changed(DiagramViewModel source) { 456 updateDisplayName(); 457 Collection<Object> list = new ArrayList<>(); 458 list.add(new EditorInputGraphProvider(EditorTopComponent.this)); 459 graphContent.set(list, null); 460 diagramProvider.getChangedEvent().fire(); 461 } 462 463 }; 464 showPredSucc()465 public boolean showPredSucc() { 466 return (Boolean) predSuccAction.getValue(PredSuccAction.STATE); 467 } 468 setSelection(PropertyMatcher matcher)469 public void setSelection(PropertyMatcher matcher) { 470 471 Properties.PropertySelector<Figure> selector = new Properties.PropertySelector<>(getModel().getDiagramToView().getFigures()); 472 List<Figure> list = selector.selectMultiple(matcher); 473 setSelectedFigures(list); 474 } 475 setSelectedFigures(List<Figure> list)476 public void setSelectedFigures(List<Figure> list) { 477 scene.setSelection(list); 478 scene.centerFigures(list); 479 } 480 setSelectedNodes(Set<InputNode> nodes)481 public void setSelectedNodes(Set<InputNode> nodes) { 482 483 List<Figure> list = new ArrayList<>(); 484 Set<Integer> ids = new HashSet<>(); 485 for (InputNode n : nodes) { 486 ids.add(n.getId()); 487 } 488 489 for (Figure f : getModel().getDiagramToView().getFigures()) { 490 for (InputNode n : f.getSource().getSourceNodes()) { 491 if (ids.contains(n.getId())) { 492 list.add(f); 493 break; 494 } 495 } 496 } 497 498 setSelectedFigures(list); 499 } 500 501 @Override propertyChange(PropertyChangeEvent evt)502 public void propertyChange(PropertyChangeEvent evt) { 503 if (evt.getSource() == this.predSuccAction) { 504 boolean b = (Boolean) predSuccAction.getValue(PredSuccAction.STATE); 505 this.getModel().setShowNodeHull(b); 506 } else if (evt.getSource() == this.overviewAction) { 507 boolean b = (Boolean) overviewAction.getValue(OverviewAction.STATE); 508 if (b) { 509 showSatellite(); 510 } else { 511 showScene(); 512 } 513 } else if (evt.getSource() == this.blockLayoutAction) { 514 boolean b = (Boolean) blockLayoutAction.getValue(EnableBlockLayoutAction.STATE); 515 this.getModel().setShowBlocks(b); 516 } else if (evt.getSource() == this.hideDuplicatesAction) { 517 boolean b = (Boolean) hideDuplicatesAction.getValue(HideDuplicatesAction.STATE); 518 this.getModel().setHideDuplicates(b); 519 } else if (evt.getSource() == this.selectionModeAction || evt.getSource() == this.panModeAction) { 520 if (panModeAction.isSelected()) { 521 scene.setInteractionMode(DiagramViewer.InteractionMode.PANNING); 522 } else if (selectionModeAction.isSelected()) { 523 scene.setInteractionMode(DiagramViewer.InteractionMode.SELECTION); 524 } 525 } else { 526 assert false : "Unknown event source"; 527 } 528 } 529 extract()530 public void extract() { 531 getModel().showOnly(getModel().getSelectedNodes()); 532 } 533 hideNodes()534 public void hideNodes() { 535 Set<Integer> selectedNodes = this.getModel().getSelectedNodes(); 536 HashSet<Integer> nodes = new HashSet<>(getModel().getHiddenNodes()); 537 nodes.addAll(selectedNodes); 538 this.getModel().showNot(nodes); 539 } 540 expandPredecessors()541 public void expandPredecessors() { 542 Set<Figure> oldSelection = getModel().getSelectedFigures(); 543 Set<Figure> figures = new HashSet<>(); 544 545 for (Figure f : this.getDiagramModel().getDiagramToView().getFigures()) { 546 boolean ok = false; 547 if (oldSelection.contains(f)) { 548 ok = true; 549 } else { 550 for (Figure pred : f.getSuccessors()) { 551 if (oldSelection.contains(pred)) { 552 ok = true; 553 break; 554 } 555 } 556 } 557 558 if (ok) { 559 figures.add(f); 560 } 561 } 562 563 getModel().showAll(figures); 564 } 565 expandSuccessors()566 public void expandSuccessors() { 567 Set<Figure> oldSelection = getModel().getSelectedFigures(); 568 Set<Figure> figures = new HashSet<>(); 569 570 for (Figure f : this.getDiagramModel().getDiagramToView().getFigures()) { 571 boolean ok = false; 572 if (oldSelection.contains(f)) { 573 ok = true; 574 } else { 575 for (Figure succ : f.getPredecessors()) { 576 if (oldSelection.contains(succ)) { 577 ok = true; 578 break; 579 } 580 } 581 } 582 583 if (ok) { 584 figures.add(f); 585 } 586 } 587 588 getModel().showAll(figures); 589 } 590 showAll()591 public void showAll() { 592 getModel().showNot(new HashSet<Integer>()); 593 } 594 getDiagram()595 public Diagram getDiagram() { 596 return getDiagramModel().getDiagramToView(); 597 } 598 599 @Override componentHidden()600 protected void componentHidden() { 601 super.componentHidden(); 602 scene.componentHidden(); 603 604 } 605 606 @Override componentShowing()607 protected void componentShowing() { 608 super.componentShowing(); 609 scene.componentShowing(); 610 } 611 612 @Override requestActive()613 public void requestActive() { 614 super.requestActive(); 615 scene.getComponent().requestFocus(); 616 } 617 618 @Override getUndoRedo()619 public UndoRedo getUndoRedo() { 620 return scene.getUndoRedo(); 621 } 622 623 @Override writeReplace()624 protected Object writeReplace() throws ObjectStreamException { 625 throw new NotSerializableException(); 626 } 627 } 628