kdiff3.cpp 44 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

225
    m_bAutoFlag = false; //disable --auto option git hard codes this unwanted flag.
226
    m_bAutoMode = m_bAutoFlag || m_pOptions->m_bAutoSaveAndQuitOnMergeWithoutConflicts;
227 228 229 230 231 232 233 234 235 236
    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
237
        {
238
            if(m_bAutoFlag)
Michael Reeves's avatar
Michael Reeves committed
239
            {
240
                QTextStream(stderr) << i18n("Option --auto used, but no output file specified.") << "\n";
241 242
            }
            m_bAutoMode = false;
Michael Reeves's avatar
Michael Reeves committed
243
        }
244 245

        if(m_outputFilename.isEmpty() && KDiff3Shell::getParser()->isSet("merge"))
Michael Reeves's avatar
Michael Reeves committed
246
        {
247 248 249
            m_outputFilename = "unnamed.txt";
            m_bDefaultFilename = true;
        }
250 251
        else
        {
252
            m_bDefaultFilename = false;
253
        }
Michael Reeves's avatar
Michael Reeves committed
254

255 256
        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
257

258 259 260 261 262 263 264 265 266 267 268
        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]);
        }
269 270 271
        //Set m_bDirCompare flag
        m_bDirCompare = FileAccess(m_sd1.getFilename()).isDir();

272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
        QStringList aliasList; //KDiff3Shell::getParser()->values( "fname" );
        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
293 294
        }

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

    ///////////////////////////////////////////////////////////////////
    // call inits to invoke all other construction parts
314
    initActions(actionCollection());
Michael Reeves's avatar
Michael Reeves committed
315 316
    initStatusBar();

317 318
    m_pFindDialog = new FindDialog(this);
    connect(m_pFindDialog, &FindDialog::findNext, this, &KDiff3App::slotEditFindNext);
Michael Reeves's avatar
Michael Reeves committed
319

320 321 322 323 324 325 326
    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
