--- /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.simulation.ui.handlers;\r
+\r
+import java.util.concurrent.ScheduledFuture;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+import org.eclipse.jface.action.IContributionItem;\r
+import org.eclipse.jface.resource.ColorDescriptor;\r
+import org.eclipse.jface.resource.JFaceResources;\r
+import org.eclipse.jface.resource.LocalResourceManager;\r
+import org.eclipse.jface.resource.ResourceManager;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.graphics.Color;\r
+import org.eclipse.swt.graphics.Point;\r
+import org.eclipse.swt.graphics.RGB;\r
+import org.eclipse.swt.widgets.Event;\r
+import org.eclipse.swt.widgets.Listener;\r
+import org.eclipse.swt.widgets.Text;\r
+import org.eclipse.swt.widgets.ToolBar;\r
+import org.eclipse.swt.widgets.ToolItem;\r
+import org.eclipse.ui.PlatformUI;\r
+import org.eclipse.ui.actions.CompoundContributionItem;\r
+import org.simantics.project.IProject;\r
+import org.simantics.simulation.experiment.ExperimentState;\r
+import org.simantics.simulation.experiment.IDynamicExperiment;\r
+import org.simantics.simulation.experiment.IDynamicExperimentListener;\r
+import org.simantics.simulation.experiment.IExperiment;\r
+import org.simantics.simulation.experiment.IExperimentListener;\r
+import org.simantics.simulation.experiment.SimulationTimeUtil;\r
+import org.simantics.simulation.project.IExperimentManager;\r
+import org.simantics.simulation.project.IExperimentManagerListener;\r
+import org.simantics.ui.SimanticsUI;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+\r
+\r
+public class TimerContribution extends CompoundContributionItem {\r
+\r
+ private static final long LABEL_UPDATE_MIN_PERIOD_MS = 100;\r
+\r
+ enum Mode {\r
+ HMS,\r
+ SECONDS;\r
+ Mode next() {\r
+ switch (this) {\r
+ case HMS: return SECONDS;\r
+ case SECONDS: return HMS;\r
+ default: return HMS;\r
+ }\r
+ }\r
+ }\r
+\r
+ boolean disposed = false;\r
+ Text label;\r
+ int width;\r
+ double time = 0.0;\r
+ private Mode mode = Mode.HMS;\r
+ private ToolItem ti;\r
+ private IExperimentManager experimentManager;\r
+ private IExperimentManagerListener experimentManagerListener;\r
+ private ExperimentState currentState;\r
+\r
+ private ResourceManager resourceManager;\r
+\r
+ private static ColorDescriptor RUNNING_BG = ColorDescriptor.createFrom(new RGB(0, 128, 0));\r
+ private static ColorDescriptor RUNNING_FG = ColorDescriptor.createFrom(new RGB(255, 255, 255));\r
+\r
+ public TimerContribution() {\r
+ super("org.simantics.simulation.ui.timer");\r
+ }\r
+\r
+ @Override\r
+ protected IContributionItem[] getContributionItems() {\r
+ return new IContributionItem[0];\r
+ }\r
+\r
+ String getTime() {\r
+ if (mode == Mode.SECONDS)\r
+ return SimulationTimeUtil.formatSeconds(time);\r
+ return SimulationTimeUtil.formatHMSS(time);\r
+ }\r
+\r
+ @Override\r
+ public void fill(final ToolBar parent, final int index) {\r
+ //System.out.println(this + "(" + System.identityHashCode(this) + ") FILL");\r
+\r
+ IProject project = SimanticsUI.peekProject();\r
+ if (project == null)\r
+ return;\r
+\r
+ IExperimentManager manager = project.getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER);\r
+ if(manager == null)\r
+ return;\r
+\r
+ IExperiment active = manager.getActiveExperiment();\r
+ if (!(active instanceof IDynamicExperiment))\r
+ return;\r
+\r
+ //System.out.println(this + "(" + System.identityHashCode(this) + ") got DynamicExperiment: " + active);\r
+\r
+ ti = new ToolItem(parent, SWT.SEPARATOR, index);\r
+ ti.setText("Simulation Timer");\r
+ ti.setToolTipText("Simulation Timer");\r
+ label = new Text(parent, SWT.BORDER | SWT.CENTER | SWT.READ_ONLY);\r
+ label.setText(getTime());\r
+\r
+ this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), label);\r
+\r
+ updateTooltip();\r
+\r
+ Listener labelListener = new Listener() {\r
+ boolean pressed = false;\r
+ boolean inside = false;\r
+ @Override\r
+ public void handleEvent(Event event) {\r
+ switch (event.type) {\r
+ case SWT.MouseDown:\r
+ if (inside && (event.button == 1 || event.button == 2))\r
+ pressed = true;\r
+ break;\r
+ case SWT.MouseUp:\r
+ if (pressed && inside) {\r
+ pressed = false;\r
+ toggleMode();\r
+ }\r
+ break;\r
+ case SWT.MouseEnter:\r
+ inside = true;\r
+ break;\r
+ case SWT.MouseExit:\r
+ inside = false;\r
+ break;\r
+ }\r
+ }\r
+ };\r
+ label.addListener(SWT.MouseDown, labelListener);\r
+ label.addListener(SWT.MouseEnter, labelListener);\r
+ label.addListener(SWT.MouseExit, labelListener);\r
+ label.addListener(SWT.MouseUp, labelListener);\r
+\r
+ width = label.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x;\r
+ ti.setWidth(width);\r
+ ti.setControl(label);\r
+\r
+ if (currentState != null)\r
+ setLabelVisualsByState(currentState);\r
+\r
+ attachToExperimentManager(manager);\r
+ }\r
+\r
+ private void attachToExperimentManager(final IExperimentManager manager) {\r
+ if (experimentManager != null) {\r
+ if (experimentManager.equals(manager))\r
+ return;\r
+ experimentManager.removeListener(experimentManagerListener);\r
+ }\r
+ if (manager == null)\r
+ return;\r
+\r
+ //System.out.println(this + "(" + System.identityHashCode(this) + ") ATTACH TO EXPERIMENT MANAGER " + manager);\r
+\r
+ experimentManagerListener = new IExperimentManagerListener() {\r
+ IDynamicExperiment currentExperiment;\r
+ IExperimentListener currentListener;\r
+\r
+ @Override\r
+ public void managerDisposed() {\r
+ manager.removeListener(this);\r
+ }\r
+ @Override\r
+ public void activeExperimentUnloaded() {\r
+ attachToExperiment(null);\r
+ }\r
+ @Override\r
+ public void activeExperimentLoaded(IExperiment experiment) {\r
+ attachToExperiment(experiment);\r
+ }\r
+ synchronized void attachToExperiment(final IExperiment experiment) {\r
+ if (currentExperiment != null) {\r
+ currentExperiment.removeListener(currentListener);\r
+ currentExperiment = null;\r
+ currentListener = null;\r
+ }\r
+\r
+ if (experiment == null)\r
+ return;\r
+ if (!(experiment instanceof IDynamicExperiment))\r
+ return;\r
+\r
+ IDynamicExperiment dynExp = (IDynamicExperiment) experiment;\r
+ //System.out.println(TimerContribution.this + "(" + System.identityHashCode(TimerContribution.this) + ") ATTACH TO EXPERIMENT " + dynExp);\r
+\r
+ IDynamicExperimentListener listener = new IDynamicExperimentListener() {\r
+ final IExperimentListener _this = this;\r
+ long lastUpdateTime = 0;\r
+ ScheduledFuture<?> timedUpdate = null;\r
+ ExperimentState lastState = null;\r
+ @Override\r
+ public void timeChanged(double newTime) {\r
+ //System.out.println(this + ".timeChanged: " + newTime);\r
+ time = newTime;\r
+\r
+ ScheduledFuture<?> f = timedUpdate;\r
+ if (f != null && !f.isDone())\r
+ return;\r
+\r
+ long timeSinceLastUpdate = System.currentTimeMillis() - lastUpdateTime;\r
+\r
+ if (timeSinceLastUpdate > LABEL_UPDATE_MIN_PERIOD_MS) {\r
+ scheduleLabelUpdate();\r
+ } else {\r
+ timedUpdate = ThreadUtils.getNonBlockingWorkExecutor().schedule(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ scheduleLabelUpdate();\r
+ }\r
+ }, LABEL_UPDATE_MIN_PERIOD_MS - timeSinceLastUpdate, TimeUnit.MILLISECONDS);\r
+ }\r
+ }\r
+ private void scheduleLabelUpdate() {\r
+ lastUpdateTime = System.currentTimeMillis();\r
+ timedUpdate = null;\r
+\r
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ //System.out.println("updating time label: " + time);\r
+ //System.out.println("label isdisposed: " + label.isDisposed());\r
+ if (!label.isDisposed())\r
+ updateLabel();\r
+\r
+ if (lastState != currentState) {\r
+ setLabelVisualsByState(currentState);\r
+ lastState = currentState;\r
+ }\r
+ }\r
+ });\r
+ }\r
+ @Override\r
+ public void stateChanged(ExperimentState state) {\r
+ //System.out.println("TimerContribution: state changed: " + state);\r
+ currentState = state;\r
+ if (state == ExperimentState.DISPOSED)\r
+ experiment.removeListener(_this);\r
+ else\r
+ scheduleLabelUpdate();\r
+ }\r
+ };\r
+ experiment.addListener(listener);\r
+\r
+ currentExperiment = dynExp;\r
+ currentListener = listener;\r
+ }\r
+ };\r
+\r
+ experimentManager = manager;\r
+ manager.addListener(experimentManagerListener);\r
+ }\r
+\r
+ private void toggleMode() {\r
+ mode = mode.next();\r
+ if (label.isDisposed())\r
+ return;\r
+ updateLabel();\r
+ updateTooltip();\r
+ }\r
+\r
+ private void updateTooltip() {\r
+ if (label.isDisposed())\r
+ return;\r
+ switch (mode) {\r
+ case HMS:\r
+ label.setToolTipText("Shows simulation time in HMS");\r
+ break;\r
+ case SECONDS:\r
+ label.setToolTipText("Shows simulation time in seconds");\r
+ break;\r
+ }\r
+ }\r
+\r
+ private void updateLabel() {\r
+ // Try to keep selection.\r
+ Point selection = label.getSelection();\r
+ String oldText = label.getText();\r
+ String newText = getTime();\r
+ if (selection.y == oldText.length())\r
+ selection.y = newText.length();\r
+ else\r
+ selection.y = Math.min(selection.y, newText.length());\r
+\r
+ label.setText(newText);\r
+ label.setSelection(selection);\r
+ int newWidth = label.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x;\r
+ if (newWidth != width) {\r
+ label.pack();\r
+ width = newWidth;\r
+ if (!ti.isDisposed()) {\r
+ ti.setWidth(width);\r
+ if (!ti.getParent().isDisposed())\r
+ ti.getParent().pack();\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void dispose() {\r
+ disposed = true;\r
+ //System.out.println(this + "(" + System.identityHashCode(this) + ") DISPOSE");\r
+ attachToExperimentManager(null);\r
+ }\r
+\r
+ @Override\r
+ public boolean isDynamic() {\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * @param currentState\r
+ * @thread SWT\r
+ */\r
+ private void setLabelVisualsByState(ExperimentState currentState) {\r
+ if (label.isDisposed())\r
+ return;\r
+ switch (currentState) {\r
+ case RUNNING:\r
+ label.setBackground((Color) resourceManager.get(RUNNING_BG));\r
+ label.setForeground((Color) resourceManager.get(RUNNING_FG));\r
+ //label.setFont((Font) resourceManager.get(FontDescriptor.createFrom(label.getFont()).setStyle(SWT.BOLD)));\r
+ label.setEnabled(true);\r
+ break;\r
+ case STOPPED:\r
+ label.setBackground(null);\r
+ label.setForeground(null);\r
+ label.setFont(null);\r
+ label.setEnabled(true);\r
+ break;\r
+ case INITIALIZING:\r
+ label.setBackground(null);\r
+ label.setForeground(null);\r
+ label.setFont(null);\r
+ label.setEnabled(false);\r
+ break;\r
+ }\r
+ }\r
+\r
+}\r