komparelistview.cpp 28.4 KB
Newer Older
John Firebaugh's avatar
John Firebaugh committed
1
/***************************************************************************
2
3
                                komparelistview.h
                                -----------------
John Firebaugh's avatar
John Firebaugh committed
4
        begin                   : Sun Mar 4 2001
5
        Copyright 2001-2009 Otto Bruggeman <bruggie@gmail.com>
6
7
        Copyright 2001-2003 John Firebaugh <jfirebaugh@kde.org>
        Copyright 2004      Jeff Snyder    <jeff@caffeinated.me.uk>
8
        Copyright 2007-2011 Kevin Kofler   <kevin.kofler@chello.at>
John Firebaugh's avatar
John Firebaugh committed
9
10
11
12
13
14
15
16
17
18
19
****************************************************************************/

/***************************************************************************
**
**   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.
**
***************************************************************************/

20
21
#include "komparelistview.h"

22
23
24
25
26
27
#include <QtGui/QPainter>
#include <QtCore/QRegExp>
#include <QtCore/QTimer>
#include <QtGui/QResizeEvent>
#include <QtGui/QMouseEvent>
#include <QtGui/QWheelEvent>
28
#include <QtGui/QScrollBar>
John Firebaugh's avatar
John Firebaugh committed
29
30

#include <kdebug.h>
31
#include <kglobal.h>
32
#include <kglobalsettings.h>
John Firebaugh's avatar
John Firebaugh committed
33
34
35
36

#include "diffmodel.h"
#include "diffhunk.h"
#include "difference.h"
37
#include "viewsettings.h"
38
#include "komparemodellist.h"
39
#include "komparesplitter.h"
John Firebaugh's avatar
John Firebaugh committed
40
41
42
43

#define COL_LINE_NO      0
#define COL_MAIN         1

John Firebaugh's avatar
John Firebaugh committed
44
45
46
#define BLANK_LINE_HEIGHT 3
#define HUNK_LINE_HEIGHT  5

47
48
#define ITEM_MARGIN 3

49
using namespace Diff2;
John Firebaugh's avatar
John Firebaugh committed
50

51
52
53
54
KompareListViewFrame::KompareListViewFrame( bool isSource,
                                            ViewSettings* settings,
                                            KompareSplitter* parent,
                                            const char* name ):
Laurent Montel's avatar
Laurent Montel committed
55
	QFrame ( parent ),
56
57
58
59
60
61
	m_view ( isSource, settings, this, name ),
	m_label ( isSource?"Source":"Dest", this ),
	m_layout ( this )
{
	setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored) );
	m_label.setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed) );
Laurent Montel's avatar
Laurent Montel committed
62
63
64
	QFrame *bottomLine = new QFrame(this);
	bottomLine->setFrameShape(QFrame::HLine);
	bottomLine->setFrameShadow ( QFrame::Plain );
65
66
	bottomLine->setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed) );
	bottomLine->setFixedHeight(1);
67
68
69
70
	m_label.setMargin(3);
	m_layout.setSpacing(0);
	m_layout.setMargin(0);
	m_layout.addWidget(&m_label);
71
	m_layout.addWidget(bottomLine);
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
	m_layout.addWidget(&m_view);

	connect( &m_view, SIGNAL(differenceClicked(const Diff2::Difference*)),
	         parent, SLOT(slotDifferenceClicked(const Diff2::Difference*)) );

	connect( parent, SIGNAL(scrollViewsToId(int)), &m_view, SLOT(scrollToId(int)) );
	connect( parent, SIGNAL(setXOffset(int)), &m_view, SLOT(setXOffset(int)) );
	connect( &m_view, SIGNAL(resized()), parent, SLOT(slotUpdateScrollBars()) );
}

void KompareListViewFrame::slotSetModel( const DiffModel* model )
{
	if( model )
	{
		if( view()->isSource() ) {
			if( !model->sourceRevision().isEmpty() )
88
				m_label.setText( model->sourceFile() + " (" + model->sourceRevision() + ')' );
89
90
91
92
			else
				m_label.setText( model->sourceFile() );
		} else {
			if( !model->destinationRevision().isEmpty() )
93
				m_label.setText( model->destinationFile() + " (" + model->destinationRevision() + ')' );
94
95
96
97
			else
				m_label.setText( model->destinationFile() );
		}
	} else {
98
		m_label.setText( QString::null );	//krazy:exclude=nullstrassign for old broken gcc
99
100
101
	}
}

Otto Bruggeman's avatar
   
Otto Bruggeman committed
102
KompareListView::KompareListView( bool isSource,
103
                                  ViewSettings* settings,
Otto Bruggeman's avatar
   
Otto Bruggeman committed
104
                                  QWidget* parent, const char* name ) :
105
	QTreeWidget( parent ),
John Firebaugh's avatar
John Firebaugh committed
106
107
108
	m_isSource( isSource ),
	m_settings( settings ),
	m_scrollId( -1 ),
Otto Bruggeman's avatar
   
Otto Bruggeman committed
109
110
	m_selectedModel( 0 ),
	m_selectedDifference( 0 )
