[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: klfflowlayout.cpp 707 2011-08-24 09:12:19Z phfaist $ */
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 
41 struct 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 
119 KLFFlowLayoutItem::~KLFFlowLayoutItem()
120 {
121 }
122 
123 struct 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;
134  flush = KLFFlowLayout::NoFlush;
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 
146 
148  bool dirty;
150  QList<QBoxLayout*> layoutLines;
151 
152  QBoxLayout *mainLayout;
153 
154  int hspc;
155  int vspc;
156  KLFFlowLayout::Flush flush;
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  {
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 
417 KLFFlowLayout::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 
445 void 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 }
453 void 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 }
463 void 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 }
501 QLayoutItem *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 }
509 QLayoutItem *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 }
531 int KLFFlowLayout::heightForWidth(int width) const
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 {
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 
582 void 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 }
A Layout that places widgets left to right, top to bottom.
Definition: klfflowlayout.h:38
void clear()
int horizontalSpacing() const
virtual void addWidget(QWidget *w, int hstretch=0, int vstretch=0, Qt::Alignment align=0)
virtual ~KLFFlowLayout()
int width() const
const char * style
Definition: klfdatautil.cpp:56
virtual void addLayout(QLayout *l, int hstretch=0, int vstretch=0)
Base declarations for klatexformula and some utilities.
virtual bool event(QEvent *event)
void setHorizontalSpacing(int spacing)
Distribute the extra space inbetween the widgets to fill the line.
Definition: klfflowlayout.h:51
void setVerticalSpacing(int spacing)
#define klfDbg(streamableItems)
print debug stream items
#define KLF_DEBUG_BLOCK(msg)
Utility to debug the execution of a block.
virtual QLayoutItem * itemAt(int index) const
T takeAt(int i)
int verticalSpacing() const
virtual int heightForWidth(int width) const
virtual bool eventFilter(QObject *obj, QEvent *event)
virtual int count() const
Leave all extra space at end of line.
Definition: klfflowlayout.h:52
typedef Alignment
int size() const
#define KLF_ASSERT_NOT_NULL(ptr, msg, failaction)
Asserting Non-NULL pointers (NON-FATAL)
virtual bool hasHeightForWidth() const
void setFlush(Flush f)
Flush flush() const
#define KLF_DEBUG_TIME_BLOCK(msg)
Utility to time the execution of a block.
virtual QSize maximumSize() const
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const
virtual void invalidate()
KLFFlowLayout(QWidget *parent, int margin=-1, int hspacing=-1, int vspacing=-1)
Leave all extra space at beginning of line.
Definition: klfflowlayout.h:53
#define KLF_FUNC_NAME
bool isValid() const
virtual Qt::Orientations expandingDirections() const
int width() const
virtual QLayoutItem * takeAt(int index)
QSize expandedTo(const QSize &otherSize) const
virtual QSize minimumSize() const
virtual QSize sizeHint() const
int height() const
typedef Orientations
void setGeometry(const QRect &rect)
virtual void addItem(QLayoutItem *item)
Definition: klfflowlayout.h:61
Give the extra space to the widgets to stretch, don&#39;t flush.
Definition: klfflowlayout.h:50
void clearAll(bool deleteItems=true)

Generated by doxygen 1.8.13