< 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 >