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.
tdesdk/kompare/komparepart/komparesplitter.cpp

713 lines
18 KiB

/***************************************************************************
komparesplitter.cpp - description
-------------------
begin : Wed Jan 14 2004
copyright : (C) 2004 by Jeff Snyder
email : jeff@caffeinated.me.uk
****************************************************************************/
/***************************************************************************
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
***************************************************************************/
#include "komparesplitter.h"
#include <tqstyle.h>
#include <tqstring.h>
#include <tqtimer.h>
#include <kdebug.h>
#include <tdeapplication.h>
#include <tdeglobalsettings.h>
#include "diffmodel.h"
#include "difference.h"
#include "viewsettings.h"
#include "kompare_part.h"
#include "komparelistview.h"
#include "kompareconnectwidget.h"
using namespace Diff2;
KompareSplitter::KompareSplitter( ViewSettings *settings, TQWidget * parent,
const char *name) :
TQSplitter(TQt::Horizontal, parent, name ),
m_settings( settings )
{
TQFrame *scrollFrame = new TQFrame( parent, "scrollFrame" );
reparent( scrollFrame, *(new TQPoint()), false );
// Set up the scrollFrame
scrollFrame->setFrameStyle( TQFrame::NoFrame | TQFrame::Plain );
scrollFrame->setLineWidth(scrollFrame->style().pixelMetric(TQStyle::PM_DefaultFrameWidth));
TQGridLayout *pairlayout = new TQGridLayout(scrollFrame, 2, 2, 0);
m_vScroll = new TQScrollBar( TQt::Vertical, scrollFrame );
pairlayout->addWidget( m_vScroll, 0, 1 );
m_hScroll = new TQScrollBar( TQt::Horizontal, scrollFrame );
pairlayout->addWidget( m_hScroll, 1, 0 );
new KompareListViewFrame(true, m_settings, this, "source");
new KompareListViewFrame(false, m_settings, this, "destination");
pairlayout->addWidget( this, 0, 0 );
// set up our looks
setFrameStyle( TQFrame::StyledPanel | TQFrame::Sunken );
setLineWidth( style().pixelMetric( TQStyle::PM_DefaultFrameWidth ) );
setHandleWidth(50);
setChildrenCollapsible( false );
setFrameStyle( TQFrame::NoFrame );
setSizePolicy( TQSizePolicy (TQSizePolicy::Ignored, TQSizePolicy::Ignored ));
setOpaqueResize( true );
setFocusPolicy( TQWidget::WheelFocus );
connect( this, TQ_SIGNAL(configChanged()), TQ_SLOT(slotConfigChanged()) );
connect( this, TQ_SIGNAL(configChanged()), TQ_SLOT(slotDelayedRepaintHandles()) );
connect( this, TQ_SIGNAL(configChanged()), TQ_SLOT(slotDelayedUpdateScrollBars()) );
// scrolling
connect( m_vScroll, TQ_SIGNAL(valueChanged(int)), TQ_SLOT(scrollToId(int)) );
connect( m_vScroll, TQ_SIGNAL(sliderMoved(int)), TQ_SLOT(scrollToId(int)) );
connect( m_hScroll, TQ_SIGNAL(valueChanged(int)), TQ_SIGNAL(setXOffset(int)) );
connect( m_hScroll, TQ_SIGNAL(sliderMoved(int)), TQ_SIGNAL(setXOffset(int)) );
m_scrollTimer=new TQTimer();
restartTimer = false;
connect (m_scrollTimer, TQ_SIGNAL(timeout()), TQ_SLOT(timerTimeout()) );
// we need to receive childEvents now so that d->list is ready for when
// slotSetSelection(...) arrives
kapp->sendPostedEvents(this, TQEvent::ChildInserted);
// init stuff
slotUpdateScrollBars();
}
KompareSplitter::~KompareSplitter(){}
/*
Inserts the widget \a w at the end (or at the beginning if \a
prepend is TRUE) of the splitter's list of widgets.
It is the responsibility of the caller to make sure that \a w is
not already in the splitter and to call recalcId() if needed. (If
\a prepend is TRUE, then recalcId() is very probably needed.)
*/
TQSplitterLayoutStruct *KompareSplitter::addWidget( KompareListViewFrame *w, bool prepend )
{
/* This function is *not* a good place to make connections to and from
* the KompareListView. Make them in the KompareListViewFrame constructor
* instad - that way the connections get made upon creation, not upon the
* next processing of the event queue. */
TQSplitterLayoutStruct *s;
KompareConnectWidgetFrame *newHandle = 0;
if ( d->list.count() > 0 ) {
s = new TQSplitterLayoutStruct;
s->resizeMode = KeepSize;
TQString tmp = "qt_splithandle_";
tmp += w->name();
KompareListView *lw =
((KompareListViewFrame*)(prepend?w:d->list.last()->wid))->view();
KompareListView *rw =
((KompareListViewFrame*)(prepend?d->list.first()->wid:w))->view();
newHandle = new KompareConnectWidgetFrame(lw, rw, m_settings, this, tmp.latin1());
s->wid = newHandle;
newHandle->setId( d->list.count() );
s->isHandle = TRUE;
s->sizer = pick( newHandle->sizeHint() );
if ( prepend )
d->list.prepend( s );
else
d->list.append( s );
}
s = new TQSplitterLayoutStruct;
s->resizeMode = DefaultResizeMode;
s->wid = w;
s->isHandle = FALSE;
if ( prepend ) d->list.prepend( s );
else d->list.append( s );
if ( newHandle && isVisible() )
newHandle->show(); // will trigger sending of post events
return s;
}
/*!
Tells the splitter that the child widget described by \a c has
been inserted or removed.
*/
void KompareSplitter::childEvent( TQChildEvent *c )
{
if ( c->type() == TQEvent::ChildInserted ) {
if ( !c->child()->isWidgetType() )
return;
if ( ((TQWidget*)c->child())->testWFlags( WType_TopLevel ) )
return;
TQSplitterLayoutStruct *s = d->list.first();
while ( s ) {
if ( s->wid == c->child() )
return;
s = d->list.next();
}
addWidget( (KompareListViewFrame*)c->child() );
recalc( isVisible() );
} else if ( c->type() == TQEvent::ChildRemoved ) {
TQSplitterLayoutStruct *prev = 0;
if ( d->list.count() > 1 )
prev = d->list.at( 1 ); // yes, this is correct
TQSplitterLayoutStruct *curr = d->list.first();
while ( curr ) {
if ( curr->wid == c->child() ) {
d->list.removeRef( curr );
if ( prev && prev->isHandle ) {
TQWidget *w = prev->wid;
d->list.removeRef( prev );
delete w; // will call childEvent()
}
recalcId();
doResize();
return;
}
prev = curr;
curr = d->list.next();
}
}
}
// This is from a private qt header (kernel/qlayoutengine_p.h). sorry.
TQSize qSmartMinSize( TQWidget *w );
static TQPoint toggle( TQWidget *w, TQPoint pos )
{
TQSize minS = qSmartMinSize( w );
return -pos - TQPoint( minS.width(), minS.height() );
}
static bool isCollapsed( TQWidget *w )
{
return w->x() < 0 || w->y() < 0;
}
static TQPoint topLeft( TQWidget *w )
{
if ( isCollapsed(w) ) {
return toggle( w, w->pos() );
} else {
return w->pos();
}
}
static TQPoint bottomRight( TQWidget *w )
{
if ( isCollapsed(w) ) {
return toggle( w, w->pos() ) - TQPoint( 1, 1 );
} else {
return w->geometry().bottomRight();
}
}
void KompareSplitter::moveSplitter( TQCOORD p, int id )
{
TQSplitterLayoutStruct *s = d->list.at( id );
int farMin;
int min;
int max;
int farMax;
p = adjustPos( p, id, &farMin, &min, &max, &farMax );
int oldP = pick( s->wid->pos() );
int* poss = new int[d->list.count()];
int* ws = new int[d->list.count()];
TQWidget *w = 0;
bool upLeft;
if ( TQApplication::reverseLayout() && orient ==TQt::Horizontal ) {
int q = p + s->wid->width();
doMove( FALSE, q, id - 1, -1, (p > max), poss, ws );
doMove( TRUE, q, id, -1, (p < min), poss, ws );
upLeft = (q > oldP);
} else {
doMove( FALSE, p, id, +1, (p > max), poss, ws );
doMove( TRUE, p, id - 1, +1, (p < min), poss, ws );
upLeft = (p < oldP);
}
if ( upLeft ) {
int count = d->list.count();
for ( int id = 0; id < count; ++id ) {
w = d->list.at( id )->wid;
if( !w->isHidden() )
setGeo( w, poss[id], ws[id], TRUE );
}
} else {
for ( int id = d->list.count() - 1; id >= 0; --id ) {
w = d->list.at( id )->wid;
if( !w->isHidden() )
setGeo( w, poss[id], ws[id], TRUE );
}
}
storeSizes();
}
void KompareSplitter::doMove( bool backwards, int pos, int id, int delta,
bool mayCollapse, int* positions, int* widths )
{
TQSplitterLayoutStruct *s;
TQWidget *w;
for ( ; id >= 0 && id < (int)d->list.count();
id = backwards ? id - delta : id + delta ) {
s = d->list.at( id );
w = s->wid;
if ( w->isHidden() ) {
mayCollapse = TRUE;
} else {
if ( s->isHandle ) {
int dd = s->getSizer( orient );
int nextPos = backwards ? pos - dd : pos + dd;
positions[id] = pos;
widths[id] = dd;
pos = nextPos;
} else {
int dd = backwards ? pos - pick( topLeft(w) )
: pick( bottomRight(w) ) - pos + 1;
if ( dd > 0 || (!isCollapsed(w) && !mayCollapse) ) {
dd = TQMAX( pick(qSmartMinSize(w)),
TQMIN(dd, pick(w->maximumSize())) );
} else {
dd = 0;
}
positions[id] = backwards ? pos - dd : pos;
widths[id] = dd;
pos = backwards ? pos - dd : pos + dd;
mayCollapse = TRUE;
}
}
}
}
void KompareSplitter::slotSetSelection( const DiffModel* model, const Difference* diff )
{
TQSplitterLayoutStruct *curr;
for ( curr = d->list.first(); curr; curr = d->list.next() )
{
if ( curr->isHandle )
((KompareConnectWidgetFrame*)
curr->wid)->wid()->slotSetSelection( model, diff );
else
{
((KompareListViewFrame*)
curr->wid)->view()->slotSetSelection( model, diff );
((KompareListViewFrame*)
curr->wid)->slotSetModel( model );
}
}
slotDelayedRepaintHandles();
slotDelayedUpdateScrollBars();
}
void KompareSplitter::slotSetSelection( const Difference* diff )
{
TQSplitterLayoutStruct *curr;
for ( curr = d->list.first(); curr; curr = d->list.next() )
if ( curr->isHandle )
((KompareConnectWidgetFrame*)
curr->wid)->wid()->slotSetSelection( diff );
else
((KompareListViewFrame*)
curr->wid)->view()->slotSetSelection( diff );
slotDelayedRepaintHandles();
slotDelayedUpdateScrollBars();
}
void KompareSplitter::slotApplyDifference( bool apply )
{
TQSplitterLayoutStruct *curr;
for ( curr = d->list.first(); curr; curr = d->list.next() )
if ( !curr->isHandle )
((KompareListViewFrame*)
curr->wid)->view()->slotApplyDifference( apply );
slotDelayedRepaintHandles();
}
void KompareSplitter::slotApplyAllDifferences( bool apply )
{
TQSplitterLayoutStruct *curr;
for ( curr = d->list.first(); curr; curr = d->list.next() )
if ( !curr->isHandle )
((KompareListViewFrame*)
curr->wid)->view()->slotApplyAllDifferences( apply );
slotDelayedRepaintHandles();
scrollToId( scrollTo ); // FIXME!
}
void KompareSplitter::slotApplyDifference( const Difference* diff, bool apply )
{
TQSplitterLayoutStruct *curr;
for ( curr = d->list.first(); curr; curr = d->list.next() )
if ( !curr->isHandle )
((KompareListViewFrame*)
curr->wid)->view()->slotApplyDifference( diff, apply );
slotDelayedRepaintHandles();
}
void KompareSplitter::slotDifferenceClicked( const Difference* diff )
{
TQSplitterLayoutStruct *curr;
for ( curr = d->list.first(); curr; curr = d->list.next() )
if ( !curr->isHandle )
((KompareListViewFrame*)
curr->wid)->view()->setSelectedDifference( diff, false );
emit selectionChanged( diff );
}
void KompareSplitter::slotConfigChanged()
{
TQSplitterLayoutStruct *curr;
for ( curr = d->list.first(); curr; curr = d->list.next() ) {
if ( !curr->isHandle )
{
((KompareListViewFrame*)
curr->wid)->view()->setSpaces( m_settings->m_tabToNumberOfSpaces );
((KompareListViewFrame*)
curr->wid)->view()->setFont( m_settings->m_font );
((KompareListViewFrame*)
curr->wid)->view()->update();
}
}
}
void KompareSplitter::slotDelayedRepaintHandles()
{
TQSplitterLayoutStruct *curr;
for ( curr = d->list.first(); curr; curr = d->list.next() )
if ( curr->isHandle )
((KompareConnectWidgetFrame*)curr->wid)->wid()->slotDelayedRepaint();
}
void KompareSplitter::repaintHandles()
{
TQSplitterLayoutStruct *curr;
for ( curr = d->list.first(); curr; curr = d->list.next() )
if ( curr->isHandle )
((KompareConnectWidgetFrame*)curr->wid)->wid()->repaint();
}
// Scrolling stuff
/*
* limit updating to once every 50 msec with a qtimer
* FIXME: i'm a nasty hack
*/
void KompareSplitter::wheelEvent( TQWheelEvent* e )
{
// scroll lines...
if ( e->orientation() == TQt::Vertical )
{
if ( e->state() & TQt::ControlButton )
if ( e->delta() < 0 ) // scroll down one page
m_vScroll->addPage();
else // scroll up one page
m_vScroll->subtractPage();
else
if ( e->delta() < 0 ) // scroll down
m_vScroll->addLine();
else // scroll up
m_vScroll->subtractLine();
}
else
{
if ( e->state() & TQt::ControlButton )
if ( e->delta() < 0 ) // scroll right one page
m_hScroll->addPage();
else // scroll left one page
m_hScroll->subtractPage();
else
if ( e->delta() < 0 ) // scroll to the right
m_hScroll->addLine();
else // scroll to the left
m_hScroll->subtractLine();
}
e->accept();
repaintHandles();
}
void KompareSplitter::keyPressEvent( TQKeyEvent* e )
{
//keyboard scrolling
switch ( e->key() ) {
case Key_Right:
case Key_L:
m_hScroll->addLine();
break;
case Key_Left:
case Key_H:
m_hScroll->subtractLine();
break;
case Key_Up:
case Key_K:
m_vScroll->subtractLine();
break;
case Key_Down:
case Key_J:
m_vScroll->addLine();
break;
case Key_PageDown:
m_vScroll->addPage();
break;
case Key_PageUp:
m_vScroll->subtractPage();
break;
}
e->accept();
repaintHandles();
}
void KompareSplitter::timerTimeout()
{
if ( restartTimer )
restartTimer = false;
else
m_scrollTimer->stop();
slotDelayedRepaintHandles();
emit scrollViewsToId( scrollTo );
}
void KompareSplitter::scrollToId( int id )
{
scrollTo = id;
if( restartTimer )
return;
if( m_scrollTimer->isActive() )
{
restartTimer = true;
}
else
{
emit scrollViewsToId( id );
slotDelayedRepaintHandles();
m_scrollTimer->start(30, false);
}
}
void KompareSplitter::slotDelayedUpdateScrollBars()
{
TQTimer::singleShot( 0, this, TQ_SLOT( slotUpdateScrollBars() ) );
}
void KompareSplitter::slotUpdateScrollBars()
{
int m_scrollDistance = m_settings->m_scrollNoOfLines * lineSpacing();
int m_pageSize = pageSize();
if( needVScrollBar() )
{
m_vScroll->show();
m_vScroll->blockSignals( true );
m_vScroll->setRange( minVScrollId(),
maxVScrollId() );
m_vScroll->setValue( scrollId() );
m_vScroll->setSteps( m_scrollDistance, m_pageSize );
m_vScroll->blockSignals( false );
}
else
{
m_vScroll->hide();
}
if( needHScrollBar() )
{
m_hScroll->show();
m_hScroll->blockSignals( true );
m_hScroll->setRange( 0, maxHScrollId() );
m_hScroll->setValue( maxContentsX() );
m_hScroll->setSteps( 10, minVisibleWidth() - 10 );
m_hScroll->blockSignals( false );
}
else
{
m_hScroll->hide();
}
}
void KompareSplitter::slotDelayedUpdateVScrollValue()
{
TQTimer::singleShot( 0, this, TQ_SLOT( slotUpdateVScrollValue() ) );
}
void KompareSplitter::slotUpdateVScrollValue()
{
m_vScroll->setValue( scrollId() );
}
/* FIXME: this should return the scrollId() from the listview containing the
* /base/ of the diff. but there's bigger issues with that atm.
*/
int KompareSplitter::scrollId()
{
TQSplitterLayoutStruct *curr = d->list.first();
for ( curr = d->list.first(); curr; curr = d->list.next() )
if ( !curr->isHandle )
return ((KompareListViewFrame*) curr->wid)->view()->scrollId();
return minVScrollId();
}
int KompareSplitter::lineSpacing()
{
TQSplitterLayoutStruct *curr = d->list.first();
for ( curr = d->list.first(); curr; curr = d->list.next() )
if ( !curr->isHandle )
return ((KompareListViewFrame*)
curr->wid)->view()->fontMetrics().lineSpacing();
return 1;
}
int KompareSplitter::pageSize()
{
TQSplitterLayoutStruct *curr;
for ( curr = d->list.first(); curr; curr = d->list.next() )
{
if ( !curr->isHandle )
{
KompareListView *view = ((KompareListViewFrame*) curr->wid)->view();
return view->visibleHeight() - TQStyle::PM_ScrollBarExtent;
}
}
return 1;
}
bool KompareSplitter::needVScrollBar()
{
TQSplitterLayoutStruct *curr;
int pagesize = pageSize();
for ( curr = d->list.first(); curr; curr = d->list.next() )
{
if( !curr->isHandle )
{
KompareListView *view = ((KompareListViewFrame*) curr->wid)->view();
if( view ->contentsHeight() > pagesize)
return true;
}
}
return false;
}
int KompareSplitter::minVScrollId()
{
TQSplitterLayoutStruct *curr;
int min = -1;
int mSId;
for ( curr = d->list.first(); curr; curr = d->list.next() )
{
if(!curr->isHandle) {
KompareListView* view = ((KompareListViewFrame*)curr->wid)->view();
mSId = view->minScrollId();
if (mSId < min || min == -1) min = mSId;
}
}
return ( min == -1 ) ? 0 : min;
}
int KompareSplitter::maxVScrollId()
{
TQSplitterLayoutStruct *curr;
int max = 0;
int mSId;
for ( curr = d->list.first(); curr; curr = d->list.next() )
{
if ( !curr->isHandle )
{
mSId = ((KompareListViewFrame*)curr->wid)->view()->maxScrollId();
if ( mSId > max )
max = mSId;
}
}
return max;
}
bool KompareSplitter::needHScrollBar()
{
TQSplitterLayoutStruct *curr;
for ( curr = d->list.first(); curr; curr = d->list.next() )
{
if( !curr->isHandle )
{
KompareListView *view = ((KompareListViewFrame*) curr->wid)->view();
if ( view->contentsWidth() > view->visibleWidth() )
return true;
}
}
return false;
}
int KompareSplitter::maxHScrollId()
{
TQSplitterLayoutStruct *curr;
int max = 0;
int mHSId;
for ( curr = d->list.first(); curr; curr = d->list.next() )
{
if( !curr->isHandle )
{
KompareListView *view = ((KompareListViewFrame*) curr->wid)->view();
mHSId = view->contentsWidth() - view->visibleWidth();
if ( mHSId > max )
max = mHSId;
}
}
return max;
}
int KompareSplitter::maxContentsX()
{
TQSplitterLayoutStruct *curr;
int max = 0;
int mCX;
for ( curr = d->list.first(); curr; curr = d->list.next() )
{
if ( !curr->isHandle )
{
mCX = ((KompareListViewFrame*) curr->wid)->view()->contentsX();
if ( mCX > max )
max = mCX;
}
}
return max;
}
int KompareSplitter::minVisibleWidth()
{
// Why the hell do we want to know this?
// ah yes, its because we use it to set the "page size" for horiz. scrolling.
// despite the fact that *noone* has a pgright and pgleft key :P
// But we do have mousewheels with horizontal scrolling functionality,
// pressing shift and scrolling then goes left and right one page at the time
TQSplitterLayoutStruct *curr;
int min = -1;
int vW;
for( curr = d->list.first(); curr; curr = d->list.next() )
{
if ( !curr->isHandle ) {
vW = ((KompareListViewFrame*)curr->wid)->view()->visibleWidth();
if ( vW < min || min == -1 )
min = vW;
}
}
return ( min == -1 ) ? 0 : min;
}
#include "komparesplitter.moc"