地面站终端 App
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

592 lines
14 KiB

15 years ago
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* Qwt Widget Library
* Copyright (C) 1997 Josef Wilgen
* Copyright (C) 2002 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_dyngrid_layout.h"
#include "qwt_math.h"
#include <qvector.h>
15 years ago
#include <qlist.h>
class QwtDynGridLayout::PrivateData
{
public:
PrivateData():
isDirty( true )
{
15 years ago
}
void updateLayoutCache();
15 years ago
mutable QList<QLayoutItem*> itemList;
15 years ago
uint maxColumns;
15 years ago
uint numRows;
uint numColumns;
15 years ago
Qt::Orientations expanding;
bool isDirty;
QVector<QSize> itemSizeHints;
15 years ago
};
void QwtDynGridLayout::PrivateData::updateLayoutCache()
{
itemSizeHints.resize( itemList.count() );
15 years ago
int index = 0;
15 years ago
for ( QList<QLayoutItem*>::iterator it = itemList.begin();
it != itemList.end(); ++it, index++ )
{
itemSizeHints[ index ] = ( *it )->sizeHint();
}
15 years ago
isDirty = false;
15 years ago
}
/*!
\param parent Parent widget
\param margin Margin
15 years ago
\param spacing Spacing
*/
QwtDynGridLayout::QwtDynGridLayout( QWidget *parent,
int margin, int spacing ):
QLayout( parent )
15 years ago
{
init();
setSpacing( spacing );
setMargin( margin );
15 years ago
}
/*!
\param spacing Spacing
*/
QwtDynGridLayout::QwtDynGridLayout( int spacing )
15 years ago
{
init();
setSpacing( spacing );
15 years ago
}
/*!
Initialize the layout with default values.
*/
void QwtDynGridLayout::init()
{
d_data = new QwtDynGridLayout::PrivateData;
d_data->maxColumns = d_data->numRows = d_data->numColumns = 0;
15 years ago
d_data->expanding = 0;
}
//! Destructor
QwtDynGridLayout::~QwtDynGridLayout()
{
for ( int i = 0; i < d_data->itemList.size(); i++ )
delete d_data->itemList[i];
15 years ago
delete d_data;
}
//! Invalidate all internal caches
15 years ago
void QwtDynGridLayout::invalidate()
{
d_data->isDirty = true;
QLayout::invalidate();
}
/*!
Limit the number of columns.
\param maxColumns upper limit, 0 means unlimited
\sa maxColumns()
15 years ago
*/
void QwtDynGridLayout::setMaxColumns( uint maxColumns )
15 years ago
{
d_data->maxColumns = maxColumns;
15 years ago
}
/*!
\brief Return the upper limit for the number of columns.
15 years ago
0 means unlimited, what is the default.
\return Upper limit for the number of columns
\sa setMaxColumns()
*/
uint QwtDynGridLayout::maxColumns() const
{
return d_data->maxColumns;
15 years ago
}
/*!
\brief Add an item to the next free position.
\param item Layout item
*/
void QwtDynGridLayout::addItem( QLayoutItem *item )
15 years ago
{
d_data->itemList.append( item );
15 years ago
invalidate();
}
/*!
\return true if this layout is empty.
15 years ago
*/
bool QwtDynGridLayout::isEmpty() const
{
return d_data->itemList.isEmpty();
}
/*!
15 years ago
\return number of layout items
*/
uint QwtDynGridLayout::itemCount() const
{
return d_data->itemList.count();
}
/*!
Find the item at a specific index
15 years ago
\param index Index
\return Item at a specific index
\sa takeAt()
15 years ago
*/
QLayoutItem *QwtDynGridLayout::itemAt( int index ) const
{
if ( index < 0 || index >= d_data->itemList.count() )
return NULL;
return d_data->itemList.at( index );
15 years ago
}
/*!
Find the item at a specific index and remove it from the layout
\param index Index
\return Layout item, removed from the layout
\sa itemAt()
*/
15 years ago
QLayoutItem *QwtDynGridLayout::takeAt( int index )
{
if ( index < 0 || index >= d_data->itemList.count() )
return NULL;
15 years ago
d_data->isDirty = true;
return d_data->itemList.takeAt( index );
15 years ago
}
//! \return Number of items in the layout
15 years ago
int QwtDynGridLayout::count() const
{
return d_data->itemList.count();
}
/*!
Set whether this layout can make use of more space than sizeHint().
A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only
one dimension, while Qt::Vertical | Qt::Horizontal means that it wants
to grow in both dimensions. The default value is 0.
\param expanding Or'd orientations
\sa expandingDirections()
*/
void QwtDynGridLayout::setExpandingDirections( Qt::Orientations expanding )
15 years ago
{
d_data->expanding = expanding;
}
/*!
\brief Returns whether this layout can make use of more space than sizeHint().
A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only
one dimension, while Qt::Vertical | Qt::Horizontal means that it wants
to grow in both dimensions.
\return Orientations, where the layout expands
\sa setExpandingDirections()
*/
15 years ago
Qt::Orientations QwtDynGridLayout::expandingDirections() const
{
return d_data->expanding;
}
/*!
Reorganizes columns and rows and resizes managed items within
a rectangle.
15 years ago
\param rect Layout geometry
*/
void QwtDynGridLayout::setGeometry( const QRect &rect )
15 years ago
{
QLayout::setGeometry( rect );
15 years ago
if ( isEmpty() )
return;
d_data->numColumns = columnsForWidth( rect.width() );
d_data->numRows = itemCount() / d_data->numColumns;
if ( itemCount() % d_data->numColumns )
15 years ago
d_data->numRows++;
QList<QRect> itemGeometries = layoutItems( rect, d_data->numColumns );
15 years ago
int index = 0;
for ( QList<QLayoutItem*>::iterator it = d_data->itemList.begin();
it != d_data->itemList.end(); ++it )
{
( *it )->setGeometry( itemGeometries[index] );
index++;
15 years ago
}
}
/*!
\brief Calculate the number of columns for a given width.
The calculation tries to use as many columns as possible
( limited by maxColumns() )
15 years ago
\param width Available width for all columns
\return Number of columns for a given width
15 years ago
\sa maxColumns(), setMaxColumns()
*/
uint QwtDynGridLayout::columnsForWidth( int width ) const
15 years ago
{
if ( isEmpty() )
return 0;
uint maxColumns = itemCount();
if ( d_data->maxColumns > 0 )
maxColumns = qMin( d_data->maxColumns, maxColumns );
if ( maxRowWidth( maxColumns ) <= width )
return maxColumns;
15 years ago
for ( uint numColumns = 2; numColumns <= maxColumns; numColumns++ )
{
const int rowWidth = maxRowWidth( numColumns );
15 years ago
if ( rowWidth > width )
return numColumns - 1;
15 years ago
}
return 1; // At least 1 column
}
/*!
15 years ago
Calculate the width of a layout for a given number of
columns.
\param numColumns Given number of columns
15 years ago
\param itemWidth Array of the width hints for all items
*/
int QwtDynGridLayout::maxRowWidth( int numColumns ) const
15 years ago
{
int col;
QVector<int> colWidth( numColumns );
for ( col = 0; col < numColumns; col++ )
15 years ago
colWidth[col] = 0;
if ( d_data->isDirty )
d_data->updateLayoutCache();
15 years ago
for ( int index = 0;
index < d_data->itemSizeHints.count(); index++ )
{
col = index % numColumns;
colWidth[col] = qMax( colWidth[col],
d_data->itemSizeHints[int( index )].width() );
15 years ago
}
int rowWidth = 2 * margin() + ( numColumns - 1 ) * spacing();
for ( col = 0; col < numColumns; col++ )
15 years ago
rowWidth += colWidth[col];
return rowWidth;
}
/*!
\return the maximum width of all layout items
*/
int QwtDynGridLayout::maxItemWidth() const
{
if ( isEmpty() )
return 0;
if ( d_data->isDirty )
d_data->updateLayoutCache();
15 years ago
int w = 0;
for ( int i = 0; i < d_data->itemSizeHints.count(); i++ )
{
const int itemW = d_data->itemSizeHints[i].width();
15 years ago
if ( itemW > w )
w = itemW;
}
return w;
}
/*!
Calculate the geometries of the layout items for a layout
with numColumns columns and a given rectangle.
15 years ago
\param rect Rect where to place the items
\param numColumns Number of columns
15 years ago
\return item geometries
*/
QList<QRect> QwtDynGridLayout::layoutItems( const QRect &rect,
uint numColumns ) const
15 years ago
{
QList<QRect> itemGeometries;
if ( numColumns == 0 || isEmpty() )
15 years ago
return itemGeometries;
uint numRows = itemCount() / numColumns;
if ( numColumns % itemCount() )
15 years ago
numRows++;
if ( numRows == 0 )
return itemGeometries;
QVector<int> rowHeight( numRows );
QVector<int> colWidth( numColumns );
layoutGrid( numColumns, rowHeight, colWidth );
15 years ago
bool expandH, expandV;
expandH = expandingDirections() & Qt::Horizontal;
expandV = expandingDirections() & Qt::Vertical;
if ( expandH || expandV )
stretchGrid( rect, numColumns, rowHeight, colWidth );
15 years ago
const int maxColumns = d_data->maxColumns;
d_data->maxColumns = numColumns;
const QRect alignedRect = alignmentRect( rect );
d_data->maxColumns = maxColumns;
15 years ago
const int xOffset = expandH ? 0 : alignedRect.x();
const int yOffset = expandV ? 0 : alignedRect.y();
QVector<int> colX( numColumns );
QVector<int> rowY( numRows );
15 years ago
const int xySpace = spacing();
rowY[0] = yOffset + margin();
for ( uint r = 1; r < numRows; r++ )
15 years ago
rowY[r] = rowY[r-1] + rowHeight[r-1] + xySpace;
colX[0] = xOffset + margin();
for ( uint c = 1; c < numColumns; c++ )
15 years ago
colX[c] = colX[c-1] + colWidth[c-1] + xySpace;
15 years ago
const int itemCount = d_data->itemList.size();
for ( int i = 0; i < itemCount; i++ )
{
const int row = i / numColumns;
const int col = i % numColumns;
15 years ago
QRect itemGeometry( colX[col], rowY[row],
colWidth[col], rowHeight[row] );
itemGeometries.append( itemGeometry );
15 years ago
}
return itemGeometries;
}
/*!
Calculate the dimensions for the columns and rows for a grid
of numColumns columns.
\param numColumns Number of columns.
15 years ago
\param rowHeight Array where to fill in the calculated row heights.
\param colWidth Array where to fill in the calculated column widths.
*/
void QwtDynGridLayout::layoutGrid( uint numColumns,
QVector<int>& rowHeight, QVector<int>& colWidth ) const
15 years ago
{
if ( numColumns <= 0 )
15 years ago
return;
if ( d_data->isDirty )
d_data->updateLayoutCache();
15 years ago
for ( int index = 0; index < d_data->itemSizeHints.count(); index++ )
{
const int row = index / numColumns;
const int col = index % numColumns;
15 years ago
const QSize &size = d_data->itemSizeHints[int( index )];
15 years ago
rowHeight[row] = ( col == 0 )
? size.height() : qMax( rowHeight[row], size.height() );
colWidth[col] = ( row == 0 )
? size.width() : qMax( colWidth[col], size.width() );
15 years ago
}
}
/*!
\return true: QwtDynGridLayout implements heightForWidth().
\sa heightForWidth()
15 years ago
*/
bool QwtDynGridLayout::hasHeightForWidth() const
{
return true;
}
/*!
\return The preferred height for this layout, given a width.
\sa hasHeightForWidth()
15 years ago
*/
int QwtDynGridLayout::heightForWidth( int width ) const
15 years ago
{
if ( isEmpty() )
return 0;
const uint numColumns = columnsForWidth( width );
uint numRows = itemCount() / numColumns;
if ( itemCount() % numColumns )
15 years ago
numRows++;
QVector<int> rowHeight( numRows );
QVector<int> colWidth( numColumns );
15 years ago
layoutGrid( numColumns, rowHeight, colWidth );
15 years ago
int h = 2 * margin() + ( numRows - 1 ) * spacing();
for ( uint row = 0; row < numRows; row++ )
15 years ago
h += rowHeight[row];
return h;
}
/*!
Stretch columns in case of expanding() & QSizePolicy::Horizontal and
rows in case of expanding() & QSizePolicy::Vertical to fill the entire
rect. Rows and columns are stretched with the same factor.
\param rect Bounding rectangle
\param numColumns Number of columns
\param rowHeight Array to be filled with the calculated row heights
\param colWidth Array to be filled with the calculated column widths
\sa setExpanding(), expanding()
*/
void QwtDynGridLayout::stretchGrid( const QRect &rect,
uint numColumns, QVector<int>& rowHeight, QVector<int>& colWidth ) const
15 years ago
{
if ( numColumns == 0 || isEmpty() )
15 years ago
return;
bool expandH, expandV;
expandH = expandingDirections() & Qt::Horizontal;
expandV = expandingDirections() & Qt::Vertical;
if ( expandH )
{
int xDelta = rect.width() - 2 * margin() - ( numColumns - 1 ) * spacing();
for ( uint col = 0; col < numColumns; col++ )
15 years ago
xDelta -= colWidth[col];
if ( xDelta > 0 )
{
for ( uint col = 0; col < numColumns; col++ )
{
const int space = xDelta / ( numColumns - col );
15 years ago
colWidth[col] += space;
xDelta -= space;
}
}
}
if ( expandV )
{
uint numRows = itemCount() / numColumns;
if ( itemCount() % numColumns )
15 years ago
numRows++;
int yDelta = rect.height() - 2 * margin() - ( numRows - 1 ) * spacing();
for ( uint row = 0; row < numRows; row++ )
15 years ago
yDelta -= rowHeight[row];
if ( yDelta > 0 )
{
for ( uint row = 0; row < numRows; row++ )
{
const int space = yDelta / ( numRows - row );
15 years ago
rowHeight[row] += space;
yDelta -= space;
}
}
}
}
/*!
Return the size hint. If maxColumns() > 0 it is the size for
a grid with maxColumns() columns, otherwise it is the size for
15 years ago
a grid with only one row.
\return Size hint
\sa maxColumns(), setMaxColumns()
*/
15 years ago
QSize QwtDynGridLayout::sizeHint() const
{
if ( isEmpty() )
return QSize();
uint numColumns = itemCount();
if ( d_data->maxColumns > 0 )
numColumns = qMin( d_data->maxColumns, numColumns );
uint numRows = itemCount() / numColumns;
if ( itemCount() % numColumns )
15 years ago
numRows++;
QVector<int> rowHeight( numRows );
QVector<int> colWidth( numColumns );
15 years ago
layoutGrid( numColumns, rowHeight, colWidth );
15 years ago
int h = 2 * margin() + ( numRows - 1 ) * spacing();
for ( uint row = 0; row < numRows; row++ )
15 years ago
h += rowHeight[row];
int w = 2 * margin() + ( numColumns - 1 ) * spacing();
for ( uint col = 0; col < numColumns; col++ )
15 years ago
w += colWidth[col];
return QSize( w, h );
15 years ago
}
/*!
\return Number of rows of the current layout.
\sa numColumns()
15 years ago
\warning The number of rows might change whenever the geometry changes
*/
uint QwtDynGridLayout::numRows() const
{
return d_data->numRows;
15 years ago
}
/*!
\return Number of columns of the current layout.
\sa numRows()
15 years ago
\warning The number of columns might change whenever the geometry changes
*/
uint QwtDynGridLayout::numColumns() const
{
return d_data->numColumns;
15 years ago
}