difftextwindow.cpp 65.9 KB
Newer Older
Joachim Eibl's avatar
Joachim Eibl committed
1
/***************************************************************************
Michael Reeves's avatar
Michael Reeves committed
2 3 4
 *   Copyright (C) 2003-2007 by Joachim Eibl                               *
 *   joachim.eibl at gmx.de                                                *
 *   Copyright (C) 2018 Michael Reeves reeves.87@gmail.com                 *
Joachim Eibl's avatar
Joachim Eibl committed
5 6 7 8 9 10 11 12
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

Joachim Eibl's avatar
Joachim Eibl committed
13
#include "difftextwindow.h"
Michael Reeves's avatar
Michael Reeves committed
14 15

#include "selection.h"
Michael Reeves's avatar
Michael Reeves committed
16
#include "kdiff3.h"
Joachim Eibl's avatar
Joachim Eibl committed
17
#include "merger.h"
18
#include "options.h"
Joachim Eibl's avatar
Joachim Eibl committed
19

Pino Toscano's avatar
Pino Toscano committed
20
#include <algorithm>
Michael Reeves's avatar
Michael Reeves committed
21 22 23
#include <cmath>
#include <cstdlib>

24
#include <QtMath>
25 26 27
#include <QDir>
#include <QDragEnterEvent>
#include <QFileDialog>
Joachim Eibl's avatar
Joachim Eibl committed
28 29
#include <QLabel>
#include <QLayout>
30
#include <QLineEdit>
31
#include <QMenu>
32 33
#include <QMimeData>
#include <QPushButton>
Michael Reeves's avatar
Michael Reeves committed
34
#include <QRunnable>
35 36 37
#include <QStatusBar>
#include <QTextCodec>
#include <QTextLayout>
Michael Reeves's avatar
Michael Reeves committed
38
#include <QThreadPool>
39 40
#include <QToolTip>
#include <QUrl>
Joachim Eibl's avatar
Joachim Eibl committed
41

42
#include <KLocalizedString>
Joachim Eibl's avatar
Joachim Eibl committed
43

Michael Reeves's avatar
Michael Reeves committed
44
QAtomicInt s_runnableCount = 0;
Joachim Eibl's avatar
Joachim Eibl committed
45

46 47
class DiffTextWindowData
{
48
  public:
Michael Reeves's avatar
Michael Reeves committed
49
    explicit DiffTextWindowData(DiffTextWindow* p)
50 51 52
    {
        m_pDiffTextWindow = p;
        m_bPaintingAllowed = false;
Michael Reeves's avatar
Michael Reeves committed
53
        m_pLineData = nullptr;
54 55 56
        m_size = 0;
        m_bWordWrap = false;
        m_delayedDrawTimer = 0;
Michael Reeves's avatar
Michael Reeves committed
57 58 59
        m_pDiff3LineVector = nullptr;
        m_pManualDiffHelpList = nullptr;
        m_pOptions = nullptr;
60 61
        m_fastSelectorLine1 = 0;
        m_fastSelectorNofLines = 0;
62
        m_bTriple = false;
63 64 65 66 67 68
        m_winIdx = 0;
        m_firstLine = 0;
        m_oldFirstLine = 0;
        m_horizScrollOffset = 0;
        m_lineNumberWidth = 0;
        m_maxTextWidth = -1;
Michael Reeves's avatar
Michael Reeves committed
69
        m_pStatusBar = nullptr;
70 71 72 73
        m_scrollDeltaX = 0;
        m_scrollDeltaY = 0;
        m_bMyUpdate = false;
        m_bSelectionInProgress = false;
Michael Reeves's avatar
Michael Reeves committed
74
        m_pTextCodec = nullptr;
75
#if defined(Q_OS_WIN)
76 77 78 79 80 81
        m_eLineEndStyle = eLineEndStyleDos;
#else
        m_eLineEndStyle = eLineEndStyleUnix;
#endif
    }
    DiffTextWindow* m_pDiffTextWindow;
Michael Reeves's avatar
Michael Reeves committed
82
    DiffTextWindowFrame* m_pDiffTextWindowFrame = nullptr;
83 84 85 86 87
    QTextCodec* m_pTextCodec;
    e_LineEndStyle m_eLineEndStyle;

    bool m_bPaintingAllowed;
    const LineData* m_pLineData;
Michael Reeves's avatar
Michael Reeves committed
88
    int m_size;
89 90 91 92 93 94 95 96 97 98 99
    QString m_filename;
    bool m_bWordWrap;
    int m_delayedDrawTimer;

    const Diff3LineVector* m_pDiff3LineVector;
    Diff3WrapLineVector m_diff3WrapLineVector;
    const ManualDiffHelpList* m_pManualDiffHelpList;

    class WrapLineCacheData
    {
      public:
100
        WrapLineCacheData()  {}
101 102
        WrapLineCacheData(int d3LineIdx, int textStart, int textLength)
            : m_d3LineIdx(d3LineIdx), m_textStart(textStart), m_textLength(textLength) {}
103 104 105
        int m_d3LineIdx = 0;
        int m_textStart = 0;
        int m_textLength = 0;
106 107 108 109 110 111 112 113 114
    };
    QList<QVector<WrapLineCacheData>> m_wrapLineCacheList;

    Options* m_pOptions;
    QColor m_cThis;
    QColor m_cDiff1;
    QColor m_cDiff2;
    QColor m_cDiffBoth;

Michael Reeves's avatar
Michael Reeves committed
115 116
    int m_fastSelectorLine1;
    int m_fastSelectorNofLines;
117 118 119 120 121 122 123 124 125 126 127

    bool m_bTriple;
    int m_winIdx;
    int m_firstLine;
    int m_oldFirstLine;
    int m_horizScrollOffset;
    int m_lineNumberWidth;
    QAtomicInt m_maxTextWidth;

    void getLineInfo(
        const Diff3Line& d,
Michael Reeves's avatar
Michael Reeves committed
128
        int& lineIdx,
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
        DiffList*& pFineDiff1, DiffList*& pFineDiff2, // return values
        int& changed, int& changed2);

    QString getString(int d3lIdx);
    QString getLineString(int line);

    void writeLine(
        MyPainter& p, const LineData* pld,
        const DiffList* pLineDiff1, const DiffList* pLineDiff2, int line,
        int whatChanged, int whatChanged2, int srcLineIdx,
        int wrapLineOffset, int wrapLineLength, bool bWrapLine, const QRect& invalidRect, int deviceWidth);

    void draw(MyPainter& p, const QRect& invalidRect, int deviceWidth, int beginLine, int endLine);

    QStatusBar* m_pStatusBar;

    Selection m_selection;

    int m_scrollDeltaX;
    int m_scrollDeltaY;

    bool m_bMyUpdate;
    void myUpdate(int afterMilliSecs);

    int leftInfoWidth() { return 4 + m_lineNumberWidth; } // Nr of information columns on left side
Michael Reeves's avatar
Michael Reeves committed
154
    int convertLineOnScreenToLineInSource(int lineOnScreen, e_CoordType coordType, bool bFirstLine);
155 156 157 158

    bool m_bSelectionInProgress;
    QPoint m_lastKnownMousePos;
    void prepareTextLayout(QTextLayout& textLayout, bool bFirstLine, int visibleTextWidth = -1);
Joachim Eibl's avatar
Joachim Eibl committed
159
};
Joachim Eibl's avatar
Joachim Eibl committed
160 161

DiffTextWindow::DiffTextWindow(
162 163 164 165 166
    DiffTextWindowFrame* pParent,
    QStatusBar* pStatusBar,
    Options* pOptions,
    int winIdx)
    : QWidget(pParent)
167
{
168 169 170
    setObjectName(QString("DiffTextWindow%1").arg(winIdx));
    setAttribute(Qt::WA_OpaquePaintEvent);
    //setAttribute( Qt::WA_PaintOnScreen );
Joachim Eibl's avatar
Joachim Eibl committed
171

172 173 174 175
    d = new DiffTextWindowData(this);
    d->m_pDiffTextWindowFrame = pParent;
    setFocusPolicy(Qt::ClickFocus);
    setAcceptDrops(true);
Joachim Eibl's avatar
Joachim Eibl committed
176

177
    d->m_pOptions = pOptions;
178
    init(QString(""), nullptr, d->m_eLineEndStyle, nullptr, 0, nullptr, nullptr, false);
Joachim Eibl's avatar
Joachim Eibl committed
179

180
    setMinimumSize(QSize(20, 20));
Joachim Eibl's avatar
Joachim Eibl committed
181

182 183 184 185
    d->m_pStatusBar = pStatusBar;
    d->m_bPaintingAllowed = true;
    d->m_bWordWrap = false;
    d->m_winIdx = winIdx;
186

187
    setFont(d->m_pOptions->m_font);
Joachim Eibl's avatar
Joachim Eibl committed
188 189
}

190 191
DiffTextWindow::~DiffTextWindow()
{
192
    delete d;
Joachim Eibl's avatar
Joachim Eibl committed
193
}
Joachim Eibl's avatar
Joachim Eibl committed
194 195

void DiffTextWindow::init(
196 197 198 199
    const QString& filename,
    QTextCodec* pTextCodec,
    e_LineEndStyle eLineEndStyle,
    const LineData* pLineData,
Michael Reeves's avatar
Michael Reeves committed
200
    int size,
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
    const Diff3LineVector* pDiff3LineVector,
    const ManualDiffHelpList* pManualDiffHelpList,
    bool bTriple)
{
    d->m_filename = filename;
    d->m_pLineData = pLineData;
    d->m_size = size;
    d->m_pDiff3LineVector = pDiff3LineVector;
    d->m_diff3WrapLineVector.clear();
    d->m_pManualDiffHelpList = pManualDiffHelpList;

    d->m_firstLine = 0;
    d->m_oldFirstLine = -1;
    d->m_horizScrollOffset = 0;
    d->m_bTriple = bTriple;
    d->m_scrollDeltaX = 0;
    d->m_scrollDeltaY = 0;
    d->m_bMyUpdate = false;
    d->m_fastSelectorLine1 = 0;
    d->m_fastSelectorNofLines = 0;
    d->m_lineNumberWidth = 0;
    d->m_maxTextWidth = -1;

    d->m_pTextCodec = pTextCodec;
    d->m_eLineEndStyle = eLineEndStyle;

    update();
    d->m_pDiffTextWindowFrame->init();
229 230 231 232
}

void DiffTextWindow::reset()
{
Michael Reeves's avatar
Michael Reeves committed
233
    d->m_pLineData = nullptr;
234
    d->m_size = 0;
Michael Reeves's avatar
Michael Reeves committed
235
    d->m_pDiff3LineVector = nullptr;
236 237
    d->m_filename = "";
    d->m_diff3WrapLineVector.clear();
238 239
}

240
void DiffTextWindow::setPaintingAllowed(bool bAllowPainting)
241
{
242 243 244 245 246 247 248 249
    if(d->m_bPaintingAllowed != bAllowPainting)
    {
        d->m_bPaintingAllowed = bAllowPainting;
        if(d->m_bPaintingAllowed)
            update();
        else
            reset();
    }
250 251
}

252
void DiffTextWindow::dragEnterEvent(QDragEnterEvent* e)
253
{
254 255
    e->setAccepted(e->mimeData()->hasUrls() || e->mimeData()->hasText());
    // Note that the corresponding drop is handled in KDiff3App::eventFilter().
256 257
}

Michael Reeves's avatar
Michael Reeves committed
258
void DiffTextWindow::setFirstLine(int firstLine)
259
{
260
    int fontHeight = fontMetrics().lineSpacing();
261

262
    int newFirstLine = std::max(0, firstLine);
263

Michael Reeves's avatar
Michael Reeves committed
264
    int deltaY = fontHeight * (d->m_firstLine - newFirstLine);
265

266
    d->m_firstLine = newFirstLine;
267

Michael Reeves's avatar
Michael Reeves committed
268
    if(d->m_bSelectionInProgress && d->m_selection.isValidFirstLine())
269
    {
Michael Reeves's avatar
Michael Reeves committed
270
        int line, pos;
271 272 273 274 275 276
        convertToLinePos(d->m_lastKnownMousePos.x(), d->m_lastKnownMousePos.y(), line, pos);
        d->m_selection.end(line, pos);
        update();
    }
    else
    {
Michael Reeves's avatar
Michael Reeves committed
277
        scroll(0, deltaY);
278 279
    }
    d->m_pDiffTextWindowFrame->setFirstLine(d->m_firstLine);
280 281 282 283
}

int DiffTextWindow::getFirstLine()
{
284
    return d->m_firstLine;
285 286 287 288
}

void DiffTextWindow::setHorizScrollOffset(int horizScrollOffset)
{
289 290
    int fontWidth = fontMetrics().width('0');
    int xOffset = d->leftInfoWidth() * fontWidth;
291

Pino Toscano's avatar
Pino Toscano committed
292
    int deltaX = d->m_horizScrollOffset - std::max(0, horizScrollOffset);
293

Pino Toscano's avatar
Pino Toscano committed
294
    d->m_horizScrollOffset = std::max(0, horizScrollOffset);
295

296
    QRect r(xOffset, 0, width() - xOffset, height());
297

298 299 300 301 302
    if(d->m_pOptions->m_bRightToLeftLanguage)
    {
        deltaX = -deltaX;
        r = QRect(width() - xOffset - 2, 0, -(width() - xOffset), height()).normalized();
    }
303

Michael Reeves's avatar
Michael Reeves committed
304
    if(d->m_bSelectionInProgress && d->m_selection.isValidFirstLine())
305
    {
Michael Reeves's avatar
Michael Reeves committed
306
        int line, pos;
307 308 309 310 311 312
        convertToLinePos(d->m_lastKnownMousePos.x(), d->m_lastKnownMousePos.y(), line, pos);
        d->m_selection.end(line, pos);
        update();
    }
    else
    {
Michael Reeves's avatar
Michael Reeves committed
313
        scroll(deltaX, 0, r);
314
    }
315 316 317 318
}

int DiffTextWindow::getMaxTextWidth()
{
319 320 321 322 323 324 325 326 327 328 329 330 331 332
    if(d->m_bWordWrap)
    {
        return getVisibleTextAreaWidth();
    }
    else if(getAtomic(d->m_maxTextWidth) < 0)
    {
        d->m_maxTextWidth = 0;
        QTextLayout textLayout(QString(), font(), this);
        for(int i = 0; i < d->m_size; ++i)
        {
            textLayout.clearLayout();
            textLayout.setText(d->getString(i));
            d->prepareTextLayout(textLayout, true);
            if(textLayout.maximumWidth() > getAtomic(d->m_maxTextWidth))
333
                d->m_maxTextWidth =  qCeil(textLayout.maximumWidth());
334 335 336
        }
    }
    return getAtomic(d->m_maxTextWidth);
Michael Reeves's avatar
Michael Reeves committed
337 338
}

339 340
int DiffTextWindow::getNofLines()
{
341
    return d->m_bWordWrap ? d->m_diff3WrapLineVector.size() : d->m_pDiff3LineVector->size();
Michael Reeves's avatar
Michael Reeves committed
342 343
}

344
int DiffTextWindow::convertLineToDiff3LineIdx(int line)
345
{
346
    if(line >= 0 && d->m_bWordWrap && d->m_diff3WrapLineVector.size() > 0)
347
        return d->m_diff3WrapLineVector[std::min(line, (int)d->m_diff3WrapLineVector.size() - 1)].diff3LineIndex;
348 349
    else
        return line;
Michael Reeves's avatar
Michael Reeves committed
350 351
}

352
int DiffTextWindow::convertDiff3LineIdxToLine(int d3lIdx)
353
{
Michael Reeves's avatar
Michael Reeves committed
354
    if(d->m_bWordWrap && d->m_pDiff3LineVector != nullptr && d->m_pDiff3LineVector->size() > 0)
355
        return (*d->m_pDiff3LineVector)[std::min(d3lIdx, (int)d->m_pDiff3LineVector->size() - 1)]->sumLinesNeededForDisplay;
356 357
    else
        return d3lIdx;
Joachim Eibl's avatar
Joachim Eibl committed
358 359 360 361 362 363
}

/** Returns a line number where the linerange [line, line+nofLines] can
    be displayed best. If it fits into the currently visible range then
    the returned value is the current firstLine.
*/
364
int getBestFirstLine(int line, int nofLines, int firstLine, int visibleLines)
365
{
366 367 368 369 370 371 372 373
    int newFirstLine = firstLine;
    if(line < firstLine || line + nofLines + 2 > firstLine + visibleLines)
    {
        if(nofLines > visibleLines || nofLines <= (2 * visibleLines / 3 - 1))
            newFirstLine = line - visibleLines / 3;
        else
            newFirstLine = line - (visibleLines - nofLines);
    }
374

375
    return newFirstLine;
376 377
}

