All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.swtchart.support.CircularLegend Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2020 SWTChart project.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 * Himanshu Balasamanta: Orignal API and implementation
 *******************************************************************************/
package org.eclipse.swtchart.support;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swtchart.Chart;
import org.eclipse.swtchart.Constants;
import org.eclipse.swtchart.ICircularSeries;
import org.eclipse.swtchart.ILegend;
import org.eclipse.swtchart.ISeries;
import org.eclipse.swtchart.internal.ChartLayoutData;
import org.eclipse.swtchart.internal.Legend;
import org.eclipse.swtchart.internal.Title;
import org.eclipse.swtchart.internal.Util;
import org.eclipse.swtchart.internal.series.Series;

/**
 * A legend for chart.
 */
public class CircularLegend extends Canvas implements ILegend, PaintListener {

	/** the plot chart */
	private Composite composite;
	private ISeries[] series;
	private Title title;
	/** the state indicating the legend visibility */
	private boolean visible;
	/** the position of legend */
	private int position;
	/** the margin */
	private static final int MARGIN = 5;
	/** the width of area to draw symbol */
	private static final int SYMBOL_WIDTH = 20;
	/** the line width */
	// private static final int LINE_WIDTH = 2; // it's not used yet.
	/** the default foreground */
	private static final Color DEFAULT_FOREGROUND = Display.getDefault().getSystemColor(SWT.COLOR_BLACK);
	/** the default background */
	private static final Color DEFAULT_BACKGROUND = Display.getDefault().getSystemColor(SWT.COLOR_WHITE);
	/** the default font */
	private Font defaultFont;
	/** the default font size */
	private static final int DEFAULT_FONT_SIZE = Constants.SMALL_FONT_SIZE;
	/** the default position */
	private static final int DEFAULT_POSITION = SWT.RIGHT;
	/** the map between series id and cell bounds */
	private Map cellBounds;

	/**
	 * Constructor.
	 *
	 * @param composite
	 *            the chart
	 * @param style
	 *            the style
	 */
	public CircularLegend(Composite composite, int style) {

		super(composite, style | SWT.DOUBLE_BUFFERED);
		this.composite = composite;
		visible = true;
		position = DEFAULT_POSITION;
		cellBounds = new HashMap();
		defaultFont = new Font(Display.getDefault(), "Tahoma", DEFAULT_FONT_SIZE, SWT.NORMAL); //$NON-NLS-1$
		setFont(defaultFont);
		setForeground(DEFAULT_FOREGROUND);
		setBackground(DEFAULT_BACKGROUND);
		addPaintListener(this);
	}

	@Override
	public void setVisible(boolean visible) {

		if(this.visible == visible) {
			return;
		}
		this.visible = visible;
	}

	@Override
	public boolean isVisible() {

		return visible;
	}

   /**
    * @see org.eclipse.swtchart.ILegend#isExtended()
    */
   @Override
   public boolean isExtended()
   {
      return false;
   }

   /**
    * @see org.eclipse.swtchart.ILegend#setExtended(boolean)
    */
   @Override
   public void setExtended(boolean extended)
   {
      // do nothing - extended mode not supported for this legend type
   }

	@Override
	public void setFont(Font font) {

		if(font == null) {
			super.setFont(defaultFont);
		} else {
			super.setFont(font);
		}
	}

	@Override
	public void setForeground(Color color) {

		if(color == null) {
			super.setForeground(DEFAULT_FOREGROUND);
		} else {
			super.setForeground(color);
		}
	}

	@Override
	public void setBackground(Color color) {

		if(color == null) {
			super.setBackground(DEFAULT_BACKGROUND);
		} else {
			super.setBackground(color);
		}
	}

	@Override
	public int getPosition() {

		return position;
	}

	@Override
	public void setPosition(int value) {

		if(value == SWT.LEFT || value == SWT.RIGHT || value == SWT.TOP || value == SWT.BOTTOM) {
			position = value;
		} else {
			position = DEFAULT_POSITION;
		}
	}

