kdiff3.cpp 44.1 KB
Newer Older
Joachim Eibl's avatar
Joachim Eibl committed
1
/***************************************************************************
Michael Reeves's avatar
Michael Reeves committed
2 3
 *   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
4 5 6 7 8 9 10 11
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

12 13 14 15 16
// application specific includes
#include "kdiff3.h"

#include "directorymergewindow.h"
#include "fileaccess.h"
Michael Reeves's avatar
cleanup  
Michael Reeves committed
17
#include "guiutils.h"
18 19 20 21 22
#include "kdiff3_part.h"
#include "kdiff3_shell.h"
#include "optiondialog.h"
#include "progress.h"
#include "smalldialogs.h"
Joachim Eibl's avatar
Joachim Eibl committed
23 24
#include "difftextwindow.h"
#include "mergeresultwindow.h"
25
#include "RLPainter.h"
Michael Reeves's avatar
cleanup  
Michael Reeves committed
26

Michael Reeves's avatar
Michael Reeves committed
27
#ifndef Q_OS_WIN
Michael Reeves's avatar
cleanup  
Michael Reeves committed
28 29 30
#include <unistd.h>
#endif

Joachim Eibl's avatar
Joachim Eibl committed
31
// include files for QT
32
#include <QClipboard>
Joachim Eibl's avatar
Joachim Eibl committed
33
#include <QCheckBox>
34 35 36 37
#include <QCommandLineParser>
#include <QDesktopWidget>
#include <QDir>
#include <QFileDialog>
Joachim Eibl's avatar
Joachim Eibl committed
38 39
#include <QLabel>
#include <QLayout>
40 41 42
#include <QLineEdit>
#include <QMenu>
#include <QMenuBar>
Joachim Eibl's avatar
Joachim Eibl committed
43
#include <QPaintDevice>
44
#include <QPainter>
45
#include <QPointer>
Joachim Eibl's avatar
Joachim Eibl committed
46
#include <QPrintDialog>
47 48 49
#include <QPrinter>
#include <QPushButton>
#include <QSplitter>
Michael Reeves's avatar
Michael Reeves committed
50
#include <QStatusBar>
51
#include <QTextStream>
Michael Reeves's avatar
Michael Reeves committed
52
#include <QUrl>
Joachim Eibl's avatar
Joachim Eibl committed
53
// include files for KDE
54
#include <KCrash>
Michael Reeves's avatar
Michael Reeves committed
55
#include <KConfig>
56 57
#include <KLocalizedString>
#include <KMessageBox>
58
#include <KStandardAction>
59 60
#include <KActionCollection>
#include <KIconLoader>
61
#include <KTextEdit>
62 63
#include <KToggleAction>
#include <KToolBar>
Joachim Eibl's avatar
Joachim Eibl committed
64

Michael Reeves's avatar
cleanup  
Michael Reeves committed
65

66

Joachim Eibl's avatar
Joachim Eibl committed
67
#define ID_STATUS_MSG 1
68
#define MAIN_TOOLBAR_NAME QLatin1String("mainToolBar")
Joachim Eibl's avatar
Joachim Eibl committed
69

70
void printDiffTextWindow(RLPainter& painter, const QRect& view, const QString& headerText, DiffTextWindow* pDiffTextWindow, int line, int linesPerPage, const QColor& fgColor);
Michael Reeves's avatar
Michael Reeves committed
71 72
KActionCollection* KDiff3App::actionCollection()
{
Michael Reeves's avatar
Michael Reeves committed
73
    if(m_pKDiff3Shell == nullptr)
Michael Reeves's avatar
Michael Reeves committed
74 75 76
        return m_pKDiff3Part->actionCollection();
    else
        return m_pKDiff3Shell->actionCollection();
Joachim Eibl's avatar
Joachim Eibl committed
77 78
}

Michael Reeves's avatar
Michael Reeves committed
79 80
QStatusBar* KDiff3App::statusBar()
{
Michael Reeves's avatar
Michael Reeves committed
81 82
    if(m_pKDiff3Shell == nullptr)
        return nullptr;
Michael Reeves's avatar
Michael Reeves committed
83 84
    else
        return m_pKDiff3Shell->statusBar();
Joachim Eibl's avatar
Joachim Eibl committed
85 86
}

87
KToolBar* KDiff3App::toolBar(const QLatin1String toolBarId)
Michael Reeves's avatar
Michael Reeves committed
88
{
Michael Reeves's avatar
Michael Reeves committed
89 90
    if(m_pKDiff3Shell == nullptr)
        return nullptr;
Michael Reeves's avatar
Michael Reeves committed
91
    else
92
        return m_pKDiff3Shell->toolBar(toolBarId);
Joachim Eibl's avatar
Joachim Eibl committed
93 94
}

Michael Reeves's avatar
Michael Reeves committed
95 96
bool KDiff3App::isPart()
{
Michael Reeves's avatar
Michael Reeves committed
97
    return m_pKDiff3Shell == nullptr;
Joachim Eibl's avatar
Joachim Eibl committed
98 99
}

Michael Reeves's avatar
Michael Reeves committed
100 101
bool KDiff3App::isFileSaved()
{
Michael Reeves's avatar
Michael Reeves committed
102
    return m_bFileSaved;
Joachim Eibl's avatar
Joachim Eibl committed
103
}
Joachim Eibl's avatar
Joachim Eibl committed
104

Michael Reeves's avatar
Michael Reeves committed
105 106
bool KDiff3App::isDirComparison()
{
Michael Reeves's avatar
Michael Reeves committed
107
    return m_bDirCompare;
Joachim Eibl's avatar
0.9.93  
Joachim Eibl committed
108 109
}

Michael Reeves's avatar
Cleanup  
Michael Reeves committed
110
KDiff3App::KDiff3App(QWidget* pParent, const QString& name, KDiff3Part* pKDiff3Part)
111
    : QSplitter(pParent) //previously KMainWindow
Michael Reeves's avatar
Michael Reeves committed
112
{
Michael Reeves's avatar
Cleanup  
Michael Reeves committed
113
    setObjectName(name);
Michael Reeves's avatar
Michael Reeves committed
114
    m_pKDiff3Part = pKDiff3Part;
115
    m_pKDiff3Shell = qobject_cast<KParts::MainWindow*>(pParent);
Michael Reeves's avatar
Michael Reeves committed
116

117 118 119
    setWindowTitle("KDiff3");
    setOpaqueResize(false); // faster resizing
    setUpdatesEnabled(false);
120
    KCrash::initialize();
Michael Reeves's avatar
Michael Reeves committed
121

Michael Reeves's avatar
Michael Reeves committed
122 123 124 125 126 127
    // set Disabled to same color as enabled to prevent flicker in DirectoryMergeWindow
    QPalette pal;
    pal.setBrush(QPalette::Base, pal.brush(QPalette::Active, QPalette::Base));
    pal.setColor(QPalette::Text, pal.color(QPalette::Active, QPalette::Text));
    setPalette(pal);

Michael Reeves's avatar
Michael Reeves committed
128 129 130 131 132 133 134 135 136 137 138 139 140
    m_pMainSplitter = nullptr;
    m_pDirectoryMergeSplitter = nullptr;
    m_pDirectoryMergeWindow = nullptr;
    m_pCornerWidget = nullptr;
    m_pMainWidget = nullptr;
    m_pDiffTextWindow1 = nullptr;
    m_pDiffTextWindow2 = nullptr;
    m_pDiffTextWindow3 = nullptr;
    m_pDiffTextWindowFrame1 = nullptr;
    m_pDiffTextWindowFrame2 = nullptr;
    m_pDiffTextWindowFrame3 = nullptr;
    m_pDiffWindowSplitter = nullptr;
    m_pOverview = nullptr;
Michael Reeves's avatar
Michael Reeves committed
141
    m_bTripleDiff = false;
Michael Reeves's avatar
Michael Reeves committed
142 143
    m_pMergeResultWindow = nullptr;
    m_pMergeWindowFrame = nullptr;
Michael Reeves's avatar
Michael Reeves committed
144 145 146
    m_bOutputModified = false;
    m_bFileSaved = false;
    m_bTimerBlock = false;
Michael Reeves's avatar
Michael Reeves committed
147 148 149 150
    m_pHScrollBar = nullptr;
    m_pDiffVScrollBar = nullptr;
    m_pMergeVScrollBar = nullptr;
    viewToolBar = nullptr;
Michael Reeves's avatar
Michael Reeves committed
151
    m_bRecalcWordWrapPosted = false;
Michael Reeves's avatar
Michael Reeves committed
152
    m_bFinishMainInit = false;
Michael Reeves's avatar
Michael Reeves committed
153
    m_pEventLoopForPrinting = nullptr;
Michael Reeves's avatar
Michael Reeves committed
154
    m_bLoadFiles = false;
Michael Reeves's avatar
Michael Reeves committed
155 156

    // Needed before any file operations via FileAccess happen.
Michael Reeves's avatar
Michael Reeves committed
157 158
    if(!g_pProgressDialog)
    {
159 160
        g_pProgressDialog = new ProgressDialog(this, statusBar());
        g_pProgressDialog->setStayHidden(true);
Michael Reeves's avatar
Michael Reeves committed
161 162 163
    }

    // All default values must be set before calling readOptions().
Michael Reeves's avatar
Michael Reeves committed
164
    m_pOptionDialog = new OptionDialog(m_pKDiff3Shell != nullptr, this);
165
    connect(m_pOptionDialog, &OptionDialog::applyDone, this, &KDiff3App::slotRefresh);
Michael Reeves's avatar
Michael Reeves committed
166 167 168 169

    // This is just a convenience variable to make code that accesses options more readable
    m_pOptions = &m_pOptionDialog->m_options;

170
    m_pOptionDialog->readOptions(KSharedConfig::openConfig());
171

Michael Reeves's avatar
Michael Reeves committed
172
    // Option handling: Only when pParent==0 (no parent)
Michael Reeves's avatar
Michael Reeves committed
173 174
    int argCount = KDiff3Shell::getParser()->optionNames().count() + KDiff3Shell::getParser()->positionalArguments().count();
    bool hasArgs = !isPart() && argCount > 0;
175
    if(hasArgs) {
Michael Reeves's avatar
Michael Reeves committed
176 177
        QString s;
        QString title;
178
        if(KDiff3Shell::getParser()->isSet("confighelp"))
Michael Reeves's avatar
Michael Reeves committed
179
        {
Michael Reeves's avatar
Michael Reeves committed
180
            s = m_pOptionDialog->calcOptionHelp();
181
            title = i18n("Current Configuration:");
Michael Reeves's avatar
Michael Reeves committed
182
        }
Michael Reeves's avatar
Michael Reeves committed
183 184
        else
        {
185 186
            s = m_pOptionDialog->parseOptions(KDiff3Shell::getParser()->values("cs"));
            title = i18n("Config Option Error:");
Michael Reeves's avatar
Michael Reeves committed
187
        }
188
        if(!s.isEmpty())
Michael Reeves's avatar
Michael Reeves committed
189
        {
Michael Reeves's avatar
Michael Reeves committed
190
            //KMessageBox::information(0, s,i18n("KDiff3-Usage"));
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
#ifndef Q_OS_WIN
            if(isatty(fileno(stderr)) != 1)
#endif
            {
                QPointer<QDialog> pDialog = QPointer<QDialog>(new QDialog(this));
                pDialog->setAttribute(Qt::WA_DeleteOnClose);
                pDialog->setModal(true);
                pDialog->setWindowTitle(title);
                QVBoxLayout* pVBoxLayout = new QVBoxLayout(pDialog);
                QPointer<KTextEdit> pTextEdit = QPointer<KTextEdit>(new KTextEdit(pDialog));
                pTextEdit->setText(s);
                pTextEdit->setReadOnly(true);
                pTextEdit->setWordWrapMode(QTextOption::NoWrap);
                pVBoxLayout->addWidget(pTextEdit);
                pDialog->resize(600, 400);
                pDialog->exec();
            }
208
#if !defined(Q_OS_WIN)
209 210 211
            else
            {
                // Launched from a console
212 213 214
                QTextStream outStream(stdout);
                outStream << title << "\n";
                outStream << s;//newline already appended by parseOptions
215
            }
Joachim Eibl's avatar
Joachim Eibl committed
216
#endif
217
            exit(1);
Michael Reeves's avatar
Michael Reeves committed
218 219 220
        }
    }

221 222 223
    m_sd1.setOptions(m_pOptions);
    m_sd2.setOptions(m_pOptions);
    m_sd3.setOptions(m_pOptions);
Michael Reeves's avatar
Michael Reeves committed
224

Michael Reeves's avatar
Michael Reeves committed
225 226 227 228 229 230
#ifdef ENABLE_AUTO
    m_bAutoFlag = hasArgs && KDiff3Shell::getParser()->isSet("auto");
#else
    m_bAutoFlag = false;
#endif

231
    m_bAutoMode = m_bAutoFlag || m_pOptions->m_bAutoSaveAndQuitOnMergeWithoutConflicts;
232 233 234 235 236 237 238 239 240 241
    if(hasArgs) {
        m_outputFilename = KDiff3Shell::getParser()->value("output");

        if(m_outputFilename.isEmpty())
            m_outputFilename = KDiff3Shell::getParser()->value("out");

        if(!m_outputFilename.isEmpty())
            m_outputFilename = FileAccess(m_outputFilename, true).absoluteFilePath();

        if(m_bAutoMode && m_outputFilename.isEmpty())
Michael Reeves's avatar
Michael Reeves committed
242
        {
243
            if(m_bAutoFlag)
Michael Reeves's avatar
Michael Reeves committed
244
            {
245
                QTextStream(stderr) << i18n("Option --auto used, but no output file specified.") << "\n";
246 247
            }
            m_bAutoMode = false;
Michael Reeves's avatar
Michael Reeves committed
248
        }
249 250

        if(m_outputFilename.isEmpty() && KDiff3Shell::getParser()->isSet("merge"))
Michael Reeves's avatar
Michael Reeves committed
251
        {
252 253 254
            m_outputFilename = "unnamed.txt";
            m_bDefaultFilename = true;
        }
255 256
        else
        {
257
            m_bDefaultFilename = false;
258
        }
Michael Reeves's avatar
Michael Reeves committed
259

260 261
        g_bAutoSolve = !KDiff3Shell::getParser()->isSet("qall"); // Note that this is effective only once.
        QStringList args = KDiff3Shell::getParser()->positionalArguments();
Michael Reeves's avatar
Michael Reeves committed
262

263 264 265 266 267 268 269 270 271 272 273
        m_sd1.setFilename(KDiff3Shell::getParser()->value("base"));
        if(m_sd1.isEmpty()) {
            if(args.count() > 0) m_sd1.setFilename(args[0]); // args->arg(0)
            if(args.count() > 1) m_sd2.setFilename(args[1]);
            if(args.count() > 2) m_sd3.setFilename(args[2]);
        }
        else
        {
            if(args.count() > 0) m_sd2.setFilename(args[0]);
            if(args.count() > 1) m_sd3.setFilename(args[1]);
        }
274 275 276
        //Set m_bDirCompare flag
        m_bDirCompare = FileAccess(m_sd1.getFilename()).isDir();

Michael Reeves's avatar
Michael Reeves committed
277
        QStringList aliasList = KDiff3Shell::getParser()->values( "fname" );
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
        QStringList::Iterator ali = aliasList.begin();

        QString an1 = KDiff3Shell::getParser()->value("L1");
        if(!an1.isEmpty()) {
            m_sd1.setAliasName(an1);
        }
        else if(ali != aliasList.end())
        {
            m_sd1.setAliasName(*ali);
            ++ali;
        }

        QString an2 = KDiff3Shell::getParser()->value("L2");
        if(!an2.isEmpty()) {
            m_sd2.setAliasName(an2);
        }
        else if(ali != aliasList.end())
        {
            m_sd2.setAliasName(*ali);
            ++ali;
Michael Reeves's avatar
Michael Reeves committed
298 299
        }

300 301 302
        QString an3 = KDiff3Shell::getParser()->value("L3");
        if(!an3.isEmpty()) {
            m_sd3.setAliasName(an3);
Michael Reeves's avatar
Michael Reeves committed
303
        }
304 305 306
        else if(ali != aliasList.end())
        {
            m_sd3.setAliasName(*ali);
Michael Reeves's avatar
Michael Reeves committed
307 308 309
            ++ali;
        }
    }
310 311
    else
    {
Michael Reeves's avatar
Michael Reeves committed
312
        m_bDefaultFilename = false;
313 314
        g_bAutoSolve = false;
    }
315
    g_pProgressDialog->setStayHidden(m_bAutoMode);
Michael Reeves's avatar
Michael Reeves committed
316 317 318

    ///////////////////////////////////////////////////////////////////
    // call inits to invoke all other construction parts
319
    initActions(actionCollection());
Michael Reeves's avatar
Michael Reeves committed
320 321
    MergeResultWindow::initActions(actionCollection());

Michael Reeves's avatar
Michael Reeves committed
322 323
    initStatusBar();

324 325
    m_pFindDialog = new FindDialog(this);
    connect(m_pFindDialog, &FindDialog::findNext, this, &KDiff3App::slotEditFindNext);
Michael Reeves's avatar
Michael Reeves committed
326

327 328 329 330 331 332 333
    autoAdvance->setChecked(m_pOptions->m_bAutoAdvance);
    showWhiteSpaceCharacters->setChecked(m_pOptions->m_bShowWhiteSpaceCharacters);
    showWhiteSpace->setChecked(m_pOptions->m_bShowWhiteSpace);
    showWhiteSpaceCharacters->setEnabled(m_pOptions->m_bShowWhiteSpace);
    showLineNumbers->setChecked(m_pOptions->m_bShowLineNumbers);
    wordWrap->setChecked(m_pOptions->m_bWordWrap);
    if(!isPart())
Michael Reeves's avatar
Michael Reeves committed
334
    {
335
        viewStatusBar->setChecked(m_pOptions->m_bShowStatusBar);
Michael Reeves's avatar
Michael Reeves committed
336
        slotViewStatusBar();
337

338 339 340 341 342
        KToolBar *mainToolBar = toolBar(MAIN_TOOLBAR_NAME);
        if(mainToolBar != nullptr){
            mainToolBar->mainWindow()->addToolBar(m_pOptions->m_toolBarPos, mainToolBar);
        }
        //   TODO restore window size/pos?
Michael Reeves's avatar
Michael Reeves committed
343 344 345 346 347 348 349 350 351 352 353 354
        /*      QSize size = m_pOptions->m_geometry;
              QPoint pos = m_pOptions->m_position;
              if(!size.isEmpty())
              {
                 m_pKDiff3Shell->resize( size );
                 QRect visibleRect = QRect( pos, size ) & QApplication::desktop()->rect();
                 if ( visibleRect.width()>100 && visibleRect.height()>100 )
                    m_pKDiff3Shell->move( pos );
              }*/
    }
    slotRefresh();

