[KLF Backend][KLF Tools][KLF Home]
KLatexFormula Project
src/klftools/klfflowlayout.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002  *   file klfflowlayout.cpp
00003  *   This file is part of the KLatexFormula Project.
00004  *   Copyright (C) 2011 by Philippe Faist
00005  *   philippe.faist at bluewin.ch
00006  *                                                                         *
00007  *   This program is free software; you can redistribute it and/or modify  *
00008  *   it under the terms of the GNU General Public License as published by  *
00009  *   the Free Software Foundation; either version 2 of the License, or     *
00010  *   (at your option) any later version.                                   *
00011  *                                                                         *
00012  *   This program is distributed in the hope that it will be useful,       *
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015  *   GNU General Public License for more details.                          *
00016  *                                                                         *
00017  *   You should have received a copy of the GNU General Public License     *
00018  *   along with this program; if not, write to the                         *
00019  *   Free Software Foundation, Inc.,                                       *
00020  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00021  ***************************************************************************/
00022 /* $Id$ */
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     //    klfDbg("widget="<<item->widget()) ;
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); // arbitrary... (?)
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       //      klfDbg("removing line #"<<k) ;
00201       // empty each layout line
00202       QLayoutItem *item;
00203       while ((item = layoutLines[k]->takeAt(0)) != NULL) {
00204         KLFFlowLayoutItem *fi = dynamic_cast<KLFFlowLayoutItem*>(item);
00205         if (fi == NULL) {
00206           // this is not a KLFFlowLayoutItem -> delete it, we don't need it
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; // skip empty items
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; // flush this line
00279         shwidth = qMax(shwidth, x);
00280         height += spaceY + thislheight;
00281         thislheight = 0;
00282         line.clear(); // clear line buffer
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     // and flush last line
00303     height += thislheight;
00304     lines << line;
00305 
00306     if (data != NULL) {
00307       data->height = height;
00308 
00309       //       int area = qMax(summinwidth * maxminheight, maxminwidth * summinheight);
00310       //       qreal ratio = (qreal)effectiveGeom.width() / effectiveGeom.height();
00311       //       int minx = (int)sqrt(area / ratio);
00312       //       data->minsize = QSize(minx, area/minx);
00313       //--
00314       //      if (lines.count() <= 3)
00315       data->minsize = QSize(maxminwidth, height);
00316       data->sizehint = QSize(shwidth, height);
00317       //      else
00318       //        data->minsize = QSize(maxminwidth, (int)(height*0.50));
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     // remove all items from all our sub-layouts and redistribute them...
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       //      klfDbg("adding line #"<<k<<", with "<<lines[k].size()<<" items in the line.") ;
00356       QBoxLayout *linelyt = new QBoxLayout(QBoxLayout::LeftToRight, NULL);
00357       linelyt->setSpacing(hspc);
00358       linevstretch = 0;
00359       if (flush == KLFFlowLayout::FlushEnd) {
00360         // start with a spacer
00361         linelyt->addStretch(0x7fffffff);
00362       }
00363       for (n = 0; n < lines[k].size(); ++n) {
00364         //      klfDbg("adding item ["<<k<<"]["<<n<<"], item="<<lines[k][n]->item) ;
00365         Qt::Alignment a = lines[k][n]->alignment();
00366         if (flush == KLFFlowLayout::NoFlush) {
00367           // remove horizontal alignemnt flags
00368           a = (a & ~Qt::AlignHorizontal_Mask);
00369         } else if (flush == KLFFlowLayout::FlushSparse) {
00370           a |= Qt::AlignLeft;
00371         }
00372         lines[k][n]->item->setAlignment(a); // correct the alignment flags
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         // setStretch(...) was introduced in Qt 4.5
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         // end with a spacer
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     //    QWidget *pwidget = K->parentWidget();
00400     //    if (pwidget != NULL)
00401     //      pwidget->updateGeometry();
00402     //    mainLayout->invalidate();
00403     //    mainLayout->update();
00404     //    mainLayout->activate();
00405     //    if (pwidget != NULL && K->sizeConstraint() == QLayout::SetDefaultConstraint)
00406     //      pwidget->setMinimumSize(QSize());
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   // this is not an error, it is documented in Qt API... just return NULL.
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   // this is not an error, it is documented in Qt API... just return NULL.
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   //  QSize s = d->mainLayout->minimumSize() + d->marginsSize;
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;//d->mainLayout->sizeHint() + 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 }

Generated by doxygen 1.7.6.1. The KLatexFormula website is hosted on sourceforge.net