1 /*
2 SPDX-FileCopyrightText: 2001-2005,2009 Otto Bruggeman <otto.bruggeman@home.nl>
3 SPDX-FileCopyrightText: 2001-2003 John Firebaugh <jfirebaugh@kde.org>
4 SPDX-FileCopyrightText: 2007-2010 Kevin Kofler <kevin.kofler@chello.at>
5 SPDX-FileCopyrightText: 2012 Jean -Nicolas Artaud <jeannicolasartaud@gmail.com>
6 
7 SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #include "komparemodellist.h"
11 
12 #include <QAction>
13 #include <QFile>
14 #include <QDir>
15 #include <QRegExp>
16 #include <QTextCodec>
17 #include <QTextStream>
18 #include <QList>
19 #include <QTemporaryFile>
20 #include <QMimeType>
21 #include <QMimeDatabase>
22 
23 #include <KActionCollection>
24 #include <KCharsets>
25 #include <KDirWatch>
26 #include <KIO/UDSEntry>
27 #include <KIO/StatJob>
28 #include <KIO/MkdirJob>
29 #include <KIO/FileCopyJob>
30 #include <KLocalizedString>
31 #include <KStandardAction>
32 
33 #include <komparediffdebug.h>
34 #include "diffhunk.h"
35 #include "kompareprocess.h"
36 #include "parser.h"
37 
38 using namespace Diff2;
39 
KompareModelList(DiffSettings * diffSettings,QWidget * widgetForKIO,QObject * parent,const char * name,bool supportReadWrite)40 KompareModelList::KompareModelList(DiffSettings* diffSettings, QWidget* widgetForKIO, QObject* parent, const char* name, bool supportReadWrite)
41     : QObject(parent),
42       m_diffProcess(nullptr),
43       m_diffSettings(diffSettings),
44       m_models(nullptr),
45       m_selectedModel(nullptr),
46       m_selectedDifference(nullptr),
47       m_modelIndex(0),
48       m_info(nullptr),
49       m_textCodec(nullptr),
50       m_widgetForKIO(widgetForKIO),
51       m_isReadWrite(supportReadWrite)
52 {
53     qCDebug(LIBKOMPAREDIFF2) << "Show me the arguments: " << diffSettings << ", " << widgetForKIO << ", " << parent << ", " << name;
54     m_actionCollection = new KActionCollection(this);
55     if (supportReadWrite) {
56         m_applyDifference = m_actionCollection->addAction(QStringLiteral("difference_apply"), this, &KompareModelList::slotActionApplyDifference);
57         m_applyDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
58         m_applyDifference->setText(i18nc("@action", "&Apply Difference"));
59         m_actionCollection->setDefaultShortcut(m_applyDifference, QKeySequence(Qt::Key_Space));
60         m_unApplyDifference = m_actionCollection->addAction(QStringLiteral("difference_unapply"), this, &KompareModelList::slotActionUnApplyDifference);
61         m_unApplyDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
62         m_unApplyDifference->setText(i18nc("@action", "Un&apply Difference"));
63         m_actionCollection->setDefaultShortcut(m_unApplyDifference, QKeySequence(Qt::Key_Backspace));
64         m_applyAll = m_actionCollection->addAction(QStringLiteral("difference_applyall"), this, &KompareModelList::slotActionApplyAllDifferences);
65         m_applyAll->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")));
66         m_applyAll->setText(i18nc("@action", "App&ly All"));
67         m_actionCollection->setDefaultShortcut(m_applyAll, QKeySequence(Qt::CTRL + Qt::Key_A));
68         m_unapplyAll = m_actionCollection->addAction(QStringLiteral("difference_unapplyall"), this, &KompareModelList::slotActionUnapplyAllDifferences);
69         m_unapplyAll->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left-double")));
70         m_unapplyAll->setText(i18nc("@action", "&Unapply All"));
71         m_actionCollection->setDefaultShortcut(m_unapplyAll, QKeySequence(Qt::CTRL + Qt::Key_U));
72     } else {
73         m_applyDifference = nullptr;
74         m_unApplyDifference = nullptr;
75         m_applyAll = nullptr;
76         m_unapplyAll = nullptr;
77     }
78     m_previousFile = m_actionCollection->addAction(QStringLiteral("difference_previousfile"), this, &KompareModelList::slotPreviousModel);
79     m_previousFile->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up-double")));
80     m_previousFile->setText(i18nc("@action", "P&revious File"));
81     m_actionCollection->setDefaultShortcut(m_previousFile, QKeySequence(Qt::CTRL + Qt::Key_PageUp));
82     m_nextFile = m_actionCollection->addAction(QStringLiteral("difference_nextfile"), this, &KompareModelList::slotNextModel);
83     m_nextFile->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down-double")));
84     m_nextFile->setText(i18nc("@action", "N&ext File"));
85     m_actionCollection->setDefaultShortcut(m_nextFile, QKeySequence(Qt::CTRL + Qt::Key_PageDown));
86     m_previousDifference = m_actionCollection->addAction(QStringLiteral("difference_previous"), this, &KompareModelList::slotPreviousDifference);
87     m_previousDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up")));
88     m_previousDifference->setText(i18nc("@action", "&Previous Difference"));
89     m_actionCollection->setDefaultShortcut(m_previousDifference, QKeySequence(Qt::CTRL + Qt::Key_Up));
90     m_nextDifference = m_actionCollection->addAction(QStringLiteral("difference_next"), this, &KompareModelList::slotNextDifference);
91     m_nextDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down")));
92     m_nextDifference->setText(i18nc("@action", "&Next Difference"));
93     m_actionCollection->setDefaultShortcut(m_nextDifference, QKeySequence(Qt::CTRL + Qt::Key_Down));
94     m_previousDifference->setEnabled(false);
95     m_nextDifference->setEnabled(false);
96 
97     if (supportReadWrite) {
98         m_save = KStandardAction::save(this, &KompareModelList::slotSaveDestination, m_actionCollection);
99         m_save->setEnabled(false);
100     } else {
101         m_save = nullptr;
102     }
103 
104     updateModelListActions();
105 }
106 
~KompareModelList()107 KompareModelList::~KompareModelList()
108 {
109     m_selectedModel = nullptr;
110     m_selectedDifference = nullptr;
111     m_info = nullptr;
112     delete m_models;
113 }
114 
isDirectory(const QString & url) const115 bool KompareModelList::isDirectory(const QString& url) const
116 {
117     QFileInfo fi(url);
118     if (fi.isDir())
119         return true;
120     else
121         return false;
122 }
123 
isDiff(const QString & mimeType) const124 bool KompareModelList::isDiff(const QString& mimeType) const
125 {
126     if (mimeType == QLatin1String("text/x-patch"))
127         return true;
128     else
129         return false;
130 }
131 
compare()132 bool KompareModelList::compare()
133 {
134     bool result = false;
135 
136     bool sourceIsDirectory = isDirectory(m_info->localSource);
137     bool destinationIsDirectory = isDirectory(m_info->localDestination);
138 
139     if (sourceIsDirectory && destinationIsDirectory)
140     {
141         m_info->mode = Kompare::ComparingDirs;
142         result = compare(m_info->mode);
143     }
144     else if (!sourceIsDirectory && !destinationIsDirectory)
145     {
146         QFile sourceFile(m_info->localSource);
147         sourceFile.open(QIODevice::ReadOnly);
148         QMimeDatabase db;
149         QString sourceMimeType = (db.mimeTypeForData(sourceFile.readAll())).name();
150         sourceFile.close();
151         qCDebug(LIBKOMPAREDIFF2) << "Mimetype source     : " << sourceMimeType;
152 
153         QFile destinationFile(m_info->localDestination);
154         destinationFile.open(QIODevice::ReadOnly);
155         QString destinationMimeType = (db.mimeTypeForData(destinationFile.readAll())).name();
156         destinationFile.close();
157         qCDebug(LIBKOMPAREDIFF2) << "Mimetype destination: " << destinationMimeType;
158 
159         // Not checking if it is a text file/something diff can even compare, we'll let diff handle that
160         if (!isDiff(sourceMimeType) && isDiff(destinationMimeType))
161         {
162             qCDebug(LIBKOMPAREDIFF2) << "Blending destination into source...";
163             m_info->mode = Kompare::BlendingFile;
164             result = openFileAndDiff();
165         }
166         else if (isDiff(sourceMimeType) && !isDiff(destinationMimeType))
167         {
168             qCDebug(LIBKOMPAREDIFF2) << "Blending source into destination...";
169             m_info->mode = Kompare::BlendingFile;
170             // Swap source and destination before calling this
171             m_info->swapSourceWithDestination();
172             // Do we need to notify anyone we swapped source and destination?
173             // No we do not need to notify anyone about swapping source with destination
174             result = openFileAndDiff();
175         }
176         else
177         {
178             qCDebug(LIBKOMPAREDIFF2) << "Comparing source with destination";
179             m_info->mode = Kompare::ComparingFiles;
180             result = compare(m_info->mode);
181         }
182     }
183     else if (sourceIsDirectory && !destinationIsDirectory)
184     {
185         m_info->mode = Kompare::BlendingDir;
186         result = openDirAndDiff();
187     }
188     else
189     {
190         m_info->mode = Kompare::BlendingDir;
191         // Swap source and destination first in m_info
192         m_info->swapSourceWithDestination();
193         // Do we need to notify anyone we swapped source and destination?
194         // No we do not need to notify anyone about swapping source with destination
195         result = openDirAndDiff();
196     }
197 
198     return result;
199 }
200 
compare(Kompare::Mode mode)201 bool KompareModelList::compare(Kompare::Mode mode)
202 {
203     clear(); // Destroy the old models...
204 
205     m_diffProcess = new KompareProcess(m_diffSettings, Kompare::Custom, m_info->localSource, m_info->localDestination, QString(), mode);
206     m_diffProcess->setEncoding(m_encoding);
207 
208     connect(m_diffProcess, &KompareProcess::diffHasFinished,
209             this, &KompareModelList::slotDiffProcessFinished);
210 
211     Q_EMIT status(Kompare::RunningDiff);
212     m_diffProcess->start();
213 
214     return true;
215 }
216 
lstripSeparators(const QString & from,uint count)217 static QString lstripSeparators(const QString& from, uint count)
218 {
219     int position = 0;
220     for (uint i = 0; i < count; ++i)
221     {
222         position = from.indexOf(QLatin1Char('/'), position);
223         if (position == -1)
224         {
225             break;
226         }
227     }
228     if (position == -1)
229     {
230         return QString();
231     }
232 
233     return from.mid(position + 1);
234 }
235 
setDepthAndApplied()236 void KompareModelList::setDepthAndApplied()
237 {
238     // Splice to avoid calling ~DiffModelList
239     const QList<Diff2::DiffModel*> splicedModelList(*m_models);
240     for (DiffModel* model : splicedModelList) {
241         model->setSourceFile(lstripSeparators(model->source(), m_info->depth));
242         model->setDestinationFile(lstripSeparators(model->destination(), m_info->depth));
243         model->applyAllDifferences(m_info->applied);
244     }
245 }
246 
openFileAndDiff()247 bool KompareModelList::openFileAndDiff()
248 {
249     clear();
250 
251     if (parseDiffOutput(readFile(m_info->localDestination)) != 0)
252     {
253         Q_EMIT error(i18n("<qt>No models or no differences, this file: <b>%1</b>, is not a valid diff file.</qt>", m_info->destination.url()));
254         return false;
255     }
256 
257     setDepthAndApplied();
258 
259     if (!blendOriginalIntoModelList(m_info->localSource))
260     {
261         qCDebug(LIBKOMPAREDIFF2) << "Oops cant blend original file into modellist : " << m_info->localSource;
262         Q_EMIT error(i18n("<qt>There were problems applying the diff <b>%1</b> to the file <b>%2</b>.</qt>", m_info->destination.url(), m_info->source.url()));
263         return false;
264     }
265 
266     updateModelListActions();
267     show();
268 
269     return true;
270 }
271 
openDirAndDiff()272 bool KompareModelList::openDirAndDiff()
273 {
274     clear();
275 
276     if (parseDiffOutput(readFile(m_info->localDestination)) != 0)
277     {
278         Q_EMIT error(i18n("<qt>No models or no differences, this file: <b>%1</b>, is not a valid diff file.</qt>", m_info->destination.url()));
279         return false;
280     }
281 
282     setDepthAndApplied();
283 
284     // Do our thing :)
285     if (!blendOriginalIntoModelList(m_info->localSource))
286     {
287         // Trouble blending the original into the model
288         qCDebug(LIBKOMPAREDIFF2) << "Oops cant blend original dir into modellist : " << m_info->localSource;
289         Q_EMIT error(i18n("<qt>There were problems applying the diff <b>%1</b> to the folder <b>%2</b>.</qt>", m_info->destination.url(), m_info->source.url()));
290         return false;
291     }
292 
293     updateModelListActions();
294     show();
295 
296     return true;
297 }
298 
slotSaveDestination()299 void KompareModelList::slotSaveDestination()
300 {
301     // Unnecessary safety check! We can now guarantee that saving is only possible when there is a model and there are unsaved changes
302     if (m_selectedModel)
303     {
304         saveDestination(m_selectedModel);
305         if (m_save) m_save->setEnabled(false);
306         Q_EMIT updateActions();
307     }
308 }
309 
saveDestination(DiffModel * model)310 bool KompareModelList::saveDestination(DiffModel* model)
311 {
312     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::saveDestination: ";
313 
314     // Unnecessary safety check, we can guarantee there are unsaved changes!!!
315     if (!model->hasUnsavedChanges())
316         return true;
317 
318     QTemporaryFile temp;
319 
320     if (!temp.open()) {
321         Q_EMIT error(i18n("Could not open a temporary file."));
322         temp.remove();
323         return false;
324     }
325 
326     QTextStream stream(&temp);
327     QStringList list;
328 
329     DiffHunkListConstIterator hunkIt = model->hunks()->constBegin();
330     DiffHunkListConstIterator hEnd   = model->hunks()->constEnd();
331 
332     for (; hunkIt != hEnd; ++hunkIt)
333     {
334         DiffHunk* hunk = *hunkIt;
335 
336         DifferenceListConstIterator diffIt = hunk->differences().constBegin();
337         DifferenceListConstIterator dEnd   = hunk->differences().constEnd();
338 
339         Difference* diff;
340         for (; diffIt != dEnd; ++diffIt)
341         {
342             diff = *diffIt;
343             if (!diff->applied())
344             {
345                 DifferenceStringListConstIterator stringIt = diff->destinationLines().begin();
346                 DifferenceStringListConstIterator sEnd     = diff->destinationLines().end();
347                 for (; stringIt != sEnd; ++stringIt)
348                 {
349                     list.append((*stringIt)->string());
350                 }
351             }
352             else
353             {
354                 DifferenceStringListConstIterator stringIt = diff->sourceLines().begin();
355                 DifferenceStringListConstIterator sEnd = diff->sourceLines().end();
356                 for (; stringIt != sEnd; ++stringIt)
357                 {
358                     list.append((*stringIt)->string());
359                 }
360             }
361         }
362     }
363 
364     // qCDebug(LIBKOMPAREDIFF2) << "Everything: " << endl << list.join( "\n" );
365 
366     if (list.count() > 0)
367         stream << list.join(QString());
368     if (temp.error() != QFile::NoError) {
369         Q_EMIT error(i18n("<qt>Could not write to the temporary file <b>%1</b>, deleting it.</qt>", temp.fileName()));
370         temp.remove();
371         return false;
372     }
373 
374     temp.close();
375     if (temp.error() != QFile::NoError) {
376         Q_EMIT error(i18n("<qt>Could not write to the temporary file <b>%1</b>, deleting it.</qt>", temp.fileName()));
377         temp.remove();
378         return false;
379     }
380 
381     bool result = false;
382 
383     // Make sure the destination directory exists, it is possible when using -N to not have the destination dir/file available
384     if (m_info->mode == Kompare::ComparingDirs)
385     {
386         // Don't use destination which was used for creating the diff directly, use the original URL!!!
387         // FIXME!!! Wrong destination this way! Need to add the sub directory to the url!!!!
388         qCDebug(LIBKOMPAREDIFF2) << "Tempfilename (save) : " << temp.fileName();
389         qCDebug(LIBKOMPAREDIFF2) << "Model->path+file    : " << model->destinationPath() << model->destinationFile();
390         qCDebug(LIBKOMPAREDIFF2) << "info->localdest     : " << m_info->localDestination;
391         QString tmp = model->destinationPath() + model->destinationFile();
392         if (tmp.startsWith(m_info->localDestination))     // It should, if not serious trouble...
393             tmp.remove(0, m_info->localDestination.size());
394         qCDebug(LIBKOMPAREDIFF2) << "DestinationURL      : " << m_info->destination;
395         qCDebug(LIBKOMPAREDIFF2) << "tmp                 : " << tmp;
396         KIO::UDSEntry entry;
397         QUrl fullDestinationPath = m_info->destination;
398         fullDestinationPath.setPath(fullDestinationPath.path() + QLatin1Char('/') + tmp);
399         qCDebug(LIBKOMPAREDIFF2) << "fullDestinationPath : " << fullDestinationPath;
400         KIO::StatJob* statJob = KIO::stat(fullDestinationPath);
401         if (!statJob->exec())
402         {
403             entry = statJob->statResult();
404             KIO::MkdirJob* mkdirJob = KIO::mkdir(fullDestinationPath);
405             if (!mkdirJob->exec())
406             {
407                 Q_EMIT error(i18n("<qt>Could not create destination directory <b>%1</b>.\nThe file has not been saved.</qt>", fullDestinationPath.path()));
408                 return false;
409             }
410         }
411         KIO::FileCopyJob* copyJob = KIO::file_copy(QUrl::fromLocalFile(temp.fileName()), fullDestinationPath, -1, KIO::Overwrite);
412         result = copyJob->exec();
413     }
414     else
415     {
416         qCDebug(LIBKOMPAREDIFF2) << "Tempfilename   : " << temp.fileName();
417         qCDebug(LIBKOMPAREDIFF2) << "DestinationURL : " << m_info->destination;
418 
419         // Get permissions of existing file and copy temporary file with the same permissions
420         int permissions = -1;
421         KIO::StatJob* statJob = KIO::stat(m_info->destination);
422         result = statJob->exec();
423         if (result)
424             permissions = statJob->statResult().numberValue(KIO::UDSEntry::UDS_ACCESS);
425 
426         KIO::FileCopyJob* copyJob = KIO::file_copy(QUrl::fromLocalFile(temp.fileName()), m_info->destination , permissions, KIO::Overwrite);
427         result = copyJob->exec();
428         qCDebug(LIBKOMPAREDIFF2) << "true or false?" << result;
429     }
430 
431     if (!result)
432     {
433         // FIXME: Wrong first argument given in case of comparing directories!
434         Q_EMIT error(i18n("<qt>Could not upload the temporary file to the destination location <b>%1</b>. The temporary file is still available under: <b>%2</b>. You can manually copy it to the right place.</qt>", m_info->destination.url(), temp.fileName()));
435         //Don't remove file when we delete temp and don't leak it.
436         temp.setAutoRemove(false);
437     }
438     else
439     {
440         temp.remove();
441     }
442 
443     // If saving was fine set all differences to saved so we can start again with a clean slate
444     if (result)
445     {
446         DifferenceListConstIterator diffIt = model->differences()->constBegin();
447         DifferenceListConstIterator endIt  = model->differences()->constEnd();
448 
449         for (; diffIt != endIt; ++diffIt)
450         {
451             (*diffIt)->setUnsaved(false);
452         }
453     }
454 
455     return true;
456 }
457 
saveAll()458 bool KompareModelList::saveAll()
459 {
460     if (modelCount() == 0)
461         return false;
462 
463     DiffModelListIterator it  =  m_models->begin();
464     DiffModelListIterator end =  m_models->end();
465     for (; it != end; ++it)
466     {
467         if (!saveDestination(*it))
468             return false;
469     }
470 
471     return true;
472 }
473 
setEncoding(const QString & encoding)474 void KompareModelList::setEncoding(const QString& encoding)
475 {
476     m_encoding = encoding;
477     if (!encoding.compare(QLatin1String("default"), Qt::CaseInsensitive))
478     {
479         m_textCodec = QTextCodec::codecForLocale();
480     }
481     else
482     {
483         qCDebug(LIBKOMPAREDIFF2) << "Encoding : " << encoding;
484         m_textCodec = KCharsets::charsets()->codecForName(encoding);
485         qCDebug(LIBKOMPAREDIFF2) << "TextCodec: " << m_textCodec;
486         if (!m_textCodec)
487             m_textCodec = QTextCodec::codecForLocale();
488     }
489     qCDebug(LIBKOMPAREDIFF2) << "TextCodec: " << m_textCodec;
490 }
491 
setReadWrite(bool isReadWrite)492 void KompareModelList::setReadWrite(bool isReadWrite)
493 {
494     if (m_isReadWrite == isReadWrite)
495         return;
496 
497     m_isReadWrite = isReadWrite;
498     updateModelListActions();
499 }
500 
isReadWrite() const501 bool KompareModelList::isReadWrite() const
502 {
503     return m_isReadWrite;
504 }
505 
slotDiffProcessFinished(bool success)506 void KompareModelList::slotDiffProcessFinished(bool success)
507 {
508     if (success)
509     {
510         Q_EMIT status(Kompare::Parsing);
511         if (parseDiffOutput(m_diffProcess->diffOutput()) != 0)
512         {
513             Q_EMIT error(i18n("Could not parse diff output."));
514         }
515         else
516         {
517             if (m_info->mode != Kompare::ShowingDiff)
518             {
519                 qCDebug(LIBKOMPAREDIFF2) << "Blend this crap please and do not give me any conflicts...";
520                 blendOriginalIntoModelList(m_info->localSource);
521             }
522             updateModelListActions();
523             show();
524         }
525         Q_EMIT status(Kompare::FinishedParsing);
526     }
527     else if (m_diffProcess->exitStatus() == 0)
528     {
529         Q_EMIT error(i18n("The files are identical."));
530     }
531     else
532     {
533         Q_EMIT error(m_diffProcess->stdErr());
534     }
535 
536     m_diffProcess->deleteLater();
537     m_diffProcess = nullptr;
538 }
539 
slotDirectoryChanged(const QString &)540 void KompareModelList::slotDirectoryChanged(const QString& /*dir*/)
541 {
542     // some debug output to see if watching works properly
543     qCDebug(LIBKOMPAREDIFF2) << "Yippie directories are being watched !!! :)";
544     if (m_diffProcess)
545     {
546         Q_EMIT status(Kompare::ReRunningDiff);
547         m_diffProcess->start();
548     }
549 }
550 
slotFileChanged(const QString &)551 void KompareModelList::slotFileChanged(const QString& /*file*/)
552 {
553     // some debug output to see if watching works properly
554     qCDebug(LIBKOMPAREDIFF2) << "Yippie files are being watched !!! :)";
555     if (m_diffProcess)
556     {
557         Q_EMIT status(Kompare::ReRunningDiff);
558         m_diffProcess->start();
559     }
560 }
561 
split(const QString & fileContents)562 QStringList KompareModelList::split(const QString& fileContents)
563 {
564     QString contents = fileContents;
565     QStringList list;
566 
567     int pos = 0;
568     int oldpos = 0;
569     // split that does not strip the split char
570 #ifdef QT_OS_MAC
571     const char split = '\r';
572 #else
573     const char split = '\n';
574 #endif
575     while ((pos = contents.indexOf(QLatin1Char(split), oldpos)) >= 0)
576     {
577         list.append(contents.mid(oldpos, pos - oldpos + 1));
578         oldpos = pos + 1;
579     }
580 
581     if (contents.length() > oldpos)
582     {
583         list.append(contents.right(contents.length() - oldpos));
584     }
585 
586     return list;
587 }
588 
readFile(const QString & fileName)589 QString KompareModelList::readFile(const QString& fileName)
590 {
591     QStringList list;
592 
593     QFile file(fileName);
594     file.open(QIODevice::ReadOnly);
595 
596     QTextStream stream(&file);
597     qCDebug(LIBKOMPAREDIFF2) << "Codec = " << m_textCodec;
598 
599     if (!m_textCodec)
600         m_textCodec = QTextCodec::codecForLocale();
601 
602     stream.setCodec(m_textCodec);
603 
604     QString contents = stream.readAll();
605 
606     file.close();
607 
608     return contents;
609 }
610 
openDiff(const QString & diffFile)611 bool KompareModelList::openDiff(const QString& diffFile)
612 {
613     qCDebug(LIBKOMPAREDIFF2) << "Stupid :) Url = " << diffFile;
614 
615     if (diffFile.isEmpty())
616         return false;
617 
618     QString diff = readFile(diffFile);
619 
620     clear(); // Clear the current models
621 
622     Q_EMIT status(Kompare::Parsing);
623 
624     if (parseDiffOutput(diff) != 0)
625     {
626         Q_EMIT error(i18n("Could not parse diff output."));
627         return false;
628     }
629 
630     updateModelListActions();
631     show();
632 
633     Q_EMIT status(Kompare::FinishedParsing);
634 
635     return true;
636 }
637 
parseAndOpenDiff(const QString & diff)638 bool KompareModelList::parseAndOpenDiff(const QString& diff)
639 {
640     clear(); // Clear the current models
641 
642     Q_EMIT status(Kompare::Parsing);
643 
644     if (parseDiffOutput(diff) != 0)
645     {
646         Q_EMIT error(i18n("Could not parse diff output."));
647         return false;
648     }
649 
650     updateModelListActions();
651     show();
652 
653     Q_EMIT status(Kompare::FinishedParsing);
654     return true;
655 }
656 
recreateDiff() const657 QString KompareModelList::recreateDiff() const
658 {
659     QString diff;
660 
661     DiffModelListConstIterator modelIt = m_models->constBegin();
662     DiffModelListConstIterator mEnd    = m_models->constEnd();
663 
664     for (; modelIt != mEnd; ++modelIt)
665     {
666         diff += (*modelIt)->recreateDiff();
667     }
668     return diff;
669 }
670 
saveDiff(const QString & url,QString directory,DiffSettings * diffSettings)671 bool KompareModelList::saveDiff(const QString& url, QString directory, DiffSettings* diffSettings)
672 {
673     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::saveDiff: ";
674 
675     m_diffTemp = new QTemporaryFile();
676     m_diffURL = QUrl(url); // ### TODO the "url" argument should be a QUrl
677 
678     if (!m_diffTemp->open()) {
679         Q_EMIT error(i18n("Could not open a temporary file."));
680         m_diffTemp->remove();
681         delete m_diffTemp;
682         m_diffTemp = nullptr;
683         return false;
684     }
685 
686     m_diffProcess = new KompareProcess(diffSettings, Kompare::Custom, m_info->localSource, m_info->localDestination, directory);
687     m_diffProcess->setEncoding(m_encoding);
688 
689     connect(m_diffProcess, &KompareProcess::diffHasFinished,
690             this, &KompareModelList::slotWriteDiffOutput);
691 
692     Q_EMIT status(Kompare::RunningDiff);
693     m_diffProcess->start();
694     return true;
695 }
696 
slotWriteDiffOutput(bool success)697 void KompareModelList::slotWriteDiffOutput(bool success)
698 {
699     qCDebug(LIBKOMPAREDIFF2) << "Success = " << success;
700 
701     if (success)
702     {
703         QTextStream stream(m_diffTemp);
704 
705         stream << m_diffProcess->diffOutput();
706 
707         m_diffTemp->close();
708 
709         if (false /*|| m_diffTemp->status() != 0 */)
710         {
711             Q_EMIT error(i18n("Could not write to the temporary file."));
712         }
713 
714         KIO::FileCopyJob* copyJob = KIO::file_copy(QUrl::fromLocalFile(m_diffTemp->fileName()), m_diffURL);
715         copyJob->exec();
716 
717         Q_EMIT status(Kompare::FinishedWritingDiff);
718     }
719 
720     m_diffURL = QUrl();
721     m_diffTemp->remove();
722 
723     delete m_diffTemp;
724     m_diffTemp = nullptr;
725 
726     delete m_diffProcess;
727     m_diffProcess = nullptr;
728 }
729 
slotSelectionChanged(const Diff2::DiffModel * model,const Diff2::Difference * diff)730 void KompareModelList::slotSelectionChanged(const Diff2::DiffModel* model, const Diff2::Difference* diff)
731 {
732 // This method will signal all the other objects about a change in selection,
733 // it will Q_EMIT setSelection( const DiffModel*, const Difference* ) to all who are connected
734     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::slotSelectionChanged( " << model << ", " << diff << " )";
735     qCDebug(LIBKOMPAREDIFF2) << "Sender is : " << sender()->metaObject()->className();
736 //     qCDebug(LIBKOMPAREDIFF2) << kBacktrace();
737 
738     m_selectedModel = const_cast<DiffModel*>(model);
739     m_modelIndex = m_models->indexOf(m_selectedModel);
740     qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex;
741     m_selectedDifference = const_cast<Difference*>(diff);
742 
743     m_selectedModel->setSelectedDifference(m_selectedDifference);
744 
745     // setSelected* search for the argument in the lists and return false if not found
746     // if found they return true and set the m_selected*
747     if (!setSelectedModel(m_selectedModel))
748     {
749         // Backup plan
750         m_selectedModel = firstModel();
751         m_selectedDifference = m_selectedModel->firstDifference();
752     }
753     else if (!m_selectedModel->setSelectedDifference(m_selectedDifference))
754     {
755         // Another backup plan
756         m_selectedDifference = m_selectedModel->firstDifference();
757     }
758 
759     Q_EMIT setSelection(model, diff);
760     Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount());
761 
762     updateModelListActions();
763 }
764 
slotSelectionChanged(const Diff2::Difference * diff)765 void KompareModelList::slotSelectionChanged(const Diff2::Difference* diff)
766 {
767 // This method will Q_EMIT setSelection( const Difference* ) to whomever is listening
768 // when for instance in kompareview the selection has changed
769     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::slotSelectionChanged( " << diff << " )";
770     qCDebug(LIBKOMPAREDIFF2) << "Sender is : " << sender()->metaObject()->className();
771 
772     m_selectedDifference = const_cast<Difference*>(diff);
773 
774     if (!m_selectedModel->setSelectedDifference(m_selectedDifference))
775     {
776         // Backup plan
777         m_selectedDifference = m_selectedModel->firstDifference();
778     }
779 
780     Q_EMIT setSelection(diff);
781     Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount());
782 
783     updateModelListActions();
784 }
785 
slotPreviousModel()786 void KompareModelList::slotPreviousModel()
787 {
788     if ((m_selectedModel = prevModel()) != nullptr)
789     {
790         m_selectedDifference = m_selectedModel->firstDifference();
791     }
792     else
793     {
794         m_selectedModel = firstModel();
795         m_selectedDifference = m_selectedModel->firstDifference();
796     }
797 
798     Q_EMIT setSelection(m_selectedModel, m_selectedDifference);
799     Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount());
800     updateModelListActions();
801 }
802 
slotNextModel()803 void KompareModelList::slotNextModel()
804 {
805     if ((m_selectedModel = nextModel()) != nullptr)
806     {
807         m_selectedDifference = m_selectedModel->firstDifference();
808     }
809     else
810     {
811         m_selectedModel = lastModel();
812         m_selectedDifference = m_selectedModel->firstDifference();
813     }
814 
815     Q_EMIT setSelection(m_selectedModel, m_selectedDifference);
816     Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount());
817     updateModelListActions();
818 }
819 
firstModel()820 DiffModel* KompareModelList::firstModel()
821 {
822     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::firstModel()";
823     m_modelIndex = 0;
824     qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex;
825 
826     m_selectedModel = m_models->first();
827 
828     return m_selectedModel;
829 }
830 
lastModel()831 DiffModel* KompareModelList::lastModel()
832 {
833     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::lastModel()";
834     m_modelIndex = m_models->count() - 1;
835     qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex;
836 
837     m_selectedModel = m_models->last();
838 
839     return m_selectedModel;
840 }
841 
prevModel()842 DiffModel* KompareModelList::prevModel()
843 {
844     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::prevModel()";
845     if (m_modelIndex > 0 && --m_modelIndex < m_models->count())
846     {
847         qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex;
848         m_selectedModel = (*m_models)[ m_modelIndex ];
849     }
850     else
851     {
852         m_selectedModel = nullptr;
853         m_modelIndex = 0;
854         qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex;
855     }
856 
857     return m_selectedModel;
858 }
859 
nextModel()860 DiffModel* KompareModelList::nextModel()
861 {
862     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::nextModel()";
863     if (++m_modelIndex < m_models->count())
864     {
865         qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex;
866         m_selectedModel = (*m_models)[ m_modelIndex ];
867     }
868     else
869     {
870         m_selectedModel = nullptr;
871         m_modelIndex = 0;
872         qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex;
873     }
874 
875     return m_selectedModel;
876 }
877 
actionCollection() const878 KActionCollection* KompareModelList::actionCollection() const
879 {
880     return m_actionCollection;
881 }
882 
slotPreviousDifference()883 void KompareModelList::slotPreviousDifference()
884 {
885     qCDebug(LIBKOMPAREDIFF2) << "slotPreviousDifference called";
886     if ((m_selectedDifference = m_selectedModel->prevDifference()) != nullptr)
887     {
888         Q_EMIT setSelection(m_selectedDifference);
889         Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount());
890         updateModelListActions();
891         return;
892     }
893 
894     qCDebug(LIBKOMPAREDIFF2) << "**** no previous difference... ok lets find the previous model...";
895 
896     if ((m_selectedModel = prevModel()) != nullptr)
897     {
898         m_selectedDifference = m_selectedModel->lastDifference();
899 
900         Q_EMIT setSelection(m_selectedModel, m_selectedDifference);
901         Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount());
902         updateModelListActions();
903         return;
904     }
905 
906 
907     qCDebug(LIBKOMPAREDIFF2) << "**** !!! No previous model, ok backup plan activated...";
908 
909     // Backup plan
910     m_selectedModel = firstModel();
911     m_selectedDifference = m_selectedModel->firstDifference();
912 
913     Q_EMIT setSelection(m_selectedModel, m_selectedDifference);
914     Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount());
915     updateModelListActions();
916 }
917 
slotNextDifference()918 void KompareModelList::slotNextDifference()
919 {
920     qCDebug(LIBKOMPAREDIFF2) << "slotNextDifference called";
921     if ((m_selectedDifference = m_selectedModel->nextDifference()) != nullptr)
922     {
923         Q_EMIT setSelection(m_selectedDifference);
924         Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount());
925         updateModelListActions();
926         return;
927     }
928 
929     qCDebug(LIBKOMPAREDIFF2) << "**** no next difference... ok lets find the next model...";
930 
931     if ((m_selectedModel = nextModel()) != nullptr)
932     {
933         m_selectedDifference = m_selectedModel->firstDifference();
934 
935         Q_EMIT setSelection(m_selectedModel, m_selectedDifference);
936         Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount());
937         updateModelListActions();
938         return;
939     }
940 
941     qCDebug(LIBKOMPAREDIFF2) << "**** !!! No next model, ok backup plan activated...";
942 
943     // Backup plan
944     m_selectedModel = lastModel();
945     m_selectedDifference = m_selectedModel->lastDifference();
946 
947     Q_EMIT setSelection(m_selectedModel, m_selectedDifference);
948     Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount());
949     updateModelListActions();
950 }
951 
slotApplyDifference(bool apply)952 void KompareModelList::slotApplyDifference(bool apply)
953 {
954     m_selectedModel->applyDifference(apply);
955     Q_EMIT applyDifference(apply);
956 }
957 
slotApplyAllDifferences(bool apply)958 void KompareModelList::slotApplyAllDifferences(bool apply)
959 {
960     m_selectedModel->applyAllDifferences(apply);
961     Q_EMIT applyAllDifferences(apply);
962 }
963 
parseDiffOutput(const QString & diff)964 int KompareModelList::parseDiffOutput(const QString& diff)
965 {
966     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::parseDiffOutput";
967     Q_EMIT diffString(diff);
968 
969     QStringList diffLines = split(diff);
970 
971     Parser* parser = new Parser(this);
972     bool malformed = false;
973     m_models = parser->parse(diffLines, &malformed);
974 
975     m_info->generator = parser->generator();
976     m_info->format    = parser->format();
977 
978     delete parser;
979 
980     if (m_models)
981     {
982         if (malformed)
983         {
984             qCDebug(LIBKOMPAREDIFF2) << "Malformed diff";
985             Q_EMIT error(i18n("The diff is malformed. Some lines could not be parsed and will not be displayed in the diff view."));
986             // proceed anyway with the lines which have been parsed
987         }
988         m_selectedModel = firstModel();
989         qCDebug(LIBKOMPAREDIFF2) << "Ok there are differences...";
990         m_selectedDifference = m_selectedModel->firstDifference();
991         Q_EMIT setStatusBarModelInfo(0, 0, modelCount(), differenceCount(), 0);
992     }
993     else
994     {
995         // Wow trouble, no models, so no differences...
996         qCDebug(LIBKOMPAREDIFF2) << "Now i'll be damned, there should be models here !!!";
997         return -1;
998     }
999 
1000     return 0;
1001 }
1002 
blendOriginalIntoModelList(const QString & localURL)1003 bool KompareModelList::blendOriginalIntoModelList(const QString& localURL)
1004 {
1005     qCDebug(LIBKOMPAREDIFF2) << "Hurrah we are blending...";
1006     QFileInfo fi(localURL);
1007 
1008     bool result = false;
1009     DiffModel* model;
1010 
1011     QString fileContents;
1012 
1013     if (fi.isDir())
1014     {   // is a dir
1015         qCDebug(LIBKOMPAREDIFF2) << "Blend Dir";
1016 //      QDir dir( localURL, QString(), QDir::Name|QDir::DirsFirst, QDir::TypeMask );
1017         DiffModelListIterator modelIt = m_models->begin();
1018         DiffModelListIterator mEnd    = m_models->end();
1019         for (; modelIt != mEnd; ++modelIt)
1020         {
1021             model = *modelIt;
1022             qCDebug(LIBKOMPAREDIFF2) << "Model : " << model;
1023             QString filename = model->source();
1024             if (!filename.startsWith(localURL))
1025                 filename = QDir(localURL).filePath(filename);
1026             QFileInfo fi2(filename);
1027             if (fi2.exists())
1028             {
1029                 qCDebug(LIBKOMPAREDIFF2) << "Reading from: " << filename;
1030                 fileContents = readFile(filename);
1031                 result = blendFile(model, fileContents);
1032             }
1033             else
1034             {
1035                 qCDebug(LIBKOMPAREDIFF2) << "File " << filename << " does not exist !";
1036                 qCDebug(LIBKOMPAREDIFF2) << "Assume empty file !";
1037                 fileContents.truncate(0);
1038                 result = blendFile(model, fileContents);
1039             }
1040         }
1041         qCDebug(LIBKOMPAREDIFF2) << "End of Blend Dir";
1042     }
1043     else if (fi.isFile())
1044     {   // is a file
1045         qCDebug(LIBKOMPAREDIFF2) << "Blend File";
1046         qCDebug(LIBKOMPAREDIFF2) << "Reading from: " << localURL;
1047         fileContents = readFile(localURL);
1048 
1049         result = blendFile((*m_models)[ 0 ], fileContents);
1050         qCDebug(LIBKOMPAREDIFF2) << "End of Blend File";
1051     }
1052 
1053     return result;
1054 }
1055 
blendFile(DiffModel * model,const QString & fileContents)1056 bool KompareModelList::blendFile(DiffModel* model, const QString& fileContents)
1057 {
1058     if (!model)
1059     {
1060         qCDebug(LIBKOMPAREDIFF2) << "**** model is null :(";
1061         return false;
1062     }
1063 
1064     model->setBlended(true);
1065 
1066     int srcLineNo = 1, destLineNo = 1;
1067 
1068     const QStringList lines = split(fileContents);
1069     auto linesIt = lines.constBegin(), lEnd = lines.constEnd();
1070 
1071     DiffHunkList* hunks = model->hunks();
1072     qCDebug(LIBKOMPAREDIFF2) << "Hunks in hunklist: " << hunks->count();
1073     DiffHunkListIterator hunkIt = hunks->begin();
1074 
1075     DiffHunk*   newHunk = nullptr;
1076     Difference* newDiff = nullptr;
1077 
1078     // FIXME: this approach is not very good, we should first check if the hunk applies cleanly
1079     // and without offset and if not use that new linenumber with offset to compare against
1080     // This will only work for files we just diffed with kompare but not for blending where
1081     // file(s) to patch has/have potentially changed
1082 
1083     for (; hunkIt != hunks->end(); ++hunkIt)
1084     {
1085         // Do we need to insert a new hunk before this one ?
1086         DiffHunk* hunk = *hunkIt;
1087         if (srcLineNo < hunk->sourceLineNumber())
1088         {
1089             newHunk = new DiffHunk(srcLineNo, destLineNo, QString(), DiffHunk::AddedByBlend);
1090 
1091             hunkIt = ++hunks->insert(hunkIt, newHunk);
1092 
1093             newDiff = new Difference(srcLineNo, destLineNo,
1094                                      Difference::Unchanged);
1095 
1096             newHunk->add(newDiff);
1097 
1098             while (srcLineNo < hunk->sourceLineNumber() && linesIt != lEnd)
1099             {
1100                 newDiff->addSourceLine(*linesIt);
1101                 newDiff->addDestinationLine(*linesIt);
1102                 ++srcLineNo;
1103                 ++destLineNo;
1104                 ++linesIt;
1105             }
1106         }
1107 
1108         // Now we add the linecount difference for the hunk that follows
1109         int size = hunk->sourceLineCount();
1110 
1111         linesIt += size;
1112         if (linesIt > lEnd)
1113         {
1114             linesIt = lEnd;
1115         }
1116 
1117         srcLineNo += size;
1118         destLineNo += hunk->destinationLineCount();
1119     }
1120 
1121     if (linesIt != lEnd)
1122     {
1123         newHunk = new DiffHunk(srcLineNo, destLineNo, QString(), DiffHunk::AddedByBlend);
1124 
1125         model->addHunk(newHunk);
1126 
1127         newDiff = new Difference(srcLineNo, destLineNo, Difference::Unchanged);
1128 
1129         newHunk->add(newDiff);
1130 
1131         while (linesIt != lEnd)
1132         {
1133             newDiff->addSourceLine(*linesIt);
1134             newDiff->addDestinationLine(*linesIt);
1135             ++linesIt;
1136         }
1137     }
1138 #if 0
1139     DifferenceList hunkDiffList   = (*hunkIt)->differences();
1140     DifferenceListIterator diffIt = hunkDiffList.begin();
1141     DifferenceListIterator dEnd   = hunkDiffList.end();
1142     qCDebug(LIBKOMPAREDIFF2) << "Number of differences in hunkDiffList = " << diffList.count();
1143 
1144     DifferenceListIterator tempIt;
1145     Difference* diff;
1146 
1147     for (; diffIt != dEnd; ++diffIt)
1148     {
1149         diff = *diffIt;
1150         qCDebug(LIBKOMPAREDIFF2) << "*(Diff it) = " << diff;
1151         // Check if there are lines in the original file before the difference
1152         // that are not yet in the diff. If so create new Unchanged diff
1153         if (srcLineNo < diff->sourceLineNumber())
1154         {
1155             newDiff = new Difference(srcLineNo, destLineNo,
1156                                      Difference::Unchanged | Difference::AddedByBlend);
1157             newHunk->add(newDiff);
1158             while (srcLineNo < diff->sourceLineNumber() && linesIt != lEnd)
1159             {
1160 //                  qCDebug(LIBKOMPAREDIFF2) << "SourceLine = " << srcLineNo << ": " << *linesIt;
1161                 newDiff->addSourceLine(*linesIt);
1162                 newDiff->addDestinationLine(*linesIt);
1163                 ++srcLineNo;
1164                 ++destLineNo;
1165                 ++linesIt;
1166             }
1167         }
1168         // Now i've got to add that diff
1169         switch (diff->type())
1170         {
1171         case Difference::Unchanged:
1172             qCDebug(LIBKOMPAREDIFF2) << "Unchanged";
1173             for (int i = 0; i < diff->sourceLineCount(); ++i)
1174             {
1175                 if (linesIt != lEnd && *linesIt != diff->sourceLineAt(i)->string())
1176                 {
1177                     qCDebug(LIBKOMPAREDIFF2) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt;
1178                     qCDebug(LIBKOMPAREDIFF2) << "Conflict: DiffLine   = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt(i)->string();
1179 
1180                     // Do conflict resolution (well sort of)
1181                     diff->sourceLineAt(i)->setConflictString(*linesIt);
1182                     diff->setConflict(true);
1183                 }
1184 //                  qCDebug(LIBKOMPAREDIFF2) << "SourceLine = " << srcLineNo << ": " << *linesIt;
1185 //                  qCDebug(LIBKOMPAREDIFF2) << "DiffLine   = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string();
1186                 ++srcLineNo;
1187                 ++destLineNo;
1188                 ++linesIt;
1189             }
1190 
1191             tempIt = diffIt;
1192             --diffIt;
1193             diffList.remove(tempIt);
1194             newHunk->add(diff);
1195 
1196             break;
1197         case Difference::Change:
1198             qCDebug(LIBKOMPAREDIFF2) << "Change";
1199 
1200             //QStringListConstIterator saveIt = linesIt;
1201 
1202             for (int i = 0; i < diff->sourceLineCount(); ++i)
1203             {
1204                 if (linesIt != lEnd && *linesIt != diff->sourceLineAt(i)->string())
1205                 {
1206                     qCDebug(LIBKOMPAREDIFF2) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt;
1207                     qCDebug(LIBKOMPAREDIFF2) << "Conflict: DiffLine   = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt(i)->string();
1208 
1209                     // Do conflict resolution (well sort of)
1210                     diff->sourceLineAt(i)->setConflictString(*linesIt);
1211                     diff->setConflict(true);
1212                 }
1213                 ++srcLineNo;
1214                 ++destLineNo;
1215                 ++linesIt;
1216             }
1217 
1218             destLineNo += diff->destinationLineCount();
1219 
1220             tempIt = diffIt;
1221             --diffIt;
1222             diffList.remove(tempIt);
1223             newHunk->add(diff);
1224             newModel->addDiff(diff);
1225 
1226             break;
1227         case Difference::Insert:
1228             qCDebug(LIBKOMPAREDIFF2) << "Insert";
1229             destLineNo += diff->destinationLineCount();
1230             tempIt = diffIt;
1231             --diffIt;
1232             diffList.remove(tempIt);
1233             newHunk->add(diff);
1234             newModel->addDiff(diff);
1235             break;
1236         case Difference::Delete:
1237             qCDebug(LIBKOMPAREDIFF2) << "Delete";
1238             qCDebug(LIBKOMPAREDIFF2) << "Number of lines in Delete: " << diff->sourceLineCount();
1239             for (int i = 0; i < diff->sourceLineCount(); ++i)
1240             {
1241                 if (linesIt != lEnd && *linesIt != diff->sourceLineAt(i)->string())
1242                 {
1243                     qCDebug(LIBKOMPAREDIFF2) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt;
1244                     qCDebug(LIBKOMPAREDIFF2) << "Conflict: DiffLine   = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt(i)->string();
1245 
1246                     // Do conflict resolution (well sort of)
1247                     diff->sourceLineAt(i)->setConflictString(*linesIt);
1248                     diff->setConflict(true);
1249                 }
1250 
1251 //                  qCDebug(LIBKOMPAREDIFF2) << "SourceLine = " << srcLineNo << ": " << *it;
1252 //                  qCDebug(LIBKOMPAREDIFF2) << "DiffLine   = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string();
1253                 ++srcLineNo;
1254                 ++linesIt;
1255             }
1256 
1257             tempIt = diffIt;
1258             --diffIt;
1259             diffList.remove(tempIt);
1260             newHunk->add(diff);
1261             newModel->addDiff(diff);
1262             break;
1263         default:
1264             qCDebug(LIBKOMPAREDIFF2) << "****, some diff type we do not know about ???";
1265         }
1266     }
1267 }
1268 #endif
1269 
1270 /*
1271     diffList = newModel->differences();
1272 
1273     diff = diffList.first();
1274     qCDebug(LIBKOMPAREDIFF2) << "Count = " << diffList.count();
1275     for ( diff = diffList.first(); diff; diff = diffList.next() )
1276     {
1277         qCDebug(LIBKOMPAREDIFF2) << "sourcelinenumber = " << diff->sourceLineNumber();
1278     }
1279 */
1280 
1281 m_selectedModel = firstModel();
1282 
1283 m_selectedDifference = m_selectedModel->firstDifference();
1284 
1285 return true;
1286 }
1287 
show()1288 void KompareModelList::show()
1289 {
1290     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::Show Number of models = " << m_models->count();
1291     Q_EMIT modelsChanged(m_models);
1292     Q_EMIT setSelection(m_selectedModel, m_selectedDifference);
1293 }
1294 
clear()1295 void KompareModelList::clear()
1296 {
1297     if (m_models)
1298         m_models->clear();
1299 
1300     Q_EMIT modelsChanged(m_models);
1301 }
1302 
refresh()1303 void KompareModelList::refresh()
1304 {
1305     // FIXME: I can imagine blending also wants to be refreshed so make a switch case here
1306     compare(m_info->mode);
1307 }
1308 
swap()1309 void KompareModelList::swap()
1310 {
1311     //FIXME Not sure if any mode could be swapped
1312     if (m_info->mode == Kompare::ComparingFiles)
1313         compare(m_info->mode);
1314     else if (m_info->mode == Kompare::ComparingDirs)
1315         compare(m_info->mode);
1316 }
1317 
hasUnsavedChanges() const1318 bool KompareModelList::hasUnsavedChanges() const
1319 {
1320     if (modelCount() == 0)
1321         return false;
1322 
1323     DiffModelListConstIterator modelIt = m_models->constBegin();
1324     DiffModelListConstIterator endIt   = m_models->constEnd();
1325 
1326     for (; modelIt != endIt; ++modelIt)
1327     {
1328         if ((*modelIt)->hasUnsavedChanges())
1329             return true;
1330     }
1331     return false;
1332 }
1333 
modelCount() const1334 int KompareModelList::modelCount() const
1335 {
1336     return m_models ? m_models->count() : 0;
1337 }
1338 
differenceCount() const1339 int KompareModelList::differenceCount() const
1340 {
1341     return m_selectedModel ? m_selectedModel->differenceCount() : -1;
1342 }
1343 
appliedCount() const1344 int KompareModelList::appliedCount() const
1345 {
1346     return m_selectedModel ? m_selectedModel->appliedCount() : -1;
1347 }
1348 
slotKompareInfo(struct Kompare::Info * info)1349 void KompareModelList::slotKompareInfo(struct Kompare::Info* info)
1350 {
1351     m_info = info;
1352 }
1353 
setSelectedModel(DiffModel * model)1354 bool KompareModelList::setSelectedModel(DiffModel* model)
1355 {
1356     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::setSelectedModel( " << model << " )";
1357 
1358     if (model != m_selectedModel)
1359     {
1360         if (!m_models->contains(model))
1361             return false;
1362         qCDebug(LIBKOMPAREDIFF2) << "m_selectedModel (was) = " << m_selectedModel;
1363         m_modelIndex = m_models->indexOf(model);
1364         qCDebug(LIBKOMPAREDIFF2) << "m_selectedModel (is)  = " << m_selectedModel;
1365         m_selectedModel = model;
1366     }
1367 
1368     updateModelListActions();
1369 
1370     return true;
1371 }
1372 
updateModelListActions()1373 void KompareModelList::updateModelListActions()
1374 {
1375     if (m_models && m_selectedModel && m_selectedDifference)
1376     {
1377         if (m_isReadWrite && m_save)
1378         {
1379             if (m_selectedModel->appliedCount() != m_selectedModel->differenceCount())
1380                 m_applyAll->setEnabled(true);
1381             else
1382                 m_applyAll->setEnabled(false);
1383 
1384             if (m_selectedModel->appliedCount() != 0)
1385                 m_unapplyAll->setEnabled(true);
1386             else
1387                 m_unapplyAll->setEnabled(false);
1388 
1389             m_applyDifference->setEnabled(m_selectedDifference->applied() == false);
1390             m_unApplyDifference->setEnabled(m_selectedDifference->applied() == true);
1391             m_save->setEnabled(m_selectedModel->hasUnsavedChanges());
1392         }
1393         else if (m_save)
1394         {
1395             m_applyDifference->setEnabled(false);
1396             m_unApplyDifference->setEnabled(false);
1397             m_applyAll->setEnabled(false);
1398             m_unapplyAll->setEnabled(false);
1399             m_save->setEnabled(false);
1400         }
1401 
1402         m_previousFile->setEnabled(hasPrevModel());
1403         m_nextFile->setEnabled(hasNextModel());
1404         m_previousDifference->setEnabled(hasPrevDiff());
1405         m_nextDifference->setEnabled(hasNextDiff());
1406     }
1407     else
1408     {
1409         if (m_save) {
1410             m_applyDifference->setEnabled(false);
1411             m_unApplyDifference->setEnabled(false);
1412             m_applyAll->setEnabled(false);
1413             m_unapplyAll->setEnabled(false);
1414             m_save->setEnabled(false);
1415         }
1416 
1417         m_previousFile->setEnabled(false);
1418         m_nextFile->setEnabled(false);
1419         m_previousDifference->setEnabled(false);
1420         m_nextDifference->setEnabled(false);
1421     }
1422 }
1423 
hasPrevModel() const1424 bool KompareModelList::hasPrevModel() const
1425 {
1426     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::hasPrevModel()";
1427 
1428     if (m_modelIndex > 0)
1429     {
1430 //         qCDebug(LIBKOMPAREDIFF2) << "has prev model";
1431         return true;
1432     }
1433 
1434 //     qCDebug(LIBKOMPAREDIFF2) << "doesn't have a prev model, this is the first one...";
1435 
1436     return false;
1437 }
1438 
hasNextModel() const1439 bool KompareModelList::hasNextModel() const
1440 {
1441     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::hasNextModel()";
1442 
1443     if (m_modelIndex < (m_models->count() - 1))
1444     {
1445 //         qCDebug(LIBKOMPAREDIFF2) << "has next model";
1446         return true;
1447     }
1448 
1449 //     qCDebug(LIBKOMPAREDIFF2) << "doesn't have a next model, this is the last one...";
1450     return false;
1451 }
1452 
hasPrevDiff() const1453 bool KompareModelList::hasPrevDiff() const
1454 {
1455 //     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::hasPrevDiff()";
1456     int index = m_selectedModel->diffIndex();
1457 
1458     if (index > 0)
1459     {
1460 //         qCDebug(LIBKOMPAREDIFF2) << "has prev difference in same model";
1461         return true;
1462     }
1463 
1464     if (hasPrevModel())
1465     {
1466 //         qCDebug(LIBKOMPAREDIFF2) << "has prev difference but in prev model";
1467         return true;
1468     }
1469 
1470 //     qCDebug(LIBKOMPAREDIFF2) << "doesn't have a prev difference, not even in the previous model because there is no previous model";
1471 
1472     return false;
1473 }
1474 
hasNextDiff() const1475 bool KompareModelList::hasNextDiff() const
1476 {
1477 //     qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::hasNextDiff()";
1478     int index = m_selectedModel->diffIndex();
1479 
1480     if (index < (m_selectedModel->differenceCount() - 1))
1481     {
1482 //         qCDebug(LIBKOMPAREDIFF2) << "has next difference in same model";
1483         return true;
1484     }
1485 
1486     if (hasNextModel())
1487     {
1488 //         qCDebug(LIBKOMPAREDIFF2) << "has next difference but in next model";
1489         return true;
1490     }
1491 
1492 //     qCDebug(LIBKOMPAREDIFF2) << "doesn't have a next difference, not even in next model because there is no next model";
1493 
1494     return false;
1495 }
1496 
slotActionApplyDifference()1497 void KompareModelList::slotActionApplyDifference()
1498 {
1499     if (!m_selectedDifference->applied())
1500         slotApplyDifference(true);
1501     slotNextDifference();
1502     updateModelListActions();
1503 }
1504 
slotActionUnApplyDifference()1505 void KompareModelList::slotActionUnApplyDifference()
1506 {
1507     if (m_selectedDifference->applied())
1508         slotApplyDifference(false);
1509     slotPreviousDifference();
1510     updateModelListActions();
1511 }
1512 
slotActionApplyAllDifferences()1513 void KompareModelList::slotActionApplyAllDifferences()
1514 {
1515     slotApplyAllDifferences(true);
1516     updateModelListActions();
1517 }
1518 
slotActionUnapplyAllDifferences()1519 void KompareModelList::slotActionUnapplyAllDifferences()
1520 {
1521     slotApplyAllDifferences(false);
1522     updateModelListActions();
1523 }
1524 
1525 
1526 /* vim: set ts=4 sw=4 noet: */
1527