John Firebaugh's avatar
John Firebaugh committed
111
{
112
	setObjectName( name );
113
114
115
	setItemDelegate( new KompareListViewItemDelegate( this ) );
	setHeaderHidden( true );
	setColumnCount( 3 ); // Line Number, Main, Blank
John Firebaugh's avatar
John Firebaugh committed
116
117
	setAllColumnsShowFocus( true );
	setRootIsDecorated( false );
118
	setIndentation( 0 );
Laurent Montel's avatar
Laurent Montel committed
119
	setFrameStyle( QFrame::NoFrame );
120
121
122
	setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
	setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
	setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
123
	setFocusPolicy( Qt::NoFocus );
124
	setFont( m_settings->m_font );
Joshua Keel's avatar
Joshua Keel committed
125
	setFocusProxy( parent->parentWidget() );
John Firebaugh's avatar
John Firebaugh committed
126
127
}

128
KompareListView::~KompareListView()
John Firebaugh's avatar
John Firebaugh committed
129
{
130
131
132
	m_settings = 0;
	m_selectedModel = 0;
	m_selectedDifference = 0;
John Firebaugh's avatar
John Firebaugh committed
133
134
}

135
KompareListViewItem* KompareListView::itemAtIndex( int i )
John Firebaugh's avatar
John Firebaugh committed
136
{
137
	return m_items[ i ];
John Firebaugh's avatar
John Firebaugh committed
138
139
}

140
int KompareListView::firstVisibleDifference()
John Firebaugh's avatar
John Firebaugh committed
141
{
142
	QTreeWidgetItem* item = itemAt( QPoint( 0, 0 ) );
Laurent Montel's avatar
Laurent Montel committed
143

144
	if( item == 0 )
145
	{
Laurent Montel's avatar
Laurent Montel committed
146
		kDebug(8104) << "no item at viewport coordinates (0,0)" << endl;
147
	}
148

149
	while( item ) {
150
151
		KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>(item);
		if( lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged )
John Firebaugh's avatar
John Firebaugh committed
152
			break;
153
		item = itemBelow(item);
John Firebaugh's avatar
John Firebaugh committed
154
	}
Laurent Montel's avatar
Laurent Montel committed
155

156
	if( item )
157
		return m_items.indexOf( ((KompareListViewLineItem*)item)->diffItemParent() );
Laurent Montel's avatar
Laurent Montel committed
158

John Firebaugh's avatar
John Firebaugh committed
159
160
161
	return -1;
}

162
int KompareListView::lastVisibleDifference()
John Firebaugh's avatar
John Firebaugh committed
163
{
164
	QTreeWidgetItem* item = itemAt( QPoint( 0, visibleHeight() - 1 ) );
165
166

	if( item == 0 )
167
	{
Laurent Montel's avatar
Laurent Montel committed
168
		kDebug(8104) << "no item at viewport coordinates (0," << visibleHeight() - 1 << ")" << endl;
169
170
171
172
173
174
175
176
177
		// find last item
		item = itemAt( QPoint( 0, 0 ) );
		if( item ) {
			QTreeWidgetItem* nextItem = item;
			do {
				item = nextItem;
				nextItem = itemBelow( item );
			} while( nextItem );
		}
178
	}
Laurent Montel's avatar
Laurent Montel committed
179

180
	while( item ) {
181
182
		KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>(item);
		if( lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged )
John Firebaugh's avatar
John Firebaugh committed
183
			break;
184
		item = itemAbove(item);
John Firebaugh's avatar
John Firebaugh committed
185
	}
Laurent Montel's avatar
Laurent Montel committed
186

187
	if( item )
188
		return m_items.indexOf( ((KompareListViewLineItem*)item)->diffItemParent() );
Laurent Montel's avatar
Laurent Montel committed
189

John Firebaugh's avatar
John Firebaugh committed
190
191
192
	return -1;
}

193
194
195
196
197
198
199
200
201
202
203
204
QRect KompareListView::totalVisualItemRect( QTreeWidgetItem* item )
{
	QRect total = visualItemRect( item );
	int n = item->childCount();
	for( int i=0; i<n; i++ ) {
		QTreeWidgetItem* child = item->child( i );
		if( !child->isHidden() )
			total = total.united( totalVisualItemRect( child ) );
	}
	return total;
}

205
QRect KompareListView::itemRect( int i )
John Firebaugh's avatar
John Firebaugh committed
206
{
207
208
	QTreeWidgetItem* item = itemAtIndex( i );
	return totalVisualItemRect( item );
John Firebaugh's avatar
John Firebaugh committed
209
210
}

211
int KompareListView::minScrollId()
John Firebaugh's avatar
John Firebaugh committed
212
213
214
215
{
	return visibleHeight() / 2;
}

216
int KompareListView::maxScrollId()
John Firebaugh's avatar
John Firebaugh committed
217
{
218
219
220
	int n = topLevelItemCount();
	if(!n) return 0;
	KompareListViewItem* item = (KompareListViewItem*)topLevelItem( n-1 );
221
	int maxId = item->scrollId() + item->maxHeight() - minScrollId();
Laurent Montel's avatar
Laurent Montel committed
222
	kDebug(8104) << "Max ID = " << maxId << endl;
223
	return maxId;
John Firebaugh's avatar
John Firebaugh committed
224
225
}

226
227
228
229
230
int KompareListView::contentsHeight()
{
	return verticalScrollBar()->maximum() + viewport()->height();
}