Michael Reeves's avatar
Cleanup  
Michael Reeves committed
355
    m_pMainSplitter = this;
356 357 358 359 360 361
    m_pMainSplitter->setOrientation(Qt::Vertical);
    //   setCentralWidget( m_pMainSplitter );
    m_pDirectoryMergeSplitter = new QSplitter(m_pMainSplitter);
    m_pDirectoryMergeSplitter->setObjectName("DirectoryMergeSplitter");
    m_pMainSplitter->addWidget(m_pDirectoryMergeSplitter);
    m_pDirectoryMergeSplitter->setOrientation(Qt::Horizontal);
Michael Reeves's avatar
Michael Reeves committed
362
    m_pDirectoryMergeWindow = new DirectoryMergeWindow(m_pDirectoryMergeSplitter, m_pOptions);
363 364 365 366 367 368 369 370 371
    m_pDirectoryMergeSplitter->addWidget(m_pDirectoryMergeWindow);
    m_pDirectoryMergeInfo = new DirectoryMergeInfo(m_pDirectoryMergeSplitter);
    m_pDirectoryMergeWindow->setDirectoryMergeInfo(m_pDirectoryMergeInfo);
    m_pDirectoryMergeSplitter->addWidget(m_pDirectoryMergeInfo);

    connect(m_pDirectoryMergeWindow, &DirectoryMergeWindow::startDiffMerge, this, &KDiff3App::slotFileOpen2);
    connect(m_pDirectoryMergeWindow->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KDiff3App::slotUpdateAvailabilities);
    connect(m_pDirectoryMergeWindow->selectionModel(), &QItemSelectionModel::currentChanged, this, &KDiff3App::slotUpdateAvailabilities);
    connect(m_pDirectoryMergeWindow, &DirectoryMergeWindow::checkIfCanContinue, this, &KDiff3App::slotCheckIfCanContinue);
