/* ==================================================================== * Copyright (c) 2003-2006, Martin Hauner * http://subcommander.tigris.org * * Subcommander is licensed as described in the file doc/COPYING, which * you should have received as part of this distribution. * ==================================================================== */ // sc #include "SplitLayout.h" // qt #include #include // sys #include /////////////////////////////////////////////////////////////////////////////// class SplitHandleStrategy { public: virtual ~SplitHandleStrategy(){} virtual void setCursor() = 0; virtual void setSizePolicy() = 0; virtual int getPos() = 0; virtual int getMousePos( QMouseEvent* ) = 0; virtual void setSize() = 0; virtual void setGeometry( int ) = 0; }; /////////////////////////////////////////////////////////////////////////////// class SplitHandle : public QFrame { typedef QFrame super; public: enum Orientation { oHorizontal, oVertical }; SplitHandle( QWidget *parent, SplitLayoutHandle* layout, Orientation o ); ~SplitHandle(); void reposition( int newPos ); int getPos() const; protected: void mouseMoveEvent( QMouseEvent* e ); void mousePressEvent( QMouseEvent* e ); void mouseReleaseEvent( QMouseEvent* e ); //void mouseDoubleClickEvent( QMouseEvent* e ); private: SplitHandleStrategy* _strategy; SplitLayoutHandle* _layout; int _lastPos; bool _move; }; /////////////////////////////////////////////////////////////////////////////// class HSplitHandleStrategy : public SplitHandleStrategy { public: HSplitHandleStrategy( SplitHandle* sh ) : _handle(sh) { } void setCursor() { _handle->setCursor( QCursor(Qt::splitHCursor) ); } void setSizePolicy() { _handle->setSizePolicy( QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Expanding) ); } int getMousePos( QMouseEvent* e ) { return e->globalX(); } int getPos() { return _handle->x(); } void setSize() { _handle->setFixedWidth( 5 ); } void setGeometry( int newPos ) { _handle->setGeometry( newPos, _handle->y(), _handle->width(), _handle->height() ); } private: SplitHandle* _handle; }; class VSplitHandleStrategy : public SplitHandleStrategy { public: VSplitHandleStrategy( SplitHandle* sh ) : _handle(sh) { } void setCursor() { _handle->setCursor( QCursor(Qt::splitVCursor) ); } void setSizePolicy() { _handle->setSizePolicy( QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed) ); } int getMousePos( QMouseEvent* e ) { return e->globalY(); } int getPos() { return _handle->y(); } void setSize() { _handle->setFixedHeight( 5 ); } void setGeometry( int newPos ) { _handle->setGeometry( _handle->x(), newPos, _handle->width(), _handle->height() ); } private: SplitHandle* _handle; }; /////////////////////////////////////////////////////////////////////////////// SplitHandle::SplitHandle( QWidget *parent, SplitLayoutHandle* layout, Orientation o ) : QFrame(parent), _layout(layout), _move(false) { if( o == oHorizontal ) { _strategy = new HSplitHandleStrategy(this); } else { _strategy = new VSplitHandleStrategy(this); } _strategy->setCursor(); _strategy->setSize(); _strategy->setSizePolicy(); } SplitHandle::~SplitHandle() { delete _strategy; } void SplitHandle::mouseMoveEvent( QMouseEvent* e ) { int myPos = _strategy->getPos(); int mousePos = _strategy->getMousePos(e); int newPos = _layout->calcMovePos( myPos, mousePos - _lastPos ); _lastPos = mousePos; _move = true; reposition(newPos); } void SplitHandle::mousePressEvent( QMouseEvent* e ) { _lastPos = _strategy->getMousePos(e); } void SplitHandle::mouseReleaseEvent( QMouseEvent* e ) { if( _move ) { _move = ! _move; return; } int newPos = _layout->calcJumpPos( _strategy->getPos() ); reposition(newPos); } //void SplitHandle::mouseDoubleClickEvent( QMouseEvent* e ) //{ // int newPos = _layout->calcJumpPos( _strategy->getPos() ); // reposition(newPos); //} void SplitHandle::reposition( int newPos ) { if( newPos == _strategy->getPos() ) { return; } _strategy->setGeometry( newPos ); _layout->recalculate(); } int SplitHandle::getPos() const { return _strategy->getPos(); } /////////////////////////////////////////////////////////////////////////////// SplitLayout::SplitLayout( QWidget* parent, Pos p ) : QLayout(parent), _stretchOne(0), _stretchTwo(0), _hide(p), _initialized(false), _handlePos(-1) { _items.resize(3); _items[pOne] = 0; _items[pTwo] = 0; } SplitLayout::~SplitLayout() { if( _items[pOne] ) delete _items[pOne]; delete _items[pHandle]; if( _items[pTwo] ) delete _items[pTwo]; } // SplitLayoutHandle void SplitLayout::recalculate() { activate(); } int SplitLayout::calcMovePos( int curPos, int diffPos ) { _items[pOne]->widget()->show(); _items[pTwo]->widget()->show(); int minPos = 0; int maxPos = getSize(geometry()) - getSize(_items[pHandle]->geometry()); int newPos = curPos + diffPos; if( newPos < minPos ) { _items[pOne]->widget()->hide(); newPos = 0; } if( newPos > maxPos ) { _items[pTwo]->widget()->hide(); newPos = maxPos; } return newPos; } int SplitLayout::calcJumpPos( int curPos ) { int maxPos = getSize(geometry()) - getSize(_items[pHandle]->sizeHint()); if( curPos == 0 || curPos == maxPos ) { _items[pOne]->widget()->show(); _items[pTwo]->widget()->show(); if( _handlePos == 0 || _handlePos == maxPos ) { int w = getSize(_lastRect) / (_stretchOne+_stretchTwo); return w * _stretchOne; } else { return _handlePos; } } else { _handlePos = getHandlePos(); _items[_hide]->widget()->hide(); if( _hide == pOne ) { _items[pTwo]->widget()->show(); } else { _items[pOne]->widget()->show(); } return 0; } } // TODO common code with calcJumpPos void SplitLayout::jumpPos( bool visible ) { int maxPos = getSize(geometry()) - getSize(_items[pHandle]->sizeHint()); int w =( getSize(_lastRect) / (_stretchOne+_stretchTwo) ) * _stretchOne; if( visible ) { _items[pOne]->widget()->show(); _items[pTwo]->widget()->show(); ((SplitHandle*)(_items[pHandle]->widget()))->reposition(w); } else { if( _hide == pOne ) { _items[pOne]->widget()->hide(); _items[pTwo]->widget()->show(); ((SplitHandle*)(_items[pHandle]->widget()))->reposition(0); } else { _items[pOne]->widget()->show(); _items[pTwo]->widget()->hide(); ((SplitHandle*)(_items[pHandle]->widget()))->reposition(maxPos); } } } void SplitLayout::addWidgetOne( QWidget* w, bool hide, int stretch ) { _items[pOne] = new QWidgetItem(w); _stretchOne = stretch; if( hide ) { _hide = pOne; w->hide(); } } void SplitLayout::addWidgetTwo( QWidget* w, bool hide, int stretch ) { _items[pTwo] = new QWidgetItem(w); _stretchTwo = stretch; if( hide ) { _hide = pTwo; w->hide(); } } void SplitLayout::addItem( QLayoutItem *item ) { if( !_items[pOne] ) { _items[pOne] = item; return; } if( !_items[pTwo] ) { _items[pTwo] = item; return; } assert(false); } void SplitLayout::enableHandle( bool enable ) { if( enable ) { (_items[pHandle]->widget())->show(); } else { (_items[pHandle]->widget())->hide(); } } QSize SplitLayout::sizeHint() const { QSize s(0,0); for( TLayoutItems::const_iterator it = _items.begin(); it != _items.end(); it++ ) { adjustSize( s, (*it)->sizeHint() ); } return s; } QSize SplitLayout::minimumSize() const { QSize s(0,0); for( TLayoutItems::const_iterator it = _items.begin(); it != _items.end(); it++ ) { adjustSize( s, (*it)->minimumSize() ); } return s; } QLayoutIterator SplitLayout::iterator() { return QLayoutIterator( new SplitLayoutIterator(&_items) ); } void SplitLayout::setGeometry( const QRect &newRect ) { QLayout::setGeometry(newRect); if( _items.empty() ) { assert(false); return; } QLayoutItem* o = _items[pOne]; QLayoutItem* m = _items[pHandle]; QLayoutItem* t = _items[pTwo]; int os = 0; // one size int ms = 0; // handle size int ts = 0; // two size int op = 0; // one pos int mp = 0; // handle pos int tp = 0; // two pos if( o->widget()->isHidden() ) { os = 0; ms = getSize(m->sizeHint()); ts = getSize(newRect) - ms; op = 0; mp = 0; tp = op + ms; } else if( t->widget()->isHidden() ) { ms = getSize(m->sizeHint()); os = getSize(newRect) - ms; ts = 0; op = 0; mp = os; tp = 0; } else { int ds = getSize(newRect) - getSize(_lastRect); // delta size // move handle if( ds == 0 ) { os = getPos(m->geometry()); ms = getSize(m->sizeHint()); ts = getSize(newRect) - os - ms; op = 0; mp = getPos(m->geometry()); tp = mp + ms; } // resize else { os = getPos(m->geometry()); ms = getSize(m->sizeHint()); ts = getSize(_lastRect) - os - ms; op = 0; mp = getPos(m->geometry()); tp = mp + ms; int div = ds / (_stretchOne + _stretchTwo); int mod = ds % (_stretchOne + _stretchTwo); int mod_div = mod / 2; int mod_mod = mod % 1; int ls = _stretchOne * div + mod_div; int rs = _stretchTwo * div + mod_div; if( _stretchOne > _stretchTwo ) { ls += mod_mod; } else { rs += mod_mod; } os += ls; ts += rs; if( os < 0 ) { os = 0; o->widget()->hide(); } if( ts < 0 ) { ts = 0; t->widget()->hide(); } op = 0; mp = os; tp = mp + ms; } } QRect orect = calcRect( op, os, newRect ); o->setGeometry(orect); QRect mrect = calcRect( mp, ms, newRect ); m->setGeometry(mrect); QRect trect = calcRect( tp, ts, newRect ); t->setGeometry(trect); _lastRect = newRect; if( ! _initialized ) { _initialized = true; setHandlePos(_handlePos); } } void SplitLayout::setHandlePos( int pos ) { // the earliest time we can set the handle position is after the // first layout, so defer the initialization if we haven't done it. if( _initialized ) { // we have a handle position? if( pos != -1 ) { ((SplitHandle*)_items[pHandle]->widget())->reposition(pos); } } else { _handlePos = pos; } } int SplitLayout::getHandlePos() const { return ((SplitHandle*)_items[pHandle]->widget())->getPos(); } /////////////////////////////////////////////////////////////////////////////// HSplitLayout::HSplitLayout( QWidget* parent, Pos pos ) : SplitLayout(parent,pos) { _items[pHandle] = new QWidgetItem( new SplitHandle(parent,this,SplitHandle::oHorizontal) ); } int HSplitLayout::getPos( const QRect& r ) const { return r.x(); } int HSplitLayout::getSize( const QRect& r ) const { return r.width(); } int HSplitLayout::getSize( const QSize& s ) const { return s.width(); } void HSplitLayout::adjustSize( QSize& out, const QSize& item ) const { out.setWidth( out.width() + item.width() ); out.setHeight( out.height() < item.height() ? item.height() : out.height() ); } QRect HSplitLayout::calcRect( int pos, int size, const QRect& r ) const { return QRect( pos, r.y(), size, r.height() ); } /////////////////////////////////////////////////////////////////////////////// VSplitLayout::VSplitLayout( QWidget* parent, Pos pos ) : SplitLayout(parent,pos) { _items[pHandle] = new QWidgetItem( new SplitHandle(parent,this,SplitHandle::oVertical) ); } int VSplitLayout::getPos( const QRect& r ) const { return r.y(); } int VSplitLayout::getSize( const QRect& r ) const { return r.height(); } int VSplitLayout::getSize( const QSize& s ) const { return s.height(); } void VSplitLayout::adjustSize( QSize& out, const QSize& item ) const { out.setHeight( out.height() + item.height() ); out.setWidth( out.width() < item.width() ? item.width() : out.width() ); } QRect VSplitLayout::calcRect( int pos, int size, const QRect& r ) const { return QRect( r.x(), pos, r.width(), size ); } ///////////////////////////////////////////////////////////////////////////////