231
232
233
234
235
int KompareListView::contentsWidth()
{
	return ( columnWidth(COL_LINE_NO) + columnWidth(COL_MAIN) );
}

236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
int KompareListView::visibleHeight()
{
	return viewport()->height();
}

int KompareListView::visibleWidth()
{
	return viewport()->width();
}

int KompareListView::contentsX()
{
	return horizontalOffset();
}

int KompareListView::contentsY()
{
	return verticalOffset();
}

256
257
258
259
260
261
262
263
264
265
int KompareListView::nextPaintOffset() const
{
	return m_nextPaintOffset;
}

void KompareListView::setNextPaintOffset(int offset)
{
	m_nextPaintOffset = offset;
}

266
void KompareListView::setXOffset( int x )
John Firebaugh's avatar
John Firebaugh committed
267
{
Laurent Montel's avatar
Laurent Montel committed
268
	kDebug(8104) << "SetXOffset : Scroll to x position: " << x << endl;
269
	horizontalScrollBar()->setValue( x );
John Firebaugh's avatar
John Firebaugh committed
270
271
}

272
void KompareListView::scrollToId( int id )
John Firebaugh's avatar
John Firebaugh committed
273
{
Laurent Montel's avatar
Laurent Montel committed
274
//	kDebug(8104) << "ScrollToID : Scroll to id : " << id << endl;
275
276
277
278
279
280
281
282
283
	int n = topLevelItemCount();
	KompareListViewItem* item = 0;
	if( n ) {
		int i = 1;
		for( ; i<n; i++ ) {
			if( ((KompareListViewItem*)topLevelItem( i ))->scrollId() > id )
				break;
		}
		item = (KompareListViewItem*)topLevelItem( i-1 );
John Firebaugh's avatar
John Firebaugh committed
284
	}
Laurent Montel's avatar
Laurent Montel committed
285

John Firebaugh's avatar
John Firebaugh committed
286
	if( item ) {
287
288
		QRect rect = totalVisualItemRect( item );
		int pos = rect.top() + verticalOffset();
289
		int itemId = item->scrollId();
290
		int height = rect.height();
291
292
		double r = (double)( id - itemId ) / (double)item->maxHeight();
		int y = pos + (int)( r * (double)height ) - minScrollId();
Laurent Montel's avatar
Laurent Montel committed
293
294
295
296
297
298
299
300
301
302
//		kDebug(8104) << "scrollToID: " << endl;
//		kDebug(8104) << "            id = " << id << endl;
//		kDebug(8104) << "           pos = " << pos << endl;
//		kDebug(8104) << "        itemId = " << itemId << endl;
//		kDebug(8104) << "             r = " << r << endl;
//		kDebug(8104) << "        height = " << height << endl;
//		kDebug(8104) << "         minID = " << minScrollId() << endl;
//		kDebug(8104) << "             y = " << y << endl;
//		kDebug(8104) << "contentsHeight = " << contentsHeight() << endl;
//		kDebug(8104) << "         c - y = " << contentsHeight() - y << endl;
303
		verticalScrollBar()->setValue( y );
John Firebaugh's avatar
John Firebaugh committed
304
	}
Laurent Montel's avatar
Laurent Montel committed
305

John Firebaugh's avatar
John Firebaugh committed
306
307
308
	m_scrollId = id;
}

309
int KompareListView::scrollId()
John Firebaugh's avatar
John Firebaugh committed
310
311
312
313
314
315
{
	if( m_scrollId < 0 )
		m_scrollId = minScrollId();
	return m_scrollId;
}

John Firebaugh's avatar
John Firebaugh committed
316
void KompareListView::setSelectedDifference( const Difference* diff, bool scroll )
John Firebaugh's avatar
John Firebaugh committed
317
{
Laurent Montel's avatar
Laurent Montel committed
318
	kDebug(8104) << "KompareListView::setSelectedDifference(" << diff << ", " << scroll << ")" << endl;
319

320
321
322
323
324
325
326
327
	// 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
328
	// scroll==false, and the second call will bail out here.
329
330
	// Which is why clicking on a difference does not cause the listviews to
	// scroll.
Otto Bruggeman's avatar
   
Otto Bruggeman committed
331
332
	if ( m_selectedDifference == diff )
		return;
333

Otto Bruggeman's avatar
   
Otto Bruggeman committed
334
	m_selectedDifference = diff;
335

336
	KompareListViewItem* item = m_itemDict[ diff ];
John Firebaugh's avatar
John Firebaugh committed
337
	if( !item ) {
Laurent Montel's avatar
Laurent Montel committed
338
		kDebug(8104) << "KompareListView::slotSetSelection(): couldn't find our selection!" << endl;
John Firebaugh's avatar
John Firebaugh committed
339
340
		return;
	}
341
342

	// why does this not happen when the user clicks on a diff? see the comment above.
343
	if( scroll )
344
		scrollToId(item->scrollId());
345
346
347
348
349
350
351
	setUpdatesEnabled( false );
	int x = horizontalScrollBar()->value();
	int y = verticalScrollBar()->value();
	setCurrentItem( item );
	horizontalScrollBar()->setValue( x );
	verticalScrollBar()->setValue( y );
	setUpdatesEnabled( true );
John Firebaugh's avatar
John Firebaugh committed
352
}
353

John Firebaugh's avatar
John Firebaugh committed
354
void KompareListView::slotSetSelection( const Difference* diff )
John Firebaugh's avatar
John Firebaugh committed
355
{
Laurent Montel's avatar
Laurent Montel committed
356
	kDebug(8104) << "KompareListView::slotSetSelection( const Difference* diff )" << endl;
357

John Firebaugh's avatar
John Firebaugh committed
358
359
	setSelectedDifference( diff, true );
}
Otto Bruggeman's avatar
   
Otto Bruggeman committed
360

John Firebaugh's avatar
John Firebaugh committed
361
362
void KompareListView::slotSetSelection( const DiffModel* model, const Difference* diff )
{
Laurent Montel's avatar
Laurent Montel committed
363
	kDebug(8104) << "KompareListView::slotSetSelection( const DiffModel* model, const Difference* diff )" << endl;
364

John Firebaugh's avatar
John Firebaugh committed
365
366
	if( m_selectedModel && m_selectedModel == model ) {
		slotSetSelection( diff );
Otto Bruggeman's avatar
   
Otto Bruggeman committed
367
368
		return;
	}
Laurent Montel's avatar
Laurent Montel committed
369

John Firebaugh's avatar
John Firebaugh committed
370
371
	clear();
	m_items.clear();
372
	m_itemDict.clear();
Otto Bruggeman's avatar
   
Otto Bruggeman committed
373
	m_selectedModel = model;
Laurent Montel's avatar
Laurent Montel committed
374

375
376
	DiffHunkListConstIterator hunkIt = model->hunks()->begin();
	DiffHunkListConstIterator hEnd   = model->hunks()->end();
Laurent Montel's avatar
Laurent Montel committed
377

378
	KompareListViewItem* item = 0;
379
	m_nextPaintOffset = 0;
380
381
382

	for ( ; hunkIt != hEnd; ++hunkIt )
	{
John Firebaugh's avatar
John Firebaugh committed
383
		if( item )
384
			item = new KompareListViewHunkItem( this, item, *hunkIt, model->isBlended() );
John Firebaugh's avatar
John Firebaugh committed
385
		else
386
			item = new KompareListViewHunkItem( this, *hunkIt, model->isBlended() );
Laurent Montel's avatar
Laurent Montel committed
387

388
389
		DifferenceListConstIterator diffIt = (*hunkIt)->differences().begin();
		DifferenceListConstIterator dEnd   = (*hunkIt)->differences().end();
Laurent Montel's avatar
Laurent Montel committed
390

391
392
		for ( ; diffIt != dEnd; ++diffIt )
		{
393
			item = new KompareListViewDiffItem( this, item, *diffIt );
Laurent Montel's avatar
Laurent Montel committed
394

395
			int type = (*diffIt)->type();
396
397

			if ( type != Difference::Unchanged )
398
			{
399
				m_items.append( (KompareListViewDiffItem*)item );
400
				m_itemDict.insert( *diffIt, (KompareListViewDiffItem*)item );
John Firebaugh's avatar
John Firebaugh committed
401
402
403
			}
		}
	}
404

405
406
407
	resizeColumnToContents( COL_LINE_NO );
	resizeColumnToContents( COL_MAIN );

John Firebaugh's avatar
John Firebaugh committed
408
	slotSetSelection( diff );
John Firebaugh's avatar
John Firebaugh committed
409
410
}

411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
KompareListViewDiffItem* KompareListView::diffItemAt( const QPoint& pos )
{
	KompareListViewItem* item = static_cast<KompareListViewItem*>( itemAt( pos ) );
	if( !item )
		return 0;
	switch( item->type() ) {
		case KompareListViewItem::Hunk:
			if( item->paintHeight() ) return 0; // no diff item here
			// zero height (fake 1 pixel height), so a diff item shines through
			return static_cast<KompareListViewDiffItem*>( itemBelow( item ) );
		case KompareListViewItem::Line:
		case KompareListViewItem::Blank:
			return static_cast<KompareListViewLineItem*>( item )->diffItemParent();
		case KompareListViewItem::Container:
			return static_cast<KompareListViewLineContainerItem*>( item )->diffItemParent();
		case KompareListViewItem::Diff:
			return static_cast<KompareListViewDiffItem*>( item );
		default:
			return 0;
	}
}

433
void KompareListView::mousePressEvent( QMouseEvent* e )
John Firebaugh's avatar
John Firebaugh committed
434
{
435
	QPoint vp = e->pos();
436
437
	KompareListViewDiffItem* diffItem = diffItemAt( vp );
	if( diffItem && diffItem->difference()->type() != Difference::Unchanged ) {
438
		emit differenceClicked( diffItem->difference() );
John Firebaugh's avatar
John Firebaugh committed
439
440
441
	}
}

442
void KompareListView::mouseDoubleClickEvent( QMouseEvent* e )
443
{
444
	QPoint vp = e->pos();
445
446
	KompareListViewDiffItem* diffItem = diffItemAt( vp );
	if ( diffItem && diffItem->difference()->type() != Difference::Unchanged ) {
447
448
449
450
451
452
		// FIXME: make a new signal that does both
		emit differenceClicked( diffItem->difference() );
		emit applyDifference( !diffItem->difference()->applied() );
	}
}