Michael Reeves's avatar
Michael Reeves committed
378
void DiffTextWindow::setFastSelectorRange(int line1, int nofLines)
379 380 381 382 383
{
    d->m_fastSelectorLine1 = line1;
    d->m_fastSelectorNofLines = nofLines;
    if(isVisible())
    {
Michael Reeves's avatar
Michael Reeves committed
384
        int newFirstLine = getBestFirstLine(
385 386 387 388 389 390
            convertDiff3LineIdxToLine(d->m_fastSelectorLine1),
            convertDiff3LineIdxToLine(d->m_fastSelectorLine1 + d->m_fastSelectorNofLines) - convertDiff3LineIdxToLine(d->m_fastSelectorLine1),
            d->m_firstLine,
            getNofVisibleLines());
        if(newFirstLine != d->m_firstLine)
        {
Peter Wu's avatar
Peter Wu committed
391
            emit scrollDiffTextWindow(0, newFirstLine - d->m_firstLine);
392 393 394 395 396
        }

        update();
    }
}
397

398
void DiffTextWindow::showStatusLine(int line)
399
{
400
    int d3lIdx = convertLineToDiff3LineIdx(line);
Michael Reeves's avatar
Michael Reeves committed
401
    if(d->m_pDiff3LineVector != nullptr && d3lIdx >= 0 && d3lIdx < (int)d->m_pDiff3LineVector->size())
402 403
    {
        const Diff3Line* pD3l = (*d->m_pDiff3LineVector)[d3lIdx];
Michael Reeves's avatar
Michael Reeves committed
404
        if(pD3l != nullptr)
405
        {
Michael Reeves's avatar
Michael Reeves committed
406
            int l = pD3l->getLineInFile(d->m_winIdx);
407

Pino Toscano's avatar
Pino Toscano committed
408
            QString s;
409
            if(l != -1)
Pino Toscano's avatar
Pino Toscano committed
410
                s = i18n("File %1: Line %2", d->m_filename, l + 1);
411
            else
Pino Toscano's avatar
Pino Toscano committed
412
                s = i18n("File %1: Line not available", d->m_filename);
Michael Reeves's avatar
Michael Reeves committed
413
            if(d->m_pStatusBar != nullptr) d->m_pStatusBar->showMessage(s);
414

415 416 417
            emit lineClicked(d->m_winIdx, l);
        }
    }
418 419
}