372
    connect(m_pDirectoryMergeWindow, static_cast<void (DirectoryMergeWindow::*) (void)>(&DirectoryMergeWindow::updateAvailabilities), this, &KDiff3App::slotUpdateAvailabilities);
373
    connect(m_pDirectoryMergeWindow, &DirectoryMergeWindow::statusBarMessage, this, &KDiff3App::slotStatusMsg);
374
    connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &KDiff3App::slotClipboardChanged);
375 376
    m_pDirectoryMergeWindow->initDirectoryMergeActions(this, actionCollection());

377
    delete KDiff3Shell::getParser();
378

Michael Reeves's avatar
Michael Reeves committed
379
    if(m_pKDiff3Shell == nullptr) {
380
        completeInit(QString());
Michael Reeves's avatar
Michael Reeves committed
381
    }
Joachim Eibl's avatar
Joachim Eibl committed
382 383
}

384
void KDiff3App::completeInit(const QString& fn1, const QString& fn2, const QString& fn3)
Michael Reeves's avatar
Michael Reeves committed
385
{
Michael Reeves's avatar
Michael Reeves committed
386
    if(m_pKDiff3Shell != nullptr)
387
    {
Michael Reeves's avatar
Michael Reeves committed
388 389
        QSize size = m_pOptions->m_geometry;
        QPoint pos = m_pOptions->m_position;
390
        if(!size.isEmpty())
Michael Reeves's avatar
Michael Reeves committed
391
        {
392
            m_pKDiff3Shell->resize(size);
Michael Reeves's avatar
Michael Reeves committed
393

394 395 396 397
            QRect visibleRect = QRect(pos, size) & QApplication::desktop()->rect();
            if(visibleRect.width() > 100 && visibleRect.height() > 100)
                m_pKDiff3Shell->move(pos);
            if(!m_bAutoMode)
Michael Reeves's avatar
Michael Reeves committed
398
            {
399
                //Here we want the extra setup showMaximized does since the window has not be shown before
400
                if(m_pOptions->m_bMaximised)
401
                    m_pKDiff3Shell->showMaximized();// krazy:exclude=qmethods
Michael Reeves's avatar
Michael Reeves committed
402 403 404 405 406
                else
                    m_pKDiff3Shell->show();
            }
        }
    }
407 408
    if(!fn1.isEmpty()) {
        m_sd1.setFilename(fn1);
Michael Reeves's avatar
Michael Reeves committed
409
    }
410 411
    if(!fn2.isEmpty()) {
        m_sd2.setFilename(fn2);
Michael Reeves's avatar
Michael Reeves committed
412
    }
413 414
    if(!fn3.isEmpty()) {
        m_sd3.setFilename(fn3);
Michael Reeves's avatar
Michael Reeves committed
415 416
    }

417 418
    //should not happen now.
    Q_ASSERT(m_bDirCompare == FileAccess(m_sd1.getFilename()).isDir());
419
    bool bSuccess = improveFilenames(false);
Michael Reeves's avatar
Michael Reeves committed
420

421
    if(m_bAutoFlag && m_bAutoMode && m_bDirCompare)
Michael Reeves's avatar
Michael Reeves committed
422
    {
423
        QTextStream(stderr) << i18n("Option --auto ignored for directory comparison.") << "\n";
Michael Reeves's avatar
Michael Reeves committed
424 425
        m_bAutoMode = false;
    }
426
    if(!m_bDirCompare)
Michael Reeves's avatar
Michael Reeves committed
427
    {
Michael Reeves's avatar
Michael Reeves committed
428 429
        m_pDirectoryMergeSplitter->hide();

Michael Reeves's avatar
Michael Reeves committed
430
        mainInit();
431
        if(m_bAutoMode)
Michael Reeves's avatar
Michael Reeves committed
432
        {
Michael Reeves's avatar
Michael Reeves committed
433
            SourceData* pSD = nullptr;
434
            if(m_sd3.isEmpty()) {
Michael Reeves's avatar
Michael Reeves committed
435
                if(m_totalDiffStatus.isBinaryEqualAB()) {
Michael Reeves's avatar
Michael Reeves committed
436 437 438
                    pSD = &m_sd1;
                }
            }
439 440
            else
            {
Michael Reeves's avatar
Michael Reeves committed
441
                if(m_totalDiffStatus.isBinaryEqualBC()) {
442
                    pSD = &m_sd3; // B==C (assume A is old)
Michael Reeves's avatar
Michael Reeves committed
443
                }
Michael Reeves's avatar
Michael Reeves committed
444
                else if(m_totalDiffStatus.isBinaryEqualAB())
445 446 447
                {
                    pSD = &m_sd3; // assuming C has changed
                }
Michael Reeves's avatar
Michael Reeves committed
448
                else if(m_totalDiffStatus.isBinaryEqualAC())
449 450
                {
                    pSD = &m_sd2; // assuming B has changed
Michael Reeves's avatar
Michael Reeves committed
451
                }
Joachim Eibl's avatar
Joachim Eibl committed
452
            }
Joachim Eibl's avatar
Joachim Eibl committed
453

Michael Reeves's avatar
Michael Reeves committed
454
            if(pSD != nullptr)
455
            {
Michael Reeves's avatar
Michael Reeves committed
456
                // Save this file directly, not via the merge result window.
457 458
                FileAccess fa(m_outputFilename);
                if(m_pOptions->m_bDmCreateBakFiles && fa.exists())
Michael Reeves's avatar
Michael Reeves committed
459
                {
Michael Reeves's avatar
cleanup  
Michael Reeves committed
460
                    fa.createBackup(".orig");
Michael Reeves's avatar
Michael Reeves committed
461 462
                }

463 464 465 466 467
                bSuccess = pSD->saveNormalDataAs(m_outputFilename);
                if(bSuccess)
                    ::exit(0);
                else
                    KMessageBox::error(this, i18n("Saving failed."));
Michael Reeves's avatar
Michael Reeves committed
468
            }
469
            else if(m_pMergeResultWindow->getNrOfUnsolvedConflicts() == 0)
Michael Reeves's avatar
Michael Reeves committed
470
            {
Michael Reeves's avatar
Michael Reeves committed
471
                bSuccess = m_pMergeResultWindow->saveDocument(m_pMergeResultWindowTitle->getFileName(), m_pMergeResultWindowTitle->getEncoding(), m_pMergeResultWindowTitle->getLineEndStyle());
472
                if(bSuccess) ::exit(0);
Michael Reeves's avatar
Michael Reeves committed
473 474 475 476 477
            }
        }
    }
    m_bAutoMode = false;

478
    if(m_pKDiff3Shell)
Michael Reeves's avatar
Michael Reeves committed
479
    {
480
        if(m_pOptions->m_bMaximised)
Michael Reeves's avatar
Michael Reeves committed
481 482
            //We want showMaximized here as the window has never been shown.
            m_pKDiff3Shell->showMaximized();// krazy:exclude=qmethods
Michael Reeves's avatar
Michael Reeves committed
483 484 485 486
        else
            m_pKDiff3Shell->show();
    }

487
    g_pProgressDialog->setStayHidden(false);
Michael Reeves's avatar
Michael Reeves committed
488

Michael Reeves's avatar
Michael Reeves committed
489
    if(statusBar() != nullptr)
490
        statusBar()->setSizeGripEnabled(true);
Michael Reeves's avatar
Michael Reeves committed
491 492 493 494 495

    slotClipboardChanged(); // For initialisation.

    slotUpdateAvailabilities();

Michael Reeves's avatar
Michael Reeves committed
496
    if(!m_bDirCompare && m_pKDiff3Shell != nullptr)
Michael Reeves's avatar
Michael Reeves committed
497
    {
Michael Reeves's avatar
Michael Reeves committed
498
        bool bFileOpenError = false;
499 500 501
        if((!m_sd1.isEmpty() && !m_sd1.hasData()) ||
           (!m_sd2.isEmpty() && !m_sd2.hasData()) ||
           (!m_sd3.isEmpty() && !m_sd3.hasData()))
Michael Reeves's avatar
Michael Reeves committed
502
        {
503
            QString text(i18n("Opening of these files failed:"));
Michael Reeves's avatar
Michael Reeves committed
504
            text += "\n\n";
505
            if(!m_sd1.isEmpty() && !m_sd1.hasData())
506
                text += " - " + m_sd1.getAliasName() + '\n';
507
            if(!m_sd2.isEmpty() && !m_sd2.hasData())
508
                text += " - " + m_sd2.getAliasName() + '\n';
509
            if(!m_sd3.isEmpty() && !m_sd3.hasData())
510
                text += " - " + m_sd3.getAliasName() + '\n';
Michael Reeves's avatar
Michael Reeves committed
511

512
            KMessageBox::sorry(this, text, i18n("File Open Error"));
Michael Reeves's avatar
Michael Reeves committed
513 514 515
            bFileOpenError = true;
        }

516
        if(m_sd1.isEmpty() || m_sd2.isEmpty() || bFileOpenError)
Michael Reeves's avatar
Michael Reeves committed
517 518
            slotFileOpen();
    }
519
    else if(!bSuccess) // Directory open failed
Michael Reeves's avatar
Michael Reeves committed
520
    {
Michael Reeves's avatar
Michael Reeves committed
521 522
        slotFileOpen();
    }
Joachim Eibl's avatar
Joachim Eibl committed
523 524
}

