All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openide.actions.HeapView Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.openide.actions;

import java.awt.AWTEvent;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.prefs.Preferences;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import org.openide.util.NbBundle;
import org.openide.util.NbPreferences;

/**
 * Implements a widget to show the current heap size / max and to trigger a manual GC.
 * Appearance can be customized using the following properties in the LAF
 * 
    *
  • nb.heapview.background - Color of widget background *
  • nb.heapview.foreground - Color of text *
  • nb.heapview.chart - Color of area chart *
  • nb.heapview.highlight - Color of outline around the text, to provide a contrast against * the chart (may have a non-opaque alpha value) *
* @author sky, radim, peter */ class HeapView extends JComponent { private static final boolean AUTOMATIC_REFRESH = System.getProperty("org.netbeans.log.startup") == null; /* * How often the display is updated. */ private static final int TICK = 1500; /** * Foreground color for the chart. */ private static final Color CHART_COLOR; /** * Color for text. */ private static final Color TEXT_COLOR; /** * Color for an outline around the text. */ private static final Color OUTLINE_COLOR; /** * Color for the background. */ private static final Color BACKGROUND_COLOR; /** * Number of samples to retain in history. */ private static final int GRAPH_COUNT = 100; /** * Key for the Show Text preference. */ private static final String SHOW_TEXT = "showText"; static { //init colors Color c = UIManager.getColor("nb.heapview.chart"); //NOI18N if (null == c) { c = new Color(0x2E90E8); } CHART_COLOR = c; c = UIManager.getColor("nb.heapview.foreground"); //NOI18N if (null == c) { c = Color.DARK_GRAY; } TEXT_COLOR = c; c = UIManager.getColor("nb.heapview.background"); //NOI18N if (null == c) { c = new Color(0xCEDBE6); } BACKGROUND_COLOR = c; c = UIManager.getColor("nb.heapview.highlight"); //NOI18N if (null == c) { c = new Color(BACKGROUND_COLOR.getRed(), BACKGROUND_COLOR.getGreen(), BACKGROUND_COLOR.getBlue(), 192); } OUTLINE_COLOR = c; } /** * MessageFormat used to generate text. */ private final MessageFormat format; /** * Data for the graph as a percentage of the heap used. * It is a circular buffer. */ private final long[] graph = new long[GRAPH_COUNT]; /** * Index into graph for the next tick. */ private int graphIndex; /** * Last total heap size. */ private long lastTotal; /** * Timer used to update data. */ private Timer updateTimer; /** * Max width needed to display 999.9/999.9MB. Used to calculate pref size. */ private int maxTextWidth; /** * Current text being displayed. */ private String heapSizeText; public HeapView() { format = new MessageFormat("{0,choice,0#{0,number,0.0}|999<{0,number,0}}/{1,choice,0#{1,number,0.0}|999<{1,number,0}}MB"); heapSizeText = ""; // Enable mouse events. This is the equivalent to adding a mouse // listener. enableEvents(AWTEvent.MOUSE_EVENT_MASK); setToolTipText(NbBundle.getMessage(GarbageCollectAction.class, "CTL_GC")); updateUI(); } /** * Updates the look and feel for this component. */ @Override public void updateUI() { Font f = UIManager.getFont("Label.font"); setFont(f); /* Setting this true seems to cause some painting artifacts on 150% scaling, as we don't always manage to fill every device pixel with the background color. So leave it off. */ setOpaque(false); } /** * Sets whether the text displaying the heap size should be shown. The * default is true. * * @param showText whether the text displaying the heap size should be * shown. */ public void setShowText(boolean showText) { prefs().putBoolean(SHOW_TEXT, showText); repaint(); } /** * Returns whether the text displaying the heap size should be shown. * * @return whether the text displaying the heap size should be shown */ public boolean getShowText() { return prefs().getBoolean(SHOW_TEXT, true); } /** * Sets the font used to display the heap size. * * @param font the font used to display the heap size */ @Override public void setFont(Font font) { super.setFont(font); updateTextWidth(); } // Called by GarbageCollectAction Dimension heapViewPreferredSize() { Dimension size = new Dimension(maxTextWidth + 8, getFontMetrics( getFont()).getHeight() + 8); return size; } private Preferences prefs() { return NbPreferences.forModule(HeapView.class); } /** * Recalculates the width needed to display the heap size string. */ private void updateTextWidth() { String maxString = format.format(new Object[]{888.8f, 888.8f}); maxTextWidth = getFontMetrics(getFont()).stringWidth(maxString) + 4; } /** * Processes a mouse event. * * @param e the MouseEvent */ @Override protected void processMouseEvent(MouseEvent e) { super.processMouseEvent(e); if (!e.isConsumed()) { if (e.isPopupTrigger()) { // Show a popup allowing to configure the various options showPopup(e.getX(), e.getY()); } } if (e.getID() == MouseEvent.MOUSE_CLICKED && SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 1) { // Trigger a gc GarbageCollectAction.get(GarbageCollectAction.class).performAction(); } } /** * Shows a popup at the specified location that allows you to configure the * various options. */ private void showPopup(int x, int y) { JPopupMenu popup = new JPopupMenu(); JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(NbBundle.getMessage(HeapView.class, "LBL_ShowText")); cbmi.setSelected(getShowText()); cbmi.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setShowText(((JCheckBoxMenuItem) e.getSource()). isSelected()); } }); popup.add(cbmi); popup.show(this, x, y); } /** * Paints the component. */ @Override protected void paintComponent(Graphics g) { super.paintComponent(g); int width = getWidth(); int height = getHeight(); if (width > 0 && height > 0) { startTimerIfNecessary(); Graphics2D g2 = (Graphics2D) g.create(); try { g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); g2.clipRect(0, 0, width, height); // Draw background. g2.setColor(BACKGROUND_COLOR); g2.fillRect(0, 0, width, height); // Draw samples g2.setColor(CHART_COLOR); paintSamples(g2, width, height); // Draw text if enabled if (getShowText()) { paintText(g2, width, height); } } finally { g2.dispose(); } } else { stopTimerIfNecessary(); } } /** * Renders the text using an optional drop shadow. */ private void paintText(Graphics2D g, int w, int h) { Font font = getFont(); String text = getHeapSizeText(); GlyphVector gv = font.createGlyphVector(g.getFontRenderContext(), text); FontMetrics fm = g.getFontMetrics(font); Shape outline = gv.getOutline(); Rectangle2D bounds = outline.getBounds2D(); double x = Math.max(0, (w - bounds.getWidth()) / 2.0); double y = h / 2.0 + fm.getAscent() / 2.0 - 2.0; AffineTransform oldTransform = g.getTransform(); g.translate(x, y); g.setColor(OUTLINE_COLOR); g.setStroke(new BasicStroke(2.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); g.draw(outline); g.setColor(TEXT_COLOR); g.fill(outline); g.setTransform(oldTransform); } private String getHeapSizeText() { return heapSizeText; } /** * Invoked when component removed from a heavy weight parent. Stops the * timer. */ @Override public void removeNotify() { super.removeNotify(); stopTimerIfNecessary(); } /** * Restarts the timer. */ private void startTimerIfNecessary() { if (!AUTOMATIC_REFRESH) { return; } if (updateTimer == null) { updateTimer = new Timer(TICK, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { update(); } }); updateTimer.setRepeats(true); updateTimer.start(); } } /** * Stops the timer. */ private void stopTimerIfNecessary() { if (updateTimer != null) { updateTimer.stop(); updateTimer = null; lastTotal = 0; Arrays.fill(graph, 0); heapSizeText = ""; } } /** * Invoked when the update timer fires. Updates the necessary data * structures and triggers repaints. */ private void update() { if (isShowing()) { Runtime r = Runtime.getRuntime(); long total = r.totalMemory(); long used = total - r.freeMemory(); graph[graphIndex] = used; lastTotal = total; ++graphIndex; if (graphIndex >= GRAPH_COUNT) { graphIndex = 0; } heapSizeText = format.format( new Object[]{(double) used / (1024.0 * 1024.0), (double) total / (1024.0 * 1024.0)}); repaint(); } else { // Either we've become invisible, or one of our ancestors has. // Stop the timer and bale. Next paint will trigger timer to // restart. stopTimerIfNecessary(); } } /** * Draw the graph with the heap samples. It is a simple area chart. * * @param g Where to draw * @param width The width of the chart * @param height The height of the chart */ private void paintSamples(Graphics2D g, int width, int height) { Path2D path = new Path2D.Double(); path.moveTo(0, height); for (int i = 0; i < GRAPH_COUNT; ++i) { int index = (i + graphIndex) % GRAPH_COUNT; double x = (double) i / (double) (GRAPH_COUNT - 1) * (double) width; double y = (double) height * (1.0 - (double) graph[index] / (double) lastTotal); path.lineTo(x, y); } path.lineTo(width, height); path.closePath(); g.fill(path); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy