1 /*
2 SPDX-FileCopyrightText: 2001-2009 Otto Bruggeman <bruggie@gmail.com>
3 SPDX-FileCopyrightText: 2001-2003 John Firebaugh <jfirebaugh@kde.org>
4 SPDX-FileCopyrightText: 2004 Jeff Snyder <jeff@caffeinated.me.uk>
5 SPDX-FileCopyrightText: 2007-2012 Kevin Kofler <kevin.kofler@chello.at>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "komparelistview.h"
11
12 #include <QStyle>
13 #include <QPainter>
14 #include <QRegExp>
15 #include <QTimer>
16 #include <QResizeEvent>
17 #include <QMouseEvent>
18 #include <QWheelEvent>
19 #include <QScrollBar>
20
21 #include <KSharedConfig>
22
23 #include <libkomparediff2/diffmodel.h>
24 #include <libkomparediff2/diffhunk.h>
25 #include <libkomparediff2/difference.h>
26 #include <libkomparediff2/komparemodellist.h>
27
28 #include <komparepartdebug.h>
29 #include "viewsettings.h"
30 #include "komparesplitter.h"
31
32 #define COL_LINE_NO 0
33 #define COL_MAIN 1
34
35 #define BLANK_LINE_HEIGHT 3
36 #define HUNK_LINE_HEIGHT 5
37
38 #define ITEM_MARGIN 3
39
40 using namespace Diff2;
41
KompareListViewFrame(bool isSource,ViewSettings * settings,KompareSplitter * parent,const char * name)42 KompareListViewFrame::KompareListViewFrame(bool isSource,
43 ViewSettings* settings,
44 KompareSplitter* parent,
45 const char* name):
46 QFrame(parent),
47 m_view(isSource, settings, this, name),
48 m_label(isSource ? QStringLiteral("Source") : QStringLiteral("Dest"), this),
49 m_layout(this)
50 {
51 setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
52 m_label.setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
53 QFrame* bottomLine = new QFrame(this);
54 bottomLine->setFrameShape(QFrame::HLine);
55 bottomLine->setFrameShadow(QFrame::Plain);
56 bottomLine->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
57 bottomLine->setFixedHeight(1);
58 m_label.setMargin(3);
59 m_layout.setSpacing(0);
60 m_layout.setContentsMargins(0, 0, 0, 0);
61 m_layout.addWidget(&m_label);
62 m_layout.addWidget(bottomLine);
63 m_layout.addWidget(&m_view);
64
65 connect(&m_view, &KompareListView::differenceClicked,
66 parent, &KompareSplitter::slotDifferenceClicked);
67
68 connect(parent, &KompareSplitter::scrollViewsToId, &m_view, &KompareListView::scrollToId);
69 connect(parent, &KompareSplitter::setXOffset, &m_view, &KompareListView::setXOffset);
70 connect(&m_view, &KompareListView::resized, parent, &KompareSplitter::slotUpdateScrollBars);
71 }
72
slotSetModel(const DiffModel * model)73 void KompareListViewFrame::slotSetModel(const DiffModel* model)
74 {
75 if (model)
76 {
77 if (view()->isSource()) {
78 if (!model->sourceRevision().isEmpty())
79 m_label.setText(model->sourceFile() + QLatin1String(" (") + model->sourceRevision() + QLatin1Char(')'));
80 else
81 m_label.setText(model->sourceFile());
82 } else {
83 if (!model->destinationRevision().isEmpty())
84 m_label.setText(model->destinationFile() + QLatin1String(" (") + model->destinationRevision() + QLatin1Char(')'));
85 else
86 m_label.setText(model->destinationFile());
87 }
88 } else {
89 m_label.setText(QString());
90 }
91 }
92
KompareListView(bool isSource,ViewSettings * settings,QWidget * parent,const char * name)93 KompareListView::KompareListView(bool isSource,
94 ViewSettings* settings,
95 QWidget* parent, const char* name) :
96 QTreeWidget(parent),
97 m_isSource(isSource),
98 m_settings(settings),
99 m_scrollId(-1),
100 m_selectedModel(nullptr),
101 m_selectedDifference(nullptr)
102 {
103 setObjectName(QLatin1String(name));
104 setItemDelegate(new KompareListViewItemDelegate(this));
105 setHeaderHidden(true);
106 setColumnCount(3); // Line Number, Main, Blank
107 setAllColumnsShowFocus(true);
108 setRootIsDecorated(false);
109 setIndentation(0);
110 setFrameStyle(QFrame::NoFrame);
111 setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
112 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
113 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
114 setFocusPolicy(Qt::NoFocus);
115 setFont(m_settings->m_font);
116 setFocusProxy(parent->parentWidget());
117 }
118
~KompareListView()119 KompareListView::~KompareListView()
120 {
121 m_settings = nullptr;
122 m_selectedModel = nullptr;
123 m_selectedDifference = nullptr;
124 }
125
itemAtIndex(int i)126 KompareListViewItem* KompareListView::itemAtIndex(int i)
127 {
128 return m_items[ i ];
129 }
130
firstVisibleDifference()131 int KompareListView::firstVisibleDifference()
132 {
133 QTreeWidgetItem* item = itemAt(QPoint(0, 0));
134
135 if (item == nullptr)
136 {
137 qCDebug(KOMPAREPART) << "no item at viewport coordinates (0,0)" ;
138 }
139
140 while (item) {
141 KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>(item);
142 if (lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged)
143 break;
144 item = itemBelow(item);
145 }
146
147 if (item)
148 return m_items.indexOf(((KompareListViewLineItem*)item)->diffItemParent());
149
150 return -1;
151 }
152
lastVisibleDifference()153 int KompareListView::lastVisibleDifference()
154 {
155 QTreeWidgetItem* item = itemAt(QPoint(0, visibleHeight() - 1));
156
157 if (item == nullptr)
158 {
159 qCDebug(KOMPAREPART) << "no item at viewport coordinates (0," << visibleHeight() - 1 << ")" ;
160 // find last item
161 item = itemAt(QPoint(0, 0));
162 if (item) {
163 QTreeWidgetItem* nextItem = item;
164 do {
165 item = nextItem;
166 nextItem = itemBelow(item);
167 } while (nextItem);
168 }
169 }
170
171 while (item) {
172 KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>(item);
173 if (lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged)
174 break;
175 item = itemAbove(item);
176 }
177
178 if (item)
179 return m_items.indexOf(((KompareListViewLineItem*)item)->diffItemParent());
180
181 return -1;
182 }
183
totalVisualItemRect(QTreeWidgetItem * item)184 QRect KompareListView::totalVisualItemRect(QTreeWidgetItem* item)
185 {
186 QRect total = visualItemRect(item);
187 int n = item->childCount();
188 for (int i = 0; i < n; ++i) {
189 QTreeWidgetItem* child = item->child(i);
190 if (!child->isHidden())
191 total = total.united(totalVisualItemRect(child));
192 }
193 return total;
194 }
195
itemRect(int i)196 QRect KompareListView::itemRect(int i)
197 {
198 QTreeWidgetItem* item = itemAtIndex(i);
199 return totalVisualItemRect(item);
200 }
201
minScrollId()202 int KompareListView::minScrollId()
203 {
204 return visibleHeight() / 2;
205 }
206
maxScrollId()207 int KompareListView::maxScrollId()
208 {
209 int n = topLevelItemCount();
210 if (!n) return 0;
211 KompareListViewItem* item = (KompareListViewItem*)topLevelItem(n - 1);
212 int maxId = item->scrollId() + item->maxHeight() - minScrollId();
213 qCDebug(KOMPAREPART) << "Max ID = " << maxId ;
214 return maxId;
215 }
216
contentsHeight()217 int KompareListView::contentsHeight()
218 {
219 return verticalScrollBar()->maximum() + viewport()->height() - style()->pixelMetric(QStyle::PM_ScrollBarExtent);
220 }
221
contentsWidth()222 int KompareListView::contentsWidth()
223 {
224 return (columnWidth(COL_LINE_NO) + columnWidth(COL_MAIN));
225 }
226
visibleHeight()227 int KompareListView::visibleHeight()
228 {
229 return viewport()->height();
230 }
231
visibleWidth()232 int KompareListView::visibleWidth()
233 {
234 return viewport()->width();
235 }
236
contentsX()237 int KompareListView::contentsX()
238 {
239 return horizontalOffset();
240 }
241
contentsY()242 int KompareListView::contentsY()
243 {
244 return verticalOffset();
245 }
246
nextPaintOffset() const247 int KompareListView::nextPaintOffset() const
248 {
249 return m_nextPaintOffset;
250 }
251
setNextPaintOffset(int offset)252 void KompareListView::setNextPaintOffset(int offset)
253 {
254 m_nextPaintOffset = offset;
255 }
256
setXOffset(int x)257 void KompareListView::setXOffset(int x)
258 {
259 qCDebug(KOMPAREPART) << "SetXOffset : Scroll to x position: " << x ;
260 horizontalScrollBar()->setValue(x);
261 }
262
scrollToId(int id)263 void KompareListView::scrollToId(int id)
264 {
265 // qCDebug(KOMPAREPART) << "ScrollToID : Scroll to id : " << id ;
266 int n = topLevelItemCount();
267 KompareListViewItem* item = nullptr;
268 if (n) {
269 int i = 1;
270 for (; i < n; ++i) {
271 if (((KompareListViewItem*)topLevelItem(i))->scrollId() > id)
272 break;
273 }
274 item = (KompareListViewItem*)topLevelItem(i - 1);
275 }
276
277 if (item) {
278 QRect rect = totalVisualItemRect(item);
279 int pos = rect.top() + verticalOffset();
280 int itemId = item->scrollId();
281 int height = rect.height();
282 double r = (double)(id - itemId) / (double)item->maxHeight();
283 int y = pos + (int)(r * (double)height) - minScrollId();
284 // qCDebug(KOMPAREPART) << "scrollToID: " ;
285 // qCDebug(KOMPAREPART) << " id = " << id ;
286 // qCDebug(KOMPAREPART) << " pos = " << pos ;
287 // qCDebug(KOMPAREPART) << " itemId = " << itemId ;
288 // qCDebug(KOMPAREPART) << " r = " << r ;
289 // qCDebug(KOMPAREPART) << " height = " << height ;
290 // qCDebug(KOMPAREPART) << " minID = " << minScrollId() ;
291 // qCDebug(KOMPAREPART) << " y = " << y ;
292 // qCDebug(KOMPAREPART) << "contentsHeight = " << contentsHeight() ;
293 // qCDebug(KOMPAREPART) << " c - y = " << contentsHeight() - y ;
294 verticalScrollBar()->setValue(y);
295 }
296
297 m_scrollId = id;
298 }
299
scrollId()300 int KompareListView::scrollId()
301 {
302 if (m_scrollId < 0)
303 m_scrollId = minScrollId();
304 return m_scrollId;
305 }
306
setSelectedDifference(const Difference * diff,bool scroll)307 void KompareListView::setSelectedDifference(const Difference* diff, bool scroll)
308 {
309 qCDebug(KOMPAREPART) << "KompareListView::setSelectedDifference(" << diff << ", " << scroll << ")" ;
310
311 // When something other than a click causes this function to be called,
312 // it'll only get called once, and all is simple.
313 //
314 // When the user clicks on a diff, this function will get called once when
315 // komparesplitter::slotDifferenceClicked runs, and again when the
316 // setSelection signal from the modelcontroller arrives.
317 //
318 // the first call (which will always be from the splitter) will have
319 // scroll==false, and the second call will bail out here.
320 // Which is why clicking on a difference does not cause the listviews to
321 // scroll.
322 if (m_selectedDifference == diff)
323 return;
324
325 m_selectedDifference = diff;
326
327 KompareListViewItem* item = m_itemDict[ diff ];
328 if (!item) {
329 qCDebug(KOMPAREPART) << "KompareListView::slotSetSelection(): couldn't find our selection!" ;
330 return;
331 }
332
333 // why does this not happen when the user clicks on a diff? see the comment above.
334 if (scroll)
335 scrollToId(item->scrollId());
336 setUpdatesEnabled(false);
337 int x = horizontalScrollBar()->value();
338 int y = verticalScrollBar()->value();
339 setCurrentItem(item);
340 horizontalScrollBar()->setValue(x);
341 verticalScrollBar()->setValue(y);
342 setUpdatesEnabled(true);
343 }
344
slotSetSelection(const Difference * diff)345 void KompareListView::slotSetSelection(const Difference* diff)
346 {
347 qCDebug(KOMPAREPART) << "KompareListView::slotSetSelection( const Difference* diff )" ;
348
349 setSelectedDifference(diff, true);
350 }
351
slotSetSelection(const DiffModel * model,const Difference * diff)352 void KompareListView::slotSetSelection(const DiffModel* model, const Difference* diff)
353 {
354 qCDebug(KOMPAREPART) << "KompareListView::slotSetSelection( const DiffModel* model, const Difference* diff )" ;
355
356 if (m_selectedModel && m_selectedModel == model) {
357 slotSetSelection(diff);
358 return;
359 }
360
361 clear();
362 m_items.clear();
363 m_itemDict.clear();
364 m_selectedModel = model;
365
366 DiffHunkListConstIterator hunkIt = model->hunks()->begin();
367 DiffHunkListConstIterator hEnd = model->hunks()->end();
368
369 KompareListViewItem* item = nullptr;
370 m_nextPaintOffset = 0;
371
372 for (; hunkIt != hEnd; ++hunkIt)
373 {
374 if (item)
375 item = new KompareListViewHunkItem(this, item, *hunkIt, model->isBlended());
376 else
377 item = new KompareListViewHunkItem(this, *hunkIt, model->isBlended());
378
379 DifferenceListConstIterator diffIt = (*hunkIt)->differences().begin();
380 DifferenceListConstIterator dEnd = (*hunkIt)->differences().end();
381
382 for (; diffIt != dEnd; ++diffIt)
383 {
384 item = new KompareListViewDiffItem(this, item, *diffIt);
385
386 int type = (*diffIt)->type();
387
388 if (type != Difference::Unchanged)
389 {
390 m_items.append((KompareListViewDiffItem*)item);
391 m_itemDict.insert(*diffIt, (KompareListViewDiffItem*)item);
392 }
393 }
394 }
395
396 resizeColumnToContents(COL_LINE_NO);
397 resizeColumnToContents(COL_MAIN);
398
399 slotSetSelection(diff);
400 }
401
diffItemAt(const QPoint & pos)402 KompareListViewDiffItem* KompareListView::diffItemAt(const QPoint& pos)
403 {
404 KompareListViewItem* item = static_cast<KompareListViewItem*>(itemAt(pos));
405 if (!item)
406 return nullptr;
407 switch (item->type()) {
408 case KompareListViewItem::Hunk:
409 if (item->paintHeight()) return nullptr; // no diff item here
410 // zero height (fake 1 pixel height), so a diff item shines through
411 return static_cast<KompareListViewDiffItem*>(itemBelow(item));
412 case KompareListViewItem::Line:
413 case KompareListViewItem::Blank:
414 return static_cast<KompareListViewLineItem*>(item)->diffItemParent();
415 case KompareListViewItem::Container:
416 return static_cast<KompareListViewLineContainerItem*>(item)->diffItemParent();
417 case KompareListViewItem::Diff:
418 return static_cast<KompareListViewDiffItem*>(item);
419 default:
420 return nullptr;
421 }
422 }
423
mousePressEvent(QMouseEvent * e)424 void KompareListView::mousePressEvent(QMouseEvent* e)
425 {
426 QPoint vp = e->pos();
427 KompareListViewDiffItem* diffItem = diffItemAt(vp);
428 if (diffItem && diffItem->difference()->type() != Difference::Unchanged) {
429 Q_EMIT differenceClicked(diffItem->difference());
430 }
431 }
432
mouseDoubleClickEvent(QMouseEvent * e)433 void KompareListView::mouseDoubleClickEvent(QMouseEvent* e)
434 {
435 QPoint vp = e->pos();
436 KompareListViewDiffItem* diffItem = diffItemAt(vp);
437 if (diffItem && diffItem->difference()->type() != Difference::Unchanged) {
438 // FIXME: make a new signal that does both
439 Q_EMIT differenceClicked(diffItem->difference());
440 Q_EMIT applyDifference(!diffItem->difference()->applied());
441 }
442 }
443
renumberLines()444 void KompareListView::renumberLines()
445 {
446 // qCDebug(KOMPAREPART) << "Begin" ;
447 unsigned int newLineNo = 1;
448 if (!topLevelItemCount()) return;
449 KompareListViewItem* item = (KompareListViewItem*)topLevelItem(0);
450 while (item) {
451 // qCDebug(KOMPAREPART) << "type: " << item->type() ;
452 if (item->type() != KompareListViewItem::Container
453 && item->type() != KompareListViewItem::Blank
454 && item->type() != KompareListViewItem::Hunk)
455 {
456 // qCDebug(KOMPAREPART) << QString::number( newLineNo ) ;
457 item->setText(COL_LINE_NO, QString::number(newLineNo++));
458 }
459 item = (KompareListViewItem*)itemBelow(item);
460 }
461 }
462
slotApplyDifference(bool apply)463 void KompareListView::slotApplyDifference(bool apply)
464 {
465 m_itemDict[ m_selectedDifference ]->applyDifference(apply);
466 // now renumber the line column if this is the destination
467 if (!m_isSource)
468 renumberLines();
469 }
470
slotApplyAllDifferences(bool apply)471 void KompareListView::slotApplyAllDifferences(bool apply)
472 {
473 QHash<const Diff2::Difference*, KompareListViewDiffItem*>::ConstIterator it = m_itemDict.constBegin();
474 QHash<const Diff2::Difference*, KompareListViewDiffItem*>::ConstIterator end = m_itemDict.constEnd();
475 for (; it != end; ++it)
476 it.value()->applyDifference(apply);
477
478 // now renumber the line column if this is the destination
479 if (!m_isSource)
480 renumberLines();
481 update();
482 }
483
slotApplyDifference(const Difference * diff,bool apply)484 void KompareListView::slotApplyDifference(const Difference* diff, bool apply)
485 {
486 m_itemDict[ diff ]->applyDifference(apply);
487 // now renumber the line column if this is the destination
488 if (!m_isSource)
489 renumberLines();
490 }
491
wheelEvent(QWheelEvent * e)492 void KompareListView::wheelEvent(QWheelEvent* e)
493 {
494 e->ignore(); // we want the parent to catch wheel events
495 }
496
resizeEvent(QResizeEvent * e)497 void KompareListView::resizeEvent(QResizeEvent* e)
498 {
499 QTreeWidget::resizeEvent(e);
500 Q_EMIT resized();
501 }
502
KompareListViewItemDelegate(QObject * parent)503 KompareListViewItemDelegate::KompareListViewItemDelegate(QObject* parent)
504 : QStyledItemDelegate(parent)
505 {
506 }
507
~KompareListViewItemDelegate()508 KompareListViewItemDelegate::~KompareListViewItemDelegate()
509 {
510 }
511
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const512 void KompareListViewItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
513 {
514 int column = index.column();
515 QStyleOptionViewItem changedOption = option;
516 if (column == COL_LINE_NO)
517 changedOption.displayAlignment = Qt::AlignRight;
518 KompareListViewItem* item = static_cast<KompareListViewItem*>(static_cast<KompareListView*>(parent())->itemFromIndex(index));
519 item->paintCell(painter, changedOption, column);
520 }
521
sizeHint(const QStyleOptionViewItem & option,const QModelIndex & index) const522 QSize KompareListViewItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
523 {
524 KompareListViewItem* item = static_cast<KompareListViewItem*>(static_cast<KompareListView*>(parent())->itemFromIndex(index));
525 QSize hint = QStyledItemDelegate::sizeHint(option, index);
526 return QSize(hint.width() + ITEM_MARGIN, item->height());
527 }
528
KompareListViewItem(KompareListView * parent,int type)529 KompareListViewItem::KompareListViewItem(KompareListView* parent, int type)
530 : QTreeWidgetItem(parent, type),
531 m_scrollId(0),
532 m_height(0),
533 m_paintHeight(0),
534 m_paintOffset(parent->nextPaintOffset())
535 {
536 // qCDebug(KOMPAREPART) << "Created KompareListViewItem with scroll id " << m_scrollId ;
537 }
538
KompareListViewItem(KompareListView * parent,KompareListViewItem * after,int type)539 KompareListViewItem::KompareListViewItem(KompareListView* parent, KompareListViewItem* after, int type)
540 : QTreeWidgetItem(parent, after, type),
541 m_scrollId(after->scrollId() + after->maxHeight()),
542 m_height(0),
543 m_paintHeight(0),
544 m_paintOffset(parent->nextPaintOffset())
545 {
546 // qCDebug(KOMPAREPART) << "Created KompareListViewItem with scroll id " << m_scrollId ;
547 }
548
KompareListViewItem(KompareListViewItem * parent,int type)549 KompareListViewItem::KompareListViewItem(KompareListViewItem* parent, int type)
550 : QTreeWidgetItem(parent, type),
551 m_scrollId(0),
552 m_height(0),
553 m_paintHeight(0),
554 m_paintOffset(parent->kompareListView()->nextPaintOffset())
555 {
556 }
557
KompareListViewItem(KompareListViewItem * parent,KompareListViewItem *,int type)558 KompareListViewItem::KompareListViewItem(KompareListViewItem* parent, KompareListViewItem* /*after*/, int type)
559 : QTreeWidgetItem(parent, type),
560 m_scrollId(0),
561 m_height(0),
562 m_paintHeight(0),
563 m_paintOffset(parent->kompareListView()->nextPaintOffset())
564 {
565 }
566
height() const567 int KompareListViewItem::height() const
568 {
569 return m_height;
570 }
571
setHeight(int h)572 void KompareListViewItem::setHeight(int h)
573 {
574 m_height = m_paintHeight = h;
575 // QTreeWidget doesn't like zero height, fudge around it.
576 m_height -= m_paintOffset;
577 if (m_height <= 0) {
578 kompareListView()->setNextPaintOffset(1 - m_height);
579 m_height = 1;
580 } else kompareListView()->setNextPaintOffset(0);
581 }
582
paintHeight() const583 int KompareListViewItem::paintHeight() const
584 {
585 return m_paintHeight;
586 }
587
paintOffset() const588 int KompareListViewItem::paintOffset() const
589 {
590 return m_paintOffset;
591 }
592
isCurrent() const593 bool KompareListViewItem::isCurrent() const
594 {
595 return treeWidget()->currentItem() == this;
596 }
597
kompareListView() const598 KompareListView* KompareListViewItem::kompareListView() const
599 {
600 return (KompareListView*)treeWidget();
601 }
602
paintCell(QPainter * p,const QStyleOptionViewItem & option,int column)603 void KompareListViewItem::paintCell(QPainter* p, const QStyleOptionViewItem& option, int column)
604 {
605 // Default implementation for zero-height items.
606 // We have to paint the item which shines through or we'll end up with glitches.
607 KompareListViewItem* nextItem = (KompareListViewItem*)kompareListView()->itemBelow(this);
608 if (nextItem) {
609 QStyleOptionViewItem changedOption = option;
610 changedOption.rect.translate(0, height());
611 nextItem->paintCell(p, changedOption, column);
612 }
613 }
614
KompareListViewDiffItem(KompareListView * parent,Difference * difference)615 KompareListViewDiffItem::KompareListViewDiffItem(KompareListView* parent, Difference* difference)
616 : KompareListViewItem(parent, Diff),
617 m_difference(difference),
618 m_sourceItem(nullptr),
619 m_destItem(nullptr)
620 {
621 init();
622 }
623
KompareListViewDiffItem(KompareListView * parent,KompareListViewItem * after,Difference * difference)624 KompareListViewDiffItem::KompareListViewDiffItem(KompareListView* parent, KompareListViewItem* after, Difference* difference)
625 : KompareListViewItem(parent, after, Diff),
626 m_difference(difference),
627 m_sourceItem(nullptr),
628 m_destItem(nullptr)
629 {
630 init();
631 }
632
~KompareListViewDiffItem()633 KompareListViewDiffItem::~KompareListViewDiffItem()
634 {
635 m_difference = nullptr;
636 }
637
init()638 void KompareListViewDiffItem::init()
639 {
640 setHeight(0);
641 setExpanded(true);
642 int nextPaintOffset = kompareListView()->nextPaintOffset();
643 m_destItem = new KompareListViewLineContainerItem(this, false);
644 kompareListView()->setNextPaintOffset(nextPaintOffset);
645 m_sourceItem = new KompareListViewLineContainerItem(this, true);
646 setVisibility();
647 }
648
setVisibility()649 void KompareListViewDiffItem::setVisibility()
650 {
651 m_sourceItem->setHidden(!(kompareListView()->isSource() || m_difference->applied()));
652 m_destItem->setHidden(!m_sourceItem->isHidden());
653 }
654
applyDifference(bool apply)655 void KompareListViewDiffItem::applyDifference(bool apply)
656 {
657 qCDebug(KOMPAREPART) << "KompareListViewDiffItem::applyDifference( " << apply << " )" ;
658 setVisibility();
659 }
660
maxHeight()661 int KompareListViewDiffItem::maxHeight()
662 {
663 int lines = qMax(m_difference->sourceLineCount(), m_difference->destinationLineCount());
664 if (lines == 0)
665 return BLANK_LINE_HEIGHT;
666 else
667 return lines * treeWidget()->fontMetrics().height();
668 }
669
KompareListViewLineContainerItem(KompareListViewDiffItem * parent,bool isSource)670 KompareListViewLineContainerItem::KompareListViewLineContainerItem(KompareListViewDiffItem* parent, bool isSource)
671 : KompareListViewItem(parent, Container),
672 m_blankLineItem(nullptr),
673 m_isSource(isSource)
674 {
675 // qCDebug(KOMPAREPART) << "isSource ? " << (isSource ? " Yes!" : " No!") ;
676 setHeight(0);
677 setExpanded(true);
678
679 int lines = lineCount();
680 int line = lineNumber();
681 // qCDebug(KOMPAREPART) << "LineNumber : " << lineNumber() ;
682 if (lines == 0) {
683 m_blankLineItem = new KompareListViewBlankLineItem(this);
684 return;
685 }
686
687 for (int i = 0; i < lines; ++i, ++line) {
688 new KompareListViewLineItem(this, line, lineAt(i));
689 }
690 }
691
~KompareListViewLineContainerItem()692 KompareListViewLineContainerItem::~KompareListViewLineContainerItem()
693 {
694 }
695
diffItemParent() const696 KompareListViewDiffItem* KompareListViewLineContainerItem::diffItemParent() const
697 {
698 return (KompareListViewDiffItem*)parent();
699 }
700
lineCount() const701 int KompareListViewLineContainerItem::lineCount() const
702 {
703 return m_isSource ? diffItemParent()->difference()->sourceLineCount() :
704 diffItemParent()->difference()->destinationLineCount();
705 }
706
lineNumber() const707 int KompareListViewLineContainerItem::lineNumber() const
708 {
709 return m_isSource ? diffItemParent()->difference()->sourceLineNumber() :
710 diffItemParent()->difference()->destinationLineNumber();
711 }
712
lineAt(int i) const713 DifferenceString* KompareListViewLineContainerItem::lineAt(int i) const
714 {
715 return m_isSource ? diffItemParent()->difference()->sourceLineAt(i) :
716 diffItemParent()->difference()->destinationLineAt(i);
717 }
718
KompareListViewLineItem(KompareListViewLineContainerItem * parent,int line,DifferenceString * text)719 KompareListViewLineItem::KompareListViewLineItem(KompareListViewLineContainerItem* parent, int line, DifferenceString* text)
720 : KompareListViewItem(parent, Line)
721 {
722 init(line, text);
723 }
724
KompareListViewLineItem(KompareListViewLineContainerItem * parent,int line,DifferenceString * text,int type)725 KompareListViewLineItem::KompareListViewLineItem(KompareListViewLineContainerItem* parent, int line, DifferenceString* text, int type)
726 : KompareListViewItem(parent, type)
727 {
728 init(line, text);
729 }
730
~KompareListViewLineItem()731 KompareListViewLineItem::~KompareListViewLineItem()
732 {
733 m_text = nullptr;
734 }
735
init(int line,DifferenceString * text)736 void KompareListViewLineItem::init(int line, DifferenceString* text)
737 {
738 setHeight(treeWidget()->fontMetrics().height());
739 setText(COL_LINE_NO, QString::number(line));
740 setText(COL_MAIN, text->string());
741 m_text = text;
742 }
743
paintCell(QPainter * p,const QStyleOptionViewItem & option,int column)744 void KompareListViewLineItem::paintCell(QPainter* p, const QStyleOptionViewItem& option, int column)
745 {
746 int width = option.rect.width();
747 Qt::Alignment align = option.displayAlignment;
748
749 p->setRenderHint(QPainter::Antialiasing);
750 p->translate(option.rect.topLeft());
751 p->translate(0, -paintOffset());
752
753 QColor bg(Qt::white); // Always make the background white when it is not a real difference
754 if (diffItemParent()->difference()->type() == Difference::Unchanged)
755 {
756 if (column == COL_LINE_NO)
757 {
758 bg = QColor(Qt::lightGray);
759 }
760 }
761 else
762 {
763 bg = kompareListView()->settings()->colorForDifferenceType(
764 diffItemParent()->difference()->type(),
765 diffItemParent()->isCurrent(),
766 diffItemParent()->difference()->applied());
767 }
768
769 // Paint background
770 p->fillRect(0, 0, width, paintHeight(), bg);
771
772 // Paint foreground
773 if (diffItemParent()->difference()->type() == Difference::Unchanged)
774 p->setPen(QColor(Qt::darkGray)); // always make normal text gray
775 else
776 p->setPen(QColor(Qt::black)); // make text with changes black
777
778 paintText(p, bg, column, width, align);
779
780 // Paint darker lines around selected item
781 if (diffItemParent()->isCurrent())
782 {
783 p->translate(0.5, 0.5);
784 p->setPen(bg.darker(135));
785 QTreeWidgetItem* parentItem = parent();
786 if (this == parentItem->child(0))
787 p->drawLine(0, 0, width, 0);
788 if (this == parentItem->child(parentItem->childCount() - 1))
789 p->drawLine(0, paintHeight() - 1, width, paintHeight() - 1);
790 }
791
792 p->resetTransform();
793 }
794
paintText(QPainter * p,const QColor & bg,int column,int width,int align)795 void KompareListViewLineItem::paintText(QPainter* p, const QColor& bg, int column, int width, int align)
796 {
797 if (column == COL_MAIN)
798 {
799 QString textChunk;
800 int offset = ITEM_MARGIN;
801 int prevValue = 0;
802 int charsDrawn = 0;
803 int chunkWidth;
804 QBrush changeBrush(bg, Qt::Dense3Pattern);
805 QBrush normalBrush(bg, Qt::SolidPattern);
806 QBrush brush;
807
808 if (m_text->string().isEmpty())
809 {
810 p->fillRect(0, 0, width, paintHeight(), normalBrush);
811 return;
812 }
813
814 p->fillRect(0, 0, offset, paintHeight(), normalBrush);
815
816 if (!m_text->markerList().isEmpty())
817 {
818 MarkerListConstIterator markerIt = m_text->markerList().begin();
819 MarkerListConstIterator mEnd = m_text->markerList().end();
820 Marker* m = *markerIt;
821
822 for (; markerIt != mEnd; ++markerIt)
823 {
824 m = *markerIt;
825 textChunk = m_text->string().mid(prevValue, m->offset() - prevValue);
826 // qCDebug(KOMPAREPART) << "TextChunk = \"" << textChunk << "\"" ;
827 // qCDebug(KOMPAREPART) << "c->offset() = " << c->offset() ;
828 // qCDebug(KOMPAREPART) << "prevValue = " << prevValue ;
829 expandTabs(textChunk, kompareListView()->settings()->m_tabToNumberOfSpaces, charsDrawn);
830 charsDrawn += textChunk.length();
831 prevValue = m->offset();
832 if (m->type() == Marker::End)
833 {
834 QFont font(p->font());
835 font.setBold(true);
836 p->setFont(font);
837 // p->setPen( Qt::blue );
838 brush = changeBrush;
839 }
840 else
841 {
842 QFont font(p->font());
843 font.setBold(false);
844 p->setFont(font);
845 // p->setPen( Qt::black );
846 brush = normalBrush;
847 }
848 chunkWidth = p->fontMetrics().horizontalAdvance(textChunk);
849 p->fillRect(offset, 0, chunkWidth, paintHeight(), brush);
850 p->drawText(offset, 0,
851 chunkWidth, paintHeight(),
852 align, textChunk);
853 offset += chunkWidth;
854 }
855 }
856 if (prevValue < m_text->string().length())
857 {
858 // Still have to draw some string without changes
859 textChunk = m_text->string().mid(prevValue, qMax(1, m_text->string().length() - prevValue));
860 expandTabs(textChunk, kompareListView()->settings()->m_tabToNumberOfSpaces, charsDrawn);
861 // qCDebug(KOMPAREPART) << "TextChunk = \"" << textChunk << "\"" ;
862 QFont font(p->font());
863 font.setBold(false);
864 p->setFont(font);
865 chunkWidth = p->fontMetrics().horizontalAdvance(textChunk);
866 p->fillRect(offset, 0, chunkWidth, paintHeight(), normalBrush);
867 p->drawText(offset, 0,
868 chunkWidth, paintHeight(),
869 align, textChunk);
870 offset += chunkWidth;
871 }
872 p->fillRect(offset, 0, width - offset, paintHeight(), normalBrush);
873 }
874 else
875 {
876 p->fillRect(0, 0, width, paintHeight(), bg);
877 p->drawText(ITEM_MARGIN, 0,
878 width - ITEM_MARGIN, paintHeight(),
879 align, text(column));
880 }
881 }
882
expandTabs(QString & text,int tabstop,int startPos) const883 void KompareListViewLineItem::expandTabs(QString& text, int tabstop, int startPos) const
884 {
885 int index;
886 while ((index = text.indexOf(QChar(9))) != -1)
887 text.replace(index, 1, QString(tabstop - ((startPos + index) % tabstop), QLatin1Char(' ')));
888 }
889
diffItemParent() const890 KompareListViewDiffItem* KompareListViewLineItem::diffItemParent() const
891 {
892 KompareListViewLineContainerItem* p = (KompareListViewLineContainerItem*)parent();
893 return p->diffItemParent();
894 }
895
KompareListViewBlankLineItem(KompareListViewLineContainerItem * parent)896 KompareListViewBlankLineItem::KompareListViewBlankLineItem(KompareListViewLineContainerItem* parent)
897 : KompareListViewLineItem(parent, 0, new DifferenceString(), Blank)
898 {
899 setHeight(BLANK_LINE_HEIGHT);
900 }
901
paintText(QPainter * p,const QColor & bg,int column,int width,int)902 void KompareListViewBlankLineItem::paintText(QPainter* p, const QColor& bg, int column, int width, int /* align */)
903 {
904 if (column == COL_MAIN)
905 {
906 QBrush normalBrush(bg, Qt::SolidPattern);
907 p->fillRect(0, 0, width, paintHeight(), normalBrush);
908 }
909 }
910
KompareListViewHunkItem(KompareListView * parent,DiffHunk * hunk,bool zeroHeight)911 KompareListViewHunkItem::KompareListViewHunkItem(KompareListView* parent, DiffHunk* hunk, bool zeroHeight)
912 : KompareListViewItem(parent, Hunk),
913 m_zeroHeight(zeroHeight),
914 m_hunk(hunk)
915 {
916 setHeight(maxHeight());
917 setFlags(flags() & ~Qt::ItemIsSelectable);
918 }
919
KompareListViewHunkItem(KompareListView * parent,KompareListViewItem * after,DiffHunk * hunk,bool zeroHeight)920 KompareListViewHunkItem::KompareListViewHunkItem(KompareListView* parent, KompareListViewItem* after, DiffHunk* hunk, bool zeroHeight)
921 : KompareListViewItem(parent, after, Hunk),
922 m_zeroHeight(zeroHeight),
923 m_hunk(hunk)
924 {
925 setHeight(maxHeight());
926 setFlags(flags() & ~Qt::ItemIsSelectable);
927 }
928
~KompareListViewHunkItem()929 KompareListViewHunkItem::~KompareListViewHunkItem()
930 {
931 m_hunk = nullptr;
932 }
933
maxHeight()934 int KompareListViewHunkItem::maxHeight()
935 {
936 if (m_zeroHeight) {
937 return 0;
938 } else if (m_hunk->function().isEmpty()) {
939 return HUNK_LINE_HEIGHT;
940 } else {
941 return treeWidget()->fontMetrics().height();
942 }
943 }
944
paintCell(QPainter * p,const QStyleOptionViewItem & option,int column)945 void KompareListViewHunkItem::paintCell(QPainter* p, const QStyleOptionViewItem& option, int column)
946 {
947 if (m_zeroHeight) {
948 KompareListViewItem::paintCell(p, option, column);
949 } else {
950 int x = option.rect.left();
951 int y = option.rect.top() - paintOffset();
952 int width = option.rect.width();
953 Qt::Alignment align = option.displayAlignment;
954
955 p->fillRect(x, y, width, paintHeight(), QColor(Qt::lightGray)); // Hunk headers should be lightgray
956 p->setPen(QColor(Qt::black)); // Text color in hunk should be black
957 if (column == COL_MAIN) {
958 p->drawText(x + ITEM_MARGIN, y, width - ITEM_MARGIN, paintHeight(),
959 align, m_hunk->function());
960 }
961 }
962 }
963