File indexing completed on 2024-04-14 05:35:49

0001 // clang-format off
0002 /*
0003  *  This file is part of KDiff3.
0004  *
0005  * SPDX-FileCopyrightText: 2002-2011 Joachim Eibl, joachim.eibl at gmx.de
0006  * SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com
0007  * SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 // clang-format on
0010 #include "Overview.h"
0011 
0012 #include "diff.h"
0013 #include "kdiff3.h"
0014 #include "MergeEditLine.h"
0015 #include "options.h"
0016 
0017 #include <algorithm> // for max
0018 
0019 #include <QColor>
0020 #include <QMouseEvent>
0021 #include <QPainter>
0022 #include <QPixmap>
0023 #include <QScrollBar>
0024 #include <QSize>
0025 
0026 Overview::Overview()
0027 //: QWidget( pParent, 0, Qt::WNoAutoErase )
0028 {
0029     m_pDiff3LineList = nullptr;
0030     mOverviewMode = e_OverviewMode::eOMNormal;
0031     m_nofLines = 1;
0032     setUpdatesEnabled(false);
0033     m_firstLine = 0;
0034     m_pageHeight = 0;
0035 
0036     setFixedWidth(20);
0037 }
0038 
0039 void Overview::init(Diff3LineList* pDiff3LineList)
0040 {
0041     m_pDiff3LineList = pDiff3LineList;
0042     m_pixmap = QPixmap(QSize(0, 0)); // make sure that a redraw happens
0043     update();
0044 }
0045 
0046 void Overview::reset()
0047 {
0048     m_pDiff3LineList = nullptr;
0049 }
0050 
0051 void Overview::slotRedraw()
0052 {
0053     m_pixmap = QPixmap(QSize(0, 0)); // make sure that a redraw happens
0054     update();
0055 }
0056 
0057 void Overview::setRange(LineRef firstLine, qint32 pageHeight)
0058 {
0059     assert(firstLine.isValid());
0060     m_firstLine = firstLine;
0061     m_pageHeight = pageHeight;
0062     update();
0063 }
0064 
0065 void Overview::setFirstLine(LineRef firstLine)
0066 {
0067     QScrollBar* scrollBar = qobject_cast<QScrollBar*>(sender());
0068     assert(firstLine.isValid());
0069 
0070     if(Q_UNLIKELY(scrollBar == nullptr))
0071     {
0072         m_firstLine = firstLine;
0073         update();
0074     }
0075     else
0076         setRange(firstLine, scrollBar->pageStep());
0077 }
0078 
0079 void Overview::setOverviewMode(e_OverviewMode eOverviewMode)
0080 {
0081     mOverviewMode = eOverviewMode;
0082     slotRedraw();
0083 }
0084 
0085 e_OverviewMode Overview::getOverviewMode()
0086 {
0087     return mOverviewMode;
0088 }
0089 
0090 void Overview::mousePressEvent(QMouseEvent* e)
0091 {
0092     qint32 h = height() - 1;
0093     qint32 h1 = h * m_pageHeight / std::max(1, m_nofLines) + 3;
0094     if(h > 0)
0095         Q_EMIT setLine((e->pos().y() - h1 / 2) * m_nofLines / h);
0096 }
0097 
0098 void Overview::mouseMoveEvent(QMouseEvent* e)
0099 {
0100     mousePressEvent(e);
0101 }
0102 
0103 void Overview::setPaintingAllowed(bool bAllowPainting)
0104 {
0105     if(updatesEnabled() != bAllowPainting)
0106     {
0107 
0108         setUpdatesEnabled(bAllowPainting);
0109         if(bAllowPainting)
0110             update();
0111         else
0112             reset();
0113     }
0114 }
0115 
0116 void Overview::drawColumn(QPainter& p, e_OverviewMode eOverviewMode, qint32 x, qint32 w, qint32 h, qint32 nofLines)
0117 {
0118     p.setPen(Qt::black);
0119     p.drawLine(x, 0, x, h);
0120 
0121     if(nofLines == 0) return;
0122 
0123     qint32 line = 0;
0124     qint32 oldY = 0;
0125     qint32 oldConflictY = -1;
0126     qint32 wrapLineIdx = 0;
0127     Diff3LineList::const_iterator i;
0128 
0129     for(i = m_pDiff3LineList->begin(); i != m_pDiff3LineList->end();)
0130     {
0131         const Diff3Line& d3l = *i;
0132         qint32 y = h * (line + 1) / nofLines;
0133         MergeBlock lMergeBlock;
0134         e_MergeDetails md;
0135         bool bConflict;
0136         bool bLineRemoved;
0137 
0138         lMergeBlock.mergeOneLine(d3l, bLineRemoved, !KDiff3App::isTripleDiff());
0139         md = lMergeBlock.details();
0140         bConflict = lMergeBlock.isConflict();
0141 
0142         QColor c = gOptions->backgroundColor();
0143         bool bWhiteSpaceChange = false;
0144         //if( bConflict )  c=gOptions->conflictColor();
0145         //else
0146         switch(eOverviewMode)
0147         {
0148             case e_OverviewMode::eOMNormal:
0149                 switch(md)
0150                 {
0151                     case e_MergeDetails::eDefault:
0152                     case e_MergeDetails::eNoChange:
0153                         c = gOptions->backgroundColor();
0154                         break;
0155 
0156                     case e_MergeDetails::eBAdded:
0157                     case e_MergeDetails::eBDeleted:
0158                     case e_MergeDetails::eBChanged:
0159                         c = bConflict ? gOptions->conflictColor() : gOptions->bColor();
0160                         bWhiteSpaceChange = d3l.isEqualAB() || (d3l.isWhiteLine(e_SrcSelector::A) && d3l.isWhiteLine(e_SrcSelector::B));
0161                         break;
0162 
0163                     case e_MergeDetails::eCAdded:
0164                     case e_MergeDetails::eCDeleted:
0165                     case e_MergeDetails::eCChanged:
0166                         bWhiteSpaceChange = d3l.isEqualAC() || (d3l.isWhiteLine(e_SrcSelector::A) && d3l.isWhiteLine(e_SrcSelector::C));
0167                         c = bConflict ? gOptions->conflictColor() : gOptions->cColor();
0168                         break;
0169 
0170                     case e_MergeDetails::eBCChanged:         // conflict
0171                     case e_MergeDetails::eBCChangedAndEqual: // possible conflict
0172                     case e_MergeDetails::eBCDeleted:         // possible conflict
0173                     case e_MergeDetails::eBChanged_CDeleted: // conflict
0174                     case e_MergeDetails::eCChanged_BDeleted: // conflict
0175                     case e_MergeDetails::eBCAdded:           // conflict
0176                     case e_MergeDetails::eBCAddedAndEqual:   // possible conflict
0177                         c = gOptions->conflictColor();
0178                         break;
0179                 }
0180                 break;
0181             case e_OverviewMode::eOMAvsB:
0182                 switch(md)
0183                 {
0184                     case e_MergeDetails::eDefault:
0185                     case e_MergeDetails::eNoChange:
0186                     case e_MergeDetails::eCAdded:
0187                     case e_MergeDetails::eCDeleted:
0188                     case e_MergeDetails::eCChanged:
0189                         break;
0190                     default:
0191                         c = gOptions->conflictColor();
0192                         bWhiteSpaceChange = d3l.isEqualAB() || (d3l.isWhiteLine(e_SrcSelector::A) && d3l.isWhiteLine(e_SrcSelector::B));
0193                         break;
0194                 }
0195                 break;
0196             case e_OverviewMode::eOMAvsC:
0197                 switch(md)
0198                 {
0199                     case e_MergeDetails::eDefault:
0200                     case e_MergeDetails::eNoChange:
0201                     case e_MergeDetails::eBAdded:
0202                     case e_MergeDetails::eBDeleted:
0203                     case e_MergeDetails::eBChanged:
0204                         break;
0205                     default:
0206                         c = gOptions->conflictColor();
0207                         bWhiteSpaceChange = d3l.isEqualAC() || (d3l.isWhiteLine(e_SrcSelector::A) && d3l.isWhiteLine(e_SrcSelector::C));
0208                         break;
0209                 }
0210                 break;
0211             case e_OverviewMode::eOMBvsC:
0212                 switch(md)
0213                 {
0214                     case e_MergeDetails::eDefault:
0215                     case e_MergeDetails::eNoChange:
0216                     case e_MergeDetails::eBCChangedAndEqual:
0217                     case e_MergeDetails::eBCDeleted:
0218                     case e_MergeDetails::eBCAddedAndEqual:
0219                         break;
0220                     default:
0221                         c = gOptions->conflictColor();
0222                         bWhiteSpaceChange = d3l.isEqualBC() || (d3l.isWhiteLine(e_SrcSelector::B) && d3l.isWhiteLine(e_SrcSelector::C));
0223                         break;
0224                 }
0225                 break;
0226         }
0227 
0228         qint32 x2 = x;
0229         qint32 w2 = w;
0230 
0231         if(!KDiff3App::isTripleDiff())
0232         {
0233             if(!d3l.getLineA().isValid() && d3l.getLineB().isValid())
0234             {
0235                 c = gOptions->aColor();
0236                 x2 = w / 2;
0237                 w2 = x2;
0238             }
0239             if(d3l.getLineA().isValid() && !d3l.getLineB().isValid())
0240             {
0241                 c = gOptions->bColor();
0242                 w2 = w / 2;
0243             }
0244         }
0245 
0246         if(!bWhiteSpaceChange || gOptions->m_bShowWhiteSpace)
0247         {
0248             // Make sure that lines with conflict are not overwritten.
0249             if(c == gOptions->conflictColor())
0250             {
0251                 p.fillRect(x2 + 1, oldY, w2, std::max(1, y - oldY), bWhiteSpaceChange ? QBrush(c, Qt::Dense4Pattern) : QBrush(c));
0252                 oldConflictY = oldY;
0253             }
0254             else if(c != gOptions->backgroundColor() && oldY > oldConflictY)
0255             {
0256                 p.fillRect(x2 + 1, oldY, w2, std::max(1, y - oldY), bWhiteSpaceChange ? QBrush(c, Qt::Dense4Pattern) : QBrush(c));
0257             }
0258         }
0259 
0260         oldY = y;
0261 
0262         ++line;
0263         if(gOptions->wordWrapOn())
0264         {
0265             ++wrapLineIdx;
0266             if(wrapLineIdx >= d3l.linesNeededForDisplay())
0267             {
0268                 wrapLineIdx = 0;
0269                 ++i;
0270             }
0271         }
0272         else
0273         {
0274             ++i;
0275         }
0276     }
0277 }
0278 
0279 void Overview::paintEvent(QPaintEvent*)
0280 {
0281     if(m_pDiff3LineList == nullptr) return;
0282     qint32 h = height() - 1;
0283     qint32 w = width();
0284 
0285     const auto dpr = devicePixelRatioF();
0286     if(m_pixmap.size() != size() * dpr)
0287     {
0288         m_nofLines = m_pDiff3LineList->numberOfLines(gOptions->wordWrapOn());
0289 
0290         m_pixmap = QPixmap(size() * dpr);
0291         m_pixmap.setDevicePixelRatio(dpr);
0292 
0293         QPainter p(&m_pixmap);
0294         p.fillRect(rect(), gOptions->backgroundColor());
0295 
0296         if(!KDiff3App::isTripleDiff() || mOverviewMode == e_OverviewMode::eOMNormal)
0297         {
0298             drawColumn(p, e_OverviewMode::eOMNormal, 0, w, h, m_nofLines);
0299         }
0300         else
0301         {
0302             drawColumn(p, e_OverviewMode::eOMNormal, 0, w / 2, h, m_nofLines);
0303             drawColumn(p, mOverviewMode, w / 2, w / 2, h, m_nofLines);
0304         }
0305     }
0306 
0307     QPainter painter(this);
0308     painter.drawPixmap(0, 0, m_pixmap);
0309     qint32 y1 = 0, h1 = 0;
0310     if(m_nofLines > 0)
0311     {
0312         y1 = h * m_firstLine / m_nofLines - 1;
0313         h1 = h * m_pageHeight / m_nofLines + 3;
0314     }
0315     painter.setPen(Qt::black);
0316     painter.drawRect(1, y1, w - 1, h1);
0317 }