Java tutorial
/* * This file is part of the openSCADA project * Copyright (C) 2006-2012 TH4 SYSTEMS GmbH (http://th4-systems.com) * * openSCADA is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * openSCADA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with openSCADA. If not, see * <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License. */ package org.openscada.ui.chart.viewer; import java.util.Collection; import java.util.Date; 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.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.swt.SWT; 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.Color; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; import org.openscada.chart.Realm; import org.openscada.chart.swt.ChartMouseListener.MouseState; import org.openscada.chart.swt.ChartRenderer; import org.openscada.chart.swt.DisplayRealm; import org.openscada.chart.swt.DisposeListener; import org.openscada.chart.swt.controller.MouseHover; import org.openscada.chart.swt.controller.MouseHover.Listener; import org.openscada.chart.swt.render.CurrentTimeRuler; import org.openscada.da.ui.connection.data.Item; import org.openscada.ui.chart.model.ChartModel.ArchiveSeries; import org.openscada.ui.chart.model.ChartModel.Chart; import org.openscada.ui.chart.model.ChartModel.ChartFactory; import org.openscada.ui.chart.model.ChartModel.ChartPackage; import org.openscada.ui.chart.model.ChartModel.DataItemSeries; import org.openscada.ui.chart.model.ChartModel.IdItem; import org.openscada.ui.chart.model.ChartModel.UriItem; import org.openscada.ui.chart.model.ChartModel.XAxis; import org.openscada.ui.chart.model.ChartModel.YAxis; import org.openscada.ui.chart.viewer.controller.ControllerManager; import org.openscada.ui.chart.viewer.input.ChartInput; import org.openscada.ui.chart.viewer.profile.ProfileManager; import org.openscada.ui.utils.AbstractSelectionProvider; 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 CurrentTimeRuler timeRuler; private final Display display; private ChartInput selection; private final Chart chart; private final DataBindingContext ctx; private final ResourceManager resourceManager; private Color chartBackground; private final YAxisManager leftManager; private final YAxisManager rightManager; private final XAxisManager topManager; private final XAxisManager bottomManager; private XAxisViewer selectedXAxis; private YAxisViewer selectedYAxis; private boolean showTimeRuler; private final DisplayRealm realm; private final SimpleAxisLocator<XAxis, XAxisViewer> xLocator; private final SimpleAxisLocator<YAxis, YAxisViewer> yLocator; private final InputManager inputManager; private YAxis selectedYAxisElement; private XAxis selectedXAxisElement; private final Set<ChartViewerListener> listeners = new LinkedHashSet<ChartViewerListener>(); private boolean mutable; private boolean hoverable; private final MouseHover.Listener hoverListener = new Listener() { @Override public void mouseMove(final MouseState e, final long timestamp) { // no-op } }; private MouseHover mouseHover; private final ControllerManager controllerManager; private final ProfileManager profileManager; private final ChartContext chartContext; public ChartViewer(final ChartRenderer chartRenderer, final Chart chart, final ExtensionSpaceProvider extensionSpaceProvider, final ResetHandler resetHandler) { this.chart = chart; this.display = chartRenderer.getDisplay(); 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.realm = new DisplayRealm(this.display); this.manager.createDropTarget(new Transfer[] { LocalSelectionTransfer.getTransfer() }, createDropTarget()); 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.manager, "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, "selectedXAxis"), //$NON-NLS-1$ EMFObservables.observeValue(this.chart, ChartPackage.Literals.CHART__SELECTED_XAXIS)); this.ctx.bindValue(PojoObservables.observeValue(this, "selectedYAxis"), //$NON-NLS-1$ EMFObservables.observeValue(this.chart, ChartPackage.Literals.CHART__SELECTED_YAXIS)); this.chartContext = new ChartContextImpl(this.xLocator, this.yLocator, extensionSpaceProvider, chartRenderer, chart, resetHandler); 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)); startTimer(); this.manager.addDisposeListener(new DisposeListener() { @Override public void onDispose() { handleDispose(); } }); setSelection(new StructuredSelection(this)); } 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 Date date = new Date(timestamp); for (final Object input : this.items) { ((ChartInput) input).setSelection(date); } } 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.openscada.chart.XAxis x; final org.openscada.chart.YAxis y; x = getSelectedXAxisViewer(); y = getSelectedYAxisViewer(); // update mouse controllers if (this.mouseHover != null) { this.mouseHover.dispose(); this.mouseHover = null; } if (x != null && y != null && isHoverable()) { this.mouseHover = new MouseHover(this.manager, x, this.hoverListener); this.mouseHover.setVisible(true); } // update current time tracker if (this.timeRuler == null && this.showTimeRuler && x != null) { this.timeRuler = new CurrentTimeRuler(x); this.timeRuler.setColor(this.manager.getDisplay().getSystemColor(SWT.COLOR_BLUE)); this.manager.addRenderer(this.timeRuler, 100); } else if (this.timeRuler != null && !this.showTimeRuler || x == null) { this.manager.removeRenderer(this.timeRuler); this.timeRuler = null; } } private org.openscada.chart.YAxis getSelectedYAxisViewer() { return this.selectedYAxis != null ? this.selectedYAxis.getAxis() : null; } private org.openscada.chart.XAxis getSelectedXAxisViewer() { return this.selectedXAxis != null ? this.selectedXAxis.getAxis() : null; } public XAxis getSelectedXAxis() { return this.selectedXAxisElement; } public YAxis getSelectedYAxis() { return this.selectedYAxisElement; } public void setSelectedXAxis(final XAxis axis) { final XAxisViewer newSelection = this.xLocator.findAxis(axis); if (this.selectedXAxis == newSelection) { return; } this.selectedXAxis = newSelection; this.selectedXAxisElement = axis; updateState(); } public void setSelectedYAxis(final YAxis axis) { final YAxisViewer newSelection = this.yLocator.findAxis(axis); if (this.selectedYAxis == newSelection) { return; } this.selectedYAxis = newSelection; this.selectedYAxisElement = axis; updateState(); } public void setChartBackground(final RGB rgb) { if (this.chartBackground != null) { this.resourceManager.destroyColor(rgb); } this.chartBackground = this.resourceManager.createColor(rgb); this.manager.setChartBackground(this.chartBackground); } public RGB getChartBackground() { final Color background = this.chartBackground; if (background == null) { return null; } else { return background.getRGB(); } } protected void handleDispose() { this.inputManager.dispose(); this.topManager.dispose(); this.bottomManager.dispose(); this.leftManager.dispose(); this.rightManager.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.openscada.da.ui.connection.data.Item> data = org.openscada.da.ui.connection.data.ItemSelectionHelper .getSelection(selection); if (!data.isEmpty()) { event.detail = DND.DROP_COPY; return; } } { final Collection<org.openscada.hd.ui.connection.data.Item> data = org.openscada.hd.ui.connection.data.ItemSelectionHelper .getSelection(LocalSelectionTransfer.getTransfer().getSelection()); 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 addItem(final org.openscada.hd.ui.connection.data.Item item) { if (this.selectedXAxisElement == null || this.selectedYAxisElement == null) { return; } org.openscada.ui.chart.model.ChartModel.Item itemRef = null; switch (item.getType()) { case ID: { itemRef = ChartFactory.eINSTANCE.createIdItem(); itemRef.setItemId(item.getId()); ((IdItem) itemRef).setConnectionId(item.getConnectionString()); break; } case URI: { itemRef = ChartFactory.eINSTANCE.createUriItem(); itemRef.setItemId(item.getId()); ((UriItem) itemRef).setConnectionUri(item.getConnectionString()); break; } } if (itemRef == null) { return; } final ArchiveSeries input = ChartFactory.eINSTANCE.createArchiveSeries(); input.setLabel(item.toLabel()); input.setItem(itemRef); input.setX(this.selectedXAxisElement); input.setY(this.selectedYAxisElement); input.getLineProperties().setColor(nextFreeColor()); this.chart.getInputs().add(input); } public void addItem(final Item item) { if (this.selectedXAxisElement == null || this.selectedYAxisElement == null) { return; } org.openscada.ui.chart.model.ChartModel.Item itemRef = null; switch (item.getType()) { case ID: { itemRef = ChartFactory.eINSTANCE.createIdItem(); ((IdItem) itemRef).setConnectionId(item.getId()); itemRef.setItemId(item.getId()); break; } case URI: { itemRef = ChartFactory.eINSTANCE.createUriItem(); ((UriItem) itemRef).setConnectionUri(item.getConnectionString()); itemRef.setItemId(item.getId()); break; } } if (itemRef == null) { return; } final DataItemSeries input = ChartFactory.eINSTANCE.createDataItemSeries(); input.setLabel(item.toLabel()); input.setItem(itemRef); input.setX(this.selectedXAxisElement); input.setY(this.selectedYAxisElement); input.getLineProperties().setColor(nextFreeColor()); this.chart.getInputs().add(input); } private static RGB[] DEFAULT_COLORS = new RGB[] { // new RGB(255, 0, 0), // red new RGB(0, 255, 0), // green new RGB(0, 255, 255), // blue new RGB(255, 194, 0), // yellow new RGB(255, 0, 255), // magenta new RGB(0, 255, 255), // cyan }; private RGB nextFreeColor() { return DEFAULT_COLORS[this.items.size() % DEFAULT_COLORS.length]; } public void addInput(final ChartInput input) { if (this.items.size() == 1) { ((ChartInput) this.items.get(0)).setSelection(false); } this.items.add(input); if (this.items.size() == 1) { setSelection(input); } updateTitle(); // fire events fireInputAdded(input); } public void removeInput(final ChartInput input) { if (input == this.selection) { setSelection((ChartInput) null); } if (this.items.remove(input)) { fireInputRemoved(input); } } protected void updateTitle() { if (this.items.isEmpty()) { this.chart.setTitle(Messages.ChartViewer_Title_NoItem); } else if (this.items.size() == 1) { this.chart.setTitle(((ChartInput) this.items.get(0)).getLabel()); } else if (this.selection != null) { this.chart.setTitle(String.format(Messages.ChartViewer_Title_Selection, this.items.size(), this.selection.getLabel())); } else { this.chart.setTitle(String.format(Messages.ChartViewer_Title_NoSelection, this.items.size())); } } 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); } updateTitle(); } public void tick() { final long now = System.currentTimeMillis(); try { this.manager.setStale(true); for (final Object item : this.items) { ((ChartInput) item).tick(now); } } finally { this.manager.setStale(false, true); } } 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 Observables.unmodifiableObservableList(this.items); } public void showTimespan(final long duration, final TimeUnit timeUnit) { final org.openscada.chart.XAxis x = getSelectedXAxisViewer(); if (x != null) { x.setByTimespan(duration, timeUnit); } } public void pageTimespan(final long duration, final TimeUnit timeUnit) { final org.openscada.chart.XAxis x = getSelectedXAxisViewer(); if (x != null) { x.shiftByTimespan(duration, timeUnit); } } public void setNowCenter() { final org.openscada.chart.XAxis x = getSelectedXAxisViewer(); if (x != null) { x.setNowCenter(); } } public Chart getChartConfiguration() { return this.chart; } private void handleDrop() { if (!this.mutable) { return; } final ISelection selection = LocalSelectionTransfer.getTransfer().getSelection(); { final Collection<org.openscada.da.ui.connection.data.Item> data = org.openscada.da.ui.connection.data.ItemSelectionHelper .getSelection(selection); if (!data.isEmpty()) { for (final Item item : data) { addItem(item); } return; } } { final Collection<org.openscada.hd.ui.connection.data.Item> data = org.openscada.hd.ui.connection.data.ItemSelectionHelper .getSelection(LocalSelectionTransfer.getTransfer().getSelection()); if (!data.isEmpty()) { for (final org.openscada.hd.ui.connection.data.Item item : data) { addItem(item); } return; } } } }