# qwt_dyngrid_layout.cpp

    00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
    00002  * Qwt Widget Library
    00003  * Copyright (C) 1997   Josef Wilgen
    00004  * Copyright (C) 2002   Uwe Rathmann
    00005  *
    00006  * This library is free software; you can redistribute it and/or
    00007  * modify it under the terms of the Qwt License, Version 1.0
    00008  *****************************************************************************/
    00009
    00010 #include <qwidget.h>
    00011 #include "qwt_dyngrid_layout.h"
    00012 #include "qwt_math.h"
    00013
    00014 #if QT_VERSION < 0x040000
    00015 #include <qvaluelist.h>
    00016 #else
    00017 #include <qlist.h>
    00018 #endif
    00019
    00020 class QwtDynGridLayout::PrivateData
    00021 {
    00022 public:
    00023
    00024 #if QT_VERSION < 0x040000
    00025     class LayoutIterator: public QGLayoutIterator
    00026     {
    00027     public:
    00028         LayoutIterator(PrivateData *data):
    00029             d_data(data)
    00030         {
    00031             d_iterator = d_data->itemList.begin();
    00032         }
    00033
    00034         virtual QLayoutItem *current()
    00035         {
    00036             if (d_iterator == d_data->itemList.end())
    00037                return NULL;
    00038
    00039             return *d_iterator;
    00040         }
    00041
    00042         virtual QLayoutItem *next()
    00043         {
    00044             if (d_iterator == d_data->itemList.end())
    00045                return NULL;
    00046
    00047             d_iterator++;
    00048             if (d_iterator == d_data->itemList.end())
    00049                return NULL;
    00050
    00051             return *d_iterator;
    00052         }
    00053
    00054         virtual QLayoutItem *takeCurrent()
    00055         {
    00056             if ( d_iterator == d_data->itemList.end() )
    00057                 return NULL;
    00058
    00059             QLayoutItem *item = *d_iterator;
    00060
    00061             d_data->isDirty = true;
    00062             d_iterator = d_data->itemList.remove(d_iterator);
    00063             return item;
    00064         }
    00065
    00066     private:
    00067
    00068         QValueListIterator<QLayoutItem*> d_iterator;
    00069         QwtDynGridLayout::PrivateData *d_data;
    00070     };
    00071 #endif
    00072
    00073     PrivateData():
    00074         isDirty(true)
    00075     {
    00076     }
    00077
    00078 #if QT_VERSION < 0x040000
    00079     typedef QValueList<QLayoutItem*> LayoutItemList;
    00080 #else
    00081     typedef QList<QLayoutItem*> LayoutItemList;
    00082 #endif
    00083
    00084     mutable LayoutItemList itemList;
    00085
    00086     uint maxCols;
    00087     uint numRows;
    00088     uint numCols;
    00089
    00090 #if QT_VERSION < 0x040000
    00091     QSizePolicy::ExpandData expanding;
    00092 #else
    00093     Qt::Orientations expanding;
    00094 #endif
    00095
    00096     bool isDirty;
    00097     QwtArray<QSize> itemSizeHints;
    00098 };
    00099
    00100
    00107 QwtDynGridLayout::QwtDynGridLayout(QWidget *parent,
    00108         int margin, int spacing):
    00109     QLayout(parent)
    00110 {
    00111     init();
    00112
    00113     setSpacing(spacing);
    00114     setMargin(margin);
    00115 }
    00116
    00117 #if QT_VERSION < 0x040000
    00118
    00122 QwtDynGridLayout::QwtDynGridLayout(QLayout *parent, int spacing):
    00123     QLayout(parent, spacing)
    00124 {
    00125     init();
    00126 }
    00127 #endif
    00128
    00133 QwtDynGridLayout::QwtDynGridLayout(int spacing)
    00134 {
    00135     init();
    00136     setSpacing(spacing);
    00137 }
    00138
    00142 void QwtDynGridLayout::init()
    00143 {
    00144     d_data = new QwtDynGridLayout::PrivateData;
    00145     d_data->maxCols = d_data->numRows
    00146         = d_data->numCols = 0;
    00147
    00148 #if QT_VERSION < 0x040000
    00149     d_data->expanding = QSizePolicy::NoDirection;
    00150     setSupportsMargin(true);
    00151 #else
    00152     d_data->expanding = 0;
    00153 #endif
    00154 }
    00155
    00157
    00158 QwtDynGridLayout::~QwtDynGridLayout()
    00159 {
    00160 #if QT_VERSION < 0x040000
    00161     deleteAllItems();
    00162 #endif
    00163
    00164     delete d_data;
    00165 }
    00166
    00167 void QwtDynGridLayout::invalidate()
    00168 {
    00169     d_data->isDirty = true;
    00170     QLayout::invalidate();
    00171 }
    00172
    00173 void QwtDynGridLayout::updateLayoutCache()
    00174 {
    00175     d_data->itemSizeHints.resize(itemCount());
    00176
    00177     int index = 0;
    00178
    00179     for (PrivateData::LayoutItemList::iterator it = d_data->itemList.begin();
    00180         it != d_data->itemList.end(); ++it, index++)
    00181     {
    00182         d_data->itemSizeHints[int(index)] = (*it)->sizeHint();
    00183     }
    00184
    00185     d_data->isDirty = false;
    00186 }
    00187
    00194 void QwtDynGridLayout::setMaxCols(uint maxCols)
    00195 {
    00196     d_data->maxCols = maxCols;
    00197 }
    00198
    00205 uint QwtDynGridLayout::maxCols() const
    00206 {
    00207     return d_data->maxCols;
    00208 }
    00209
    00211
    00212 void QwtDynGridLayout::addItem(QLayoutItem *item)
    00213 {
    00214     d_data->itemList.append(item);
    00215     invalidate();
    00216 }
    00217
    00222 bool QwtDynGridLayout::isEmpty() const
    00223 {
    00224     return d_data->itemList.isEmpty();
    00225 }
    00226
    00231 uint QwtDynGridLayout::itemCount() const
    00232 {
    00233     return d_data->itemList.count();
    00234 }
    00235
    00236 #if  QT_VERSION < 0x040000
    00237
    00241 QLayoutIterator QwtDynGridLayout::iterator()
    00242 {
    00243     return QLayoutIterator(
    00244         new QwtDynGridLayout::PrivateData::LayoutIterator(d_data) );
    00245 }
    00246
    00255 void QwtDynGridLayout::setExpanding(QSizePolicy::ExpandData expanding)
    00256 {
    00257     d_data->expanding = expanding;
    00258 }
    00259
    00268 QSizePolicy::ExpandData QwtDynGridLayout::expanding() const
    00269 {
    00270     return d_data->expanding;
    00271 }
    00272
    00273 #else // QT_VERSION >= 0x040000
    00274
    00275 QLayoutItem *QwtDynGridLayout::itemAt( int index ) const
    00276 {
    00277     if ( index < 0 || index >= d_data->itemList.count() )
    00278         return NULL;
    00279
    00280     return d_data->itemList.at(index);
    00281 }
    00282
    00283 QLayoutItem *QwtDynGridLayout::takeAt( int index )
    00284 {
    00285     if ( index < 0 || index >= d_data->itemList.count() )
    00286         return NULL;
    00287
    00288     d_data->isDirty = true;
    00289     return d_data->itemList.takeAt(index);
    00290 }
    00291
    00292 int QwtDynGridLayout::count() const
    00293 {
    00294     return d_data->itemList.count();
    00295 }
    00296
    00297 void QwtDynGridLayout::setExpandingDirections(Qt::Orientations expanding)
    00298 {
    00299     d_data->expanding = expanding;
    00300 }
    00301
    00302 Qt::Orientations QwtDynGridLayout::expandingDirections() const
    00303 {
    00304     return d_data->expanding;
    00305 }
    00306
    00307 #endif
    00308
    00314 void QwtDynGridLayout::setGeometry(const QRect &rect)
    00315 {
    00316     QLayout::setGeometry(rect);
    00317
    00318     if ( isEmpty() )
    00319         return;
    00320
    00321     d_data->numCols = columnsForWidth(rect.width());
    00322     d_data->numRows = itemCount() / d_data->numCols;
    00323     if ( itemCount() % d_data->numCols )
    00324         d_data->numRows++;
    00325
    00326 #if QT_VERSION < 0x040000
    00327     QValueList<QRect> itemGeometries = layoutItems(rect, d_data->numCols);
    00328 #else
    00329     QList<QRect> itemGeometries = layoutItems(rect, d_data->numCols);
    00330 #endif
    00331
    00332     int index = 0;
    00333     for (PrivateData::LayoutItemList::iterator it = d_data->itemList.begin();
    00334         it != d_data->itemList.end(); ++it)
    00335     {
    00336         QWidget *w = (*it)->widget();
    00337         if ( w )
    00338         {
    00339             w->setGeometry(itemGeometries[index]);
    00340             index++;
    00341         }
    00342     }
    00343 }
    00344
    00353 uint QwtDynGridLayout::columnsForWidth(int width) const
    00354 {
    00355     if ( isEmpty() )
    00356         return 0;
    00357
    00358     const int maxCols = (d_data->maxCols > 0) ? d_data->maxCols : itemCount();
    00359     if ( maxRowWidth(maxCols) <= width )
    00360         return maxCols;
    00361
    00362     for (int numCols = 2; numCols <= maxCols; numCols++ )
    00363     {
    00364         const int rowWidth = maxRowWidth(numCols);
    00365         if ( rowWidth > width )
    00366             return numCols - 1;
    00367     }
    00368
    00369     return 1; // At least 1 column
    00370 }
    00371
    00379 int QwtDynGridLayout::maxRowWidth(int numCols) const
    00380 {
    00381     int col;
    00382
    00383     QwtArray<int> colWidth(numCols);
    00384     for ( col = 0; col < (int)numCols; col++ )
    00385         colWidth[col] = 0;
    00386
    00387     if ( d_data->isDirty )
    00388         ((QwtDynGridLayout*)this)->updateLayoutCache();
    00389
    00390     for ( uint index = 0;
    00391         index < (uint)d_data->itemSizeHints.count(); index++ )
    00392     {
    00393         col = index % numCols;
    00394         colWidth[col] = qwtMax(colWidth[col],
    00395             d_data->itemSizeHints[int(index)].width());
    00396     }
    00397
    00398     int rowWidth = 2 * margin() + (numCols - 1) * spacing();
    00399     for ( col = 0; col < (int)numCols; col++ )
    00400         rowWidth += colWidth[col];
    00401
    00402     return rowWidth;
    00403 }
    00404
    00409 int QwtDynGridLayout::maxItemWidth() const
    00410 {
    00411     if ( isEmpty() )
    00412         return 0;
    00413
    00414     if ( d_data->isDirty )
    00415         ((QwtDynGridLayout*)this)->updateLayoutCache();
    00416
    00417     int w = 0;
    00418     for ( uint i = 0; i < (uint)d_data->itemSizeHints.count(); i++ )
    00419     {
    00420         const int itemW = d_data->itemSizeHints[int(i)].width();
    00421         if ( itemW > w )
    00422             w = itemW;
    00423     }
    00424
    00425     return w;
    00426 }
    00427
    00436 #if QT_VERSION < 0x040000
    00437 QValueList<QRect> QwtDynGridLayout::layoutItems(const QRect &rect,
    00438     uint numCols) const
    00439 #else
    00440 QList<QRect> QwtDynGridLayout::layoutItems(const QRect &rect,
    00441     uint numCols) const
    00442 #endif
    00443 {
    00444 #if QT_VERSION < 0x040000
    00445     QValueList<QRect> itemGeometries;
    00446 #else
    00447     QList<QRect> itemGeometries;
    00448 #endif
    00449     if ( numCols == 0 || isEmpty() )
    00450         return itemGeometries;
    00451
    00452     uint numRows = itemCount() / numCols;
    00453     if ( numRows % itemCount() )
    00454         numRows++;
    00455
    00456     QwtArray<int> rowHeight(numRows);
    00457     QwtArray<int> colWidth(numCols);
    00458
    00459     layoutGrid(numCols, rowHeight, colWidth);
    00460
    00461     bool expandH, expandV;
    00462 #if QT_VERSION >= 0x040000
    00463     expandH = expandingDirections() & Qt::Horizontal;
    00464     expandV = expandingDirections() & Qt::Vertical;
    00465 #else
    00466     expandH = expanding() & QSizePolicy::Horizontally;
    00467     expandV = expanding() & QSizePolicy::Vertically;
    00468 #endif
    00469
    00470     if ( expandH || expandV )
    00471         stretchGrid(rect, numCols, rowHeight, colWidth);
    00472
    00473     QwtDynGridLayout *that = (QwtDynGridLayout *)this;
    00474     const int maxCols = d_data->maxCols;
    00475     that->d_data->maxCols = numCols;
    00476     const QRect alignedRect = alignmentRect(rect);
    00477     that->d_data->maxCols = maxCols;
    00478
    00479     const int xOffset = expandH ? 0 : alignedRect.x();
    00480     const int yOffset = expandV ? 0 : alignedRect.y();
    00481
    00482     QwtArray<int> colX(numCols);
    00483     QwtArray<int> rowY(numRows);
    00484
    00485     const int xySpace = spacing();
    00486
    00487     rowY[0] = yOffset + margin();
    00488     for ( int r = 1; r < (int)numRows; r++ )
    00489         rowY[r] = rowY[r-1] + rowHeight[r-1] + xySpace;
    00490
    00491     colX[0] = xOffset + margin();
    00492     for ( int c = 1; c < (int)numCols; c++ )
    00493         colX[c] = colX[c-1] + colWidth[c-1] + xySpace;
    00494
    00495     const int itemCount = d_data->itemList.size();
    00496     for ( int i = 0; i < itemCount; i++ )
    00497     {
    00498         const int row = i / numCols;
    00499         const int col = i % numCols;
    00500
    00501         QRect itemGeometry(colX[col], rowY[row],
    00502             colWidth[col], rowHeight[row]);
    00503         itemGeometries.append(itemGeometry);
    00504     }
    00505
    00506     return itemGeometries;
    00507 }
    00508
    00509
    00518 void QwtDynGridLayout::layoutGrid(uint numCols,
    00519     QwtArray<int>& rowHeight, QwtArray<int>& colWidth) const
    00520 {
    00521     if ( numCols <= 0 )
    00522         return;
    00523
    00524     if ( d_data->isDirty )
    00525         ((QwtDynGridLayout*)this)->updateLayoutCache();
    00526
    00527     for ( uint index = 0;
    00528         index < (uint)d_data->itemSizeHints.count(); index++ )
    00529     {
    00530         const int row = index / numCols;
    00531         const int col = index % numCols;
    00532
    00533         const QSize &size = d_data->itemSizeHints[int(index)];
    00534
    00535         rowHeight[row] = (col == 0)
    00536             ? size.height() : qwtMax(rowHeight[row], size.height());
    00537         colWidth[col] = (row == 0)
    00538             ? size.width() : qwtMax(colWidth[col], size.width());
    00539     }
    00540 }
    00541
    00547 bool QwtDynGridLayout::hasHeightForWidth() const
    00548 {
    00549     return true;
    00550 }
    00551
    00557 int QwtDynGridLayout::heightForWidth(int width) const
    00558 {
    00559     if ( isEmpty() )
    00560         return 0;
    00561
    00562     const uint numCols = columnsForWidth(width);
    00563     uint numRows = itemCount() / numCols;
    00564     if ( itemCount() % numCols )
    00565         numRows++;
    00566
    00567     QwtArray<int> rowHeight(numRows);
    00568     QwtArray<int> colWidth(numCols);
    00569
    00570     layoutGrid(numCols, rowHeight, colWidth);
    00571
    00572     int h = 2 * margin() + (numRows - 1) * spacing();
    00573     for ( int row = 0; row < (int)numRows; row++ )
    00574         h += rowHeight[row];
    00575
    00576     return h;
    00577 }
    00578
    00586 void QwtDynGridLayout::stretchGrid(const QRect &rect,
    00587     uint numCols, QwtArray<int>& rowHeight, QwtArray<int>& colWidth) const
    00588 {
    00589     if ( numCols == 0 || isEmpty() )
    00590         return;
    00591
    00592     bool expandH, expandV;
    00593 #if QT_VERSION >= 0x040000
    00594     expandH = expandingDirections() & Qt::Horizontal;
    00595     expandV = expandingDirections() & Qt::Vertical;
    00596 #else
    00597     expandH = expanding() & QSizePolicy::Horizontally;
    00598     expandV = expanding() & QSizePolicy::Vertically;
    00599 #endif
    00600
    00601     if ( expandH )
    00602     {
    00603         int xDelta = rect.width() - 2 * margin() - (numCols - 1) * spacing();
    00604         for ( int col = 0; col < (int)numCols; col++ )
    00605             xDelta -= colWidth[col];
    00606
    00607         if ( xDelta > 0 )
    00608         {
    00609             for ( int col = 0; col < (int)numCols; col++ )
    00610             {
    00611                 const int space = xDelta / (numCols - col);
    00612                 colWidth[col] += space;
    00613                 xDelta -= space;
    00614             }
    00615         }
    00616     }
    00617
    00618     if ( expandV )
    00619     {
    00620         uint numRows = itemCount() / numCols;
    00621         if ( itemCount() % numCols )
    00622             numRows++;
    00623
    00624         int yDelta = rect.height() - 2 * margin() - (numRows - 1) * spacing();
    00625         for ( int row = 0; row < (int)numRows; row++ )
    00626             yDelta -= rowHeight[row];
    00627
    00628         if ( yDelta > 0 )
    00629         {
    00630             for ( int row = 0; row < (int)numRows; row++ )
    00631             {
    00632                 const int space = yDelta / (numRows - row);
    00633                 rowHeight[row] += space;
    00634                 yDelta -= space;
    00635             }
    00636         }
    00637     }
    00638 }
    00639
    00647 QSize QwtDynGridLayout::sizeHint() const
    00648 {
    00649     if ( isEmpty() )
    00650         return QSize();
    00651
    00652     const uint numCols = (d_data->maxCols > 0 ) ? d_data->maxCols : itemCount();
    00653     uint numRows = itemCount() / numCols;
    00654     if ( itemCount() % numCols )
    00655         numRows++;
    00656
    00657     QwtArray<int> rowHeight(numRows);
    00658     QwtArray<int> colWidth(numCols);
    00659
    00660     layoutGrid(numCols, rowHeight, colWidth);
    00661
    00662     int h = 2 * margin() + (numRows - 1) * spacing();
    00663     for ( int row = 0; row < (int)numRows; row++ )
    00664         h += rowHeight[row];
    00665
    00666     int w = 2 * margin() + (numCols - 1) * spacing();
    00667     for ( int col = 0; col < (int)numCols; col++ )
    00668         w += colWidth[col];
    00669
    00670     return QSize(w, h);
    00671 }
    00672
    00678 uint QwtDynGridLayout::numRows() const
    00679 {
    00680     return d_data->numRows;
    00681 }
    00682
    00688 uint QwtDynGridLayout::numCols() const
    00689 {
    00690     return d_data->numCols;
    00691 }