453
454
455
456
void KompareListView::renumberLines( void )
{
//	kDebug( 8104 ) << "Begin" << endl;
	unsigned int newLineNo = 1;
457
458
	if( !topLevelItemCount() ) return;
	KompareListViewItem* item = (KompareListViewItem*)topLevelItem( 0 );
459
	while( item ) {
460
461
462
463
//		kDebug( 8104 ) << "type: " << item->type() << endl;
		if ( item->type() != KompareListViewItem::Container 
		     && item->type() != KompareListViewItem::Blank 
		     && item->type() != KompareListViewItem::Hunk )
464
465
466
467
		{
//			kDebug( 8104 ) << QString::number( newLineNo ) << endl;
			item->setText( COL_LINE_NO, QString::number( newLineNo++ ) );
		}
468
		item = (KompareListViewItem*)itemBelow( item );
469
470
471
	}
}

Otto Bruggeman's avatar
   
Otto Bruggeman committed
472
473
void KompareListView::slotApplyDifference( bool apply )
{
474
	m_itemDict[ m_selectedDifference ]->applyDifference( apply );
475
476
477
	// now renumber the line column if this is the destination
	if ( !m_isSource )
		renumberLines();
Otto Bruggeman's avatar
   
Otto Bruggeman committed
478
479
480
}

void KompareListView::slotApplyAllDifferences( bool apply )
481
{
482
483
484
485
486
	QHash<const Diff2::Difference*, KompareListViewDiffItem*>::ConstIterator it = m_itemDict.constBegin();
	QHash<const Diff2::Difference*, KompareListViewDiffItem*>::ConstIterator end = m_itemDict.constEnd();
	for ( ; it != end; ++it )
		it.value()->applyDifference( apply );

487
488
489
	// now renumber the line column if this is the destination
	if ( !m_isSource )
		renumberLines();
490
	update();
491
492
}

493
494
void KompareListView::slotApplyDifference( const Difference* diff, bool apply )
{
495
	m_itemDict[ diff ]->applyDifference( apply );
496
497
498
	// now renumber the line column if this is the destination
	if ( !m_isSource )
		renumberLines();
499
500
}

Otto Bruggeman's avatar
   
Otto Bruggeman committed
501
502
void KompareListView::wheelEvent( QWheelEvent* e )
{
503
504
505
	e->ignore(); // we want the parent to catch wheel events
}

506
507
void KompareListView::resizeEvent( QResizeEvent* e )
{
508
	QTreeWidget::resizeEvent(e);
509
510
511
	emit resized();
}

512
KompareListViewItemDelegate::KompareListViewItemDelegate( QObject* parent )
513
	: QStyledItemDelegate( parent )
514
515
516
517
518
519
520
521
522
{
}

KompareListViewItemDelegate::~KompareListViewItemDelegate()
{
}

void KompareListViewItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
523
	int column = index.column();
524
	QStyleOptionViewItemV4 changedOption = option;
525
	if( column == COL_LINE_NO )
526
		changedOption.displayAlignment = Qt::AlignRight;
527
528
	KompareListViewItem* item = static_cast<KompareListViewItem*>( static_cast<KompareListView*>( parent() )->itemFromIndex( index ) );
	item->paintCell( painter, changedOption, column );
529
530
531
532
533
}

QSize KompareListViewItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
	KompareListViewItem* item = static_cast<KompareListViewItem*>( static_cast<KompareListView*>( parent() )->itemFromIndex( index ) );
534
	QSize hint = QStyledItemDelegate::sizeHint( option, index );
535
536
537
	return QSize( hint.width() + ITEM_MARGIN, item->height() );
}

538
539
KompareListViewItem::KompareListViewItem( KompareListView* parent, int type )
	: QTreeWidgetItem( parent, type ),
540
	m_scrollId( 0 ),
541
542
543
	m_height( 0 ),
	m_paintHeight( 0 ),
	m_paintOffset( parent->nextPaintOffset() )
544
{
Laurent Montel's avatar
Laurent Montel committed
545
//	kDebug(8104) << "Created KompareListViewItem with scroll id " << m_scrollId << endl;
Otto Bruggeman's avatar
   
Otto Bruggeman committed
546
547
}

548
549
KompareListViewItem::KompareListViewItem( KompareListView* parent, KompareListViewItem* after, int type )
	: QTreeWidgetItem( parent, after, type ),
550
	m_scrollId( after->scrollId() + after->maxHeight() ),
551
552
553
	m_height( 0 ),
	m_paintHeight( 0 ),
	m_paintOffset( parent->nextPaintOffset() )
John Firebaugh's avatar
John Firebaugh committed
554
{
Laurent Montel's avatar
Laurent Montel committed
555
//	kDebug(8104) << "Created KompareListViewItem with scroll id " << m_scrollId << endl;
John Firebaugh's avatar
John Firebaugh committed
556
557
}

558
559
KompareListViewItem::KompareListViewItem( KompareListViewItem* parent, int type )
	: QTreeWidgetItem( parent, type ),
560
	m_scrollId( 0 ),
561
562
563
	m_height( 0 ),
	m_paintHeight( 0 ),
	m_paintOffset( parent->kompareListView()->nextPaintOffset() )
