/* * Copyright (c) 2007, 2011, 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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.tools.visualvm.core.ui.components; import java.awt.AWTException; import java.awt.Color; import java.awt.Dimension; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.ImageCapabilities; import java.awt.Insets; import java.awt.image.VolatileImage; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JComponent.AccessibleJComponent; import javax.swing.border.BevelBorder; /** * Graphical component for fall-off level indicator * @author Jaroslav Bachorik */ public class LevelIndicator extends JComponent { //~ Static fields/initializers ----------------------------------------------------------------------------------------------- private static final Insets NULL_INSETS = new Insets(0, 0, 0, 0); private static final Dimension PREFERRED_SIZE = new Dimension(40, 20); //~ Instance fields ---------------------------------------------------------------------------------------------------------- private Color maximumColor = Color.RED; private Color minimumColor = Color.GREEN; private Color peakColor = null; private Dimension canvasDimension = null; private Insets canvasInsets = NULL_INSETS; private boolean autoRepaint = true; private boolean followPeak; private int peakMarkSize = 8; // peak mark size in pixels private long max = 0; private long min = 0; private long peak = Long.MIN_VALUE; private long val = min; //~ Constructors ------------------------------------------------------------------------------------------------------------- public LevelIndicator() { setPreferredSize(PREFERRED_SIZE); setMinimumSize(PREFERRED_SIZE); setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); } //~ Methods ------------------------------------------------------------------------------------------------------------------ /** * Sets the auto repainting of the component on/off * @param autoRepaint The auto repainting on/off */ public void setAutoRepaint(boolean autoRepaint) { this.autoRepaint = autoRepaint; } /** * Status of the component auto repainting * @return Returns the status of the component auto repainting */ public boolean isAutoRepaint() { return autoRepaint; } /** * When set the component will mark the so-far highest value * @param markPeaks Set peak following on/off */ public void setFollowPeak(boolean markPeaks) { this.followPeak = markPeaks; } /** * Status of the follow-peak * @return Returns the status of the follow-peak property */ public boolean isFollowPeak() { return followPeak; } /** * Sets the component maximum value * Will repaint the component if {@linkplain #isAutoRepaint() } is on * @param max The maximum value the component would display */ public void setMaximum(long max) { this.max = max; if (autoRepaint) { repaint(); } } /** * Returns the component maximum value * @return Returns the component maximum value */ public long getMaximum() { return max; } /** * With this property you can control the {@linkplain Color} of the highest value * Will repaint the component if {@linkplain #isAutoRepaint() } is on * @param maximumColor The {@linkplain Color} to be used for the highest values */ public void setMaximumColor(Color maximumColor) { this.maximumColor = maximumColor; } /** * Returns the {@linkplain Color} of the highest values * @return Returns the {@linkplain Color} of the highest values */ public Color getMaximumColor() { return maximumColor; } /** * Sets the minimum displayable value of the component * Will repaint the component if {@linkplain #isAutoRepaint() } is on * @param min The minimal value that should be displayed */ public void setMinimum(long min) { this.min = min; if (autoRepaint) { repaint(); } } /** * Returns the component minimal value * @return Returns the component minimal value */ public long getMinimum() { return min; } /** * Sets the {@linkplain Color} for the minimal value * Will repaint the component if {@linkplain #isAutoRepaint() } is on * @param minimumColor The {@linkplain Color} to use for the minimal value */ public void setMinimumColor(Color minimumColor) { this.minimumColor = minimumColor; } /** * Returns the {@linkplain Color} used for the minimal value * @return Returns the {@linkplain Color} used for the minimal value */ public Color getMinimumColor() { return minimumColor; } /** * Manually sets the actual value as the peak * Will repaint the component if {@linkplain #isAutoRepaint() } is on */ public void setPeak() { peak = val; if (autoRepaint) { repaint(); } } /** * Manually sets the peak * Will repaint the component if {@linkplain #isAutoRepaint() } is on * @param value The value to set as the peak */ public void setPeak(long value) { if (peak <= max) { peak = value; if (autoRepaint) { repaint(); } } } /** * Returns the current peak value * @return Returns the current peak value */ public long getPeak() { return peak; } /** * Sets the peak mark size in pixels * The peak mark is rendered in the indicator at the place of peak value * @param peakMarkSize The peak mark size in pixels */ public void setPeakMarkSize(int peakMarkSize) { this.peakMarkSize = peakMarkSize; if (autoRepaint) { repaint(); } } /** * Returns the peak mark size in pixels * The peak mark is rendered in the indicator at the place of peak value * @return Returns the peak mark size in pixels */ public int getPeakMarkSize() { return peakMarkSize; } /** * Sets the current value of the inidicator * @param value The current value */ public void setValue(long value) { val = Math.max(Math.min(value, max), 0); if (followPeak && (val > peak)) { peak = this.val; } if (autoRepaint) { repaint(); } } /** * Returns the current value * @return Returns the current value */ public long getValue() { return val; } @Override public void doLayout() { super.doLayout(); canvasInsets = getInsets(); canvasDimension = new Dimension(getBounds().width - (canvasInsets.left + canvasInsets.right), getBounds().height - (canvasInsets.top + canvasInsets.bottom)); } @Override public void paintComponent(Graphics g) { if (canvasDimension == null) { return; } if ((canvasDimension.getHeight() < 0) || (canvasDimension.getWidth() < 0)) { return; // no rendering if dimensions are negative } try { VolatileImage img = createVolatileImage(getBounds().width - (canvasInsets.left + canvasInsets.right), getBounds().height - (canvasInsets.top + canvasInsets.bottom), new ImageCapabilities(true)); Graphics2D gr = img.createGraphics(); renderLevel(gr, img); renderPeak(gr); gr.dispose(); g.drawImage(img, canvasInsets.left, canvasInsets.top, this); // // if (getBorder() != null) { // getBorder().paintBorder(this, g, 0, 0, getBounds().width, getBounds().height); // } } catch (AWTException e) { } } /** * Cleans the peak mark * Will repaint the component */ public void unsetPeak() { peak = Integer.MIN_VALUE; repaint(); } /** * Gets a dummy AccessibleContext associated with this LevelIndicator. * * @return a dummy AccessibleContext associated with this LevelIndicator */ public AccessibleContext getAccessibleContext() { if (accessibleContext == null) { accessibleContext = new AccessibleLevelIndicator(); } return accessibleContext; } private Color getColorAt(VolatileImage img, int x, int y) { if ((x <= 0) || (y < 0) || (x > img.getWidth()) || (y > img.getHeight())) { return null; } int RGB = img.getSnapshot().getRGB(x - 1, 0); int red = (RGB & 0x00ff0000) >> 16; int green = (RGB & 0x0000ff00) >> 8; int blue = RGB & 0x000000ff; // and the Java Color is ... return new Color(red, green, blue); } private int getPosition(long value) { float ratio = (float) value / (float) (max - min); return Math.round((float) (ratio * canvasDimension.getWidth())); } private void renderLevel(Graphics2D gr, VolatileImage img) { gr.setPaint(new GradientPaint(0, 0, minimumColor, canvasDimension.width, 0, maximumColor)); gr.fillRect(0, 0, canvasDimension.width, canvasDimension.height); if (peak > Integer.MIN_VALUE) { peakColor = getColorAt(img, getPosition(peak), 0); } int position = getPosition(val); gr.setPaint(getBackground()); gr.fillRect(position, 0, canvasDimension.width - position + 1, canvasDimension.height); } private void renderPeak(Graphics2D gr) { if (peakColor == null) { return; } int position = getPosition(peak); int decrement = 0; int left = 0; int right = 0; do { left = Math.round(position - ((peakMarkSize - decrement) / 2f)); right = Math.round(left + ((peakMarkSize - decrement) / 2f)); if (left < 0) { right += Math.abs(left); left = 0; } if (right > canvasDimension.getWidth()) { left -= (right - canvasDimension.getWidth()); right = (int) canvasDimension.getWidth(); } decrement++; } while (((left < 0) || (right > canvasDimension.getWidth())) && (left != right)); gr.setPaint(peakColor); gr.fillRect(left, 0, right - left + 1, canvasDimension.height); } /** * Dummy AccessibleContext implementation for the LevelIndicator. */ private class AccessibleLevelIndicator extends AccessibleJComponent { /** * Get the role of this object. * * @return an instance of AccessibleRole describing the role of the * object */ public AccessibleRole getAccessibleRole() { return AccessibleRole.SWING_COMPONENT; } } }