File indexing completed on 2024-05-12 09:48:32

0001 /*
0002     SPDX-FileCopyrightText: 2001-2009 Otto Bruggeman <bruggie@gmail.com>
0003     SPDX-FileCopyrightText: 2001-2003 John Firebaugh <jfirebaugh@kde.org>
0004     SPDX-FileCopyrightText: 2004 Jeff Snyder <jeff@caffeinated.me.uk>
0005     SPDX-FileCopyrightText: 2007-2012 Kevin Kofler <kevin.kofler@chello.at>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "komparelistview.h"
0011 
0012 #include <QStyle>
0013 #include <QPainter>
0014 #include <QTimer>
0015 #include <QResizeEvent>
0016 #include <QMouseEvent>
0017 #include <QWheelEvent>
0018 #include <QScrollBar>
0019 
0020 #include <KSharedConfig>
0021 
0022 #include <KompareDiff2/DiffModel>
0023 #include <KompareDiff2/DiffHunk>
0024 #include <KompareDiff2/Difference>
0025 #include <KompareDiff2/KompareModelList>
0026 
0027 #include <komparepartdebug.h>
0028 #include "viewsettings.h"
0029 #include "komparesplitter.h"
0030 
0031 #define COL_LINE_NO      0
0032 #define COL_MAIN         1
0033 
0034 #define BLANK_LINE_HEIGHT 3
0035 #define HUNK_LINE_HEIGHT  5
0036 
0037 #define ITEM_MARGIN 3
0038 
0039 using namespace Diff2;
0040 
0041 KompareListViewFrame::KompareListViewFrame(bool isSource,
0042                                            ViewSettings* settings,
0043                                            KompareSplitter* parent,
0044                                            const char* name):
0045     QFrame(parent),
0046     m_view(isSource, settings, this, name),
0047     m_label(isSource ? QStringLiteral("Source") : QStringLiteral("Dest"), this),
0048     m_layout(this)
0049 {
0050     setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
0051     m_label.setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
0052     QFrame* bottomLine = new QFrame(this);
0053     bottomLine->setFrameShape(QFrame::HLine);
0054     bottomLine->setFrameShadow(QFrame::Plain);
0055     bottomLine->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
0056     bottomLine->setFixedHeight(1);
0057     m_label.setMargin(3);
0058     m_layout.setSpacing(0);
0059     m_layout.setContentsMargins(0, 0, 0, 0);
0060     m_layout.addWidget(&m_label);
0061     m_layout.addWidget(bottomLine);
0062     m_layout.addWidget(&m_view);
0063 
0064     connect(&m_view, &KompareListView::differenceClicked,
0065             parent, &KompareSplitter::slotDifferenceClicked);
0066 
0067     connect(parent, &KompareSplitter::scrollViewsToId, &m_view, &KompareListView::scrollToId);
0068     connect(parent, &KompareSplitter::setXOffset, &m_view, &KompareListView::setXOffset);
0069     connect(&m_view, &KompareListView::resized, parent, &KompareSplitter::slotUpdateScrollBars);
0070 }
0071 
0072 void KompareListViewFrame::slotSetModel(const DiffModel* model)
0073 {
0074     if (model)
0075     {
0076         if (view()->isSource()) {
0077             if (!model->sourceRevision().isEmpty())
0078                 m_label.setText(model->sourceFile() + QLatin1String(" (") + model->sourceRevision() + QLatin1Char(')'));
0079             else
0080                 m_label.setText(model->sourceFile());
0081         } else {
0082             if (!model->destinationRevision().isEmpty())
0083                 m_label.setText(model->destinationFile() + QLatin1String(" (") + model->destinationRevision() + QLatin1Char(')'));
0084             else
0085                 m_label.setText(model->destinationFile());
0086         }
0087     } else {
0088         m_label.setText(QString());
0089     }
0090 }
0091 
0092 KompareListView::KompareListView(bool isSource,
0093                                  ViewSettings* settings,
0094                                  QWidget* parent, const char* name) :
0095     QTreeWidget(parent),
0096     m_isSource(isSource),
0097     m_settings(settings),
0098     m_scrollId(-1),
0099     m_selectedModel(nullptr),
0100     m_selectedDifference(nullptr)
0101 {
0102     setObjectName(QLatin1String(name));
0103     setItemDelegate(new KompareListViewItemDelegate(this));
0104     setHeaderHidden(true);
0105     setColumnCount(3);   // Line Number, Main, Blank
0106     setAllColumnsShowFocus(true);
0107     setRootIsDecorated(false);
0108     setIndentation(0);
0109     setFrameStyle(QFrame::NoFrame);
0110     setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
0111     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0112     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0113     setFocusPolicy(Qt::NoFocus);
0114     setFont(m_settings->m_font);
0115     setFocusProxy(parent->parentWidget());
0116 }
0117 
0118 KompareListView::~KompareListView()
0119 {
0120     m_settings = nullptr;
0121     m_selectedModel = nullptr;
0122     m_selectedDifference = nullptr;
0123 }
0124 
0125 KompareListViewItem* KompareListView::itemAtIndex(int i)
0126 {
0127     return m_items[ i ];
0128 }
0129 
0130 int KompareListView::firstVisibleDifference()
0131 {
0132     QTreeWidgetItem* item = itemAt(QPoint(0, 0));
0133 
0134     if (item == nullptr)
0135     {
0136         qCDebug(KOMPAREPART) << "no item at viewport coordinates (0,0)" ;
0137     }
0138 
0139     while (item) {
0140         KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>(item);
0141         if (lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged)
0142             break;
0143         item = itemBelow(item);
0144     }
0145 
0146     if (item)
0147         return m_items.indexOf(((KompareListViewLineItem*)item)->diffItemParent());
0148 
0149     return -1;
0150 }
0151 
0152 int KompareListView::lastVisibleDifference()
0153 {
0154     QTreeWidgetItem* item = itemAt(QPoint(0, visibleHeight() - 1));
0155 
0156     if (item == nullptr)
0157     {
0158         qCDebug(KOMPAREPART) << "no item at viewport coordinates (0," << visibleHeight() - 1 << ")" ;
0159         // find last item
0160         item = itemAt(QPoint(0, 0));
0161         if (item) {
0162             QTreeWidgetItem* nextItem = item;
0163             do {
0164                 item = nextItem;
0165                 nextItem = itemBelow(item);
0166             } while (nextItem);
0167         }
0168     }
0169 
0170     while (item) {
0171         KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>(item);
0172         if (lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged)
0173             break;
0174         item = itemAbove(item);
0175     }
0176 
0177     if (item)
0178         return m_items.indexOf(((KompareListViewLineItem*)item)->diffItemParent());
0179 
0180     return -1;
0181 }
0182 
0183 QRect KompareListView::totalVisualItemRect(QTreeWidgetItem* item)
0184 {
0185     QRect total = visualItemRect(item);
0186     int n = item->childCount();
0187     for (int i = 0; i < n; ++i) {
0188         QTreeWidgetItem* child = item->child(i);
0189         if (!child->isHidden())
0190             total = total.united(totalVisualItemRect(child));
0191     }
0192     return total;
0193 }
0194 
0195 QRect KompareListView::itemRect(int i)
0196 {
0197     QTreeWidgetItem* item = itemAtIndex(i);
0198     return totalVisualItemRect(item);
0199 }
0200 
0201 int KompareListView::minScrollId()
0202 {
0203     return visibleHeight() / 2;
0204 }
0205 
0206 int KompareListView::maxScrollId()
0207 {
0208     int n = topLevelItemCount();
0209     if (!n) return 0;
0210     KompareListViewItem* item = (KompareListViewItem*)topLevelItem(n - 1);
0211     int maxId = item->scrollId() + item->maxHeight() - minScrollId();
0212     qCDebug(KOMPAREPART) << "Max ID = " << maxId ;
0213     return maxId;
0214 }
0215 
0216 int KompareListView::contentsHeight()
0217 {
0218     return verticalScrollBar()->maximum() + viewport()->height() - style()->pixelMetric(QStyle::PM_ScrollBarExtent);
0219 }
0220 
0221 int KompareListView::contentsWidth()
0222 {
0223     return (columnWidth(COL_LINE_NO) + columnWidth(COL_MAIN));
0224 }
0225 
0226 int KompareListView::visibleHeight()
0227 {
0228     return viewport()->height();
0229 }
0230 
0231 int KompareListView::visibleWidth()
0232 {
0233     return viewport()->width();
0234 }
0235 
0236 int KompareListView::contentsX()
0237 {
0238     return horizontalOffset();
0239 }
0240 
0241 int KompareListView::contentsY()
0242 {
0243     return verticalOffset();
0244 }
0245 
0246 int KompareListView::nextPaintOffset() const
0247 {
0248     return m_nextPaintOffset;
0249 }
0250 
0251 void KompareListView::setNextPaintOffset(int offset)
0252 {
0253     m_nextPaintOffset = offset;
0254 }
0255 
0256 void KompareListView::setXOffset(int x)
0257 {
0258     qCDebug(KOMPAREPART) << "SetXOffset : Scroll to x position: " << x ;
0259     horizontalScrollBar()->setValue(x);
0260 }
0261 
0262 void KompareListView::scrollToId(int id)
0263 {
0264 //     qCDebug(KOMPAREPART) << "ScrollToID : Scroll to id : " << id ;
0265     int n = topLevelItemCount();
0266     KompareListViewItem* item = nullptr;
0267     if (n) {
0268         int i = 1;
0269         for (; i < n; ++i) {
0270             if (((KompareListViewItem*)topLevelItem(i))->scrollId() > id)
0271                 break;
0272         }
0273         item = (KompareListViewItem*)topLevelItem(i - 1);
0274     }
0275 
0276     if (item) {
0277         QRect rect = totalVisualItemRect(item);
0278         int pos = rect.top() + verticalOffset();
0279         int itemId = item->scrollId();
0280         int height = rect.height();
0281         double r = (double)(id - itemId) / (double)item->maxHeight();
0282         int y = pos + (int)(r * (double)height) - minScrollId();
0283 //         qCDebug(KOMPAREPART) << "scrollToID: " ;
0284 //         qCDebug(KOMPAREPART) << "            id = " << id ;
0285 //         qCDebug(KOMPAREPART) << "           pos = " << pos ;
0286 //         qCDebug(KOMPAREPART) << "        itemId = " << itemId ;
0287 //         qCDebug(KOMPAREPART) << "             r = " << r ;
0288 //         qCDebug(KOMPAREPART) << "        height = " << height ;
0289 //         qCDebug(KOMPAREPART) << "         minID = " << minScrollId() ;
0290 //         qCDebug(KOMPAREPART) << "             y = " << y ;
0291 //         qCDebug(KOMPAREPART) << "contentsHeight = " << contentsHeight() ;
0292 //         qCDebug(KOMPAREPART) << "         c - y = " << contentsHeight() - y ;
0293         verticalScrollBar()->setValue(y);
0294     }
0295 
0296     m_scrollId = id;
0297 }
0298 
0299 int KompareListView::scrollId()
0300 {
0301     if (m_scrollId < 0)
0302         m_scrollId = minScrollId();
0303     return m_scrollId;
0304 }
0305 
0306 void KompareListView::setSelectedDifference(const Difference* diff, bool scroll)
0307 {
0308     qCDebug(KOMPAREPART) << "KompareListView::setSelectedDifference(" << diff << ", " << scroll << ")" ;
0309 
0310     // When something other than a click causes this function to be called,
0311     // it'll only get called once, and all is simple.
0312     //
0313     // When the user clicks on a diff, this function will get called once when
0314     // komparesplitter::slotDifferenceClicked runs, and again when the
0315     // setSelection signal from the modelcontroller arrives.
0316     //
0317     // the first call (which will always be from the splitter) will have
0318     // scroll==false, and the second call will bail out here.
0319     // Which is why clicking on a difference does not cause the listviews to
0320     // scroll.
0321     if (m_selectedDifference == diff)
0322         return;
0323 
0324     m_selectedDifference = diff;
0325 
0326     KompareListViewItem* item = m_itemDict[ diff ];
0327     if (!item) {
0328         qCDebug(KOMPAREPART) << "KompareListView::slotSetSelection(): couldn't find our selection!" ;
0329         return;
0330     }
0331 
0332     // why does this not happen when the user clicks on a diff? see the comment above.
0333     if (scroll)
0334         scrollToId(item->scrollId());
0335     setUpdatesEnabled(false);
0336     int x = horizontalScrollBar()->value();
0337     int y = verticalScrollBar()->value();
0338     setCurrentItem(item);
0339     horizontalScrollBar()->setValue(x);
0340     verticalScrollBar()->setValue(y);
0341     setUpdatesEnabled(true);
0342 }
0343 
0344 void KompareListView::slotSetSelection(const Difference* diff)
0345 {
0346     qCDebug(KOMPAREPART) << "KompareListView::slotSetSelection( const Difference* diff )" ;
0347 
0348     setSelectedDifference(diff, true);
0349 }
0350 
0351 void KompareListView::slotSetSelection(const DiffModel* model, const Difference* diff)
0352 {
0353     qCDebug(KOMPAREPART) << "KompareListView::slotSetSelection( const DiffModel* model, const Difference* diff )" ;
0354 
0355     if (m_selectedModel && m_selectedModel == model) {
0356         slotSetSelection(diff);
0357         return;
0358     }
0359 
0360     clear();
0361     m_items.clear();
0362     m_itemDict.clear();
0363     m_selectedModel = model;
0364 
0365     DiffHunkListConstIterator hunkIt = model->hunks()->begin();
0366     DiffHunkListConstIterator hEnd   = model->hunks()->end();
0367 
0368     KompareListViewItem* item = nullptr;
0369     m_nextPaintOffset = 0;
0370 
0371     for (; hunkIt != hEnd; ++hunkIt)
0372     {
0373         if (item)
0374             item = new KompareListViewHunkItem(this, item, *hunkIt, model->isBlended());
0375         else
0376             item = new KompareListViewHunkItem(this, *hunkIt, model->isBlended());
0377 
0378         DifferenceListConstIterator diffIt = (*hunkIt)->differences().begin();
0379         DifferenceListConstIterator dEnd   = (*hunkIt)->differences().end();
0380 
0381         for (; diffIt != dEnd; ++diffIt)
0382         {
0383             item = new KompareListViewDiffItem(this, item, *diffIt);
0384 
0385             int type = (*diffIt)->type();
0386 
0387             if (type != Difference::Unchanged)
0388             {
0389                 m_items.append((KompareListViewDiffItem*)item);
0390                 m_itemDict.insert(*diffIt, (KompareListViewDiffItem*)item);
0391             }
0392         }
0393     }
0394 
0395     resizeColumnToContents(COL_LINE_NO);
0396     resizeColumnToContents(COL_MAIN);
0397 
0398     slotSetSelection(diff);
0399 }
0400 
0401 KompareListViewDiffItem* KompareListView::diffItemAt(const QPoint& pos)
0402 {
0403     KompareListViewItem* item = static_cast<KompareListViewItem*>(itemAt(pos));
0404     if (!item)
0405         return nullptr;
0406     switch (item->type()) {
0407         case KompareListViewItem::Hunk:
0408             if (item->paintHeight()) return nullptr;  // no diff item here
0409             // zero height (fake 1 pixel height), so a diff item shines through
0410             return static_cast<KompareListViewDiffItem*>(itemBelow(item));
0411         case KompareListViewItem::Line:
0412         case KompareListViewItem::Blank:
0413             return static_cast<KompareListViewLineItem*>(item)->diffItemParent();
0414         case KompareListViewItem::Container:
0415             return static_cast<KompareListViewLineContainerItem*>(item)->diffItemParent();
0416         case KompareListViewItem::Diff:
0417             return static_cast<KompareListViewDiffItem*>(item);
0418         default:
0419             return nullptr;
0420     }
0421 }
0422 
0423 void KompareListView::mousePressEvent(QMouseEvent* e)
0424 {
0425     QPoint vp = e->pos();
0426     KompareListViewDiffItem* diffItem = diffItemAt(vp);
0427     if (diffItem && diffItem->difference()->type() != Difference::Unchanged) {
0428         Q_EMIT differenceClicked(diffItem->difference());
0429     }
0430 }
0431 
0432 void KompareListView::mouseDoubleClickEvent(QMouseEvent* e)
0433 {
0434     QPoint vp = e->pos();
0435     KompareListViewDiffItem* diffItem = diffItemAt(vp);
0436     if (diffItem && diffItem->difference()->type() != Difference::Unchanged) {
0437         // FIXME: make a new signal that does both
0438         Q_EMIT differenceClicked(diffItem->difference());
0439         Q_EMIT applyDifference(!diffItem->difference()->applied());
0440     }
0441 }
0442 
0443 void KompareListView::renumberLines()
0444 {
0445 //     qCDebug(KOMPAREPART) << "Begin" ;
0446     unsigned int newLineNo = 1;
0447     if (!topLevelItemCount()) return;
0448     KompareListViewItem* item = (KompareListViewItem*)topLevelItem(0);
0449     while (item) {
0450 //         qCDebug(KOMPAREPART) << "type: " << item->type() ;
0451         if (item->type() != KompareListViewItem::Container
0452                 && item->type() != KompareListViewItem::Blank
0453                 && item->type() != KompareListViewItem::Hunk)
0454         {
0455 //             qCDebug(KOMPAREPART) << QString::number( newLineNo ) ;
0456             item->setText(COL_LINE_NO, QString::number(newLineNo++));
0457         }
0458         item = (KompareListViewItem*)itemBelow(item);
0459     }
0460 }
0461 
0462 void KompareListView::slotApplyDifference(bool apply)
0463 {
0464     m_itemDict[ m_selectedDifference ]->applyDifference(apply);
0465     // now renumber the line column if this is the destination
0466     if (!m_isSource)
0467         renumberLines();
0468 }
0469 
0470 void KompareListView::slotApplyAllDifferences(bool apply)
0471 {
0472     QHash<const Diff2::Difference*, KompareListViewDiffItem*>::ConstIterator it = m_itemDict.constBegin();
0473     QHash<const Diff2::Difference*, KompareListViewDiffItem*>::ConstIterator end = m_itemDict.constEnd();
0474     for (; it != end; ++it)
0475         it.value()->applyDifference(apply);
0476 
0477     // now renumber the line column if this is the destination
0478     if (!m_isSource)
0479         renumberLines();
0480     update();
0481 }
0482 
0483 void KompareListView::slotApplyDifference(const Difference* diff, bool apply)
0484 {
0485     m_itemDict[ diff ]->applyDifference(apply);
0486     // now renumber the line column if this is the destination
0487     if (!m_isSource)
0488         renumberLines();
0489 }
0490 
0491 void KompareListView::wheelEvent(QWheelEvent* e)
0492 {
0493     e->ignore(); // we want the parent to catch wheel events
0494 }
0495 
0496 void KompareListView::resizeEvent(QResizeEvent* e)
0497 {
0498     QTreeWidget::resizeEvent(e);
0499     Q_EMIT resized();
0500 }
0501 
0502 KompareListViewItemDelegate::KompareListViewItemDelegate(QObject* parent)
0503     : QStyledItemDelegate(parent)
0504 {
0505 }
0506 
0507 KompareListViewItemDelegate::~KompareListViewItemDelegate()
0508 {
0509 }
0510 
0511 void KompareListViewItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
0512 {
0513     int column = index.column();
0514     QStyleOptionViewItem changedOption = option;
0515     if (column == COL_LINE_NO)
0516         changedOption.displayAlignment = Qt::AlignRight;
0517     KompareListViewItem* item = static_cast<KompareListViewItem*>(static_cast<KompareListView*>(parent())->itemFromIndex(index));
0518     item->paintCell(painter, changedOption, column);
0519 }
0520 
0521 QSize KompareListViewItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
0522 {
0523     KompareListViewItem* item = static_cast<KompareListViewItem*>(static_cast<KompareListView*>(parent())->itemFromIndex(index));
0524     QSize hint = QStyledItemDelegate::sizeHint(option, index);
0525     return QSize(hint.width() + ITEM_MARGIN, item->height());
0526 }
0527 
0528 KompareListViewItem::KompareListViewItem(KompareListView* parent, int type)
0529     : QTreeWidgetItem(parent, type),
0530       m_scrollId(0),
0531       m_height(0),
0532       m_paintHeight(0),
0533       m_paintOffset(parent->nextPaintOffset())
0534 {
0535 //     qCDebug(KOMPAREPART) << "Created KompareListViewItem with scroll id " << m_scrollId ;
0536 }
0537 
0538 KompareListViewItem::KompareListViewItem(KompareListView* parent, KompareListViewItem* after, int type)
0539     : QTreeWidgetItem(parent, after, type),
0540       m_scrollId(after->scrollId() + after->maxHeight()),
0541       m_height(0),
0542       m_paintHeight(0),
0543       m_paintOffset(parent->nextPaintOffset())
0544 {
0545 //     qCDebug(KOMPAREPART) << "Created KompareListViewItem with scroll id " << m_scrollId ;
0546 }
0547 
0548 KompareListViewItem::KompareListViewItem(KompareListViewItem* parent, int type)
0549     : QTreeWidgetItem(parent, type),
0550       m_scrollId(0),
0551       m_height(0),
0552       m_paintHeight(0),
0553       m_paintOffset(parent->kompareListView()->nextPaintOffset())
0554 {
0555 }
0556 
0557 KompareListViewItem::KompareListViewItem(KompareListViewItem* parent, KompareListViewItem* /*after*/, int type)
0558     : QTreeWidgetItem(parent, type),
0559       m_scrollId(0),
0560       m_height(0),
0561       m_paintHeight(0),
0562       m_paintOffset(parent->kompareListView()->nextPaintOffset())
0563 {
0564 }
0565 
0566 int KompareListViewItem::height() const
0567 {
0568     return m_height;
0569 }
0570 
0571 void KompareListViewItem::setHeight(int h)
0572 {
0573     m_height = m_paintHeight = h;
0574     // QTreeWidget doesn't like zero height, fudge around it.
0575     m_height -= m_paintOffset;
0576     if (m_height <= 0) {
0577         kompareListView()->setNextPaintOffset(1 - m_height);
0578         m_height = 1;
0579     } else kompareListView()->setNextPaintOffset(0);
0580 }
0581 
0582 int KompareListViewItem::paintHeight() const
0583 {
0584     return m_paintHeight;
0585 }
0586 
0587 int KompareListViewItem::paintOffset() const
0588 {
0589     return m_paintOffset;
0590 }
0591 
0592 bool KompareListViewItem::isCurrent() const
0593 {
0594     return treeWidget()->currentItem() == this;
0595 }
0596 
0597 KompareListView* KompareListViewItem::kompareListView() const
0598 {
0599     return (KompareListView*)treeWidget();
0600 }
0601 
0602 void KompareListViewItem::paintCell(QPainter* p, const QStyleOptionViewItem& option, int column)
0603 {
0604     // Default implementation for zero-height items.
0605     // We have to paint the item which shines through or we'll end up with glitches.
0606     KompareListViewItem* nextItem = (KompareListViewItem*)kompareListView()->itemBelow(this);
0607     if (nextItem) {
0608         QStyleOptionViewItem changedOption = option;
0609         changedOption.rect.translate(0, height());
0610         nextItem->paintCell(p, changedOption, column);
0611     }
0612 }
0613 
0614 KompareListViewDiffItem::KompareListViewDiffItem(KompareListView* parent, Difference* difference)
0615     : KompareListViewItem(parent, Diff),
0616       m_difference(difference),
0617       m_sourceItem(nullptr),
0618       m_destItem(nullptr)
0619 {
0620     init();
0621 }
0622 
0623 KompareListViewDiffItem::KompareListViewDiffItem(KompareListView* parent, KompareListViewItem* after, Difference* difference)
0624     : KompareListViewItem(parent, after, Diff),
0625       m_difference(difference),
0626       m_sourceItem(nullptr),
0627       m_destItem(nullptr)
0628 {
0629     init();
0630 }
0631 
0632 KompareListViewDiffItem::~KompareListViewDiffItem()
0633 {
0634     m_difference = nullptr;
0635 }
0636 
0637 void KompareListViewDiffItem::init()
0638 {
0639     setHeight(0);
0640     setExpanded(true);
0641     int nextPaintOffset = kompareListView()->nextPaintOffset();
0642     m_destItem = new KompareListViewLineContainerItem(this, false);
0643     kompareListView()->setNextPaintOffset(nextPaintOffset);
0644     m_sourceItem = new KompareListViewLineContainerItem(this, true);
0645     setVisibility();
0646 }
0647 
0648 void KompareListViewDiffItem::setVisibility()
0649 {
0650     m_sourceItem->setHidden(!(kompareListView()->isSource() || m_difference->applied()));
0651     m_destItem->setHidden(!m_sourceItem->isHidden());
0652 }
0653 
0654 void KompareListViewDiffItem::applyDifference(bool apply)
0655 {
0656     qCDebug(KOMPAREPART) << "KompareListViewDiffItem::applyDifference( " << apply << " )" ;
0657     setVisibility();
0658 }
0659 
0660 int KompareListViewDiffItem::maxHeight()
0661 {
0662     int lines = qMax(m_difference->sourceLineCount(), m_difference->destinationLineCount());
0663     if (lines == 0)
0664         return BLANK_LINE_HEIGHT;
0665     else
0666         return lines * treeWidget()->fontMetrics().height();
0667 }
0668 
0669 KompareListViewLineContainerItem::KompareListViewLineContainerItem(KompareListViewDiffItem* parent, bool isSource)
0670     : KompareListViewItem(parent, Container),
0671       m_blankLineItem(nullptr),
0672       m_isSource(isSource)
0673 {
0674 //     qCDebug(KOMPAREPART) << "isSource ? " << (isSource ? " Yes!" : " No!") ;
0675     setHeight(0);
0676     setExpanded(true);
0677 
0678     int lines = lineCount();
0679     int line = lineNumber();
0680 //     qCDebug(KOMPAREPART) << "LineNumber : " << lineNumber() ;
0681     if (lines == 0) {
0682         m_blankLineItem = new KompareListViewBlankLineItem(this);
0683         return;
0684     }
0685 
0686     for (int i = 0; i < lines; ++i, ++line) {
0687         new KompareListViewLineItem(this, line, lineAt(i));
0688     }
0689 }
0690 
0691 KompareListViewLineContainerItem::~KompareListViewLineContainerItem()
0692 {
0693 }
0694 
0695 KompareListViewDiffItem* KompareListViewLineContainerItem::diffItemParent() const
0696 {
0697     return (KompareListViewDiffItem*)parent();
0698 }
0699 
0700 int KompareListViewLineContainerItem::lineCount() const
0701 {
0702     return m_isSource ? diffItemParent()->difference()->sourceLineCount() :
0703            diffItemParent()->difference()->destinationLineCount();
0704 }
0705 
0706 int KompareListViewLineContainerItem::lineNumber() const
0707 {
0708     return m_isSource ? diffItemParent()->difference()->sourceLineNumber() :
0709            diffItemParent()->difference()->destinationLineNumber();
0710 }
0711 
0712 DifferenceString* KompareListViewLineContainerItem::lineAt(int i) const
0713 {
0714     return m_isSource ? diffItemParent()->difference()->sourceLineAt(i) :
0715            diffItemParent()->difference()->destinationLineAt(i);
0716 }
0717 
0718 KompareListViewLineItem::KompareListViewLineItem(KompareListViewLineContainerItem* parent, int line, DifferenceString* text)
0719     : KompareListViewItem(parent, Line)
0720 {
0721     init(line, text);
0722 }
0723 
0724 KompareListViewLineItem::KompareListViewLineItem(KompareListViewLineContainerItem* parent, int line, DifferenceString* text, int type)
0725     : KompareListViewItem(parent, type)
0726 {
0727     init(line, text);
0728 }
0729 
0730 KompareListViewLineItem::~KompareListViewLineItem()
0731 {
0732     m_text = nullptr;
0733 }
0734 
0735 void KompareListViewLineItem::init(int line, DifferenceString* text)
0736 {
0737     setHeight(treeWidget()->fontMetrics().height());
0738     setText(COL_LINE_NO, QString::number(line));
0739     setText(COL_MAIN, text->string());
0740     m_text = text;
0741 }
0742 
0743 void KompareListViewLineItem::paintCell(QPainter* p, const QStyleOptionViewItem& option, int column)
0744 {
0745     int width = option.rect.width();
0746     Qt::Alignment align = option.displayAlignment;
0747 
0748     p->setRenderHint(QPainter::Antialiasing);
0749     p->translate(option.rect.topLeft());
0750     p->translate(0, -paintOffset());
0751 
0752     QColor bg(Qt::white);   // Always make the background white when it is not a real difference
0753     if (diffItemParent()->difference()->type() == Difference::Unchanged)
0754     {
0755         if (column == COL_LINE_NO)
0756         {
0757             bg = QColor(Qt::lightGray);
0758         }
0759     }
0760     else
0761     {
0762         bg = kompareListView()->settings()->colorForDifferenceType(
0763                  diffItemParent()->difference()->type(),
0764                  diffItemParent()->isCurrent(),
0765                  diffItemParent()->difference()->applied());
0766     }
0767 
0768     // Paint background
0769     p->fillRect(0, 0, width, paintHeight(), bg);
0770 
0771     // Paint foreground
0772     if (diffItemParent()->difference()->type() == Difference::Unchanged)
0773         p->setPen(QColor(Qt::darkGray));     // always make normal text gray
0774     else
0775         p->setPen(QColor(Qt::black));     // make text with changes black
0776 
0777     paintText(p, bg, column, width, align);
0778 
0779     // Paint darker lines around selected item
0780     if (diffItemParent()->isCurrent())
0781     {
0782         p->translate(0.5, 0.5);
0783         p->setPen(bg.darker(135));
0784         QTreeWidgetItem* parentItem = parent();
0785         if (this == parentItem->child(0))
0786             p->drawLine(0, 0, width, 0);
0787         if (this == parentItem->child(parentItem->childCount() - 1))
0788             p->drawLine(0, paintHeight() - 1, width, paintHeight() - 1);
0789     }
0790 
0791     p->resetTransform();
0792 }
0793 
0794 void KompareListViewLineItem::paintText(QPainter* p, const QColor& bg, int column, int width, int align)
0795 {
0796     if (column == COL_MAIN)
0797     {
0798         QString textChunk;
0799         int offset = ITEM_MARGIN;
0800         int prevValue = 0;
0801         int charsDrawn = 0;
0802         int chunkWidth;
0803         QBrush changeBrush(bg, Qt::Dense3Pattern);
0804         QBrush normalBrush(bg, Qt::SolidPattern);
0805         QBrush brush;
0806 
0807         if (m_text->string().isEmpty())
0808         {
0809             p->fillRect(0, 0, width, paintHeight(), normalBrush);
0810             return;
0811         }
0812 
0813         p->fillRect(0, 0, offset, paintHeight(), normalBrush);
0814 
0815         if (!m_text->markerList().isEmpty())
0816         {
0817             MarkerListConstIterator markerIt = m_text->markerList().begin();
0818             MarkerListConstIterator mEnd     = m_text->markerList().end();
0819             Marker* m = *markerIt;
0820 
0821             for (; markerIt != mEnd; ++markerIt)
0822             {
0823                 m  = *markerIt;
0824                 textChunk = m_text->string().mid(prevValue, m->offset() - prevValue);
0825 //                 qCDebug(KOMPAREPART) << "TextChunk   = \"" << textChunk << "\"" ;
0826 //                 qCDebug(KOMPAREPART) << "c->offset() = " << c->offset() ;
0827 //                 qCDebug(KOMPAREPART) << "prevValue   = " << prevValue ;
0828                 expandTabs(textChunk, kompareListView()->settings()->m_tabToNumberOfSpaces, charsDrawn);
0829                 charsDrawn += textChunk.length();
0830                 prevValue = m->offset();
0831                 if (m->type() == Marker::End)
0832                 {
0833                     QFont font(p->font());
0834                     font.setBold(true);
0835                     p->setFont(font);
0836 //                     p->setPen( Qt::blue );
0837                     brush = changeBrush;
0838                 }
0839                 else
0840                 {
0841                     QFont font(p->font());
0842                     font.setBold(false);
0843                     p->setFont(font);
0844 //                     p->setPen( Qt::black );
0845                     brush = normalBrush;
0846                 }
0847                 chunkWidth = p->fontMetrics().horizontalAdvance(textChunk);
0848                 p->fillRect(offset, 0, chunkWidth, paintHeight(), brush);
0849                 p->drawText(offset, 0,
0850                             chunkWidth, paintHeight(),
0851                             align, textChunk);
0852                 offset += chunkWidth;
0853             }
0854         }
0855         if (prevValue < m_text->string().length())
0856         {
0857             // Still have to draw some string without changes
0858             textChunk = m_text->string().mid(prevValue, qMax(1, m_text->string().length() - prevValue));
0859             expandTabs(textChunk, kompareListView()->settings()->m_tabToNumberOfSpaces, charsDrawn);
0860 //             qCDebug(KOMPAREPART) << "TextChunk   = \"" << textChunk << "\"" ;
0861             QFont font(p->font());
0862             font.setBold(false);
0863             p->setFont(font);
0864             chunkWidth = p->fontMetrics().horizontalAdvance(textChunk);
0865             p->fillRect(offset, 0, chunkWidth, paintHeight(), normalBrush);
0866             p->drawText(offset, 0,
0867                         chunkWidth, paintHeight(),
0868                         align, textChunk);
0869             offset += chunkWidth;
0870         }
0871         p->fillRect(offset, 0, width - offset, paintHeight(), normalBrush);
0872     }
0873     else
0874     {
0875         p->fillRect(0, 0, width, paintHeight(), bg);
0876         p->drawText(ITEM_MARGIN, 0,
0877                     width - ITEM_MARGIN, paintHeight(),
0878                     align, text(column));
0879     }
0880 }
0881 
0882 void KompareListViewLineItem::expandTabs(QString& text, int tabstop, int startPos) const
0883 {
0884     int index;
0885     while ((index = text.indexOf(QChar(9))) != -1)
0886         text.replace(index, 1, QString(tabstop - ((startPos + index) % tabstop), QLatin1Char(' ')));
0887 }
0888 
0889 KompareListViewDiffItem* KompareListViewLineItem::diffItemParent() const
0890 {
0891     KompareListViewLineContainerItem* p = (KompareListViewLineContainerItem*)parent();
0892     return p->diffItemParent();
0893 }
0894 
0895 KompareListViewBlankLineItem::KompareListViewBlankLineItem(KompareListViewLineContainerItem* parent)
0896     : KompareListViewLineItem(parent, 0, new DifferenceString(), Blank)
0897 {
0898     setHeight(BLANK_LINE_HEIGHT);
0899 }
0900 
0901 void KompareListViewBlankLineItem::paintText(QPainter* p, const QColor& bg, int column, int width, int /* align */)
0902 {
0903     if (column == COL_MAIN)
0904     {
0905         QBrush normalBrush(bg, Qt::SolidPattern);
0906         p->fillRect(0, 0, width, paintHeight(), normalBrush);
0907     }
0908 }
0909 
0910 KompareListViewHunkItem::KompareListViewHunkItem(KompareListView* parent, DiffHunk* hunk, bool zeroHeight)
0911     : KompareListViewItem(parent, Hunk),
0912       m_zeroHeight(zeroHeight),
0913       m_hunk(hunk)
0914 {
0915     setHeight(maxHeight());
0916     setFlags(flags() & ~Qt::ItemIsSelectable);
0917 }
0918 
0919 KompareListViewHunkItem::KompareListViewHunkItem(KompareListView* parent, KompareListViewItem* after, DiffHunk* hunk,  bool zeroHeight)
0920     : KompareListViewItem(parent, after, Hunk),
0921       m_zeroHeight(zeroHeight),
0922       m_hunk(hunk)
0923 {
0924     setHeight(maxHeight());
0925     setFlags(flags() & ~Qt::ItemIsSelectable);
0926 }
0927 
0928 KompareListViewHunkItem::~KompareListViewHunkItem()
0929 {
0930     m_hunk = nullptr;
0931 }
0932 
0933 int KompareListViewHunkItem::maxHeight()
0934 {
0935     if (m_zeroHeight) {
0936         return 0;
0937     } else if (m_hunk->function().isEmpty()) {
0938         return HUNK_LINE_HEIGHT;
0939     } else {
0940         return treeWidget()->fontMetrics().height();
0941     }
0942 }
0943 
0944 void KompareListViewHunkItem::paintCell(QPainter* p, const QStyleOptionViewItem& option, int column)
0945 {
0946     if (m_zeroHeight) {
0947         KompareListViewItem::paintCell(p, option, column);
0948     } else {
0949         int x = option.rect.left();
0950         int y = option.rect.top() - paintOffset();
0951         int width = option.rect.width();
0952         Qt::Alignment align = option.displayAlignment;
0953 
0954         p->fillRect(x, y, width, paintHeight(), QColor(Qt::lightGray));     // Hunk headers should be lightgray
0955         p->setPen(QColor(Qt::black));     // Text color in hunk should be black
0956         if (column == COL_MAIN) {
0957             p->drawText(x + ITEM_MARGIN, y, width - ITEM_MARGIN, paintHeight(),
0958                         align, m_hunk->function());
0959         }
0960     }
0961 }
0962 
0963 #include "moc_komparelistview.cpp"