Michael Reeves's avatar
Michael Reeves committed
525 526
KDiff3App::~KDiff3App()
{
Joachim Eibl's avatar
Joachim Eibl committed
527 528
}

Joachim Eibl's avatar
0.9.93  
Joachim Eibl committed
529 530 531 532
/**
 * Helper function used to create actions into the ac collection
 */

533
void KDiff3App::initActions(KActionCollection* ac)
Michael Reeves's avatar
Michael Reeves committed
534
{
Michael Reeves's avatar
Michael Reeves committed
535 536
    if(ac == nullptr){
        KMessageBox::error(nullptr, "actionCollection==0");
Yuri Chornoivan's avatar
Yuri Chornoivan committed
537
        exit(-1);//we cannot recover from this.
538
    }
Michael Reeves's avatar
Michael Reeves committed
539
    fileOpen = KStandardAction::open(this, &KDiff3App::slotFileOpen, ac);
540
    fileOpen->setStatusTip(i18n("Opens documents for comparison..."));
Joachim Eibl's avatar
0.9.93  
Joachim Eibl committed
541

542
    fileReload = GuiUtils::createAction<QAction>(i18n("Reload"), QKeySequence(QKeySequence::Refresh), this, &KDiff3App::slotReload, ac, QLatin1String("file_reload"));
Joachim Eibl's avatar
0.9.93  
Joachim Eibl committed
543

Michael Reeves's avatar
Michael Reeves committed
544
    fileSave = KStandardAction::save(this, &KDiff3App::slotFileSave, ac);
545
    fileSave->setStatusTip(i18n("Saves the merge result. All conflicts must be solved!"));
Michael Reeves's avatar
Michael Reeves committed
546
    fileSaveAs = KStandardAction::saveAs(this, &KDiff3App::slotFileSaveAs, ac);
547
    fileSaveAs->setStatusTip(i18n("Saves the current document as..."));
Joachim Eibl's avatar
Joachim Eibl committed
548
#ifndef QT_NO_PRINTER
Michael Reeves's avatar
Michael Reeves committed
549
    filePrint = KStandardAction::print(this, &KDiff3App::slotFilePrint, ac);
550
    filePrint->setStatusTip(i18n("Print the differences"));
Joachim Eibl's avatar
Joachim Eibl committed
551
#endif
Michael Reeves's avatar
Michael Reeves committed
552
    fileQuit = KStandardAction::quit(this, &KDiff3App::slotFileQuit, ac);
553
    fileQuit->setStatusTip(i18n("Quits the application"));
Michael Reeves's avatar
Michael Reeves committed
554 555
    editCut = KStandardAction::cut(this, &KDiff3App::slotEditCut, ac);
    editCut->setShortcuts(QKeySequence::Cut);
556
    editCut->setStatusTip(i18n("Cuts the selected section and puts it to the clipboard"));
Michael Reeves's avatar
Michael Reeves committed
557 558
    editCopy = KStandardAction::copy(this, &KDiff3App::slotEditCopy, ac);
    editCopy->setShortcut(QKeySequence::Copy);
559
    editCopy->setStatusTip(i18n("Copies the selected section to the clipboard"));
Michael Reeves's avatar
Michael Reeves committed
560
    editPaste = KStandardAction::paste(this, &KDiff3App::slotEditPaste, ac);
561
    editPaste->setStatusTip(i18n("Pastes the clipboard contents to current position"));
Michael Reeves's avatar
Michael Reeves committed
562 563
    editCut->setShortcut(QKeySequence::Paste);
    editSelectAll = KStandardAction::selectAll(this, &KDiff3App::slotEditSelectAll, ac);
564
    editSelectAll->setStatusTip(i18n("Select everything in current window"));
Michael Reeves's avatar
Michael Reeves committed
565 566
    editFind = KStandardAction::find(this, &KDiff3App::slotEditFind, ac);
    editFind->setShortcut(QKeySequence::Find);
567
    editFind->setStatusTip(i18n("Search for a string"));
Michael Reeves's avatar
Michael Reeves committed
568
    editFindNext = KStandardAction::findNext(this, &KDiff3App::slotEditFindNext, ac);
569
    editFindNext->setStatusTip(i18n("Search again for the string"));
Michael Reeves's avatar
Michael Reeves committed
570
    /*   FIXME figure out how to implement this action
Michael Reeves's avatar
Michael Reeves committed
571
       viewToolBar = KStandardAction::showToolbar(this, &KDiff3App::slotViewToolBar, ac);
Michael Reeves's avatar
Michael Reeves committed
572
       viewToolBar->setStatusTip(i18n("Enables/disables the toolbar")); */
Michael Reeves's avatar
Michael Reeves committed
573
    viewStatusBar = KStandardAction::showStatusbar(this, &KDiff3App::slotViewStatusBar, ac);
574
    viewStatusBar->setStatusTip(i18n("Enables/disables the statusbar"));
Michael Reeves's avatar
Michael Reeves committed
575 576
    KStandardAction::keyBindings(this, &KDiff3App::slotConfigureKeys, ac);
    QAction* pAction = KStandardAction::preferences(this, &KDiff3App::slotConfigure, ac);
577 578
    if(isPart())
        pAction->setText(i18n("Configure KDiff3..."));
Joachim Eibl's avatar
Joachim Eibl committed
579

580
#include "xpm/autoadvance.xpm"
Joachim Eibl's avatar
Joachim Eibl committed
581 582 583
#include "xpm/currentpos.xpm"
#include "xpm/down1arrow.xpm"
#include "xpm/down2arrow.xpm"
584
#include "xpm/downend.xpm"
Joachim Eibl's avatar
Joachim Eibl committed
585 586 587
#include "xpm/iconA.xpm"
#include "xpm/iconB.xpm"
#include "xpm/iconC.xpm"
588 589 590
#include "xpm/nextunsolved.xpm"
#include "xpm/prevunsolved.xpm"
#include "xpm/showlinenumbers.xpm"
Joachim Eibl's avatar
Joachim Eibl committed
591
#include "xpm/showwhitespace.xpm"
Joachim Eibl's avatar
Joachim Eibl committed
592
#include "xpm/showwhitespacechars.xpm"
593 594 595 596
#include "xpm/up1arrow.xpm"
#include "xpm/up2arrow.xpm"
#include "xpm/upend.xpm"

597
    goCurrent = GuiUtils::createAction<QAction>(i18n("Go to Current Delta"), QIcon(QPixmap(currentpos)), i18n("Current\nDelta"), QKeySequence(Qt::CTRL + Qt::Key_Space), this, &KDiff3App::slotGoCurrent, ac, "go_current");
598

599
    goTop = GuiUtils::createAction<QAction>(i18n("Go to First Delta"), QIcon(QPixmap(upend)), i18n("First\nDelta"), this, &KDiff3App::slotGoTop, ac, "go_top");
600

601
    goBottom = GuiUtils::createAction<QAction>(i18n("Go to Last Delta"), QIcon(QPixmap(downend)), i18n("Last\nDelta"), this, &KDiff3App::slotGoBottom, ac, "go_bottom");
602 603 604

    QString omitsWhitespace = ".\n" + i18n("(Skips white space differences when \"Show White Space\" is disabled.)");
    QString includeWhitespace = ".\n" + i18n("(Does not skip white space differences even when \"Show White Space\" is disabled.)");
605
    goPrevDelta = GuiUtils::createAction<QAction>(i18n("Go to Previous Delta"), QIcon(QPixmap(up1arrow)), i18n("Prev\nDelta"), QKeySequence(Qt::CTRL + Qt::Key_Up), this, &KDiff3App::slotGoPrevDelta, ac, "go_prev_delta");
606
    goPrevDelta->setToolTip(goPrevDelta->text() + omitsWhitespace);
607
    goNextDelta = GuiUtils::createAction<QAction>(i18n("Go to Next Delta"), QIcon(QPixmap(down1arrow)), i18n("Next\nDelta"), QKeySequence(Qt::CTRL + Qt::Key_Down), this, &KDiff3App::slotGoNextDelta, ac, "go_next_delta");
608
    goNextDelta->setToolTip(goNextDelta->text() + omitsWhitespace);
609
    goPrevConflict = GuiUtils::createAction<QAction>(i18n("Go to Previous Conflict"), QIcon(QPixmap(up2arrow)), i18n("Prev\nConflict"), QKeySequence(Qt::CTRL + Qt::Key_PageUp), this, &KDiff3App::slotGoPrevConflict, ac, "go_prev_conflict");
610
    goPrevConflict->setToolTip(goPrevConflict->text() + omitsWhitespace);
611
    goNextConflict = GuiUtils::createAction<QAction>(i18n("Go to Next Conflict"), QIcon(QPixmap(down2arrow)), i18n("Next\nConflict"), QKeySequence(Qt::CTRL + Qt::Key_PageDown), this, &KDiff3App::slotGoNextConflict, ac, "go_next_conflict");
612
    goNextConflict->setToolTip(goNextConflict->text() + omitsWhitespace);
613
    goPrevUnsolvedConflict = GuiUtils::createAction<QAction>(i18n("Go to Previous Unsolved Conflict"), QIcon(QPixmap(prevunsolved)), i18n("Prev\nUnsolved"), this, &KDiff3App::slotGoPrevUnsolvedConflict, ac, "go_prev_unsolved_conflict");
614
    goPrevUnsolvedConflict->setToolTip(goPrevUnsolvedConflict->text() + includeWhitespace);
615
    goNextUnsolvedConflict = GuiUtils::createAction<QAction>(i18n("Go to Next Unsolved Conflict"), QIcon(QPixmap(nextunsolved)), i18n("Next\nUnsolved"), this, &KDiff3App::slotGoNextUnsolvedConflict, ac, "go_next_unsolved_conflict");
616
    goNextUnsolvedConflict->setToolTip(goNextUnsolvedConflict->text() + includeWhitespace);
617 618 619 620 621 622 623 624 625
    chooseA = GuiUtils::createAction<KToggleAction>(i18n("Select Line(s) From A"), QIcon(QPixmap(iconA)), i18n("Choose\nA"), QKeySequence(Qt::CTRL + Qt::Key_1), this, &KDiff3App::slotChooseA, ac, "merge_choose_a");
    chooseB = GuiUtils::createAction<KToggleAction>(i18n("Select Line(s) From B"), QIcon(QPixmap(iconB)), i18n("Choose\nB"), QKeySequence(Qt::CTRL + Qt::Key_2), this, &KDiff3App::slotChooseB, ac, "merge_choose_b");
    chooseC = GuiUtils::createAction<KToggleAction>(i18n("Select Line(s) From C"), QIcon(QPixmap(iconC)), i18n("Choose\nC"), QKeySequence(Qt::CTRL + Qt::Key_3), this, &KDiff3App::slotChooseC, ac, "merge_choose_c");
    autoAdvance = GuiUtils::createAction<KToggleAction>(i18n("Automatically Go to Next Unsolved Conflict After Source Selection"), QIcon(QPixmap(autoadvance)), i18n("Auto\nNext"), this, &KDiff3App::slotAutoAdvanceToggled, ac, "merge_autoadvance");

    showWhiteSpaceCharacters = GuiUtils::createAction<KToggleAction>(i18n("Show Space && Tabulator Characters"), QIcon(QPixmap(showwhitespacechars)), i18n("White\nCharacters"), this, &KDiff3App::slotShowWhiteSpaceToggled, ac, "diff_show_whitespace_characters");
    showWhiteSpace = GuiUtils::createAction<KToggleAction>(i18n("Show White Space"), QIcon(QPixmap(showwhitespace)), i18n("White\nDeltas"), this, &KDiff3App::slotShowWhiteSpaceToggled, ac, "diff_show_whitespace");

    showLineNumbers = GuiUtils::createAction<KToggleAction>(i18n("Show Line Numbers"), QIcon(QPixmap(showlinenumbers)), i18n("Line\nNumbers"), this, &KDiff3App::slotShowLineNumbersToggled, ac, "diff_showlinenumbers");
Michael Reeves's avatar
Michael Reeves committed
626

627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
    autoSolve = GuiUtils::createAction<QAction>(i18n("Automatically Solve Simple Conflicts"), this, &KDiff3App::slotAutoSolve, ac, "merge_autosolve");
    unsolve = GuiUtils::createAction<QAction>(i18n("Set Deltas to Conflicts"), this, &KDiff3App::slotUnsolve, ac, "merge_autounsolve");
    mergeRegExp = GuiUtils::createAction<QAction>(i18n("Run Regular Expression Auto Merge"), this, &KDiff3App::slotRegExpAutoMerge, ac, "merge_regexp_automerge");
    mergeHistory = GuiUtils::createAction<QAction>(i18n("Automatically Solve History Conflicts"), this, &KDiff3App::slotMergeHistory, ac, "merge_versioncontrol_history");
    splitDiff = GuiUtils::createAction<QAction>(i18n("Split Diff At Selection"), this, &KDiff3App::slotSplitDiff, ac, "merge_splitdiff");
    joinDiffs = GuiUtils::createAction<QAction>(i18n("Join Selected Diffs"), this, &KDiff3App::slotJoinDiffs, ac, "merge_joindiffs");

    showWindowA = GuiUtils::createAction<KToggleAction>(i18n("Show Window A"), this, &KDiff3App::slotShowWindowAToggled, ac, "win_show_a");
    showWindowB = GuiUtils::createAction<KToggleAction>(i18n("Show Window B"), this, &KDiff3App::slotShowWindowBToggled, ac, "win_show_b");
    showWindowC = GuiUtils::createAction<KToggleAction>(i18n("Show Window C"), this, &KDiff3App::slotShowWindowCToggled, ac, "win_show_c");

    overviewModeNormal = GuiUtils::createAction<KToggleAction>(i18n("Normal Overview"), this, &KDiff3App::slotOverviewNormal, ac, "diff_overview_normal");
    overviewModeAB = GuiUtils::createAction<KToggleAction>(i18n("A vs. B Overview"), this, &KDiff3App::slotOverviewAB, ac, "diff_overview_ab");
    overviewModeAC = GuiUtils::createAction<KToggleAction>(i18n("A vs. C Overview"), this, &KDiff3App::slotOverviewAC, ac, "diff_overview_ac");
    overviewModeBC = GuiUtils::createAction<KToggleAction>(i18n("B vs. C Overview"), this, &KDiff3App::slotOverviewBC, ac, "diff_overview_bc");
    wordWrap = GuiUtils::createAction<KToggleAction>(i18n("Word Wrap Diff Windows"), this, &KDiff3App::slotWordWrapToggled, ac, "diff_wordwrap");
    addManualDiffHelp = GuiUtils::createAction<QAction>(i18n("Add Manual Diff Alignment"), QKeySequence(Qt::CTRL + Qt::Key_Y), this, &KDiff3App::slotAddManualDiffHelp, ac, "diff_add_manual_diff_help");
    clearManualDiffHelpList = GuiUtils::createAction<QAction>(i18n("Clear All Manual Diff Alignments"), QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Y), this, &KDiff3App::slotClearManualDiffHelpList, ac, "diff_clear_manual_diff_help_list");

    winFocusNext = GuiUtils::createAction<QAction>(i18n("Focus Next Window"), QKeySequence(Qt::ALT + Qt::Key_Right), this, &KDiff3App::slotWinFocusNext, ac, "win_focus_next");
    winFocusPrev = GuiUtils::createAction<QAction>(i18n("Focus Prev Window"), QKeySequence(Qt::ALT + Qt::Key_Left), this, &KDiff3App::slotWinFocusPrev, ac, "win_focus_prev");
    winToggleSplitOrientation = GuiUtils::createAction<QAction>(i18n("Toggle Split Orientation"), this, &KDiff3App::slotWinToggleSplitterOrientation, ac, "win_toggle_split_orientation");

    dirShowBoth = GuiUtils::createAction<KToggleAction>(i18n("Dir && Text Split Screen View"), this, &KDiff3App::slotDirShowBoth, ac, "win_dir_show_both");
