/*************************************************************************** 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 #include #include #include #include #include #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, QWidget * parent, const char *name) : QSplitter( Horizontal, parent, name ), m_settings( settings ) { QFrame *scrollFrame = new QFrame( parent, "scrollFrame" ); reparent( scrollFrame, *(new QPoint()), false ); // Set up the scrollFrame scrollFrame->setFrameStyle( QFrame::NoFrame | QFrame::Plain ); scrollFrame->setLineWidth(scrollFrame->style().pixelMetric(QStyle::PM_DefaultFrameWidth)); QGridLayout *pairlayout = new QGridLayout(scrollFrame, 2, 2, 0); m_vScroll = new QScrollBar( QScrollBar::Vertical, scrollFrame ); pairlayout->addWidget( m_vScroll, 0, 1 ); m_hScroll = new QScrollBar( QScrollBar::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( QFrame::StyledPanel | QFrame::Sunken ); setLineWidth( style().pixelMetric( QStyle::PM_DefaultFrameWidth ) ); setHandleWidth(50); setChildrenCollapsible( false ); setFrameStyle( QFrame::NoFrame ); setSizePolicy( QSizePolicy (QSizePolicy::Ignored, QSizePolicy::Ignored )); setOpaqueResize( true ); setFocusPolicy( QWidget::WheelFocus ); connect( this, SIGNAL(configChanged()), SLOT(slotConfigChanged()) ); connect( this, SIGNAL(configChanged()), SLOT(slotDelayedRepaintHandles()) ); connect( this, SIGNAL(configChanged()), SLOT(slotDelayedUpdateScrollBars()) ); // scrolling connect( m_vScroll, SIGNAL(valueChanged(int)), SLOT(scrollToId(int)) ); connect( m_vScroll, SIGNAL(sliderMoved(int)), SLOT(scrollToId(int)) ); connect( m_hScroll, SIGNAL(valueChanged(int)), SIGNAL(setXOffset(int)) ); connect( m_hScroll, SIGNAL(sliderMoved(int)), SIGNAL(setXOffset(int)) ); m_scrollTimer=new QTimer(); restartTimer = false; connect (m_scrollTimer, SIGNAL(timeout()), SLOT(timerTimeout()) ); // we need to receive childEvents now so that d->list is ready for when // slotSetSelection(...) arrives kapp->sendPostedEvents(this, QEvent::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.) */ QSplitterLayoutStruct *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. */ QSplitterLayoutStruct *s; KompareConnectWidgetFrame *newHandle = 0; if ( d->list.count() > 0 ) { s = new QSplitterLayoutStruct; s->resizeMode = KeepSize; QString 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 QSplitterLayoutStruct; 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( QChildEvent *c ) { if ( c->type() == QEvent::ChildInserted ) { if ( !c->child()->isWidgetType() ) return; if ( ((QWidget*)c->child())->testWFlags( WType_TopLevel ) ) return; QSplitterLayoutStruct *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() == QEvent::ChildRemoved ) { QSplitterLayoutStruct *prev = 0; if ( d->list.count() > 1 ) prev = d->list.at( 1 ); // yes, this is correct QSplitterLayoutStruct *curr = d->list.first(); while ( curr ) { if ( curr->wid == c->child() ) { d->list.removeRef( curr ); if ( prev && prev->isHandle ) { QWidget *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. QSize qSmartMinSize( QWidget *w ); static QPoint toggle( QWidget *w, QPoint pos ) { QSize minS = qSmartMinSize( w ); return -pos - QPoint( minS.width(), minS.height() ); } static bool isCollapsed( QWidget *w ) { return w->x() < 0 || w->y() < 0; } static QPoint topLeft( QWidget *w ) { if ( isCollapsed(w) ) { return toggle( w, w->pos() ); } else { return w->pos(); } } static QPoint bottomRight( QWidget *w ) { if ( isCollapsed(w) ) { return toggle( w, w->pos() ) - QPoint( 1, 1 ); } else { return w->geometry().bottomRight(); } } void KompareSplitter::moveSplitter( QCOORD p, int id ) { QSplitterLayoutStruct *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()]; QWidget *w = 0; bool upLeft; if ( QApplication::reverseLayout() && orient == 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 ) { QSplitterLayoutStruct *s; QWidget *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 = QMAX( pick(qSmartMinSize(w)), QMIN(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 ) { QSplitterLayoutStruct *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 ) { QSplitterLayoutStruct *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 ) { QSplitterLayoutStruct *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 ) { QSplitterLayoutStruct *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 ) { QSplitterLayoutStruct *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 ) { QSplitterLayoutStruct *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() { QSplitterLayoutStruct *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() { QSplitterLayoutStruct *curr; for ( curr = d->list.first(); curr; curr = d->list.next() ) if ( curr->isHandle ) ((KompareConnectWidgetFrame*)curr->wid)->wid()->slotDelayedRepaint(); } void KompareSplitter::repaintHandles() { QSplitterLayoutStruct *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( QWheelEvent* e ) { // scroll lines... if ( e->orientation() == Qt::Vertical ) { if ( e->state() & Qt::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() & Qt::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( QKeyEvent* 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() { QTimer::singleShot( 0, this, 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() { QTimer::singleShot( 0, this, 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() { QSplitterLayoutStruct *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() { QSplitterLayoutStruct *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() { QSplitterLayoutStruct *curr; for ( curr = d->list.first(); curr; curr = d->list.next() ) { if ( !curr->isHandle ) { KompareListView *view = ((KompareListViewFrame*) curr->wid)->view(); return view->visibleHeight() - QStyle::PM_ScrollBarExtent; } } return 1; } bool KompareSplitter::needVScrollBar() { QSplitterLayoutStruct *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() { QSplitterLayoutStruct *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() { QSplitterLayoutStruct *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() { QSplitterLayoutStruct *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() { QSplitterLayoutStruct *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() { QSplitterLayoutStruct *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 QSplitterLayoutStruct *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"