--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.workbench.internal.contributions;\r
+\r
+import org.eclipse.jface.action.Action;\r
+import org.eclipse.jface.action.IMenuListener;\r
+import org.eclipse.jface.action.IMenuManager;\r
+import org.eclipse.jface.action.MenuManager;\r
+import org.eclipse.jface.preference.IPreferenceStore;\r
+import org.eclipse.jface.util.IPropertyChangeListener;\r
+import org.eclipse.jface.util.PropertyChangeEvent;\r
+import org.eclipse.osgi.util.NLS;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.graphics.Color;\r
+import org.eclipse.swt.graphics.GC;\r
+import org.eclipse.swt.graphics.Point;\r
+import org.eclipse.swt.graphics.Rectangle;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Display;\r
+import org.eclipse.swt.widgets.Event;\r
+import org.eclipse.swt.widgets.Listener;\r
+import org.eclipse.swt.widgets.Menu;\r
+import org.eclipse.ui.internal.TrimUtil;\r
+import org.eclipse.ui.internal.WorkbenchMessages;\r
+import org.simantics.db.MonitorContext;\r
+import org.simantics.db.MonitorHandler;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.SessionReference;\r
+import org.simantics.db.SessionVariables;\r
+import org.simantics.db.service.LifecycleSupport;\r
+import org.simantics.db.service.SessionMonitorSupport;\r
+\r
+/**\r
+ * The graph request status control, which shows the current graph request state\r
+ * of a workbench window in the window trim.\r
+ * <p>\r
+ * Adapted from Eclipse's Heap Status control.\r
+ * </p>\r
+ */\r
+public class GraphRequestStatusTrim extends Composite {\r
+\r
+ final String requestStatusMessage = "{0}R / {1}W";\r
+ final String requestToolTip = "Session: {0}\nPending: {1} read requests, {2} write requests";\r
+\r
+ private boolean armed;\r
+ //private final Image gcImage;\r
+ private final Color bgCol, lowMemCol, freeMemCol;\r
+ private Color topLeftCol;\r
+ private final Color bottomRightCol, sepCol, textCol;\r
+ //private Color armCol;\r
+ //private final Canvas button;\r
+ private final IPreferenceStore prefStore;\r
+ private final int updateInterval = 200;\r
+ private boolean hasChanged;\r
+ // start with 12x12\r
+ private Rectangle imgBounds = new Rectangle(0,0,12,12);\r
+\r
+ private SessionMonitorSupport session;\r
+ private MonitorContext monitorContext;\r
+ private long lastRedrawTime = 0;\r
+\r
+ class Status {\r
+ SessionReference sessionRef;\r
+ int reads;\r
+ int writes;\r
+\r
+ void clear() {\r
+ sessionRef = null;\r
+ reads = 0;\r
+ writes = 0;\r
+ }\r
+ }\r
+\r
+ private final Status status = new Status();\r
+\r
+ private final MonitorHandler monitorHandler = new MonitorHandler() {\r
+ @Override\r
+ public void valuesChanged(MonitorContext c) {\r
+ //updateStats();\r
+ long time = System.currentTimeMillis();\r
+ // Require that 100ms has passed before refreshing again.\r
+ if ((time - 100) > lastRedrawTime) {\r
+ lastRedrawTime = time;\r
+ if (!isDisposed()) {\r
+ getDisplay().asyncExec(timer);\r
+ }\r
+ }\r
+ }\r
+ };\r
+\r
+ private final Runnable timer = new Runnable() {\r
+ public void run() {\r
+ if (!isDisposed()) {\r
+ updateStats();\r
+ if (hasChanged) {\r
+ updateToolTip();\r
+ redraw();\r
+ hasChanged = false;\r
+ }\r
+ getDisplay().timerExec(updateInterval, this);\r
+ }\r
+ }\r
+ };\r
+\r
+ private final IPropertyChangeListener prefListener = new IPropertyChangeListener() {\r
+ public void propertyChange(PropertyChangeEvent event) {\r
+// if (IHeapStatusConstants.PREF_UPDATE_INTERVAL.equals(event.getProperty())) {\r
+// setUpdateIntervalInMS(prefStore.getInt(IHeapStatusConstants.PREF_UPDATE_INTERVAL));\r
+// }\r
+// else if (IHeapStatusConstants.PREF_SHOW_MAX.equals(event.getProperty())) {\r
+// showMax = prefStore.getBoolean(IHeapStatusConstants.PREF_SHOW_MAX);\r
+// }\r
+ }\r
+ };\r
+\r
+ public void attachToSession(Session session) {\r
+ if (session != this.session) {\r
+ if (monitorContext != null) {\r
+ this.session.unregisterMonitor(monitorContext);\r
+ monitorContext = null;\r
+ this.session = null;\r
+ status.clear();\r
+ }\r
+\r
+ SessionMonitorSupport support = null;\r
+ if (session != null)\r
+ support = session.peekService(SessionMonitorSupport.class);\r
+ if (support == null) {\r
+ hasChanged = true;\r
+ return;\r
+ }\r
+\r
+ this.session = support;\r
+ monitorContext = this.session.registerMonitor(monitorHandler);\r
+ LifecycleSupport lifecycleSupport = session.getService(LifecycleSupport.class);\r
+ status.sessionRef = lifecycleSupport.getSessionReference();\r
+ getDisplay().asyncExec(timer);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Creates a new heap status control with the given parent, and using\r
+ * the given preference store to obtain settings such as the refresh\r
+ * interval.\r
+ * \r
+ * @param parent the parent composite\r
+ * @param prefStore the preference store\r
+ */\r
+ public GraphRequestStatusTrim(Composite parent, IPreferenceStore prefStore) {\r
+ super(parent, SWT.NONE);\r
+\r
+ this.prefStore = prefStore;\r
+ prefStore.addPropertyChangeListener(prefListener);\r
+\r
+// setUpdateIntervalInMS(prefStore.getInt(IHeapStatusConstants.PREF_UPDATE_INTERVAL));\r
+// showMax = prefStore.getBoolean(IHeapStatusConstants.PREF_SHOW_MAX);\r
+\r
+ //button = new Canvas(this, SWT.NONE);\r
+\r
+// ImageDescriptor imageDesc = SimanticsUI.getImageDescriptor("icons/etool16/wrench.png"); //$NON-NLS-1$\r
+// gcImage = imageDesc.createImage();\r
+// if (gcImage != null) {\r
+// imgBounds = gcImage.getBounds();\r
+// }\r
+ Display display = getDisplay();\r
+// usedMemCol = display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);\r
+ lowMemCol = new Color(display, 255, 70, 70); // medium red\r
+ freeMemCol = new Color(display, 255, 190, 125); // light orange\r
+ bgCol = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);\r
+ sepCol = topLeftCol = /*armCol =*/ display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);\r
+ bottomRightCol = display.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);\r
+ textCol = display.getSystemColor(SWT.COLOR_INFO_FOREGROUND);\r
+// markCol = textCol;\r
+\r
+ createContextMenu();\r
+\r
+ Listener listener = new Listener() {\r
+\r
+ public void handleEvent(Event event) {\r
+ switch (event.type) {\r
+ case SWT.Dispose:\r
+ doDispose();\r
+ break;\r
+// case SWT.Resize:\r
+// Rectangle rect = getClientArea();\r
+// button.setBounds(rect.width - imgBounds.width - 1, 1, imgBounds.width, rect.height - 2);\r
+// break;\r
+ case SWT.Paint:\r
+ if (event.widget == GraphRequestStatusTrim.this) {\r
+ paintComposite(event.gc);\r
+ }\r
+// else if (event.widget == button) {\r
+// paintButton(event.gc);\r
+// }\r
+ break;\r
+ case SWT.MouseUp:\r
+ if (event.button == 1) {\r
+ // TODO: if the DB is locked, somehow release it ?\r
+ arm(false);\r
+ }\r
+ break;\r
+ case SWT.MouseDown:\r
+ if (event.button == 1) {\r
+ if (event.widget == GraphRequestStatusTrim.this) {\r
+// setMark();\r
+// } else if (event.widget == button) {\r
+// arm(true);\r
+ }\r
+ }\r
+ break;\r
+ case SWT.MouseExit:\r
+ arm(false);\r
+ break;\r
+ }\r
+ }\r
+\r
+ };\r
+ addListener(SWT.Dispose, listener);\r
+ addListener(SWT.MouseDown, listener);\r
+ addListener(SWT.Paint, listener);\r
+ addListener(SWT.Resize, listener);\r
+// button.addListener(SWT.MouseDown, listener);\r
+// button.addListener(SWT.MouseExit, listener);\r
+// button.addListener(SWT.MouseUp, listener);\r
+// button.addListener(SWT.Paint, listener);\r
+\r
+ // make sure stats are updated before first paint\r
+ //updateStats();\r
+\r
+ getDisplay().asyncExec(new Runnable() {\r
+ public void run() {\r
+ if (!isDisposed()) {\r
+ getDisplay().timerExec(updateInterval, timer);\r
+ }\r
+ }\r
+ });\r
+ }\r
+\r
+// private void setUpdateIntervalInMS(int interval) {\r
+// updateInterval = Math.max(100, interval);\r
+// }\r
+\r
+ private void doDispose() {\r
+ prefStore.removePropertyChangeListener(prefListener);\r
+// if (gcImage != null) {\r
+// gcImage.dispose();\r
+// }\r
+\r
+ if (lowMemCol != null) {\r
+ lowMemCol.dispose();\r
+ }\r
+ if (freeMemCol != null) {\r
+ freeMemCol.dispose();\r
+ }\r
+ }\r
+\r
+ /* (non-Javadoc)\r
+ * @see org.eclipse.swt.widgets.Composite#computeSize(int, int, boolean)\r
+ */\r
+ @Override\r
+ public Point computeSize(int wHint, int hHint, boolean changed) {\r
+ GC gc = new GC(this);\r
+ Point p = gc.textExtent("MMMMMMM");\r
+ int height = imgBounds.height;\r
+ // choose the largest of\r
+ // - Text height + margins\r
+ // - Image height + margins\r
+ // - Default Trim heightin\r
+ height = Math.max(height, p.y) + 4;\r
+ height = Math.max(TrimUtil.TRIM_DEFAULT_HEIGHT, height);\r
+ gc.dispose();\r
+ return new Point(p.x + 2, height);\r
+ }\r
+\r
+ private void arm(boolean armed) {\r
+ if (this.armed == armed) {\r
+ return;\r
+ }\r
+ this.armed = armed;\r
+// button.redraw();\r
+// button.update();\r
+ }\r
+\r
+ /**\r
+ * Creates the context menu\r
+ */\r
+ private void createContextMenu() {\r
+ MenuManager menuMgr = new MenuManager();\r
+ menuMgr.setRemoveAllWhenShown(true);\r
+ menuMgr.addMenuListener(new IMenuListener() {\r
+ public void menuAboutToShow(IMenuManager menuMgr) {\r
+ fillMenu(menuMgr);\r
+ }\r
+ });\r
+ Menu menu = menuMgr.createContextMenu(this);\r
+ setMenu(menu);\r
+ }\r
+\r
+ private void fillMenu(IMenuManager menuMgr) {\r
+ //menuMgr.add(new ShowSessionsAction());\r
+ menuMgr.add(new CloseAction());\r
+ }\r
+\r
+// private void paintButton(GC gc) {\r
+// Rectangle rect = button.getClientArea();\r
+//\r
+// if (armed) {\r
+// gc.setBackground(armCol);\r
+// gc.fillRectangle(rect.x, rect.y, rect.width, rect.height);\r
+// }\r
+//// if (gcImage != null) {\r
+//// int by = (rect.height - imgBounds.height) / 2 + rect.y; // button y\r
+//// gc.drawImage(gcImage, rect.x, by);\r
+//// }\r
+// }\r
+\r
+\r
+ private void paintComposite(GC gc) {\r
+ Rectangle rect = getClientArea();\r
+ int x = rect.x;\r
+ int y = rect.y;\r
+ int w = rect.width;\r
+ int h = rect.height;\r
+// int bw = imgBounds.width; // button width\r
+ int bw = 0;\r
+ int dx = x + w - bw - 2; // divider x\r
+// int sw = w - bw - 3; // status width\r
+// int uw = (int) (sw * usedMem / totalMem); // used mem width\r
+// int ux = x + 1 + uw; // used mem right edge\r
+\r
+ gc.setBackground(bgCol);\r
+ gc.fillRectangle(rect);\r
+ gc.setForeground(sepCol);\r
+ gc.drawLine(dx, y, dx, y + h);\r
+// gc.drawLine(ux, y, ux, y + h);\r
+ gc.setForeground(topLeftCol);\r
+ gc.drawLine(x, y, x+w, y);\r
+ gc.drawLine(x, y, x, y+h);\r
+ gc.setForeground(bottomRightCol);\r
+ gc.drawLine(x+w-1, y, x+w-1, y+h);\r
+ gc.drawLine(x, y+h-1, x+w, y+h-1);\r
+\r
+// gc.setBackground(usedMemCol);\r
+// gc.fillRectangle(x + 1, y + 1, uw, h - 2);\r
+\r
+ String reads = String.valueOf(status.reads);\r
+ String writes = String.valueOf(status.writes);\r
+ String s = NLS.bind(requestStatusMessage, new String[] { reads, writes });\r
+\r
+ Point p = gc.textExtent(s);\r
+ //int sx = (rect.width - 15 - p.x) / 2 + rect.x + 1;\r
+ int sx = (rect.width - 2 - p.x) / 2 + rect.x + 1;\r
+ int sy = (rect.height - 2 - p.y) / 2 + rect.y + 1;\r
+ gc.setForeground(textCol);\r
+ gc.setAlpha(192);\r
+ gc.drawString(s, sx, sy, true);\r
+\r
+ // draw an I-shaped bar in the foreground colour for the mark (if present)\r
+// if (mark != -1) {\r
+// int ssx = (int) (sw * mark / totalMem) + x + 1;\r
+// paintMark(gc, ssx, y, h);\r
+// }\r
+ }\r
+\r
+// private void paintMark(GC gc, int x, int y, int h) {\r
+// gc.setForeground(markCol);\r
+// gc.drawLine(x, y+1, x, y+h-2);\r
+// gc.drawLine(x-1, y+1, x+1, y+1);\r
+// gc.drawLine(x-1, y+h-2, x+1, y+h-2);\r
+// }\r
+\r
+ private void updateStats() {\r
+ if (monitorContext == null)\r
+ return;\r
+\r
+ int reads = monitorContext.getInteger(SessionVariables.QUEUED_READS);\r
+ int writes = monitorContext.getInteger(SessionVariables.QUEUED_WRITES);\r
+ if (reads != status.reads) {\r
+ status.reads = reads;\r
+ this.hasChanged = true;\r
+ }\r
+ if (writes != status.writes) {\r
+ status.writes = writes;\r
+ this.hasChanged = true;\r
+ }\r
+ }\r
+\r
+ private void updateToolTip() {\r
+ String server = "";\r
+ if (status.sessionRef != null)\r
+ server = status.sessionRef.getServerReference().toString();\r
+ String reads = String.valueOf(status.reads);\r
+ String writes = String.valueOf(status.writes);\r
+ String toolTip = NLS.bind(requestToolTip, new String[] { server, reads, writes });\r
+ if (!toolTip.equals(getToolTipText())) {\r
+ setToolTipText(toolTip);\r
+ }\r
+ }\r
+\r
+\r
+ class ShowSessionsAction extends Action{\r
+ ShowSessionsAction(){\r
+ super("Show Local Sessions");\r
+ }\r
+\r
+ /* (non-Javadoc)\r
+ * @see org.eclipse.jface.action.IAction#run()\r
+ */\r
+ @Override\r
+ public void run(){\r
+ }\r
+ }\r
+\r
+ class CloseAction extends Action{\r
+ CloseAction(){\r
+ super(WorkbenchMessages.WorkbenchWindow_close);\r
+ }\r
+\r
+ /* (non-Javadoc)\r
+ * @see org.eclipse.jface.action.IAction#run()\r
+ */\r
+ @Override\r
+ public void run(){\r
+ dispose();\r
+ }\r
+ }\r
+\r
+}\r
+\r