You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kexi/widget/utils/kexiflowlayout.cpp

452 lines
14 KiB

/* This file is part of the KDE project
Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
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 <kdebug.h>
/// Iterator class
class KexiFlowLayoutIterator : public TQGLayoutIterator
{
public:
KexiFlowLayoutIterator( TQPtrList<TQLayoutItem> *list )
: m_idx(0), m_list( list )
{}
uint count() const;
TQLayoutItem *current();
TQLayoutItem *next();
TQLayoutItem *takeCurrent();
private:
int m_idx;
TQPtrList<TQLayoutItem> *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()
{
return TQLayoutIterator( new KexiFlowLayoutIterator(&m_list) );
}
TQPtrList<TQWidget>*
KexiFlowLayout::widgetList() const
{
TQPtrList<TQWidget> *list = new TQPtrList<TQWidget>();
for (TQPtrListIterator<TQLayoutItem> 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<TQLayoutItem> it(m_list);
TQPtrList<TQLayoutItem> 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<TQLayoutItem> 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<TQLayoutItem> 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<TQLayoutItem> it(m_list);
TQPtrList<TQLayoutItem> 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<TQLayoutItem> 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<TQLayoutItem> 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();
}