	@Override
	public Rectangle getBounds(String seriesId) {

		if(seriesId == null) {
			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
		}
		return cellBounds.get(seriesId.trim());
	}

	@Override
	public void dispose() {

		super.dispose();
		if(!defaultFont.isDisposed()) {
			defaultFont.dispose();
		}
	}

	/**
	 * Sorts the given series array. For instance, if there are two stack series
	 * in horizontal orientation, the top of stack series should appear at top
	 * of legend.
	 * 

* If there are multiple x axes, the given series array will be sorted with * x axis first. And then, the series in each x axis will be sorted with * {@link Legend#sort(List, boolean, boolean)}. * * @param seriesArray * the series array * @return the sorted series array */ private ISeries[] sort(ISeries[] seriesArray) { // create a map between axis id and series list Map>> map = new HashMap>>(); for(ISeries series : seriesArray) { int axisId = series.getXAxisId(); List> list = map.get(axisId); if(list == null) { list = new ArrayList>(); } list.add(series); map.put(axisId, list); } // sort an each series list List> sortedArray = new ArrayList>(); for(Entry>> entry : map.entrySet()) { sortedArray.addAll(sort(entry.getValue(), false, true)); } return sortedArray.toArray(new ISeries[sortedArray.size()]); } /** * Sorts the given series list which belongs to a certain x axis. *

    *
  • The stacked series will be gathered, and the order of stack series * will be reversed.
  • *
  • In the case of vertical orientation, the order of whole series will * be reversed.
  • *
* * @param seriesList * the series list which belongs to a certain x axis * @param isCategoryEnabled * true if category is enabled * @param isVertical * true in the case of vertical orientation * @return the sorted series array */ private static List> sort(List> seriesList, boolean isCategoryEnabled, boolean isVertical) { List> sortedArray = new ArrayList>(); // gather the stacked series reversing the order of stack series int insertIndex = -1; for(int i = 0; i < seriesList.size(); i++) { if(isCategoryEnabled && ((Series)seriesList.get(i)).isValidStackSeries()) { if(insertIndex == -1) { insertIndex = i; } else { sortedArray.add(insertIndex, seriesList.get(i)); continue; } } sortedArray.add(seriesList.get(i)); } // reverse the order of whole series in the case of vertical orientation if(isVertical) { Collections.reverse(sortedArray); } return sortedArray; } /** * Update the layout data. */ public void updateLayoutData() { if(!visible) { setLayoutData(new ChartLayoutData(0, 0)); return; } int width = 0; int height = 0; ISeries[] seriesArray = sort(series); Rectangle r = composite.getClientArea(); Rectangle titleBounds = (title).getBounds(); int titleHeight = titleBounds.y + titleBounds.height; int cellHeight = Util.getExtentInGC(getFont(), null).y; if(position == SWT.RIGHT || position == SWT.LEFT) { int columns = 1; int yPosition = MARGIN; int maxCellWidth = 0; for(ISeries series : seriesArray) { if(!series.isVisibleInLegend()) { continue; } if(series instanceof ICircularSeries) { if(((ICircularSeries)series).getLabels() != null) { String[] labels = ((ICircularSeries)series).getLabels(); for(int i = 0; i != labels.length; i++) { int textWidth = Util.getExtentInGC(getFont(), labels[i]).x; int cellWidth = textWidth + SYMBOL_WIDTH + MARGIN * 3; maxCellWidth = Math.max(maxCellWidth, cellWidth); if(yPosition + cellHeight < r.height - titleHeight - MARGIN || yPosition == MARGIN) { yPosition += cellHeight + MARGIN; } else { columns++; yPosition = cellHeight + MARGIN * 2; } cellBounds.put(labels[i], new Rectangle(maxCellWidth * (columns - 1), yPosition - cellHeight - MARGIN, cellWidth, cellHeight)); height = Math.max(yPosition, height); } width = maxCellWidth * columns; continue; } } } width = maxCellWidth * columns; } else if(position == SWT.TOP || position == SWT.BOTTOM) { int rows = 1; int xPosition = 0; for(ISeries series : seriesArray) { if(!series.isVisibleInLegend()) { continue; } if(series instanceof ICircularSeries) { if(((ICircularSeries)series).getLabels() != null) { String[] labels = ((ICircularSeries)series).getLabels(); for(int i = 0; i != labels.length; i++) { int textWidth = Util.getExtentInGC(getFont(), labels[i]).x; int cellWidth = textWidth + SYMBOL_WIDTH + MARGIN * 3; if(xPosition + cellWidth < r.width || xPosition == 0) { xPosition += cellWidth; } else { rows++; xPosition = cellWidth; } cellBounds.put(labels[i], new Rectangle(xPosition - cellWidth, (cellHeight + MARGIN) * (rows - 1) + MARGIN, cellWidth, cellHeight)); width = Math.max(xPosition, width); } height = (cellHeight + MARGIN) * rows + MARGIN; continue; } } } height = (cellHeight + MARGIN) * rows + MARGIN; } setLayoutData(new ChartLayoutData(width, height)); } /** * Gets the legend label. * * @param series * the series * @return the legend label */ private static String getLegendLabel(ISeries series) { String description = series.getDescription(); if(description == null) { return series.getId(); } return description; } /** * Draws the symbol of series. * * @param gc * the graphics context * @param series * the series * @param r * the rectangle to draw the symbol of series */ protected void drawSymbol(GC gc, Series series, Rectangle r) { return; } @Override public void paintControl(PaintEvent e) { if(!visible) { return; } GC gc = e.gc; gc.setFont(getFont()); ISeries[] seriesArray = series; if(seriesArray.length == 0) { return; } // draw frame gc.fillRectangle(0, 0, getSize().x - 1, getSize().y - 1); gc.setLineStyle(SWT.LINE_SOLID); gc.setLineWidth(1); gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY)); gc.drawRectangle(0, 0, getSize().x - 1, getSize().y - 1); // draw content for(int i = 0; i < seriesArray.length; i++) { if(!seriesArray[i].isVisibleInLegend()) { continue; } // if(seriesArray[i] instanceof ICircularSeries) { ICircularSeries pieSeries = (ICircularSeries)seriesArray[i]; String[] labels = pieSeries.getLabels(); Color[] color = pieSeries.getColors(); for(int j = 0; j != labels.length; j++) { Rectangle r = cellBounds.get(labels[j]); if(r != null) { String labelPie = labels[j]; Color colorPie = color[j]; if(labelPie != null && colorPie != null) { drawPieSymbol(gc, labelPie, colorPie, new Rectangle(r.x + MARGIN, r.y + MARGIN, SYMBOL_WIDTH, r.height - MARGIN * 2)); gc.setBackground(getBackground()); gc.setForeground(getForeground()); gc.drawText(labelPie, r.x + SYMBOL_WIDTH + MARGIN * 2, r.y, true); } } } } else { // draw plot line, symbol etc String id = seriesArray[i].getId(); Rectangle r = cellBounds.get(id); drawSymbol(gc, (Series)seriesArray[i], new Rectangle(r.x + MARGIN, r.y + MARGIN, SYMBOL_WIDTH, r.height - MARGIN * 2)); // draw label String label = getLegendLabel(seriesArray[i]); gc.setBackground(getBackground()); gc.setForeground(getForeground()); gc.drawText(label, r.x + SYMBOL_WIDTH + MARGIN * 2, r.y, true); } } } private void drawPieSymbol(GC gc, String string, Color color, Rectangle r) { gc.setBackground(color); int size = SYMBOL_WIDTH / 2; int x = r.x + size / 2; int y = (int)(r.y - size / 2d + r.height / 2d); gc.fillArc(x, y, size, size, 0, 360); } public void setChart(Chart chart) { this.series = chart.getSeriesSet().getSeries(); this.title = (Title)chart.getTitle(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy