00001
00002
00003
00004
00005
00006
00007 package org.swtchart.internal;
00008
00009 import java.util.ArrayList;
00010 import java.util.Collections;
00011 import java.util.HashMap;
00012 import java.util.List;
00013 import java.util.Map;
00014 import java.util.Map.Entry;
00015
00016 import org.eclipse.swt.SWT;
00017 import org.eclipse.swt.events.PaintEvent;
00018 import org.eclipse.swt.events.PaintListener;
00019 import org.eclipse.swt.graphics.Color;
00020 import org.eclipse.swt.graphics.Font;
00021 import org.eclipse.swt.graphics.GC;
00022 import org.eclipse.swt.graphics.Rectangle;
00023 import org.eclipse.swt.widgets.Composite;
00024 import org.eclipse.swt.widgets.Display;
00025 import org.swtchart.Chart;
00026 import org.swtchart.Constants;
00027 import org.swtchart.IBarSeries;
00028 import org.swtchart.ILegend;
00029 import org.swtchart.ILineSeries;
00030 import org.swtchart.ISeries;
00031 import org.swtchart.internal.series.LineSeries;
00032 import org.swtchart.internal.series.Series;
00033
00037 public class Legend extends Composite implements ILegend, PaintListener {
00038
00040 private Chart chart;
00041
00043 private boolean visible;
00044
00046 private int position;
00047
00049 private static final int MARGIN = 5;
00050
00052 private static final int SYMBOL_WIDTH = 20;
00053
00055 private static final int LINE_WIDTH = 2;
00056
00058 private static final Color DEFAULT_FOREGROUND = Display.getDefault()
00059 .getSystemColor(SWT.COLOR_BLACK);
00060
00062 private static final Color DEFAULT_BACKGROUND = Display.getDefault()
00063 .getSystemColor(SWT.COLOR_WHITE);
00064
00066 private Font defaultFont;
00067
00069 private static final int DEFAULT_FONT_SIZE = Constants.SMALL_FONT_SIZE;
00070
00072 private static final int DEFAULT_POSITION = SWT.RIGHT;
00073
00075 private Map<String, Rectangle> cellBounds;
00076
00085 public Legend(Chart chart, int style) {
00086 super(chart, style | SWT.DOUBLE_BUFFERED);
00087 this.chart = chart;
00088
00089 visible = true;
00090 position = DEFAULT_POSITION;
00091 cellBounds = new HashMap<String, Rectangle>();
00092 defaultFont = new Font(Display.getDefault(), "Tahoma",
00093 DEFAULT_FONT_SIZE, SWT.NORMAL);
00094 setFont(defaultFont);
00095 setForeground(DEFAULT_FOREGROUND);
00096 setBackground(DEFAULT_BACKGROUND);
00097 addPaintListener(this);
00098 }
00099
00100
00101
00102
00103 @Override
00104 public void setVisible(boolean visible) {
00105 if (this.visible == visible) {
00106 return;
00107 }
00108
00109 this.visible = visible;
00110 chart.updateLayout();
00111 }
00112
00113
00114
00115
00116 @Override
00117 public boolean isVisible() {
00118 return visible;
00119 }
00120
00121
00122
00123
00124 @Override
00125 public void setFont(Font font) {
00126 if (font == null) {
00127 super.setFont(defaultFont);
00128 } else {
00129 super.setFont(font);
00130 }
00131 chart.updateLayout();
00132 }
00133
00134
00135
00136
00137 @Override
00138 public void setForeground(Color color) {
00139 if (color == null) {
00140 super.setForeground(DEFAULT_FOREGROUND);
00141 } else {
00142 super.setForeground(color);
00143 }
00144 }
00145
00146
00147
00148
00149 @Override
00150 public void setBackground(Color color) {
00151 if (color == null) {
00152 super.setBackground(DEFAULT_BACKGROUND);
00153 } else {
00154 super.setBackground(color);
00155 }
00156 }
00157
00158
00159
00160
00161 public int getPosition() {
00162 return position;
00163 }
00164
00165
00166
00167
00168 public void setPosition(int value) {
00169 if (value == SWT.LEFT || value == SWT.RIGHT || value == SWT.TOP
00170 || value == SWT.BOTTOM) {
00171 position = value;
00172 } else {
00173 position = DEFAULT_POSITION;
00174 }
00175 chart.updateLayout();
00176 }
00177
00178
00179
00180
00181 public Rectangle getBounds(String seriesId) {
00182 return cellBounds.get(seriesId);
00183 }
00184
00185
00186
00187
00188 @Override
00189 public void dispose() {
00190 super.dispose();
00191 if (!defaultFont.isDisposed()) {
00192 defaultFont.dispose();
00193 }
00194 }
00195
00209 private ISeries[] sort(ISeries[] seriesArray) {
00210
00211
00212 Map<Integer, List<ISeries>> map = new HashMap<Integer, List<ISeries>>();
00213 for (ISeries series : seriesArray) {
00214 int axisId = series.getXAxisId();
00215 List<ISeries> list = map.get(axisId);
00216 if (list == null) {
00217 list = new ArrayList<ISeries>();
00218 }
00219 list.add(series);
00220 map.put(axisId, list);
00221 }
00222
00223
00224 List<ISeries> sortedArray = new ArrayList<ISeries>();
00225 boolean isVertical = chart.getOrientation() == SWT.VERTICAL;
00226 for (Entry<Integer, List<ISeries>> entry : map.entrySet()) {
00227 boolean isCategoryEnabled = chart.getAxisSet()
00228 .getXAxis(entry.getKey()).isCategoryEnabled();
00229 sortedArray.addAll(sort(entry.getValue(), isCategoryEnabled,
00230 isVertical));
00231 }
00232
00233 return sortedArray.toArray(new ISeries[sortedArray.size()]);
00234 }
00235
00253 private static List<ISeries> sort(List<ISeries> seriesList,
00254 boolean isCategoryEnabled, boolean isVertical) {
00255 List<ISeries> sortedArray = new ArrayList<ISeries>();
00256
00257
00258 int insertIndex = -1;
00259 for (int i = 0; i < seriesList.size(); i++) {
00260 if (isCategoryEnabled
00261 && ((Series) seriesList.get(i)).isValidStackSeries()) {
00262 if (insertIndex == -1) {
00263 insertIndex = i;
00264 } else {
00265 sortedArray.add(insertIndex, seriesList.get(i));
00266 continue;
00267 }
00268 }
00269 sortedArray.add(seriesList.get(i));
00270 }
00271
00272
00273 if (isVertical) {
00274 Collections.reverse(sortedArray);
00275 }
00276
00277 return sortedArray;
00278 }
00279
00283 public void updateLayoutData() {
00284 if (!visible) {
00285 setLayoutData(new ChartLayoutData(0, 0));
00286 return;
00287 }
00288
00289 int width = 0;
00290 int height = 0;
00291
00292 ISeries[] seriesArray = sort(chart.getSeriesSet().getSeries());
00293
00294 Rectangle r = chart.getClientArea();
00295 Rectangle titleBounds = ((Title) chart.getTitle()).getBounds();
00296 int titleHeight = titleBounds.y + titleBounds.height;
00297 int cellHeight = Util.getExtentInGC(getFont(), "dummy").y;
00298
00299 if (position == SWT.RIGHT || position == SWT.LEFT) {
00300 int columns = 1;
00301 int yPosition = MARGIN;
00302 int maxCellWidth = 0;
00303
00304 for (ISeries series : seriesArray) {
00305 if (!series.isVisibleInLegend()) {
00306 continue;
00307 }
00308
00309 String label = getLegendLabel(series);
00310 int textWidth = Util.getExtentInGC(getFont(), label).x;
00311 int cellWidth = textWidth + SYMBOL_WIDTH + MARGIN * 3;
00312 maxCellWidth = Math.max(maxCellWidth, cellWidth);
00313 if (yPosition + cellHeight < r.height - titleHeight - MARGIN
00314 || yPosition == MARGIN) {
00315 yPosition += cellHeight + MARGIN;
00316 } else {
00317 columns++;
00318 yPosition = cellHeight + MARGIN * 2;
00319 }
00320 cellBounds.put(series.getId(), new Rectangle(maxCellWidth
00321 * (columns - 1), yPosition - cellHeight - MARGIN,
00322 cellWidth, cellHeight));
00323 height = Math.max(yPosition, height);
00324 }
00325 width = maxCellWidth * columns;
00326 } else if (position == SWT.TOP || position == SWT.BOTTOM) {
00327 int rows = 1;
00328 int xPosition = 0;
00329
00330 for (ISeries series : seriesArray) {
00331 if (!series.isVisibleInLegend()) {
00332 continue;
00333 }
00334
00335 String label = getLegendLabel(series);
00336 int textWidth = Util.getExtentInGC(getFont(), label).x;
00337 int cellWidth = textWidth + SYMBOL_WIDTH + MARGIN * 3;
00338 if (xPosition + cellWidth < r.width || xPosition == 0) {
00339 xPosition += cellWidth;
00340 } else {
00341 rows++;
00342 xPosition = cellWidth;
00343 }
00344 cellBounds.put(series.getId(), new Rectangle(xPosition
00345 - cellWidth, (cellHeight + MARGIN) * (rows - 1)
00346 + MARGIN, cellWidth, cellHeight));
00347 width = Math.max(xPosition, width);
00348 }
00349 height = (cellHeight + MARGIN) * rows + MARGIN;
00350 }
00351
00352 setLayoutData(new ChartLayoutData(width, height));
00353 }
00354
00362 private static String getLegendLabel(ISeries series) {
00363 String description = series.getDescription();
00364
00365 if (description == null) {
00366 return series.getId();
00367 }
00368 return description;
00369 }
00370
00381 protected void drawSymbol(GC gc, Series series, Rectangle r) {
00382
00383 if (!visible) {
00384 return;
00385 }
00386
00387 if (series instanceof ILineSeries) {
00388
00389 gc.setForeground(((ILineSeries) series).getLineColor());
00390 gc.setLineWidth(LINE_WIDTH);
00391 int lineStyle = Util.getIndexDefinedInSWT(((ILineSeries) series)
00392 .getLineStyle());
00393 int x = r.x;
00394 int y = r.y + r.height / 2;
00395 if (lineStyle != SWT.NONE) {
00396 gc.setLineStyle(lineStyle);
00397 gc.drawLine(x, y, x + SYMBOL_WIDTH, y);
00398 }
00399
00400
00401 Color color = ((ILineSeries) series).getSymbolColor();
00402 Color[] colors = ((ILineSeries) series).getSymbolColors();
00403 if (colors != null && colors.length > 0) {
00404 color = colors[0];
00405 }
00406 ((LineSeries) series).drawSeriesSymbol(gc, x + SYMBOL_WIDTH / 2, y,
00407 color);
00408 } else if (series instanceof IBarSeries) {
00409
00410 gc.setBackground(((IBarSeries) series).getBarColor());
00411 int size = SYMBOL_WIDTH / 2;
00412 int x = r.x + size / 2;
00413 int y = (int) (r.y - size / 2d + r.height / 2d);
00414 gc.fillRectangle(x, y, size, size);
00415 }
00416 }
00417
00418
00419
00420
00421 public void paintControl(PaintEvent e) {
00422 if (!visible) {
00423 return;
00424 }
00425
00426 GC gc = e.gc;
00427 gc.setFont(getFont());
00428 ISeries[] seriesArray = chart.getSeriesSet().getSeries();
00429 if (seriesArray.length == 0) {
00430 return;
00431 }
00432
00433
00434 gc.fillRectangle(0, 0, getSize().x - 1, getSize().y - 1);
00435 gc.setLineStyle(SWT.LINE_SOLID);
00436 gc.setLineWidth(1);
00437 gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
00438 gc.drawRectangle(0, 0, getSize().x - 1, getSize().y - 1);
00439
00440
00441 for (int i = 0; i < seriesArray.length; i++) {
00442 if (!seriesArray[i].isVisibleInLegend()) {
00443 continue;
00444 }
00445
00446
00447 String id = seriesArray[i].getId();
00448 Rectangle r = cellBounds.get(id);
00449 drawSymbol(gc, (Series) seriesArray[i], new Rectangle(r.x + MARGIN,
00450 r.y + MARGIN, SYMBOL_WIDTH, r.height - MARGIN * 2));
00451
00452
00453 String label = getLegendLabel(seriesArray[i]);
00454 gc.setBackground(getBackground());
00455 gc.setForeground(getForeground());
00456 gc.drawText(label, r.x + SYMBOL_WIDTH + MARGIN * 2, r.y, true);
00457 }
00458 }
00459 }