/*******************************************************************************
* 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 );
}
}