420 421 422 423 424
void DiffTextWindow::focusInEvent(QFocusEvent* e)
{
    emit gotFocus();
    QWidget::focusInEvent(e);
}
425

426
void DiffTextWindow::mousePressEvent(QMouseEvent* e)
427
{
428 429
    if(e->button() == Qt::LeftButton)
    {
Michael Reeves's avatar
Michael Reeves committed
430
        int line;
431 432
        int pos;
        convertToLinePos(e->x(), e->y(), line, pos);
433

434 435
        int fontWidth = fontMetrics().width('0');
        int xOffset = d->leftInfoWidth() * fontWidth;
436

437 438 439
        if((!d->m_pOptions->m_bRightToLeftLanguage && e->x() < xOffset) || (d->m_pOptions->m_bRightToLeftLanguage && e->x() > width() - xOffset))
        {
            emit setFastSelectorLine(convertLineToDiff3LineIdx(line));
440
            d->m_selection.reset(); // Disable current d->m_selection
441 442 443 444 445 446 447 448 449 450 451 452
        }
        else
        { // Selection
            resetSelection();
            d->m_selection.start(line, pos);
            d->m_selection.end(line, pos);
            d->m_bSelectionInProgress = true;
            d->m_lastKnownMousePos = e->pos();

            showStatusLine(line);
        }
    }
453 454
}