John Firebaugh's avatar
John Firebaugh committed
564
565
566
{
}

567
568
KompareListViewItem::KompareListViewItem( KompareListViewItem* parent, KompareListViewItem* /*after*/, int type )
	: QTreeWidgetItem( parent, type ),
569
	m_scrollId( 0 ),
570
571
572
	m_height( 0 ),
	m_paintHeight( 0 ),
	m_paintOffset( parent->kompareListView()->nextPaintOffset() )
573
574
575
{
}

576
int KompareListViewItem::height() const
577
{
578
	return m_height;
579
580
}

581
void KompareListViewItem::setHeight( int h )
582
{
583
584
585
586
587
588
589
	m_height = m_paintHeight = h;
	// QTreeWidget doesn't like zero height, fudge around it.
	m_height -= m_paintOffset;
	if( m_height <= 0 ) {
		kompareListView()->setNextPaintOffset( 1 - m_height );
		m_height = 1;
	} else kompareListView()->setNextPaintOffset( 0 );
590
591
}

592
int KompareListViewItem::paintHeight() const
593
{
594
	return m_paintHeight;
595
596
}

597
int KompareListViewItem::paintOffset() const
598
{
599
	return m_paintOffset;
600
601
602
}

bool KompareListViewItem::isCurrent() const
603
{
604
	return treeWidget()->currentItem() == this;
605
606
}

607
KompareListView* KompareListViewItem::kompareListView() const
John Firebaugh's avatar
John Firebaugh committed
608
{
609
	return (KompareListView*)treeWidget();
John Firebaugh's avatar
John Firebaugh committed
610
611
}

612
613
614
615
616
617
618
619
620
621
622
623
void KompareListViewItem::paintCell( QPainter* p, const QStyleOptionViewItem& option, int column )
{
	// Default implementation for zero-height items.
	// We have to paint the item which shines through or we'll end up with glitches.
	KompareListViewItem* nextItem = (KompareListViewItem*)kompareListView()->itemBelow(this);
	if( nextItem ) {
		QStyleOptionViewItemV4 changedOption = option;
		changedOption.rect.translate( 0, height() );
		nextItem->paintCell( p, changedOption, column );
	}
}

624
KompareListViewDiffItem::KompareListViewDiffItem( KompareListView* parent, Difference* difference )
625
	: KompareListViewItem( parent, Diff ),
626
627
628
	m_difference( difference ),
	m_sourceItem( 0L ),
	m_destItem( 0L )
John Firebaugh's avatar
John Firebaugh committed
629
{
630
	init();
John Firebaugh's avatar
John Firebaugh committed
631
632
}

633
KompareListViewDiffItem::KompareListViewDiffItem( KompareListView* parent, KompareListViewItem* after, Difference* difference )
634
	: KompareListViewItem( parent, after, Diff ),
635
636
637
	m_difference( difference ),
	m_sourceItem( 0L ),
	m_destItem( 0L )
John Firebaugh's avatar
John Firebaugh committed
638
{
639
	init();
John Firebaugh's avatar
John Firebaugh committed
640
641
}

642
643
644
645
646
KompareListViewDiffItem::~KompareListViewDiffItem()
{
	m_difference = 0;
}

647
void KompareListViewDiffItem::init()
John Firebaugh's avatar
John Firebaugh committed
648
{
649
	setHeight( 0 );
650
	setExpanded( true );
651
	int nextPaintOffset = kompareListView()->nextPaintOffset();
John Firebaugh's avatar
John Firebaugh committed
652
	m_destItem = new KompareListViewLineContainerItem( this, false );
653
	kompareListView()->setNextPaintOffset(nextPaintOffset);
654
655
656
	m_sourceItem = new KompareListViewLineContainerItem( this, true );
	setVisibility();
}
Laurent Montel's avatar
Laurent Montel committed
657

658
659
void KompareListViewDiffItem::setVisibility()
{
660
661
	m_sourceItem->setHidden( !(kompareListView()->isSource() || m_difference->applied()) );
	m_destItem->setHidden( !m_sourceItem->isHidden() );
John Firebaugh's avatar
John Firebaugh committed
662
663
}

664
void KompareListViewDiffItem::applyDifference( bool apply )
John Firebaugh's avatar
John Firebaugh committed
665
{
Laurent Montel's avatar
Laurent Montel committed
666
	kDebug(8104) << "KompareListViewDiffItem::applyDifference( " << apply << " )" << endl;
667
	setVisibility();
John Firebaugh's avatar
John Firebaugh committed
668
669
}

670
int KompareListViewDiffItem::maxHeight()
John Firebaugh's avatar
John Firebaugh committed
671
{
672
	int lines = qMax( m_difference->sourceLineCount(), m_difference->destinationLineCount() );
John Firebaugh's avatar
John Firebaugh committed
673
	if( lines == 0 )
674
		return BLANK_LINE_HEIGHT;
John Firebaugh's avatar
John Firebaugh committed
675
	else
676
677
678
		return lines * treeWidget()->fontMetrics().height();
}

679
KompareListViewLineContainerItem::KompareListViewLineContainerItem( KompareListViewDiffItem* parent, bool isSource )
680
	: KompareListViewItem( parent, Container ),
