File indexing completed on 2024-05-05 05:45:47

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/ModelList>
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 KompareDiff2;
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     const DiffHunkList* hunks = model->hunks();
0366     DiffHunkListConstIterator hunkIt = hunks->begin();
0367     DiffHunkListConstIterator hEnd   = hunks->end();
0368 
0369     KompareListViewItem* item = nullptr;
0370     m_nextPaintOffset = 0;
0371 
0372     for (; hunkIt != hEnd; ++hunkIt)
0373     {
0374         if (item)
0375             item = new KompareListViewHunkItem(this, item, *hunkIt, model->isBlended());
0376         else
0377             item = new KompareListViewHunkItem(this, *hunkIt, model->isBlended());
0378 
0379         const DifferenceList differences = (*hunkIt)->differences();
0380         DifferenceListConstIterator diffIt = differences.begin();
0381         DifferenceListConstIterator dEnd   = differences.end();
0382 
0383         for (; diffIt != dEnd; ++diffIt)
0384         {
0385             item = new KompareListViewDiffItem(this, item, *diffIt);
0386 
0387             int type = (*diffIt)->type();
0388 
0389             if (type != Difference::Unchanged)
0390             {
0391                 m_items.append((KompareListViewDiffItem*)item);
0392                 m_itemDict.insert(*diffIt, (KompareListViewDiffItem*)item);
0393             }
0394         }
0395     }
0396 
0397     resizeColumnToContents(COL_LINE_NO);
0398     resizeColumnToContents(COL_MAIN);
0399 
0400     slotSetSelection(diff);
0401 }
0402 
0403 KompareListViewDiffItem* KompareListView::diffItemAt(const QPoint& pos)
0404 {
0405     KompareListViewItem* item = static_cast<KompareListViewItem*>(itemAt(pos));
0406     if (!item)
0407         return nullptr;
0408     switch (item->type()) {
0409         case KompareListViewItem::Hunk:
0410             if (item->paintHeight()) return nullptr;  // no diff item here
0411             // zero height (fake 1 pixel height), so a diff item shines through
0412             return static_cast<KompareListViewDiffItem*>(itemBelow(item));
0413         case KompareListViewItem::Line:
0414         case KompareListViewItem::Blank:
0415             return static_cast<KompareListViewLineItem*>(item)->diffItemParent();
0416         case KompareListViewItem::Container:
0417             return static_cast<KompareListViewLineContainerItem*>(item)->diffItemParent();
0418         case KompareListViewItem::Diff:
0419             return static_cast<KompareListViewDiffItem*>(item);
0420         default:
0421             return nullptr;
0422     }
0423 }
0424 
0425 void KompareListView::mousePressEvent(QMouseEvent* e)
0426 {
0427     QPoint vp = e->pos();
0428     KompareListViewDiffItem* diffItem = diffItemAt(vp);
0429     if (diffItem && diffItem->difference()->type() != Difference::Unchanged) {
0430         Q_EMIT differenceClicked(diffItem->difference());
0431     }
0432 }
0433 
0434 void KompareListView::mouseDoubleClickEvent(QMouseEvent* e)
0435 {
0436     QPoint vp = e->pos();
0437     KompareListViewDiffItem* diffItem = diffItemAt(vp);
0438     if (diffItem && diffItem->difference()->type() != Difference::Unchanged) {
0439         // FIXME: make a new signal that does both
0440         Q_EMIT differenceClicked(diffItem->difference());
0441         Q_EMIT applyDifference(!diffItem->difference()->applied());
0442     }
0443 }
0444 
0445 void KompareListView::renumberLines()
0446 {
0447 //     qCDebug(KOMPAREPART) << "Begin" ;
0448     unsigned int newLineNo = 1;
0449     if (!topLevelItemCount()) return;
0450     KompareListViewItem* item = (KompareListViewItem*)topLevelItem(0);
0451     while (item) {
0452 //         qCDebug(KOMPAREPART) << "type: " << item->type() ;
0453         if (item->type() != KompareListViewItem::Container
0454                 && item->type() != KompareListViewItem::Blank
0455                 && item->type() != KompareListViewItem::Hunk)
0456         {
0457 //             qCDebug(KOMPAREPART) << QString::number( newLineNo ) ;
0458             item->setText(COL_LINE_NO, QString::number(newLineNo++));
0459         }
0460         item = (KompareListViewItem*)itemBelow(item);
0461     }
0462 }
0463 
0464 void KompareListView::slotApplyDifference(bool apply)
0465 {
0466     m_itemDict[ m_selectedDifference ]->applyDifference(apply);
0467     // now renumber the line column if this is the destination
0468     if (!m_isSource)
0469         renumberLines();
0470 }
0471 
0472 void KompareListView::slotApplyAllDifferences(bool apply)
0473 {
0474     QHash<const KompareDiff2::Difference*, KompareListViewDiffItem*>::ConstIterator it = m_itemDict.constBegin();
0475     QHash<const KompareDiff2::Difference*, KompareListViewDiffItem*>::ConstIterator end = m_itemDict.constEnd();
0476     for (; it != end; ++it)
0477         it.value()->applyDifference(apply);
0478 
0479     // now renumber the line column if this is the destination
0480     if (!m_isSource)
0481         renumberLines();
0482     update();
0483 }
0484 
0485 void KompareListView::slotApplyDifference(const Difference* diff, bool apply)
0486 {
0487     m_itemDict[ diff ]->applyDifference(apply);
0488     // now renumber the line column if this is the destination
0489     if (!m_isSource)
0490         renumberLines();
0491 }
0492 
0493 void KompareListView::wheelEvent(QWheelEvent* e)
0494 {
0495     e->ignore(); // we want the parent to catch wheel events
0496 }
0497 
0498 void KompareListView::resizeEvent(QResizeEvent* e)
0499 {
0500     QTreeWidget::resizeEvent(e);
0501     Q_EMIT resized();
0502 }
0503 
0504 KompareListViewItemDelegate::KompareListViewItemDelegate(QObject* parent)
0505     : QStyledItemDelegate(parent)
0506 {
0507 }
0508 
0509 KompareListViewItemDelegate::~KompareListViewItemDelegate()
0510 {
0511 }
0512 
0513 void KompareListViewItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
0514 {
0515     int column = index.column();
0516     QStyleOptionViewItem changedOption = option;
0517     if (column == COL_LINE_NO)
0518         changedOption.displayAlignment = Qt::AlignRight;
0519     KompareListViewItem* item = static_cast<KompareListViewItem*>(static_cast<KompareListView*>(parent())->itemFromIndex(index));
0520     item->paintCell(painter, changedOption, column);
0521 }
0522 
0523 QSize KompareListViewItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
0524 {
0525     KompareListViewItem* item = static_cast<KompareListViewItem*>(static_cast<KompareListView*>(parent())->itemFromIndex(index));
0526     QSize hint = QStyledItemDelegate::sizeHint(option, index);
0527     return QSize(hint.width() + ITEM_MARGIN, item->height());
0528 }
0529 
0530 KompareListViewItem::KompareListViewItem(KompareListView* parent, int type)
0531     : QTreeWidgetItem(parent, type),
0532       m_scrollId(0),
0533       m_height(0),
0534       m_paintHeight(0),
0535       m_paintOffset(parent->nextPaintOffset())
0536 {
0537 //     qCDebug(KOMPAREPART) << "Created KompareListViewItem with scroll id " << m_scrollId ;
0538 }
0539 
0540 KompareListViewItem::KompareListViewItem(KompareListView* parent, KompareListViewItem* after, int type)
0541     : QTreeWidgetItem(parent, after, type),
0542       m_scrollId(after->scrollId() + after->maxHeight()),
0543       m_height(0),
0544       m_paintHeight(0),
0545       m_paintOffset(parent->nextPaintOffset())
0546 {
0547 //     qCDebug(KOMPAREPART) << "Created KompareListViewItem with scroll id " << m_scrollId ;
0548 }
0549 
0550 KompareListViewItem::KompareListViewItem(KompareListViewItem* parent, int type)
0551     : QTreeWidgetItem(parent, type),
0552       m_scrollId(0),
0553       m_height(0),
0554       m_paintHeight(0),
0555       m_paintOffset(parent->kompareListView()->nextPaintOffset())
0556 {
0557 }
0558 
0559 KompareListViewItem::KompareListViewItem(KompareListViewItem* parent, KompareListViewItem* /*after*/, int type)
0560     : QTreeWidgetItem(parent, type),
0561       m_scrollId(0),
0562       m_height(0),
0563       m_paintHeight(0),
0564       m_paintOffset(parent->kompareListView()->nextPaintOffset())
0565 {
0566 }
0567 
0568 int KompareListViewItem::height() const
0569 {
0570     return m_height;
0571 }
0572 
0573 void KompareListViewItem::setHeight(int h)
0574 {
0575     m_height = m_paintHeight = h;
0576     // QTreeWidget doesn't like zero height, fudge around it.
0577     m_height -= m_paintOffset;
0578     if (m_height <= 0) {
0579         kompareListView()->setNextPaintOffset(1 - m_height);
0580         m_height = 1;
0581     } else kompareListView()->setNextPaintOffset(0);
0582 }
0583 
0584 int KompareListViewItem::paintHeight() const
0585 {
0586     return m_paintHeight;
0587 }
0588 
0589 int KompareListViewItem::paintOffset() const
0590 {
0591     return m_paintOffset;
0592 }
0593 
0594 bool KompareListViewItem::isCurrent() const
0595 {
0596     return treeWidget()->currentItem() == this;
0597 }
0598 
0599 KompareListView* KompareListViewItem::kompareListView() const
0600 {
0601     return (KompareListView*)treeWidget();
0602 }
0603 
0604 void KompareListViewItem::paintCell(QPainter* p, const QStyleOptionViewItem& option, int column)
0605 {
0606     // Default implementation for zero-height items.
0607     // We have to paint the item which shines through or we'll end up with glitches.
0608     KompareListViewItem* nextItem = (KompareListViewItem*)kompareListView()->itemBelow(this);
0609     if (nextItem) {
0610         QStyleOptionViewItem changedOption = option;
0611         changedOption.rect.translate(0, height());
0612         nextItem->paintCell(p, changedOption, column);
0613     }
0614 }
0615 
0616 KompareListViewDiffItem::KompareListViewDiffItem(KompareListView* parent, Difference* difference)
0617     : KompareListViewItem(parent, Diff),
0618       m_difference(difference),
0619       m_sourceItem(nullptr),
0620       m_destItem(nullptr)
0621 {
0622     init();
0623 }
0624 
0625 KompareListViewDiffItem::KompareListViewDiffItem(KompareListView* parent, KompareListViewItem* after, Difference* difference)
0626     : KompareListViewItem(parent, after, Diff),
0627       m_difference(difference),
0628       m_sourceItem(nullptr),
0629       m_destItem(nullptr)
0630 {
0631     init();
0632 }
0633 
0634 KompareListViewDiffItem::~KompareListViewDiffItem()
0635 {
0636     m_difference = nullptr;
0637 }
0638 
0639 void KompareListViewDiffItem::init()
0640 {
0641     setHeight(0);
0642     setExpanded(true);
0643     int nextPaintOffset = kompareListView()->nextPaintOffset();
0644     m_destItem = new KompareListViewLineContainerItem(this, false);
0645     kompareListView()->setNextPaintOffset(nextPaintOffset);
0646     m_sourceItem = new KompareListViewLineContainerItem(this, true);
0647     setVisibility();
0648 }
0649 
0650 void KompareListViewDiffItem::setVisibility()
0651 {
0652     m_sourceItem->setHidden(!(kompareListView()->isSource() || m_difference->applied()));
0653     m_destItem->setHidden(!m_sourceItem->isHidden());
0654 }
0655 
0656 void KompareListViewDiffItem::applyDifference(bool apply)
0657 {
0658     qCDebug(KOMPAREPART) << "KompareListViewDiffItem::applyDifference( " << apply << " )" ;
0659     setVisibility();
0660 }
0661 
0662 int KompareListViewDiffItem::maxHeight()
0663 {
0664     int lines = qMax(m_difference->sourceLineCount(), m_difference->destinationLineCount());
0665     if (lines == 0)
0666         return BLANK_LINE_HEIGHT;
0667     else
0668         return lines * treeWidget()->fontMetrics().height();
0669 }
0670 
0671 KompareListViewLineContainerItem::KompareListViewLineContainerItem(KompareListViewDiffItem* parent, bool isSource)
0672     : KompareListViewItem(parent, Container),
0673       m_blankLineItem(nullptr),
0674       m_isSource(isSource)
0675 {
0676 //     qCDebug(KOMPAREPART) << "isSource ? " << (isSource ? " Yes!" : " No!") ;
0677     setHeight(0);
0678     setExpanded(true);
0679 
0680     int lines = lineCount();
0681     int line = lineNumber();
0682 //     qCDebug(KOMPAREPART) << "LineNumber : " << lineNumber() ;
0683     if (lines == 0) {
0684         m_blankLineItem = new KompareListViewBlankLineItem(this);
0685         return;
0686     }
0687 
0688     for (int i = 0; i < lines; ++i, ++line) {
0689         new KompareListViewLineItem(this, line, lineAt(i));
0690     }
0691 }
0692 
0693 KompareListViewLineContainerItem::~KompareListViewLineContainerItem()
0694 {
0695 }
0696 
0697 KompareListViewDiffItem* KompareListViewLineContainerItem::diffItemParent() const
0698 {
0699     return (KompareListViewDiffItem*)parent();
0700 }
0701 
0702 int KompareListViewLineContainerItem::lineCount() const
0703 {
0704     return m_isSource ? diffItemParent()->difference()->sourceLineCount() :
0705            diffItemParent()->difference()->destinationLineCount();
0706 }
0707 
0708 int KompareListViewLineContainerItem::lineNumber() const
0709 {
0710     return m_isSource ? diffItemParent()->difference()->sourceLineNumber() :
0711            diffItemParent()->difference()->destinationLineNumber();
0712 }
0713 
0714 DifferenceString* KompareListViewLineContainerItem::lineAt(int i) const
0715 {
0716     return m_isSource ? diffItemParent()->difference()->sourceLineAt(i) :
0717            diffItemParent()->difference()->destinationLineAt(i);
0718 }
0719 
0720 KompareListViewLineItem::KompareListViewLineItem(KompareListViewLineContainerItem* parent, int line, DifferenceString* text)
0721     : KompareListViewItem(parent, Line)
0722 {
0723     init(line, text);
0724 }
0725 
0726 KompareListViewLineItem::KompareListViewLineItem(KompareListViewLineContainerItem* parent, int line, DifferenceString* text, int type)
0727     : KompareListViewItem(parent, type)
0728 {
0729     init(line, text);
0730 }
0731 
0732 KompareListViewLineItem::~KompareListViewLineItem()
0733 {
0734     m_text = nullptr;
0735 }
0736 
0737 void KompareListViewLineItem::init(int line, DifferenceString* text)
0738 {
0739     setHeight(treeWidget()->fontMetrics().height());
0740     setText(COL_LINE_NO, QString::number(line));
0741     setText(COL_MAIN, text->string());
0742     m_text = text;
0743 }
0744 
0745 void KompareListViewLineItem::paintCell(QPainter* p, const QStyleOptionViewItem& option, int column)
0746 {
0747     int width = option.rect.width();
0748     Qt::Alignment align = option.displayAlignment;
0749 
0750     p->setRenderHint(QPainter::Antialiasing);
0751     p->translate(option.rect.topLeft());
0752     p->translate(0, -paintOffset());
0753 
0754     QColor bg(Qt::white);   // Always make the background white when it is not a real difference
0755     if (diffItemParent()->difference()->type() == Difference::Unchanged)
0756     {
0757         if (column == COL_LINE_NO)
0758         {
0759             bg = QColor(Qt::lightGray);
0760         }
0761     }
0762     else
0763     {
0764         bg = kompareListView()->settings()->colorForDifferenceType(
0765                  diffItemParent()->difference()->type(),
0766                  diffItemParent()->isCurrent(),
0767                  diffItemParent()->difference()->applied());
0768     }
0769 
0770     // Paint background
0771     p->fillRect(0, 0, width, paintHeight(), bg);
0772 
0773     // Paint foreground
0774     if (diffItemParent()->difference()->type() == Difference::Unchanged)
0775         p->setPen(QColor(Qt::darkGray));     // always make normal text gray
0776     else
0777         p->setPen(QColor(Qt::black));     // make text with changes black
0778 
0779     paintText(p, bg, column, width, align);
0780 
0781     // Paint darker lines around selected item
0782     if (diffItemParent()->isCurrent())
0783     {
0784         p->translate(0.5, 0.5);
0785         p->setPen(bg.darker(135));
0786         QTreeWidgetItem* parentItem = parent();
0787         if (this == parentItem->child(0))
0788             p->drawLine(0, 0, width, 0);
0789         if (this == parentItem->child(parentItem->childCount() - 1))
0790             p->drawLine(0, paintHeight() - 1, width, paintHeight() - 1);
0791     }
0792 
0793     p->resetTransform();
0794 }
0795 
0796 void KompareListViewLineItem::paintText(QPainter* p, const QColor& bg, int column, int width, int align)
0797 {
0798     if (column == COL_MAIN)
0799     {
0800         QString textChunk;
0801         int offset = ITEM_MARGIN;
0802         int prevValue = 0;
0803         int charsDrawn = 0;
0804         int chunkWidth;
0805         QBrush changeBrush(bg, Qt::Dense3Pattern);
0806         QBrush normalBrush(bg, Qt::SolidPattern);
0807         QBrush brush;
0808 
0809         if (m_text->string().isEmpty())
0810         {
0811             p->fillRect(0, 0, width, paintHeight(), normalBrush);
0812             return;
0813         }
0814 
0815         p->fillRect(0, 0, offset, paintHeight(), normalBrush);
0816 
0817         const MarkerList markerList = m_text->markerList();
0818         if (!markerList.isEmpty())
0819         {
0820             MarkerListConstIterator markerIt = markerList.begin();
0821             MarkerListConstIterator mEnd     = markerList.end();
0822             Marker* m = *markerIt;
0823 
0824             for (; markerIt != mEnd; ++markerIt)
0825             {
0826                 m  = *markerIt;
0827                 textChunk = m_text->string().mid(prevValue, m->offset() - prevValue);
0828 //                 qCDebug(KOMPAREPART) << "TextChunk   = \"" << textChunk << "\"" ;
0829 //                 qCDebug(KOMPAREPART) << "c->offset() = " << c->offset() ;
0830 //                 qCDebug(KOMPAREPART) << "prevValue   = " << prevValue ;
0831                 expandTabs(textChunk, kompareListView()->settings()->m_tabToNumberOfSpaces, charsDrawn);
0832                 charsDrawn += textChunk.length();
0833                 prevValue = m->offset();
0834                 if (m->type() == Marker::End)
0835                 {
0836                     QFont font(p->font());
0837                     font.setBold(true);
0838                     p->setFont(font);
0839 //                     p->setPen( Qt::blue );
0840                     brush = changeBrush;
0841                 }
0842                 else
0843                 {
0844                     QFont font(p->font());
0845                     font.setBold(false);
0846                     p->setFont(font);
0847 //                     p->setPen( Qt::black );
0848                     brush = normalBrush;
0849                 }
0850                 chunkWidth = p->fontMetrics().horizontalAdvance(textChunk);
0851                 p->fillRect(offset, 0, chunkWidth, paintHeight(), brush);
0852                 p->drawText(offset, 0,
0853                             chunkWidth, paintHeight(),
0854                             align, textChunk);
0855                 offset += chunkWidth;
0856             }
0857         }
0858         if (prevValue < m_text->string().length())
0859         {
0860             // Still have to draw some string without changes
0861             textChunk = m_text->string().mid(prevValue, qMax(1, m_text->string().length() - prevValue));
0862             expandTabs(textChunk, kompareListView()->settings()->m_tabToNumberOfSpaces, charsDrawn);
0863 //             qCDebug(KOMPAREPART) << "TextChunk   = \"" << textChunk << "\"" ;
0864             QFont font(p->font());
0865             font.setBold(false);
0866             p->setFont(font);
0867             chunkWidth = p->fontMetrics().horizontalAdvance(textChunk);
0868             p->fillRect(offset, 0, chunkWidth, paintHeight(), normalBrush);
0869             p->drawText(offset, 0,
0870                         chunkWidth, paintHeight(),
0871                         align, textChunk);
0872             offset += chunkWidth;
0873         }
0874         p->fillRect(offset, 0, width - offset, paintHeight(), normalBrush);
0875     }
0876     else
0877     {
0878         p->fillRect(0, 0, width, paintHeight(), bg);
0879         p->drawText(ITEM_MARGIN, 0,
0880                     width - ITEM_MARGIN, paintHeight(),
0881                     align, text(column));
0882     }
0883 }
0884 
0885 void KompareListViewLineItem::expandTabs(QString& text, int tabstop, int startPos) const
0886 {
0887     int index;
0888     while ((index = text.indexOf(QChar(9))) != -1)
0889         text.replace(index, 1, QString(tabstop - ((startPos + index) % tabstop), QLatin1Char(' ')));
0890 }
0891 
0892 KompareListViewDiffItem* KompareListViewLineItem::diffItemParent() const
0893 {
0894     KompareListViewLineContainerItem* p = (KompareListViewLineContainerItem*)parent();
0895     return p->diffItemParent();
0896 }
0897 
0898 KompareListViewBlankLineItem::KompareListViewBlankLineItem(KompareListViewLineContainerItem* parent)
0899     : KompareListViewLineItem(parent, 0, new DifferenceString(), Blank)
0900 {
0901     setHeight(BLANK_LINE_HEIGHT);
0902 }
0903 
0904 void KompareListViewBlankLineItem::paintText(QPainter* p, const QColor& bg, int column, int width, int /* align */)
0905 {
0906     if (column == COL_MAIN)
0907     {
0908         QBrush normalBrush(bg, Qt::SolidPattern);
0909         p->fillRect(0, 0, width, paintHeight(), normalBrush);
0910     }
0911 }
0912 
0913 KompareListViewHunkItem::KompareListViewHunkItem(KompareListView* parent, DiffHunk* hunk, bool zeroHeight)
0914     : KompareListViewItem(parent, Hunk),
0915       m_zeroHeight(zeroHeight),
0916       m_hunk(hunk)
0917 {
0918     setHeight(maxHeight());
0919     setFlags(flags() & ~Qt::ItemIsSelectable);
0920 }
0921 
0922 KompareListViewHunkItem::KompareListViewHunkItem(KompareListView* parent, KompareListViewItem* after, DiffHunk* hunk,  bool zeroHeight)
0923     : KompareListViewItem(parent, after, Hunk),
0924       m_zeroHeight(zeroHeight),
0925       m_hunk(hunk)
0926 {
0927     setHeight(maxHeight());
0928     setFlags(flags() & ~Qt::ItemIsSelectable);
0929 }
0930 
0931 KompareListViewHunkItem::~KompareListViewHunkItem()
0932 {
0933     m_hunk = nullptr;
0934 }
0935 
0936 int KompareListViewHunkItem::maxHeight()
0937 {
0938     if (m_zeroHeight) {
0939         return 0;
0940     } else if (m_hunk->function().isEmpty()) {
0941         return HUNK_LINE_HEIGHT;
0942     } else {
0943         return treeWidget()->fontMetrics().height();
0944     }
0945 }
0946 
0947 void KompareListViewHunkItem::paintCell(QPainter* p, const QStyleOptionViewItem& option, int column)
0948 {
0949     if (m_zeroHeight) {
0950         KompareListViewItem::paintCell(p, option, column);
0951     } else {
0952         int x = option.rect.left();
0953         int y = option.rect.top() - paintOffset();
0954         int width = option.rect.width();
0955         Qt::Alignment align = option.displayAlignment;
0956 
0957         p->fillRect(x, y, width, paintHeight(), QColor(Qt::lightGray));     // Hunk headers should be lightgray
0958         p->setPen(QColor(Qt::black));     // Text color in hunk should be black
0959         if (column == COL_MAIN) {
0960             p->drawText(x + ITEM_MARGIN, y, width - ITEM_MARGIN, paintHeight(),
0961                         align, m_hunk->function());
0962         }
0963     }
0964 }
0965 
0966 #include "moc_komparelistview.cpp"