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 }