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 */ 24package com.sun.hotspot.igv.view.widgets; 25 26import com.sun.hotspot.igv.data.InputGraph; 27import com.sun.hotspot.igv.data.Properties; 28import com.sun.hotspot.igv.data.services.GraphViewer; 29import com.sun.hotspot.igv.graph.Figure; 30import com.sun.hotspot.igv.util.DoubleClickAction; 31import com.sun.hotspot.igv.util.DoubleClickHandler; 32import com.sun.hotspot.igv.util.PropertiesSheet; 33import com.sun.hotspot.igv.view.DiagramScene; 34import java.awt.*; 35import java.awt.event.ActionEvent; 36import java.util.ArrayList; 37import java.util.HashSet; 38import java.util.Set; 39import javax.swing.AbstractAction; 40import javax.swing.Action; 41import javax.swing.BorderFactory; 42import javax.swing.JMenu; 43import javax.swing.JPopupMenu; 44import javax.swing.event.MenuEvent; 45import javax.swing.event.MenuListener; 46import org.netbeans.api.visual.action.PopupMenuProvider; 47import org.netbeans.api.visual.action.WidgetAction; 48import org.netbeans.api.visual.layout.LayoutFactory; 49import org.netbeans.api.visual.model.ObjectState; 50import org.netbeans.api.visual.widget.LabelWidget; 51import org.netbeans.api.visual.widget.Widget; 52import org.openide.nodes.AbstractNode; 53import org.openide.nodes.Children; 54import org.openide.nodes.Node; 55import org.openide.nodes.Sheet; 56import org.openide.util.Lookup; 57 58/** 59 * 60 * @author Thomas Wuerthinger 61 */ 62public class FigureWidget extends Widget implements Properties.Provider, PopupMenuProvider, DoubleClickHandler { 63 64 public static final boolean VERTICAL_LAYOUT = true; 65 private static final double LABEL_ZOOM_FACTOR = 0.3; 66 private Figure figure; 67 private Widget leftWidget; 68 private Widget rightWidget; 69 private Widget middleWidget; 70 private ArrayList<LabelWidget> labelWidgets; 71 private DiagramScene diagramScene; 72 private boolean boundary; 73 private final Node node; 74 private Widget dummyTop; 75 76 public void setBoundary(boolean b) { 77 boundary = b; 78 } 79 80 public boolean isBoundary() { 81 return boundary; 82 } 83 84 public Node getNode() { 85 return node; 86 } 87 88 @Override 89 public boolean isHitAt(Point localLocation) { 90 return middleWidget.isHitAt(localLocation); 91 } 92 93 public FigureWidget(final Figure f, WidgetAction hoverAction, WidgetAction selectAction, DiagramScene scene, Widget parent) { 94 super(scene); 95 96 assert this.getScene() != null; 97 assert this.getScene().getView() != null; 98 99 this.figure = f; 100 this.setCheckClipping(true); 101 this.diagramScene = scene; 102 parent.addChild(this); 103 104 Widget outer = new Widget(scene); 105 outer.setBackground(f.getColor()); 106 outer.setLayout(LayoutFactory.createOverlayLayout()); 107 108 middleWidget = new Widget(scene); 109 middleWidget.setLayout(LayoutFactory.createVerticalFlowLayout(LayoutFactory.SerialAlignment.CENTER, 0)); 110 middleWidget.setBackground(f.getColor()); 111 middleWidget.setOpaque(true); 112 middleWidget.getActions().addAction(new DoubleClickAction(this)); 113 middleWidget.setCheckClipping(true); 114 115 dummyTop = new Widget(scene); 116 dummyTop.setMinimumSize(new Dimension(Figure.INSET / 2, 1)); 117 middleWidget.addChild(dummyTop); 118 119 String[] strings = figure.getLines(); 120 labelWidgets = new ArrayList<>(strings.length); 121 122 for (String displayString : strings) { 123 LabelWidget lw = new LabelWidget(scene); 124 labelWidgets.add(lw); 125 middleWidget.addChild(lw); 126 lw.setLabel(displayString); 127 lw.setFont(figure.getDiagram().getFont()); 128 lw.setForeground(getTextColor()); 129 lw.setAlignment(LabelWidget.Alignment.CENTER); 130 lw.setVerticalAlignment(LabelWidget.VerticalAlignment.CENTER); 131 lw.setBorder(BorderFactory.createEmptyBorder()); 132 } 133 134 Widget dummyBottom = new Widget(scene); 135 dummyBottom.setMinimumSize(new Dimension(Figure.INSET / 2, 1)); 136 middleWidget.addChild(dummyBottom); 137 138 middleWidget.setPreferredBounds(new Rectangle(0, Figure.SLOT_WIDTH - Figure.OVERLAPPING, f.getWidth(), f.getHeight())); 139 this.addChild(middleWidget); 140 141 // Initialize node for property sheet 142 node = new AbstractNode(Children.LEAF) { 143 144 @Override 145 protected Sheet createSheet() { 146 Sheet s = super.createSheet(); 147 PropertiesSheet.initializeSheet(f.getProperties(), s); 148 return s; 149 } 150 }; 151 node.setDisplayName(getName()); 152 } 153 154 public Widget getLeftWidget() { 155 return leftWidget; 156 } 157 158 public Widget getRightWidget() { 159 return rightWidget; 160 } 161 162 @Override 163 protected void notifyStateChanged(ObjectState previousState, ObjectState state) { 164 super.notifyStateChanged(previousState, state); 165 166 Font font = this.figure.getDiagram().getFont(); 167 if (state.isSelected()) { 168 font = this.figure.getDiagram().getBoldFont(); 169 } 170 171 Color borderColor = Color.BLACK; 172 Color innerBorderColor = getFigure().getColor(); 173 if (state.isHighlighted()) { 174 innerBorderColor = borderColor = Color.BLUE; 175 } 176 177 middleWidget.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(borderColor, 1), BorderFactory.createLineBorder(innerBorderColor, 1))); 178 for (LabelWidget labelWidget : labelWidgets) { 179 labelWidget.setFont(font); 180 } 181 repaint(); 182 } 183 184 public String getName() { 185 return getProperties().get("name"); 186 } 187 188 @Override 189 public Properties getProperties() { 190 return figure.getProperties(); 191 } 192 193 public Figure getFigure() { 194 return figure; 195 } 196 197 private Color getTextColor() { 198 Color bg = figure.getColor(); 199 double brightness = bg.getRed() * 0.21 + bg.getGreen() * 0.72 + bg.getBlue() * 0.07; 200 if (brightness < 150) { 201 return Color.WHITE; 202 } else { 203 return Color.BLACK; 204 } 205 } 206 207 @Override 208 protected void paintChildren() { 209 Composite oldComposite = null; 210 if (boundary) { 211 oldComposite = getScene().getGraphics().getComposite(); 212 float alpha = DiagramScene.ALPHA; 213 this.getScene().getGraphics().setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); 214 } 215 216 if (diagramScene.getZoomFactor() < LABEL_ZOOM_FACTOR) { 217 for (LabelWidget labelWidget : labelWidgets) { 218 labelWidget.setVisible(false); 219 } 220 super.paintChildren(); 221 for (LabelWidget labelWidget : labelWidgets) { 222 labelWidget.setVisible(true); 223 } 224 } else { 225 Color oldColor = null; 226 if (boundary) { 227 for (LabelWidget labelWidget : labelWidgets) { 228 oldColor = labelWidget.getForeground(); 229 labelWidget.setForeground(Color.BLACK); 230 } 231 } 232 super.paintChildren(); 233 if (boundary) { 234 for (LabelWidget labelWidget : labelWidgets) { 235 labelWidget.setForeground(oldColor); 236 } 237 } 238 } 239 240 if (boundary) { 241 getScene().getGraphics().setComposite(oldComposite); 242 } 243 } 244 245 @Override 246 public JPopupMenu getPopupMenu(Widget widget, Point point) { 247 JPopupMenu menu = diagramScene.createPopupMenu(); 248 menu.addSeparator(); 249 250 build(menu, getFigure(), this, false, diagramScene); 251 menu.addSeparator(); 252 build(menu, getFigure(), this, true, diagramScene); 253 254 if (getFigure().getSubgraphs() != null) { 255 menu.addSeparator(); 256 JMenu subgraphs = new JMenu("Subgraphs"); 257 menu.add(subgraphs); 258 259 final GraphViewer viewer = Lookup.getDefault().lookup(GraphViewer.class); 260 for (final InputGraph subgraph : getFigure().getSubgraphs()) { 261 Action a = new AbstractAction() { 262 263 @Override 264 public void actionPerformed(ActionEvent e) { 265 viewer.view(subgraph, true); 266 } 267 }; 268 269 a.setEnabled(true); 270 a.putValue(Action.NAME, subgraph.getName()); 271 subgraphs.add(a); 272 } 273 } 274 275 return menu; 276 } 277 278 public static void build(JPopupMenu menu, Figure figure, FigureWidget figureWidget, boolean successors, DiagramScene diagramScene) { 279 Set<Figure> set = figure.getPredecessorSet(); 280 if (successors) { 281 set = figure.getSuccessorSet(); 282 } 283 284 boolean first = true; 285 for (Figure f : set) { 286 if (f == figure) { 287 continue; 288 } 289 290 if (first) { 291 first = false; 292 } else { 293 menu.addSeparator(); 294 } 295 296 Action go = diagramScene.createGotoAction(f); 297 menu.add(go); 298 299 JMenu preds = new JMenu("Nodes Above"); 300 preds.addMenuListener(figureWidget.new NeighborMenuListener(preds, f, false)); 301 menu.add(preds); 302 303 JMenu succs = new JMenu("Nodes Below"); 304 succs.addMenuListener(figureWidget.new NeighborMenuListener(succs, f, true)); 305 menu.add(succs); 306 } 307 308 if (figure.getPredecessorSet().isEmpty() && figure.getSuccessorSet().isEmpty()) { 309 menu.add("(none)"); 310 } 311 } 312 313 /** 314 * Builds the submenu for a figure's neighbors on demand. 315 */ 316 public class NeighborMenuListener implements MenuListener { 317 318 private final JMenu menu; 319 private final Figure figure; 320 private final boolean successors; 321 322 public NeighborMenuListener(JMenu menu, Figure figure, boolean successors) { 323 this.menu = menu; 324 this.figure = figure; 325 this.successors = successors; 326 } 327 328 @Override 329 public void menuSelected(MenuEvent e) { 330 if (menu.getItemCount() > 0) { 331 // already built before 332 return; 333 } 334 335 build(menu.getPopupMenu(), figure, FigureWidget.this, successors, diagramScene); 336 } 337 338 @Override 339 public void menuDeselected(MenuEvent e) { 340 // ignore 341 } 342 343 @Override 344 public void menuCanceled(MenuEvent e) { 345 // ignore 346 } 347 } 348 349 @Override 350 public void handleDoubleClick(Widget w, WidgetAction.WidgetMouseEvent e) { 351 352 if (diagramScene.isAllVisible()) { 353 final Set<Integer> hiddenNodes = new HashSet<>(diagramScene.getModel().getGraphToView().getGroup().getAllNodes()); 354 hiddenNodes.removeAll(this.getFigure().getSource().getSourceNodesAsSet()); 355 this.diagramScene.getModel().showNot(hiddenNodes); 356 } else if (isBoundary()) { 357 358 final Set<Integer> hiddenNodes = new HashSet<>(diagramScene.getModel().getHiddenNodes()); 359 hiddenNodes.removeAll(this.getFigure().getSource().getSourceNodesAsSet()); 360 this.diagramScene.getModel().showNot(hiddenNodes); 361 } else { 362 final Set<Integer> hiddenNodes = new HashSet<>(diagramScene.getModel().getHiddenNodes()); 363 hiddenNodes.addAll(this.getFigure().getSource().getSourceNodesAsSet()); 364 this.diagramScene.getModel().showNot(hiddenNodes); 365 } 366 } 367} 368