/* ====================================================================
* 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 <qcursor.h>
#include <qframe.h>
// sys
#include <assert.h>
///////////////////////////////////////////////////////////////////////////////
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 );
}
///////////////////////////////////////////////////////////////////////////////
syntax highlighted by Code2HTML, v. 0.9.1