00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <math.h>
00026
00027 #include <QHash>
00028 #include <QWidget>
00029 #include <QEvent>
00030 #include <QResizeEvent>
00031 #include <QMoveEvent>
00032 #include <QApplication>
00033 #include <QStyle>
00034
00035 #include <klfdefs.h>
00036
00037 #include "klfflowlayout.h"
00038
00039
00040
00041 struct KLFFlowLayoutItem : public QLayoutItem
00042 {
00043 KLFFlowLayoutItem(QLayoutItem *li, Qt::Alignment a = 0)
00044 : QLayoutItem(a), item(li), hstretch(0), vstretch(0)
00045 {
00046 KLF_ASSERT_NOT_NULL(item, "item is NULL! Expect crash!", ;) ;
00047 }
00048 virtual ~KLFFlowLayoutItem();
00049
00050 virtual Qt::Orientations expandingDirections() const
00051 {
00052 return item->expandingDirections();
00053 }
00054 virtual QRect geometry() const
00055 {
00056 klfDbg("geometry") ;
00057 return item->geometry();
00058 }
00059 virtual bool hasHeightForWidth() const
00060 {
00061 return item->hasHeightForWidth();
00062 }
00063 virtual int heightForWidth(int w) const
00064 {
00065 return item->heightForWidth(w);
00066 }
00067 virtual void invalidate()
00068 {
00069 item->invalidate();
00070 }
00071 virtual bool isEmpty() const
00072 {
00073 return item->isEmpty();
00074 }
00075 virtual QLayout *layout()
00076 {
00077 return item->layout();
00078 }
00079 virtual QSize maximumSize() const
00080 {
00081 return item->maximumSize();
00082 }
00083 virtual int minimumHeightForWidth(int w) const
00084 {
00085 return item->minimumHeightForWidth(w);
00086 }
00087 virtual QSize minimumSize() const
00088 {
00089 return item->minimumSize();
00090 }
00091 virtual void setGeometry(const QRect& r)
00092 {
00093 klfDbg("item/widget="<<item->widget()<<"; setGeom: "<<r) ;
00094 item->setGeometry(r);
00095 }
00096 virtual QSize sizeHint() const
00097 {
00098 return item->sizeHint();
00099 }
00100 virtual QSpacerItem * spacerItem()
00101 {
00102 return item->spacerItem();
00103 }
00104 virtual QWidget * widget()
00105 {
00106
00107 return item->widget();
00108 }
00109
00110
00111
00112 QLayoutItem *item;
00113
00114 int hstretch;
00115 int vstretch;
00116 };
00117
00118
00119 KLFFlowLayoutItem::~KLFFlowLayoutItem()
00120 {
00121 }
00122
00123 struct KLFFlowLayoutPrivate
00124 {
00126 KLFFlowLayout * K;
00128 KLFFlowLayoutPrivate(KLFFlowLayout *f)
00129 {
00130 K = f;
00131 mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, NULL);
00132 mainLayout->setContentsMargins(0,0,0,0);
00133 hspc = vspc = -1;
00134 flush = KLFFlowLayout::NoFlush;
00135 geom = effectiveGeom = QRect(0, 0, 640, 480);
00136 marginsSize = QSize(0,0);
00137 hfw_w = -1;
00138 hfw_h = -1;
00139 min_size = QSize(0,0);
00140
00141 dirty = true;
00142 }
00143
00145 QList<KLFFlowLayoutItem*> items;
00146
00148 bool dirty;
00150 QList<QBoxLayout*> layoutLines;
00151
00152 QBoxLayout *mainLayout;
00153
00154 int hspc;
00155 int vspc;
00156 KLFFlowLayout::Flush flush;
00157
00158 QRect geom;
00159 QRect effectiveGeom;
00160 QSize marginsSize;
00161 int hfw_w;
00162 int hfw_h;
00163 QSize min_size;
00164 QSize size_hint;
00165 QSize max_size;
00166
00167
00168
00169
00170 typedef QList<KLFFlowLayoutItem*> ItemLine;
00171
00172 void calc()
00173 {
00174 if (!dirty)
00175 return;
00176
00177 doLayout();
00178 }
00179
00180 void removeItemFromSubLayouts(KLFFlowLayoutItem* fi)
00181 {
00182 int k, n;
00183 QLayoutItem *item;
00184 for (k = 0; k < layoutLines.size(); ++k) {
00185 n = 0;
00186 while ((item = layoutLines[k]->itemAt(n)) != NULL) {
00187 if (item == fi) {
00188 layoutLines[k]->takeAt(n);
00189 return;
00190 }
00191 n++;
00192 }
00193 }
00194 }
00195
00196 void clean()
00197 {
00198 int k;
00199 for (k = 0; k < layoutLines.size(); ++k) {
00200
00201
00202 QLayoutItem *item;
00203 while ((item = layoutLines[k]->takeAt(0)) != NULL) {
00204 KLFFlowLayoutItem *fi = dynamic_cast<KLFFlowLayoutItem*>(item);
00205 if (fi == NULL) {
00206
00207 delete item;
00208 }
00209 }
00210 mainLayout->removeItem(layoutLines[k]);
00211 delete layoutLines[k];
00212 layoutLines[k] = NULL;
00213 }
00214 layoutLines.clear();
00215 dirty = true;
00216 }
00217
00218 struct CalcData
00219 {
00220 QSize minsize;
00221 QSize sizehint;
00222 QSize maxsize;
00223 int height;
00224 };
00225
00226 QList<ItemLine> calcLines(CalcData *data = NULL)
00227 {
00228 QList<ItemLine> lines;
00229
00230 int maxminwidth = 0;
00231 int summinwidth = 0;
00232 int maxwidth = 0;
00233 int maxminheight = 0;
00234 int summinheight = 0;
00235 int maxheight = 0;
00236 int shwidth = 0;
00237
00238 ItemLine line;
00239
00240 QStyle *style = NULL;
00241 QWidget *pwidget = K->parentWidget();
00242 if (pwidget != NULL)
00243 style = pwidget->style();
00244 if (style == NULL)
00245 style = qApp->style();
00246
00247 int x = 0;
00248 int fitwidth = effectiveGeom.width();
00249 int height = 0;
00250 int thislheight = 0;
00251
00252 KLFFlowLayoutItem *item;
00253 KLFFlowLayoutItem *prevItem = NULL;
00254 int k;
00255 for (k = 0; k < items.size(); ++k) {
00256 item = items[k];
00257
00258 QSize mins = item->minimumSize();
00259 QSize maxs = item->maximumSize();
00260 QSize sh = item->sizeHint();
00261
00262 if (item->isEmpty())
00263 continue;
00264
00265 QSizePolicy::ControlTypes t = QSizePolicy::DefaultType;
00266 if (prevItem != NULL)
00267 t = prevItem->controlTypes();
00268
00269 int spaceX = hspc;
00270 if (spaceX == -1)
00271 spaceX = style->combinedLayoutSpacing(t, item->controlTypes(), Qt::Horizontal);
00272
00273 int spaceY = vspc;
00274 if (spaceY == -1)
00275 spaceY = style->combinedLayoutSpacing(t, item->controlTypes(), Qt::Vertical);
00276
00277 if (x + sh.width() > fitwidth) {
00278 lines << line;
00279 shwidth = qMax(shwidth, x);
00280 height += spaceY + thislheight;
00281 thislheight = 0;
00282 line.clear();
00283
00284 x = 0;
00285 }
00286 int nextX = x + sh.width() + spaceX;
00287
00288 line << item;
00289 x = nextX;
00290 maxminwidth = qMax(mins.width(), maxminwidth);
00291 maxminheight = qMax(mins.height(), maxminheight);
00292 summinwidth += mins.width();
00293 summinheight += mins.height();
00294 if (maxwidth < (int)0x7fffffff)
00295 maxwidth += maxs.width() + spaceX;
00296 if (maxheight < (int)0x7fffffff)
00297 maxheight += maxs.height() + spaceY;
00298 thislheight = qMax(item->sizeHint().height(), thislheight);
00299 prevItem = item;
00300 }
00301
00302
00303 height += thislheight;
00304 lines << line;
00305
00306 if (data != NULL) {
00307 data->height = height;
00308
00309
00310
00311
00312
00313
00314
00315 data->minsize = QSize(maxminwidth, height);
00316 data->sizehint = QSize(shwidth, height);
00317
00318
00319
00320 data->maxsize = QSize(maxwidth, maxheight);
00321 }
00322
00323 return lines;
00324 }
00325
00326 void doLayout()
00327 {
00328 KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00329
00330 if (!geom.isValid()) {
00331 klfDbg("geom is not valid yet, returning.") ;
00332 return;
00333 }
00334
00335
00336 clean();
00337
00338 int left, top, right, bottom;
00339 K->getContentsMargins(&left, &top, &right, &bottom);
00340 effectiveGeom = geom.adjusted(+left, +top, -right, -bottom);
00341 marginsSize = QSize(left+right, top+bottom);
00342
00343 klfDbg("geom = "<<geom<<"; mainlayout/setgeom, effGeom="<<effectiveGeom) ;
00344
00345 CalcData sizedat;
00346
00347 QList<ItemLine> lines = calcLines(&sizedat);
00348
00349 klfDbg("calculated lines. minsize="<<sizedat.minsize<<"; maxsize="<<sizedat.maxsize<<", height="
00350 <<sizedat.height) ;
00351
00352 int k, n, i;
00353 int linevstretch;
00354 for (k = 0; k < lines.size(); ++k) {
00355
00356 QBoxLayout *linelyt = new QBoxLayout(QBoxLayout::LeftToRight, NULL);
00357 linelyt->setSpacing(hspc);
00358 linevstretch = 0;
00359 if (flush == KLFFlowLayout::FlushEnd) {
00360
00361 linelyt->addStretch(0x7fffffff);
00362 }
00363 for (n = 0; n < lines[k].size(); ++n) {
00364
00365 Qt::Alignment a = lines[k][n]->alignment();
00366 if (flush == KLFFlowLayout::NoFlush) {
00367
00368 a = (a & ~Qt::AlignHorizontal_Mask);
00369 } else if (flush == KLFFlowLayout::FlushSparse) {
00370 a |= Qt::AlignLeft;
00371 }
00372 lines[k][n]->item->setAlignment(a);
00373 linelyt->addItem(lines[k][n]);
00374 for (i = 0; i < linelyt->count() && linelyt->itemAt(i) != lines[k][n]; ++i)
00375 ;
00376 #if QT_VERSION >= 0x040500
00377
00378 if (i < linelyt->count()) {
00379 linelyt->setStretch(i, lines[k][n]->hstretch);
00380 }
00381 #endif
00382 linevstretch = qMax(linevstretch, lines[k][n]->vstretch);
00383 }
00384 if (flush == KLFFlowLayout::FlushBegin) {
00385
00386 linelyt->addStretch(0x7fffffff);
00387 }
00388 mainLayout->addLayout(linelyt, linevstretch);
00389 layoutLines << linelyt;
00390 }
00391
00392 hfw_w = geom.width();
00393 hfw_h = sizedat.height;
00394
00395 min_size = sizedat.minsize;
00396 size_hint = sizedat.sizehint;
00397 max_size = sizedat.maxsize;
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407 if (K->sizeConstraint() == QLayout::SetDefaultConstraint)
00408 K->setSizeConstraint(QLayout::SetMinimumSize);
00409 K->update();
00410
00411 dirty = false;
00412 }
00413 };
00414
00415
00416
00417 KLFFlowLayout::KLFFlowLayout(QWidget *parent, int margin, int hspacing, int vspacing)
00418 : QLayout(parent)
00419 {
00420 d = new KLFFlowLayoutPrivate(this);
00421 addChildLayout(d->mainLayout);
00422 setContentsMargins(margin, margin, margin, margin);
00423 setSpacing(-1);
00424 d->hspc = hspacing;
00425 d->vspc = vspacing;
00426 d->mainLayout->setSpacing(d->vspc);
00427 }
00428
00429 KLFFlowLayout::~KLFFlowLayout()
00430 {
00431 delete d->mainLayout;
00432 delete d;
00433 }
00434
00435 bool KLFFlowLayout::event(QEvent *event)
00436 {
00437 return QLayout::event(event);
00438 }
00439
00440 bool KLFFlowLayout::eventFilter(QObject *obj, QEvent *event)
00441 {
00442 return QLayout::eventFilter(obj, event);
00443 }
00444
00445 void KLFFlowLayout::addItem(QLayoutItem *item, int hstretch, int vstretch)
00446 {
00447 invalidate();
00448 KLFFlowLayoutItem *fi = new KLFFlowLayoutItem(item, item->alignment());
00449 fi->hstretch = hstretch;
00450 fi->vstretch = vstretch;
00451 d->items << fi;
00452 }
00453 void KLFFlowLayout::addWidget(QWidget *w, int hstretch, int vstretch, Qt::Alignment align)
00454 {
00455 addChildWidget(w);
00456
00457 w->installEventFilter(this);
00458
00459 QWidgetItem *wi = new QWidgetItem(w);
00460 wi->setAlignment(align);
00461 addItem(wi, hstretch, vstretch);
00462 }
00463 void KLFFlowLayout::addLayout(QLayout *l, int hstretch, int vstretch)
00464 {
00465 addChildLayout(l);
00466 addItem(l, hstretch, vstretch);
00467 }
00468
00469 int KLFFlowLayout::horizontalSpacing() const
00470 {
00471 return d->hspc;
00472 }
00473 void KLFFlowLayout::setHorizontalSpacing(int spacing)
00474 {
00475 invalidate();
00476 d->hspc = spacing;
00477 }
00478 int KLFFlowLayout::verticalSpacing() const
00479 {
00480 return d->vspc;
00481 }
00482 void KLFFlowLayout::setVerticalSpacing(int spacing)
00483 {
00484 invalidate();
00485 d->vspc = spacing;
00486 d->mainLayout->setSpacing(d->vspc);
00487 }
00488 KLFFlowLayout::Flush KLFFlowLayout::flush() const
00489 {
00490 return d->flush;
00491 }
00492 void KLFFlowLayout::setFlush(Flush f)
00493 {
00494 invalidate();
00495 d->flush = f;
00496 }
00497 int KLFFlowLayout::count() const
00498 {
00499 return d->items.size();
00500 }
00501 QLayoutItem *KLFFlowLayout::itemAt(int index) const
00502 {
00503
00504 if (index < 0 || index >= d->items.size())
00505 return NULL;
00506
00507 return d->items[index]->item;
00508 }
00509 QLayoutItem *KLFFlowLayout::takeAt(int index)
00510 {
00511
00512 if (index < 0 || index >= d->items.size())
00513 return NULL;
00514
00515 KLFFlowLayoutItem *fi = d->items.takeAt(index);
00516 d->removeItemFromSubLayouts(fi);
00517 QLayoutItem *item = fi->item;
00518 delete fi;
00519 return item;
00520 }
00521
00522 Qt::Orientations KLFFlowLayout::expandingDirections() const
00523 {
00524 d->calc();
00525 return Qt::Horizontal | d->mainLayout->expandingDirections();
00526 }
00527 bool KLFFlowLayout::hasHeightForWidth() const
00528 {
00529 return true;
00530 }
00531 int KLFFlowLayout::heightForWidth(int width) const
00532 {
00533 if (d->hfw_w != width) {
00534 d->hfw_w = width;
00535 d->dirty = true;
00536 d->calc();
00537 }
00538 return d->hfw_h;
00539 }
00540 QSize KLFFlowLayout::minimumSize() const
00541 {
00542 d->calc();
00543
00544 QSize s = d->min_size + d->marginsSize;
00545 klfDbg("minimumSize is "<<s) ;
00546 return s;
00547 }
00548 QSize KLFFlowLayout::maximumSize() const
00549 {
00550 d->calc();
00551 QSize s = d->max_size;
00552 klfDbg("maximumSize is "<<s) ;
00553 return s.expandedTo(QSize(200,200));
00554 }
00555 void KLFFlowLayout::setGeometry(const QRect &rect)
00556 {
00557 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00558 klfDbg("rect="<<rect) ;
00559 if (d->geom != rect) {
00560 invalidate();
00561 d->geom = rect;
00562 }
00563 QLayout::setGeometry(rect);
00564 d->calc();
00565 d->mainLayout->setGeometry(d->effectiveGeom);
00566 }
00567 QSize KLFFlowLayout::sizeHint() const
00568 {
00569 d->calc();
00570 QSize s = d->size_hint + d->marginsSize;
00571 klfDbg("sizeHint is "<<s) ;
00572 return s;
00573 }
00574
00575 void KLFFlowLayout::invalidate()
00576 {
00577 d->dirty = true;
00578 QLayout::invalidate();
00579 d->mainLayout->invalidate();
00580 }
00581
00582 void KLFFlowLayout::clearAll(bool deleteItems)
00583 {
00584 QList<QLayoutItem*> todelete;
00585 QLayoutItem *it;
00586 while ((it = takeAt(0)) != NULL) {
00587 if (deleteItems && it->widget() != NULL)
00588 it->widget()->deleteLater();
00589 if (deleteItems && it->layout() != NULL)
00590 todelete << it->layout();
00591 if (deleteItems)
00592 todelete << it;
00593 }
00594
00595 foreach (QLayoutItem *item, todelete) {
00596 delete item;
00597 }
00598 }