681
	m_blankLineItem( 0 ),
682
	m_isSource( isSource )
683
{
Laurent Montel's avatar
Laurent Montel committed
684
//	kDebug(8104) << "isSource ? " << (isSource ? " Yes!" : " No!") << endl;
685
	setHeight( 0 );
686
	setExpanded( true );
687

688
	int lines = lineCount();
689
	int line = lineNumber();
Laurent Montel's avatar
Laurent Montel committed
690
//	kDebug(8104) << "LineNumber : " << lineNumber() << endl;
691
	if( lines == 0 ) {
692
		m_blankLineItem = new KompareListViewBlankLineItem( this );
693
694
		return;
	}
695

696
	for( int i = 0; i < lines; i++, line++ ) {
697
		new KompareListViewLineItem( this, line, lineAt( i ) );
John Firebaugh's avatar
John Firebaugh committed
698
	}
699
}
Laurent Montel's avatar
Laurent Montel committed
700

701
702
703
704
KompareListViewLineContainerItem::~KompareListViewLineContainerItem()
{
}

705
KompareListViewDiffItem* KompareListViewLineContainerItem::diffItemParent() const
John Firebaugh's avatar
John Firebaugh committed
706
{
707
708
	return (KompareListViewDiffItem*)parent();
}
Laurent Montel's avatar
Laurent Montel committed
709

710
711
712
713
714
int KompareListViewLineContainerItem::lineCount() const
{
	return m_isSource ? diffItemParent()->difference()->sourceLineCount() :
	                    diffItemParent()->difference()->destinationLineCount();
}
Laurent Montel's avatar
Laurent Montel committed
715

716
717
718
719
720
int KompareListViewLineContainerItem::lineNumber() const
{
	return m_isSource ? diffItemParent()->difference()->sourceLineNumber() :
	                    diffItemParent()->difference()->destinationLineNumber();
}
Laurent Montel's avatar
Laurent Montel committed
721

722
DifferenceString* KompareListViewLineContainerItem::lineAt( int i ) const
723
724
725
{
	return m_isSource ? diffItemParent()->difference()->sourceLineAt( i ) :
	                    diffItemParent()->difference()->destinationLineAt( i );
John Firebaugh's avatar
John Firebaugh committed
726
727
}

728
KompareListViewLineItem::KompareListViewLineItem( KompareListViewLineContainerItem* parent, int line, DifferenceString* text )
729
	: KompareListViewItem( parent, Line )
730
{
731
732
733
734
735
736
737
	init( line, text );
}

KompareListViewLineItem::KompareListViewLineItem( KompareListViewLineContainerItem* parent, int line, DifferenceString* text, int type )
	: KompareListViewItem( parent, type )
{
	init( line, text );
738
739
}

740
741
742
743
744
KompareListViewLineItem::~KompareListViewLineItem()
{
	m_text = 0;
}

745
746
747
748
749
750
751
752
void KompareListViewLineItem::init( int line, DifferenceString* text )
{
	setHeight( treeWidget()->fontMetrics().height() );
	setText( COL_LINE_NO, QString::number( line ) );
	setText( COL_MAIN, text->string() );
	m_text = text;
}

753
void KompareListViewLineItem::paintCell( QPainter* p, const QStyleOptionViewItem& option, int column )
John Firebaugh's avatar
John Firebaugh committed
754
{
755
756
757
	int width = option.rect.width();
	Qt::Alignment align = option.displayAlignment;

758
	p->setRenderHint(QPainter::Antialiasing);
759
	p->translate(option.rect.topLeft());
760
	p->translate(0, -paintOffset());
761

762
	QColor bg( Qt::white ); // Always make the background white when it is not a real difference
763
764
765
766
	if ( diffItemParent()->difference()->type() == Difference::Unchanged )
	{
		if ( column == COL_LINE_NO )
		{
767
			bg = QColor( Qt::lightGray );
768
769
770
771
772
773
		}
	}
	else
	{
		bg = kompareListView()->settings()->colorForDifferenceType(
		          diffItemParent()->difference()->type(),
774
		          diffItemParent()->isCurrent(),
775
776
777
		          diffItemParent()->difference()->applied() );
	}

778
	// Paint background
779
	p->fillRect( 0, 0, width, paintHeight(), bg );
780
781

	// Paint foreground
782
783
784
785
	if ( diffItemParent()->difference()->type() == Difference::Unchanged )
		p->setPen( QColor( Qt::darkGray ) ); // always make normal text gray
	else
		p->setPen( QColor( Qt::black ) ); // make text with changes black
786

787
	paintText( p, bg, column, width, align );
788

789
	// Paint darker lines around selected item
790
	if ( diffItemParent()->isCurrent() )
791
	{
792
		p->translate(0.5,0.5);
793
		p->setPen( bg.dark(135) );
794
795
		QTreeWidgetItem* parentItem = parent();
		if ( this == parentItem->child( 0 ) )
796
			p->drawLine( 0, 0, width, 0 );
797
		if ( this == parentItem->child( parentItem->childCount() - 1 ) )
798
			p->drawLine( 0, paintHeight() - 1, width, paintHeight() - 1 );
799
	}
800
801

	p->resetTransform();
802
}
John Firebaugh's avatar
John Firebaugh committed
803

