/* * Copyright (c) 2004-2011 Marco Maccaferri 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: * Marco Maccaferri - initial API and implementation */ package org.eclipsetrader.ui.charts; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.ScrollBar; import org.eclipsetrader.core.charts.IDataSeries; import org.eclipsetrader.core.charts.IDataSeriesVisitor; import org.eclipsetrader.ui.internal.UIActivator; public class ChartViewer { private static final String K_NEEDS_REDRAW = "needs_redraw"; //$NON-NLS-1$ private Composite composite; private SashForm sashForm; private ChartItem[] items = new ChartItem[0]; private Canvas horizontalScaleCanvas; private Label verticalScaleLabel; private IAxis horizontalAxis; private IAxis verticalAxis; private IChartContentProvider contentProvider; private IChartRenderer renderer; private boolean verticalScaleVisible; private boolean horizontalScaleVisible; private Image horizontalScaleImage; private boolean needsRedraw; private Object input; public ChartViewer(Composite parent, int style) { composite = new Composite(parent, SWT.H_SCROLL | SWT.V_SCROLL); GridLayout gridLayout = new GridLayout(2, false); gridLayout.marginWidth = gridLayout.marginHeight = 0; gridLayout.horizontalSpacing = gridLayout.verticalSpacing = 3; composite.setLayout(gridLayout); sashForm = new SashForm(composite, SWT.VERTICAL); sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); horizontalScaleCanvas = new Canvas(composite, SWT.DOUBLE_BUFFERED); horizontalScaleCanvas.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); horizontalScaleCanvas.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); ((GridData) horizontalScaleCanvas.getLayoutData()).heightHint = ChartItem.HORIZONTAL_SCALE_HEIGHT; ((GridData) horizontalScaleCanvas.getLayoutData()).exclude = true; horizontalScaleCanvas.setVisible(false); horizontalScaleCanvas.addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { onPaintHorizontalScale(e); } }); horizontalScaleCanvas.addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { if (horizontalScaleImage != null) { horizontalScaleImage.dispose(); horizontalScaleImage = null; } } }); verticalScaleLabel = new Label(composite, SWT.NONE); verticalScaleLabel.setLayoutData(new GridData(ChartItem.VERTICAL_SCALE_WIDTH, SWT.DEFAULT)); ((GridData) verticalScaleLabel.getLayoutData()).exclude = true; verticalScaleLabel.setVisible(false); Label label = new Label(sashForm, SWT.NONE); label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); composite.getHorizontalBar().setVisible(false); composite.getHorizontalBar().addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { needsRedraw = true; for (int i = 0; i < items.length; i++) { items[i].getCanvas().setData(K_NEEDS_REDRAW, Boolean.TRUE); if (items[i].getVerticalScaleCanvas() != null) { items[i].getVerticalScaleCanvas().setData(K_NEEDS_REDRAW, Boolean.TRUE); } items[i].redraw(); } if (horizontalScaleCanvas != null) { horizontalScaleCanvas.setData(K_NEEDS_REDRAW, Boolean.TRUE); horizontalScaleCanvas.redraw(); } } }); composite.getVerticalBar().setVisible(false); composite.getVerticalBar().addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { sashForm.redraw(0, 0, 0, 0, true); } }); sashForm.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { if (contentProvider != null) { contentProvider.dispose(); } if (renderer != null) { renderer.dispose(); } } }); } public void dispose() { composite.dispose(); } public IAxis getHorizontalAxis() { return horizontalAxis; } public void setHorizontalAxis(IAxis horizontalAxis) { this.horizontalAxis = horizontalAxis; } public boolean isHorizontalScaleVisible() { return horizontalScaleVisible; } public void setHorizontalScaleVisible(boolean visible) { this.horizontalScaleVisible = visible; } public boolean isVerticalScaleVisible() { return verticalScaleVisible; } public void setVerticalScaleVisible(boolean verticalScaleVisible) { this.verticalScaleVisible = verticalScaleVisible; } public IAxis getVerticalAxis() { return verticalAxis; } public void setVerticalAxis(IAxis verticalAxis) { this.verticalAxis = verticalAxis; } public IChartContentProvider getContentProvider() { return contentProvider; } public void setContentProvider(IChartContentProvider contentProvider) { this.contentProvider = contentProvider; } public IChartRenderer getRenderer() { return renderer; } public void setRenderer(IChartRenderer renderer) { this.renderer = renderer; } public Object getInput() { return input; } public void setInput(Object input) { Assert.isTrue(getContentProvider() != null, "ChartTemplate must have a content provider when input is set."); //$NON-NLS-1$ Object oldInput = getInput(); contentProvider.inputChanged(this, oldInput, input); this.input = input; refresh(); } public void refresh() { horizontalAxis.clear(); verticalAxis.clear(); Object[] elements = getInput() != null ? contentProvider.getElements(getInput()) : new Object[0]; ChartItem[] newItems = new ChartItem[elements.length]; if (items != null) { int length = Math.min(items.length, newItems.length); System.arraycopy(items, 0, newItems, 0, length); for (int i = length; i < items.length; i++) { items[i].dispose(); } if (items.length == 0) { Control[] c = sashForm.getChildren(); if (c.length != 0) { c[0].dispose(); } } } items = newItems; for (int i = 0; i < elements.length; i++) { if (items[i] == null || items[i].isDisposed()) { items[i] = new ChartItem(sashForm, SWT.NONE); items[i].setVerticalScaleVisible(verticalScaleVisible); items[i].getCanvas().addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { onPaintItem(e); } }); items[i].getCanvas().addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { e.widget.setData(K_NEEDS_REDRAW, Boolean.TRUE); } }); items[i].getVerticalScaleCanvas().addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { onPaintItemVerticalScale(e); } }); items[i].getVerticalScaleCanvas().addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { e.widget.setData(K_NEEDS_REDRAW, Boolean.TRUE); } }); if (i == 0) { items[i].getCanvas().addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { updateScrollbars(); } }); } } items[i].setData(elements[i]); items[i].getCanvas().setData(K_NEEDS_REDRAW, Boolean.TRUE); if (items[i].getVerticalScaleCanvas() != null) { items[i].getVerticalScaleCanvas().setData(K_NEEDS_REDRAW, Boolean.TRUE); } items[i].redraw(); IDataSeries dataSeries = null; if (elements[i] instanceof IDataSeries) { dataSeries = (IDataSeries) elements[i]; } else if (elements[i] instanceof IAdaptable) { dataSeries = (IDataSeries) ((IAdaptable) elements[i]).getAdapter(IDataSeries.class); } if (dataSeries != null) { dataSeries.accept(new IDataSeriesVisitor() { @Override public boolean visit(IDataSeries data) { horizontalAxis.addValues(data.getValues()); return true; } }); verticalAxis.addValues(new IAdaptable[] { dataSeries.getHighest(), dataSeries.getLowest() }); } } if (items.length == 0) { Label label = new Label(sashForm, SWT.NONE); label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); } needsRedraw = true; if (horizontalScaleCanvas != null) { horizontalScaleCanvas.setData(K_NEEDS_REDRAW, Boolean.TRUE); horizontalScaleCanvas.redraw(); } updateScrollbars(); int[] weights = new int[items.length]; if (weights.length != 0) { weights[0] = 100; for (int i = 1; i < weights.length; i++) { weights[i] = 25; } sashForm.setWeights(weights); } sashForm.layout(); if (items.length != 0 && verticalScaleVisible && !verticalScaleLabel.getVisible()) { ((GridData) verticalScaleLabel.getLayoutData()).exclude = false; verticalScaleLabel.setVisible(true); } if ((items.length == 0 || !verticalScaleVisible) && verticalScaleLabel.getVisible()) { ((GridData) verticalScaleLabel.getLayoutData()).exclude = true; verticalScaleLabel.setVisible(false); } if (items.length != 0 && horizontalScaleVisible && !horizontalScaleCanvas.getVisible()) { ((GridData) horizontalScaleCanvas.getLayoutData()).exclude = false; horizontalScaleCanvas.setVisible(true); composite.layout(); } if ((items.length == 0 || !horizontalScaleVisible) && horizontalScaleCanvas.getVisible()) { ((GridData) horizontalScaleCanvas.getLayoutData()).exclude = true; horizontalScaleCanvas.setVisible(false); composite.layout(); } } public Display getDisplay() { return sashForm.getDisplay(); } public boolean isDisposed() { return sashForm.isDisposed(); } private void onPaintHorizontalScale(PaintEvent event) { if (!(renderer instanceof IScaleRenderer)) { return; } Rectangle clientArea = horizontalScaleCanvas.getClientArea(); ScrollBar hScroll = composite.getHorizontalBar(); Object firstElement = horizontalAxis.mapToValue(hScroll != null ? hScroll.getSelection() : 0); Object lastElement = horizontalAxis.mapToValue((hScroll != null ? hScroll.getSelection() : 0) + clientArea.width); if (getInput() != null && needsRedraw) { Object[] elements = contentProvider.getElements(getInput()); if (elements != null && elements.length != 0) { RenderTarget graphics = new RenderTarget(); try { Rectangle canvasArea = horizontalScaleCanvas.getClientArea(); if (horizontalScaleImage == null) { horizontalScaleImage = new Image(horizontalScaleCanvas.getDisplay(), canvasArea.width, canvasArea.height); } graphics.gc = new GC(horizontalScaleImage); graphics.display = horizontalScaleCanvas.getDisplay(); graphics.x = hScroll != null ? -hScroll.getSelection() : 0; graphics.y = 0; graphics.width = canvasArea.width; graphics.height = canvasArea.height; graphics.widget = horizontalScaleCanvas; graphics.horizontalAxis = horizontalAxis; graphics.verticalAxis = verticalAxis; graphics.firstValue = new AdaptableWrapper(firstElement); graphics.lastValue = new AdaptableWrapper(lastElement); graphics.input = input; graphics.gc.setAntialias(SWT.OFF); graphics.gc.setForeground(horizontalScaleCanvas.getForeground()); graphics.gc.setBackground(horizontalScaleCanvas.getBackground()); ((IScaleRenderer) renderer).renderHorizontalScale(graphics); } catch (Error e) { Status status = new Status(IStatus.ERROR, UIActivator.PLUGIN_ID, Messages.ChartViewer_HorizontalScaleRenderingError); UIActivator.log(status); } finally { if (graphics.gc != null) { graphics.gc.dispose(); } } } } if (horizontalScaleImage != null && !horizontalScaleImage.isDisposed()) { event.gc.drawImage(horizontalScaleImage, 0, 0); } else { event.gc.setForeground(horizontalScaleCanvas.getForeground()); event.gc.setBackground(horizontalScaleCanvas.getBackground()); event.gc.fillRectangle(event.x, event.y, event.width, event.height); } } protected void updateScrollbars() { if (items != null && items.length != 0) { Point chartSize = new Point(0, 0); Rectangle clientArea = items[0].getCanvas().getClientArea(); ScrollBar hScroll = composite.getHorizontalBar(); boolean wasVisible = hScroll.getVisible(); for (int i = 0; i < 2; i++) { if (horizontalAxis != null) { chartSize.x = horizontalAxis.computeSize(clientArea.width); if (hScroll != null) { if (chartSize.x > clientArea.width) { hScroll.setVisible(true); } else { hScroll.setVisible(false); hScroll.setValues(0, 0, 1, 1, 1, 1); } } } clientArea = items[0].getCanvas().getClientArea(); } if (hScroll.getVisible()) { int hiddenArea = chartSize.x - clientArea.width + 1; int currentSelection = hScroll.getSelection(); int rightAnchor = hScroll.getMaximum() - hScroll.getThumb(); int selection = Math.min(currentSelection, hiddenArea - 1); if (!wasVisible || currentSelection == rightAnchor) { selection = hiddenArea - 1; } hScroll.setValues(selection, 0, hiddenArea + clientArea.width - 1, clientArea.width, 5, clientArea.width); } } } private void onPaintItem(PaintEvent event) { ChartItem item = (ChartItem) event.widget.getData(); Canvas canvas = item.getCanvas(); Image image = item.getImage(); boolean needsRedraw = Boolean.TRUE.equals(canvas.getData(K_NEEDS_REDRAW)); Rectangle clientArea = canvas.getClientArea(); ScrollBar hScroll = composite.getHorizontalBar(); if (image != null && !image.isDisposed()) { if (image.getBounds().width != clientArea.width || image.getBounds().height != clientArea.height) { image.dispose(); } } if (image == null || image.isDisposed()) { image = new Image(canvas.getDisplay(), clientArea.width, clientArea.height); item.setImage(image); needsRedraw = true; } if (needsRedraw && image != null && !image.isDisposed()) { RenderTarget target = new RenderTarget(); try { Object firstElement = horizontalAxis.mapToValue(hScroll.getSelection()); Object lastElement = horizontalAxis.mapToValue(hScroll.getSelection() + clientArea.width); target.gc = new GC(image); target.display = event.display; target.x = -hScroll.getSelection(); target.y = 0; target.width = clientArea.width; target.height = clientArea.height; target.widget = event.widget; target.horizontalAxis = horizontalAxis; target.verticalAxis = verticalAxis; target.input = item.getData(); target.firstValue = firstElement instanceof IAdaptable ? firstElement : new AdaptableWrapper(firstElement); target.lastValue = lastElement instanceof IAdaptable ? lastElement : new AdaptableWrapper(lastElement); //target.gc.setAntialias(SWT.ON); target.gc.setForeground(canvas.getForeground()); target.gc.setBackground(canvas.getBackground()); IChartRenderer renderer = getRenderer(); renderer.renderBackground(target); Object[] elements = getContentProvider().getChildren(item.getData()); if (elements != null) { for (int i = 0; i < elements.length; i++) { target.gc.setForeground(canvas.getForeground()); target.gc.setBackground(canvas.getBackground()); target.gc.setLineWidth(1); renderer.renderElement(target, elements[i]); } } } catch (Error e) { Status status = new Status(IStatus.ERROR, UIActivator.PLUGIN_ID, Messages.ChartViewer_RenderingErrorMessage); UIActivator.log(status); } finally { if (target.gc != null) { target.gc.dispose(); } } canvas.setData(K_NEEDS_REDRAW, Boolean.FALSE); } if (image != null && !image.isDisposed()) { Rectangle bounds = image.getBounds(); int width = event.width; if (event.x + width > bounds.width) { width = bounds.width - event.x; } int height = event.height; if (event.y + height > bounds.height) { height = bounds.height - event.y; } if (width != 0 && height != 0) { event.gc.drawImage(image, event.x, event.y, width, height, event.x, event.y, width, height); } } } private void onPaintItemVerticalScale(PaintEvent event) { ChartItem item = (ChartItem) event.widget.getData(); Canvas canvas = item.getVerticalScaleCanvas(); Image image = item.getVerticalScaleImage(); boolean needsRedraw = Boolean.TRUE.equals(canvas.getData(K_NEEDS_REDRAW)); Rectangle clientArea = canvas.getClientArea(); ScrollBar hScroll = composite.getHorizontalBar(); if (image != null && !image.isDisposed()) { if (image.getBounds().width != clientArea.width || image.getBounds().height != clientArea.height) { image.dispose(); } } if (image == null || image.isDisposed()) { image = new Image(canvas.getDisplay(), clientArea.width, clientArea.height); item.setVerticalScaleImage(image); needsRedraw = true; } if (needsRedraw && image != null && !image.isDisposed()) { RenderTarget target = new RenderTarget(); try { Object firstElement = horizontalAxis.mapToValue(hScroll.getSelection()); Object lastElement = horizontalAxis.mapToValue(hScroll.getSelection() + item.getCanvas().getClientArea().width); target.gc = new GC(image); target.display = event.display; target.x = -hScroll.getSelection(); target.y = 0; target.width = clientArea.width; target.height = clientArea.height; target.widget = event.widget; target.horizontalAxis = horizontalAxis; target.verticalAxis = verticalAxis; target.input = item.getData(); target.firstValue = firstElement instanceof IAdaptable ? firstElement : new AdaptableWrapper(firstElement); target.lastValue = lastElement instanceof IAdaptable ? lastElement : new AdaptableWrapper(lastElement); target.gc.setAntialias(SWT.OFF); target.gc.setForeground(canvas.getForeground()); target.gc.setBackground(canvas.getBackground()); IScaleRenderer renderer = (IScaleRenderer) getRenderer(); renderer.renderVerticalScale(target); } catch (Error e) { Status status = new Status(IStatus.ERROR, UIActivator.PLUGIN_ID, Messages.ChartViewer_RenderingErrorMessage); UIActivator.log(status); } finally { if (target.gc != null) { target.gc.dispose(); } } canvas.setData(K_NEEDS_REDRAW, Boolean.FALSE); } if (image != null && !image.isDisposed()) { Rectangle bounds = image.getBounds(); int width = event.width; if (event.x + width > bounds.width) { width = bounds.width - event.x; } int height = event.height; if (event.y + height > bounds.height) { height = bounds.height - event.y; } if (width != 0 && height != 0) { event.gc.drawImage(image, event.x, event.y, width, height, event.x, event.y, width, height); } } } public Control getControl() { return composite; } }