327
    {
328
        viewStatusBar->setChecked(m_pOptions->m_bShowStatusBar);
Michael Reeves's avatar
Michael Reeves committed
329
        slotViewStatusBar();
330

331 332 333 334 335
        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
336 337 338 339 340 341 342 343 344 345 346 347
        /*      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
348
    m_pMainSplitter = this;
349 350 351 352 353 354
    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
355
    m_pDirectoryMergeWindow = new DirectoryMergeWindow(m_pDirectoryMergeSplitter, m_pOptions);
356 357 358 359 360 361 362 363 364
    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);
365
    connect(m_pDirectoryMergeWindow, static_cast<void (DirectoryMergeWindow::*) (void)>(&DirectoryMergeWindow::updateAvailabilities), this, &KDiff3App::slotUpdateAvailabilities);
366
    connect(m_pDirectoryMergeWindow, &DirectoryMergeWindow::statusBarMessage, this, &KDiff3App::slotStatusMsg);
367
    connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &KDiff3App::slotClipboardChanged);
368 369
    m_pDirectoryMergeWindow->initDirectoryMergeActions(this, actionCollection());

370
    delete KDiff3Shell::getParser();
371

Michael Reeves's avatar
Michael Reeves committed
372
    if(m_pKDiff3Shell == nullptr) {
373
        completeInit(QString());
Michael Reeves's avatar
Michael Reeves committed
374
    }
Joachim Eibl's avatar
Joachim Eibl committed
375 376
}

377
void KDiff3App::completeInit(const QString& fn1, const QString& fn2, const QString& fn3)
Michael Reeves's avatar
Michael Reeves committed
378
{
Michael Reeves's avatar
Michael Reeves committed
379
    if(m_pKDiff3Shell != nullptr)
380
    {
Michael Reeves's avatar
Michael Reeves committed
381 382
        QSize size = m_pOptions->m_geometry;
        QPoint pos = m_pOptions->m_position;
383
        if(!size.isEmpty())
Michael Reeves's avatar
Michael Reeves committed
384
        {
385
            m_pKDiff3Shell->resize(size);
Michael Reeves's avatar
Michael Reeves committed
386

387 388 389 390
            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
391
            {
392
                //Here we want the extra setup showMaximized does since the window has not be shown before
393
                if(m_pOptions->m_bMaximised)
394
                    m_pKDiff3Shell->showMaximized();// krazy:exclude=qmethods
Michael Reeves's avatar
Michael Reeves committed
395 396 397 398 399
                else
                    m_pKDiff3Shell->show();
            }
        }
    }
400 401
    if(!fn1.isEmpty()) {
        m_sd1.setFilename(fn1);
Michael Reeves's avatar
Michael Reeves committed
402
    }
403 404
    if(!fn2.isEmpty()) {
        m_sd2.setFilename(fn2);
Michael Reeves's avatar
Michael Reeves committed
405
    }
406 407
    if(!fn3.isEmpty()) {
        m_sd3.setFilename(fn3);
Michael Reeves's avatar
Michael Reeves committed
408 409
    }

410 411
    //should not happen now.
    Q_ASSERT(m_bDirCompare == FileAccess(m_sd1.getFilename()).isDir());
412
    bool bSuccess = improveFilenames(false);
Michael Reeves's avatar
Michael Reeves committed
413

414
    if(m_bAutoFlag && m_bAutoMode && m_bDirCompare)
Michael Reeves's avatar
Michael Reeves committed
415
    {
416
        QTextStream(stderr) << i18n("Option --auto ignored for directory comparison.") << "\n";
Michael Reeves's avatar
Michael Reeves committed
417 418
        m_bAutoMode = false;
    }
419
    if(!m_bDirCompare)
Michael Reeves's avatar
Michael Reeves committed
420
    {
Michael Reeves's avatar
Michael Reeves committed
421 422
        m_pDirectoryMergeSplitter->hide();

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

Michael Reeves's avatar
Michael Reeves committed
447
            if(pSD != nullptr)
448
            {
Michael Reeves's avatar
Michael Reeves committed
449
                // Save this file directly, not via the merge result window.
450 451
                FileAccess fa(m_outputFilename);
                if(m_pOptions->m_bDmCreateBakFiles && fa.exists())
Michael Reeves's avatar
Michael Reeves committed
452
                {
Michael Reeves's avatar
cleanup  
Michael Reeves committed
453
                    fa.createBackup(".orig");
Michael Reeves's avatar
Michael Reeves committed
454 455
                }

456 457 458 459 460
                bSuccess = pSD->saveNormalDataAs(m_outputFilename);
                if(bSuccess)
                    ::exit(0);
                else
                    KMessageBox::error(this, i18n("Saving failed."));
Michael Reeves's avatar
Michael Reeves committed
461
            }
462
            else if(m_pMergeResultWindow->getNrOfUnsolvedConflicts() == 0)
Michael Reeves's avatar
Michael Reeves committed
463
            {
Michael Reeves's avatar
Michael Reeves committed
464
                bSuccess = m_pMergeResultWindow->saveDocument(m_pMergeResultWindowTitle->getFileName(), m_pMergeResultWindowTitle->getEncoding(), m_pMergeResultWindowTitle->getLineEndStyle());
465
                if(bSuccess) ::exit(0);
Michael Reeves's avatar
Michael Reeves committed
466 467 468 469 470
            }
        }
    }
    m_bAutoMode = false;

471
    if(m_pKDiff3Shell)
Michael Reeves's avatar
Michael Reeves committed
472
    {
473
        if(m_pOptions->m_bMaximised)
Michael Reeves's avatar
Michael Reeves committed
474 475
            //We want showMaximized here as the window has never been shown.
            m_pKDiff3Shell->showMaximized();// krazy:exclude=qmethods
Michael Reeves's avatar
Michael Reeves committed
476 477 478 479
        else
            m_pKDiff3Shell->show();
    }

480
    g_pProgressDialog->setStayHidden(false);
Michael Reeves's avatar
Michael Reeves committed
481

Michael Reeves's avatar
Michael Reeves committed
482
    if(statusBar() != nullptr)
483
        statusBar()->setSizeGripEnabled(true);
Michael Reeves's avatar
Michael Reeves committed
484 485 486 487 488

    slotClipboardChanged(); // For initialisation.

    slotUpdateAvailabilities();

Michael Reeves's avatar
Michael Reeves committed
489
    if(!m_bDirCompare && m_pKDiff3Shell != nullptr)
Michael Reeves's avatar
Michael Reeves committed
490
    {
Michael Reeves's avatar
Michael Reeves committed
491
        bool bFileOpenError = false;
492 493 494
        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
495
        {
496
            QString text(i18n("Opening of these files failed:"));
Michael Reeves's avatar
Michael Reeves committed
497
            text += "\n\n";
498
            if(!m_sd1.isEmpty() && !m_sd1.hasData())
499
                text += " - " + m_sd1.getAliasName() + '\n';
500
            if(!m_sd2.isEmpty() && !m_sd2.hasData())
501
                text += " - " + m_sd2.getAliasName() + '\n';
502
            if(!m_sd3.isEmpty() && !m_sd3.hasData())
503
                text += " - " + m_sd3.getAliasName() + '\n';
Michael Reeves's avatar
Michael Reeves committed
504

505
            KMessageBox::sorry(this, text, i18n("File Open Error"));
Michael Reeves's avatar
Michael Reeves committed
506 507 508
            bFileOpenError = true;
        }

509
        if(m_sd1.isEmpty() || m_sd2.isEmpty() || bFileOpenError)
Michael Reeves's avatar
Michael Reeves committed
510 511
            slotFileOpen();
    }
512
    else if(!bSuccess) // Directory open failed
Michael Reeves's avatar
Michael Reeves committed
513
    {
Michael Reeves's avatar
Michael Reeves committed
514 515
        slotFileOpen();
    }
Joachim Eibl's avatar
Joachim Eibl committed
516 517
}

Michael Reeves's avatar
Michael Reeves committed
518 519
KDiff3App::~KDiff3App()
{
Joachim Eibl's avatar
Joachim Eibl committed
520 521
}

Joachim Eibl's avatar
0.9.93  
Joachim Eibl committed
522 523 524 525
/**
 * Helper function used to create actions into the ac collection
 */

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

535
    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
536

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

573
#include "xpm/autoadvance.xpm"
Joachim Eibl's avatar
Joachim Eibl committed
574 575 576
#include "xpm/currentpos.xpm"
#include "xpm/down1arrow.xpm"
#include "xpm/down2arrow.xpm"
577
#include "xpm/downend.xpm"
Joachim Eibl's avatar
Joachim Eibl committed
578 579 580
#include "xpm/iconA.xpm"
#include "xpm/iconB.xpm"
#include "xpm/iconC.xpm"
581 582 583
#include "xpm/nextunsolved.xpm"
#include "xpm/prevunsolved.xpm"
#include "xpm/showlinenumbers.xpm"
Joachim Eibl's avatar
Joachim Eibl committed
584
#include "xpm/showwhitespace.xpm"
Joachim Eibl's avatar
Joachim Eibl committed
585
#include "xpm/showwhitespacechars.xpm"
586 587 588 589
#include "xpm/up1arrow.xpm"
#include "xpm/up2arrow.xpm"
#include "xpm/upend.xpm"

590
    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");
591

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

594
    goBottom = GuiUtils::createAction<QAction>(i18n("Go to Last Delta"), QIcon(QPixmap(downend)), i18n("Last\nDelta"), this, &KDiff3App::slotGoBottom, ac, "go_bottom");
595 596 597

    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.)");
