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