651
    dirShowBoth->setChecked(true);
652
    dirViewToggle = GuiUtils::createAction<QAction>(i18n("Toggle Between Dir && Text View"), this, &KDiff3App::slotDirViewToggle, ac, "win_dir_view_toggle");
Michael Reeves's avatar
Michael Reeves committed
653

654
    m_pMergeEditorPopupMenu = new QMenu(this);
Michael Reeves's avatar
Michael Reeves committed
655 656 657
    /*   chooseA->plug( m_pMergeEditorPopupMenu );
       chooseB->plug( m_pMergeEditorPopupMenu );
       chooseC->plug( m_pMergeEditorPopupMenu );*/
658 659 660
    m_pMergeEditorPopupMenu->addAction(chooseA);
    m_pMergeEditorPopupMenu->addAction(chooseB);
    m_pMergeEditorPopupMenu->addAction(chooseC);
Joachim Eibl's avatar
Joachim Eibl committed
661 662
}

663
void KDiff3App::showPopupMenu(const QPoint& point)
Michael Reeves's avatar
Michael Reeves committed
664
{
665
    m_pMergeEditorPopupMenu->popup(point);
Joachim Eibl's avatar
Joachim Eibl committed
666 667
}

Michael Reeves's avatar
Michael Reeves committed
668 669
void KDiff3App::initStatusBar()
{
Michael Reeves's avatar
Michael Reeves committed
670 671
    ///////////////////////////////////////////////////////////////////
    // STATUSBAR
Michael Reeves's avatar
Michael Reeves committed
672
    if(statusBar() != nullptr)
673
        statusBar()->showMessage(i18n("Ready."));
Joachim Eibl's avatar
Joachim Eibl committed
674 675
}

