|
|
|
/* 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 =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<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 ==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<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();
|
|
|
|
}
|