KompareDiff2

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

KDE's Doxygen guidelines are available online.