/******************************************************************************* * Copyright (c) 2008-2011 SWTChart project. All rights reserved. * * This code is distributed under the terms of the Eclipse Public License v1.0 * which is available at http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.swtchart.internal; import java.util.ArrayList; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Layout; import org.swtchart.Chart; import org.swtchart.IAxis; import org.swtchart.IAxisSet; import org.swtchart.IAxis.Position; import org.swtchart.internal.axis.Axis; import org.swtchart.internal.axis.AxisTickLabels; import org.swtchart.internal.axis.AxisTickMarks; import org.swtchart.internal.axis.AxisTitle; /** * Manages the layout on plot chart panel. */ public class ChartLayout extends Layout { /** the title height */ private int titleHeight; /** the title width */ private int titleWidth; /** the legend width */ private int legendWidth; /** the legend height */ private int legendHeight; /** the bottom axis height */ private int bottomAxisHeight; /** the top axis height */ private int topAxisHeight; /** the left axis width */ private int leftAxisWidth; /** the right axis width */ private int rightAxisWidth; /** the chart title */ private ChartTitle title; /** the legend */ private Legend legend; /** the plot area */ private PlotArea plot; /** the axes */ private List<Axis> axes; /** the offset for bottom axis */ private int bottomAxisOffset = 0; /** the offset for top axis */ private int topAxisOffset = 0; /** the offset for left axis */ private int leftAxisOffset = 0; /** the offset for right axis */ private int rightAxisOffset = 0; /** the margin */ public static final int MARGIN = 5; /** the padding */ public static final int PADDING = 5; /** * Axis layout data. */ private static class AxisLayoutData { /** the axis tick marks */ public AxisTickMarks axisTickMarks; /** the axis tick labels */ public AxisTickLabels axisTickLabels; /** the axis title */ public AxisTitle axisTitle; /** the axis title layout data */ public ChartLayoutData titleLayoutdata; /** the tick label layout data */ public ChartLayoutData tickLabelsLayoutdata; /** the tick marks layout data */ public ChartLayoutData tickMarksLayoutdata; /** * Constructor. * * @param axis * the axis */ public AxisLayoutData(Axis axis) { axisTickMarks = axis.getTick().getAxisTickMarks(); axisTickLabels = axis.getTick().getAxisTickLabels(); axisTitle = (AxisTitle) axis.getTitle(); titleLayoutdata = (ChartLayoutData) axisTitle.getLayoutData(); tickLabelsLayoutdata = axisTickLabels.getLayoutData(); tickMarksLayoutdata = axisTickMarks.getLayoutData(); } } /** * Constructor. */ public ChartLayout() { initWidgetSizeVariables(); axes = new ArrayList<Axis>(); } /** * Initializes the size variables of widgets. */ private void initWidgetSizeVariables() { titleHeight = 0; titleWidth = 0; bottomAxisHeight = 0; topAxisHeight = 0; leftAxisWidth = 0; rightAxisWidth = 0; legendWidth = 0; legendHeight = 0; } /* * @see Layout#computeSize(Composite , int, int, boolean) */ @Override protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { return new Point(wHint, hHint); } /* * @see Layout#layout(Composite, boolean) */ @Override protected void layout(Composite composite, boolean flushCache) { if (!parseControls(composite)) { return; } initWidgetSizeVariables(); computeControlSize(); Rectangle r = composite.getClientArea(); layoutTitle(r); layoutLegend(r); layoutPlot(r); layoutAxes(r); } /** * Parses the controls on given composite. * * @param composite * the composite * @return true if all children found */ private boolean parseControls(Composite composite) { Control[] children = composite.getChildren(); axes.clear(); for (Control child : children) { if (child instanceof Legend) { legend = (Legend) child; } else if (child instanceof ChartTitle) { title = (ChartTitle) child; } else if (child instanceof PlotArea) { plot = (PlotArea) child; } } if (composite instanceof Chart) { IAxisSet axisSet = ((Chart) composite).getAxisSet(); if (axisSet != null) { IAxis[] axisArray = axisSet.getAxes(); for (IAxis axis : axisArray) { axes.add((Axis) axis); } } } if (title == null || legend == null || plot == null || axes.size() < 2) { // the initialization of chart is not completed yet return false; } return true; } /** * Computes the size of child controls. */ private void computeControlSize() { titleWidth = ((ChartLayoutData) title.getLayoutData()).widthHint; titleHeight = ((ChartLayoutData) title.getLayoutData()).heightHint; legendWidth = ((ChartLayoutData) legend.getLayoutData()).widthHint; legendHeight = ((ChartLayoutData) legend.getLayoutData()).heightHint; for (Axis axis : axes) { AxisLayoutData layoutData = new AxisLayoutData(axis); if (layoutData.titleLayoutdata == null || layoutData.tickLabelsLayoutdata == null || layoutData.tickMarksLayoutdata == null) { continue; } Position position = axis.getPosition(); if (position == Position.Primary && axis.isHorizontalAxis()) { bottomAxisHeight += layoutData.titleLayoutdata.heightHint + layoutData.tickLabelsLayoutdata.heightHint + layoutData.tickMarksLayoutdata.heightHint; } else if (position == Position.Secondary && axis.isHorizontalAxis()) { topAxisHeight += layoutData.titleLayoutdata.heightHint + layoutData.tickLabelsLayoutdata.heightHint + layoutData.tickMarksLayoutdata.heightHint; } else if (position == Position.Primary && !axis.isHorizontalAxis()) { leftAxisWidth += layoutData.titleLayoutdata.widthHint + layoutData.tickLabelsLayoutdata.widthHint + layoutData.tickMarksLayoutdata.widthHint; } else if (position == Position.Secondary && !axis.isHorizontalAxis()) { rightAxisWidth += layoutData.titleLayoutdata.widthHint + layoutData.tickLabelsLayoutdata.widthHint + layoutData.tickMarksLayoutdata.widthHint; } } } /** * Layouts the title. * * @param r * the rectangle to layout */ private void layoutTitle(Rectangle r) { int x = (int) ((r.width - titleWidth) / 2d); int y = MARGIN; int width = titleWidth; int height = titleHeight; title.setBounds(x, y, width, height); } /** * Layouts the legend. * * @param r * the rectangle to layout */ private void layoutLegend(Rectangle r) { int legendPosition = legend.getPosition(); int tHeight = titleHeight + ((titleHeight == 0) ? 0 : PADDING); int x; int y; if (legendPosition == SWT.RIGHT) { x = r.width - legendWidth - MARGIN; y = (tHeight + r.height - legendHeight) / 2; } else if (legendPosition == SWT.LEFT) { x = MARGIN; y = (tHeight + r.height - legendHeight) / 2; } else if (legendPosition == SWT.TOP) { x = (r.width - legendWidth) / 2; y = tHeight + MARGIN; } else if (legendPosition == SWT.BOTTOM) { x = (r.width - legendWidth) / 2; y = r.height - legendHeight - MARGIN; } else { throw new IllegalStateException(); } int width = legendWidth; int height = legendHeight; if (y < tHeight) { y = tHeight; } legend.setBounds(x, y, width, height); } /** * Layouts the plot. * * @param r * the rectangle to layout */ private void layoutPlot(Rectangle r) { int legendPosition = legend.getPosition(); int x = leftAxisWidth + MARGIN + (legendPosition == SWT.LEFT ? legendWidth + (legendWidth == 0 ? 0 : PADDING) : 0); int y = titleHeight + topAxisHeight + MARGIN + (titleHeight == 0 ? 0 : PADDING) + (legendPosition == SWT.TOP ? legendHeight + (legendHeight == 0 ? 0 : PADDING) : 0); int width = r.width - leftAxisWidth - rightAxisWidth - (legendPosition == SWT.LEFT || legendPosition == SWT.RIGHT ? legendWidth + (legendWidth == 0 ? 0 : PADDING) : 0) - MARGIN * 2; int height = r.height - bottomAxisHeight - topAxisHeight - titleHeight - MARGIN * 2 - (titleHeight == 0 ? 0 : PADDING) - (legendPosition == SWT.TOP || legendPosition == SWT.BOTTOM ? legendHeight + (legendHeight == 0 ? 0 : PADDING) : 0); plot.setBounds(x, y, width, height); } /** * Layouts the axes. * * @param r * the rectangle to layout */ private void layoutAxes(Rectangle r) { bottomAxisOffset = 0; topAxisOffset = 0; leftAxisOffset = 0; rightAxisOffset = 0; for (Axis axis : axes) { AxisLayoutData layoutData = new AxisLayoutData(axis); if (layoutData.titleLayoutdata == null || layoutData.tickLabelsLayoutdata == null || layoutData.tickMarksLayoutdata == null) { continue; } Position position = axis.getPosition(); if (position == Position.Primary && axis.isHorizontalAxis()) { layoutBottomAxis(r, layoutData); } else if (position == Position.Secondary && axis.isHorizontalAxis()) { layoutTopAxis(r, layoutData); } else if (position == Position.Primary && !axis.isHorizontalAxis()) { layoutLeftAxis(r, layoutData); } else if (position == Position.Secondary && !axis.isHorizontalAxis()) { layoutRightAxis(r, layoutData); } } } /** * Layouts the bottom axis. * * @param r * the rectangle * @param layoutData * the layout data */ private void layoutBottomAxis(Rectangle r, AxisLayoutData layoutData) { int legendPosition = legend.getPosition(); int width = r.width - leftAxisWidth - rightAxisWidth - (legendPosition == SWT.LEFT || legendPosition == SWT.RIGHT ? legendWidth + (legendWidth == 0 ? 0 : PADDING) : 0) - MARGIN * 2; int height = layoutData.titleLayoutdata.heightHint; int x = leftAxisWidth + MARGIN + (legendPosition == SWT.LEFT ? legendWidth + (legendWidth == 0 ? 0 : PADDING) : 0); int y = r.height - height - bottomAxisOffset - MARGIN - (legendPosition == SWT.BOTTOM ? legendHeight + (legendHeight == 0 ? 0 : PADDING) : 0); bottomAxisOffset += height; if (y - layoutData.tickLabelsLayoutdata.heightHint - layoutData.tickMarksLayoutdata.heightHint < titleHeight + (titleHeight == 0 ? 0 : PADDING)) { y = titleHeight + (titleHeight == 0 ? 0 : PADDING) + layoutData.tickLabelsLayoutdata.heightHint + layoutData.tickMarksLayoutdata.heightHint; } width = width > 0 ? width : 0; layoutData.axisTitle.setBounds(x, y, width, height); height = layoutData.tickLabelsLayoutdata.heightHint; y -= height; bottomAxisOffset += height; layoutData.axisTickLabels.setBounds(0, y, r.width, height); height = layoutData.tickMarksLayoutdata.heightHint; y -= height; bottomAxisOffset += height; layoutData.axisTickMarks.setBounds(x, y, width, height); } /** * Layouts the top axis. * * @param r * the rectangle * @param layoutData * the layout data */ private void layoutTopAxis(Rectangle r, AxisLayoutData layoutData) { int legendPosition = legend.getPosition(); int width = r.width - leftAxisWidth - rightAxisWidth - (legendPosition == SWT.LEFT || legendPosition == SWT.RIGHT ? legendWidth + (legendWidth == 0 ? 0 : PADDING) : 0) - MARGIN * 2; int height = layoutData.titleLayoutdata.heightHint; int x = leftAxisWidth + MARGIN + (legendPosition == SWT.LEFT ? legendWidth + (legendWidth == 0 ? 0 : PADDING) : 0); int y = titleHeight + topAxisOffset + MARGIN + ((titleHeight == 0) ? 0 : PADDING); topAxisOffset += height; width = width > 0 ? width : 0; layoutData.axisTitle.setBounds(x, y, width, height); y += height; height = layoutData.tickLabelsLayoutdata.heightHint; topAxisOffset += height; layoutData.axisTickLabels.setBounds(0, y, r.width, height); y += height; height = layoutData.tickMarksLayoutdata.heightHint; topAxisOffset += height; layoutData.axisTickMarks.setBounds(x, y, width, height); } /** * Layouts the left axis. * * @param r * the rectangle * @param layoutData * the layout data */ private void layoutLeftAxis(Rectangle r, AxisLayoutData layoutData) { int legendPosition = legend.getPosition(); int yAxisMargin = Axis.MARGIN + AxisTickMarks.TICK_LENGTH; int width = layoutData.titleLayoutdata.widthHint; int height = r.height - bottomAxisHeight - topAxisHeight - titleHeight - MARGIN * 2 - ((titleHeight == 0) ? 0 : PADDING) - (legendPosition == SWT.TOP || legendPosition == SWT.BOTTOM ? legendHeight + (legendHeight == 0 ? 0 : PADDING) : 0); int x = MARGIN + leftAxisOffset + (legendPosition == SWT.LEFT ? legendWidth + (legendWidth == 0 ? 0 : PADDING) : 0); int y = titleHeight + topAxisHeight + MARGIN + ((titleHeight == 0) ? 0 : PADDING) + (legendPosition == SWT.TOP ? legendHeight + (legendHeight == 0 ? 0 : PADDING) : 0); leftAxisOffset += width; height = height > 0 ? height : 0; layoutData.axisTitle.setBounds(x, y, width, height); x += width; width = layoutData.tickLabelsLayoutdata.widthHint; leftAxisOffset += width; layoutData.axisTickLabels.setBounds(x, y - yAxisMargin, width, height + yAxisMargin * 2); x += width; width = layoutData.tickMarksLayoutdata.widthHint; leftAxisOffset += width; layoutData.axisTickMarks.setBounds(x, y, width, height); } /** * Layouts the right axis. * * @param r * the rectangle * @param layoutData * the layout data */ private void layoutRightAxis(Rectangle r, AxisLayoutData layoutData) { int legendPosition = legend.getPosition(); int yAxisMargin = Axis.MARGIN + AxisTickMarks.TICK_LENGTH; int width = layoutData.titleLayoutdata.widthHint; int height = r.height - bottomAxisHeight - topAxisHeight - titleHeight - MARGIN * 2 - ((titleHeight == 0) ? 0 : PADDING) - (legendPosition == SWT.TOP || legendPosition == SWT.BOTTOM ? legendHeight + (legendHeight == 0 ? 0 : PADDING) : 0); int x = r.width - width - rightAxisOffset - MARGIN - (legendPosition == SWT.RIGHT ? legendWidth + (legendWidth == 0 ? 0 : PADDING) : 0); int y = titleHeight + topAxisHeight + MARGIN + ((titleHeight == 0) ? 0 : PADDING) + (legendPosition == SWT.TOP ? legendHeight + (legendHeight == 0 ? 0 : PADDING) : 0); rightAxisOffset += width; height = height > 0 ? height : 0; layoutData.axisTitle.setBounds(x, y, width, height); width = layoutData.tickLabelsLayoutdata.widthHint; x -= width; rightAxisOffset += width; layoutData.axisTickLabels.setBounds(x, y - yAxisMargin, width, height + yAxisMargin * 2); width = layoutData.tickMarksLayoutdata.widthHint; x -= width; rightAxisOffset += width; layoutData.axisTickMarks.setBounds(x, y, width, height); } }