/* This file is part of the KDE project Copyright (C) 2005 Cedric Pasteur This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kexiflowlayout.h" #include /// Iterator class class KexiFlowLayoutIterator : public QGLayoutIterator { public: KexiFlowLayoutIterator( QPtrList *list ) : m_idx(0), m_list( list ) {} uint count() const; QLayoutItem *current(); QLayoutItem *next(); QLayoutItem *takeCurrent(); private: int m_idx; QPtrList *m_list; }; uint KexiFlowLayoutIterator::count() const { return m_list->count(); } QLayoutItem * KexiFlowLayoutIterator::current() { return (m_idx < (int)count()) ? m_list->at(m_idx) : 0; } QLayoutItem * KexiFlowLayoutIterator::next() { m_idx++; return current(); } QLayoutItem * KexiFlowLayoutIterator::takeCurrent() { return (m_idx < (int)count()) ? m_list->take(m_idx) : 0; } //// The layout itself KexiFlowLayout::KexiFlowLayout(QWidget *parent, int border, int space, const char *name) : QLayout(parent, border, space, name) { m_orientation = Horizontal; m_justify = false; m_cached_width = 0; } KexiFlowLayout::KexiFlowLayout(QLayout* parent, int space, const char *name) : QLayout( parent, space, name ) { m_orientation = Horizontal; m_justify = false; m_cached_width = 0; } KexiFlowLayout::KexiFlowLayout(int space, const char *name) : QLayout(space, name) { m_orientation = Horizontal; m_justify = false; m_cached_width = 0; } KexiFlowLayout::~KexiFlowLayout() { deleteAllItems(); } void KexiFlowLayout::addItem(QLayoutItem *item) { m_list.append(item); } void KexiFlowLayout::addSpacing(int size) { if (m_orientation == Horizontal) addItem( new QSpacerItem( size, 0, QSizePolicy::Fixed, QSizePolicy::Minimum ) ); else addItem( new QSpacerItem( 0, size, QSizePolicy::Minimum, QSizePolicy::Fixed ) ); } QLayoutIterator KexiFlowLayout::iterator() { return QLayoutIterator( new KexiFlowLayoutIterator(&m_list) ); } QPtrList* KexiFlowLayout::widgetList() const { QPtrList *list = new QPtrList(); for (QPtrListIterator it(m_list); it.current(); ++it) { if(it.current()->widget()) list->append(it.current()->widget()); } return list; } void KexiFlowLayout::invalidate() { QLayout::invalidate(); m_cached_sizeHint = QSize(); m_cached_minSize = QSize(); m_cached_width = 0; } bool KexiFlowLayout::isEmpty() { return m_list.isEmpty(); } bool KexiFlowLayout::hasHeightForWidth() const { return (m_orientation == Horizontal); } int KexiFlowLayout::heightForWidth(int w) const { if(m_cached_width != w) { // workaround to allow this method to stay 'const' KexiFlowLayout *mthis = (KexiFlowLayout*)this; int h = mthis->simulateLayout( QRect(0,0,w,0) ); mthis->m_cached_hfw = h; mthis->m_cached_width = w; return h; } return m_cached_hfw; } QSize KexiFlowLayout::sizeHint() const { if(m_cached_sizeHint.isEmpty()) { KexiFlowLayout *mthis = (KexiFlowLayout*)this; QRect r = QRect(0, 0, 2000, 2000); mthis->simulateLayout(r); } return m_cached_sizeHint; } QSize KexiFlowLayout::minimumSize() const { //js: do we really need to simulate layout here? // I commented this out because it was impossible to stretch layout conveniently. // Now, minimum size is computed automatically based on item's minimumSize... #if 0 if(m_cached_minSize.isEmpty()) { KexiFlowLayout *mthis = (KexiFlowLayout*)this; QRect r = QRect(0, 0, 2000, 2000); mthis->simulateLayout(r); } #endif return m_cached_minSize; } QSizePolicy::ExpandData KexiFlowLayout::expanding() const { if(m_orientation == Vertical) return QSizePolicy::Vertically; else return QSizePolicy::Horizontally; } void KexiFlowLayout::setGeometry(const QRect &r) { QLayout::setGeometry(r); if(m_orientation == Horizontal) doHorizontalLayout(r); else doVerticalLayout(r); } int KexiFlowLayout::simulateLayout(const QRect &r) { if(m_orientation == Horizontal) return doHorizontalLayout(r, true); else return doVerticalLayout(r, true); } int KexiFlowLayout::doHorizontalLayout(const QRect &r, bool testOnly) { int x = r.x(); int y = r.y(); int h = 0; // height of this line int availableSpace = r.width() + spacing(); int expandingWidgets=0; // number of widgets in the line with QSizePolicy == Expanding QPtrListIterator it(m_list); QPtrList currentLine; QLayoutItem *o; QSize minSize, sizeHint(20, 20); int minSizeHeight = 0 - spacing(); while ( (o = it.current()) != 0 ) { if(o->isEmpty()) { /// do not consider hidden widgets ++it; continue; } // kdDebug() << "- doHorizontalLayout(): " << o->widget()->className() << " " << o->widget()->name() << endl; QSize oSizeHint = o->sizeHint(); // we cache these ones because it can take a while to get it (eg for child layouts) if ((x + oSizeHint.width()) > r.right() && h > 0) { // do the layout of current line QPtrListIterator it2(currentLine); QLayoutItem *item; int wx = r.x(); int sizeHintWidth = 0 -spacing(), minSizeWidth=0 - spacing(), lineMinHeight=0; while( (item = it2.current()) != 0 ) { QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take QSize itemMinSize = item->minimumSize(); // a while to get them QSize s; if(m_justify) { if(expandingWidgets != 0) { if(item->expanding() == QSizePolicy::Horizontally || item->expanding() == QSizePolicy::BothDirections) s = QSize( QMIN(itemSizeHint.width() + availableSpace / expandingWidgets , r.width()), itemSizeHint.height() ); else s = QSize( QMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() ); } else s = QSize( QMIN(itemSizeHint.width() + availableSpace / (int)currentLine.count() , r.width()), itemSizeHint.height() ); } else s = QSize ( QMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() ); if(!testOnly) item->setGeometry( QRect(QPoint(wx, y), s) ); wx = wx + s.width() + spacing(); minSizeWidth = minSizeWidth + spacing() + itemMinSize.width(); sizeHintWidth = sizeHintWidth + spacing() + itemSizeHint.width(); lineMinHeight = QMAX( lineMinHeight, itemMinSize.height() ); ++it2; } sizeHint = sizeHint.expandedTo( QSize(sizeHintWidth, 0) ); minSize = minSize.expandedTo( QSize(minSizeWidth, 0) ); minSizeHeight = minSizeHeight + spacing() + lineMinHeight; // start a new line y = y + spacing() + h; h = 0; x = r.x(); currentLine.clear(); expandingWidgets = 0; availableSpace = r.width() + spacing(); } x = x + spacing() + oSizeHint.width(); h = QMAX( h, oSizeHint.height() ); currentLine.append(o); if(o->expanding() == QSizePolicy::Horizontally || o->expanding() == QSizePolicy::BothDirections) ++expandingWidgets; availableSpace = QMAX(0, availableSpace - spacing() - oSizeHint.width()); ++it; } // don't forget to layout the last line QPtrListIterator it2(currentLine); QLayoutItem *item; int wx = r.x(); int sizeHintWidth = 0 -spacing(), minSizeWidth=0 - spacing(), lineMinHeight=0; while( (item = it2.current()) != 0 ) { QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take QSize itemMinSize = item->minimumSize(); // a while to get them QSize s; if(m_justify) { if(expandingWidgets != 0) { if(item->expanding() == QSizePolicy::Horizontally || item->expanding() == QSizePolicy::BothDirections) s = QSize( QMIN(itemSizeHint.width() + availableSpace / expandingWidgets , r.width()), itemSizeHint.height() ); else s = QSize( QMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() ); } else s = QSize( QMIN(itemSizeHint.width() + availableSpace / (int)currentLine.count() , r.width()), itemSizeHint.height() ); } else s = QSize ( QMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() ); if(!testOnly) item->setGeometry( QRect(QPoint(wx, y), s) ); wx = wx + s.width() + spacing(); minSizeWidth = minSizeWidth + spacing() + itemMinSize.width(); sizeHintWidth = sizeHintWidth + spacing() + itemSizeHint.width(); lineMinHeight = QMAX( lineMinHeight, itemMinSize.height() ); ++it2; } sizeHint = sizeHint.expandedTo( QSize(sizeHintWidth, y + spacing() + h) ); minSizeHeight = minSizeHeight + spacing() + lineMinHeight; minSize = minSize.expandedTo( QSize(minSizeWidth, minSizeHeight) ); // store sizeHint() and minimumSize() m_cached_sizeHint = sizeHint + QSize(2* margin(), 2*margin()); m_cached_minSize = minSize + QSize(2* margin() , 2*margin()); // return our height return y + h - r.y(); } int KexiFlowLayout::doVerticalLayout(const QRect &r, bool testOnly) { int x = r.x(); int y = r.y(); int w = 0; // width of this line int availableSpace = r.height() + spacing(); int expandingWidgets=0; // number of widgets in the line with QSizePolicy == Expanding QPtrListIterator it(m_list); QPtrList currentLine; QLayoutItem *o; QSize minSize, sizeHint(20, 20); int minSizeWidth = 0 - spacing(); while ( (o = it.current()) != 0 ) { if(o->isEmpty()) { /// do not consider hidden widgets ++it; continue; } QSize oSizeHint = o->sizeHint(); // we cache these ones because it can take a while to get it (eg for child layouts) if (y + oSizeHint.height() > r.bottom() && w > 0) { // do the layout of current line QPtrListIterator it2(currentLine); QLayoutItem *item; int wy = r.y(); int sizeHintHeight = 0 - spacing(), minSizeHeight = 0 - spacing(), colMinWidth=0; while( (item = it2.current()) != 0 ) { QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take QSize itemMinSize = item->minimumSize(); // a while to get them QSize s; if(m_justify) { if(expandingWidgets != 0) { if(item->expanding() == QSizePolicy::Vertically || item->expanding() == QSizePolicy::BothDirections) s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height() + availableSpace / expandingWidgets , r.height()) ); else s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height(), r.height()) ); } else s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height() + availableSpace / (int)currentLine.count() , r.height()) ); } else s = QSize ( itemSizeHint.width(), QMIN(itemSizeHint.height(), r.height()) ); if(!testOnly) item->setGeometry( QRect(QPoint(x, wy), s) ); wy = wy + s.height() + spacing(); minSizeHeight = minSizeHeight + spacing() + itemMinSize.height(); sizeHintHeight = sizeHintHeight + spacing() + itemSizeHint.height(); colMinWidth = QMAX( colMinWidth, itemMinSize.width() ); ++it2; } sizeHint = sizeHint.expandedTo( QSize(0, sizeHintHeight) ); minSize = minSize.expandedTo( QSize(0, minSizeHeight) ); minSizeWidth = minSizeWidth + spacing() + colMinWidth; // start a new column x = x + spacing() + w; w = 0; y = r.y(); currentLine.clear(); expandingWidgets = 0; availableSpace = r.height() + spacing(); } y = y + spacing() + oSizeHint.height(); w = QMAX( w, oSizeHint.width() ); currentLine.append(o); if(o->expanding() == QSizePolicy::Vertically || o->expanding() == QSizePolicy::BothDirections) ++expandingWidgets; availableSpace = QMAX(0, availableSpace - spacing() - oSizeHint.height()); ++it; } // don't forget to layout the last line QPtrListIterator it2(currentLine); QLayoutItem *item; int wy = r.y(); int sizeHintHeight = 0 - spacing(), minSizeHeight = 0 - spacing(), colMinWidth=0; while( (item = it2.current()) != 0 ) { QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take QSize itemMinSize = item->minimumSize(); // a while to get them QSize s; if(m_justify) { if(expandingWidgets != 0) { if(item->expanding() == QSizePolicy::Vertically || item->expanding() == QSizePolicy::BothDirections) s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height() + availableSpace / expandingWidgets , r.height()) ); else s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height(), r.height()) ); } else s = QSize( itemSizeHint.width(), QMIN(itemSizeHint.height() + availableSpace / (int)currentLine.count() , r.height()) ); } else s = QSize ( itemSizeHint.width(), QMIN(itemSizeHint.height(), r.height()) ); if(!testOnly) item->setGeometry( QRect(QPoint(x, wy), s) ); wy = wy + s.height() + spacing(); minSizeHeight = minSizeHeight + spacing() + itemMinSize.height(); sizeHintHeight = sizeHintHeight + spacing() + itemSizeHint.height(); colMinWidth = QMAX( colMinWidth, itemMinSize.width() ); ++it2; } sizeHint = sizeHint.expandedTo( QSize( x + spacing() + w, sizeHintHeight) ); minSizeWidth = minSizeWidth + spacing() + colMinWidth; minSize = minSize.expandedTo( QSize(minSizeWidth, minSizeHeight) ); // store sizeHint() and minimumSize() m_cached_sizeHint = sizeHint + QSize(2* margin(), 2*margin()); m_cached_minSize = minSize + QSize(2* margin(), 2*margin()); // return our width return x + w - r.x(); }