source: ntrip/trunk/BNC/qwt/qwt_dyngrid_layout.cpp @ 4271

Last change on this file since 4271 was 4271, checked in by mervart, 7 years ago
File size: 13.4 KB
Line 
1/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2 * Qwt Widget Library
3 * Copyright (C) 1997   Josef Wilgen
4 * Copyright (C) 2002   Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_dyngrid_layout.h"
11#include "qwt_math.h"
12#include <qwidget.h>
13#include <qlist.h>
14
15class QwtDynGridLayout::PrivateData
16{
17public:
18    PrivateData():
19        isDirty( true )
20    {
21    }
22
23    void updateLayoutCache();
24
25    mutable QList<QLayoutItem*> itemList;
26
27    uint maxCols;
28    uint numRows;
29    uint numCols;
30
31    Qt::Orientations expanding;
32
33    bool isDirty;
34    QVector<QSize> itemSizeHints;
35};
36
37void QwtDynGridLayout::PrivateData::updateLayoutCache()
38{
39    itemSizeHints.resize( itemList.count() );
40
41    int index = 0;
42
43    for ( QList<QLayoutItem*>::iterator it = itemList.begin();
44        it != itemList.end(); ++it, index++ )
45    {
46        itemSizeHints[ index ] = ( *it )->sizeHint();
47    }
48
49    isDirty = false;
50}
51
52/*!
53  \param parent Parent widget
54  \param margin Margin
55  \param spacing Spacing
56*/
57
58QwtDynGridLayout::QwtDynGridLayout( QWidget *parent,
59        int margin, int spacing ):
60    QLayout( parent )
61{
62    init();
63
64    setSpacing( spacing );
65    setMargin( margin );
66}
67
68/*!
69  \param spacing Spacing
70*/
71
72QwtDynGridLayout::QwtDynGridLayout( int spacing )
73{
74    init();
75    setSpacing( spacing );
76}
77
78/*!
79  Initialize the layout with default values.
80*/
81void QwtDynGridLayout::init()
82{
83    d_data = new QwtDynGridLayout::PrivateData;
84    d_data->maxCols = d_data->numRows = d_data->numCols = 0;
85    d_data->expanding = 0;
86}
87
88//! Destructor
89
90QwtDynGridLayout::~QwtDynGridLayout()
91{
92    for ( int i = 0; i < d_data->itemList.size(); i++ )
93        delete d_data->itemList[i];
94
95    delete d_data;
96}
97
98//! Invalidate all internal caches
99void QwtDynGridLayout::invalidate()
100{
101    d_data->isDirty = true;
102    QLayout::invalidate();
103}
104
105/*!
106  Limit the number of columns.
107  \param maxCols upper limit, 0 means unlimited
108  \sa maxCols()
109*/
110void QwtDynGridLayout::setMaxCols( uint maxCols )
111{
112    d_data->maxCols = maxCols;
113}
114
115/*!
116  Return the upper limit for the number of columns.
117  0 means unlimited, what is the default.
118  \sa setMaxCols()
119*/
120
121uint QwtDynGridLayout::maxCols() const
122{
123    return d_data->maxCols;
124}
125
126//! Adds item to the next free position.
127
128void QwtDynGridLayout::addItem( QLayoutItem *item )
129{
130    d_data->itemList.append( item );
131    invalidate();
132}
133
134/*!
135  \return true if this layout is empty.
136*/
137
138bool QwtDynGridLayout::isEmpty() const
139{
140    return d_data->itemList.isEmpty();
141}
142
143/*!
144  \return number of layout items
145*/
146
147uint QwtDynGridLayout::itemCount() const
148{
149    return d_data->itemList.count();
150}
151
152/*!
153  Find the item at a spcific index
154
155  \param index Index
156  \sa takeAt()
157*/
158QLayoutItem *QwtDynGridLayout::itemAt( int index ) const
159{
160    if ( index < 0 || index >= d_data->itemList.count() )
161        return NULL;
162
163    return d_data->itemList.at( index );
164}
165
166/*!
167  Find the item at a spcific index and remove it from the layout
168
169  \param index Index
170  \sa itemAt()
171*/
172QLayoutItem *QwtDynGridLayout::takeAt( int index )
173{
174    if ( index < 0 || index >= d_data->itemList.count() )
175        return NULL;
176
177    d_data->isDirty = true;
178    return d_data->itemList.takeAt( index );
179}
180
181//! \return Number of items in the layout
182int QwtDynGridLayout::count() const
183{
184    return d_data->itemList.count();
185}
186
187/*!
188  Set whether this layout can make use of more space than sizeHint().
189  A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only
190  one dimension, while Qt::Vertical | Qt::Horizontal means that it wants
191  to grow in both dimensions. The default value is 0.
192
193  \param expanding Or'd orientations
194  \sa expandingDirections()
195*/
196void QwtDynGridLayout::setExpandingDirections( Qt::Orientations expanding )
197{
198    d_data->expanding = expanding;
199}
200
201/*!
202  Returns whether this layout can make use of more space than sizeHint().
203  A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only
204  one dimension, while Qt::Vertical | Qt::Horizontal means that it wants
205  to grow in both dimensions.
206  \sa setExpandingDirections()
207*/
208Qt::Orientations QwtDynGridLayout::expandingDirections() const
209{
210    return d_data->expanding;
211}
212
213/*!
214  Reorganizes columns and rows and resizes managed widgets within
215  the rectangle rect.
216
217  \param rect Layout geometry
218*/
219void QwtDynGridLayout::setGeometry( const QRect &rect )
220{
221    QLayout::setGeometry( rect );
222
223    if ( isEmpty() )
224        return;
225
226    d_data->numCols = columnsForWidth( rect.width() );
227    d_data->numRows = itemCount() / d_data->numCols;
228    if ( itemCount() % d_data->numCols )
229        d_data->numRows++;
230
231    QList<QRect> itemGeometries = layoutItems( rect, d_data->numCols );
232
233    int index = 0;
234    for ( QList<QLayoutItem*>::iterator it = d_data->itemList.begin();
235        it != d_data->itemList.end(); ++it )
236    {
237        QWidget *w = ( *it )->widget();
238        if ( w )
239        {
240            w->setGeometry( itemGeometries[index] );
241            index++;
242        }
243    }
244}
245
246/*!
247  Calculate the number of columns for a given width. It tries to
248  use as many columns as possible (limited by maxCols())
249
250  \param width Available width for all columns
251  \sa maxCols(), setMaxCols()
252*/
253
254uint QwtDynGridLayout::columnsForWidth( int width ) const
255{
256    if ( isEmpty() )
257        return 0;
258
259    const int maxCols = ( d_data->maxCols > 0 ) ? d_data->maxCols : itemCount();
260    if ( maxRowWidth( maxCols ) <= width )
261        return maxCols;
262
263    for ( int numCols = 2; numCols <= maxCols; numCols++ )
264    {
265        const int rowWidth = maxRowWidth( numCols );
266        if ( rowWidth > width )
267            return numCols - 1;
268    }
269
270    return 1; // At least 1 column
271}
272
273/*!
274  Calculate the width of a layout for a given number of
275  columns.
276
277  \param numCols Given number of columns
278  \param itemWidth Array of the width hints for all items
279*/
280int QwtDynGridLayout::maxRowWidth( int numCols ) const
281{
282    int col;
283
284    QVector<int> colWidth( numCols );
285    for ( col = 0; col < numCols; col++ )
286        colWidth[col] = 0;
287
288    if ( d_data->isDirty )
289        d_data->updateLayoutCache();
290
291    for ( int index = 0;
292        index < d_data->itemSizeHints.count(); index++ )
293    {
294        col = index % numCols;
295        colWidth[col] = qMax( colWidth[col],
296            d_data->itemSizeHints[int( index )].width() );
297    }
298
299    int rowWidth = 2 * margin() + ( numCols - 1 ) * spacing();
300    for ( col = 0; col < numCols; col++ )
301        rowWidth += colWidth[col];
302
303    return rowWidth;
304}
305
306/*!
307  \return the maximum width of all layout items
308*/
309int QwtDynGridLayout::maxItemWidth() const
310{
311    if ( isEmpty() )
312        return 0;
313
314    if ( d_data->isDirty )
315        d_data->updateLayoutCache();
316
317    int w = 0;
318    for ( int i = 0; i < d_data->itemSizeHints.count(); i++ )
319    {
320        const int itemW = d_data->itemSizeHints[i].width();
321        if ( itemW > w )
322            w = itemW;
323    }
324
325    return w;
326}
327
328/*!
329  Calculate the geometries of the layout items for a layout
330  with numCols columns and a given rect.
331
332  \param rect Rect where to place the items
333  \param numCols Number of columns
334  \return item geometries
335*/
336
337QList<QRect> QwtDynGridLayout::layoutItems( const QRect &rect,
338    uint numCols ) const
339{
340    QList<QRect> itemGeometries;
341    if ( numCols == 0 || isEmpty() )
342        return itemGeometries;
343
344    uint numRows = itemCount() / numCols;
345    if ( numRows % itemCount() )
346        numRows++;
347
348    QVector<int> rowHeight( numRows );
349    QVector<int> colWidth( numCols );
350
351    layoutGrid( numCols, rowHeight, colWidth );
352
353    bool expandH, expandV;
354    expandH = expandingDirections() & Qt::Horizontal;
355    expandV = expandingDirections() & Qt::Vertical;
356
357    if ( expandH || expandV )
358        stretchGrid( rect, numCols, rowHeight, colWidth );
359
360    const int maxCols = d_data->maxCols;
361    d_data->maxCols = numCols;
362    const QRect alignedRect = alignmentRect( rect );
363    d_data->maxCols = maxCols;
364
365    const int xOffset = expandH ? 0 : alignedRect.x();
366    const int yOffset = expandV ? 0 : alignedRect.y();
367
368    QVector<int> colX( numCols );
369    QVector<int> rowY( numRows );
370
371    const int xySpace = spacing();
372
373    rowY[0] = yOffset + margin();
374    for ( int r = 1; r < ( int )numRows; r++ )
375        rowY[r] = rowY[r-1] + rowHeight[r-1] + xySpace;
376
377    colX[0] = xOffset + margin();
378    for ( int c = 1; c < ( int )numCols; c++ )
379        colX[c] = colX[c-1] + colWidth[c-1] + xySpace;
380
381    const int itemCount = d_data->itemList.size();
382    for ( int i = 0; i < itemCount; i++ )
383    {
384        const int row = i / numCols;
385        const int col = i % numCols;
386
387        QRect itemGeometry( colX[col], rowY[row],
388            colWidth[col], rowHeight[row] );
389        itemGeometries.append( itemGeometry );
390    }
391
392    return itemGeometries;
393}
394
395
396/*!
397  Calculate the dimensions for the columns and rows for a grid
398  of numCols columns.
399
400  \param numCols Number of columns.
401  \param rowHeight Array where to fill in the calculated row heights.
402  \param colWidth Array where to fill in the calculated column widths.
403*/
404
405void QwtDynGridLayout::layoutGrid( uint numCols,
406    QVector<int>& rowHeight, QVector<int>& colWidth ) const
407{
408    if ( numCols <= 0 )
409        return;
410
411    if ( d_data->isDirty )
412        d_data->updateLayoutCache();
413
414    for ( uint index = 0;
415        index < ( uint )d_data->itemSizeHints.count(); index++ )
416    {
417        const int row = index / numCols;
418        const int col = index % numCols;
419
420        const QSize &size = d_data->itemSizeHints[int( index )];
421
422        rowHeight[row] = ( col == 0 )
423            ? size.height() : qMax( rowHeight[row], size.height() );
424        colWidth[col] = ( row == 0 )
425            ? size.width() : qMax( colWidth[col], size.width() );
426    }
427}
428
429/*!
430  \return true: QwtDynGridLayout implements heightForWidth.
431  \sa heightForWidth()
432*/
433bool QwtDynGridLayout::hasHeightForWidth() const
434{
435    return true;
436}
437
438/*!
439  \return The preferred height for this layout, given the width w.
440  \sa hasHeightForWidth()
441*/
442int QwtDynGridLayout::heightForWidth( int width ) const
443{
444    if ( isEmpty() )
445        return 0;
446
447    const uint numCols = columnsForWidth( width );
448    uint numRows = itemCount() / numCols;
449    if ( itemCount() % numCols )
450        numRows++;
451
452    QVector<int> rowHeight( numRows );
453    QVector<int> colWidth( numCols );
454
455    layoutGrid( numCols, rowHeight, colWidth );
456
457    int h = 2 * margin() + ( numRows - 1 ) * spacing();
458    for ( int row = 0; row < ( int )numRows; row++ )
459        h += rowHeight[row];
460
461    return h;
462}
463
464/*!
465  Stretch columns in case of expanding() & QSizePolicy::Horizontal and
466  rows in case of expanding() & QSizePolicy::Vertical to fill the entire
467  rect. Rows and columns are stretched with the same factor.
468
469  \sa setExpanding(), expanding()
470*/
471void QwtDynGridLayout::stretchGrid( const QRect &rect,
472    uint numCols, QVector<int>& rowHeight, QVector<int>& colWidth ) const
473{
474    if ( numCols == 0 || isEmpty() )
475        return;
476
477    bool expandH, expandV;
478    expandH = expandingDirections() & Qt::Horizontal;
479    expandV = expandingDirections() & Qt::Vertical;
480
481    if ( expandH )
482    {
483        int xDelta = rect.width() - 2 * margin() - ( numCols - 1 ) * spacing();
484        for ( int col = 0; col < ( int )numCols; col++ )
485            xDelta -= colWidth[col];
486
487        if ( xDelta > 0 )
488        {
489            for ( int col = 0; col < ( int )numCols; col++ )
490            {
491                const int space = xDelta / ( numCols - col );
492                colWidth[col] += space;
493                xDelta -= space;
494            }
495        }
496    }
497
498    if ( expandV )
499    {
500        uint numRows = itemCount() / numCols;
501        if ( itemCount() % numCols )
502            numRows++;
503
504        int yDelta = rect.height() - 2 * margin() - ( numRows - 1 ) * spacing();
505        for ( int row = 0; row < ( int )numRows; row++ )
506            yDelta -= rowHeight[row];
507
508        if ( yDelta > 0 )
509        {
510            for ( int row = 0; row < ( int )numRows; row++ )
511            {
512                const int space = yDelta / ( numRows - row );
513                rowHeight[row] += space;
514                yDelta -= space;
515            }
516        }
517    }
518}
519
520/*!
521   Return the size hint. If maxCols() > 0 it is the size for
522   a grid with maxCols() columns, otherwise it is the size for
523   a grid with only one row.
524
525   \sa maxCols(), setMaxCols()
526*/
527QSize QwtDynGridLayout::sizeHint() const
528{
529    if ( isEmpty() )
530        return QSize();
531
532    const uint numCols = ( d_data->maxCols > 0 ) ? d_data->maxCols : itemCount();
533    uint numRows = itemCount() / numCols;
534    if ( itemCount() % numCols )
535        numRows++;
536
537    QVector<int> rowHeight( numRows );
538    QVector<int> colWidth( numCols );
539
540    layoutGrid( numCols, rowHeight, colWidth );
541
542    int h = 2 * margin() + ( numRows - 1 ) * spacing();
543    for ( int row = 0; row < ( int )numRows; row++ )
544        h += rowHeight[row];
545
546    int w = 2 * margin() + ( numCols - 1 ) * spacing();
547    for ( int col = 0; col < ( int )numCols; col++ )
548        w += colWidth[col];
549
550    return QSize( w, h );
551}
552
553/*!
554  \return Number of rows of the current layout.
555  \sa numCols()
556  \warning The number of rows might change whenever the geometry changes
557*/
558uint QwtDynGridLayout::numRows() const
559{
560    return d_data->numRows;
561}
562
563/*!
564  \return Number of columns of the current layout.
565  \sa numRows()
566  \warning The number of columns might change whenever the geometry changes
567*/
568uint QwtDynGridLayout::numCols() const
569{
570    return d_data->numCols;
571}
Note: See TracBrowser for help on using the repository browser.