[KLF Backend][KLF Tools][KLF Home]
KLatexFormula Project
klfflowlayout.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * file klfflowlayout.cpp
3 * This file is part of the KLatexFormula Project.
4 * Copyright (C) 2011 by Philippe Faist
5 * philippe.faist at bluewin.ch
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
21 ***************************************************************************/
22/* $Id$ */
23
24
25#include <math.h>
26
27#include <QHash>
28#include <QWidget>
29#include <QEvent>
30#include <QResizeEvent>
31#include <QMoveEvent>
32#include <QApplication>
33#include <QStyle>
34
35#include <klfdefs.h>
36
37#include "klfflowlayout.h"
38
39
40
41struct KLFFlowLayoutItem : public QLayoutItem
42{
43 KLFFlowLayoutItem(QLayoutItem *li, Qt::Alignment a = 0)
44 : QLayoutItem(a), item(li), hstretch(0), vstretch(0)
45 {
46 KLF_ASSERT_NOT_NULL(item, "item is NULL! Expect crash!", ;) ;
47 }
48 virtual ~KLFFlowLayoutItem();
49
50 virtual Qt::Orientations expandingDirections() const
51 {
52 return item->expandingDirections();
53 }
54 virtual QRect geometry() const
55 {
56 klfDbg("geometry") ;
57 return item->geometry();
58 }
59 virtual bool hasHeightForWidth() const
60 {
61 return item->hasHeightForWidth();
62 }
63 virtual int heightForWidth(int w) const
64 {
65 return item->heightForWidth(w);
66 }
67 virtual void invalidate()
68 {
69 item->invalidate();
70 }
71 virtual bool isEmpty() const
72 {
73 return item->isEmpty();
74 }
75 virtual QLayout *layout()
76 {
77 return item->layout();
78 }
79 virtual QSize maximumSize() const
80 {
81 return item->maximumSize();
82 }
83 virtual int minimumHeightForWidth(int w) const
84 {
85 return item->minimumHeightForWidth(w);
86 }
87 virtual QSize minimumSize() const
88 {
89 return item->minimumSize();
90 }
91 virtual void setGeometry(const QRect& r)
92 {
93 klfDbg("item/widget="<<item->widget()<<"; setGeom: "<<r) ;
94 item->setGeometry(r);
95 }
96 virtual QSize sizeHint() const
97 {
98 return item->sizeHint();
99 }
100 virtual QSpacerItem * spacerItem()
101 {
102 return item->spacerItem();
103 }
104 virtual QWidget * widget()
105 {
106 // klfDbg("widget="<<item->widget()) ;
107 return item->widget();
108 }
109
110 // ---
111
112 QLayoutItem *item;
113
114 int hstretch;
115 int vstretch;
116};
117
118
119KLFFlowLayoutItem::~KLFFlowLayoutItem()
120{
121}
122
123struct KLFFlowLayoutPrivate
124{
126 KLFFlowLayout * K;
128 KLFFlowLayoutPrivate(KLFFlowLayout *f)
129 {
130 K = f;
131 mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, NULL);
132 mainLayout->setContentsMargins(0,0,0,0);
133 hspc = vspc = -1;
135 geom = effectiveGeom = QRect(0, 0, 640, 480); // arbitrary... (?)
136 marginsSize = QSize(0,0);
137 hfw_w = -1;
138 hfw_h = -1;
139 min_size = QSize(0,0);
140
141 dirty = true;
142 }
143
145 QList<KLFFlowLayoutItem*> items;
146
148 bool dirty;
150 QList<QBoxLayout*> layoutLines;
151
152 QBoxLayout *mainLayout;
153
154 int hspc;
155 int vspc;
157
158 QRect geom;
159 QRect effectiveGeom;
160 QSize marginsSize;
161 int hfw_w;
162 int hfw_h;
163 QSize min_size;
164 QSize size_hint;
165 QSize max_size;
166
167 // -------
168
169
170 typedef QList<KLFFlowLayoutItem*> ItemLine;
171
172 void calc()
173 {
174 if (!dirty)
175 return;
176
177 doLayout();
178 }
179
180 void removeItemFromSubLayouts(KLFFlowLayoutItem* fi)
181 {
182 int k, n;
183 QLayoutItem *item;
184 for (k = 0; k < layoutLines.size(); ++k) {
185 n = 0;
186 while ((item = layoutLines[k]->itemAt(n)) != NULL) {
187 if (item == fi) {
188 layoutLines[k]->takeAt(n);
189 return;
190 }
191 n++;
192 }
193 }
194 }
195
196 void clean()
197 {
198 int k;
199 for (k = 0; k < layoutLines.size(); ++k) {
200 // klfDbg("removing line #"<<k) ;
201 // empty each layout line
202 QLayoutItem *item;
203 while ((item = layoutLines[k]->takeAt(0)) != NULL) {
204 KLFFlowLayoutItem *fi = dynamic_cast<KLFFlowLayoutItem*>(item);
205 if (fi == NULL) {
206 // this is not a KLFFlowLayoutItem -> delete it, we don't need it
207 delete item;
208 }
209 }
210 mainLayout->removeItem(layoutLines[k]);
211 delete layoutLines[k];
212 layoutLines[k] = NULL;
213 }
214 layoutLines.clear();
215 dirty = true;
216 }
217
218 struct CalcData
219 {
220 QSize minsize;
221 QSize sizehint;
222 QSize maxsize;
223 int height;
224 };
225
226 QList<ItemLine> calcLines(CalcData *data = NULL)
227 {
228 QList<ItemLine> lines;
229
230 int maxminwidth = 0;
231 int summinwidth = 0;
232 int maxwidth = 0;
233 int maxminheight = 0;
234 int summinheight = 0;
235 int maxheight = 0;
236 int shwidth = 0;
237
238 ItemLine line;
239
240 QStyle *style = NULL;
241 QWidget *pwidget = K->parentWidget();
242 if (pwidget != NULL)
243 style = pwidget->style();
244 if (style == NULL)
245 style = qApp->style();
246
247 int x = 0;
248 int fitwidth = effectiveGeom.width();
249 int height = 0;
250 int thislheight = 0;
251
252 KLFFlowLayoutItem *item;
253 KLFFlowLayoutItem *prevItem = NULL;
254 int k;
255 for (k = 0; k < items.size(); ++k) {
256 item = items[k];
257
258 QSize mins = item->minimumSize();
259 QSize maxs = item->maximumSize();
260 QSize sh = item->sizeHint();
261
262 if (item->isEmpty())
263 continue; // skip empty items
264
265 QSizePolicy::ControlTypes t = QSizePolicy::DefaultType;
266 if (prevItem != NULL)
267 t = prevItem->controlTypes();
268
269 int spaceX = hspc;
270 if (spaceX == -1)
271 spaceX = style->combinedLayoutSpacing(t, item->controlTypes(), Qt::Horizontal);
272
273 int spaceY = vspc;
274 if (spaceY == -1)
275 spaceY = style->combinedLayoutSpacing(t, item->controlTypes(), Qt::Vertical);
276
277 if (x + sh.width() > fitwidth) {
278 lines << line; // flush this line
279 shwidth = qMax(shwidth, x);
280 height += spaceY + thislheight;
281 thislheight = 0;
282 line.clear(); // clear line buffer
283
284 x = 0;
285 }
286 int nextX = x + sh.width() + spaceX;
287
288 line << item;
289 x = nextX;
290 maxminwidth = qMax(mins.width(), maxminwidth);
291 maxminheight = qMax(mins.height(), maxminheight);
292 summinwidth += mins.width();
293 summinheight += mins.height();
294 if (maxwidth < (int)0x7fffffff)
295 maxwidth += maxs.width() + spaceX;
296 if (maxheight < (int)0x7fffffff)
297 maxheight += maxs.height() + spaceY;
298 thislheight = qMax(item->sizeHint().height(), thislheight);
299 prevItem = item;
300 }
301
302 // and flush last line
303 height += thislheight;
304 lines << line;
305
306 if (data != NULL) {
307 data->height = height;
308
309 // int area = qMax(summinwidth * maxminheight, maxminwidth * summinheight);
310 // qreal ratio = (qreal)effectiveGeom.width() / effectiveGeom.height();
311 // int minx = (int)sqrt(area / ratio);
312 // data->minsize = QSize(minx, area/minx);
313 //--
314 // if (lines.count() <= 3)
315 data->minsize = QSize(maxminwidth, height);
316 data->sizehint = QSize(shwidth, height);
317 // else
318 // data->minsize = QSize(maxminwidth, (int)(height*0.50));
319
320 data->maxsize = QSize(maxwidth, maxheight);
321 }
322
323 return lines;
324 }
325
326 void doLayout()
327 {
328 KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
329
330 if (!geom.isValid()) {
331 klfDbg("geom is not valid yet, returning.") ;
332 return;
333 }
334
335 // remove all items from all our sub-layouts and redistribute them...
336 clean();
337
338 int left, top, right, bottom;
339 K->getContentsMargins(&left, &top, &right, &bottom);
340 effectiveGeom = geom.adjusted(+left, +top, -right, -bottom);
341 marginsSize = QSize(left+right, top+bottom);
342
343 klfDbg("geom = "<<geom<<"; mainlayout/setgeom, effGeom="<<effectiveGeom) ;
344
345 CalcData sizedat;
346
347 QList<ItemLine> lines = calcLines(&sizedat);
348
349 klfDbg("calculated lines. minsize="<<sizedat.minsize<<"; maxsize="<<sizedat.maxsize<<", height="
350 <<sizedat.height) ;
351
352 int k, n, i;
353 int linevstretch;
354 for (k = 0; k < lines.size(); ++k) {
355 // klfDbg("adding line #"<<k<<", with "<<lines[k].size()<<" items in the line.") ;
356 QBoxLayout *linelyt = new QBoxLayout(QBoxLayout::LeftToRight, NULL);
357 linelyt->setSpacing(hspc);
358 linevstretch = 0;
359 if (flush == KLFFlowLayout::FlushEnd) {
360 // start with a spacer
361 linelyt->addStretch(0x7fffffff);
362 }
363 for (n = 0; n < lines[k].size(); ++n) {
364 // klfDbg("adding item ["<<k<<"]["<<n<<"], item="<<lines[k][n]->item) ;
365 Qt::Alignment a = lines[k][n]->alignment();
366 if (flush == KLFFlowLayout::NoFlush) {
367 // remove horizontal alignemnt flags
368 a = (a & ~Qt::AlignHorizontal_Mask);
369 } else if (flush == KLFFlowLayout::FlushSparse) {
370 a |= Qt::AlignLeft;
371 }
372 lines[k][n]->item->setAlignment(a); // correct the alignment flags
373 linelyt->addItem(lines[k][n]);
374 for (i = 0; i < linelyt->count() && linelyt->itemAt(i) != lines[k][n]; ++i)
375 ;
376#if QT_VERSION >= 0x040500
377 // setStretch(...) was introduced in Qt 4.5
378 if (i < linelyt->count()) {
379 linelyt->setStretch(i, lines[k][n]->hstretch);
380 }
381#endif
382 linevstretch = qMax(linevstretch, lines[k][n]->vstretch);
383 }
384 if (flush == KLFFlowLayout::FlushBegin) {
385 // end with a spacer
386 linelyt->addStretch(0x7fffffff);
387 }
388 mainLayout->addLayout(linelyt, linevstretch);
389 layoutLines << linelyt;
390 }
391
392 hfw_w = geom.width();
393 hfw_h = sizedat.height;
394
395 min_size = sizedat.minsize;
396 size_hint = sizedat.sizehint;
397 max_size = sizedat.maxsize;
398
399 // QWidget *pwidget = K->parentWidget();
400 // if (pwidget != NULL)
401 // pwidget->updateGeometry();
402 // mainLayout->invalidate();
403 // mainLayout->update();
404 // mainLayout->activate();
405 // if (pwidget != NULL && K->sizeConstraint() == QLayout::SetDefaultConstraint)
406 // pwidget->setMinimumSize(QSize());
407 if (K->sizeConstraint() == QLayout::SetDefaultConstraint)
408 K->setSizeConstraint(QLayout::SetMinimumSize);
409 K->update();
410
411 dirty = false;
412 }
413};
414
415
416
417KLFFlowLayout::KLFFlowLayout(QWidget *parent, int margin, int hspacing, int vspacing)
418 : QLayout(parent)
419{
420 d = new KLFFlowLayoutPrivate(this);
421 addChildLayout(d->mainLayout);
422 setContentsMargins(margin, margin, margin, margin);
423 setSpacing(-1);
424 d->hspc = hspacing;
425 d->vspc = vspacing;
426 d->mainLayout->setSpacing(d->vspc);
427}
428
430{
431 delete d->mainLayout;
432 delete d;
433}
434
436{
437 return QLayout::event(event);
438}
439
441{
442 return QLayout::eventFilter(obj, event);
443}
444
445void KLFFlowLayout::addItem(QLayoutItem *item, int hstretch, int vstretch)
446{
447 invalidate();
448 KLFFlowLayoutItem *fi = new KLFFlowLayoutItem(item, item->alignment());
449 fi->hstretch = hstretch;
450 fi->vstretch = vstretch;
451 d->items << fi;
452}
453void KLFFlowLayout::addWidget(QWidget *w, int hstretch, int vstretch, Qt::Alignment align)
454{
455 addChildWidget(w);
456
457 w->installEventFilter(this);
458
459 QWidgetItem *wi = new QWidgetItem(w);
460 wi->setAlignment(align);
461 addItem(wi, hstretch, vstretch);
462}
463void KLFFlowLayout::addLayout(QLayout *l, int hstretch, int vstretch)
464{
465 addChildLayout(l);
466 addItem(l, hstretch, vstretch);
467}
468
470{
471 return d->hspc;
472}
474{
475 invalidate();
476 d->hspc = spacing;
477}
479{
480 return d->vspc;
481}
483{
484 invalidate();
485 d->vspc = spacing;
486 d->mainLayout->setSpacing(d->vspc);
487}
489{
490 return d->flush;
491}
493{
494 invalidate();
495 d->flush = f;
496}
498{
499 return d->items.size();
500}
501QLayoutItem *KLFFlowLayout::itemAt(int index) const
502{
503 // this is not an error, it is documented in Qt API... just return NULL.
504 if (index < 0 || index >= d->items.size())
505 return NULL;
506
507 return d->items[index]->item;
508}
509QLayoutItem *KLFFlowLayout::takeAt(int index)
510{
511 // this is not an error, it is documented in Qt API... just return NULL.
512 if (index < 0 || index >= d->items.size())
513 return NULL;
514
515 KLFFlowLayoutItem *fi = d->items.takeAt(index);
516 d->removeItemFromSubLayouts(fi);
517 QLayoutItem *item = fi->item;
518 delete fi;
519 return item;
520}
521
523{
524 d->calc();
525 return Qt::Horizontal | d->mainLayout->expandingDirections();
526}
528{
529 return true;
530}
532{
533 if (d->hfw_w != width) {
534 d->hfw_w = width;
535 d->dirty = true;
536 d->calc();
537 }
538 return d->hfw_h;
539}
541{
542 d->calc();
543 // QSize s = d->mainLayout->minimumSize() + d->marginsSize;
544 QSize s = d->min_size + d->marginsSize;
545 klfDbg("minimumSize is "<<s) ;
546 return s;
547}
549{
550 d->calc();
551 QSize s = d->max_size;
552 klfDbg("maximumSize is "<<s) ;
553 return s.expandedTo(QSize(200,200));
554}
556{
557 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
558 klfDbg("rect="<<rect) ;
559 if (d->geom != rect) {
560 invalidate();
561 d->geom = rect;
562 }
563 QLayout::setGeometry(rect);
564 d->calc();
565 d->mainLayout->setGeometry(d->effectiveGeom);
566}
568{
569 d->calc();
570 QSize s = d->size_hint + d->marginsSize;//d->mainLayout->sizeHint() + d->marginsSize;
571 klfDbg("sizeHint is "<<s) ;
572 return s;
573}
574
576{
577 d->dirty = true;
578 QLayout::invalidate();
579 d->mainLayout->invalidate();
580}
581
582void KLFFlowLayout::clearAll(bool deleteItems)
583{
584 QList<QLayoutItem*> todelete;
585 QLayoutItem *it;
586 while ((it = takeAt(0)) != NULL) {
587 if (deleteItems && it->widget() != NULL)
588 it->widget()->deleteLater();
589 if (deleteItems && it->layout() != NULL)
590 todelete << it->layout();
591 if (deleteItems)
592 todelete << it;
593 }
594
595 foreach (QLayoutItem *item, todelete) {
596 delete item;
597 }
598}
virtual int heightForWidth(int width) const
virtual QSize minimumSize() const
KLFFlowLayout(QWidget *parent, int margin=-1, int hspacing=-1, int vspacing=-1)
virtual int count() const
virtual QLayoutItem * itemAt(int index) const
virtual void invalidate()
virtual QLayoutItem * takeAt(int index)
void clearAll(bool deleteItems=true)
virtual QSize maximumSize() const
virtual void addWidget(QWidget *w, int hstretch=0, int vstretch=0, Qt::Alignment align=0)
virtual void addItem(QLayoutItem *item)
virtual QSize sizeHint() const
virtual bool hasHeightForWidth() const
void setGeometry(const QRect &rect)
void setFlush(Flush f)
virtual void addLayout(QLayout *l, int hstretch=0, int vstretch=0)
@ FlushEnd
Leave all extra space at beginning of line.
@ NoFlush
Give the extra space to the widgets to stretch, don't flush.
@ FlushSparse
Distribute the extra space inbetween the widgets to fill the line.
@ FlushBegin
Leave all extra space at end of line.
virtual bool eventFilter(QObject *obj, QEvent *event)
virtual Qt::Orientations expandingDirections() const
virtual bool event(QEvent *event)
void setVerticalSpacing(int spacing)
void setHorizontalSpacing(int spacing)
virtual ~KLFFlowLayout()
const char * style
#define KLF_DEBUG_TIME_BLOCK(msg)
Utility to time the execution of a block.
Definition klfdebug.h:151
#define KLF_DEBUG_BLOCK(msg)
Utility to debug the execution of a block.
Definition klfdebug.h:152
#define KLF_ASSERT_NOT_NULL(ptr, msg, failaction)
Asserting Non-NULL pointers (NON-FATAL)
Definition klfdebug.h:210
#define klfDbg(streamableItems)
print debug stream items
Definition klfdebug.h:158
Base declarations for klatexformula and some utilities.
int size() const
QSize expandedTo(const QSize &otherSize) const
int height() const
int width() const
typedef Alignment
typedef Orientations

Generated by doxygen 1.13.2