676
void KDiff3App::saveOptions(KSharedConfigPtr config)
Michael Reeves's avatar
Michael Reeves committed
677
{
678 679 680 681
    if(!m_bAutoMode)
    {
        if(!isPart())
        {
Michael Reeves's avatar
Michael Reeves committed
682
            m_pOptions->m_bMaximised = m_pKDiff3Shell->isMaximized();
683
            if(!m_pKDiff3Shell->isMaximized() && m_pKDiff3Shell->isVisible())
Michael Reeves's avatar
Michael Reeves committed
684
            {
Michael Reeves's avatar
Michael Reeves committed
685 686 687 688 689 690 691 692
                m_pOptions->m_geometry = m_pKDiff3Shell->size();
                m_pOptions->m_position = m_pKDiff3Shell->pos();
            }
            /*  TODO change this option as now KToolbar uses QToolbar positioning style
                     if ( toolBar(MAIN_TOOLBAR_NAME)!=0 )
                        m_pOptionDialog->m_toolBarPos = (int) toolBar(MAIN_TOOLBAR_NAME)->allowedAreas();*/
        }

Michael Reeves's avatar
Michael Reeves committed
693
        m_pOptionDialog->saveOptions(std::move(config));
Michael Reeves's avatar
Michael Reeves committed
694
    }
Joachim Eibl's avatar
Joachim Eibl committed
695 696
}

