kexi

kexiflowlayout.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "kexiflowlayout.h"
00021 
00022 #include <kdebug.h>
00023 
00025 
00026 class KexiFlowLayoutIterator : public QGLayoutIterator
00027 {
00028     public:
00029         KexiFlowLayoutIterator( QPtrList<QLayoutItem> *list )
00030           : m_idx(0), m_list( list )
00031         {}
00032         uint count() const;
00033         QLayoutItem *current();
00034         QLayoutItem *next();
00035         QLayoutItem *takeCurrent();
00036 
00037     private:
00038         int m_idx;
00039         QPtrList<QLayoutItem> *m_list;
00040 };
00041 
00042 uint
00043 KexiFlowLayoutIterator::count() const
00044 {
00045     return m_list->count();
00046 }
00047 
00048 QLayoutItem *
00049 KexiFlowLayoutIterator::current()
00050 {
00051     return (m_idx < (int)count()) ? m_list->at(m_idx) : 0;
00052 }
00053 
00054 QLayoutItem *
00055 KexiFlowLayoutIterator::next()
00056 {
00057     m_idx++;
00058     return current();
00059 }
00060 
00061 QLayoutItem *
00062 KexiFlowLayoutIterator::takeCurrent()
00063 {
00064     return (m_idx < (int)count()) ? m_list->take(m_idx) : 0;
00065 }
00066 
00068 
00069 KexiFlowLayout::KexiFlowLayout(QWidget *parent, int border, int space, const char *name)
00070  : QLayout(parent, border, space, name)
00071 {
00072     m_orientation = Horizontal;
00073     m_justify = false;
00074     m_cached_width = 0;
00075 }
00076 
00077 KexiFlowLayout::KexiFlowLayout(QLayout* parent, int space, const char *name)
00078  : QLayout( parent, space, name )
00079 {
00080     m_orientation = Horizontal;
00081     m_justify = false;
00082     m_cached_width = 0;
00083 }
00084 
00085 KexiFlowLayout::KexiFlowLayout(int space, const char *name)
00086  : QLayout(space, name)
00087  {
00088     m_orientation = Horizontal;
00089     m_justify = false;
00090     m_cached_width = 0;
00091  }
00092 
00093 KexiFlowLayout::~KexiFlowLayout()
00094 {
00095     deleteAllItems();
00096 }
00097 
00098 void
00099 KexiFlowLayout::addItem(QLayoutItem *item)
00100 {
00101     m_list.append(item);
00102 }
00103 
00104 void
00105 KexiFlowLayout::addSpacing(int size)
00106 {
00107     if (m_orientation == Horizontal)
00108         addItem( new QSpacerItem( size, 0, QSizePolicy::Fixed, QSizePolicy::Minimum ) );
00109     else
00110         addItem( new QSpacerItem( 0, size, QSizePolicy::Minimum, QSizePolicy::Fixed ) );
00111 }
00112 
00113 QLayoutIterator
00114 KexiFlowLayout::iterator()
00115 {
00116     return QLayoutIterator( new KexiFlowLayoutIterator(&m_list) );
00117 }
00118 
00119 QPtrList<QWidget>*
00120 KexiFlowLayout::widgetList() const
00121 {
00122     QPtrList<QWidget> *list = new QPtrList<QWidget>();
00123     for (QPtrListIterator<QLayoutItem> it(m_list); it.current(); ++it) {
00124         if(it.current()->widget())
00125             list->append(it.current()->widget());
00126     }
00127     return list;
00128 }
00129 
00130 void
00131 KexiFlowLayout::invalidate()
00132 {
00133     QLayout::invalidate();
00134     m_cached_sizeHint = QSize();
00135     m_cached_minSize = QSize();
00136     m_cached_width = 0;
00137 }
00138 
00139 bool
00140 KexiFlowLayout::isEmpty()
00141 {
00142     return m_list.isEmpty();
00143 }
00144 
00145 bool
00146 KexiFlowLayout::hasHeightForWidth() const
00147 {
00148     return (m_orientation == Horizontal);
00149 }
00150 
00151 int
00152 KexiFlowLayout::heightForWidth(int w) const
00153 {
00154     if(m_cached_width != w) {
00155         // workaround to allow this method to stay 'const'
00156         KexiFlowLayout *mthis = (KexiFlowLayout*)this;
00157         int h = mthis->simulateLayout( QRect(0,0,w,0) );
00158         mthis->m_cached_hfw = h;
00159         mthis->m_cached_width = w;
00160         return h;
00161     }
00162     return m_cached_hfw;
00163 }
00164 
00165 QSize
00166 KexiFlowLayout::sizeHint() const
00167 {
00168     if(m_cached_sizeHint.isEmpty()) {
00169         KexiFlowLayout *mthis = (KexiFlowLayout*)this;
00170         QRect r = QRect(0, 0, 2000, 2000);
00171         mthis->simulateLayout(r);
00172     }
00173     return m_cached_sizeHint;
00174 }
00175 
00176 QSize
00177 KexiFlowLayout::minimumSize() const
00178 {
00179 //js: do we really need to simulate layout here?
00180 //    I commented this out because it was impossible to stretch layout conveniently.
00181 //    Now, minimum size is computed automatically based on item's minimumSize...
00182 #if 0
00183     if(m_cached_minSize.isEmpty()) {
00184         KexiFlowLayout *mthis = (KexiFlowLayout*)this;
00185         QRect r = QRect(0, 0, 2000, 2000);
00186         mthis->simulateLayout(r);
00187     }
00188 #endif
00189     return m_cached_minSize;
00190 }
00191 
00192 QSizePolicy::ExpandData
00193 KexiFlowLayout::expanding() const
00194 {
00195     if(m_orientation == Vertical)
00196         return QSizePolicy::Vertically;
00197     else
00198         return QSizePolicy::Horizontally;
00199 }
00200 
00201 void
00202 KexiFlowLayout::setGeometry(const QRect &r)
00203 {
00204     QLayout::setGeometry(r);
00205     if(m_orientation == Horizontal)
00206         doHorizontalLayout(r);
00207     else
00208         doVerticalLayout(r);
00209 }
00210 
00211 int
00212 KexiFlowLayout::simulateLayout(const QRect &r)
00213 {
00214     if(m_orientation == Horizontal)
00215         return doHorizontalLayout(r, true);
00216     else
00217         return doVerticalLayout(r, true);
00218 }
00219 
00220 int
00221 KexiFlowLayout::doHorizontalLayout(const QRect &r, bool testOnly)
00222 {
00223     int x = r.x();
00224     int y = r.y();
00225     int h = 0; // height of this line
00226     int availableSpace = r.width() + spacing();
00227     int expandingWidgets=0; // number of widgets in the line with QSizePolicy == Expanding
00228     QPtrListIterator<QLayoutItem> it(m_list);
00229     QPtrList<QLayoutItem> currentLine;
00230     QLayoutItem *o;
00231     QSize minSize, sizeHint(20, 20);
00232     int minSizeHeight = 0 - spacing();
00233 
00234     while ( (o = it.current()) != 0 ) {
00235         if(o->isEmpty()) { 
00236             ++it;
00237             continue;
00238         }
00239 
00240 //      kdDebug() << "- doHorizontalLayout(): " << o->widget()->className() << " " << o->widget()->name() << endl;
00241         QSize oSizeHint = o->sizeHint(); // we cache these ones because it can take a while to get it (eg for child layouts)
00242         if ((x + oSizeHint.width()) > r.right() && h > 0) {
00243             // do the layout of current line
00244             QPtrListIterator<QLayoutItem> it2(currentLine);
00245             QLayoutItem *item;
00246             int wx = r.x();
00247             int sizeHintWidth = 0 -spacing(), minSizeWidth=0 - spacing(), lineMinHeight=0;
00248             while( (item = it2.current()) != 0 ) {
00249                 QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
00250                 QSize itemMinSize = item->minimumSize(); // a while to get them
00251                 QSize s;
00252                 if(m_justify) {
00253                     if(expandingWidgets != 0) {
00254                         if(item->expanding() == QSizePolicy::Horizontally || item->expanding() == QSizePolicy::BothDirections)
00255                             s = QSize( QMIN(itemSizeHint.width() + availableSpace / expandingWidgets
00256                                 , r.width()), itemSizeHint.height() );
00257                         else
00258                             s = QSize( QMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() );
00259                     }
00260                     else
00261                         s = QSize( QMIN(itemSizeHint.width() + availableSpace / (int)currentLine.count()
00262                             , r.width()), itemSizeHint.height() );
00263                 }
00264                 else
00265                     s = QSize ( QMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() );
00266                 if(!testOnly)
00267                     item->setGeometry( QRect(QPoint(wx, y), s) );
00268                 wx = wx + s.width() + spacing();
00269                 minSizeWidth = minSizeWidth + spacing() + itemMinSize.width();
00270                 sizeHintWidth = sizeHintWidth + spacing() +  itemSizeHint.width();
00271                 lineMinHeight = QMAX( lineMinHeight, itemMinSize.height() );
00272                 ++it2;
00273             }
00274             sizeHint = sizeHint.expandedTo( QSize(sizeHintWidth, 0) );
00275             minSize = minSize.expandedTo( QSize(minSizeWidth, 0) );
00276             minSizeHeight = minSizeHeight + spacing() + lineMinHeight;
00277             // start a new line
00278             y = y + spacing() + h;
00279             h = 0;
00280             x = r.x();
00281             currentLine.clear();
00282             expandingWidgets = 0;
00283             availableSpace = r.width() + spacing();
00284         }
00285 
00286         x = x + spacing() + oSizeHint.width();
00287         h = QMAX( h,  oSizeHint.height() );
00288         currentLine.append(o);
00289         if(o->expanding() == QSizePolicy::Horizontally || o->expanding() == QSizePolicy::BothDirections)
00290             ++expandingWidgets;
00291         availableSpace = QMAX(0, availableSpace - spacing() - oSizeHint.width());
00292         ++it;
00293     }
00294 
00295     // don't forget to layout the last line
00296     QPtrListIterator<QLayoutItem> it2(currentLine);
00297     QLayoutItem *item;
00298     int wx = r.x();
00299     int sizeHintWidth = 0 -spacing(), minSizeWidth=0 - spacing(), lineMinHeight=0;
00300     while( (item = it2.current()) != 0 ) {
00301         QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
00302         QSize itemMinSize = item->minimumSize(); // a while to get them
00303         QSize s;
00304         if(m_justify) {
00305             if(expandingWidgets != 0) {
00306                 if(item->expanding() == QSizePolicy::Horizontally || item->expanding() == QSizePolicy::BothDirections)
00307                     s = QSize( QMIN(itemSizeHint.width() + availableSpace / expandingWidgets
00308                         , r.width()), itemSizeHint.height() );
00309                 else
00310                     s = QSize( QMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() );
00311             }
00312             else
00313                 s = QSize( QMIN(itemSizeHint.width() + availableSpace / (int)currentLine.count()
00314                     , r.width()), itemSizeHint.height() );
00315         }
00316         else
00317             s = QSize ( QMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() );
00318         if(!testOnly)
00319             item->setGeometry( QRect(QPoint(wx, y), s) );
00320         wx = wx + s.width() + spacing();
00321         minSizeWidth = minSizeWidth + spacing() + itemMinSize.width();
00322         sizeHintWidth = sizeHintWidth + spacing() +  itemSizeHint.width();
00323         lineMinHeight = QMAX( lineMinHeight, itemMinSize.height() );
00324         ++it2;
00325     }
00326     sizeHint = sizeHint.expandedTo( QSize(sizeHintWidth, y + spacing() + h) );
00327     minSizeHeight = minSizeHeight + spacing() + lineMinHeight;
00328     minSize = minSize.expandedTo( QSize(minSizeWidth, minSizeHeight) );
00329 
00330     // store sizeHint() and minimumSize()
00331     m_cached_sizeHint = sizeHint + QSize(2* margin(), 2*margin());
00332     m_cached_minSize = minSize + QSize(2* margin() , 2*margin());
00333     // return our height
00334     return y + h - r.y();
00335 }
00336 
00337 int
00338 KexiFlowLayout::doVerticalLayout(const QRect &r, bool testOnly)
00339 {
00340     int x = r.x();
00341     int y = r.y();
00342     int w = 0; // width of this line
00343     int availableSpace = r.height() + spacing();
00344     int expandingWidgets=0; // number of widgets in the line with QSizePolicy == Expanding
00345     QPtrListIterator<QLayoutItem> it(m_list);
00346     QPtrList<QLayoutItem> currentLine;
00347     QLayoutItem *o;
00348     QSize minSize, sizeHint(20, 20);
00349     int minSizeWidth = 0 - spacing();
00350 
00351     while ( (o = it.current()) != 0 ) {
00352         if(o->isEmpty()) { 
00353             ++it;
00354             continue;
00355         }
00356 
00357         QSize oSizeHint = o->sizeHint(); // we cache these ones because it can take a while to get it (eg for child layouts)
00358         if (y + oSizeHint.height() > r.bottom() && w > 0) {
00359             // do the layout of current line
00360             QPtrListIterator<QLayoutItem> it2(currentLine);
00361             QLayoutItem *item;
00362             int wy = r.y();
00363             int sizeHintHeight = 0 - spacing(), minSizeHeight = 0 - spacing(), colMinWidth=0;
00364             while( (item = it2.current()) != 0 ) {
00365                 QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
00366                 QSize itemMinSize = item->minimumSize(); // a while to get them
00367                 QSize s;
00368                 if(m_justify) {
00369                     if(expandingWidgets != 0) {
00370                         if(item->expanding() == QSizePolicy::Vertically || item->expanding() == QSizePolicy::BothDirections)
00371                             s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height() + availableSpace / expandingWidgets
00372                                 , r.height()) );
00373                         else
00374                             s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height(), r.height()) );
00375                     }
00376                     else
00377                         s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height() + availableSpace / (int)currentLine.count()
00378                             , r.height()) );
00379                 }
00380                 else
00381                     s = QSize (  itemSizeHint.width(), QMIN(itemSizeHint.height(), r.height()) );
00382                 if(!testOnly)
00383                     item->setGeometry( QRect(QPoint(x, wy), s) );
00384                 wy = wy + s.height() + spacing();
00385                 minSizeHeight = minSizeHeight + spacing() + itemMinSize.height();
00386                 sizeHintHeight = sizeHintHeight + spacing() + itemSizeHint.height();
00387                 colMinWidth = QMAX( colMinWidth, itemMinSize.width() );
00388                 ++it2;
00389             }
00390             sizeHint = sizeHint.expandedTo( QSize(0, sizeHintHeight) );
00391             minSize = minSize.expandedTo( QSize(0, minSizeHeight) );
00392             minSizeWidth = minSizeWidth + spacing() + colMinWidth;
00393             // start a new column
00394             x = x + spacing() + w;
00395             w = 0;
00396             y = r.y();
00397             currentLine.clear();
00398             expandingWidgets = 0;
00399             availableSpace = r.height() + spacing();
00400         }
00401 
00402         y = y + spacing() + oSizeHint.height();
00403         w = QMAX( w,  oSizeHint.width() );
00404         currentLine.append(o);
00405         if(o->expanding() == QSizePolicy::Vertically || o->expanding() == QSizePolicy::BothDirections)
00406             ++expandingWidgets;
00407         availableSpace = QMAX(0, availableSpace - spacing() - oSizeHint.height());
00408         ++it;
00409     }
00410 
00411     // don't forget to layout the last line
00412     QPtrListIterator<QLayoutItem> it2(currentLine);
00413     QLayoutItem *item;
00414     int wy = r.y();
00415     int sizeHintHeight = 0 - spacing(), minSizeHeight = 0 - spacing(), colMinWidth=0;
00416     while( (item = it2.current()) != 0 ) {
00417         QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
00418         QSize itemMinSize = item->minimumSize(); // a while to get them
00419         QSize s;
00420         if(m_justify) {
00421             if(expandingWidgets != 0) {
00422                 if(item->expanding() == QSizePolicy::Vertically || item->expanding() == QSizePolicy::BothDirections)
00423                     s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height() + availableSpace / expandingWidgets
00424                         , r.height()) );
00425                 else
00426                     s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height(), r.height()) );
00427             }
00428             else
00429                 s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height() + availableSpace / (int)currentLine.count()
00430                     , r.height()) );
00431         }
00432         else
00433             s = QSize (  itemSizeHint.width(), QMIN(itemSizeHint.height(), r.height()) );
00434         if(!testOnly)
00435             item->setGeometry( QRect(QPoint(x, wy), s) );
00436         wy = wy + s.height() + spacing();
00437         minSizeHeight = minSizeHeight + spacing() + itemMinSize.height();
00438         sizeHintHeight = sizeHintHeight + spacing() + itemSizeHint.height();
00439         colMinWidth = QMAX( colMinWidth, itemMinSize.width() );
00440         ++it2;
00441     }
00442     sizeHint = sizeHint.expandedTo( QSize( x + spacing() + w, sizeHintHeight) );
00443     minSizeWidth = minSizeWidth + spacing() + colMinWidth;
00444     minSize = minSize.expandedTo( QSize(minSizeWidth, minSizeHeight) );
00445 
00446     // store sizeHint() and minimumSize()
00447     m_cached_sizeHint = sizeHint + QSize(2* margin(), 2*margin());
00448     m_cached_minSize = minSize + QSize(2* margin(), 2*margin());
00449     // return our width
00450     return x + w - r.x();
00451 }
00452 
KDE Home | KDE Accessibility Home | Description of Access Keys