]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.simulation.ui/src/org/simantics/simulation/ui/handlers/TimerContribution.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.simulation.ui / src / org / simantics / simulation / ui / handlers / TimerContribution.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.simulation.ui.handlers;\r
13 \r
14 import java.util.concurrent.ScheduledFuture;\r
15 import java.util.concurrent.TimeUnit;\r
16 \r
17 import org.eclipse.jface.action.IContributionItem;\r
18 import org.eclipse.jface.resource.ColorDescriptor;\r
19 import org.eclipse.jface.resource.JFaceResources;\r
20 import org.eclipse.jface.resource.LocalResourceManager;\r
21 import org.eclipse.jface.resource.ResourceManager;\r
22 import org.eclipse.swt.SWT;\r
23 import org.eclipse.swt.graphics.Color;\r
24 import org.eclipse.swt.graphics.Point;\r
25 import org.eclipse.swt.graphics.RGB;\r
26 import org.eclipse.swt.widgets.Event;\r
27 import org.eclipse.swt.widgets.Listener;\r
28 import org.eclipse.swt.widgets.Text;\r
29 import org.eclipse.swt.widgets.ToolBar;\r
30 import org.eclipse.swt.widgets.ToolItem;\r
31 import org.eclipse.ui.PlatformUI;\r
32 import org.eclipse.ui.actions.CompoundContributionItem;\r
33 import org.simantics.project.IProject;\r
34 import org.simantics.simulation.experiment.ExperimentState;\r
35 import org.simantics.simulation.experiment.IDynamicExperiment;\r
36 import org.simantics.simulation.experiment.IDynamicExperimentListener;\r
37 import org.simantics.simulation.experiment.IExperiment;\r
38 import org.simantics.simulation.experiment.IExperimentListener;\r
39 import org.simantics.simulation.experiment.SimulationTimeUtil;\r
40 import org.simantics.simulation.project.IExperimentManager;\r
41 import org.simantics.simulation.project.IExperimentManagerListener;\r
42 import org.simantics.ui.SimanticsUI;\r
43 import org.simantics.utils.threads.ThreadUtils;\r
44 \r
45 \r
46 public class TimerContribution extends CompoundContributionItem {\r
47 \r
48     private static final long LABEL_UPDATE_MIN_PERIOD_MS = 100;\r
49 \r
50     enum Mode {\r
51         HMS,\r
52         SECONDS;\r
53         Mode next() {\r
54             switch (this) {\r
55                 case HMS: return SECONDS;\r
56                 case SECONDS: return HMS;\r
57                 default: return HMS;\r
58             }\r
59         }\r
60     }\r
61 \r
62     boolean              disposed = false;\r
63     Text                 label;\r
64     int                  width;\r
65     double               time     = 0.0;\r
66     private Mode         mode     = Mode.HMS;\r
67     private ToolItem     ti;\r
68     private IExperimentManager experimentManager;\r
69     private IExperimentManagerListener experimentManagerListener;\r
70     private ExperimentState currentState;\r
71 \r
72     private ResourceManager            resourceManager;\r
73 \r
74     private static ColorDescriptor     RUNNING_BG = ColorDescriptor.createFrom(new RGB(0, 128, 0));\r
75     private static ColorDescriptor     RUNNING_FG = ColorDescriptor.createFrom(new RGB(255, 255, 255));\r
76 \r
77     public TimerContribution() {\r
78         super("org.simantics.simulation.ui.timer");\r
79     }\r
80 \r
81     @Override\r
82     protected IContributionItem[] getContributionItems() {\r
83         return new IContributionItem[0];\r
84     }\r
85 \r
86     String getTime() {\r
87         if (mode == Mode.SECONDS)\r
88             return SimulationTimeUtil.formatSeconds(time);\r
89         return SimulationTimeUtil.formatHMSS(time);\r
90     }\r
91 \r
92     @Override\r
93     public void fill(final ToolBar parent, final int index) {\r
94         //System.out.println(this + "(" + System.identityHashCode(this) + ") FILL");\r
95 \r
96         IProject project = SimanticsUI.peekProject();\r
97         if (project == null)\r
98             return;\r
99 \r
100         IExperimentManager manager = project.getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER);\r
101         if(manager == null)\r
102             return;\r
103 \r
104         IExperiment active = manager.getActiveExperiment();\r
105         if (!(active instanceof IDynamicExperiment))\r
106             return;\r
107 \r
108         //System.out.println(this + "(" + System.identityHashCode(this) + ") got DynamicExperiment: " + active);\r
109 \r
110         ti = new ToolItem(parent, SWT.SEPARATOR, index);\r
111         ti.setText("Simulation Timer");\r
112         ti.setToolTipText("Simulation Timer");\r
113         label = new Text(parent, SWT.BORDER | SWT.CENTER | SWT.READ_ONLY);\r
114         label.setText(getTime());\r
115 \r
116         this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), label);\r
117 \r
118         updateTooltip();\r
119 \r
120         Listener labelListener = new Listener() {\r
121             boolean pressed = false;\r
122             boolean inside = false;\r
123             @Override\r
124             public void handleEvent(Event event) {\r
125                 switch (event.type) {\r
126                     case SWT.MouseDown:\r
127                         if (inside && (event.button == 1 || event.button == 2))\r
128                             pressed = true;\r
129                         break;\r
130                     case SWT.MouseUp:\r
131                         if (pressed && inside) {\r
132                             pressed = false;\r
133                             toggleMode();\r
134                         }\r
135                         break;\r
136                     case SWT.MouseEnter:\r
137                         inside = true;\r
138                         break;\r
139                     case SWT.MouseExit:\r
140                         inside = false;\r
141                         break;\r
142                 }\r
143             }\r
144         };\r
145         label.addListener(SWT.MouseDown, labelListener);\r
146         label.addListener(SWT.MouseEnter, labelListener);\r
147         label.addListener(SWT.MouseExit, labelListener);\r
148         label.addListener(SWT.MouseUp, labelListener);\r
149 \r
150         width = label.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x;\r
151         ti.setWidth(width);\r
152         ti.setControl(label);\r
153 \r
154         if (currentState != null)\r
155             setLabelVisualsByState(currentState);\r
156 \r
157         attachToExperimentManager(manager);\r
158     }\r
159 \r
160     private void attachToExperimentManager(final IExperimentManager manager) {\r
161         if (experimentManager != null) {\r
162             if (experimentManager.equals(manager))\r
163                 return;\r
164             experimentManager.removeListener(experimentManagerListener);\r
165         }\r
166         if (manager == null)\r
167             return;\r
168 \r
169         //System.out.println(this + "(" + System.identityHashCode(this) + ") ATTACH TO EXPERIMENT MANAGER " + manager);\r
170 \r
171         experimentManagerListener = new IExperimentManagerListener() {\r
172             IDynamicExperiment currentExperiment;\r
173             IExperimentListener currentListener;\r
174 \r
175             @Override\r
176             public void managerDisposed() {\r
177                 manager.removeListener(this);\r
178             }\r
179             @Override\r
180             public void activeExperimentUnloaded() {\r
181                 attachToExperiment(null);\r
182             }\r
183             @Override\r
184             public void activeExperimentLoaded(IExperiment experiment) {\r
185                 attachToExperiment(experiment);\r
186             }\r
187             synchronized void attachToExperiment(final IExperiment experiment) {\r
188                 if (currentExperiment != null) {\r
189                     currentExperiment.removeListener(currentListener);\r
190                     currentExperiment = null;\r
191                     currentListener = null;\r
192                 }\r
193 \r
194                 if (experiment == null)\r
195                     return;\r
196                 if (!(experiment instanceof IDynamicExperiment))\r
197                     return;\r
198 \r
199                 IDynamicExperiment dynExp = (IDynamicExperiment) experiment;\r
200                 //System.out.println(TimerContribution.this + "(" + System.identityHashCode(TimerContribution.this) + ") ATTACH TO EXPERIMENT " + dynExp);\r
201 \r
202                 IDynamicExperimentListener listener = new IDynamicExperimentListener() {\r
203                     final IExperimentListener _this = this;\r
204                     long lastUpdateTime = 0;\r
205                     ScheduledFuture<?> timedUpdate = null;\r
206                     ExperimentState lastState = null;\r
207                     @Override\r
208                     public void timeChanged(double newTime) {\r
209                         //System.out.println(this + ".timeChanged: " + newTime);\r
210                         time = newTime;\r
211 \r
212                         ScheduledFuture<?> f = timedUpdate;\r
213                         if (f != null && !f.isDone())\r
214                             return;\r
215 \r
216                         long timeSinceLastUpdate = System.currentTimeMillis() - lastUpdateTime;\r
217 \r
218                         if (timeSinceLastUpdate > LABEL_UPDATE_MIN_PERIOD_MS) {\r
219                             scheduleLabelUpdate();\r
220                         } else {\r
221                             timedUpdate = ThreadUtils.getNonBlockingWorkExecutor().schedule(new Runnable() {\r
222                                 @Override\r
223                                 public void run() {\r
224                                     scheduleLabelUpdate();\r
225                                 }\r
226                             }, LABEL_UPDATE_MIN_PERIOD_MS - timeSinceLastUpdate, TimeUnit.MILLISECONDS);\r
227                         }\r
228                     }\r
229                     private void scheduleLabelUpdate() {\r
230                         lastUpdateTime = System.currentTimeMillis();\r
231                         timedUpdate = null;\r
232 \r
233                         PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {\r
234                             @Override\r
235                             public void run() {\r
236                                 //System.out.println("updating time label: " + time);\r
237                                 //System.out.println("label isdisposed: " + label.isDisposed());\r
238                                 if (!label.isDisposed())\r
239                                     updateLabel();\r
240 \r
241                                 if (lastState != currentState) {\r
242                                     setLabelVisualsByState(currentState);\r
243                                     lastState = currentState;\r
244                                 }\r
245                             }\r
246                         });\r
247                     }\r
248                     @Override\r
249                     public void stateChanged(ExperimentState state) {\r
250                         //System.out.println("TimerContribution: state changed: " + state);\r
251                         currentState = state;\r
252                         if (state == ExperimentState.DISPOSED)\r
253                             experiment.removeListener(_this);\r
254                         else\r
255                             scheduleLabelUpdate();\r
256                     }\r
257                 };\r
258                 experiment.addListener(listener);\r
259 \r
260                 currentExperiment = dynExp;\r
261                 currentListener = listener;\r
262             }\r
263         };\r
264 \r
265         experimentManager = manager;\r
266         manager.addListener(experimentManagerListener);\r
267     }\r
268 \r
269     private void toggleMode() {\r
270         mode = mode.next();\r
271         if (label.isDisposed())\r
272             return;\r
273         updateLabel();\r
274         updateTooltip();\r
275     }\r
276 \r
277     private void updateTooltip() {\r
278         if (label.isDisposed())\r
279             return;\r
280         switch (mode) {\r
281             case HMS:\r
282                 label.setToolTipText("Shows simulation time in HMS");\r
283                 break;\r
284             case SECONDS:\r
285                 label.setToolTipText("Shows simulation time in seconds");\r
286                 break;\r
287         }\r
288     }\r
289 \r
290     private void updateLabel() {\r
291         // Try to keep selection.\r
292         Point selection = label.getSelection();\r
293         String oldText = label.getText();\r
294         String newText = getTime();\r
295         if (selection.y == oldText.length())\r
296             selection.y = newText.length();\r
297         else\r
298             selection.y = Math.min(selection.y, newText.length());\r
299 \r
300         label.setText(newText);\r
301         label.setSelection(selection);\r
302         int newWidth = label.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x;\r
303         if (newWidth != width) {\r
304             label.pack();\r
305             width = newWidth;\r
306             if (!ti.isDisposed()) {\r
307                 ti.setWidth(width);\r
308                 if (!ti.getParent().isDisposed())\r
309                     ti.getParent().pack();\r
310             }\r
311         }\r
312     }\r
313 \r
314     @Override\r
315     public void dispose() {\r
316         disposed = true;\r
317         //System.out.println(this + "(" + System.identityHashCode(this) + ") DISPOSE");\r
318         attachToExperimentManager(null);\r
319     }\r
320 \r
321     @Override\r
322     public boolean isDynamic() {\r
323         return true;\r
324     }\r
325 \r
326     /**\r
327      * @param currentState\r
328      * @thread SWT\r
329      */\r
330     private void setLabelVisualsByState(ExperimentState currentState) {\r
331         if (label.isDisposed())\r
332             return;\r
333         switch (currentState) {\r
334             case RUNNING:\r
335                 label.setBackground((Color) resourceManager.get(RUNNING_BG));\r
336                 label.setForeground((Color) resourceManager.get(RUNNING_FG));\r
337                 //label.setFont((Font) resourceManager.get(FontDescriptor.createFrom(label.getFont()).setStyle(SWT.BOLD)));\r
338                 label.setEnabled(true);\r
339                 break;\r
340             case STOPPED:\r
341                 label.setBackground(null);\r
342                 label.setForeground(null);\r
343                 label.setFont(null);\r
344                 label.setEnabled(true);\r
345                 break;\r
346             case INITIALIZING:\r
347                 label.setBackground(null);\r
348                 label.setForeground(null);\r
349                 label.setFont(null);\r
350                 label.setEnabled(false);\r
351                 break;\r
352         }\r
353     }\r
354 \r
355 }\r