Michael Reeves's avatar
Michael Reeves committed
697 698
bool KDiff3App::queryClose()
{
699 700 701 702 703
    saveOptions(KSharedConfig::openConfig());

    if(m_bOutputModified)
    {
        int result = KMessageBox::warningYesNoCancel(this,
Pino Toscano's avatar
Pino Toscano committed
704
                                                     i18n("The merge result has not been saved."),
705 706 707 708
                                                     i18n("Warning"),
                                                     KGuiItem(i18n("Save && Quit")),
                                                     KGuiItem(i18n("Quit Without Saving")));
        if(result == KMessageBox::Cancel)
Joachim Eibl's avatar
Joachim Eibl committed
709
            return false;
710 711
        else if(result == KMessageBox::Yes)
        {
Michael Reeves's avatar
Michael Reeves committed
712
            slotFileSave();
713
            if(m_bOutputModified)
Michael Reeves's avatar
Michael Reeves committed
714
            {
715
                KMessageBox::sorry(this, i18n("Saving the merge result failed."), i18n("Warning"));
Michael Reeves's avatar
Michael Reeves committed
716 717 718 719 720 721 722
                return false;
            }
        }
    }

    m_bOutputModified = false;

723
    if(m_pDirectoryMergeWindow->isDirectoryMergeInProgress())
Michael Reeves's avatar
Michael Reeves committed
724
    {
725 726 727 728 729 730
        int result = KMessageBox::warningYesNo(this,
                                               i18n("You are currently doing a directory merge. Are you sure, you want to abort?"),
                                               i18n("Warning"),
                                               KStandardGuiItem::quit(),
                                               KStandardGuiItem::cont() /* i18n("Continue Merging") */);
        if(result != KMessageBox::Yes)
Michael Reeves's avatar
Michael Reeves committed
731 732 733 734
            return false;
    }

    return true;
Joachim Eibl's avatar
Joachim Eibl committed
735 736 737 738 739 740
}

/////////////////////////////////////////////////////////////////////
// SLOT IMPLEMENTATION
/////////////////////////////////////////////////////////////////////

Michael Reeves's avatar
Michael Reeves committed
741 742
void KDiff3App::slotFileSave()
{
743
    if(m_bDefaultFilename)
Michael Reeves's avatar
Michael Reeves committed
744
    {
Michael Reeves's avatar
Michael Reeves committed
745 746
        slotFileSaveAs();
    }
Michael Reeves's avatar
Michael Reeves committed
747 748
    else
    {
749
        slotStatusMsg(i18n("Saving file..."));
Michael Reeves's avatar
Michael Reeves committed
750

751 752
        bool bSuccess = m_pMergeResultWindow->saveDocument(m_outputFilename, m_pMergeResultWindowTitle->getEncoding(), m_pMergeResultWindowTitle->getLineEndStyle());
        if(bSuccess)
Michael Reeves's avatar
Michael Reeves committed
753
        {
Michael Reeves's avatar
Michael Reeves committed
754 755
            m_bFileSaved = true;
            m_bOutputModified = false;
756 757
            if(m_bDirCompare)
                m_pDirectoryMergeWindow->mergeResultSaved(m_outputFilename);
Michael Reeves's avatar
Michael Reeves committed
758 759
        }

760
        slotStatusMsg(i18n("Ready."));
Michael Reeves's avatar
Michael Reeves committed
761
    }
Joachim Eibl's avatar
Joachim Eibl committed
762 763
}