455
bool isCTokenChar(QChar c)
456
{
457 458 459
    return (c == '_') ||
           (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
           (c >= '0' && c <= '9');
460 461
}

462 463
/// Calculate where a token starts and ends, given the x-position on screen.
void calcTokenPos(const QString& s, int posOnScreen, int& pos1, int& pos2, int tabSize)
464
{
465
    // Cursor conversions that consider g_tabSize
466
    int pos = convertToPosInText(s, std::max(0, posOnScreen), tabSize);
467 468 469 470 471 472
    if(pos >= (int)s.length())
    {
        pos1 = s.length();
        pos2 = s.length();
        return;
    }
473

474 475
    pos1 = pos;
    pos2 = pos + 1;
476

477 478 479 480 481
    if(isCTokenChar(s[pos1]))
    {
        while(pos1 >= 0 && isCTokenChar(s[pos1]))
            --pos1;
        ++pos1;
482

483 484 485
        while(pos2 < (int)s.length() && isCTokenChar(s[pos2]))
            ++pos2;
    }
486 487
}

488
void DiffTextWindow::mouseDoubleClickEvent(QMouseEvent* e)
489
{
490 491 492 493
    d->m_bSelectionInProgress = false;
    d->m_lastKnownMousePos = e->pos();
    if(e->button() == Qt::LeftButton)
    {
Michael Reeves's avatar
Michael Reeves committed
494
        int line;
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
        int pos;
        convertToLinePos(e->x(), e->y(), line, pos);

        // Get the string data of the current line
        QString s;
        if(d->m_bWordWrap)
        {
            if(line < 0 || line >= (int)d->m_diff3WrapLineVector.size())
                return;
            const Diff3WrapLine& d3wl = d->m_diff3WrapLineVector[line];
            s = d->getString(d3wl.diff3LineIndex).mid(d3wl.wrapLineOffset, d3wl.wrapLineLength);
        }
        else
        {
            if(line < 0 || line >= (int)d->m_pDiff3LineVector->size())
                return;
            s = d->getString(line);
        }

        if(!s.isEmpty())
        {
            int pos1, pos2;
            calcTokenPos(s, pos, pos1, pos2, d->m_pOptions->m_tabSize);

            resetSelection();
            d->m_selection.start(line, convertToPosOnScreen(s, pos1, d->m_pOptions->m_tabSize));
            d->m_selection.end(line, convertToPosOnScreen(s, pos2, d->m_pOptions->m_tabSize));
            update();
            // emit d->m_selectionEnd() happens in the mouseReleaseEvent.
            showStatusLine(line);
        }
    }
Joachim Eibl's avatar
Joachim Eibl committed
527 528
}

529 530 531 532 533 534 535 536 537
void DiffTextWindow::mouseReleaseEvent(QMouseEvent* e)
{
    d->m_bSelectionInProgress = false;
    d->m_lastKnownMousePos = e->pos();
    //if ( e->button() == LeftButton )
    {
        if(d->m_delayedDrawTimer)
            killTimer(d->m_delayedDrawTimer);
        d->m_delayedDrawTimer = 0;
Michael Reeves's avatar
Michael Reeves committed
538
        if(d->m_selection.isValidFirstLine())
539 540 541 542 543 544
        {
            emit selectionEnd();
        }
    }
    d->m_scrollDeltaX = 0;
    d->m_scrollDeltaY = 0;
545 546
}

547 548 549 550
inline int sqr(int x) { return x * x; }

void DiffTextWindow::mouseMoveEvent(QMouseEvent* e)
{
Michael Reeves's avatar
Michael Reeves committed
551
    int line;
552 553 554 555
    int pos;
    convertToLinePos(e->x(), e->y(), line, pos);
    d->m_lastKnownMousePos = e->pos();

Michael Reeves's avatar
Michael Reeves committed
556
    if(d->m_selection.isValidFirstLine())
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
    {
        d->m_selection.end(line, pos);

        showStatusLine(line);

        // Scroll because mouse moved out of the window
        const QFontMetrics& fm = fontMetrics();
        int fontWidth = fm.width('0');
        int deltaX = 0;
        int deltaY = 0;
        if(!d->m_pOptions->m_bRightToLeftLanguage)
        {
            if(e->x() < d->leftInfoWidth() * fontWidth) deltaX = -1 - abs(e->x() - d->leftInfoWidth() * fontWidth) / fontWidth;
            if(e->x() > width()) deltaX = +1 + abs(e->x() - width()) / fontWidth;
        }
        else
        {
            if(e->x() > width() - 1 - d->leftInfoWidth() * fontWidth) deltaX = +1 + abs(e->x() - (width() - 1 - d->leftInfoWidth() * fontWidth)) / fontWidth;
            if(e->x() < fontWidth) deltaX = -1 - abs(e->x() - fontWidth) / fontWidth;
        }
        if(e->y() < 0) deltaY = -1 - sqr(e->y()) / sqr(fm.lineSpacing());
        if(e->y() > height()) deltaY = +1 + sqr(e->y() - height()) / sqr(fm.lineSpacing());
        if((deltaX != 0 && d->m_scrollDeltaX != deltaX) || (deltaY != 0 && d->m_scrollDeltaY != deltaY))
        {
            d->m_scrollDeltaX = deltaX;
            d->m_scrollDeltaY = deltaY;
Peter Wu's avatar
Peter Wu committed
583
            emit scrollDiffTextWindow(deltaX, deltaY);
584 585 586 587 588 589 590 591 592 593 594 595
            if(d->m_delayedDrawTimer)
                killTimer(d->m_delayedDrawTimer);
            d->m_delayedDrawTimer = startTimer(50);
        }
        else
        {
            d->m_scrollDeltaX = deltaX;
            d->m_scrollDeltaY = deltaY;
            d->myUpdate(0);
        }
    }
}
596 597 598

void DiffTextWindowData::myUpdate(int afterMilliSecs)
{
599 600 601 602
    if(m_delayedDrawTimer)
        m_pDiffTextWindow->killTimer(m_delayedDrawTimer);
    m_bMyUpdate = true;
    m_delayedDrawTimer = m_pDiffTextWindow->startTimer(afterMilliSecs);
603 604 605 606
}

void DiffTextWindow::timerEvent(QTimerEvent*)
{
607 608 609 610 611 612 613
    killTimer(d->m_delayedDrawTimer);
    d->m_delayedDrawTimer = 0;

    if(d->m_bMyUpdate)
    {
        int fontHeight = fontMetrics().lineSpacing();

Michael Reeves's avatar
Michael Reeves committed
614
        if(d->m_selection.getOldLastLine() != -1)
615
        {
Michael Reeves's avatar
Michael Reeves committed
616 617
            int lastLine;
            int firstLine;
Michael Reeves's avatar
Michael Reeves committed
618
            if(d->m_selection.getOldFirstLine() != -1)
619
            {
Michael Reeves's avatar
Michael Reeves committed
620 621
                firstLine = min3(d->m_selection.getOldFirstLine(), d->m_selection.getLastLine(), d->m_selection.getOldLastLine());
                lastLine = max3(d->m_selection.getOldFirstLine(), d->m_selection.getLastLine(), d->m_selection.getOldLastLine());
622 623 624
            }
            else
            {
625 626
                firstLine = std::min(d->m_selection.getLastLine(), d->m_selection.getOldLastLine());
                lastLine = std::max(d->m_selection.getLastLine(), d->m_selection.getOldLastLine());
627 628
            }
            int y1 = (firstLine - d->m_firstLine) * fontHeight;
629
            int y2 = std::min(height(), (lastLine - d->m_firstLine + 1) * fontHeight);
630 631 632 633 634 635 636 637 638 639 640 641 642

            if(y1 < height() && y2 > 0)
            {
                QRect invalidRect = QRect(0, y1 - 1, width(), y2 - y1 + fontHeight); // Some characters in exotic exceed the regular bottom.
                update(invalidRect);
            }
        }

        d->m_bMyUpdate = false;
    }

    if(d->m_scrollDeltaX != 0 || d->m_scrollDeltaY != 0)
    {
Michael Reeves's avatar
Michael Reeves committed
643
        d->m_selection.end(d->m_selection.getLastLine() + d->m_scrollDeltaY, d->m_selection.getLastPos() + d->m_scrollDeltaX);
Peter Wu's avatar
Peter Wu committed
644
        emit scrollDiffTextWindow(d->m_scrollDeltaX, d->m_scrollDeltaY);
645 646 647
        killTimer(d->m_delayedDrawTimer);
        d->m_delayedDrawTimer = startTimer(50);
    }
648 649 650 651
}

void DiffTextWindow::resetSelection()
{
652 653
    d->m_selection.reset();
    update();
654 655
}

Michael Reeves's avatar
Michael Reeves committed
656
void DiffTextWindow::convertToLinePos(int x, int y, int& line, int& pos)
657
{
658 659
    const QFontMetrics& fm = fontMetrics();
    int fontHeight = fm.lineSpacing();
660

661
    int yOffset = -d->m_firstLine * fontHeight;
662

663 664 665 666 667 668 669 670 671 672
    line = (y - yOffset) / fontHeight;
    if(line >= 0 && (!d->m_pOptions->m_bWordWrap || line < d->m_diff3WrapLineVector.count()))
    {
        QString s = d->getLineString(line);
        QTextLayout textLayout(s, font(), this);
        d->prepareTextLayout(textLayout, !d->m_pOptions->m_bWordWrap || d->m_diff3WrapLineVector[line].wrapLineOffset == 0);
        pos = textLayout.lineAt(0).xToCursor(x - textLayout.position().x());
    }
    else
        pos = -1;
673 674 675 676
}

class FormatRangeHelper
{
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
  private:
    QFont m_font;
    QPen m_pen;
    QColor m_background;
    int m_currentPos;

  public:
    QVector<QTextLayout::FormatRange> m_formatRanges;

    FormatRangeHelper()
    {
        m_pen = QColor(Qt::black);
        m_background = QColor(Qt::white);
        m_currentPos = 0;
    }
    void setFont(const QFont& f)
    {
        m_font = f;
    }
    void setPen(const QPen& pen)
    {
        m_pen = pen;
    }
    void setBackground(const QColor& background)
    {
        m_background = background;
    }

    void next()
    {
        if(m_formatRanges.isEmpty() || m_formatRanges.back().format.foreground().color() != m_pen.color() || m_formatRanges.back().format.background().color() != m_background)
        {
            QTextLayout::FormatRange fr;
            fr.length = 1;
            fr.start = m_currentPos;
            fr.format.setForeground(m_pen.color());
            fr.format.setBackground(m_background);
            m_formatRanges.append(fr);
        }
        else
        {
            ++m_formatRanges.back().length;
        }
        ++m_currentPos;
    }
722 723
};

724
void DiffTextWindowData::prepareTextLayout(QTextLayout& textLayout, bool /*bFirstLine*/, int visibleTextWidth)
725
{
Michael Reeves's avatar
Michael Reeves committed
726
    QTextOption textOption;
Michael Reeves's avatar
Michael Reeves committed
727
#if QT_VERSION < QT_VERSION_CHECK(5,10,0)
728
    textOption.setTabStop(QFontMetricsF(m_pDiffTextWindow->font()).width(' ') * m_pOptions->m_tabSize);
Michael Reeves's avatar
Michael Reeves committed
729 730 731
#else
    textOption.setTabStopDistance(QFontMetricsF(m_pDiffTextWindow->font()).width(' ') * m_pOptions->m_tabSize);
#endif
732 733 734
    if(m_pOptions->m_bShowWhiteSpaceCharacters)
        textOption.setFlags(QTextOption::ShowTabsAndSpaces);
    if(m_pOptions->m_bRightToLeftLanguage)
Michael Reeves's avatar
Michael Reeves committed
735
        textOption.setAlignment(Qt::AlignRight); // only relevant for multi line text layout
736 737
    if(visibleTextWidth >= 0)
        textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
Michael Reeves's avatar
Michael Reeves committed
738

739
    textLayout.setTextOption(textOption);
Michael Reeves's avatar
Michael Reeves committed
740

741 742 743 744 745 746 747 748 749 750 751 752
    if(m_pOptions->m_bShowWhiteSpaceCharacters)
    {
        // This additional format is only necessary for the tab arrow
        QVector<QTextLayout::FormatRange> formats;
        QTextLayout::FormatRange formatRange;
        formatRange.start = 0;
        formatRange.length = textLayout.text().length();
        formatRange.format.setFont(m_pDiffTextWindow->font());
        formats.append(formatRange);
        textLayout.setFormats(formats);
    }
    textLayout.beginLayout();
Michael Reeves's avatar
Michael Reeves committed
753

754 755
    int leading = m_pDiffTextWindow->fontMetrics().leading();
    int height = 0;
Michael Reeves's avatar
Michael Reeves committed
756

Michael Reeves's avatar
Michael Reeves committed
757
    int fontWidth = m_pDiffTextWindow->fontMetrics().width('0');
Michael Reeves's avatar
Michael Reeves committed
758 759
    int xOffset = leftInfoWidth() * fontWidth - m_horizScrollOffset;
    int textWidth = visibleTextWidth;
760
    if(textWidth < 0)
Michael Reeves's avatar
Michael Reeves committed
761 762 763
        textWidth = m_pDiffTextWindow->width() - xOffset;

    int indentation = 0;
764
    while(true)
765
    {
Michael Reeves's avatar
Michael Reeves committed
766
        QTextLine line = textLayout.createLine();
767
        if(!line.isValid())
Michael Reeves's avatar
Michael Reeves committed
768 769 770
            break;

        height += leading;
771 772 773
        //if ( !bFirstLine )
        //   indentation = m_pDiffTextWindow->fontMetrics().width(' ') * m_pOptions->m_tabSize;
        if(visibleTextWidth >= 0)
Michael Reeves's avatar
Michael Reeves committed
774
        {
775 776
            line.setLineWidth(visibleTextWidth - indentation);
            line.setPosition(QPointF(indentation, height));
777
            height +=  qCeil(line.height());
Michael Reeves's avatar
Michael Reeves committed
778
            //bFirstLine = false;
Michael Reeves's avatar
Michael Reeves committed
779
        }
Michael Reeves's avatar
Michael Reeves committed
780 781
        else // only one line
        {
782
            line.setPosition(QPointF(indentation, height));
Michael Reeves's avatar
Michael Reeves committed
783 784 785 786 787
            break;
        }
    }

    textLayout.endLayout();
788 789
    if(m_pOptions->m_bRightToLeftLanguage)
        textLayout.setPosition(QPointF(textWidth - textLayout.maximumWidth(), 0));
Michael Reeves's avatar
Michael Reeves committed
790
    else
791
        textLayout.setPosition(QPointF(xOffset, 0));
792
}
Joachim Eibl's avatar
Joachim Eibl committed
793

Joachim Eibl's avatar
Joachim Eibl committed
794
void DiffTextWindowData::writeLine(
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
    MyPainter& p,
    const LineData* pld,
    const DiffList* pLineDiff1,
    const DiffList* pLineDiff2,
    int line,
    int whatChanged,
    int whatChanged2,
    int srcLineIdx,
    int wrapLineOffset,
    int wrapLineLength,
    bool bWrapLine,
    const QRect& invalidRect,
    int deviceWidth)
{
    QFont normalFont = p.font();
Michael Reeves's avatar
Michael Reeves committed
810

811 812 813 814 815 816 817 818
    const QFontMetrics& fm = p.fontMetrics();
    int fontHeight = fm.lineSpacing();
    int fontAscent = fm.ascent();
    int fontWidth = fm.width('0');

    int xOffset = leftInfoWidth() * fontWidth - m_horizScrollOffset;
    int yOffset = (line - m_firstLine) * fontHeight;

819
    QRect lineRect(xOffset, yOffset, deviceWidth, fontHeight);
820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841
    if(!invalidRect.intersects(lineRect))
    {
        return;
    }

    int fastSelectorLine1 = m_pDiffTextWindow->convertDiff3LineIdxToLine(m_fastSelectorLine1);
    int fastSelectorLine2 = m_pDiffTextWindow->convertDiff3LineIdxToLine(m_fastSelectorLine1 + m_fastSelectorNofLines) - 1;

    bool bFastSelectionRange = (line >= fastSelectorLine1 && line <= fastSelectorLine2);
    QColor bgColor = m_pOptions->m_bgColor;
    QColor diffBgColor = m_pOptions->m_diffBgColor;

    if(bFastSelectionRange)
    {
        bgColor = m_pOptions->m_currentRangeBgColor;
        diffBgColor = m_pOptions->m_currentRangeDiffBgColor;
    }

    if(yOffset + fontHeight < invalidRect.top() || invalidRect.bottom() < yOffset - fontHeight)
        return;

    int changed = whatChanged;
Michael Reeves's avatar
Michael Reeves committed
842 843
    if(pLineDiff1 != nullptr) changed |= 1;
    if(pLineDiff2 != nullptr) changed |= 2;
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858

    QColor c = m_pOptions->m_fgColor;
    p.setPen(c);
    if(changed == 2) {
        c = m_cDiff2;
    }
    else if(changed == 1)
    {
        c = m_cDiff1;
    }
    else if(changed == 3)
    {
        c = m_cDiffBoth;
    }

Michael Reeves's avatar
Michael Reeves committed
859
    if(pld != nullptr)
860 861 862
    {
        // First calculate the "changed" information for each character.
        int i = 0;
863
        QString lineString(pld->getLine(), pld->size());
864 865 866 867 868 869 870 871 872 873 874 875 876
        if(!lineString.isEmpty())
        {
            switch(lineString[lineString.length() - 1].unicode())
            {
            case '\n':
                lineString[lineString.length() - 1] = 0x00B6;
                break; // "Pilcrow", "paragraph mark"
            case '\r':
                lineString[lineString.length() - 1] = 0x00A4;
                break; // Currency sign ;0x2761 "curved stem paragraph sign ornament"
                //case '\0b' : lineString[lineString.length()-1] = 0x2756; break; // some other nice looking character
            }
        }
877
        QVector<quint8> charChanged(pld->size());
878
        Merger merger(pLineDiff1, pLineDiff2);
879
        while(!merger.isEndReached() && i < pld->size())
880
        {
881
            if(i < pld->size())
Joachim Eibl's avatar
Joachim Eibl committed
882
            {
883 884 885 886 887 888 889 890 891 892 893
                charChanged[i] = merger.whatChanged();
                ++i;
            }
            merger.next();
        }

        int outPos = 0;

        int lineLength = m_bWordWrap ? wrapLineOffset + wrapLineLength : lineString.length();

        FormatRangeHelper frh;
Michael Reeves's avatar
Michael Reeves committed
894

895 896
        for(i = wrapLineOffset; i < lineLength; ++i)
        {
Michael Reeves's avatar
Michael Reeves committed
897
            c = m_pOptions->m_fgColor;
898
            int cchanged = charChanged[i] | whatChanged;
Michael Reeves's avatar
Michael Reeves committed
899

900 901
            if(cchanged == 2) {
                c = m_cDiff2;
Michael Reeves's avatar
Michael Reeves committed
902
            }
903 904 905 906 907
            else if(cchanged == 1)
            {
                c = m_cDiff1;
            }
            else if(cchanged == 3)
908
            {
909 910 911 912 913 914 915 916
                c = m_cDiffBoth;
            }

            if(c != m_pOptions->m_fgColor && whatChanged2 == 0 && !m_pOptions->m_bShowWhiteSpace)
            {
                // The user doesn't want to see highlighted white space.
                c = m_pOptions->m_fgColor;
            }
Michael Reeves's avatar
Michael Reeves committed
917

918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
            {
                frh.setBackground(bgColor);
                if(!m_selection.within(line, outPos))
                {

                    if(c != m_pOptions->m_fgColor)
                    {
                        QColor lightc = diffBgColor;
                        frh.setBackground(lightc);
                        // Setting italic font here doesn't work: Changing the font only when drawing is too late
                    }

                    frh.setPen(c);
                    frh.next();
                    frh.setFont(normalFont);
                }
                else
                {
                    frh.setBackground(m_pDiffTextWindow->palette().highlight().color());
                    frh.setPen(m_pDiffTextWindow->palette().highlightedText().color());
                    frh.next();

                    m_selection.bSelectionContainsData = true;
                }
Michael Reeves's avatar
Michael Reeves committed
942
            }
943 944 945 946 947 948 949 950 951 952 953 954 955 956

            ++outPos;
        } // end for

        QTextLayout textLayout(lineString.mid(wrapLineOffset, lineLength - wrapLineOffset), m_pDiffTextWindow->font(), m_pDiffTextWindow);
        prepareTextLayout(textLayout, !m_bWordWrap || wrapLineOffset == 0);
        textLayout.draw(&p, QPoint(0, yOffset), frh.m_formatRanges /*, const QRectF & clip = QRectF() */);
    }

    p.fillRect(0, yOffset, leftInfoWidth() * fontWidth, fontHeight, m_pOptions->m_bgColor);

    xOffset = (m_lineNumberWidth + 2) * fontWidth;
    int xLeft = m_lineNumberWidth * fontWidth;
    p.setPen(m_pOptions->m_fgColor);
Michael Reeves's avatar
Michael Reeves committed
957
    if(pld != nullptr)
958 959 960 961 962 963 964 965 966 967 968
    {
        if(m_pOptions->m_bShowLineNumbers && !bWrapLine)
        {
            QString num;
            num.sprintf("%0*d", m_lineNumberWidth, srcLineIdx + 1);
            p.drawText(0, yOffset + fontAscent, num);
            //p.drawLine( xLeft -1, yOffset, xLeft -1, yOffset+fontHeight-1 );
        }
        if(!bWrapLine || wrapLineLength > 0)
        {
            Qt::PenStyle wrapLinePenStyle = Qt::DotLine;
969

970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
            p.setPen(QPen(m_pOptions->m_fgColor, 0, bWrapLine ? wrapLinePenStyle : Qt::SolidLine));
            p.drawLine(xOffset + 1, yOffset, xOffset + 1, yOffset + fontHeight - 1);
            p.setPen(QPen(m_pOptions->m_fgColor, 0, Qt::SolidLine));
        }
    }
    if(c != m_pOptions->m_fgColor && whatChanged2 == 0) //&& whatChanged==0 )
    {
        if(m_pOptions->m_bShowWhiteSpace)
        {
            p.setBrushOrigin(0, 0);
            p.fillRect(xLeft, yOffset, fontWidth * 2 - 1, fontHeight, QBrush(c, Qt::Dense5Pattern));
        }
    }
    else
    {
        p.fillRect(xLeft, yOffset, fontWidth * 2 - 1, fontHeight, c == m_pOptions->m_fgColor ? bgColor : c);
    }

    if(bFastSelectionRange)
    {
        p.fillRect(xOffset + fontWidth - 1, yOffset, 3, fontHeight, m_pOptions->m_fgColor);
    }

    // Check if line needs a manual diff help mark
    ManualDiffHelpList::const_iterator ci;
    for(ci = m_pManualDiffHelpList->begin(); ci != m_pManualDiffHelpList->end(); ++ci)
    {
        const ManualDiffHelpEntry& mdhe = *ci;
        int rangeLine1 = -1;
        int rangeLine2 = -1;
        if(m_winIdx == 1) {
            rangeLine1 = mdhe.lineA1;
            rangeLine2 = mdhe.lineA2;
        }
        if(m_winIdx == 2) {
            rangeLine1 = mdhe.lineB1;
            rangeLine2 = mdhe.lineB2;
        }
        if(m_winIdx == 3) {
            rangeLine1 = mdhe.lineC1;
            rangeLine2 = mdhe.lineC2;
        }
        if(rangeLine1 >= 0 && rangeLine2 >= 0 && srcLineIdx >= rangeLine1 && srcLineIdx <= rangeLine2)
        {
            p.fillRect(xOffset - fontWidth, yOffset, fontWidth - 1, fontHeight, m_pOptions->m_manualHelpRangeColor);
            break;
        }
    }
}

void DiffTextWindow::paintEvent(QPaintEvent* e)
{
    QRect invalidRect = e->rect();
    if(invalidRect.isEmpty() || !d->m_bPaintingAllowed)
        return;

Michael Reeves's avatar
Michael Reeves committed
1026
    if(d->m_pDiff3LineVector == nullptr || (d->m_diff3WrapLineVector.empty() && d->m_bWordWrap))
1027 1028 1029 1030 1031 1032 1033 1034 1035
    {
        QPainter p(this);
        p.fillRect(invalidRect, d->m_pOptions->m_bgColor);
        return;
    }

    bool bOldSelectionContainsData = d->m_selection.bSelectionContainsData;
    d->m_selection.bSelectionContainsData = false;

1036
    int endLine = std::min(d->m_firstLine + getNofVisibleLines() + 2, getNofLines());
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046

    MyPainter p(this, d->m_pOptions->m_bRightToLeftLanguage, width(), fontMetrics().width('0'));

    p.setFont(font());
    p.QPainter::fillRect(invalidRect, d->m_pOptions->m_bgColor);

    d->draw(p, invalidRect, width(), d->m_firstLine, endLine);
    p.end();

    d->m_oldFirstLine = d->m_firstLine;
Michael Reeves's avatar
Michael Reeves committed
1047
    d->m_selection.clearOldSelection();
1048

Michael Reeves's avatar
Michael Reeves committed
1049
    if(!bOldSelectionContainsData && d->m_selection.selectionContainsData())
1050 1051 1052 1053 1054
        emit newSelection();
}

void DiffTextWindow::print(MyPainter& p, const QRect&, int firstLine, int nofLinesPerPage)
{
Michael Reeves's avatar
Michael Reeves committed
1055
    if(d->m_pDiff3LineVector == nullptr || !d->m_bPaintingAllowed ||
1056 1057 1058 1059 1060 1061 1062 1063
       (d->m_diff3WrapLineVector.empty() && d->m_bWordWrap))
        return;
    resetSelection();
    int oldFirstLine = d->m_firstLine;
    d->m_firstLine = firstLine;
    QRect invalidRect = QRect(0, 0, 1000000000, 1000000000);
    QColor bgColor = d->m_pOptions->m_bgColor;
    d->m_pOptions->m_bgColor = Qt::white;
1064
    d->draw(p, invalidRect, p.window().width(), firstLine, std::min(firstLine + nofLinesPerPage, getNofLines()));
1065 1066 1067 1068 1069 1070
    d->m_pOptions->m_bgColor = bgColor;
    d->m_firstLine = oldFirstLine;
}

void DiffTextWindowData::draw(MyPainter& p, const QRect& invalidRect, int deviceWidth, int beginLine, int endLine)
{
Pino Toscano's avatar
Pino Toscano committed
1071
    m_lineNumberWidth = m_pOptions->m_bShowLineNumbers ? (int)log10((double)std::max(m_size, 1)) + 1 : 0;
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098

    if(m_winIdx == 1)
    {
        m_cThis = m_pOptions->m_colorA;
        m_cDiff1 = m_pOptions->m_colorB;
        m_cDiff2 = m_pOptions->m_colorC;
    }
    if(m_winIdx == 2)
    {
        m_cThis = m_pOptions->m_colorB;
        m_cDiff1 = m_pOptions->m_colorC;
        m_cDiff2 = m_pOptions->m_colorA;
    }
    if(m_winIdx == 3)
    {
        m_cThis = m_pOptions->m_colorC;
        m_cDiff1 = m_pOptions->m_colorA;
        m_cDiff2 = m_pOptions->m_colorB;
    }
    m_cDiffBoth = m_pOptions->m_colorForConflict; // Conflict color

    p.setPen(m_cThis);

    for(int line = beginLine; line < endLine; ++line)
    {
        int wrapLineOffset = 0;
        int wrapLineLength = 0;
Michael Reeves's avatar
Michael Reeves committed
1099
        const Diff3Line* d3l = nullptr;
1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
        bool bWrapLine = false;
        if(m_bWordWrap)
        {
            Diff3WrapLine& d3wl = m_diff3WrapLineVector[line];
            wrapLineOffset = d3wl.wrapLineOffset;
            wrapLineLength = d3wl.wrapLineLength;
            d3l = d3wl.pD3L;
            bWrapLine = line > 0 && m_diff3WrapLineVector[line - 1].pD3L == d3l;
        }
        else
        {
            d3l = (*m_pDiff3LineVector)[line];
        }
        DiffList* pFineDiff1;
        DiffList* pFineDiff2;
        int changed = 0;
        int changed2 = 0;

Michael Reeves's avatar
Michael Reeves committed
1118
        int srcLineIdx = -1;
1119 1120 1121 1122
        getLineInfo(*d3l, srcLineIdx, pFineDiff1, pFineDiff2, changed, changed2);

        writeLine(
            p,                                               // QPainter
Michael Reeves's avatar
Michael Reeves committed
1123
            srcLineIdx == -1 ? nullptr : &m_pLineData[srcLineIdx], // Text in this line
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136