598
    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");
599
    goPrevDelta->setToolTip(goPrevDelta->text() + omitsWhitespace);
600
    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");
601
    goNextDelta->setToolTip(goNextDelta->text() + omitsWhitespace);
602
    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");
603
    goPrevConflict->setToolTip(goPrevConflict->text() + omitsWhitespace);
604
    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");
605
    goNextConflict->setToolTip(goNextConflict->text() + omitsWhitespace);
606
    goPrevUnsolvedConflict = GuiUtils::createAction<QAction>(i18n("Go to Previous Unsolved Conflict"), QIcon(QPixmap(prevunsolved)), i18n("Prev\nUnsolved"), this, &KDiff3App::slotGoPrevUnsolvedConflict, ac, "go_prev_unsolved_conflict");
607
    goPrevUnsolvedConflict->setToolTip(goPrevUnsolvedConflict->text() + includeWhitespace);
608
    goNextUnsolvedConflict = GuiUtils::createAction<QAction>(i18n("Go to Next Unsolved Conflict"), QIcon(QPixmap(nextunsolved)), i18n("Next\nUnsolved"), this, &KDiff3App::slotGoNextUnsolvedConflict, ac, "go_next_unsolved_conflict");
609
    goNextUnsolvedConflict->setToolTip(goNextUnsolvedConflict->text() + includeWhitespace);
610 611 612 613 614 615 616 617 618
    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
619

620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
    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");
644
    dirShowBoth->setChecked(true);
645
    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
646

647
    m_pMergeEditorPopupMenu = new QMenu(this);
Michael Reeves's avatar
Michael Reeves committed
648 649 650
    /*   chooseA->plug( m_pMergeEditorPopupMenu );
       chooseB->plug( m_pMergeEditorPopupMenu );
       chooseC->plug( m_pMergeEditorPopupMenu );*/