Michael Reeves's avatar
Michael Reeves committed
764 765
void KDiff3App::slotFileSaveAs()
{
766
    slotStatusMsg(i18n("Saving file with a new filename..."));
Michael Reeves's avatar
Michael Reeves committed
767

768
    QString s = QFileDialog::getSaveFileUrl(this, i18n("Save As..."), QUrl::fromLocalFile(QDir::currentPath())).url(QUrl::PreferLocalFile);
769
    if(!s.isEmpty()) {
Michael Reeves's avatar
Michael Reeves committed
770
        m_outputFilename = s;
771 772 773
        m_pMergeResultWindowTitle->setFileName(m_outputFilename);
        bool bSuccess = m_pMergeResultWindow->saveDocument(m_outputFilename, m_pMergeResultWindowTitle->getEncoding(), m_pMergeResultWindowTitle->getLineEndStyle());
        if(bSuccess)
Michael Reeves's avatar
Michael Reeves committed
774
        {
Michael Reeves's avatar
Michael Reeves committed
775
            m_bOutputModified = false;
776 777
            if(m_bDirCompare)
                m_pDirectoryMergeWindow->mergeResultSaved(m_outputFilename);
Michael Reeves's avatar
Michael Reeves committed
778 779 780 781 782 783
        }
        //setWindowTitle(url.fileName(),doc->isModified());

        m_bDefaultFilename = false;
    }

784
    slotStatusMsg(i18n("Ready."));
Joachim Eibl's avatar
Joachim Eibl committed
785 786
}

787
void printDiffTextWindow(RLPainter& painter, const QRect& view, const QString& headerText, DiffTextWindow* pDiffTextWindow, int line, int linesPerPage, const QColor &fgColor)
Michael Reeves's avatar
Michael Reeves committed
788
{
Michael Reeves's avatar
Michael Reeves committed
789
    QRect clipRect = view;
790 791 792
    clipRect.setTop(0);
    painter.setClipRect(clipRect);
    painter.translate(view.left(), 0);
Michael Reeves's avatar
Michael Reeves committed
793 794 795 796 797
    QFontMetrics fm = painter.fontMetrics();
    //if ( fm.width(headerText) > view.width() )
    {
        // A simple wrapline algorithm
        int l = 0;
798
        for(int p = 0; p < headerText.length();)
Michael Reeves's avatar
Michael Reeves committed
799
        {
800
            QString s = headerText.mid(p);
Michael Reeves's avatar
Michael Reeves committed
801
            int i;
802 803
            for(i = 2; i < s.length(); ++i)
                if(fm.width(s, i) > view.width())
Michael Reeves's avatar
Michael Reeves committed
804
                {
Michael Reeves's avatar
Michael Reeves committed
805 806 807 808
                    --i;
                    break;
                }
            //QString s2 = s.left(i);
809
            painter.drawText(0, l * fm.height() + fm.ascent(), s.left(i));
Michael Reeves's avatar
Michael Reeves committed
810 811 812
            p += i;
            ++l;
        }
813 814
        painter.setPen(fgColor);
        painter.drawLine(0, view.top() - 2, view.width(), view.top() - 2);
Michael Reeves's avatar
Michael Reeves committed
815 816
    }

817 818
    painter.translate(0, view.top());
    pDiffTextWindow->print(painter, view, line, linesPerPage);
Michael Reeves's avatar
Michael Reeves committed
819
    painter.resetMatrix();
Joachim Eibl's avatar
Joachim Eibl committed
820 821
}

Michael Reeves's avatar
Michael Reeves committed
822 823
void KDiff3App::slotFilePrint()
{
Michael Reeves's avatar
Michael Reeves committed
824
    if(m_pDiffTextWindow1 == nullptr)
Michael Reeves's avatar
Michael Reeves committed
825
        return;
Joachim Eibl's avatar
Joachim Eibl committed
826
#ifdef QT_NO_PRINTER
827
    slotStatusMsg(i18n("Printing not implemented."));
Michael Reeves's avatar
Michael Reeves committed
828
#else
Michael Reeves's avatar
Michael Reeves committed
829
    QPrinter printer;
830
    QPointer<QPrintDialog> printDialog=QPointer<QPrintDialog>(new QPrintDialog(&printer, this));
Michael Reeves's avatar
Michael Reeves committed
831

832 833
    LineRef firstSelectionD3LIdx = -1;
    LineRef lastSelectionD3LIdx = -1;
834

835
    m_pDiffTextWindow1->getSelectionRange(&firstSelectionD3LIdx, &lastSelectionD3LIdx, eD3LLineCoords);
836

837 838
    if(firstSelectionD3LIdx < 0 && m_pDiffTextWindow2) {
        m_pDiffTextWindow2->getSelectionRange(&firstSelectionD3LIdx, &lastSelectionD3LIdx, eD3LLineCoords);
Michael Reeves's avatar
Michael Reeves committed
839
    }
840 841
    if(firstSelectionD3LIdx < 0 && m_pDiffTextWindow3) {
        m_pDiffTextWindow3->getSelectionRange(&firstSelectionD3LIdx, &lastSelectionD3LIdx, eD3LLineCoords);
Michael Reeves's avatar
Michael Reeves committed
842
    }
Michael Reeves's avatar
Michael Reeves committed
843

844
    if(firstSelectionD3LIdx >= 0) {
845
        printDialog->addEnabledOption(QPrintDialog::PrintSelection);
Michael Reeves's avatar
Michael Reeves committed
846
        //printer.setOptionEnabled(QPrinter::PrintSelection,true);
847
        printDialog->setPrintRange(QAbstractPrintDialog::Selection);
Michael Reeves's avatar
Michael Reeves committed
848
    }
Joachim Eibl's avatar
Joachim Eibl committed
849

850
    if(firstSelectionD3LIdx == -1)
851
        printDialog->setPrintRange(QAbstractPrintDialog::AllPages);
Michael Reeves's avatar
Michael Reeves committed
852
    //printDialog.setMinMax(0,0);
853
    printDialog->setFromTo(0, 0);
Joachim Eibl's avatar
Joachim Eibl committed
854

Michael Reeves's avatar
Michael Reeves committed
855
    int currentFirstLine = m_pDiffTextWindow1->getFirstLine();
856
    int currentFirstD3LIdx = m_pDiffTextWindow1->convertLineToDiff3LineIdx(currentFirstLine);
Joachim Eibl's avatar
Joachim Eibl committed
857

Michael Reeves's avatar
Michael Reeves committed
858
    // do some printer initialization
859
    printer.setFullPage(false);
Joachim Eibl's avatar
Joachim Eibl committed
860

Michael Reeves's avatar
Michael Reeves committed
861
    // initialize the printer using the print dialog
862
    if(printDialog->exec() == QDialog::Accepted)
Michael Reeves's avatar
Michael Reeves committed
863
    {
864
        slotStatusMsg(i18n("Printing..."));
Michael Reeves's avatar
Michael Reeves committed
865
        // create a painter to paint on the printer object
866
        RLPainter painter(&printer, m_pOptions->m_bRightToLeftLanguage, width(), fontMetrics().width('W'));
Joachim Eibl's avatar
Joachim Eibl committed
867

Michael Reeves's avatar
Michael Reeves committed
868 869
        QPaintDevice* pPaintDevice = painter.device();
        int dpiy = pPaintDevice->logicalDpiY();
870
        int columnDistance = (int)((0.5 / 2.54) * dpiy); // 0.5 cm between the columns
Joachim Eibl's avatar
Joachim Eibl committed
871

Michael Reeves's avatar
Michael Reeves committed
872
        int columns = m_bTripleDiff ? 3 : 2;
873
        int columnWidth = (pPaintDevice->width() - (columns - 1) * columnDistance) / columns;
Joachim Eibl's avatar
Joachim Eibl committed
874

Michael Reeves's avatar
Michael Reeves committed
875
        QFont f = m_pOptions->m_font;
876 877
        f.setPointSizeF(f.pointSizeF() - 1); // Print with slightly smaller font.
        painter.setFont(f);
Michael Reeves's avatar
Michael Reeves committed
878
        QFontMetrics fm = painter.fontMetrics();
Joachim Eibl's avatar
Joachim Eibl committed
879

Michael Reeves's avatar