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

Last change on this file since 8127 was 8127, checked in by stoecker, 2 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 14.1 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 <qvector.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 maxColumns;
28    uint numRows;
29    uint numColumns;
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->maxColumns = d_data->numRows = d_data->numColumns = 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 maxColumns upper limit, 0 means unlimited
108  \sa maxColumns()
109*/
110void QwtDynGridLayout::setMaxColumns( uint maxColumns )
111{
112    d_data->maxColumns = maxColumns;
113}
114
115/*!
116  \brief Return the upper limit for the number of columns.
117
118  0 means unlimited, what is the default.
119
120  \return Upper limit for the number of columns
121  \sa setMaxColumns()
122*/
123uint QwtDynGridLayout::maxColumns() const
124{
125    return d_data->maxColumns;
126}
127
128/*!
129  \brief Add an item to the next free position.
130  \param item Layout item
131 */
132void QwtDynGridLayout::addItem( QLayoutItem *item )
133{
134    d_data->itemList.append( item );
135    invalidate();
136}
137
138/*!
139  \return true if this layout is empty.
140*/
141bool QwtDynGridLayout::isEmpty() const
142{
143    return d_data->itemList.isEmpty();
144}
145
146/*!
147  \return number of layout items
148*/
149uint QwtDynGridLayout::itemCount() const
150{
151    return d_data->itemList.count();
152}
153
154/*!
155  Find the item at a specific index
156
157  \param index Index
158  \return Item at a specific index
159  \sa takeAt()
160*/
161QLayoutItem *QwtDynGridLayout::itemAt( int index ) const
162{
163    if ( index < 0 || index >= d_data->itemList.count() )
164        return NULL;
165
166    return d_data->itemList.at( index );
167}
168
169/*!
170  Find the item at a specific index and remove it from the layout
171
172  \param index Index
173  \return Layout item, removed from the layout
174  \sa itemAt()
175*/
176QLayoutItem *QwtDynGridLayout::takeAt( int index )
177{
178    if ( index < 0 || index >= d_data->itemList.count() )
179        return NULL;
180
181    d_data->isDirty = true;
182    return d_data->itemList.takeAt( index );
183}
184
185//! \return Number of items in the layout
186int QwtDynGridLayout::count() const
187{
188    return d_data->itemList.count();
189}
190
191/*!
192  Set whether this layout can make use of more space than sizeHint().
193  A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only
194  one dimension, while Qt::Vertical | Qt::Horizontal means that it wants
195  to grow in both dimensions. The default value is 0.
196
197  \param expanding Or'd orientations
198  \sa expandingDirections()
199*/
200void QwtDynGridLayout::setExpandingDirections( Qt::Orientations expanding )
201{
202    d_data->expanding = expanding;
203}
204
205/*!
206  \brief Returns whether this layout can make use of more space than sizeHint().
207
208  A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only
209  one dimension, while Qt::Vertical | Qt::Horizontal means that it wants
210  to grow in both dimensions.
211
212  \return Orientations, where the layout expands
213  \sa setExpandingDirections()
214*/
215Qt::Orientations QwtDynGridLayout::expandingDirections() const
216{
217    return d_data->expanding;
218}
219
220/*!
221  Reorganizes columns and rows and resizes managed items within
222  a rectangle.
223
224  \param rect Layout geometry
225*/
226void QwtDynGridLayout::setGeometry( const QRect &rect )
227{
228    QLayout::setGeometry( rect );
229
230    if ( isEmpty() )
231        return;
232
233    d_data->numColumns = columnsForWidth( rect.width() );
234    d_data->numRows = itemCount() / d_data->numColumns;
235    if ( itemCount() % d_data->numColumns )
236        d_data->numRows++;
237
238    QList<QRect> itemGeometries = layoutItems( rect, d_data->numColumns );
239
240    int index = 0;
241    for ( QList<QLayoutItem*>::iterator it = d_data->itemList.begin();
242        it != d_data->itemList.end(); ++it )
243    {
244        ( *it )->setGeometry( itemGeometries[index] );
245        index++;
246    }
247}
248
249/*!
250  \brief Calculate the number of columns for a given width.
251
252  The calculation tries to use as many columns as possible
253  ( limited by maxColumns() )
254
255  \param width Available width for all columns
256  \return Number of columns for a given width
257
258  \sa maxColumns(), setMaxColumns()
259*/
260uint QwtDynGridLayout::columnsForWidth( int width ) const
261{
262    if ( isEmpty() )
263        return 0;
264
265    uint maxColumns = itemCount();
266    if ( d_data->maxColumns > 0 ) 
267        maxColumns = qMin( d_data->maxColumns, maxColumns );
268
269    if ( maxRowWidth( maxColumns ) <= width )
270        return maxColumns;
271
272    for ( uint numColumns = 2; numColumns <= maxColumns; numColumns++ )
273    {
274        const int rowWidth = maxRowWidth( numColumns );
275        if ( rowWidth > width )
276            return numColumns - 1;
277    }
278
279    return 1; // At least 1 column
280}
281
282/*!
283  Calculate the width of a layout for a given number of
284  columns.
285
286  \param numColumns Given number of columns
287  \param itemWidth Array of the width hints for all items
288*/
289int QwtDynGridLayout::maxRowWidth( int numColumns ) const
290{
291    int col;
292
293    QVector<int> colWidth( numColumns );
294    for ( col = 0; col < numColumns; col++ )
295        colWidth[col] = 0;
296
297    if ( d_data->isDirty )
298        d_data->updateLayoutCache();
299
300    for ( int index = 0;
301        index < d_data->itemSizeHints.count(); index++ )
302    {
303        col = index % numColumns;
304        colWidth[col] = qMax( colWidth[col],
305            d_data->itemSizeHints[int( index )].width() );
306    }
307
308    int rowWidth = 2 * margin() + ( numColumns - 1 ) * spacing();
309    for ( col = 0; col < numColumns; col++ )
310        rowWidth += colWidth[col];
311
312    return rowWidth;
313}
314
315/*!
316  \return the maximum width of all layout items
317*/
318int QwtDynGridLayout::maxItemWidth() const
319{
320    if ( isEmpty() )
321        return 0;
322
323    if ( d_data->isDirty )
324        d_data->updateLayoutCache();
325
326    int w = 0;
327    for ( int i = 0; i < d_data->itemSizeHints.count(); i++ )
328    {
329        const int itemW = d_data->itemSizeHints[i].width();
330        if ( itemW > w )
331            w = itemW;
332    }
333
334    return w;
335}
336
337/*!
338  Calculate the geometries of the layout items for a layout
339  with numColumns columns and a given rectangle.
340
341  \param rect Rect where to place the items
342  \param numColumns Number of columns
343  \return item geometries
344*/
345
346QList<QRect> QwtDynGridLayout::layoutItems( const QRect &rect,
347    uint numColumns ) const
348{
349    QList<QRect> itemGeometries;
350    if ( numColumns == 0 || isEmpty() )
351        return itemGeometries;
352
353    uint numRows = itemCount() / numColumns;
354    if ( numColumns % itemCount() )
355        numRows++;
356
357    if ( numRows == 0 )
358        return itemGeometries;
359
360    QVector<int> rowHeight( numRows );
361    QVector<int> colWidth( numColumns );
362
363    layoutGrid( numColumns, rowHeight, colWidth );
364
365    bool expandH, expandV;
366    expandH = expandingDirections() & Qt::Horizontal;
367    expandV = expandingDirections() & Qt::Vertical;
368
369    if ( expandH || expandV )
370        stretchGrid( rect, numColumns, rowHeight, colWidth );
371
372    const int maxColumns = d_data->maxColumns;
373    d_data->maxColumns = numColumns;
374    const QRect alignedRect = alignmentRect( rect );
375    d_data->maxColumns = maxColumns;
376
377    const int xOffset = expandH ? 0 : alignedRect.x();
378    const int yOffset = expandV ? 0 : alignedRect.y();
379
380    QVector<int> colX( numColumns );
381    QVector<int> rowY( numRows );
382
383    const int xySpace = spacing();
384
385    rowY[0] = yOffset + margin();
386    for ( uint r = 1; r < numRows; r++ )
387        rowY[r] = rowY[r-1] + rowHeight[r-1] + xySpace;
388
389    colX[0] = xOffset + margin();
390    for ( uint c = 1; c < numColumns; c++ )
391        colX[c] = colX[c-1] + colWidth[c-1] + xySpace;
392
393    const int itemCount = d_data->itemList.size();
394    for ( int i = 0; i < itemCount; i++ )
395    {
396        const int row = i / numColumns;
397        const int col = i % numColumns;
398
399        QRect itemGeometry( colX[col], rowY[row],
400            colWidth[col], rowHeight[row] );
401        itemGeometries.append( itemGeometry );
402    }
403
404    return itemGeometries;
405}
406
407
408/*!
409  Calculate the dimensions for the columns and rows for a grid
410  of numColumns columns.
411
412  \param numColumns Number of columns.
413  \param rowHeight Array where to fill in the calculated row heights.
414  \param colWidth Array where to fill in the calculated column widths.
415*/
416
417void QwtDynGridLayout::layoutGrid( uint numColumns,
418    QVector<int>& rowHeight, QVector<int>& colWidth ) const
419{
420    if ( numColumns <= 0 )
421        return;
422
423    if ( d_data->isDirty )
424        d_data->updateLayoutCache();
425
426    for ( int index = 0; index < d_data->itemSizeHints.count(); index++ )
427    {
428        const int row = index / numColumns;
429        const int col = index % numColumns;
430
431        const QSize &size = d_data->itemSizeHints[int( index )];
432
433        rowHeight[row] = ( col == 0 )
434            ? size.height() : qMax( rowHeight[row], size.height() );
435        colWidth[col] = ( row == 0 )
436            ? size.width() : qMax( colWidth[col], size.width() );
437    }
438}
439
440/*!
441  \return true: QwtDynGridLayout implements heightForWidth().
442  \sa heightForWidth()
443*/
444bool QwtDynGridLayout::hasHeightForWidth() const
445{
446    return true;
447}
448
449/*!
450  \return The preferred height for this layout, given a width.
451  \sa hasHeightForWidth()
452*/
453int QwtDynGridLayout::heightForWidth( int width ) const
454{
455    if ( isEmpty() )
456        return 0;
457
458    const uint numColumns = columnsForWidth( width );
459    uint numRows = itemCount() / numColumns;
460    if ( itemCount() % numColumns )
461        numRows++;
462
463    QVector<int> rowHeight( numRows );
464    QVector<int> colWidth( numColumns );
465
466    layoutGrid( numColumns, rowHeight, colWidth );
467
468    int h = 2 * margin() + ( numRows - 1 ) * spacing();
469    for ( uint row = 0; row < numRows; row++ )
470        h += rowHeight[row];
471
472    return h;
473}
474
475/*!
476  Stretch columns in case of expanding() & QSizePolicy::Horizontal and
477  rows in case of expanding() & QSizePolicy::Vertical to fill the entire
478  rect. Rows and columns are stretched with the same factor.
479
480  \param rect Bounding rectangle
481  \param numColumns Number of columns
482  \param rowHeight Array to be filled with the calculated row heights
483  \param colWidth Array to be filled with the calculated column widths
484
485  \sa setExpanding(), expanding()
486*/
487void QwtDynGridLayout::stretchGrid( const QRect &rect,
488    uint numColumns, QVector<int>& rowHeight, QVector<int>& colWidth ) const
489{
490    if ( numColumns == 0 || isEmpty() )
491        return;
492
493    bool expandH, expandV;
494    expandH = expandingDirections() & Qt::Horizontal;
495    expandV = expandingDirections() & Qt::Vertical;
496
497    if ( expandH )
498    {
499        int xDelta = rect.width() - 2 * margin() - ( numColumns - 1 ) * spacing();
500        for ( uint col = 0; col < numColumns; col++ )
501            xDelta -= colWidth[col];
502
503        if ( xDelta > 0 )
504        {
505            for ( uint col = 0; col < numColumns; col++ )
506            {
507                const int space = xDelta / ( numColumns - col );
508                colWidth[col] += space;
509                xDelta -= space;
510            }
511        }
512    }
513
514    if ( expandV )
515    {
516        uint numRows = itemCount() / numColumns;
517        if ( itemCount() % numColumns )
518            numRows++;
519
520        int yDelta = rect.height() - 2 * margin() - ( numRows - 1 ) * spacing();
521        for ( uint row = 0; row < numRows; row++ )
522            yDelta -= rowHeight[row];
523
524        if ( yDelta > 0 )
525        {
526            for ( uint row = 0; row < numRows; row++ )
527            {
528                const int space = yDelta / ( numRows - row );
529                rowHeight[row] += space;
530                yDelta -= space;
531            }
532        }
533    }
534}
535
536/*!
537   Return the size hint. If maxColumns() > 0 it is the size for
538   a grid with maxColumns() columns, otherwise it is the size for
539   a grid with only one row.
540
541   \return Size hint
542   \sa maxColumns(), setMaxColumns()
543*/
544QSize QwtDynGridLayout::sizeHint() const
545{
546    if ( isEmpty() )
547        return QSize();
548
549    uint numColumns = itemCount();
550    if ( d_data->maxColumns > 0 )
551        numColumns = qMin( d_data->maxColumns, numColumns );
552
553    uint numRows = itemCount() / numColumns;
554    if ( itemCount() % numColumns )
555        numRows++;
556
557    QVector<int> rowHeight( numRows );
558    QVector<int> colWidth( numColumns );
559
560    layoutGrid( numColumns, rowHeight, colWidth );
561
562    int h = 2 * margin() + ( numRows - 1 ) * spacing();
563    for ( uint row = 0; row < numRows; row++ )
564        h += rowHeight[row];
565
566    int w = 2 * margin() + ( numColumns - 1 ) * spacing();
567    for ( uint col = 0; col < numColumns; col++ )
568        w += colWidth[col];
569
570    return QSize( w, h );
571}
572
573/*!
574  \return Number of rows of the current layout.
575  \sa numColumns()
576  \warning The number of rows might change whenever the geometry changes
577*/
578uint QwtDynGridLayout::numRows() const
579{
580    return d_data->numRows;
581}
582
583/*!
584  \return Number of columns of the current layout.
585  \sa numRows()
586  \warning The number of columns might change whenever the geometry changes
587*/
588uint QwtDynGridLayout::numColumns() const
589{
590    return d_data->numColumns;
591}
Note: See TracBrowser for help on using the repository browser.