1 /*******************************************************************************
2 * Copyright (c) 2015, 2016 Association for Decentralized Information Management
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.simulation.ui.handlers.e4;
14 import java.util.concurrent.ScheduledFuture;
15 import java.util.concurrent.TimeUnit;
17 import javax.annotation.PostConstruct;
18 import javax.annotation.PreDestroy;
19 import javax.inject.Inject;
21 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
22 import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
23 import org.eclipse.e4.core.di.annotations.Optional;
24 import org.eclipse.e4.core.di.extensions.Preference;
25 import org.eclipse.e4.ui.di.UISynchronize;
26 import org.eclipse.e4.ui.model.application.ui.menu.MToolControl;
27 import org.eclipse.jface.resource.ColorDescriptor;
28 import org.eclipse.jface.resource.JFaceResources;
29 import org.eclipse.jface.resource.LocalResourceManager;
30 import org.eclipse.jface.resource.ResourceManager;
31 import org.eclipse.swt.SWT;
32 import org.eclipse.swt.graphics.Color;
33 import org.eclipse.swt.graphics.Point;
34 import org.eclipse.swt.graphics.RGB;
35 import org.eclipse.swt.widgets.Composite;
36 import org.eclipse.swt.widgets.Event;
37 import org.eclipse.swt.widgets.Listener;
38 import org.eclipse.swt.widgets.Text;
39 import org.simantics.databoard.util.ObjectUtils;
40 import org.simantics.project.IProject;
41 import org.simantics.simulation.experiment.ExperimentState;
42 import org.simantics.simulation.experiment.IDynamicExperiment;
43 import org.simantics.simulation.experiment.IDynamicExperimentListener;
44 import org.simantics.simulation.experiment.IExperiment;
45 import org.simantics.simulation.experiment.IExperimentListener;
46 import org.simantics.simulation.experiment.SimulationTimeUtil;
47 import org.simantics.simulation.project.IExperimentManager;
48 import org.simantics.simulation.project.IExperimentManagerListener;
49 import org.simantics.ui.SimanticsUI;
50 import org.simantics.utils.threads.ThreadUtils;
53 * E4-version of the old
54 * {@link org.simantics.simulation.ui.handlers.TimerContribution}.
57 * Bound to org.simantics.chart/chart.timeformat preference for the used time
58 * format. This is not the nicest of solutions since it makes the
59 * <code>org.simantics.simulation.ui</code> plug-in depend on the
60 * <code>org.simantics.charts</code> plug-in. However the binding is optional
61 * and therefore the thin dependency is acceptable for now.
63 * @author Jani Simomaa / Semantum Oy
64 * @author Tuukka Lehtonen / Semantum Oy
67 public class TimerContribution {
69 private static final String PREF_CHART_BUNDLE_ID = "org.simantics.charts";
70 private static final String PREF_CHART_TIMEFORMAT = "chart.timeformat";
72 private static final long LABEL_UPDATE_MIN_PERIOD_MS = 100;
79 case HMS: return SECONDS;
80 case SECONDS: return HMS;
86 boolean disposed = false;
90 private Mode mode = Mode.HMS;
92 private IExperimentManager experimentManager;
93 private IExperimentManagerListener experimentManagerListener;
94 private ExperimentState currentState;
96 private ResourceManager resourceManager;
99 private UISynchronize uisync;
102 * For listening to the current chart time format preference.
106 @Preference(nodePath = PREF_CHART_BUNDLE_ID)
107 private IEclipsePreferences chartPreferences;
109 private static String toTimeFormatPreference(Mode mode) {
111 case SECONDS: return "Decimal";
113 default: return "Time";
117 private static Mode toMode(String timeFormat) {
118 if (timeFormat == null)
120 switch (timeFormat) {
121 case "Decimal": return Mode.SECONDS;
123 default: return Mode.HMS;
127 private IPreferenceChangeListener chartTimeFormatListener = event -> {
128 if (PREF_CHART_TIMEFORMAT.equals(event.getKey())) {
129 Mode newMode = toMode((String) event.getNewValue());
130 if (newMode != mode) {
132 uisync.asyncExec(() -> {
133 if (!label.isDisposed()) {
142 private static ColorDescriptor RUNNING_BG = ColorDescriptor.createFrom(new RGB(0, 128, 0));
143 private static ColorDescriptor RUNNING_FG = ColorDescriptor.createFrom(new RGB(255, 255, 255));
146 public void createControls(Composite parent, MToolControl toolControl) {
147 IProject project = SimanticsUI.peekProject();
151 IExperimentManager manager = project.getHint(IExperimentManager.KEY_EXPERIMENT_MANAGER);
155 label = new Text(parent, SWT.BORDER | SWT.CENTER | SWT.READ_ONLY);
156 label.setEnabled(false);
157 label.setText(getTime());
158 label.setToolTipText("Simulation Timer");
160 this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), label);
164 Listener labelListener = new Listener() {
165 boolean pressed = false;
166 boolean inside = false;
168 public void handleEvent(Event event) {
169 switch (event.type) {
171 if (inside && (event.button == 1 || event.button == 2))
175 if (pressed && inside) {
189 label.addListener(SWT.MouseDown, labelListener);
190 label.addListener(SWT.MouseEnter, labelListener);
191 label.addListener(SWT.MouseExit, labelListener);
192 label.addListener(SWT.MouseUp, labelListener);
194 size = label.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
195 if (currentState != null)
196 setLabelVisualsByState(currentState);
198 attachToExperimentManager(manager);
200 if (chartPreferences != null) {
201 chartPreferences.addPreferenceChangeListener(chartTimeFormatListener);
202 mode = toMode((String) chartPreferences.get(PREF_CHART_TIMEFORMAT, null));
207 public void destroy() {
208 if (chartPreferences != null) {
209 chartPreferences.removePreferenceChangeListener(chartTimeFormatListener);
213 private void attachToExperimentManager(final IExperimentManager manager) {
214 if (experimentManager != null) {
215 if (experimentManager.equals(manager))
217 experimentManager.removeListener(experimentManagerListener);
222 //System.out.println(this + "(" + System.identityHashCode(this) + ") ATTACH TO EXPERIMENT MANAGER " + manager);
224 experimentManagerListener = new IExperimentManagerListener() {
225 IDynamicExperiment currentExperiment;
226 IExperimentListener currentListener;
229 public void managerDisposed() {
230 manager.removeListener(this);
233 public void activeExperimentUnloaded() {
234 attachToExperiment(null);
237 public void activeExperimentLoaded(IExperiment experiment) {
238 attachToExperiment(experiment);
240 synchronized void attachToExperiment(final IExperiment experiment) {
241 if (currentExperiment != null) {
242 currentExperiment.removeListener(currentListener);
243 currentExperiment = null;
244 currentListener = null;
247 if (!(experiment instanceof IDynamicExperiment)) {
248 // Ensure that the timer text value is reset to zero.
250 uisync.asyncExec(() -> {
251 if (!label.isDisposed()) {
253 setLabelVisualsByState(ExperimentState.DISPOSED);
259 IDynamicExperiment dynExp = (IDynamicExperiment) experiment;
260 //System.out.println(TimerContribution.this + "(" + System.identityHashCode(TimerContribution.this) + ") ATTACH TO EXPERIMENT " + dynExp);
262 IDynamicExperimentListener listener = new IDynamicExperimentListener() {
263 final IExperimentListener _this = this;
264 long lastUpdateTime = 0;
265 ScheduledFuture<?> timedUpdate = null;
266 ExperimentState lastState = null;
268 public void timeChanged(double newTime) {
269 //System.out.println(this + ".timeChanged: " + newTime);
272 ScheduledFuture<?> f = timedUpdate;
273 if (f != null && !f.isDone())
276 long timeSinceLastUpdate = System.currentTimeMillis() - lastUpdateTime;
278 if (timeSinceLastUpdate > LABEL_UPDATE_MIN_PERIOD_MS) {
279 scheduleLabelUpdate();
281 timedUpdate = ThreadUtils.getNonBlockingWorkExecutor().schedule(
282 () -> scheduleLabelUpdate(),
283 LABEL_UPDATE_MIN_PERIOD_MS - timeSinceLastUpdate,
284 TimeUnit.MILLISECONDS);
287 private void scheduleLabelUpdate() {
288 lastUpdateTime = System.currentTimeMillis();
291 uisync.asyncExec(() -> {
292 //System.out.println("updating time label: " + time);
293 //System.out.println("label isdisposed: " + label.isDisposed());
294 if (!label.isDisposed()) {
296 if (lastState != currentState) {
297 setLabelVisualsByState(currentState);
298 lastState = currentState;
304 public void stateChanged(ExperimentState state) {
305 //System.out.println("TimerContribution: state changed: " + state);
306 currentState = state;
307 if (state == ExperimentState.DISPOSED)
308 experiment.removeListener(_this);
310 scheduleLabelUpdate();
313 experiment.addListener(listener);
315 currentExperiment = dynExp;
316 currentListener = listener;
320 experimentManager = manager;
321 manager.addListener(experimentManagerListener);
324 private void toggleMode() {
326 if (!label.isDisposed()) {
329 setTimeFormatPreference(mode);
333 private void setTimeFormatPreference(Mode mode) {
334 if (chartPreferences != null) {
335 chartPreferences.put(PREF_CHART_TIMEFORMAT, toTimeFormatPreference(mode));
339 private void updateTooltip() {
340 if (label.isDisposed())
344 label.setToolTipText("Shows simulation time in HMS");
347 label.setToolTipText("Shows simulation time in seconds");
352 private void updateLabel() {
353 // Try to keep selection.
354 Point selection = label.getSelection();
355 String oldText = label.getText();
356 String newText = getTime();
357 if (selection.y == oldText.length())
358 selection.y = newText.length();
360 selection.y = Math.min(selection.y, newText.length());
362 label.setText(newText);
363 label.setSelection(selection);
364 Point newSize = label.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
365 if (!ObjectUtils.objectEquals(newSize, size)) {
366 label.setSize(newSize);
368 Composite parent = label.getParent();
369 if (parent != null) {
376 * @param currentState
379 private void setLabelVisualsByState(ExperimentState currentState) {
380 if (label.isDisposed())
382 switch (currentState) {
384 label.setBackground((Color) resourceManager.get(RUNNING_BG));
385 label.setForeground((Color) resourceManager.get(RUNNING_FG));
386 //label.setFont((Font) resourceManager.get(FontDescriptor.createFrom(label.getFont()).setStyle(SWT.BOLD)));
387 label.setEnabled(true);
390 label.setBackground(null);
391 label.setForeground(null);
393 label.setEnabled(true);
397 label.setBackground(null);
398 label.setForeground(null);
400 label.setEnabled(false);
405 private String getTime() {
406 if (mode == Mode.SECONDS)
407 return SimulationTimeUtil.formatSeconds(time);
408 return SimulationTimeUtil.formatHMSS(time);