| package org.eclipse.stem.ui.reports.views; |
| |
| /******************************************************************************* |
| * Copyright (c) 2006 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.birt.chart.device.IDeviceRenderer; |
| import org.eclipse.birt.chart.exception.ChartException; |
| import org.eclipse.birt.chart.factory.Generator; |
| import org.eclipse.birt.chart.model.Chart; |
| import org.eclipse.birt.chart.model.ChartWithAxes; |
| import org.eclipse.birt.chart.model.attribute.AxisType; |
| import org.eclipse.birt.chart.model.attribute.Bounds; |
| import org.eclipse.birt.chart.model.attribute.IntersectionType; |
| import org.eclipse.birt.chart.model.attribute.Marker; |
| import org.eclipse.birt.chart.model.attribute.SortOption; |
| import org.eclipse.birt.chart.model.attribute.TickStyle; |
| import org.eclipse.birt.chart.model.attribute.impl.BoundsImpl; |
| import org.eclipse.birt.chart.model.attribute.impl.ColorDefinitionImpl; |
| import org.eclipse.birt.chart.model.component.Axis; |
| import org.eclipse.birt.chart.model.component.Series; |
| import org.eclipse.birt.chart.model.component.impl.SeriesImpl; |
| import org.eclipse.birt.chart.model.data.NumberDataSet; |
| import org.eclipse.birt.chart.model.data.SeriesDefinition; |
| import org.eclipse.birt.chart.model.data.impl.NumberDataElementImpl; |
| import org.eclipse.birt.chart.model.data.impl.NumberDataSetImpl; |
| import org.eclipse.birt.chart.model.data.impl.SeriesDefinitionImpl; |
| import org.eclipse.birt.chart.model.impl.ChartWithAxesImpl; |
| import org.eclipse.birt.chart.model.layout.Legend; |
| import org.eclipse.birt.chart.model.layout.Plot; |
| import org.eclipse.birt.chart.model.type.ScatterSeries; |
| import org.eclipse.birt.chart.model.type.impl.ScatterSeriesImpl; |
| import org.eclipse.birt.chart.util.PluginSettings; |
| import org.eclipse.emf.edit.provider.ItemPropertyDescriptor; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.stem.core.model.STEMTime; |
| import org.eclipse.stem.definitions.adapters.relativevalue.history.RelativeValueHistoryProvider; |
| import org.eclipse.stem.definitions.adapters.relativevalue.history.RelativeValueHistoryProviderAdapter; |
| import org.eclipse.stem.ui.reports.Activator; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.PaintEvent; |
| import org.eclipse.swt.events.PaintListener; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.widgets.Canvas; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.ui.IWorkbenchActionConstants; |
| |
| /** |
| * TimeSeriesCanvas is a subclass of Canvas suitable for chart drawings. |
| */ |
| public class PhaseSpaceCanvas extends Canvas { |
| |
| protected IDeviceRenderer idr = null; |
| |
| /** |
| * This is the <code>Chart</code> that plots the relative values. |
| */ |
| protected Chart cm = null; |
| |
| /** |
| * The provider of relative values. |
| */ |
| private RelativeValueHistoryProvider rvhp; |
| |
| /** |
| * This is the property of the label updated by the selected decorator whose |
| * relative value will be plotted along X |
| */ |
| private ItemPropertyDescriptor propertyToPlotX; |
| |
| /** |
| * This is the property of the label updated by the selected decorator whose |
| * relative value will be plotted along Y |
| */ |
| private ItemPropertyDescriptor propertyToPlotY; |
| |
| /** |
| * These are the values that will be plotted in X |
| * |
| */ |
| private final List<Double> relativeValuesX = new ArrayList<Double>(); |
| |
| /** |
| * These are the values that will be plotted in Y |
| * |
| */ |
| private final List<Double> relativeValuesY = new ArrayList<Double>(); |
| |
| /** |
| * Log of zero is negative infinity so for each location we will cut off the minimum value |
| * of log(y) or log(x) at 0.5/POPULATION for display purposes only |
| */ |
| private double minLogScaleValue = 1.0; |
| |
| |
| /** |
| * Chart generator instance (reference to a singleton) |
| */ |
| Generator gr; |
| |
| /** |
| * The xAxis in the Phase Space Plot |
| */ |
| private Axis xAxisPrimary; |
| |
| /** |
| * The yAxis in the Phase Space Plot |
| */ |
| private Axis yAxisPrimary; |
| |
| /** |
| * default y axis label. This value is read from |
| * messages.properties (NLS) |
| */ |
| private static String defaultAxisLabel_Y="Y Axis"; |
| |
| /** |
| * default x axis label. This value is read from |
| * messages.properties (NLS) |
| */ |
| private static String defaultAxisLabel_X="X Axis"; |
| |
| boolean useLinearScales = true; |
| |
| private LinearScalesAction linearScalesAction; |
| |
| private LogarithmicScalesAction logTimeScalesAction; |
| |
| private String xAxisLabel; |
| |
| private String yAxisLabel; |
| |
| Image imgChart = null; |
| |
| /** |
| * Constructor. |
| * |
| * @param parent |
| * the SWT parent of the Widget |
| */ |
| public PhaseSpaceCanvas(final Composite parent) { |
| super(parent, SWT.DOUBLE_BUFFERED | SWT.BORDER); |
| |
| gr = Generator.instance(); |
| |
| try { |
| idr = PluginSettings.instance().getDevice("dv.SWT"); //$NON-NLS-1$ |
| } catch (final ChartException pex) { |
| Activator.logError("Problem initializing chart", pex); //$NON-NLS-1$ |
| return; |
| } |
| |
| resetData(); |
| cm = createPhaseSpaceLineChart(relativeValuesX, relativeValuesY, |
| Messages.getString("PH.title")); //$NON-NLS-1$ |
| |
| |
| addPaintListener(new PaintListener() { |
| public void paintControl(final PaintEvent pe) { |
| |
| final Composite source = (Composite) pe.getSource(); |
| final org.eclipse.swt.graphics.Rectangle d = source |
| .getClientArea(); |
| |
| if(imgChart != null) imgChart.dispose(); |
| imgChart = new Image(source.getDisplay(), d); |
| idr.setProperty(IDeviceRenderer.GRAPHICS_CONTEXT, new GC( |
| imgChart)); |
| final Bounds bounds = BoundsImpl.create(d.x, d.y, d.width, |
| d.height); |
| bounds.scale(72d / idr.getDisplayServer().getDpiResolution()); |
| // BOUNDS MUST BE SPECIFIED IN POINTS |
| |
| try { |
| gr.render(idr, gr.build(idr.getDisplayServer(), cm, bounds, |
| null, null, null)); |
| pe.gc.drawImage(imgChart, d.x, d.y); |
| } catch (final ChartException ce) { |
| Activator.logError("Problem rendering chart", ce); //$NON-NLS-1$ |
| } |
| |
| } // paintControl |
| } // PaintListener |
| |
| ); |
| |
| //Create a context menu for the canvas |
| createContextMenu(this); |
| |
| } // TimeSeriesCanvas |
| |
| /** |
| * The method which gets the TimeSeriesCanvas' reports list, and draws it on |
| * the TimeSeriesCanvas. |
| * |
| */ |
| public void draw() { |
| // Has a relative value provider been provided? |
| if (rvhp != null) { |
| // Yes |
| |
| ///////////////////////////////////////////////////////////// |
| // Log(0.0) is negative infinity so for display purposes only |
| // we set the minimum axis value at 0.5/POPULATION |
| double denom = rvhp.getDenominator(null); |
| if(denom <=0.0) denom = 1.0; |
| minLogScaleValue = 0.5/denom; |
| ///////////////////////////////////////////////////////////// |
| |
| if(propertyToPlotX == null || propertyToPlotY==null) return; |
| // time |
| STEMTime[] time = rvhp.getAllHistoricTimeValues(); |
| // Get the values for the property to be plotted |
| final double[] relativeValuesX = rvhp |
| .getHistoricInstances(propertyToPlotX,time); |
| final double[] relativeValuesY = rvhp |
| .getHistoricInstances(propertyToPlotY,time); |
| // Any values? |
| if ((relativeValuesX.length > 0) && ((relativeValuesY.length > 0))) { |
| // Yes |
| clearData(); |
| for (int cycleNumber = 0; cycleNumber < relativeValuesX.length; cycleNumber++) { |
| Double valueX = null, valueY = null; |
| double displayValueX = relativeValuesX[cycleNumber]; |
| double displayValueY = relativeValuesY[cycleNumber]; |
| |
| if (displayValueX <= minLogScaleValue) { |
| // Log(0.0) is negative infinity so for display purposes only |
| // we set the minimum axis value at 0.5/POPULATION |
| displayValueX = minLogScaleValue; |
| } |
| |
| if (displayValueY <= minLogScaleValue) { |
| // Log(0.0) is negative infinity so for display purposes only |
| // we set the minimum axis value at 0.5/POPULATION |
| displayValueY = minLogScaleValue; |
| } |
| |
| |
| if (useLinearScales) { |
| valueX = new Double(displayValueX); |
| valueY = new Double(displayValueY); |
| } |
| else { |
| valueX = new Double(Math.log(displayValueX)); |
| valueY = new Double(Math.log(displayValueY)); |
| } |
| this.relativeValuesX.add(valueX); |
| this.relativeValuesY.add(valueY); |
| } // for |
| } // if |
| else { |
| resetData(); |
| } |
| if (!this.isDisposed()) redraw(); |
| } // if a relative value provider has been provided |
| |
| } // paintControl |
| |
| /** |
| * Create the view's context menu and add the action handlers to it. |
| */ |
| private void createContextMenu(final Composite parent) { |
| |
| // Context Menu |
| final MenuManager contextMenuManager = new MenuManager(); |
| |
| // --------------------------------------------------------------------- |
| |
| linearScalesAction = new LinearScalesAction(); |
| logTimeScalesAction = new LogarithmicScalesAction(); |
| contextMenuManager.add(linearScalesAction); |
| contextMenuManager.add(logTimeScalesAction); |
| |
| |
| // Place Holder for Menu Additions |
| contextMenuManager.add(new Separator( |
| IWorkbenchActionConstants.MB_ADDITIONS)); |
| |
| // --------------------------------------------------------------------- |
| |
| final Menu popUpMenu = contextMenuManager.createContextMenu(parent); |
| |
| // Set the context menu for the viewer |
| parent.setMenu(popUpMenu); |
| |
| } // createContextMenu |
| |
| /** |
| * @param relativeValuesX |
| * the <code>List</code> that will contain the relative X axis |
| * values (0.0-1.0) to plot |
| * @param relativeValuesY |
| * that will contain the relative Y axis values (0.0-1.0) to plot |
| * @param seriesIdentifier |
| * the title of the graph |
| * @return a <code>Chart</code> |
| */ |
| @SuppressWarnings("deprecation") |
| public final Chart createPhaseSpaceLineChart( |
| final List<Double> relativeValuesX, |
| final List<Double> relativeValuesY, final String seriesIdentifier) { |
| final ChartWithAxes retValue = ChartWithAxesImpl.create(); |
| |
| // get the NLS default labels |
| defaultAxisLabel_X = Messages.getString("XAXISDEF.title"); |
| defaultAxisLabel_Y = Messages.getString("YAXISDEF.title"); |
| // Plot |
| retValue.getBlock().setBackground(ColorDefinitionImpl.WHITE()); |
| final Plot p = retValue.getPlot(); |
| p.getClientArea().setBackground( |
| ColorDefinitionImpl.create(255, 255, 225)); |
| |
| // Title |
| // cwaLine.getTitle( ).getLabel( ).getCaption( ).setValue( "Line Chart" |
| // );//$NON-NLS-1$ |
| retValue.getTitle().setVisible(false); |
| |
| // Legend |
| final Legend lg = retValue.getLegend(); |
| lg.setVisible(false); |
| /* |
| final LineAttributes lia = lg.getOutline(); |
| lg.getText().getFont().setSize(8); |
| lia.setStyle(LineStyle.SOLID_LITERAL); |
| lg.getInsets().set(10, 5, 0, 0); |
| lg.getOutline().setVisible(false); |
| lg.setAnchor(Anchor.NORTH_LITERAL); |
| lg.setPosition(Position.BELOW_LITERAL); |
| */ |
| // cwaLine.getLegend( ).getText().getFont().setSize(16);; |
| // cwaLine.getLegend( ).setVisible( true ); |
| |
| // X-Axis |
| xAxisPrimary = retValue.getPrimaryBaseAxes()[0]; |
| xAxisPrimary.setType(AxisType.LINEAR_LITERAL); |
| xAxisPrimary.getMajorGrid().setTickStyle(TickStyle.BELOW_LITERAL); |
| xAxisPrimary.getOrigin().setType(IntersectionType.VALUE_LITERAL); |
| xAxisPrimary.getTitle( ).getCaption( ).getFont( ).setSize( 8 ); |
| xAxisLabel = defaultAxisLabel_X; |
| if (propertyToPlotX!= null) { |
| xAxisLabel = this.propertyToPlotX.getDisplayName(propertyToPlotX); |
| } |
| xAxisPrimary.getTitle( ).getCaption( ).setValue( xAxisLabel ); |
| xAxisPrimary.getTitle( ).setVisible(true); |
| |
| // Y-Axis |
| yAxisPrimary = retValue.getPrimaryOrthogonalAxis(xAxisPrimary); |
| yAxisPrimary.getMajorGrid().setTickStyle(TickStyle.LEFT_LITERAL); |
| yAxisPrimary.getTitle( ).getCaption( ).getFont( ).setSize( 8 ); |
| yAxisLabel = defaultAxisLabel_Y; |
| if (propertyToPlotY!= null) { |
| yAxisLabel = this.propertyToPlotY.getDisplayName(propertyToPlotY); |
| } |
| yAxisPrimary.getTitle( ).getCaption( ).setValue( yAxisLabel ); |
| yAxisPrimary.getTitle( ).setVisible(true); |
| |
| final NumberDataSet orthoValuesX = NumberDataSetImpl.create(relativeValuesX); |
| final NumberDataSet orthoValuesY = NumberDataSetImpl.create(relativeValuesY); |
| |
| // X-Series |
| final Series lsx = SeriesImpl.create(); |
| // final LineSeries lsx = (ScatterSeries) ScatterSeriesImpl.create(); |
| // seCategory.setDataSet(orthoValuesX); |
| lsx.setDataSet(orthoValuesX); |
| |
| // Y-Series |
| final ScatterSeries lsy = (ScatterSeries) ScatterSeriesImpl.create(); |
| lsy.setDataSet(orthoValuesY); |
| lsy.getLineAttributes().setVisible(true); |
| |
| // fix to bug 375126 |
| // The latest version of birt uses Palette colors by default. In order to set the color the way we want |
| // we must first issue: |
| lsy.setPaletteLineColor(false); |
| |
| lsy.getLineAttributes().setColor(ColorDefinitionImpl.BLUE()); |
| lsy.setSeriesIdentifier(seriesIdentifier); |
| |
| //lsy.getMarker().setVisible(false); |
| // replaces deprecated code: lineSeries.getMarker().setVisible(false); |
| if (!lsy.getMarkers().isEmpty()) { |
| Marker marker = lsy.getMarkers().get(0); |
| marker.setVisible(false); |
| } |
| |
| final SeriesDefinition sdX = SeriesDefinitionImpl.create(); |
| sdX.setSorting(SortOption.ASCENDING_LITERAL); |
| final SeriesDefinition sdY = SeriesDefinitionImpl.create(); |
| sdY.setSorting(SortOption.ASCENDING_LITERAL); |
| |
| sdY.getSeriesPalette().update(-2); |
| |
| xAxisPrimary.getSeriesDefinitions().add(sdX); |
| xAxisPrimary.setCategoryAxis(false); |
| yAxisPrimary.getSeriesDefinitions().add(sdY); |
| |
| // sdX.getSeries().add(seCategory); |
| sdX.getSeries().add(lsx); |
| sdY.getSeries().add(lsy); |
| |
| xAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0.0)); |
| xAxisPrimary.getScale().setMax(NumberDataElementImpl.create(1.0)); |
| xAxisPrimary.getScale().setStep(0.25); |
| xAxisPrimary.getLabel().getCaption().getFont().setSize(9); |
| |
| yAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0.0)); |
| yAxisPrimary.getScale().setMax(NumberDataElementImpl.create(1.0)); |
| yAxisPrimary.getScale().setStep(0.25); |
| yAxisPrimary.getLabel().getCaption().getFont().setSize(9); |
| |
| return retValue; |
| } // createPhaseSpaceLineChart |
| |
| /** |
| * @param rvhp |
| * @param propertyToPlotX |
| * @param propertyToPlotY |
| */ |
| public void setDataSourceAndRedraw( |
| final RelativeValueHistoryProviderAdapter rvhp, |
| final ItemPropertyDescriptor propertyToPlotX, |
| final ItemPropertyDescriptor propertyToPlotY) { |
| this.rvhp = rvhp; |
| this.propertyToPlotX = propertyToPlotX; |
| this.propertyToPlotY = propertyToPlotY; |
| |
| xAxisLabel = defaultAxisLabel_X; |
| if (propertyToPlotX!= null) { |
| xAxisLabel = this.propertyToPlotX.getDisplayName(propertyToPlotX); |
| if(!useLinearScales) xAxisLabel = "Log("+xAxisLabel+")"; |
| } |
| xAxisPrimary.getTitle( ).getCaption( ).setValue( xAxisLabel ); |
| xAxisPrimary.getTitle( ).setVisible(true); |
| |
| yAxisPrimary.getTitle( ).getCaption( ).getFont( ).setSize( 8 ); |
| yAxisLabel = defaultAxisLabel_Y; |
| if (propertyToPlotY!= null) { |
| yAxisLabel = this.propertyToPlotY.getDisplayName(propertyToPlotY); |
| if(!useLinearScales) yAxisLabel = "Log("+yAxisLabel+")"; |
| } |
| yAxisPrimary.getTitle( ).getCaption( ).setValue( yAxisLabel ); |
| yAxisPrimary.getTitle( ).setVisible(true); |
| |
| draw(); |
| } // setDataSourceAndRedraw |
| |
| /** |
| * Set the provider of relative values. It will be consulted to produce a |
| * sequence of relative values (0.0 to 1.0) that will be plotted. |
| * |
| * @param rvhp |
| * the provider of relative values. |
| */ |
| public void setRelativeValueHistoryProvider( |
| final RelativeValueHistoryProvider rvhp) { |
| this.rvhp = rvhp; |
| } // setRelativeValueHistoryProvider |
| |
| /** |
| * @return the propertyToPlotX |
| */ |
| public final ItemPropertyDescriptor getPropertyToPlotX() { |
| return propertyToPlotX; |
| } |
| |
| /** |
| * @return the propertyToPlotY |
| */ |
| public final ItemPropertyDescriptor getPropertyToPlotY() { |
| return propertyToPlotY; |
| } |
| |
| /** |
| * @param propertyToPlotX |
| * the propertyToPlotX to set |
| */ |
| public final void setPropertyToPlotX( |
| final ItemPropertyDescriptor propertyToPlotX) { |
| this.propertyToPlotX = propertyToPlotX; |
| } |
| |
| /** |
| * @param propertyToPlotY |
| * the propertyToPlotY to set |
| */ |
| public final void setPropertyToPlotY( |
| final ItemPropertyDescriptor propertyToPlotY) { |
| this.propertyToPlotY = propertyToPlotY; |
| } |
| |
| /** |
| * Disposes the Color objects |
| */ |
| @Override |
| public void dispose() { |
| super.dispose(); |
| } |
| |
| /** |
| * reset |
| */ |
| public void reset() { |
| resetData(); |
| rvhp = null; |
| redraw(); |
| } |
| |
| protected void resetData() { |
| clearData(); |
| relativeValuesX.add(new Double(1.0)); |
| relativeValuesY.add(new Double(0.0)); |
| } |
| |
| private void clearData() { |
| relativeValuesX.clear(); |
| relativeValuesY.clear(); |
| } |
| |
| void toggleScales() { |
| if (useLinearScales) { //Switch to logarithmic scale |
| logTimeScalesAction.setChecked(true); |
| linearScalesAction.setChecked(false); |
| |
| xAxisPrimary.getScale().setMin(null); |
| xAxisPrimary.getScale().setMax(null); |
| xAxisPrimary.getScale().unsetStep(); |
| xAxisPrimary.getScale().unsetStepNumber(); |
| xAxisPrimary.getTitle().getCaption().setValue("Log(" + xAxisLabel + ")"); |
| |
| yAxisPrimary.getScale().setMin(null); |
| yAxisPrimary.getScale().setMax(null); |
| yAxisPrimary.getScale().unsetStep(); |
| yAxisPrimary.getScale().unsetStepNumber(); |
| yAxisPrimary.getTitle().getCaption().setValue("Log(" + yAxisLabel + ")"); |
| } |
| else { //Switch to linear scale |
| logTimeScalesAction.setChecked(false); |
| linearScalesAction.setChecked(true); |
| |
| xAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0.0)); |
| xAxisPrimary.getScale().setMax(NumberDataElementImpl.create(1.0)); |
| xAxisPrimary.getScale().setStep(0.25); |
| xAxisPrimary.getTitle().getCaption().setValue(xAxisLabel); |
| |
| yAxisPrimary.getScale().setMin(NumberDataElementImpl.create(0.0)); |
| yAxisPrimary.getScale().setMax(NumberDataElementImpl.create(1.0)); |
| yAxisPrimary.getScale().setStep(0.25); |
| yAxisPrimary.getTitle().getCaption().setValue(yAxisLabel); |
| } |
| useLinearScales = !useLinearScales; |
| redraw(); |
| } |
| |
| protected class LinearScalesAction extends Action |
| { |
| public LinearScalesAction() |
| { |
| super(Messages.getString("ContextMenu.LinearScaling"), IAction.AS_CHECK_BOX); |
| setChecked(useLinearScales); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#getText() |
| */ |
| @Override |
| public String getText() { |
| return Messages.getString("ContextMenu.LinearScaling"); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#run() |
| */ |
| @Override |
| public void run() { |
| if (useLinearScales) { |
| setChecked(true); |
| //Nothing to do. It's already linear-time. |
| } |
| else { |
| toggleScales(); |
| } |
| } |
| } |
| |
| class LogarithmicScalesAction extends Action |
| { |
| public LogarithmicScalesAction() |
| { |
| super(Messages.getString("ContextMenu.LogScaling"), IAction.AS_CHECK_BOX); |
| setChecked(!useLinearScales); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#getText() |
| */ |
| @Override |
| public String getText() { |
| return Messages.getString("ContextMenu.LogScaling"); |
| } |
| |
| /** |
| * @see org.eclipse.jface.action.Action#run() |
| */ |
| @Override |
| public void run() { |
| if (!useLinearScales) { |
| setChecked(true); |
| //Nothing to do. It's already log-time. |
| } |
| else { |
| toggleScales(); |
| } |
| } |
| } |
| } // PhaseSpaceCanvas |