1 /*******************************************************************************
2 * Copyright (c) 2017 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Semantum Oy - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.ui.workspace.tracker.internal.contributions;
14 import java.io.IOException;
15 import java.nio.file.FileStore;
16 import java.nio.file.Files;
17 import java.nio.file.Path;
19 import org.eclipse.e4.ui.model.application.ui.menu.MToolControl;
20 import org.eclipse.jface.action.Action;
21 import org.eclipse.jface.action.IMenuManager;
22 import org.eclipse.jface.action.MenuManager;
23 import org.eclipse.jface.preference.IPreferenceStore;
24 import org.eclipse.jface.util.IPropertyChangeListener;
25 import org.eclipse.osgi.util.NLS;
26 import org.eclipse.swt.SWT;
27 import org.eclipse.swt.graphics.Color;
28 import org.eclipse.swt.graphics.GC;
29 import org.eclipse.swt.graphics.Point;
30 import org.eclipse.swt.graphics.Rectangle;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.Control;
33 import org.eclipse.swt.widgets.Display;
34 import org.eclipse.swt.widgets.Listener;
35 import org.eclipse.swt.widgets.Menu;
36 import org.eclipse.swt.widgets.Shell;
37 import org.simantics.filesystem.services.sizetracker.SizeTracker;
38 import org.simantics.ui.workspace.tracker.IWorkspaceSizeTrackerConstants;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
43 * The Heap Status control, which shows the heap usage statistics in the window trim.
47 public class WorkspaceSizeTrackerTrim extends Composite {
49 private Logger logger = LoggerFactory.getLogger(WorkspaceSizeTrackerTrim.class);
51 private MToolControl toolControl;
52 private SizeTracker sizeTracker;
53 private IPreferenceStore prefStore;
56 * How many MB of free disk space means we are low on disk space?
58 private long lowSpaceThreshold = IWorkspaceSizeTrackerConstants.DEFAULT_LOW_SPACE_THRESHOLD;
59 private boolean highlightLowSpace = IWorkspaceSizeTrackerConstants.DEFAULT_HIGHLIGHT_LOW_SPACE;
60 private int updateInterval = IWorkspaceSizeTrackerConstants.DEFAULT_UPDATE_INTERVAL;
62 private Color bgCol, usedSpaceCol, lowSpaceCol, topLeftCol, bottomRightCol, sepCol, textCol;
63 @SuppressWarnings("unused")
64 private Color markCol;
66 private String storeName;
67 private long totalSpace;
68 private long availableSpace;
69 private long usedSpace;
70 private long prevTotalSpace = -1L;
71 private long prevAvailableSpace = -1L;
72 private long prevUsedSpace = -1L;
74 private boolean hasChanged;
75 private long mark = -1;
77 private boolean updateTooltip = false;
79 private final Runnable timer = new Runnable() {
91 getDisplay().timerExec(updateInterval, this);
96 private final IPropertyChangeListener prefListener = event -> {
97 if (IWorkspaceSizeTrackerConstants.PREF_UPDATE_INTERVAL.equals(event.getProperty())) {
98 setUpdateIntervalInMS(prefStore.getInt(IWorkspaceSizeTrackerConstants.PREF_UPDATE_INTERVAL));
99 } else if (IWorkspaceSizeTrackerConstants.PREF_HIGHLIGHT_LOW_SPACE.equals(event.getProperty())) {
100 highlightLowSpace = prefStore.getBoolean(IWorkspaceSizeTrackerConstants.PREF_HIGHLIGHT_LOW_SPACE);
102 } else if (IWorkspaceSizeTrackerConstants.PREF_LOW_SPACE_THRESHOLD.equals(event.getProperty())) {
103 lowSpaceThreshold = prefStore.getLong(IWorkspaceSizeTrackerConstants.PREF_LOW_SPACE_THRESHOLD);
105 } else if (IWorkspaceSizeTrackerConstants.PREF_SHOW_MONITOR.equals(event.getProperty())) {
106 boolean show = prefStore.getBoolean(IWorkspaceSizeTrackerConstants.PREF_SHOW_MONITOR);
113 * Creates a new heap status control with the given parent, and using
114 * the given preference store to obtain settings such as the refresh
118 * @param parent the parent composite
119 * @param sizeTracker the workspace sizeTracker service
120 * @param prefStore the preference store
122 public WorkspaceSizeTrackerTrim(Composite parent, MToolControl toolControl, SizeTracker sizeTracker, IPreferenceStore prefStore) {
123 super(parent, SWT.NONE);
124 this.toolControl = toolControl;
125 this.sizeTracker = sizeTracker;
127 this.prefStore = prefStore;
128 prefStore.addPropertyChangeListener(prefListener);
130 setUpdateIntervalInMS(prefStore.getInt(IWorkspaceSizeTrackerConstants.PREF_UPDATE_INTERVAL));
131 highlightLowSpace = prefStore.getBoolean(IWorkspaceSizeTrackerConstants.PREF_HIGHLIGHT_LOW_SPACE);
132 lowSpaceThreshold = prefStore.getLong(IWorkspaceSizeTrackerConstants.PREF_LOW_SPACE_THRESHOLD);
134 Display display = getDisplay();
135 usedSpaceCol = display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
136 lowSpaceCol = new Color(display, 255, 70, 70); // medium red
137 bgCol = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
138 sepCol = topLeftCol = display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
139 bottomRightCol = display.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
140 markCol = textCol = display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND);
144 Listener listener = event -> {
145 switch (event.type) {
150 if (event.widget == WorkspaceSizeTrackerTrim.this) {
151 paintComposite(event.gc);
155 if (event.button == 1) {
156 if (event.widget == WorkspaceSizeTrackerTrim.this) {
162 WorkspaceSizeTrackerTrim.this.updateTooltip = true;
166 if (event.widget == WorkspaceSizeTrackerTrim.this) {
167 WorkspaceSizeTrackerTrim.this.updateTooltip = false;
172 addListener(SWT.Dispose, listener);
173 addListener(SWT.MouseDown, listener);
174 addListener(SWT.Paint, listener);
175 addListener(SWT.MouseEnter, listener);
176 addListener(SWT.MouseExit, listener);
178 // make sure stats are updated before first paint
181 getDisplay().asyncExec(() -> {
183 getDisplay().timerExec(updateInterval, timer);
189 public void setBackground(Color color) {
194 public void setForeground(Color color) {
196 markCol = textCol = getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
198 markCol = textCol = color;
203 public Color getForeground() {
204 if (usedSpaceCol != null) {
207 return getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
210 private void setUpdateIntervalInMS(int interval) {
211 updateInterval = Math.max(100, interval);
214 private void doDispose() {
215 prefStore.removePropertyChangeListener(prefListener);
216 if (lowSpaceCol != null) {
217 lowSpaceCol.dispose();
222 public Point computeSize(int wHint, int hHint, boolean changed) {
223 GC gc = new GC(this);
224 Point p = gc.textExtent(Messages.WorkspaceSizeTrackerTrim_widthStr);
225 int height = p.y + 4;
227 return new Point(p.x, height);
231 * Creates the context menu
233 private void createContextMenu() {
234 MenuManager menuMgr = new MenuManager();
235 menuMgr.setRemoveAllWhenShown(true);
236 menuMgr.addMenuListener(menuMgr1 -> fillMenu(menuMgr1));
237 Menu menu = menuMgr.createContextMenu(this);
241 private void fillMenu(IMenuManager menuMgr) {
242 menuMgr.add(new SetMarkAction());
243 menuMgr.add(new ClearMarkAction());
244 menuMgr.add(new CloseSizeTrackerTrimAction());
248 * Sets the mark to the current usedMem level.
250 private void setMark() {
251 safeUpdateStats(); // get up-to-date stats before taking the mark
260 private void clearMark() {
266 private void paintComposite(GC gc) {
267 paintCompositeMaxUnknown(gc);
270 private void paintCompositeMaxUnknown(GC gc) {
271 Rectangle rect = getClientArea();
276 int sw = w - 1; // status width
277 long storeUsedSpace = totalSpace - availableSpace;
278 int uw = (int) (sw * storeUsedSpace / totalSpace); // used space width
279 int ux = x + 1 + uw; // used space right edge
281 gc.setBackground(bgCol);
283 gc.fillRectangle(rect);
285 boolean lowOnSpace = false;
286 if (highlightLowSpace) {
287 lowOnSpace = (1024L*1024L*lowSpaceThreshold) >= availableSpace;
290 gc.setForeground(sepCol);
291 gc.drawLine(ux, y, ux, y + h);
292 gc.setForeground(topLeftCol);
293 gc.drawLine(x, y, x+w, y);
294 gc.drawLine(x, y, x, y+h);
295 gc.setForeground(bottomRightCol);
296 gc.drawLine(x+w-1, y, x+w-1, y+h);
297 gc.drawLine(x, y+h-1, x+w, y+h-1);
299 gc.setBackground(lowOnSpace ? lowSpaceCol : usedSpaceCol);
300 gc.fillRectangle(x + 1, y + 1, uw, h - 2);
302 String s = NLS.bind(Messages.WorkspaceSizeTrackerTrim_status, convertToSizeString(usedSpace), convertToSizeString(availableSpace));
303 Point p = gc.textExtent(s);
304 int sx = (rect.width - 15 - p.x) / 2 + rect.x + 1;
305 int sy = (rect.height - 2 - p.y) / 2 + rect.y + 1;
306 gc.setForeground(textCol);
307 gc.drawString(s, sx, sy, true);
310 private void safeUpdateStats() {
313 } catch (IOException e) {
314 logger.error("Failed to update workspace size statistics.", e);
318 private void updateStats() throws IOException {
319 Path path = sizeTracker.path();
320 FileStore store = Files.getFileStore(path);
322 storeName = store.toString();
323 totalSpace = store.getTotalSpace();
324 availableSpace = store.getUsableSpace();
325 usedSpace = sizeTracker.size();
327 if (convertToMeg(prevTotalSpace) != convertToMeg(totalSpace)) {
328 prevTotalSpace = totalSpace;
329 this.hasChanged = true;
331 if (prevAvailableSpace != availableSpace) {
332 prevAvailableSpace = availableSpace;
333 this.hasChanged = true;
335 if (convertToMeg(prevUsedSpace) != convertToMeg(usedSpace)) {
336 prevUsedSpace = usedSpace;
337 this.hasChanged = true;
341 private void updateToolTip() {
342 String usedStr = convertToSizeString(usedSpace);
343 String availableStr = convertToSizeString(availableSpace);
344 String totalStr = convertToSizeString(totalSpace);
345 String markStr = mark == -1 ? Messages.WorkspaceSizeTrackerTrim_noMark : convertToSizeString(mark);
346 String toolTip = NLS.bind(Messages.WorkspaceSizeTrackerTrim_memoryToolTip, new Object[] { usedStr, storeName, availableStr, totalStr, markStr });
347 if (!toolTip.equals(getToolTipText())) {
348 setToolTipText(toolTip);
353 * Converts the given number of bytes to a printable number of megabytes (rounded up).
355 private String convertToSizeString(long numBytes) {
356 long megs = convertToMeg(numBytes);
358 double megsd = (double) megs;
359 long gigs = (long) Math.floor(megsd / 1024.0);
360 long decimals = (long) (megsd - gigs*1024);
361 decimals = (decimals + 5) / 10;
362 return NLS.bind(Messages.WorkspaceSizeTrackerTrim_gig, new Long(gigs), new Long(decimals));
364 return NLS.bind(Messages.WorkspaceSizeTrackerTrim_meg, new Long(megs));
369 * Converts the given number of bytes to the corresponding number of megabytes (rounded up).
371 private long convertToMeg(long numBytes) {
372 return (numBytes + (512 * 1024)) / (1024 * 1024);
375 class SetMarkAction extends Action {
377 super(Messages.SetMarkAction_text);
386 class ClearMarkAction extends Action {
388 super(Messages.ClearMarkAction_text);
397 class CloseSizeTrackerTrimAction extends Action{
398 CloseSizeTrackerTrimAction(){
399 super(Messages.WorkspaceSizeTrackerTrim_close);
408 private void showTracker(boolean show) {
409 if (toolControl.isToBeRendered() != show) {
410 Object widget = toolControl.getWidget();
411 Shell shell = widget instanceof Control ? ((Control) widget).getShell() : null;
412 toolControl.setToBeRendered(show);
414 shell.layout(null, SWT.ALL | SWT.CHANGED | SWT.DEFER);
415 prefStore.setValue(IWorkspaceSizeTrackerConstants.PREF_SHOW_MONITOR, show);