blob: 61c38f3e139a200c5011799a06364ecb3525b7a2 [file] [log] [blame]
/*
* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package com.sun.hotspot.igv.view.widgets;
import com.sun.hotspot.igv.data.InputGraph;
import com.sun.hotspot.igv.data.Properties;
import com.sun.hotspot.igv.data.services.GraphViewer;
import com.sun.hotspot.igv.graph.Figure;
import com.sun.hotspot.igv.util.DoubleClickAction;
import com.sun.hotspot.igv.util.DoubleClickHandler;
import com.sun.hotspot.igv.util.PropertiesSheet;
import com.sun.hotspot.igv.view.DiagramScene;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import org.netbeans.api.visual.action.PopupMenuProvider;
import org.netbeans.api.visual.action.WidgetAction;
import org.netbeans.api.visual.layout.LayoutFactory;
import org.netbeans.api.visual.model.ObjectState;
import org.netbeans.api.visual.widget.LabelWidget;
import org.netbeans.api.visual.widget.Widget;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.nodes.Sheet;
import org.openide.util.Lookup;
/**
*
* @author Thomas Wuerthinger
*/
public class FigureWidget extends Widget implements Properties.Provider, PopupMenuProvider, DoubleClickHandler {
public static final boolean VERTICAL_LAYOUT = true;
private static final double LABEL_ZOOM_FACTOR = 0.3;
private Figure figure;
private Widget leftWidget;
private Widget rightWidget;
private Widget middleWidget;
private ArrayList<LabelWidget> labelWidgets;
private DiagramScene diagramScene;
private boolean boundary;
private final Node node;
private Widget dummyTop;
public void setBoundary(boolean b) {
boundary = b;
}
public boolean isBoundary() {
return boundary;
}
public Node getNode() {
return node;
}
@Override
public boolean isHitAt(Point localLocation) {
return middleWidget.isHitAt(localLocation);
}
public FigureWidget(final Figure f, WidgetAction hoverAction, WidgetAction selectAction, DiagramScene scene, Widget parent) {
super(scene);
assert this.getScene() != null;
assert this.getScene().getView() != null;
this.figure = f;
this.setCheckClipping(true);
this.diagramScene = scene;
parent.addChild(this);
Widget outer = new Widget(scene);
outer.setBackground(f.getColor());
outer.setLayout(LayoutFactory.createOverlayLayout());
middleWidget = new Widget(scene);
middleWidget.setLayout(LayoutFactory.createVerticalFlowLayout(LayoutFactory.SerialAlignment.CENTER, 0));
middleWidget.setBackground(f.getColor());
middleWidget.setOpaque(true);
middleWidget.getActions().addAction(new DoubleClickAction(this));
middleWidget.setCheckClipping(true);
dummyTop = new Widget(scene);
dummyTop.setMinimumSize(new Dimension(Figure.INSET / 2, 1));
middleWidget.addChild(dummyTop);
String[] strings = figure.getLines();
labelWidgets = new ArrayList<>(strings.length);
for (String displayString : strings) {
LabelWidget lw = new LabelWidget(scene);
labelWidgets.add(lw);
middleWidget.addChild(lw);
lw.setLabel(displayString);
lw.setFont(figure.getDiagram().getFont());
lw.setForeground(getTextColor());
lw.setAlignment(LabelWidget.Alignment.CENTER);
lw.setVerticalAlignment(LabelWidget.VerticalAlignment.CENTER);
lw.setBorder(BorderFactory.createEmptyBorder());
}
Widget dummyBottom = new Widget(scene);
dummyBottom.setMinimumSize(new Dimension(Figure.INSET / 2, 1));
middleWidget.addChild(dummyBottom);
middleWidget.setPreferredBounds(new Rectangle(0, Figure.SLOT_WIDTH - Figure.OVERLAPPING, f.getWidth(), f.getHeight()));
this.addChild(middleWidget);
// Initialize node for property sheet
node = new AbstractNode(Children.LEAF) {
@Override
protected Sheet createSheet() {
Sheet s = super.createSheet();
PropertiesSheet.initializeSheet(f.getProperties(), s);
return s;
}
};
node.setDisplayName(getName());
}
public Widget getLeftWidget() {
return leftWidget;
}
public Widget getRightWidget() {
return rightWidget;
}
@Override
protected void notifyStateChanged(ObjectState previousState, ObjectState state) {
super.notifyStateChanged(previousState, state);
Font font = this.figure.getDiagram().getFont();
if (state.isSelected()) {
font = this.figure.getDiagram().getBoldFont();
}
Color borderColor = Color.BLACK;
Color innerBorderColor = getFigure().getColor();
if (state.isHighlighted()) {
innerBorderColor = borderColor = Color.BLUE;
}
middleWidget.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(borderColor, 1), BorderFactory.createLineBorder(innerBorderColor, 1)));
for (LabelWidget labelWidget : labelWidgets) {
labelWidget.setFont(font);
}
repaint();
}
public String getName() {
return getProperties().get("name");
}
@Override
public Properties getProperties() {
return figure.getProperties();
}
public Figure getFigure() {
return figure;
}
private Color getTextColor() {
Color bg = figure.getColor();
double brightness = bg.getRed() * 0.21 + bg.getGreen() * 0.72 + bg.getBlue() * 0.07;
if (brightness < 150) {
return Color.WHITE;
} else {
return Color.BLACK;
}
}
@Override
protected void paintChildren() {
Composite oldComposite = null;
if (boundary) {
oldComposite = getScene().getGraphics().getComposite();
float alpha = DiagramScene.ALPHA;
this.getScene().getGraphics().setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
}
if (diagramScene.getZoomFactor() < LABEL_ZOOM_FACTOR) {
for (LabelWidget labelWidget : labelWidgets) {
labelWidget.setVisible(false);
}
super.paintChildren();
for (LabelWidget labelWidget : labelWidgets) {
labelWidget.setVisible(true);
}
} else {
Color oldColor = null;
if (boundary) {
for (LabelWidget labelWidget : labelWidgets) {
oldColor = labelWidget.getForeground();
labelWidget.setForeground(Color.BLACK);
}
}
super.paintChildren();
if (boundary) {
for (LabelWidget labelWidget : labelWidgets) {
labelWidget.setForeground(oldColor);
}
}
}
if (boundary) {
getScene().getGraphics().setComposite(oldComposite);
}
}
@Override
public JPopupMenu getPopupMenu(Widget widget, Point point) {
JPopupMenu menu = diagramScene.createPopupMenu();
menu.addSeparator();
build(menu, getFigure(), this, false, diagramScene);
menu.addSeparator();
build(menu, getFigure(), this, true, diagramScene);
if (getFigure().getSubgraphs() != null) {
menu.addSeparator();
JMenu subgraphs = new JMenu("Subgraphs");
menu.add(subgraphs);
final GraphViewer viewer = Lookup.getDefault().lookup(GraphViewer.class);
for (final InputGraph subgraph : getFigure().getSubgraphs()) {
Action a = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
viewer.view(subgraph, true);
}
};
a.setEnabled(true);
a.putValue(Action.NAME, subgraph.getName());
subgraphs.add(a);
}
}
return menu;
}
public static void build(JPopupMenu menu, Figure figure, FigureWidget figureWidget, boolean successors, DiagramScene diagramScene) {
Set<Figure> set = figure.getPredecessorSet();
if (successors) {
set = figure.getSuccessorSet();
}
boolean first = true;
for (Figure f : set) {
if (f == figure) {
continue;
}
if (first) {
first = false;
} else {
menu.addSeparator();
}
Action go = diagramScene.createGotoAction(f);
menu.add(go);
JMenu preds = new JMenu("Nodes Above");
preds.addMenuListener(figureWidget.new NeighborMenuListener(preds, f, false));
menu.add(preds);
JMenu succs = new JMenu("Nodes Below");
succs.addMenuListener(figureWidget.new NeighborMenuListener(succs, f, true));
menu.add(succs);
}
if (figure.getPredecessorSet().isEmpty() && figure.getSuccessorSet().isEmpty()) {
menu.add("(none)");
}
}
/**
* Builds the submenu for a figure's neighbors on demand.
*/
public class NeighborMenuListener implements MenuListener {
private final JMenu menu;
private final Figure figure;
private final boolean successors;
public NeighborMenuListener(JMenu menu, Figure figure, boolean successors) {
this.menu = menu;
this.figure = figure;
this.successors = successors;
}
@Override
public void menuSelected(MenuEvent e) {
if (menu.getItemCount() > 0) {
// already built before
return;
}
build(menu.getPopupMenu(), figure, FigureWidget.this, successors, diagramScene);
}
@Override
public void menuDeselected(MenuEvent e) {
// ignore
}
@Override
public void menuCanceled(MenuEvent e) {
// ignore
}
}
@Override
public void handleDoubleClick(Widget w, WidgetAction.WidgetMouseEvent e) {
if (diagramScene.isAllVisible()) {
final Set<Integer> hiddenNodes = new HashSet<>(diagramScene.getModel().getGraphToView().getGroup().getAllNodes());
hiddenNodes.removeAll(this.getFigure().getSource().getSourceNodesAsSet());
this.diagramScene.getModel().showNot(hiddenNodes);
} else if (isBoundary()) {
final Set<Integer> hiddenNodes = new HashSet<>(diagramScene.getModel().getHiddenNodes());
hiddenNodes.removeAll(this.getFigure().getSource().getSourceNodesAsSet());
this.diagramScene.getModel().showNot(hiddenNodes);
} else {
final Set<Integer> hiddenNodes = new HashSet<>(diagramScene.getModel().getHiddenNodes());
hiddenNodes.addAll(this.getFigure().getSource().getSourceNodesAsSet());
this.diagramScene.getModel().showNot(hiddenNodes);
}
}
}