/* 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 =Qt::Horizontal; m_justify = false; m_cached_width = 0; } KexiFlowLayout::KexiFlowLayout(TQLayout* parent, int space, const char *name) : TQLayout( parent, space, name ) { m_orientation =Qt::Horizontal; m_justify = false; m_cached_width = 0; } KexiFlowLayout::KexiFlowLayout(int space, const char *name) : TQLayout(space, name) { m_orientation =Qt::Horizontal; m_justify = false; m_cached_width = 0; } KexiFlowLayout::~KexiFlowLayout() { deleteAllItems(); } void KexiFlowLayout::addItem(TQLayoutItem *item) { m_list.append(TQT_TQLAYOUTITEM(item)); } void KexiFlowLayout::addSpacing(int size) { if (m_orientation ==Qt::Horizontal) addItem( TQT_TQLAYOUTITEM(new TQSpacerItem( size, 0, TQSizePolicy::Fixed, TQSizePolicy::Minimum )) ); else addItem( TQT_TQLAYOUTITEM(new TQSpacerItem( 0, size, TQSizePolicy::Minimum, TQSizePolicy::Fixed )) ); } TQLayoutIterator KexiFlowLayout::iterator() { // [FIXME] #ifdef USE_QT4 #warning [FIXME] ContainerAreaLayout iterators may not function correctly under Qt4 return TQLayoutIterator( this ); // [FIXME] #else // USE_QT4 return TQLayoutIterator( new KexiFlowLayoutIterator(&m_list) ); #endif // USE_QT4 } 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 ==Qt::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 == Qt::Vertical) return TQSizePolicy::Vertically; else return TQSizePolicy::Horizontally; } void KexiFlowLayout::setGeometry(const TQRect &r) { TQLayout::setGeometry(r); if(m_orientation ==Qt::Horizontal) doHorizontalLayout(r); else doVerticalLayout(r); } int KexiFlowLayout::simulateLayout(const TQRect &r) { if(m_orientation ==Qt::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(); } #ifdef USE_QT4 /*! \reimp */ int KexiFlowLayout::count() const { return m_list.count(); } /*! \reimp */ TQLayoutItem* KexiFlowLayout::itemAt(int index) const { return index >= 0 && index < m_list.count() ? (const_cast&>(m_list).at(index)) : 0; } /*! \reimp */ TQLayoutItem* KexiFlowLayout::takeAt(int index) { if (index < 0 || index >= m_list.count()) return 0; TQLayoutItem *item = m_list.at(index); m_list.remove(m_list.at(index)); delete item; invalidate(); return item; } #endif // USE_QT4