1 /* 2 * Copyright (c) 2008, 2015, 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 148 private void updateDisplayName() { 149 setDisplayName(getDiagram().getName()); 150 setToolTipText(getDiagram().getGraph().getGroup().getName()); 151 } 152 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 quicksearch.setMinimumSize(quicksearch.getPreferredSize()); // necessary for GTK LAF 293 toolBar.add(quicksearch); 294 295 centerPanel = new JPanel(); 296 this.add(centerPanel, BorderLayout.CENTER); 297 cardLayout = new CardLayout(); 298 centerPanel.setLayout(cardLayout); 299 centerPanel.add(SCENE_STRING, scene.getComponent()); 300 centerPanel.setBackground(Color.WHITE); 301 satelliteComponent = scene.createSatelliteView(); 302 satelliteComponent.setSize(200, 200); 303 centerPanel.add(SATELLITE_STRING, satelliteComponent); 304 305 // TODO: Fix the hot key for entering the satellite view 306 this.addKeyListener(keyListener); 307 308 scene.getComponent().addHierarchyBoundsListener(new HierarchyBoundsListener() { 309 310 @Override 311 public void ancestorMoved(HierarchyEvent e) { 312 } 313 314 @Override 315 public void ancestorResized(HierarchyEvent e) { 316 if (!notFirstTime && scene.getComponent().getBounds().width > 0) { 317 notFirstTime = true; 318 SwingUtilities.invokeLater(new Runnable() { 319 320 @Override 321 public void run() { 322 EditorTopComponent.this.scene.initialize(); 323 } 324 }); 325 } 326 } 327 }); 328 329 if (diagram.getGraph().getGroup().getGraphsCount() == 1) { 330 rangeSlider.setVisible(false); 331 } 332 333 updateDisplayName(); 334 } 335 private KeyListener keyListener = new KeyListener() { 336 337 @Override 338 public void keyTyped(KeyEvent e) { 339 } 340 341 @Override 342 public void keyPressed(KeyEvent e) { 343 if (e.getKeyCode() == KeyEvent.VK_S) { 344 EditorTopComponent.this.overviewButton.setSelected(true); 345 EditorTopComponent.this.overviewAction.setState(true); 346 } 347 } 348 349 @Override 350 public void keyReleased(KeyEvent e) { 351 if (e.getKeyCode() == KeyEvent.VK_S) { 352 EditorTopComponent.this.overviewButton.setSelected(false); 353 EditorTopComponent.this.overviewAction.setState(false); 354 } 355 } 356 }; 357 358 public DiagramViewModel getDiagramModel() { 359 return rangeSliderModel; 360 } 361 362 private void showSatellite() { 363 cardLayout.show(centerPanel, SATELLITE_STRING); 364 satelliteComponent.requestFocus(); 365 366 } 367 368 private void showScene() { 369 cardLayout.show(centerPanel, SCENE_STRING); 370 scene.getComponent().requestFocus(); 371 } 372 373 public void zoomOut() { 374 scene.zoomOut(); 375 } 376 377 public void zoomIn() { 378 scene.zoomIn(); 379 } 380 381 public void showPrevDiagram() { 382 int fp = getModel().getFirstPosition(); 383 int sp = getModel().getSecondPosition(); 384 if (fp != 0) { 385 fp--; 386 sp--; 387 getModel().setPositions(fp, sp); 388 } 389 } 390 391 public DiagramViewModel getModel() { 392 return rangeSliderModel; 393 } 394 395 public FilterChain getFilterChain() { 396 return getModel().getFilterChain(); 397 } 398 399 public static EditorTopComponent getActive() { 400 Set<? extends Mode> modes = WindowManager.getDefault().getModes(); 401 for (Mode m : modes) { 402 TopComponent tc = m.getSelectedTopComponent(); 403 if (tc instanceof EditorTopComponent) { 404 return (EditorTopComponent) tc; 405 } 406 } 407 return null; 408 } 409 410 /** This method is called from within the constructor to 411 * initialize the form. 412 * WARNING: Do NOT modify this code. The content of this method is 413 * always regenerated by the Form Editor. 414 */ 415 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents 416 private void initComponents() { 417 jCheckBox1 = new javax.swing.JCheckBox(); 418 419 org.openide.awt.Mnemonics.setLocalizedText(jCheckBox1, "jCheckBox1"); 420 jCheckBox1.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0)); 421 jCheckBox1.setMargin(new java.awt.Insets(0, 0, 0, 0)); 422 423 setLayout(new java.awt.BorderLayout()); 424 425 }// </editor-fold>//GEN-END:initComponents 426 // Variables declaration - do not modify//GEN-BEGIN:variables 427 private javax.swing.JCheckBox jCheckBox1; 428 // End of variables declaration//GEN-END:variables 429 430 @Override 431 public int getPersistenceType() { 432 return TopComponent.PERSISTENCE_NEVER; 433 } 434 435 @Override 436 public void componentOpened() { 437 } 438 439 @Override 440 public void componentClosed() { 441 rangeSliderModel.close(); 442 } 443 444 @Override 445 protected String preferredID() { 446 return PREFERRED_ID; 447 } 448 449 private ChangedListener<DiagramViewModel> diagramChangedListener = new ChangedListener<DiagramViewModel>() { 450 451 @Override 452 public void changed(DiagramViewModel source) { 453 updateDisplayName(); 454 Collection<Object> list = new ArrayList<>(); 455 list.add(new EditorInputGraphProvider(EditorTopComponent.this)); 456 graphContent.set(list, null); 457 diagramProvider.getChangedEvent().fire(); 458 } 459 460 }; 461 462 public boolean showPredSucc() { 463 return (Boolean) predSuccAction.getValue(PredSuccAction.STATE); 464 } 465 466 public void setSelection(PropertyMatcher matcher) { 467 468 Properties.PropertySelector<Figure> selector = new Properties.PropertySelector<>(getModel().getDiagramToView().getFigures()); 469 List<Figure> list = selector.selectMultiple(matcher); 470 setSelectedFigures(list); 471 } 472 473 public void setSelectedFigures(List<Figure> list) { 474 scene.setSelection(list); 475 scene.centerFigures(list); 476 } 477 478 public void setSelectedNodes(Set<InputNode> nodes) { 479 480 List<Figure> list = new ArrayList<>(); 481 Set<Integer> ids = new HashSet<>(); 482 for (InputNode n : nodes) { 483 ids.add(n.getId()); 484 } 485 486 for (Figure f : getModel().getDiagramToView().getFigures()) { 487 for (InputNode n : f.getSource().getSourceNodes()) { 488 if (ids.contains(n.getId())) { 489 list.add(f); 490 break; 491 } 492 } 493 } 494 495 setSelectedFigures(list); 496 } 497 498 @Override 499 public void propertyChange(PropertyChangeEvent evt) { 500 if (evt.getSource() == this.predSuccAction) { 501 boolean b = (Boolean) predSuccAction.getValue(PredSuccAction.STATE); 502 this.getModel().setShowNodeHull(b); 503 } else if (evt.getSource() == this.overviewAction) { 504 boolean b = (Boolean) overviewAction.getValue(OverviewAction.STATE); 505 if (b) { 506 showSatellite(); 507 } else { 508 showScene(); 509 } 510 } else if (evt.getSource() == this.blockLayoutAction) { 511 boolean b = (Boolean) blockLayoutAction.getValue(EnableBlockLayoutAction.STATE); 512 this.getModel().setShowBlocks(b); 513 } else if (evt.getSource() == this.hideDuplicatesAction) { 514 boolean b = (Boolean) hideDuplicatesAction.getValue(HideDuplicatesAction.STATE); 515 this.getModel().setHideDuplicates(b); 516 } else if (evt.getSource() == this.selectionModeAction || evt.getSource() == this.panModeAction) { 517 if (panModeAction.isSelected()) { 518 scene.setInteractionMode(DiagramViewer.InteractionMode.PANNING); 519 } else if (selectionModeAction.isSelected()) { 520 scene.setInteractionMode(DiagramViewer.InteractionMode.SELECTION); 521 } 522 } else { 523 assert false : "Unknown event source"; 524 } 525 } 526 527 public void extract() { 528 getModel().showOnly(getModel().getSelectedNodes()); 529 } 530 531 public void hideNodes() { 532 Set<Integer> selectedNodes = this.getModel().getSelectedNodes(); 533 HashSet<Integer> nodes = new HashSet<>(getModel().getHiddenNodes()); 534 nodes.addAll(selectedNodes); 535 this.getModel().showNot(nodes); 536 } 537 538 public void expandPredecessors() { 539 Set<Figure> oldSelection = getModel().getSelectedFigures(); 540 Set<Figure> figures = new HashSet<>(); 541 542 for (Figure f : this.getDiagramModel().getDiagramToView().getFigures()) { 543 boolean ok = false; 544 if (oldSelection.contains(f)) { 545 ok = true; 546 } else { 547 for (Figure pred : f.getSuccessors()) { 548 if (oldSelection.contains(pred)) { 549 ok = true; 550 break; 551 } 552 } 553 } 554 555 if (ok) { 556 figures.add(f); 557 } 558 } 559 560 getModel().showAll(figures); 561 } 562 563 public void expandSuccessors() { 564 Set<Figure> oldSelection = getModel().getSelectedFigures(); 565 Set<Figure> figures = new HashSet<>(); 566 567 for (Figure f : this.getDiagramModel().getDiagramToView().getFigures()) { 568 boolean ok = false; 569 if (oldSelection.contains(f)) { 570 ok = true; 571 } else { 572 for (Figure succ : f.getPredecessors()) { 573 if (oldSelection.contains(succ)) { 574 ok = true; 575 break; 576 } 577 } 578 } 579 580 if (ok) { 581 figures.add(f); 582 } 583 } 584 585 getModel().showAll(figures); 586 } 587 588 public void showAll() { 589 getModel().showNot(new HashSet<Integer>()); 590 } 591 592 public Diagram getDiagram() { 593 return getDiagramModel().getDiagramToView(); 594 } 595 596 @Override 597 protected void componentHidden() { 598 super.componentHidden(); 599 scene.componentHidden(); 600 601 } 602 603 @Override 604 protected void componentShowing() { 605 super.componentShowing(); 606 scene.componentShowing(); 607 } 608 609 @Override 610 public void requestActive() { 611 super.requestActive(); 612 scene.getComponent().requestFocus(); 613 } 614 615 @Override 616 public UndoRedo getUndoRedo() { 617 return scene.getUndoRedo(); 618 } 619 620 @Override 621 protected Object writeReplace() throws ObjectStreamException { 622 throw new NotSerializableException(); 623 } 624 }