804
void KompareListViewLineItem::paintText( QPainter* p, const QColor& bg, int column, int width, int align )
805
{
806
807
	if ( column == COL_MAIN )
	{
808
		QString textChunk;
809
		int offset = ITEM_MARGIN;
Andi Fischer's avatar
Andi Fischer committed
810
		int prevValue = 0;
811
		int charsDrawn = 0;
812
		int chunkWidth;
813
814
		QBrush changeBrush( bg, Qt::Dense3Pattern );
		QBrush normalBrush( bg, Qt::SolidPattern );
815
816
817
818
		QBrush brush;

		if ( m_text->string().isEmpty() )
		{
819
			p->fillRect( 0, 0, width, paintHeight(), normalBrush );
820
821
822
			return;
		}

823
		p->fillRect( 0, 0, offset, paintHeight(), normalBrush );
824

Otto Bruggeman's avatar
Otto Bruggeman committed
825
		if ( !m_text->markerList().isEmpty() )
826
		{
Otto Bruggeman's avatar
Otto Bruggeman committed
827
828
829
			MarkerListConstIterator markerIt = m_text->markerList().begin();
			MarkerListConstIterator mEnd     = m_text->markerList().end();
			Marker* m = *markerIt;
830

Otto Bruggeman's avatar
Otto Bruggeman committed
831
			for ( ; markerIt != mEnd; ++markerIt )
832
			{
Otto Bruggeman's avatar
Otto Bruggeman committed
833
834
				m  = *markerIt;
				textChunk = m_text->string().mid( prevValue, m->offset() - prevValue );
Laurent Montel's avatar
Laurent Montel committed
835
836
837
//				kDebug(8104) << "TextChunk   = \"" << textChunk << "\"" << endl;
//				kDebug(8104) << "c->offset() = " << c->offset() << endl;
//				kDebug(8104) << "prevValue   = " << prevValue << endl;
838
839
				expandTabs(textChunk, kompareListView()->settings()->m_tabToNumberOfSpaces, charsDrawn);
				charsDrawn += textChunk.length();
Otto Bruggeman's avatar
Otto Bruggeman committed
840
841
				prevValue = m->offset();
				if ( m->type() == Marker::End )
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
				{
					QFont font( p->font() );
					font.setBold( true );
					p->setFont( font );
//					p->setPen( Qt::blue );
					brush = changeBrush;
				}
				else
				{
					QFont font( p->font() );
					font.setBold( false );
					p->setFont( font );
//					p->setPen( Qt::black );
					brush = normalBrush;
				}
				chunkWidth = p->fontMetrics().width( textChunk );
858
				p->fillRect( offset, 0, chunkWidth, paintHeight(), brush );
859
				p->drawText( offset, 0,
860
				             chunkWidth, paintHeight(),
861
862
				             align, textChunk );
				offset += chunkWidth;
863
864
			}
		}
865
		if ( prevValue < m_text->string().length() )
866
867
		{
			// Still have to draw some string without changes
Andi Fischer's avatar
Andi Fischer committed
868
			textChunk = m_text->string().mid( prevValue, qMax( 1, m_text->string().length() - prevValue ) );
869
			expandTabs(textChunk, kompareListView()->settings()->m_tabToNumberOfSpaces, charsDrawn);
Laurent Montel's avatar
Laurent Montel committed
870
//			kDebug(8104) << "TextChunk   = \"" << textChunk << "\"" << endl;
871
872
873
874
			QFont font( p->font() );
			font.setBold( false );
			p->setFont( font );
			chunkWidth = p->fontMetrics().width( textChunk );
875
			p->fillRect( offset, 0, chunkWidth, paintHeight(), normalBrush );
876
			p->drawText( offset, 0,
877
			             chunkWidth, paintHeight(),
878
879
880
			             align, textChunk );
			offset += chunkWidth;
		}
881
		p->fillRect( offset, 0, width - offset, paintHeight(), normalBrush );
882
883
884
	}
	else
	{
885
		p->fillRect( 0, 0, width, paintHeight(), bg );
886
		p->drawText( ITEM_MARGIN, 0,
887
		             width - ITEM_MARGIN, paintHeight(),
888
889
		             align, text( column ) );
	}
890
}
Laurent Montel's avatar
Laurent Montel committed
891

892
893
894
895
896
897
898
void KompareListViewLineItem::expandTabs(QString& text, int tabstop, int startPos) const
{
	int index;
	while((index = text.indexOf(QChar(9)))!= -1)
		text.replace(index, 1, QString(tabstop-((startPos+index)%4),' '));
}

899
900
901
902
903
KompareListViewDiffItem* KompareListViewLineItem::diffItemParent() const
{
	KompareListViewLineContainerItem* p = (KompareListViewLineContainerItem*)parent();
	return p->diffItemParent();
}
Laurent Montel's avatar
Laurent Montel committed
904

905
KompareListViewBlankLineItem::KompareListViewBlankLineItem( KompareListViewLineContainerItem* parent )
906
	: KompareListViewLineItem( parent, 0, new DifferenceString(), Blank )
907
{
John Firebaugh's avatar
John Firebaugh committed
908
	setHeight( BLANK_LINE_HEIGHT );
John Firebaugh's avatar
John Firebaugh committed
909
910
}

911