/******************************************************************************* * Copyright (c) 2008-2009 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 - legendWidth - MARGIN - ( ( legendWidth == 0 ) ? 0 : PADDING ); 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 ); } }