/* 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 TQGLayoutIterator { public: KexiFlowLayoutIterator( TQPtrList *list ) : m_idx(0), m_list( list ) {} uint count() const; TQLayoutItem *current(); TQLayoutItem *next(); TQLayoutItem *takeCurrent(); private: int m_idx; TQPtrList *m_list; }; uint KexiFlowLayoutIterator::count() const { return m_list->count(); } TQLayoutItem * KexiFlowLayoutIterator::current() { return (m_idx < (int)count()) ? m_list->at(m_idx) : 0; } TQLayoutItem * KexiFlowLayoutIterator::next() { m_idx++; return current(); } TQLayoutItem * KexiFlowLayoutIterator::takeCurrent() { return (m_idx < (int)count()) ? m_list->take(m_idx) : 0; } //// The layout itself KexiFlowLayout::KexiFlowLayout(TQWidget *parent, int border, int space, const char *name) : TQLayout(parent, border, space, name) { m_orientation =TQt::Horizontal; m_justify = false; m_cached_width = 0; } KexiFlowLayout::KexiFlowLayout(TQLayout* parent, int space, const char *name) : TQLayout( parent, space, name ) { m_orientation =TQt::Horizontal; m_justify = false; m_cached_width = 0; } KexiFlowLayout::KexiFlowLayout(int space, const char *name) : TQLayout(space, name) { m_orientation =TQt::Horizontal; m_justify = false; m_cached_width = 0; } KexiFlowLayout::~KexiFlowLayout() { deleteAllItems(); } void KexiFlowLayout::addItem(TQLayoutItem *item) { m_list.append(item); } void KexiFlowLayout::addSpacing(int size) { if (m_orientation ==TQt::Horizontal) addItem( new TQSpacerItem( size, 0, TQSizePolicy::Fixed, TQSizePolicy::Minimum ) ); else addItem( new TQSpacerItem( 0, size, TQSizePolicy::Minimum, TQSizePolicy::Fixed ) ); } TQLayoutIterator KexiFlowLayout::iterator() { return TQLayoutIterator( new KexiFlowLayoutIterator(&m_list) ); } TQPtrList* KexiFlowLayout::widgetList() const { TQPtrList *list = new TQPtrList(); for (TQPtrListIterator it(m_list); it.current(); ++it) { if(it.current()->widget()) list->append(it.current()->widget()); } return list; } void KexiFlowLayout::invalidate() { TQLayout::invalidate(); m_cached_sizeHint = TQSize(); m_cached_minSize = TQSize(); m_cached_width = 0; } bool KexiFlowLayout::isEmpty() { return m_list.isEmpty(); } bool KexiFlowLayout::hasHeightForWidth() const { return (m_orientation ==TQt::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( TQRect(0,0,w,0) ); mthis->m_cached_hfw = h; mthis->m_cached_width = w; return h; } return m_cached_hfw; } TQSize KexiFlowLayout::sizeHint() const { if(m_cached_sizeHint.isEmpty()) { KexiFlowLayout *mthis = (KexiFlowLayout*)this; TQRect r = TQRect(0, 0, 2000, 2000); mthis->simulateLayout(r); } return m_cached_sizeHint; } TQSize 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; TQRect r = TQRect(0, 0, 2000, 2000); mthis->simulateLayout(r); } #endif return m_cached_minSize; } TQSizePolicy::ExpandData KexiFlowLayout::expanding() const { if(m_orientation == TQt::Vertical) return TQSizePolicy::Vertically; else return TQSizePolicy::Horizontally; } void KexiFlowLayout::setGeometry(const TQRect &r) { TQLayout::setGeometry(r); if(m_orientation ==TQt::Horizontal) doHorizontalLayout(r); else doVerticalLayout(r); } int KexiFlowLayout::simulateLayout(const TQRect &r) { if(m_orientation ==TQt::Horizontal) return doHorizontalLayout(r, true); else return doVerticalLayout(r, true); } int KexiFlowLayout::doHorizontalLayout(const TQRect &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 TQSizePolicy == Expanding TQPtrListIterator it(m_list); TQPtrList currentLine; TQLayoutItem *o; TQSize 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; TQSize 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 TQPtrListIterator it2(currentLine); TQLayoutItem *item; int wx = r.x(); int sizeHintWidth = 0 -spacing(), minSizeWidth=0 - spacing(), lineMinHeight=0; while( (item = it2.current()) != 0 ) { TQSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take TQSize itemMinSize = item->minimumSize(); // a while to get them TQSize s; if(m_justify) { if(expandingWidgets != 0) { if(item->expanding() == TQSizePolicy::Horizontally || item->expanding() == TQSizePolicy::BothDirections) s = TQSize( TQMIN(itemSizeHint.width() + availableSpace / expandingWidgets , r.width()), itemSizeHint.height() ); else s = TQSize( TQMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() ); } else s = TQSize( TQMIN(itemSizeHint.width() + availableSpace / (int)currentLine.count() , r.width()), itemSizeHint.height() ); } else s = TQSize ( TQMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() ); if(!testOnly) item->setGeometry( TQRect(TQPoint(wx, y), s) ); wx = wx + s.width() + spacing(); minSizeWidth = minSizeWidth + spacing() + itemMinSize.width(); sizeHintWidth = sizeHintWidth + spacing() + itemSizeHint.width(); lineMinHeight = TQMAX( lineMinHeight, itemMinSize.height() ); ++it2; } sizeHint = sizeHint.expandedTo( TQSize(sizeHintWidth, 0) ); minSize = minSize.expandedTo( TQSize(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 = TQMAX( h, oSizeHint.height() ); currentLine.append(o); if(o->expanding() == TQSizePolicy::Horizontally || o->expanding() == TQSizePolicy::BothDirections) ++expandingWidgets; availableSpace = TQMAX(0, availableSpace - spacing() - oSizeHint.width()); ++it; } // don't forget to layout the last line TQPtrListIterator it2(currentLine); TQLayoutItem *item; int wx = r.x(); int sizeHintWidth = 0 -spacing(), minSizeWidth=0 - spacing(), lineMinHeight=0; while( (item = it2.current()) != 0 ) { TQSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take TQSize itemMinSize = item->minimumSize(); // a while to get them TQSize s; if(m_justify) { if(expandingWidgets != 0) { if(item->expanding() == TQSizePolicy::Horizontally || item->expanding() == TQSizePolicy::BothDirections) s = TQSize( TQMIN(itemSizeHint.width() + availableSpace / expandingWidgets , r.width()), itemSizeHint.height() ); else s = TQSize( TQMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() ); } else s = TQSize( TQMIN(itemSizeHint.width() + availableSpace / (int)currentLine.count() , r.width()), itemSizeHint.height() ); } else s = TQSize ( TQMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() ); if(!testOnly) item->setGeometry( TQRect(TQPoint(wx, y), s) ); wx = wx + s.width() + spacing(); minSizeWidth = minSizeWidth + spacing() + itemMinSize.width(); sizeHintWidth = sizeHintWidth + spacing() + itemSizeHint.width(); lineMinHeight = TQMAX( lineMinHeight, itemMinSize.height() ); ++it2; } sizeHint = sizeHint.expandedTo( TQSize(sizeHintWidth, y + spacing() + h) ); minSizeHeight = minSizeHeight + spacing() + lineMinHeight; minSize = minSize.expandedTo( TQSize(minSizeWidth, minSizeHeight) ); // store sizeHint() and minimumSize() m_cached_sizeHint = sizeHint + TQSize(2* margin(), 2*margin()); m_cached_minSize = minSize + TQSize(2* margin() , 2*margin()); // return our height return y + h - r.y(); } int KexiFlowLayout::doVerticalLayout(const TQRect &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 TQSizePolicy == Expanding TQPtrListIterator it(m_list); TQPtrList currentLine; TQLayoutItem *o; TQSize minSize, sizeHint(20, 20); int minSizeWidth = 0 - spacing(); while ( (o = it.current()) != 0 ) { if(o->isEmpty()) { /// do not consider hidden widgets ++it; continue; } TQSize 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 TQPtrListIterator it2(currentLine); TQLayoutItem *item; int wy = r.y(); int sizeHintHeight = 0 - spacing(), minSizeHeight = 0 - spacing(), colMinWidth=0; while( (item = it2.current()) != 0 ) { TQSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take TQSize itemMinSize = item->minimumSize(); // a while to get them TQSize s; if(m_justify) { if(expandingWidgets != 0) { if(item->expanding() == TQSizePolicy::Vertically || item->expanding() == TQSizePolicy::BothDirections) s = TQSize( itemSizeHint.width(), TQMIN(itemSizeHint.height() + availableSpace / expandingWidgets , r.height()) ); else s = TQSize( itemSizeHint.width(), TQMIN(itemSizeHint.height(), r.height()) ); } else s = TQSize( itemSizeHint.width(), TQMIN(itemSizeHint.height() + availableSpace / (int)currentLine.count() , r.height()) ); } else s = TQSize ( itemSizeHint.width(), TQMIN(itemSizeHint.height(), r.height()) ); if(!testOnly) item->setGeometry( TQRect(TQPoint(x, wy), s) ); wy = wy + s.height() + spacing(); minSizeHeight = minSizeHeight + spacing() + itemMinSize.height(); sizeHintHeight = sizeHintHeight + spacing() + itemSizeHint.height(); colMinWidth = TQMAX( colMinWidth, itemMinSize.width() ); ++it2; } sizeHint = sizeHint.expandedTo( TQSize(0, sizeHintHeight) ); minSize = minSize.expandedTo( TQSize(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 = TQMAX( w, oSizeHint.width() ); currentLine.append(o); if(o->expanding() == TQSizePolicy::Vertically || o->expanding() == TQSizePolicy::BothDirections) ++expandingWidgets; availableSpace = TQMAX(0, availableSpace - spacing() - oSizeHint.height()); ++it; } // don't forget to layout the last line TQPtrListIterator it2(currentLine); TQLayoutItem *item; int wy = r.y(); int sizeHintHeight = 0 - spacing(), minSizeHeight = 0 - spacing(), colMinWidth=0; while( (item = it2.current()) != 0 ) { TQSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take TQSize itemMinSize = item->minimumSize(); // a while to get them TQSize s; if(m_justify) { if(expandingWidgets != 0) { if(item->expanding() == TQSizePolicy::Vertically || item->expanding() == TQSizePolicy::BothDirections) s = TQSize( itemSizeHint.width(), TQMIN(itemSizeHint.height() + availableSpace / expandingWidgets , r.height()) ); else s = TQSize( itemSizeHint.width(), TQMIN(itemSizeHint.height(), r.height()) ); } else s = TQSize( itemSizeHint.width(), TQMIN(itemSizeHint.height() + availableSpace / (int)currentLine.count() , r.height()) ); } else s = TQSize ( itemSizeHint.width(), TQMIN(itemSizeHint.height(), r.height()) ); if(!testOnly) item->setGeometry( TQRect(TQPoint(x, wy), s) ); wy = wy + s.height() + spacing(); minSizeHeight = minSizeHeight + spacing() + itemMinSize.height(); sizeHintHeight = sizeHintHeight + spacing() + itemSizeHint.height(); colMinWidth = TQMAX( colMinWidth, itemMinSize.width() ); ++it2; } sizeHint = sizeHint.expandedTo( TQSize( x + spacing() + w, sizeHintHeight) ); minSizeWidth = minSizeWidth + spacing() + colMinWidth; minSize = minSize.expandedTo( TQSize(minSizeWidth, minSizeHeight) ); // store sizeHint() and minimumSize() m_cached_sizeHint = sizeHint + TQSize(2* margin(), 2*margin()); m_cached_minSize = minSize + TQSize(2* margin(), 2*margin()); // return our width return x + w - r.x(); }