Java tutorial
/******************************************************************************* * Copyright (c) 2012, 2014 TH4 SYSTEMS GmbH and others. * 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: * TH4 SYSTEMS GmbH - initial API and implementation * Jens Reimann - additional work * IBH SYSTEMS GmbH - additional work, stale initial update * IBH SYSTEMS GmbH - bug fixes and extensions, enhancements for legends *******************************************************************************/ package org.eclipse.scada.ui.chart.viewer; import java.util.Calendar; import java.util.Collection; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.Set; import java.util.concurrent.TimeUnit; import org.eclipse.core.databinding.AggregateValidationStatus; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.UpdateListStrategy; import org.eclipse.core.databinding.beans.PojoObservables; import org.eclipse.core.databinding.observable.Observables; import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.core.databinding.observable.list.WritableList; import org.eclipse.core.databinding.observable.value.IValueChangeListener; import org.eclipse.core.databinding.observable.value.ValueChangeEvent; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.Status; import org.eclipse.emf.databinding.EMFObservables; import org.eclipse.jface.databinding.swt.SWTObservables; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.resource.ResourceManager; import org.eclipse.jface.util.LocalSelectionTransfer; import org.eclipse.jface.util.SafeRunnable; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.scada.chart.Realm; import org.eclipse.scada.chart.swt.ChartRenderer; import org.eclipse.scada.chart.swt.DisplayRealm; import org.eclipse.scada.chart.swt.DisposeListener; import org.eclipse.scada.chart.swt.controller.AbstractMouseHandler.AxisFunction; import org.eclipse.scada.chart.swt.controller.MouseHover; import org.eclipse.scada.chart.swt.render.CurrentTimeRuler; import org.eclipse.scada.chart.swt.render.TitleRenderer; import org.eclipse.scada.da.ui.connection.data.Item; import org.eclipse.scada.ui.chart.AxisConverter; import org.eclipse.scada.ui.chart.configuration.Charts; import org.eclipse.scada.ui.chart.model.Chart; import org.eclipse.scada.ui.chart.model.ChartPackage; import org.eclipse.scada.ui.chart.model.XAxis; import org.eclipse.scada.ui.chart.model.YAxis; import org.eclipse.scada.ui.chart.viewer.controller.ControllerManager; import org.eclipse.scada.ui.chart.viewer.input.ChartInput; import org.eclipse.scada.ui.chart.viewer.profile.ProfileManager; import org.eclipse.scada.ui.utils.AbstractSelectionProvider; import org.eclipse.scada.ui.utils.SelectionHelper; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTargetAdapter; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ChartViewer extends AbstractSelectionProvider { private final static Logger logger = LoggerFactory.getLogger(ChartViewer.class); private final ChartRenderer manager; private final WritableList items = new WritableList(new LinkedList<Object>(), ChartInput.class); private final IObservableList unmodItems = Observables.unmodifiableObservableList(this.items); private CurrentTimeRuler timeRuler; private ChartInput selection; private final Chart chart; private final DataBindingContext ctx; private final ResourceManager resourceManager; private final YAxisManager leftManager; private final YAxisManager rightManager; private final XAxisManager topManager; private final XAxisManager bottomManager; private XAxisViewer timeRulerAxis; private boolean showTimeRuler; private final Realm realm; private final SimpleAxisLocator<XAxis, XAxisViewer> xLocator; private final SimpleAxisLocator<YAxis, YAxisViewer> yLocator; private final InputManager inputManager; private XAxis timeRulerAxisElement; private final Set<ChartViewerListener> listeners = new LinkedHashSet<ChartViewerListener>(); private boolean mutable; private boolean hoverable; private MouseHover mouseHover; private final ControllerManager controllerManager; private final ProfileManager profileManager; private final ChartContext chartContext; private TitleRenderer titleRenderer; private Display display; private final WritableList selectedXAxis = new WritableList(); public ChartViewer(final Display display, final ChartRenderer chartRenderer, final Chart chart, final ExtensionSpaceProvider extensionSpaceProvider, final ResetHandler resetHandler) { this.chart = chart; this.display = display; this.resourceManager = new LocalResourceManager(JFaceResources.getResources(this.display)); this.ctx = new DataBindingContext(SWTObservables.getRealm(this.display)); final AggregateValidationStatus status = new AggregateValidationStatus(this.ctx, AggregateValidationStatus.MERGED); status.addValueChangeListener(new IValueChangeListener() { @Override public void handleValueChange(final ValueChangeEvent event) { if (event.diff.getNewValue() == null) { return; } final Status value = (Status) event.diff.getNewValue(); if (value.isOK() || value.getException() == null) { return; } logger.warn("Changed", value.getException()); } }); // create content this.manager = chartRenderer; this.manager.setStale(true); this.realm = new DisplayRealm(this.display); this.manager.createDropTarget(new Transfer[] { LocalSelectionTransfer.getTransfer() }, createDropTarget()); this.titleRenderer = new TitleRenderer(this.manager); this.manager.addRenderer(this.titleRenderer, -100); this.leftManager = new YAxisManager(this.ctx, this.manager, true); this.ctx.bindList(this.leftManager.getList(), EMFObservables.observeList(chart, ChartPackage.Literals.CHART__LEFT)); this.rightManager = new YAxisManager(this.ctx, this.manager, false); this.ctx.bindList(this.rightManager.getList(), EMFObservables.observeList(chart, ChartPackage.Literals.CHART__RIGHT)); this.topManager = new XAxisManager(this.ctx, this.manager, true); this.ctx.bindList(this.topManager.getList(), EMFObservables.observeList(chart, ChartPackage.Literals.CHART__TOP)); this.bottomManager = new XAxisManager(this.ctx, this.manager, false); this.ctx.bindList(this.bottomManager.getList(), EMFObservables.observeList(chart, ChartPackage.Literals.CHART__BOTTOM)); this.xLocator = new SimpleAxisLocator<XAxis, XAxisViewer>(this.topManager, this.bottomManager); this.yLocator = new SimpleAxisLocator<YAxis, YAxisViewer>(this.leftManager, this.rightManager); this.inputManager = new InputManager(this.ctx, this, this.resourceManager, this.xLocator, this.yLocator); this.ctx.bindList(this.inputManager.getList(), EMFObservables.observeList(chart, ChartPackage.Literals.CHART__INPUTS)); this.ctx.bindValue(PojoObservables.observeValue(this.titleRenderer, "title"), //$NON-NLS-1$ EMFObservables.observeValue(this.chart, ChartPackage.Literals.CHART__TITLE)); this.ctx.bindValue(PojoObservables.observeValue(this, "showCurrentTimeRuler"), //$NON-NLS-1$ EMFObservables.observeValue(this.chart, ChartPackage.Literals.CHART__SHOW_CURRENT_TIME_RULER)); this.ctx.bindValue(PojoObservables.observeValue(this, "mutable"), //$NON-NLS-1$ EMFObservables.observeValue(this.chart, ChartPackage.Literals.CHART__MUTABLE)); this.ctx.bindValue(PojoObservables.observeValue(this, "hoverable"), //$NON-NLS-1$ EMFObservables.observeValue(this.chart, ChartPackage.Literals.CHART__HOVERABLE)); this.ctx.bindValue(PojoObservables.observeValue(this, "chartBackground"), //$NON-NLS-1$ EMFObservables.observeValue(this.chart, ChartPackage.Literals.CHART__BACKGROUND_COLOR)); this.ctx.bindValue(PojoObservables.observeValue(this, "timeRulerAxis"), //$NON-NLS-1$ EMFObservables.observeValue(this.chart, ChartPackage.Literals.CHART__TIME_RULER_AXIS)); this.ctx.bindList(this.selectedXAxis, EMFObservables.observeList(chart, ChartPackage.Literals.CHART__SELECTED_XAXIS), null, new UpdateListStrategy().setConverter( new AxisConverter<>(XAxis.class, org.eclipse.scada.chart.XAxis.class, this.xLocator))); this.chartContext = new ChartContextImpl(this.xLocator, this.yLocator, extensionSpaceProvider, chartRenderer, chart, resetHandler, this.unmodItems); this.controllerManager = new ControllerManager(this.ctx, this.ctx.getValidationRealm(), this.chartContext); this.ctx.bindList(this.controllerManager.getList(), EMFObservables.observeList(chart, ChartPackage.Literals.CHART__CONTROLLERS)); this.profileManager = new ProfileManager(this.ctx, this.ctx.getValidationRealm(), extensionSpaceProvider, this.chartContext); this.ctx.bindList(this.profileManager.getList(), EMFObservables.observeList(chart, ChartPackage.Literals.CHART__PROFILES)); this.ctx.bindValue(PojoObservables.observeValue(this.profileManager, "type"), //$NON-NLS-1$ EMFObservables.observeValue(chart, ChartPackage.Literals.CHART__PROFILE_SWITCHER_TYPE)); this.ctx.bindValue(PojoObservables.observeValue(this.profileManager, "activeProfile"), //$NON-NLS-1$ EMFObservables.observeValue(chart, ChartPackage.Literals.CHART__ACTIVE_PROFILE)); this.manager.setStale(false); startTimer(); this.manager.addDisposeListener(new DisposeListener() { @Override public void onDispose() { handleDispose(); } }); setSelection(new StructuredSelection(this)); updateState(); } public boolean isHoverable() { return this.hoverable; } public void setHoverable(final boolean hoverable) { this.hoverable = hoverable; updateState(); } public ChartRenderer getChartRenderer() { return this.manager; } protected void handleMouseMove(final MouseEvent e, final long timestamp) { setInputSelection(timestamp); } private void setInputSelection(final long timestamp) { final Calendar c = Calendar.getInstance(); c.setTimeInMillis(timestamp); for (final Object input : this.items) { ((ChartInput) input).setSelection(c); } } public void setMutable(final boolean mutable) { this.mutable = mutable; } public boolean isMutable() { return this.mutable; } public void addChartViewerListener(final ChartViewerListener chartViewerListener) { this.listeners.add(chartViewerListener); } public void removeChartViewerListener(final ChartViewerListener chartViewerListener) { this.listeners.remove(chartViewerListener); } private void fireInputAdded(final ChartInput input) { for (final ChartViewerListener listener : this.listeners) { SafeRunner.run(new SafeRunnable() { @Override public void run() throws Exception { listener.inputAdded(input); } }); } } private void fireInputRemoved(final ChartInput input) { for (final ChartViewerListener listener : this.listeners) { SafeRunner.run(new SafeRunnable() { @Override public void run() throws Exception { listener.inputRemoved(input); } }); } } public Realm getRealm() { return this.realm; } protected void updateState() { final org.eclipse.scada.chart.XAxis x; x = getTimeRulerViewer(); // update mouse controllers if (this.mouseHover != null) { this.mouseHover.dispose(); this.mouseHover = null; } if (isHoverable()) { this.mouseHover = new MouseHover(this.manager); this.mouseHover.setVisible(true); } // update current time tracker if (this.timeRuler == null && this.showTimeRuler && x != null) { this.timeRuler = new CurrentTimeRuler(x); this.timeRuler.setColor(new RGB(0, 0, 255)); this.manager.addRenderer(this.timeRuler, 100); } else if (this.timeRuler != null && !this.showTimeRuler || x == null) { disposeTimeRuler(); } } protected void disposeTimeRuler() { this.manager.removeRenderer(this.timeRuler); this.timeRuler = null; } private org.eclipse.scada.chart.XAxis getTimeRulerViewer() { return this.timeRulerAxis != null ? this.timeRulerAxis.getAxis() : null; } public XAxis getTimeRulerAxis() { return this.timeRulerAxisElement; } public void setTimeRulerAxis(final XAxis axis) { final XAxisViewer newSelection = this.xLocator.findAxis(axis); if (this.timeRulerAxis == newSelection) { return; } this.timeRulerAxis = newSelection; this.timeRulerAxisElement = axis; disposeTimeRuler(); // dispose time ruler, as the axis changed updateState(); } public void setChartBackground(final RGB rgb) { this.manager.setChartBackground(rgb); this.manager.refresh(); } public RGB getChartBackground() { return this.manager.getChartBackground(); } protected void handleDispose() { this.titleRenderer.dispose(); this.inputManager.dispose(); this.topManager.dispose(); this.bottomManager.dispose(); this.leftManager.dispose(); this.rightManager.dispose(); this.manager.dispose(); this.ctx.dispose(); } private DropTargetAdapter createDropTarget() { return new DropTargetAdapter() { @Override public void dragEnter(final DropTargetEvent event) { event.detail = DND.DROP_NONE; if (!ChartViewer.this.mutable) { return; } final ISelection selection = LocalSelectionTransfer.getTransfer().getSelection(); { final Collection<org.eclipse.scada.da.ui.connection.data.Item> data = SelectionHelper .list(selection, org.eclipse.scada.da.ui.connection.data.Item.class); if (!data.isEmpty()) { event.detail = DND.DROP_COPY; return; } } { final Collection<org.eclipse.scada.hd.ui.connection.data.Item> data = SelectionHelper .list(selection, org.eclipse.scada.hd.ui.connection.data.Item.class); if (!data.isEmpty()) { event.detail = DND.DROP_COPY; return; } } }; @Override public void drop(final DropTargetEvent event) { handleDrop(); }; }; } public void setShowCurrentTimeRuler(final boolean state) { this.showTimeRuler = state; updateState(); } public boolean isShowCurrentTimeRuler() { return this.showTimeRuler; } private void startTimer() { if (this.display.isDisposed()) { return; } this.display.timerExec(250, new Runnable() { @Override public void run() { if (ChartViewer.this.manager.isDisposed()) { return; } tick(); startTimer(); } }); } public void addInput(final ChartInput input) { if (this.items.size() == 1) { ((ChartInput) this.items.get(0)).setSelection(false); } if (!this.items.add(input)) { return; } if (this.items.size() == 1) { setSelection(input); } // fire events fireInputAdded(input); } public void removeInput(final ChartInput input) { if (input == this.selection) { setSelection((ChartInput) null); } if (this.items.remove(input)) { fireInputRemoved(input); } } public void setSelection(final ChartInput chartInput) { if (chartInput != null && !this.items.contains(chartInput)) { return; } this.selection = chartInput; for (final Object input : this.items) { ((ChartInput) input).setSelection(false); } if (chartInput != null) { chartInput.setSelection(true); } } public void tick() { final long now = System.currentTimeMillis(); boolean forceUpdate = false; try { this.manager.setStale(true); for (final Object item : this.items) { if (((ChartInput) item).tick(now)) { forceUpdate = true; } } if (this.timeRuler != null) { forceUpdate = this.timeRuler.showingCurrentTime(); } } finally { this.manager.setStale(false, forceUpdate); } } public void setFocus() { this.manager.setFocus(); setSelection(new StructuredSelection(this)); } public void dispose() { final Object[] items = this.items.toArray(); this.items.clear(); for (final Object item : items) { ((ChartInput) item).dispose(); } this.controllerManager.dispose(); } public IObservableList getItems() { return this.unmodItems; } protected void processX(final AxisFunction<org.eclipse.scada.chart.XAxis> func) { for (final Object o : this.selectedXAxis) { func.process((org.eclipse.scada.chart.XAxis) o); } } public void showTimespan(final long duration, final TimeUnit timeUnit) { processX(new AxisFunction<org.eclipse.scada.chart.XAxis>() { @Override public void process(final org.eclipse.scada.chart.XAxis axis) { axis.setByTimespan(duration, timeUnit); } }); } public void pageTimespan(final long duration, final TimeUnit timeUnit) { processX(new AxisFunction<org.eclipse.scada.chart.XAxis>() { @Override public void process(final org.eclipse.scada.chart.XAxis axis) { axis.shiftByTimespan(duration, timeUnit); } }); } public void setNowCenter() { processX(new AxisFunction<org.eclipse.scada.chart.XAxis>() { @Override public void process(final org.eclipse.scada.chart.XAxis axis) { axis.setNowCenter(); } }); } public Chart getChartConfiguration() { return this.chart; } private void handleDrop() { logger.debug("Handle drop"); if (!this.mutable) { logger.debug("Drop, but we are not mutable"); return; } final ISelection selection = LocalSelectionTransfer.getTransfer().getSelection(); { final Collection<org.eclipse.scada.da.ui.connection.data.Item> data = SelectionHelper.list(selection, org.eclipse.scada.da.ui.connection.data.Item.class); if (!data.isEmpty()) { for (final Item item : data) { Charts.addItem(this.chart, item); } return; } } { final Collection<org.eclipse.scada.hd.ui.connection.data.Item> data = SelectionHelper.list(selection, org.eclipse.scada.hd.ui.connection.data.Item.class); if (!data.isEmpty()) { for (final org.eclipse.scada.hd.ui.connection.data.Item item : data) { Charts.addItem(this.chart, item); } return; } } } }