< prev index next >
application/org.openjdk.jmc.ui/src/main/java/org/openjdk/jmc/ui/misc/ChartCanvas.java
Print this page
*** 1,7 ****
/*
! * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The contents of this file are subject to the terms of either the Universal Permissive License
* v 1.0 as shown at http://oss.oracle.com/licenses/upl
--- 1,8 ----
/*
! * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
! * Copyright (c) 2019, Red Hat Inc. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The contents of this file are subject to the terms of either the Universal Permissive License
* v 1.0 as shown at http://oss.oracle.com/licenses/upl
*** 30,60 ****
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.openjdk.jmc.ui.misc;
- import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
--- 31,63 ----
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.openjdk.jmc.ui.misc;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
+ import java.util.function.Consumer;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
+ import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
+ import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
*** 70,90 ****
--- 73,98 ----
import org.openjdk.jmc.ui.charts.IXDataRenderer;
import org.openjdk.jmc.ui.charts.XYChart;
import org.openjdk.jmc.ui.common.util.Environment;
import org.openjdk.jmc.ui.common.util.Environment.OSType;
import org.openjdk.jmc.ui.handlers.MCContextMenuManager;
+ import org.openjdk.jmc.ui.misc.PatternFly.Palette;
public class ChartCanvas extends Canvas {
+ private static int MIN_LANE_HEIGHT = 50;
private int lastMouseX = -1;
private int lastMouseY = -1;
private List<Rectangle2D> highlightRects;
private Object hoveredItemData;
private class Selector extends MouseAdapter implements MouseMoveListener, MouseTrackListener {
int selectionStartX = -1;
int selectionStartY = -1;
+ Point highlightSelectionStart;
+ Point highlightSelectionEnd;
+ Point lastSelection;
boolean selectionIsClick = false;
@Override
public void mouseDown(MouseEvent e) {
/*
*** 101,115 ****
* The code below instead relies on ignoring mouse down events when SWT.MOD4 is
* depressed. Since MOD4 is CTRL on OS X and 0 on all other current platforms, this
* suffices. Except for an additional platform check, this approach is also used in
* org.eclipse.swt.custom.StyledText.handleMouseDown(Event).
*/
! if ((e.button == 1) && ((e.stateMask & SWT.MOD4) == 0)) {
selectionStartX = e.x;
selectionStartY = e.y;
selectionIsClick = true;
toggleSelect(selectionStartX, selectionStartY);
}
}
@Override
public void mouseMove(MouseEvent e) {
--- 109,158 ----
* The code below instead relies on ignoring mouse down events when SWT.MOD4 is
* depressed. Since MOD4 is CTRL on OS X and 0 on all other current platforms, this
* suffices. Except for an additional platform check, this approach is also used in
* org.eclipse.swt.custom.StyledText.handleMouseDown(Event).
*/
! if ((e.button == 1) && ((e.stateMask & SWT.MOD4) == 0) && ((e.stateMask & SWT.CTRL) == 0 ) && ((e.stateMask & SWT.SHIFT) == 0 )) {
selectionStartX = e.x;
selectionStartY = e.y;
+ highlightSelectionEnd = new Point(-1, -1);
+ lastSelection = new Point(-1, -1);
selectionIsClick = true;
toggleSelect(selectionStartX, selectionStartY);
+ } else if (((e.stateMask & SWT.CTRL) != 0) && (e.button == 1)) {
+ select(e.x, e.x, e.y, e.y, false);
+ if (selectionListener != null) {
+ selectionListener.run();
+ }
+ } else if (((e.stateMask & SWT.SHIFT) != 0) && (e.button == 1)) {
+ if (highlightSelectionEnd.y == -1) {
+ highlightSelectionEnd = new Point(e.x, e.y);
+ lastSelection = highlightSelectionEnd;
+ if (highlightSelectionStart.y > highlightSelectionEnd.y) {
+ Point temp = highlightSelectionStart;
+ highlightSelectionStart = highlightSelectionEnd;
+ highlightSelectionEnd = temp;
+ }
+ } else {
+ if (e.y > highlightSelectionStart.y && e.y < highlightSelectionEnd.y) {
+ if (e.y < lastSelection.y) {
+ highlightSelectionEnd = new Point(e.x, e.y);
+ } else if (e.y > lastSelection.y) {
+ highlightSelectionStart = new Point(e.x, e.y);
+ }
+ } else if (e.y < highlightSelectionStart.y) {
+ highlightSelectionStart = new Point(e.x, e.y);
+ lastSelection = highlightSelectionStart;
+ } else if (e.y > highlightSelectionEnd.y) {
+ highlightSelectionEnd = new Point(e.x, e.y);
+ lastSelection = highlightSelectionEnd;
+ }
+ }
+ select(highlightSelectionStart.x, highlightSelectionEnd.x, highlightSelectionStart.y, highlightSelectionEnd.y, true);
+ if (selectionListener != null) {
+ selectionListener.run();
+ }
}
}
@Override
public void mouseMove(MouseEvent e) {
*** 129,150 ****
if (selectionIsClick && ((Math.abs(x - selectionStartX) > 3) || (Math.abs(y - selectionStartY) > 3))) {
selectionIsClick = false;
}
if (!selectionIsClick) {
select((int) (selectionStartX / xScale), (int) (x / xScale), (int) (selectionStartY / yScale),
! (int) (y / yScale));
}
}
@Override
public void mouseUp(MouseEvent e) {
if (selectionStartX >= 0 && (e.button == 1)) {
updateSelectionState(e);
selectionStartX = -1;
selectionStartY = -1;
if (selectionListener != null) {
selectionListener.run();
}
}
}
@Override
--- 172,200 ----
if (selectionIsClick && ((Math.abs(x - selectionStartX) > 3) || (Math.abs(y - selectionStartY) > 3))) {
selectionIsClick = false;
}
if (!selectionIsClick) {
select((int) (selectionStartX / xScale), (int) (x / xScale), (int) (selectionStartY / yScale),
! (int) (y / yScale), true);
}
}
@Override
public void mouseUp(MouseEvent e) {
if (selectionStartX >= 0 && (e.button == 1)) {
updateSelectionState(e);
+ highlightSelectionStart = new Point(selectionStartX, selectionStartY);
selectionStartX = -1;
selectionStartY = -1;
+ if (selectionIsClick) {
+ notifyZoomOnClickListener(e.button);
+ }
if (selectionListener != null) {
selectionListener.run();
+ if (zoomToSelectionListener != null && !selectionIsClick) {
+ zoomToSelectionListener.run();
+ }
}
}
}
@Override
*** 162,182 ****
@Override
public void mouseHover(MouseEvent e) {
}
}
class Painter implements PaintListener {
@Override
public void paintControl(PaintEvent e) {
! Rectangle rect = getClientArea();
if (awtNeedsRedraw || !awtCanvas.hasImage(rect.width, rect.height)) {
Graphics2D g2d = awtCanvas.getGraphics(rect.width, rect.height);
- g2d.setColor(Color.WHITE);
- g2d.fillRect(0, 0, rect.width, rect.height);
Point adjusted = translateDisplayToImageCoordinates(rect.width, rect.height);
render(g2d, adjusted.x, adjusted.y);
if (highlightRects != null) {
updateHighlightRects();
}
awtNeedsRedraw = false;
}
--- 212,252 ----
@Override
public void mouseHover(MouseEvent e) {
}
}
+ private int numItems = 0;
+ public void setNumItems(int numItems) {
+ this.numItems = numItems;
+ }
+
+ private int getNumItems() {
+ return numItems;
+ }
+
class Painter implements PaintListener {
@Override
public void paintControl(PaintEvent e) {
! Rectangle rect = new Rectangle(0, 0, getParent().getSize().x, getParent().getSize().y);
! if (getNumItems() == 0) {
! rect = getClientArea();
! } else if (getNumItems() == 1 || (MIN_LANE_HEIGHT * getNumItems() < rect.height)) {
! // it fills the height
! } else {
! rect.height = MIN_LANE_HEIGHT * getNumItems();
! }
!
if (awtNeedsRedraw || !awtCanvas.hasImage(rect.width, rect.height)) {
Graphics2D g2d = awtCanvas.getGraphics(rect.width, rect.height);
Point adjusted = translateDisplayToImageCoordinates(rect.width, rect.height);
+ g2d.setColor(Palette.PF_BLACK_100.getAWTColor());
+ g2d.fillRect(0, 0, adjusted.x, adjusted.y);
render(g2d, adjusted.x, adjusted.y);
+ if (getParent() instanceof ScrolledComposite) {
+ ((ScrolledComposite) getParent()).setMinSize(rect.width, rect.height);
+ }
if (highlightRects != null) {
updateHighlightRects();
}
awtNeedsRedraw = false;
}
*** 285,294 ****
--- 355,372 ----
case '-':
zoom(-1);
break;
default:
switch (event.keyCode) {
+ case SWT.ESC:
+ awtChart.clearSelection();
+ if (selectionListener != null) {
+ selectionListener.run();
+ }
+ redrawChart();
+ redrawChartText();
+ break;
case SWT.ARROW_RIGHT:
pan(10);
break;
case SWT.ARROW_LEFT:
pan(-10);
*** 331,360 ****
private final double yScale = Display.getDefault().getDPI().y / Environment.getNormalDPI();
private final AwtCanvas awtCanvas = new AwtCanvas();
private boolean awtNeedsRedraw;
private Runnable selectionListener;
private IPropertyChangeListener aaListener;
private XYChart awtChart;
private MCContextMenuManager chartMenu;
public ChartCanvas(Composite parent) {
super(parent, SWT.NO_BACKGROUND);
addPaintListener(new Painter());
Selector selector = new Selector();
addMouseListener(selector);
addMouseMoveListener(selector);
- addMouseTrackListener(selector);
FocusTracker.enableFocusTracking(this);
- addListener(SWT.MouseVerticalWheel, new Zoomer());
addKeyListener(new KeyNavigator());
aaListener = new AntiAliasingListener();
UIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(aaListener);
addDisposeListener(e -> UIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(aaListener));
if (Environment.getOSType() == OSType.WINDOWS) {
addMouseTrackListener(new WheelStealingZoomer());
}
}
public IMenuManager getContextMenu() {
if (chartMenu == null) {
chartMenu = MCContextMenuManager.create(this);
--- 409,453 ----
private final double yScale = Display.getDefault().getDPI().y / Environment.getNormalDPI();
private final AwtCanvas awtCanvas = new AwtCanvas();
private boolean awtNeedsRedraw;
private Runnable selectionListener;
+ private Runnable zoomToSelectionListener;
+ private Consumer<Boolean> zoomOnClickListener;
private IPropertyChangeListener aaListener;
private XYChart awtChart;
private MCContextMenuManager chartMenu;
+ private ChartTextCanvas textCanvas;
public ChartCanvas(Composite parent) {
super(parent, SWT.NO_BACKGROUND);
addPaintListener(new Painter());
Selector selector = new Selector();
addMouseListener(selector);
addMouseMoveListener(selector);
FocusTracker.enableFocusTracking(this);
addKeyListener(new KeyNavigator());
aaListener = new AntiAliasingListener();
UIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(aaListener);
addDisposeListener(e -> UIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(aaListener));
if (Environment.getOSType() == OSType.WINDOWS) {
addMouseTrackListener(new WheelStealingZoomer());
}
+ if (getParent() instanceof ScrolledComposite) { // JFR Threads Page
+ ((ScrolledComposite) getParent()).getVerticalBar()
+ .addListener(SWT.Selection, e -> vBarScroll());
+ } else {
+ addMouseTrackListener(selector);
+ addListener(SWT.MouseVerticalWheel, new Zoomer());
+ }
+ }
+
+ private void vBarScroll() {
+ if (textCanvas != null) {
+ Point location = ((ScrolledComposite) getParent()).getOrigin();
+ textCanvas.syncScroll(location);
+ }
}
public IMenuManager getContextMenu() {
if (chartMenu == null) {
chartMenu = MCContextMenuManager.create(this);
*** 363,373 ****
return chartMenu;
}
private void render(Graphics2D context, int width, int height) {
if (awtChart != null) {
! awtChart.render(context, width, height);
}
}
/**
* Translates display coordinates into image coordinates for the chart.
--- 456,469 ----
return chartMenu;
}
private void render(Graphics2D context, int width, int height) {
if (awtChart != null) {
! awtChart.renderChart(context, width, height);
! if (textCanvas == null) {
! awtChart.renderText(context, width, height);
! }
}
}
/**
* Translates display coordinates into image coordinates for the chart.
*** 376,386 ****
* the provided x coordinate
* @param y
* the provided y coordinate
* @return a Point that represents the (x,y) coordinates in the chart's coordinate space
*/
! private Point translateDisplayToImageCoordinates(int x, int y) {
int xImage = (int) Math.round(x / xScale);
int yImage = (int) Math.round(y / yScale);
return new Point(xImage, yImage);
}
--- 472,482 ----
* the provided x coordinate
* @param y
* the provided y coordinate
* @return a Point that represents the (x,y) coordinates in the chart's coordinate space
*/
! protected Point translateDisplayToImageCoordinates(int x, int y) {
int xImage = (int) Math.round(x / xScale);
int yImage = (int) Math.round(y / yScale);
return new Point(xImage, yImage);
}
*** 389,402 ****
*
* @param x
* the provided display x coordinate
* @return the x coordinate in the chart's coordinate space
*/
! private int translateDisplayToImageXCoordinates(int x) {
return (int) Math.round(x / xScale);
}
public Object getHoveredItemData() {
return this.hoveredItemData;
}
public void setHoveredItemData(Object data) {
--- 485,509 ----
*
* @param x
* the provided display x coordinate
* @return the x coordinate in the chart's coordinate space
*/
! protected int translateDisplayToImageXCoordinates(int x) {
return (int) Math.round(x / xScale);
}
+ /**
+ * Translates a display x coordinate into an image x coordinate for the chart.
+ *
+ * @param x
+ * the provided display x coordinate
+ * @return the x coordinate in the chart's coordinate space
+ */
+ protected int translateDisplayToImageYCoordinates(int y) {
+ return (int) Math.round(y / yScale);
+ }
+
public Object getHoveredItemData() {
return this.hoveredItemData;
}
public void setHoveredItemData(Object data) {
*** 405,414 ****
--- 512,526 ----
public void resetHoveredItemData() {
this.hoveredItemData = null;
}
+ public void syncHighlightedRectangles (List<Rectangle2D> newRects) {
+ highlightRects = newRects;
+ redraw();
+ }
+
private void updateHighlightRects() {
List<Rectangle2D> newRects = new ArrayList<>();
infoAt(new IChartInfoVisitor.Adapter() {
@Override
public void visit(IBucket bucket) {
*** 445,454 ****
--- 557,569 ----
}
}, lastMouseX, lastMouseY);
// Attempt to reduce flicker by avoiding unnecessary updates.
if (!newRects.equals(highlightRects)) {
highlightRects = newRects;
+ if (textCanvas != null) {
+ textCanvas.syncHighlightedRectangles(highlightRects);
+ }
redraw();
}
}
private void clearHighlightRects() {
*** 483,495 ****
if ((awtChart != null) && awtChart.zoom(x, zoomInSteps)) {
redrawChart();
}
}
! private void select(int x1, int x2, int y1, int y2) {
! if ((awtChart != null) && awtChart.select(x1, x2, y1, y2)) {
redrawChart();
}
}
private void toggleSelect(int x, int y) {
Point p = translateDisplayToImageCoordinates(x, y);
--- 598,613 ----
if ((awtChart != null) && awtChart.zoom(x, zoomInSteps)) {
redrawChart();
}
}
! private void select(int x1, int x2, int y1, int y2, boolean clear) {
! Point p1 = translateDisplayToImageCoordinates(x1, y1);
! Point p2 = translateDisplayToImageCoordinates(x2, y2);
! if ((awtChart != null) && awtChart.select(p1.x, p2.x, p1.y, p2.y, clear)) {
redrawChart();
+ redrawChartText();
}
}
private void toggleSelect(int x, int y) {
Point p = translateDisplayToImageCoordinates(x, y);
*** 513,540 ****
range[1] = (x1 instanceof IQuantity) ? (IQuantity) x1 : null;
}
}
}, x, y);
if ((range[0] != null) || (range[1] != null)) {
! if (!awtChart.select(range[0], range[1], p.y, p.y)) {
awtChart.clearSelection();
}
} else {
! if (!awtChart.select(p.x, p.x, p.y, p.y)) {
awtChart.clearSelection();
}
}
redrawChart();
}
}
public void setChart(XYChart awtChart) {
this.awtChart = awtChart;
notifyListener();
redrawChart();
}
public void replaceRenderer(IXDataRenderer rendererRoot) {
assert awtChart != null;
awtChart.setRendererRoot(rendererRoot);
notifyListener();
redrawChart();
--- 631,668 ----
range[1] = (x1 instanceof IQuantity) ? (IQuantity) x1 : null;
}
}
}, x, y);
if ((range[0] != null) || (range[1] != null)) {
! if (!awtChart.select(range[0], range[1], p.y, p.y, true)) {
awtChart.clearSelection();
}
} else {
! if (!awtChart.select(p.x, p.x, p.y, p.y, true)) {
awtChart.clearSelection();
}
}
+ notifyZoomOnClickListener(SWT.MouseDown);
redrawChart();
+ redrawChartText();
}
}
public void setChart(XYChart awtChart) {
this.awtChart = awtChart;
notifyListener();
redrawChart();
}
+ public void setTextCanvas(ChartTextCanvas textCanvas) {
+ this.textCanvas = textCanvas;
+ }
+
+ public void syncScroll(Point scrollPoint) {
+ ((ScrolledComposite) getParent()).setOrigin(scrollPoint);
+ }
+
public void replaceRenderer(IXDataRenderer rendererRoot) {
assert awtChart != null;
awtChart.setRendererRoot(rendererRoot);
notifyListener();
redrawChart();
*** 542,557 ****
--- 670,703 ----
public void setSelectionListener(Runnable selectionListener) {
this.selectionListener = selectionListener;
}
+ public void setZoomToSelectionListener(Runnable zoomListener) {
+ this.zoomToSelectionListener = zoomListener;
+ }
+
+ public void setZoomOnClickListener(Consumer<Boolean> clickListener) {
+ this.zoomOnClickListener = clickListener;
+ }
+
+ private void notifyZoomOnClickListener(Integer button) {
+ if (zoomOnClickListener != null) {
+ zoomOnClickListener.accept(button == SWT.MouseDown);
+ }
+ }
+
private void notifyListener() {
if (selectionListener != null) {
selectionListener.run();
}
}
+ public void changeCursor(Cursor cursor) {
+ setCursor(cursor);
+ }
+
public void infoAt(IChartInfoVisitor visitor, int x, int y) {
Point p = translateDisplayToImageCoordinates(x, y);
if (awtChart != null) {
awtChart.infoAt(visitor, p.x, p.y);
}
*** 562,567 ****
--- 708,720 ----
*/
public void redrawChart() {
awtNeedsRedraw = true;
redraw();
}
+
+ private void redrawChartText() {
+ if (textCanvas != null) {
+ textCanvas.redrawChartText();
+ }
+ }
+
}
< prev index next >