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

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

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 14.1 KB
RevLine 
[4271]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"
[8127]12#include <qvector.h>
[4271]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
[8127]27 uint maxColumns;
[4271]28 uint numRows;
[8127]29 uint numColumns;
[4271]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;
[8127]84 d_data->maxColumns = d_data->numRows = d_data->numColumns = 0;
[4271]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.
[8127]107 \param maxColumns upper limit, 0 means unlimited
108 \sa maxColumns()
[4271]109*/
[8127]110void QwtDynGridLayout::setMaxColumns( uint maxColumns )
[4271]111{
[8127]112 d_data->maxColumns = maxColumns;
[4271]113}
114
115/*!
[8127]116 \brief Return the upper limit for the number of columns.
117
[4271]118 0 means unlimited, what is the default.
[8127]119
120 \return Upper limit for the number of columns
121 \sa setMaxColumns()
[4271]122*/
[8127]123uint QwtDynGridLayout::maxColumns() const
[4271]124{
[8127]125 return d_data->maxColumns;
[4271]126}
127
[8127]128/*!
129 \brief Add an item to the next free position.
130 \param item Layout item
131 */
[4271]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/*!
[8127]155 Find the item at a specific index
[4271]156
157 \param index Index
[8127]158 \return Item at a specific index
[4271]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/*!
[8127]170 Find the item at a specific index and remove it from the layout
[4271]171
172 \param index Index
[8127]173 \return Layout item, removed from the layout
[4271]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/*!
[8127]206 \brief Returns whether this layout can make use of more space than sizeHint().
207
[4271]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.
[8127]211
212 \return Orientations, where the layout expands
[4271]213 \sa setExpandingDirections()
214*/
215Qt::Orientations QwtDynGridLayout::expandingDirections() const
216{
217 return d_data->expanding;
218}
219
220/*!
[8127]221 Reorganizes columns and rows and resizes managed items within
222 a rectangle.
[4271]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
[8127]233 d_data->numColumns = columnsForWidth( rect.width() );
234 d_data->numRows = itemCount() / d_data->numColumns;
235 if ( itemCount() % d_data->numColumns )
[4271]236 d_data->numRows++;
237
[8127]238 QList<QRect> itemGeometries = layoutItems( rect, d_data->numColumns );
[4271]239
240 int index = 0;
241 for ( QList<QLayoutItem*>::iterator it = d_data->itemList.begin();
242 it != d_data->itemList.end(); ++it )
243 {
[8127]244 ( *it )->setGeometry( itemGeometries[index] );
245 index++;
[4271]246 }
247}
248
249/*!
[8127]250 \brief Calculate the number of columns for a given width.
[4271]251
[8127]252 The calculation tries to use as many columns as possible
253 ( limited by maxColumns() )
254
[4271]255 \param width Available width for all columns
[8127]256 \return Number of columns for a given width
257
258 \sa maxColumns(), setMaxColumns()
[4271]259*/
260uint QwtDynGridLayout::columnsForWidth( int width ) const
261{
262 if ( isEmpty() )
263 return 0;
264
[8127]265 uint maxColumns = itemCount();
266 if ( d_data->maxColumns > 0 )
267 maxColumns = qMin( d_data->maxColumns, maxColumns );
[4271]268
[8127]269 if ( maxRowWidth( maxColumns ) <= width )
270 return maxColumns;
271
272 for ( uint numColumns = 2; numColumns <= maxColumns; numColumns++ )
[4271]273 {
[8127]274 const int rowWidth = maxRowWidth( numColumns );
[4271]275 if ( rowWidth > width )
[8127]276 return numColumns - 1;
[4271]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
[8127]286 \param numColumns Given number of columns
[4271]287 \param itemWidth Array of the width hints for all items
288*/
[8127]289int QwtDynGridLayout::maxRowWidth( int numColumns ) const
[4271]290{
291 int col;
292
[8127]293 QVector<int> colWidth( numColumns );
294 for ( col = 0; col < numColumns; col++ )
[4271]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 {
[8127]303 col = index % numColumns;
[4271]304 colWidth[col] = qMax( colWidth[col],
305 d_data->itemSizeHints[int( index )].width() );
306 }
307
[8127]308 int rowWidth = 2 * margin() + ( numColumns - 1 ) * spacing();
309 for ( col = 0; col < numColumns; col++ )
[4271]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
[8127]339 with numColumns columns and a given rectangle.
[4271]340
341 \param rect Rect where to place the items
[8127]342 \param numColumns Number of columns
[4271]343 \return item geometries
344*/
345
346QList<QRect> QwtDynGridLayout::layoutItems( const QRect &rect,
[8127]347 uint numColumns ) const
[4271]348{
349 QList<QRect> itemGeometries;
[8127]350 if ( numColumns == 0 || isEmpty() )
[4271]351 return itemGeometries;
352
[8127]353 uint numRows = itemCount() / numColumns;
354 if ( numColumns % itemCount() )
[4271]355 numRows++;
356
[8127]357 if ( numRows == 0 )
358 return itemGeometries;
359
[4271]360 QVector<int> rowHeight( numRows );
[8127]361 QVector<int> colWidth( numColumns );
[4271]362
[8127]363 layoutGrid( numColumns, rowHeight, colWidth );
[4271]364
365 bool expandH, expandV;
366 expandH = expandingDirections() & Qt::Horizontal;
367 expandV = expandingDirections() & Qt::Vertical;
368
369 if ( expandH || expandV )
[8127]370 stretchGrid( rect, numColumns, rowHeight, colWidth );
[4271]371
[8127]372 const int maxColumns = d_data->maxColumns;
373 d_data->maxColumns = numColumns;
[4271]374 const QRect alignedRect = alignmentRect( rect );
[8127]375 d_data->maxColumns = maxColumns;
[4271]376
377 const int xOffset = expandH ? 0 : alignedRect.x();
378 const int yOffset = expandV ? 0 : alignedRect.y();
379
[8127]380 QVector<int> colX( numColumns );
[4271]381 QVector<int> rowY( numRows );
382
383 const int xySpace = spacing();
384
385 rowY[0] = yOffset + margin();
[8127]386 for ( uint r = 1; r < numRows; r++ )
[4271]387 rowY[r] = rowY[r-1] + rowHeight[r-1] + xySpace;
388
389 colX[0] = xOffset + margin();
[8127]390 for ( uint c = 1; c < numColumns; c++ )
[4271]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 {
[8127]396 const int row = i / numColumns;
397 const int col = i % numColumns;
[4271]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
[8127]410 of numColumns columns.
[4271]411
[8127]412 \param numColumns Number of columns.
[4271]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
[8127]417void QwtDynGridLayout::layoutGrid( uint numColumns,
[4271]418 QVector<int>& rowHeight, QVector<int>& colWidth ) const
419{
[8127]420 if ( numColumns <= 0 )
[4271]421 return;
422
423 if ( d_data->isDirty )
424 d_data->updateLayoutCache();
425
[8127]426 for ( int index = 0; index < d_data->itemSizeHints.count(); index++ )
[4271]427 {
[8127]428 const int row = index / numColumns;
429 const int col = index % numColumns;
[4271]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/*!
[8127]441 \return true: QwtDynGridLayout implements heightForWidth().
[4271]442 \sa heightForWidth()
443*/
444bool QwtDynGridLayout::hasHeightForWidth() const
445{
446 return true;
447}
448
449/*!
[8127]450 \return The preferred height for this layout, given a width.
[4271]451 \sa hasHeightForWidth()
452*/
453int QwtDynGridLayout::heightForWidth( int width ) const
454{
455 if ( isEmpty() )
456 return 0;
457
[8127]458 const uint numColumns = columnsForWidth( width );
459 uint numRows = itemCount() / numColumns;
460 if ( itemCount() % numColumns )
[4271]461 numRows++;
462
463 QVector<int> rowHeight( numRows );
[8127]464 QVector<int> colWidth( numColumns );
[4271]465
[8127]466 layoutGrid( numColumns, rowHeight, colWidth );
[4271]467
468 int h = 2 * margin() + ( numRows - 1 ) * spacing();
[8127]469 for ( uint row = 0; row < numRows; row++ )
[4271]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
[8127]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
[4271]485 \sa setExpanding(), expanding()
486*/
487void QwtDynGridLayout::stretchGrid( const QRect &rect,
[8127]488 uint numColumns, QVector<int>& rowHeight, QVector<int>& colWidth ) const
[4271]489{
[8127]490 if ( numColumns == 0 || isEmpty() )
[4271]491 return;
492
493 bool expandH, expandV;
494 expandH = expandingDirections() & Qt::Horizontal;
495 expandV = expandingDirections() & Qt::Vertical;
496
497 if ( expandH )
498 {
[8127]499 int xDelta = rect.width() - 2 * margin() - ( numColumns - 1 ) * spacing();
500 for ( uint col = 0; col < numColumns; col++ )
[4271]501 xDelta -= colWidth[col];
502
503 if ( xDelta > 0 )
504 {
[8127]505 for ( uint col = 0; col < numColumns; col++ )
[4271]506 {
[8127]507 const int space = xDelta / ( numColumns - col );
[4271]508 colWidth[col] += space;
509 xDelta -= space;
510 }
511 }
512 }
513
514 if ( expandV )
515 {
[8127]516 uint numRows = itemCount() / numColumns;
517 if ( itemCount() % numColumns )
[4271]518 numRows++;
519
520 int yDelta = rect.height() - 2 * margin() - ( numRows - 1 ) * spacing();
[8127]521 for ( uint row = 0; row < numRows; row++ )
[4271]522 yDelta -= rowHeight[row];
523
524 if ( yDelta > 0 )
525 {
[8127]526 for ( uint row = 0; row < numRows; row++ )
[4271]527 {
528 const int space = yDelta / ( numRows - row );
529 rowHeight[row] += space;
530 yDelta -= space;
531 }
532 }
533 }
534}
535
536/*!
[8127]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
[4271]539 a grid with only one row.
540
[8127]541 \return Size hint
542 \sa maxColumns(), setMaxColumns()
[4271]543*/
544QSize QwtDynGridLayout::sizeHint() const
545{
546 if ( isEmpty() )
547 return QSize();
548
[8127]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 )
[4271]555 numRows++;
556
557 QVector<int> rowHeight( numRows );
[8127]558 QVector<int> colWidth( numColumns );
[4271]559
[8127]560 layoutGrid( numColumns, rowHeight, colWidth );
[4271]561
562 int h = 2 * margin() + ( numRows - 1 ) * spacing();
[8127]563 for ( uint row = 0; row < numRows; row++ )
[4271]564 h += rowHeight[row];
565
[8127]566 int w = 2 * margin() + ( numColumns - 1 ) * spacing();
567 for ( uint col = 0; col < numColumns; col++ )
[4271]568 w += colWidth[col];
569
570 return QSize( w, h );
571}
572
573/*!
574 \return Number of rows of the current layout.
[8127]575 \sa numColumns()
[4271]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*/
[8127]588uint QwtDynGridLayout::numColumns() const
[4271]589{
[8127]590 return d_data->numColumns;
[4271]591}
Note: See TracBrowser for help on using the repository browser.