blob: d706c3ac845a43e47b2fb44e7f986dd986a894d6 [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2009 Metascape, LLC.
* 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:
* Metascape - Initial API and Implementation
*
* </copyright>
*
*/
package org.eclipse.amp.agf.chart;
import java.util.List;
import org.eclipse.amp.agf.chart.ide.AGFChartPlugin;
import org.eclipse.amp.axf.core.IModel;
import org.eclipse.amp.axf.core.IObservationProvider;
import org.eclipse.amp.axf.view.SWTAsyncModelListener;
import org.eclipse.birt.chart.api.ChartEngine;
import org.eclipse.birt.chart.device.ICallBackNotifier;
import org.eclipse.birt.chart.device.IDeviceRenderer;
import org.eclipse.birt.chart.exception.ChartException;
import org.eclipse.birt.chart.factory.GeneratedChartState;
import org.eclipse.birt.chart.factory.Generator;
import org.eclipse.birt.chart.factory.RunTimeContext;
import org.eclipse.birt.chart.model.Chart;
import org.eclipse.birt.chart.model.attribute.Bounds;
import org.eclipse.birt.chart.model.attribute.CallBackValue;
import org.eclipse.birt.chart.model.attribute.ColorDefinition;
import org.eclipse.birt.chart.model.attribute.Fill;
import org.eclipse.birt.chart.model.attribute.Palette;
import org.eclipse.birt.chart.model.attribute.impl.BoundsImpl;
import org.eclipse.birt.chart.model.attribute.impl.ColorDefinitionImpl;
import org.eclipse.birt.chart.model.attribute.impl.PaletteImpl;
import org.eclipse.birt.core.framework.PlatformConfig;
import org.eclipse.core.runtime.IAdapterManager;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.draw2d.AbstractLayout;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.FigureListener;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.common.util.EList;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.statushandlers.StatusManager;
public class ChartEditPart extends AbstractGraphicalEditPart implements ICallBackNotifier {
private Chart chart;
private final ResourceManager resourceManager;
private IDeviceRenderer renderer;
private GeneratedChartState chartState;
private IDataProvider dataProvider;
private IColorProvider seriesColorProvider;
private Palette genericPalette;
private Object dataSource;
private ChartModelListener chartListener;
private IDataSelectionListener dataListener;
private int nextOpenPaletteIndex;
private IChartDesignStrategy chartStrategy;
/**
* Instantiates a new chart edit part.
*/
public ChartEditPart() {
resourceManager = new ResourceManager();
PlatformConfig config = new PlatformConfig();
try {
config.setProperty(IDeviceRenderer.UPDATE_NOTIFIER, this);
renderer = ChartEngine.instance(config).getRenderer("dv.SWT");
} catch (ChartException e) {
throw new RuntimeException(e);
}
genericPalette = PaletteImpl.create(0, true);
genericPalette.shift(0);
}
/**
* The listener interface for receiving chartModel events. The class that is
* interested in processing a chartModel event implements this interface, and
* the object created with that class is registered with a component using the
* component's <code>addChartModelListener<code> method. When
* the chartModel event occurs, that object's appropriate
* method is invoked.
*
* @see ChartModelEvent
*/
class ChartModelListener extends SWTAsyncModelListener {
/**
* Instantiates a new chart model listener.
*/
public ChartModelListener() {
super(null, "Chart Updated View", 10000);
}
@Override
public void observing(IObservationProvider observed) {
// wait for data source to be available from model set..
while (getDataProvider() == null) {
try {
Thread.sleep(25);
} catch (InterruptedException e) {
//
}
}
setChartStrategy(ChartType.TIME_SERIES.createStrategy());
}
@Override
public void observeStart(IObservationProvider observed) {
updateChartSelection();
endPainting();
}
@Override
public void observeInitialize(IObservationProvider model) {
getResourceManager().setImage(null);
}
@Override
public void update(final IModel model) {
beginPainting();
getChartStrategy().update();
refresh();
}
@Override
public void observationEnd(IObservationProvider model) {
getDataProvider().removeListener(getDataSource(), getDataListener());
model = null;
}
}
private void updateValueSetColor(Object valueSet) {
String seriesLabel = dataProvider.getText(valueSet);
Color swtColor = seriesColorProvider.getForeground(seriesLabel);
EList<Fill> entries = chartStrategy.getValueSetsDefinition().getSeriesPalette().getEntries();
ColorDefinition cd = null;
if (swtColor == null) {
// List<Fill> palette = ySeriesDefinition.getSeriesPalette().getEntries();
// if (nextOpenPaletteIndex >= palette.size()) {
// nextOpenPaletteIndex = 0;
// }
// cd = (ColorDefinition) palette.get(nextOpenPaletteIndex);
// Color lineColor = new
// Color(org.eclipse.swt.widgets.Display.getCurrent(), cd.getRed(),
// cd.getGreen(), cd
// .getBlue());
if (seriesColorProvider instanceof CachedColorProvider) {
cd = (ColorDefinition) genericPalette.getEntries().get(nextOpenPaletteIndex++);
if (nextOpenPaletteIndex > genericPalette.getEntries().size() - 1) {
nextOpenPaletteIndex = 0;
}
swtColor = new Color(org.eclipse.swt.widgets.Display.getCurrent(), cd.getRed(), cd.getGreen(), cd.getBlue());
((CachedColorProvider) seriesColorProvider).putForegroundColor(seriesLabel, swtColor);
}
} else {
cd = ColorDefinitionImpl.create(swtColor.getRed(), swtColor.getGreen(), swtColor.getBlue());
// if (chartType == ChartType.TIME_SERIES) {
// cd = ((LineSeries) lineSeries).getLineAttributes().getColor();
// } else if (chartType == ChartType.STACKED_AREA) {
// cd = ((AreaSeries) lineSeries).getLineAttributes().getColor();
// }
// cd.setRed(swtColor.getRed());
// cd.setGreen(swtColor.getGreen());
// cd.setBlue(swtColor.getBlue());
}
entries.add(cd);
}
@Override
protected IFigure createFigure() {
chartListener.setWidget(getViewer().getControl());
final Figure newFigure = new Figure() {
@Override
public void paintFigure(Graphics graphics) {
if (getResourceManager().getImage() != null) {
graphics.drawImage(getResourceManager().getImage(), 0, 0);
if (getChartListener() != null) {
getChartListener().endPainting();
}
}
}
};
newFigure.setLayoutManager(new AbstractLayout() {
@Override
public Dimension getMinimumSize(IFigure container, int hintWidth, int hintHeight) {
return new Dimension(hintWidth < 1 ? 1 : hintWidth, hintHeight < 1 ? 1 : hintHeight);
}
@Override
protected Dimension calculatePreferredSize(IFigure container, int hintWidth, int hintHeight) {
return new Dimension(hintWidth < 1 ? 100 : hintWidth, hintHeight < 1 ? 100 : hintHeight);
}
public void layout(IFigure container) {
newFigure.setLocation(new Point(0, 0));
}
});
newFigure.addFigureListener(new FigureListener() {
public void figureMoved(IFigure source) {
regenerateChart();
}
});
return newFigure;
}
@Override
protected void createEditPolicies() {
//
}
@Override
public void refresh() {
new Thread() {
@Override
public void run() {
regenerateChart(true);
}
}.run();
}
@Override
public void setModel(Object model) {
super.setModel(model);
chartListener = new ChartModelListener();
((IModel) model).addModelListener(chartListener);
IAdapterManager adapterManager = Platform.getAdapterManager();
// We seem to need to force loading of the data provider, at least in the
// exemplar case
Object provider = adapterManager.loadAdapter(this.getModel(), IDataProvider.ID);
if (!(provider instanceof IDataProvider)) {
throw new RuntimeException("Couldn't find data provider for chart model. Please ensure that a data provider adapter has been defined for the class: " + this.getModel().getClass());
}
setDataProvider((IDataProvider) provider);
// createChart();
}
/**
*
* @see org.eclipse.birt.chart.device.IUpdateNotifier#regenerateChart()
*/
public void regenerateChart() {
regenerateChart(true);
}
/**
* Regenerate chart.
*
* @param forceNewImage
* the force new image
*/
public synchronized void regenerateChart(boolean forceNewImage) {
if (forceNewImage) {
getResourceManager().setImage(null);
}
try {
if (chart != null && chartStrategy.isInitialized() && getFigure() != null && !getFigure().getSize().isEmpty()) {
createImage();
Dimension area = getFigure().getSize();
Bounds chartBounds = BoundsImpl.create(0, 0, area.width, area.height);
chartBounds.scale(72d / renderer.getDisplayServer().getDpiResolution());
Generator gr = Generator.instance();
RunTimeContext rtc = new RunTimeContext();
rtc.setScriptingEnabled(false);
// rtc.setScriptContext(csc);
// csc.setChartInstance(chart);
chartState = gr.build(renderer.getDisplayServer(), chart, chartBounds, rtc);
gr.render(renderer, chartState);
getFigure().repaint();
} else {
chartListener.endPainting();
}
} catch (ChartException ce) {
StatusManager.getManager().handle(new Status(Status.WARNING, AGFChartPlugin.PLUGIN_ID, "Couldn't generate chart.", ce));
chartListener.endPainting();
}
}
/**
* Creates the image.
*/
public void createImage() {
Dimension area = getFigure().getSize();
Rectangle swtBounds = new Rectangle(0, 0, Math.max(area.width, 10), Math.max(area.height, 10));
if (resourceManager.getImage() == null || !resourceManager.getImage().getBounds().equals(swtBounds)) {
resourceManager.setImage(new Image(getViewer().getControl().getDisplay(), swtBounds));
resourceManager.setGc(new GC(resourceManager.getImage()));
renderer.setProperty(IDeviceRenderer.GRAPHICS_CONTEXT, resourceManager.getGc());
}
}
/**
* Creates the chart.
*/
public synchronized void createChart() {
chart = chartStrategy.createChart(dataProvider, dataSource);
chart.getTitle().getLabel().setVisible(false);
}
/**
* Update chart series.
*/
public synchronized void updateChartSelection() {
chartStrategy.updateSelection();
@SuppressWarnings("rawtypes")
List valueSets = dataProvider.getValueSets(dataSource);
chartStrategy.getValueSetsDefinition().getSeriesPalette().getEntries().clear();
for (Object valueSet : valueSets) {
updateValueSetColor(valueSet);
}
}
/**
*
* @see org.eclipse.birt.chart.device.IUpdateNotifier#getDesignTimeModel()
*/
public Chart getDesignTimeModel() {
return chart;
}
/**
*
* @see org.eclipse.birt.chart.device.IUpdateNotifier#getRunTimeModel()
*/
public Chart getRunTimeModel() {
return chartState.getChartModel();
}
ChartModelListener getChartListener() {
return chartListener;
}
IChartDesignStrategy getChartStrategy() {
return chartStrategy;
}
Object getDataSource() {
return dataSource;
}
IDataSelectionListener getDataListener() {
return dataListener;
}
/**
*
* @see org.eclipse.birt.chart.device.IUpdateNotifier#peerInstance()
*/
public Object peerInstance() {
return this;
}
/**
*
* @see org.eclipse.birt.chart.device.ICallBackNotifier#callback(java.lang.Object,
* java.lang.Object,
* org.eclipse.birt.chart.model.attribute.CallBackValue)
*/
public void callback(Object arg0, Object arg1, CallBackValue arg2) {
//
}
/**
* Gets the chart.
*
* @return the chart
*/
public Chart getChart() {
return chart;
}
/**
* Sets the series color provider.
*
* @param seriesColorProvider
* the new series color provider
*/
public void setSeriesColorProvider(IColorProvider seriesColorProvider) {
this.seriesColorProvider = seriesColorProvider;
}
/**
*
* @see org.eclipse.birt.chart.device.IUpdateNotifier#repaintChart()
*/
public void repaintChart() {
refresh();
}
@Override
public void deactivate() {
super.deactivate();
dataProvider.removeListener(dataSource, dataListener);
}
/**
* Gets the data provider.
*
* @return the data provider
*/
public IDataProvider getDataProvider() {
return dataProvider;
}
/**
* Sets the data provider.
*
* @param dataProvider
* the new data provider
*/
public synchronized void setDataProvider(IDataProvider dataProvider) {
this.dataProvider = dataProvider;
dataSource = dataProvider.getDataSource(getModel());
dataListener = new IDataSelectionListener() {
public void selectionChanged(Object dataSet) {
if (getParent() != null && getRoot() != null && getViewer() != null && getChart() != null) {
updateChartSelection();
getViewer().getControl().getDisplay().asyncExec(new Runnable() {
public void run() {
regenerateChart(true);
}
});
}
}
};
dataProvider.addListener(dataSource, dataListener);
}
public void setChartStrategy(IChartDesignStrategy chartStrategy) {
this.chartStrategy = chartStrategy;
createChart();
updateChartSelection();
if (getViewer().getControl().getDisplay() != null) {
getViewer().getControl().getDisplay().asyncExec(new Runnable() {
public void run() {
repaintChart();
}
});
}
}
/**
* Gets the series color provider.
*
* @return the series color provider
*/
public IColorProvider getSeriesColorProvider() {
return seriesColorProvider;
}
public ResourceManager getResourceManager() {
return resourceManager;
}
/**
* Encapsulate disposing of {@link GC} and {@link Image} resources.
*
* @author fei
*/
public class ResourceManager implements DisposeListener {
private GC gc;
private Image image;
private Control disposeEventProvider;
public ResourceManager() {
//
}
public void setGc(GC gc) {
if (this.gc != null && !this.gc.isDisposed()) {
this.gc.dispose();
}
this.gc = gc;
addDisposeListener();
}
GC getGc() {
return gc;
}
private void removeDisposeListener() {
if (disposeEventProvider != null) {
if (!disposeEventProvider.isDisposed()) {
if (getViewer() != null && disposeEventProvider.equals(getViewer().getControl())) {
return;
}
disposeEventProvider.removeDisposeListener(this);
}
disposeEventProvider = null;
}
}
private void addDisposeListener() {
removeDisposeListener();
if (disposeEventProvider == null && getViewer() != null && getViewer().getControl() != null && !getViewer().getControl().isDisposed()) {
disposeEventProvider = getViewer().getControl();
disposeEventProvider.addDisposeListener(this);
}
}
Image getImage() {
return image;
}
public void setImage(Image image) {
if (this.image != null && !this.image.isDisposed()) {
this.image.dispose();
}
this.image = image;
addDisposeListener();
}
public void widgetDisposed(DisposeEvent arg0) {
setImage(null);
setGc(null);
removeDisposeListener();
}
}
}