651 652 653
    m_pMergeEditorPopupMenu->addAction(chooseA);
    m_pMergeEditorPopupMenu->addAction(chooseB);
    m_pMergeEditorPopupMenu->addAction(chooseC);
Joachim Eibl's avatar
Joachim Eibl committed
654 655
}

656
void KDiff3App::showPopupMenu(const QPoint& point)
Michael Reeves's avatar
Michael Reeves committed
657
{
658
    m_pMergeEditorPopupMenu->popup(point);
Joachim Eibl's avatar
Joachim Eibl committed
659 660
}

Michael Reeves's avatar
Michael Reeves committed
661 662
void KDiff3App::initStatusBar()
{
Michael Reeves's avatar
Michael Reeves committed
663 664
    ///////////////////////////////////////////////////////////////////
    // STATUSBAR
Michael Reeves's avatar
Michael Reeves committed
665
    if(statusBar() != nullptr)
666
        statusBar()->showMessage(i18n("Ready."));
Joachim Eibl's avatar
Joachim Eibl committed
667 668
}

669
void KDiff3App::saveOptions(KSharedConfigPtr config)
Michael Reeves's avatar
Michael Reeves committed
670
{
671 672 673 674
    if(!m_bAutoMode)
    {
        if(!isPart())
        {
Michael Reeves's avatar
Michael Reeves committed
675
            m_pOptions->m_bMaximised = m_pKDiff3Shell->isMaximized();
676
            if(!m_pKDiff3Shell->isMaximized() && m_pKDiff3Shell->isVisible())
Michael Reeves's avatar
Michael Reeves committed
677
            {
Michael Reeves's avatar
Michael Reeves committed
678 679 680 681 682 683 684 685
                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
686
        m_pOptionDialog->saveOptions(std::move(config));
Michael Reeves's avatar
Michael Reeves committed
687
    }
Joachim Eibl's avatar
Joachim Eibl committed
688 689
}

Michael Reeves's avatar
Michael Reeves committed
690 691
bool KDiff3App::queryClose()
{
692 693 694 695 696
    saveOptions(KSharedConfig::openConfig());

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

    m_bOutputModified = false;

716
    if(m_pDirectoryMergeWindow->isDirectoryMergeInProgress())
Michael Reeves's avatar
Michael Reeves committed
717
    {
718 719 720 721 722 723
        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
724 725 726 727
            return false;
    }

    return true;
Joachim Eibl's avatar
Joachim Eibl committed
728 729 730 731 732 733
}

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

Michael Reeves's avatar
Michael Reeves committed
734 735
void KDiff3App::slotFileSave()
{
736
    if(m_bDefaultFilename)
Michael Reeves's avatar
Michael Reeves committed
737
    {
Michael Reeves's avatar
Michael Reeves committed
738 739
        slotFileSaveAs();
    }
Michael Reeves's avatar
Michael Reeves committed
740 741
    else
    {
742
        slotStatusMsg(i18n("Saving file..."));
Michael Reeves's avatar
Michael Reeves committed
743

744 745
        bool bSuccess = m_pMergeResultWindow->saveDocument(m_outputFilename, m_pMergeResultWindowTitle->getEncoding(), m_pMergeResultWindowTitle->getLineEndStyle());
        if(bSuccess)
Michael Reeves's avatar
Michael Reeves committed
746
        {
Michael Reeves's avatar
Michael Reeves committed
747 748
            m_bFileSaved = true;
            m_bOutputModified = false;
749 750
            if(m_bDirCompare)
                m_pDirectoryMergeWindow->mergeResultSaved(m_outputFilename);
Michael Reeves's avatar
Michael Reeves committed
751 752
        }

753
        slotStatusMsg(i18n("Ready."));
Michael Reeves's avatar
Michael Reeves committed
754
    }
Joachim Eibl's avatar
Joachim Eibl committed
755 756
}

Michael Reeves's avatar
Michael Reeves committed
757 758
void KDiff3App::slotFileSaveAs()
{
759
    slotStatusMsg(i18n("Saving file with a new filename..."));
Michael Reeves's avatar
Michael Reeves committed
760

761
    QString s = QFileDialog::getSaveFileUrl(this, i18n("Save As..."), QUrl::fromLocalFile(QDir::currentPath())).url(QUrl::PreferLocalFile);
762
    if(!s.isEmpty()) {
Michael Reeves's avatar
Michael Reeves committed
763
        m_outputFilename = s;
Michael Reeves's avatar