/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed 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 com.intellij.openapi.wm.impl.status;
import com.intellij.concurrency.JobScheduler;
import com.intellij.ide.ui.UISettings;
import com.intellij.openapi.wm.CustomStatusBarWidget;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.StatusBarWidget;
import com.intellij.ui.Gray;
import com.intellij.ui.JBColor;
import com.intellij.ui.UIBundle;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.update.Activatable;
import com.intellij.util.ui.update.UiNotifyConnector;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class MemoryUsagePanel extends JButton implements CustomStatusBarWidget {
@NonNls public static final String WIDGET_ID = "Memory";
private static final int MEGABYTE = 1024 * 1024;
@NonNls private static final String SAMPLE_STRING;
static {
long maxMemory = Math.min(Runtime.getRuntime().maxMemory() / MEGABYTE, 9999);
SAMPLE_STRING = maxMemory + " of " + maxMemory + "M ";
}
private static final Color USED_COLOR = new JBColor(Gray._185, Gray._110);
private static final Color UNUSED_COLOR = new JBColor(Gray._200.withAlpha(100), Gray._90);
private long myLastTotal = -1;
private long myLastUsed = -1;
private Image myBufferedImage;
private boolean myWasPressed;
public MemoryUsagePanel() {
setOpaque(false);
setFocusable(false);
addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.gc();
updateState();
}
});
setBorder(StatusBarWidget.WidgetBorder.INSTANCE);
updateUI();
new UiNotifyConnector(this, new Activatable() {
private ScheduledFuture<?> myFuture;
@Override
public void showNotify() {
myFuture = JobScheduler.getScheduler().scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
if (isDisplayable()) {
updateState();
}
}
}, 1, 5, TimeUnit.SECONDS);
}
@Override
public void hideNotify() {
if (myFuture != null) {
myFuture.cancel(true);
myFuture = null;
}
}
});
}
@Override
public void dispose() {
}
@Override
public void install(@NotNull StatusBar statusBar) { }
@Override
@Nullable
public WidgetPresentation getPresentation(@NotNull PlatformType type) {
return null;
}
@Override
@NotNull
public String ID() {
return WIDGET_ID;
}
public void setShowing(final boolean showing) {
if (showing != isVisible()) {
setVisible(showing);
revalidate();
}
}
@Override
public void updateUI() {
super.updateUI();
myBufferedImage = null;
setFont(getWidgetFont());
}
private static Font getWidgetFont() {
return JBUI.Fonts.label(11);
}
@Override
public JComponent getComponent() {
return this;
}
@Override
public void paintComponent(final Graphics g) {
final boolean pressed = getModel().isPressed();
final boolean stateChanged = myWasPressed != pressed;
myWasPressed = pressed;
if (myBufferedImage == null || stateChanged) {
final Dimension size = getSize();
final Insets insets = getInsets();
myBufferedImage = UIUtil.createImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2 = (Graphics2D)myBufferedImage.getGraphics().create();
final Runtime rt = Runtime.getRuntime();
final long maxMem = rt.maxMemory();
final long allocatedMem = rt.totalMemory();
final long unusedMem = rt.freeMemory();
final long usedMem = allocatedMem - unusedMem;
final int totalBarLength = size.width - insets.left - insets.right;
final int usedBarLength = (int)(totalBarLength * usedMem / maxMem);
final int unusedBarLength = (int)(totalBarLength * unusedMem / maxMem);
final int barHeight = Math.max(size.height, getFont().getSize() + JBUI.scale(2));
final int yOffset = (size.height - barHeight) / 2;
final int xOffset = insets.left;
// background
g2.setColor(UIUtil.getPanelBackground());
g2.fillRect(0, 0, size.width, size.height);
// gauge (used)
g2.setColor(USED_COLOR);
g2.fillRect(xOffset, yOffset, usedBarLength, barHeight);
// gauge (unused)
g2.setColor(UNUSED_COLOR);
g2.fillRect(xOffset + usedBarLength, yOffset, unusedBarLength, barHeight);
// label
g2.setFont(getFont());
final long used = usedMem / MEGABYTE;
final long total = maxMem / MEGABYTE;
final String info = UIBundle.message("memory.usage.panel.message.text", used, total);
final FontMetrics fontMetrics = g.getFontMetrics();
final int infoWidth = fontMetrics.charsWidth(info.toCharArray(), 0, info.length());
final int infoHeight = fontMetrics.getAscent();
UISettings.setupAntialiasing(g2);
final Color fg = pressed ? UIUtil.getLabelDisabledForeground() : JBColor.foreground();
g2.setColor(fg);
g2.drawString(info, xOffset + (totalBarLength - infoWidth) / 2, yOffset + infoHeight + (barHeight - infoHeight) / 2 - JBUI.scale(1));
g2.dispose();
}
UIUtil.drawImage(g, myBufferedImage, 0, 0, null);
if (UIUtil.isRetina() && !UIUtil.isUnderDarcula()) {
Graphics2D g2 = (Graphics2D)g.create(0, 0, getWidth(), getHeight());
g2.scale(0.5, 0.5);
g2.setColor(Gray.x91);
g2.drawLine(0,0,2 * getWidth(), 0);
g2.scale(1, 1);
g2.dispose();
}
}
@Override
public Dimension getPreferredSize() {
final Insets insets = getInsets();
int width = getFontMetrics(getWidgetFont()).stringWidth(SAMPLE_STRING) + insets.left + insets.right + JBUI.scale(2);
int height = getFontMetrics(getWidgetFont()).getHeight() + insets.top + insets.bottom + JBUI.scale(2);
return new Dimension(width, height);
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
@Override
public Dimension getMaximumSize() {
return getPreferredSize();
}
private void updateState() {
if (!isShowing()) {
return;
}
final Runtime runtime = Runtime.getRuntime();
final long total = runtime.totalMemory() / MEGABYTE;
final long used = total - runtime.freeMemory() / MEGABYTE;
if (total != myLastTotal || used != myLastUsed) {
myLastTotal = total;
myLastUsed = used;
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
myBufferedImage = null;
repaint();
}
});
setToolTipText(UIBundle.message("memory.usage.panel.statistics.message", total, used));
}
}
}