You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
445 lines
12 KiB
C++
445 lines
12 KiB
C++
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
|
|
* QwtPolar Widget Library
|
|
* Copyright (C) 2008 Uwe Rathmann
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the Qwt License, Version 1.0
|
|
*****************************************************************************/
|
|
|
|
#include "qwt_polar_layout.h"
|
|
#include "qwt_polar_plot.h"
|
|
#include "qwt_polar_canvas.h"
|
|
#include <qwt_text.h>
|
|
#include <qwt_text_label.h>
|
|
#include <qwt_legend.h>
|
|
#include <qscrollbar.h>
|
|
|
|
class QwtPolarLayout::LayoutData
|
|
{
|
|
public:
|
|
void init( const QwtPolarPlot *, const QRectF &rect );
|
|
|
|
struct t_legendData
|
|
{
|
|
int frameWidth;
|
|
int hScrollExtent;
|
|
int vScrollExtent;
|
|
QSizeF hint;
|
|
} legend;
|
|
|
|
struct t_titleData
|
|
{
|
|
QwtText text;
|
|
int frameWidth;
|
|
} title;
|
|
|
|
struct t_canvasData
|
|
{
|
|
int frameWidth;
|
|
} canvas;
|
|
};
|
|
|
|
void QwtPolarLayout::LayoutData::init(
|
|
const QwtPolarPlot *plot, const QRectF &rect )
|
|
{
|
|
// legend
|
|
|
|
if ( plot->plotLayout()->legendPosition() != QwtPolarPlot::ExternalLegend
|
|
&& plot->legend() )
|
|
{
|
|
legend.frameWidth = plot->legend()->frameWidth();
|
|
legend.hScrollExtent =
|
|
plot->legend()->scrollExtent( Qt::Horizontal );
|
|
legend.vScrollExtent =
|
|
plot->legend()->scrollExtent( Qt::Vertical );
|
|
|
|
const QSizeF hint = plot->legend()->sizeHint();
|
|
|
|
double w = qMin( hint.width(), rect.width() );
|
|
double h = plot->legend()->heightForWidth( w );
|
|
if ( h == 0.0 )
|
|
h = hint.height();
|
|
|
|
if ( h > rect.height() )
|
|
w += legend.hScrollExtent;
|
|
|
|
legend.hint = QSizeF( w, h );
|
|
}
|
|
|
|
// title
|
|
|
|
title.frameWidth = 0;
|
|
title.text = QwtText();
|
|
|
|
if ( plot->titleLabel() )
|
|
{
|
|
const QwtTextLabel *label = plot->titleLabel();
|
|
title.text = label->text();
|
|
if ( !( title.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) )
|
|
title.text.setFont( label->font() );
|
|
|
|
title.frameWidth = plot->titleLabel()->frameWidth();
|
|
}
|
|
|
|
// canvas
|
|
|
|
canvas.frameWidth = plot->canvas()->frameWidth();
|
|
}
|
|
|
|
class QwtPolarLayout::PrivateData
|
|
{
|
|
public:
|
|
PrivateData():
|
|
margin( 0 ),
|
|
spacing( 0 )
|
|
{
|
|
}
|
|
|
|
QRectF titleRect;
|
|
QRectF legendRect;
|
|
QRectF canvasRect;
|
|
|
|
QwtPolarLayout::LayoutData layoutData;
|
|
|
|
QwtPolarPlot::LegendPosition legendPos;
|
|
double legendRatio;
|
|
|
|
unsigned int margin;
|
|
unsigned int spacing;
|
|
};
|
|
|
|
/*!
|
|
\brief Constructor
|
|
*/
|
|
|
|
QwtPolarLayout::QwtPolarLayout()
|
|
{
|
|
d_data = new PrivateData;
|
|
|
|
setLegendPosition( QwtPolarPlot::BottomLegend );
|
|
invalidate();
|
|
}
|
|
|
|
//! Destructor
|
|
QwtPolarLayout::~QwtPolarLayout()
|
|
{
|
|
delete d_data;
|
|
}
|
|
|
|
/*!
|
|
\brief Specify the position of the legend
|
|
\param pos The legend's position.
|
|
\param ratio Ratio between legend and the bounding rect
|
|
of title, canvas and axes. The legend will be shrinked
|
|
if it would need more space than the given ratio.
|
|
The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
|
|
it will be reset to the default ratio.
|
|
The default vertical/horizontal ratio is 0.33/0.5.
|
|
|
|
\sa QwtPolarPlot::setLegendPosition()
|
|
*/
|
|
|
|
void QwtPolarLayout::setLegendPosition(
|
|
QwtPolarPlot::LegendPosition pos, double ratio )
|
|
{
|
|
if ( ratio > 1.0 )
|
|
ratio = 1.0;
|
|
|
|
switch( pos )
|
|
{
|
|
case QwtPolarPlot::TopLegend:
|
|
case QwtPolarPlot::BottomLegend:
|
|
{
|
|
if ( ratio <= 0.0 )
|
|
ratio = 0.33;
|
|
d_data->legendRatio = ratio;
|
|
d_data->legendPos = pos;
|
|
break;
|
|
}
|
|
case QwtPolarPlot::LeftLegend:
|
|
case QwtPolarPlot::RightLegend:
|
|
{
|
|
if ( ratio <= 0.0 )
|
|
ratio = 0.5;
|
|
d_data->legendRatio = ratio;
|
|
d_data->legendPos = pos;
|
|
break;
|
|
}
|
|
case QwtPolarPlot::ExternalLegend:
|
|
{
|
|
d_data->legendRatio = ratio; // meaningless
|
|
d_data->legendPos = pos;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\brief Specify the position of the legend
|
|
\param pos The legend's position. Valid values are
|
|
\c QwtPolarPlot::LeftLegend, \c QwtPolarPlot::RightLegend,
|
|
\c QwtPolarPlot::TopLegend, \c QwtPolarPlot::BottomLegend.
|
|
|
|
\sa QwtPolarPlot::setLegendPosition()
|
|
*/
|
|
void QwtPolarLayout::setLegendPosition( QwtPolarPlot::LegendPosition pos )
|
|
{
|
|
setLegendPosition( pos, 0.0 );
|
|
}
|
|
|
|
/*!
|
|
\return Position of the legend
|
|
\sa setLegendPosition(), QwtPolarPlot::setLegendPosition(),
|
|
QwtPolarPlot::legendPosition()
|
|
*/
|
|
QwtPolarPlot::LegendPosition QwtPolarLayout::legendPosition() const
|
|
{
|
|
return d_data->legendPos;
|
|
}
|
|
|
|
/*!
|
|
Specify the relative size of the legend in the plot
|
|
\param ratio Ratio between legend and the bounding rect
|
|
of title, canvas and axes. The legend will be shrinked
|
|
if it would need more space than the given ratio.
|
|
The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0
|
|
it will be reset to the default ratio.
|
|
The default vertical/horizontal ratio is 0.33/0.5.
|
|
*/
|
|
void QwtPolarLayout::setLegendRatio( double ratio )
|
|
{
|
|
setLegendPosition( legendPosition(), ratio );
|
|
}
|
|
|
|
/*!
|
|
\return The relative size of the legend in the plot.
|
|
\sa setLegendPosition()
|
|
*/
|
|
double QwtPolarLayout::legendRatio() const
|
|
{
|
|
return d_data->legendRatio;
|
|
}
|
|
|
|
/*!
|
|
\return Geometry for the title
|
|
\sa activate(), invalidate()
|
|
*/
|
|
|
|
const QRectF &QwtPolarLayout::titleRect() const
|
|
{
|
|
return d_data->titleRect;
|
|
}
|
|
|
|
/*!
|
|
\return Geometry for the legend
|
|
\sa activate(), invalidate()
|
|
*/
|
|
|
|
const QRectF &QwtPolarLayout::legendRect() const
|
|
{
|
|
return d_data->legendRect;
|
|
}
|
|
|
|
/*!
|
|
\return Geometry for the canvas
|
|
\sa activate(), invalidate()
|
|
*/
|
|
const QRectF &QwtPolarLayout::canvasRect() const
|
|
{
|
|
return d_data->canvasRect;
|
|
}
|
|
|
|
/*!
|
|
Invalidate the geometry of all components.
|
|
\sa activate()
|
|
*/
|
|
void QwtPolarLayout::invalidate()
|
|
{
|
|
d_data->titleRect = d_data->legendRect = d_data->canvasRect = QRect();
|
|
}
|
|
|
|
/*!
|
|
Find the geometry for the legend
|
|
\param options Options how to layout the legend
|
|
\param rect Rectangle where to place the legend
|
|
\return Geometry for the legend
|
|
*/
|
|
|
|
QRectF QwtPolarLayout::layoutLegend( Options options, QRectF &rect ) const
|
|
{
|
|
const QSizeF hint( d_data->layoutData.legend.hint );
|
|
|
|
int dim;
|
|
if ( d_data->legendPos == QwtPolarPlot::LeftLegend
|
|
|| d_data->legendPos == QwtPolarPlot::RightLegend )
|
|
{
|
|
// We don't allow vertical legends to take more than
|
|
// half of the available space.
|
|
|
|
dim = qMin( double( hint.width() ), rect.width() * d_data->legendRatio );
|
|
|
|
if ( !( options & IgnoreScrollbars ) )
|
|
{
|
|
if ( hint.height() > rect.height() )
|
|
{
|
|
// The legend will need additional
|
|
// space for the vertical scrollbar.
|
|
|
|
dim += d_data->layoutData.legend.hScrollExtent;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dim = qMin( double( hint.height() ), rect.height() * d_data->legendRatio );
|
|
dim = qMax( dim, d_data->layoutData.legend.vScrollExtent );
|
|
}
|
|
|
|
QRectF legendRect = rect;
|
|
switch( d_data->legendPos )
|
|
{
|
|
case QwtPolarPlot::LeftLegend:
|
|
{
|
|
legendRect.setWidth( dim );
|
|
rect.setLeft( legendRect.right() );
|
|
break;
|
|
}
|
|
case QwtPolarPlot::RightLegend:
|
|
{
|
|
legendRect.setX( rect.right() - dim + 1 );
|
|
legendRect.setWidth( dim );
|
|
rect.setRight( legendRect.left() );
|
|
break;
|
|
}
|
|
case QwtPolarPlot::TopLegend:
|
|
{
|
|
legendRect.setHeight( dim );
|
|
rect.setTop( legendRect.bottom() );
|
|
break;
|
|
}
|
|
case QwtPolarPlot::BottomLegend:
|
|
{
|
|
legendRect.setY( rect.bottom() - dim + 1 );
|
|
legendRect.setHeight( dim );
|
|
rect.setBottom( legendRect.top() );
|
|
break;
|
|
}
|
|
case QwtPolarPlot::ExternalLegend:
|
|
break;
|
|
}
|
|
|
|
return legendRect;
|
|
}
|
|
|
|
/*!
|
|
\brief Recalculate the geometry of all components.
|
|
|
|
\param plot Plot to be layout
|
|
\param boundingRect Rect where to place the components
|
|
\param options Options
|
|
|
|
\sa invalidate(), titleRect(), legendRect(), canvasRect()
|
|
*/
|
|
void QwtPolarLayout::activate( const QwtPolarPlot *plot,
|
|
const QRectF &boundingRect, Options options )
|
|
{
|
|
invalidate();
|
|
|
|
QRectF rect( boundingRect ); // undistributed rest of the plot rect
|
|
rect.adjust( d_data->margin, d_data->margin,
|
|
-d_data->margin, -d_data->margin );
|
|
|
|
// We extract all layout relevant data from the widgets
|
|
// and save them to d_data->layoutData.
|
|
|
|
d_data->layoutData.init( plot, rect );
|
|
if ( !( options & IgnoreLegend )
|
|
&& d_data->legendPos != QwtPolarPlot::ExternalLegend
|
|
&& plot->legend() && !plot->legend()->isEmpty() )
|
|
{
|
|
d_data->legendRect = layoutLegend( options, rect );
|
|
if ( d_data->layoutData.legend.frameWidth &&
|
|
!( options & IgnoreFrames ) )
|
|
{
|
|
// In case of a frame we have to insert a spacing.
|
|
// Otherwise the leading of the font separates
|
|
// legend and scale/canvas
|
|
|
|
switch( d_data->legendPos )
|
|
{
|
|
case QwtPolarPlot::LeftLegend:
|
|
rect.setLeft( rect.left() + d_data->spacing );
|
|
break;
|
|
|
|
case QwtPolarPlot::RightLegend:
|
|
rect.setRight( rect.right() - d_data->spacing );
|
|
break;
|
|
|
|
case QwtPolarPlot::TopLegend:
|
|
rect.setTop( rect.top() + d_data->spacing );
|
|
break;
|
|
|
|
case QwtPolarPlot::BottomLegend:
|
|
rect.setBottom( rect.bottom() - d_data->spacing );
|
|
break;
|
|
|
|
case QwtPolarPlot::ExternalLegend:
|
|
break; // suppress compiler warning
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !( options & IgnoreTitle ) &&
|
|
!d_data->layoutData.title.text.isEmpty() )
|
|
{
|
|
int h = d_data->layoutData.title.text.heightForWidth( rect.width() );
|
|
if ( !( options & IgnoreFrames ) )
|
|
h += 2 * d_data->layoutData.title.frameWidth;
|
|
|
|
d_data->titleRect = QRectF( rect.x(), rect.y(), rect.width(), h );
|
|
|
|
// subtract title
|
|
rect.setTop( rect.top() + h + d_data->spacing );
|
|
}
|
|
|
|
if ( plot->zoomPos().radius() > 0.0 || plot->zoomFactor() < 1.0 )
|
|
{
|
|
// In zoomed state we have no idea about the geometry that
|
|
// is best for the plot. So we use the complete rectangle
|
|
// accepting, that there might a lot of space wasted
|
|
// around the plot.
|
|
|
|
d_data->canvasRect = rect;
|
|
}
|
|
else
|
|
{
|
|
// In full state we know, that we want
|
|
// to display something circular.
|
|
|
|
const int dim = qMin( rect.width(), rect.height() );
|
|
|
|
d_data->canvasRect.setX( rect.center().x() - dim / 2 );
|
|
d_data->canvasRect.setY( rect.y() );
|
|
d_data->canvasRect.setSize( QSize( dim, dim ) );
|
|
}
|
|
|
|
if ( !d_data->legendRect.isEmpty() )
|
|
{
|
|
if ( d_data->legendPos == QwtPolarPlot::LeftLegend
|
|
|| d_data->legendPos == QwtPolarPlot::RightLegend )
|
|
{
|
|
// We prefer to align the legend to the canvas - not to
|
|
// the complete plot - if possible.
|
|
|
|
if ( d_data->layoutData.legend.hint.height()
|
|
< d_data->canvasRect.height() )
|
|
{
|
|
d_data->legendRect.setY( d_data->canvasRect.y() );
|
|
d_data->legendRect.setHeight( d_data->canvasRect.height() );
|
|
}
|
|
}
|
|
}
|
|
}
|