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/komparelistview.cpp

784 lines
23 KiB

/***************************************************************************
komparelistview.h - description
-------------------
begin : Sun Mar 4 2001
copyright : (C) 2001-2004 Otto Bruggeman
(C) 2001-2003 John Firebaugh
(C) 2004 Jeff Snyder
email : otto.bruggeman@home.nl
jfirebaugh@kde.org
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 <tqheader.h>
#include <tqpainter.h>
#include <tqregexp.h>
#include <tqtimer.h>
#include <kdebug.h>
#include <kglobal.h>
#include <kglobalsettings.h>
#include "diffmodel.h"
#include "diffhunk.h"
#include "difference.h"
#include "viewsettings.h"
#include "komparemodellist.h"
#include "komparesplitter.h"
#include "komparelistview.h"
#define COL_LINE_NO 0
#define COL_MAIN 1
#define BLANK_LINE_HEIGHT 3
#define HUNK_LINE_HEIGHT 5
using namespace Diff2;
KompareListViewFrame::KompareListViewFrame( bool isSource,
ViewSettings* settings,
KompareSplitter* parent,
const char* name ):
TQFrame ( parent, name ),
m_view ( isSource, settings, this, name ),
m_label ( isSource?"Source":"Dest", this ),
m_layout ( this )
{
setSizePolicy ( TQSizePolicy(TQSizePolicy::Ignored, TQSizePolicy::Ignored) );
m_label.setSizePolicy ( TQSizePolicy(TQSizePolicy::Ignored, TQSizePolicy::Fixed) );
TQFrame *bottomLine = new TQFrame(this);
bottomLine->setFrameShape(TQFrame::HLine);
bottomLine->setFrameShadow ( TQFrame::Plain );
bottomLine->setSizePolicy ( TQSizePolicy(TQSizePolicy::Ignored, TQSizePolicy::Fixed) );
bottomLine->setFixedHeight(1);
m_label.setMargin(3);
m_layout.setSpacing(0);
m_layout.setMargin(0);
m_layout.addWidget(&m_label);
m_layout.addWidget(bottomLine);
m_layout.addWidget(&m_view);
connect( &m_view, TQT_SIGNAL(differenceClicked(const Diff2::Difference*)),
parent, TQT_SLOT(slotDifferenceClicked(const Diff2::Difference*)) );
connect( parent, TQT_SIGNAL(scrollViewsToId(int)), &m_view, TQT_SLOT(scrollToId(int)) );
connect( parent, TQT_SIGNAL(setXOffset(int)), &m_view, TQT_SLOT(setXOffset(int)) );
connect( &m_view, TQT_SIGNAL(resized()), parent, TQT_SLOT(slotUpdateScrollBars()) );
}
void KompareListViewFrame::slotSetModel( const DiffModel* model )
{
if( model )
{
if( view()->isSource() ) {
if( !model->sourceRevision().isEmpty() )
m_label.setText( model->sourceFile() + " (" + model->sourceRevision() + ")" );
else
m_label.setText( model->sourceFile() );
} else {
if( !model->destinationRevision().isEmpty() )
m_label.setText( model->destinationFile() + " (" + model->destinationRevision() + ")" );
else
m_label.setText( model->destinationFile() );
}
} else {
m_label.setText( TQString() );
}
}
KompareListView::KompareListView( bool isSource,
ViewSettings* settings,
TQWidget* parent, const char* name ) :
KListView( parent, name ),
m_isSource( isSource ),
m_settings( settings ),
m_scrollId( -1 ),
m_selectedModel( 0 ),
m_selectedDifference( 0 )
{
header()->hide();
addColumn( "Line Number", 0 );
addColumn( "Main" );
addColumn( "Blank" );
setColumnAlignment( COL_LINE_NO, AlignRight );
setAllColumnsShowFocus( true );
setRootIsDecorated( false );
setSorting( -1 );
setItemMargin( 3 );
setTreeStepSize( 0 );
setColumnWidthMode( COL_LINE_NO, Maximum );
setColumnWidthMode( COL_MAIN, Maximum );
setResizeMode( LastColumn );
setFrameStyle( TQFrame::NoFrame );
setVScrollBarMode( TQScrollView::AlwaysOff );
setHScrollBarMode( TQScrollView::AlwaysOff );
setFocusPolicy( TQ_NoFocus );
setFont( m_settings->m_font );
setSpaces( m_settings->m_tabToNumberOfSpaces );
setFocusProxy( parent->parentWidget() );
}
KompareListView::~KompareListView()
{
}
KompareListViewItem* KompareListView::itemAtIndex( int i )
{
return m_items[ i ];
}
int KompareListView::firstVisibleDifference()
{
TQListViewItem* item = itemAt( TQPoint( 0, 0 ) );
if( item == 0 )
{
kdDebug(8104) << "no item at viewport coordinates (0,0)" << endl;
}
while( item ) {
KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>(item);
if( lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged )
break;
item = item->itemBelow();
}
if( item )
return m_items.findIndex( ((KompareListViewLineItem*)item)->diffItemParent() );
return -1;
}
int KompareListView::lastVisibleDifference()
{
TQListViewItem* item = itemAt( TQPoint( 0, visibleHeight() - 1 ) );
if( item == 0 )
{
kdDebug(8104) << "no item at viewport coordinates (0," << visibleHeight() - 1 << ")" << endl;
item = lastItem();
}
while( item ) {
KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>(item);
if( lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged )
break;
item = item->itemAbove();
}
if( item )
return m_items.findIndex( ((KompareListViewLineItem*)item)->diffItemParent() );
return -1;
}
TQRect KompareListView::itemRect( int i )
{
TQListViewItem* item = itemAtIndex( i );
int x = 0;
int y = itemPos( item );
int vx, vy;
contentsToViewport( x, y, vx, vy );
return TQRect( vx, vy, 0, item->totalHeight() );
}
int KompareListView::minScrollId()
{
return visibleHeight() / 2;
}
int KompareListView::maxScrollId()
{
KompareListViewItem* item = (KompareListViewItem*)firstChild();
if(!item) return 0;
while( item && item->nextSibling() ) {
item = (KompareListViewItem*)item->nextSibling();
}
int maxId = item->scrollId() + item->maxHeight() - minScrollId();
kdDebug(8104) << "Max ID = " << maxId << endl;
return maxId;
}
int KompareListView::contentsWidth()
{
return ( columnWidth(COL_LINE_NO) + columnWidth(COL_MAIN) );
}
void KompareListView::setXOffset( int x )
{
kdDebug(8104) << "SetXOffset : Scroll to x position: " << x << endl;
setContentsPos( x, contentsY() );
}
void KompareListView::scrollToId( int id )
{
// kdDebug(8104) << "ScrollToID : Scroll to id : " << id << endl;
KompareListViewItem* item = (KompareListViewItem*)firstChild();
while( item && item->nextSibling() ) {
if( ((KompareListViewItem*)item->nextSibling())->scrollId() > id )
break;
item = (KompareListViewItem*)item->nextSibling();
}
if( item ) {
int pos = item->itemPos();
int itemId = item->scrollId();
int height = item->totalHeight();
double r = (double)( id - itemId ) / (double)item->maxHeight();
int y = pos + (int)( r * (double)height ) - minScrollId();
// kdDebug(8104) << "scrollToID: " << endl;
// kdDebug(8104) << " id = " << id << endl;
// kdDebug(8104) << " id after = " << ( item->nextSibling() ? TQString::number( ((KompareListViewItem*)item->nextSibling())->scrollId() ) : "no such sibling..." ) << endl;
// kdDebug(8104) << " pos = " << pos << endl;
// kdDebug(8104) << " itemId = " << itemId << endl;
// kdDebug(8104) << " r = " << r << endl;
// kdDebug(8104) << " height = " << height << endl;
// kdDebug(8104) << " minID = " << minScrollId() << endl;
// kdDebug(8104) << " y = " << y << endl;
// kdDebug(8104) << "contentsHeight = " << contentsHeight() << endl;
// kdDebug(8104) << " c - y = " << contentsHeight() - y << endl;
setContentsPos( contentsX(), y );
}
m_scrollId = id;
}
int KompareListView::scrollId()
{
if( m_scrollId < 0 )
m_scrollId = minScrollId();
return m_scrollId;
}
void KompareListView::setSelectedDifference( const Difference* diff, bool scroll )
{
kdDebug(8104) << "KompareListView::setSelectedDifference(" << diff << ", " << scroll << ")" << endl;
// When something other than a click causes this function to be called,
// it'll only get called once, and all is simple.
//
// When the user clicks on a diff, this function will get called once when
// komparesplitter::slotDifferenceClicked runs, and again when the
// setSelection signal from the modelcontroller arrives.
//
// the first call (which will always be from the splitter) will have
// scroll==false, and the the second call will bail out here.
// Which is why clicking on a difference does not cause the listviews to
// scroll.
if ( m_selectedDifference == diff )
return;
m_selectedDifference = diff;
KompareListViewItem* item = m_itemDict[ (void*)diff ];
if( !item ) {
kdDebug(8104) << "KompareListView::slotSetSelection(): couldn't find our selection!" << endl;
return;
}
// why does this not happen when the user clicks on a diff? see the comment above.
if( scroll )
scrollToId(item->scrollId());
setSelected( item, true );
}
void KompareListView::slotSetSelection( const Difference* diff )
{
kdDebug(8104) << "KompareListView::slotSetSelection( const Difference* diff )" << endl;
setSelectedDifference( diff, true );
}
void KompareListView::slotSetSelection( const DiffModel* model, const Difference* diff )
{
kdDebug(8104) << "KompareListView::slotSetSelection( const DiffModel* model, const Difference* diff )" << endl;
if( m_selectedModel && m_selectedModel == model ) {
slotSetSelection( diff );
return;
}
clear();
m_items.clear();
m_itemDict.clear();
m_selectedModel = model;
m_itemDict.resize(model->differenceCount());
DiffHunkListConstIterator hunkIt = model->hunks()->begin();
DiffHunkListConstIterator hEnd = model->hunks()->end();
KompareListViewItem* item = 0;
Difference* tmpdiff = 0;
DiffHunk* hunk = 0;
for ( ; hunkIt != hEnd; ++hunkIt )
{
hunk = *hunkIt;
if( item )
item = new KompareListViewHunkItem( this, item, hunk, model->isBlended() );
else
item = new KompareListViewHunkItem( this, hunk, model->isBlended() );
DifferenceListConstIterator diffIt = hunk->differences().begin();
DifferenceListConstIterator dEnd = hunk->differences().end();
for ( ; diffIt != dEnd; ++diffIt )
{
tmpdiff = *diffIt;
item = new KompareListViewDiffItem( this, item, tmpdiff );
int type = tmpdiff->type();
if ( type != Difference::Unchanged )
{
m_items.append( (KompareListViewDiffItem*)item );
m_itemDict.insert( tmpdiff, (KompareListViewDiffItem*)item );
}
}
}
slotSetSelection( diff );
}
void KompareListView::contentsMousePressEvent( TQMouseEvent* e )
{
TQPoint vp = contentsToViewport( e->pos() );
KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>( itemAt( vp ) );
if( !lineItem )
return;
KompareListViewDiffItem* diffItem = lineItem->diffItemParent();
if( diffItem->difference()->type() != Difference::Unchanged ) {
emit differenceClicked( diffItem->difference() );
}
}
void KompareListView::contentsMouseDoubleClickEvent( TQMouseEvent* e )
{
TQPoint vp = contentsToViewport( e->pos() );
KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>( itemAt( vp ) );
if ( !lineItem )
return;
KompareListViewDiffItem* diffItem = lineItem->diffItemParent();
if ( diffItem->difference()->type() != Difference::Unchanged ) {
// FIXME: make a new signal that does both
emit differenceClicked( diffItem->difference() );
emit applyDifference( !diffItem->difference()->applied() );
}
}
void KompareListView::slotApplyDifference( bool apply )
{
m_itemDict[ (void*)m_selectedDifference ]->applyDifference( apply );
}
void KompareListView::slotApplyAllDifferences( bool apply )
{
TQPtrDictIterator<KompareListViewDiffItem> it ( m_itemDict );
for( ; it.current(); ++it )
it.current()->applyDifference( apply );
repaint();
}
void KompareListView::slotApplyDifference( const Difference* diff, bool apply )
{
m_itemDict[ (void*)diff ]->applyDifference( apply );
}
void KompareListView::setSpaces( int spaces )
{
m_spaces.truncate( 0 );
kdDebug( 8104 ) << "tabToNumberOfSpaces: " << spaces << endl;
for ( int i = 0; i < spaces; i++ )
m_spaces += " ";
triggerUpdate();
}
void KompareListView::wheelEvent( TQWheelEvent* e )
{
e->ignore(); // we want the parent to catch wheel events
}
void KompareListView::resizeEvent( TQResizeEvent* e )
{
KListView::resizeEvent(e);
emit resized();
kdDebug() << "resizeEvent " << endl;
}
KompareListViewItem::KompareListViewItem( KompareListView* parent )
: TQListViewItem( parent ),
m_scrollId( 0 )
{
// kdDebug(8104) << "Created KompareListViewItem with scroll id " << m_scrollId << endl;
}
KompareListViewItem::KompareListViewItem( KompareListView* parent, KompareListViewItem* after )
: TQListViewItem( parent, after ),
m_scrollId( after->scrollId() + after->maxHeight() )
{
// kdDebug(8104) << "Created KompareListViewItem with scroll id " << m_scrollId << endl;
}
KompareListViewItem::KompareListViewItem( KompareListViewItem* parent )
: TQListViewItem( parent ),
m_scrollId( 0 )
{
}
KompareListViewItem::KompareListViewItem( KompareListViewItem* parent, KompareListViewItem* /*after*/ )
: TQListViewItem( parent ),
m_scrollId( 0 )
{
}
KompareListView* KompareListViewItem::kompareListView() const
{
return (KompareListView*)listView();
}
void KompareListViewItem::paintFocus( TQPainter* /* p */, const TQColorGroup& /* cg */, const TQRect& /* r */ )
{
// Don't paint anything
}
KompareListViewDiffItem::KompareListViewDiffItem( KompareListView* parent, Difference* difference )
: KompareListViewItem( parent ),
m_difference( difference ),
m_sourceItem( 0L ),
m_destItem( 0L )
{
init();
}
KompareListViewDiffItem::KompareListViewDiffItem( KompareListView* parent, KompareListViewItem* after, Difference* difference )
: KompareListViewItem( parent, after ),
m_difference( difference ),
m_sourceItem( 0L ),
m_destItem( 0L )
{
init();
}
void KompareListViewDiffItem::init()
{
setExpandable( true );
setOpen( true );
m_destItem = new KompareListViewLineContainerItem( this, false );
m_sourceItem = new KompareListViewLineContainerItem( this, true );
setVisibility();
}
void KompareListViewDiffItem::setup()
{
KompareListViewItem::setup();
setHeight( 0 );
}
void KompareListViewDiffItem::setVisibility()
{
m_sourceItem->setVisible( kompareListView()->isSource() || m_difference->applied() );
m_destItem->setVisible( !m_sourceItem->isVisible() );
}
void KompareListViewDiffItem::applyDifference( bool apply )
{
kdDebug(8104) << "KompareListViewDiffItem::applyDifference( " << apply << " )" << endl;
setVisibility();
setup();
repaint();
}
int KompareListViewDiffItem::maxHeight()
{
int lines = TQMAX( m_difference->sourceLineCount(), m_difference->destinationLineCount() );
if( lines == 0 )
return BLANK_LINE_HEIGHT;
else
return lines * listView()->fontMetrics().lineSpacing();
}
void KompareListViewDiffItem::setSelected( bool b )
{
kdDebug(8104) << "KompareListViewDiffItem::setSelected( " << b << " )" << endl;
KompareListViewItem::setSelected( b );
TQListViewItem* item = m_sourceItem->isVisible() ?
m_sourceItem->firstChild() :
m_destItem->firstChild();
while( item && item->isVisible() ) {
item->repaint();
item = item->nextSibling();
}
}
KompareListViewLineContainerItem::KompareListViewLineContainerItem( KompareListViewDiffItem* parent, bool isSource )
: KompareListViewItem( parent ),
m_isSource( isSource )
{
// kdDebug(8104) << "isSource ? " << (isSource ? " Yes!" : " No!") << endl;
setExpandable( true );
setOpen( true );
int lines = lineCount();
int line = lineNumber() + lines - 1;
// kdDebug(8104) << "LineNumber : " << lineNumber() << endl;
if( lines == 0 ) {
new KompareListViewBlankLineItem( this );
return;
}
for( int i = lines - 1; i >= 0; i--, line-- ) {
new KompareListViewLineItem( this, line, lineAt( i ) );
}
}
void KompareListViewLineContainerItem::setup()
{
KompareListViewItem::setup();
setHeight( 0 );
}
KompareListViewDiffItem* KompareListViewLineContainerItem::diffItemParent() const
{
return (KompareListViewDiffItem*)parent();
}
int KompareListViewLineContainerItem::lineCount() const
{
return m_isSource ? diffItemParent()->difference()->sourceLineCount() :
diffItemParent()->difference()->destinationLineCount();
}
int KompareListViewLineContainerItem::lineNumber() const
{
return m_isSource ? diffItemParent()->difference()->sourceLineNumber() :
diffItemParent()->difference()->destinationLineNumber();
}
DifferenceString* KompareListViewLineContainerItem::lineAt( int i ) const
{
return m_isSource ? diffItemParent()->difference()->sourceLineAt( i ) :
diffItemParent()->difference()->destinationLineAt( i );
}
KompareListViewLineItem::KompareListViewLineItem( KompareListViewLineContainerItem* parent, int line, DifferenceString* text )
: KompareListViewItem( parent )
{
setText( COL_LINE_NO, TQString::number( line ) );
setText( COL_MAIN, text->string() );
m_text = text;
}
void KompareListViewLineItem::setup()
{
KompareListViewItem::setup();
setHeight( listView()->fontMetrics().lineSpacing() );
}
void KompareListViewLineItem::paintCell( TQPainter * p, const TQColorGroup & cg, int column, int width, int align )
{
TQColor bg = cg.base();
p->fillRect( 0, 0, width, height(), bg );
if ( diffItemParent()->difference()->type() == Difference::Unchanged )
{
if ( column == COL_LINE_NO )
{
bg = cg.background();
p->fillRect( 0, 0, width, height(), bg );
}
}
else
{
bg = kompareListView()->settings()->colorForDifferenceType(
diffItemParent()->difference()->type(),
diffItemParent()->isSelected(),
diffItemParent()->difference()->applied() );
if ( column != COL_MAIN )
p->fillRect( 0, 0, width, height(), bg );
}
p->setPen( cg.foreground() );
paintText( p, bg, column, width, align );
if ( diffItemParent()->isSelected() )
{
p->setPen( bg.dark(135) );
if ( this == parent()->firstChild() )
p->drawLine( 0, 0, width, 0 );
if ( nextSibling() == 0 )
p->drawLine( 0, height() - 1, width, height() - 1 );
}
}
void KompareListViewLineItem::paintText( TQPainter* p, const TQColor& bg, int column, int width, int align )
{
if ( column == COL_MAIN )
{
TQString textChunk;
int offset = listView()->itemMargin();
unsigned int prevValue = 0;
int chunkWidth;
TQBrush changeBrush( bg, Dense3Pattern );
TQBrush normalBrush( bg, SolidPattern );
TQBrush brush;
if ( m_text->string().isEmpty() )
{
p->fillRect( 0, 0, width, height(), normalBrush );
return;
}
p->fillRect( 0, 0, offset, height(), normalBrush );
if ( !m_text->markerList().isEmpty() )
{
MarkerListConstIterator markerIt = m_text->markerList().begin();
MarkerListConstIterator mEnd = m_text->markerList().end();
Marker* m = *markerIt;
for ( ; markerIt != mEnd; ++markerIt )
{
m = *markerIt;
textChunk = m_text->string().mid( prevValue, m->offset() - prevValue );
// kdDebug(8104) << "TextChunk = \"" << textChunk << "\"" << endl;
// kdDebug(8104) << "c->offset() = " << c->offset() << endl;
// kdDebug(8104) << "prevValue = " << prevValue << endl;
textChunk.replace( TQChar('\t'), kompareListView()->spaces() );
prevValue = m->offset();
if ( m->type() == Marker::End )
{
TQFont font( p->font() );
font.setBold( true );
p->setFont( font );
// p->setPen( TQt::blue );
brush = changeBrush;
}
else
{
TQFont font( p->font() );
font.setBold( false );
p->setFont( font );
// p->setPen( TQt::black );
brush = normalBrush;
}
chunkWidth = p->fontMetrics().width( textChunk );
p->fillRect( offset, 0, chunkWidth, height(), brush );
p->drawText( offset, 0,
chunkWidth, height(),
align, textChunk );
offset += chunkWidth;
}
}
if ( prevValue < m_text->string().length() )
{
// Still have to draw some string without changes
textChunk = m_text->string().mid( prevValue, kMax( ( unsigned int )1, m_text->string().length() - prevValue ) );
textChunk.replace( TQChar('\t'), kompareListView()->spaces() );
// kdDebug(8104) << "TextChunk = \"" << textChunk << "\"" << endl;
TQFont font( p->font() );
font.setBold( false );
p->setFont( font );
chunkWidth = p->fontMetrics().width( textChunk );
p->fillRect( offset, 0, chunkWidth, height(), normalBrush );
p->drawText( offset, 0,
chunkWidth, height(),
align, textChunk );
offset += chunkWidth;
}
p->fillRect( offset, 0, width - offset, height(), normalBrush );
}
else
{
p->fillRect( 0, 0, width, height(), bg );
p->drawText( listView()->itemMargin(), 0,
width - listView()->itemMargin(), height(),
align, text( column ) );
}
}
KompareListViewDiffItem* KompareListViewLineItem::diffItemParent() const
{
KompareListViewLineContainerItem* p = (KompareListViewLineContainerItem*)parent();
return p->diffItemParent();
}
KompareListViewBlankLineItem::KompareListViewBlankLineItem( KompareListViewLineContainerItem* parent )
: KompareListViewLineItem( parent, 0, new DifferenceString() )
{
}
void KompareListViewBlankLineItem::setup()
{
KompareListViewLineItem::setup();
setHeight( BLANK_LINE_HEIGHT );
}
void KompareListViewBlankLineItem::paintText( TQPainter* p, const TQColor& bg, int column, int width, int )
{
if ( column == COL_MAIN )
{
TQBrush normalBrush( bg, SolidPattern );
p->fillRect( 0, 0, width, height(), normalBrush );
}
}
KompareListViewHunkItem::KompareListViewHunkItem( KompareListView* parent, DiffHunk* hunk, bool zeroHeight )
: KompareListViewItem( parent ),
m_zeroHeight( zeroHeight ),
m_hunk( hunk )
{
setSelectable( false );
}
KompareListViewHunkItem::KompareListViewHunkItem( KompareListView* parent, KompareListViewItem* after, DiffHunk* hunk, bool zeroHeight )
: KompareListViewItem( parent, after ),
m_zeroHeight( zeroHeight ),
m_hunk( hunk )
{
setSelectable( false );
}
int KompareListViewHunkItem::maxHeight()
{
if( m_zeroHeight ) {
return 0;
} else if( m_hunk->function().isEmpty() ) {
return HUNK_LINE_HEIGHT;
} else {
return listView()->fontMetrics().lineSpacing();
}
}
void KompareListViewHunkItem::setup()
{
KompareListViewItem::setup();
setHeight( maxHeight() );
}
void KompareListViewHunkItem::paintCell( TQPainter * p, const TQColorGroup & cg, int column, int width, int align )
{
p->fillRect( 0, 0, width, height(), cg.mid() );
if( column == COL_MAIN ) {
p->drawText( listView()->itemMargin(), 0, width - listView()->itemMargin(), height(),
align, m_hunk->function() );
}
}
#include "komparelistview.moc"