KompareDiff2

modellist.cpp
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 "modellist.h"
11#include "modellist_p.h"
12
13// lib
14#include "diffhunk.h"
15#include "parser.h"
16#include <komparediff2_logging.h>
17// KF
18#include <KActionCollection>
19#include <KDirWatch>
20#include <KIO/FileCopyJob>
21#include <KIO/MkdirJob>
22#include <KIO/StatJob>
23#include <KIO/UDSEntry>
24#include <KLocalizedString>
25#include <KStandardAction>
26// Qt
27#include <QDir>
28#include <QFile>
29#include <QList>
30#include <QMimeDatabase>
31#include <QMimeType>
32#include <QTextStream>
33// Std
34#include <algorithm>
35
36using namespace KompareDiff2;
37
38ModelList::ModelList(DiffSettings *diffSettings, QObject *parent, bool supportReadWrite)
39 : QObject(parent)
40 , d_ptr(new ModelListPrivate(diffSettings, supportReadWrite))
41{
43
44 qCDebug(KOMPAREDIFF2_LOG) << "Show me the arguments: " << diffSettings << ", " << parent;
45 d->actionCollection = new KActionCollection(this);
46 if (supportReadWrite) {
47 d->applyDifference = d->actionCollection->addAction(QStringLiteral("difference_apply"), this, &ModelList::slotActionApplyDifference);
48 d->applyDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
49 d->applyDifference->setText(i18nc("@action", "&Apply Difference"));
50 d->actionCollection->setDefaultShortcut(d->applyDifference, QKeySequence(Qt::Key_Space));
51 d->unApplyDifference = d->actionCollection->addAction(QStringLiteral("difference_unapply"), this, &ModelList::slotActionUnApplyDifference);
52 d->unApplyDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
53 d->unApplyDifference->setText(i18nc("@action", "Un&apply Difference"));
54 d->actionCollection->setDefaultShortcut(d->unApplyDifference, QKeySequence(Qt::Key_Backspace));
55 d->applyAll = d->actionCollection->addAction(QStringLiteral("difference_applyall"), this, &ModelList::slotActionApplyAllDifferences);
56 d->applyAll->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")));
57 d->applyAll->setText(i18nc("@action", "App&ly All"));
58 d->actionCollection->setDefaultShortcut(d->applyAll, QKeySequence(Qt::CTRL | Qt::Key_A));
59 d->unapplyAll = d->actionCollection->addAction(QStringLiteral("difference_unapplyall"), this, &ModelList::slotActionUnapplyAllDifferences);
60 d->unapplyAll->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left-double")));
61 d->unapplyAll->setText(i18nc("@action", "&Unapply All"));
62 d->actionCollection->setDefaultShortcut(d->unapplyAll, QKeySequence(Qt::CTRL | Qt::Key_U));
63 } else {
64 d->applyDifference = nullptr;
65 d->unApplyDifference = nullptr;
66 d->applyAll = nullptr;
67 d->unapplyAll = nullptr;
68 }
69 d->previousFile = d->actionCollection->addAction(QStringLiteral("difference_previousfile"), this, &ModelList::slotPreviousModel);
70 d->previousFile->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up-double")));
71 d->previousFile->setText(i18nc("@action", "P&revious File"));
72 d->actionCollection->setDefaultShortcut(d->previousFile, QKeySequence(Qt::CTRL | Qt::Key_PageUp));
73 d->nextFile = d->actionCollection->addAction(QStringLiteral("difference_nextfile"), this, &ModelList::slotNextModel);
74 d->nextFile->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down-double")));
75 d->nextFile->setText(i18nc("@action", "N&ext File"));
76 d->actionCollection->setDefaultShortcut(d->nextFile, QKeySequence(Qt::CTRL | Qt::Key_PageDown));
77 d->previousDifference = d->actionCollection->addAction(QStringLiteral("difference_previous"), this, &ModelList::slotPreviousDifference);
78 d->previousDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up")));
79 d->previousDifference->setText(i18nc("@action", "&Previous Difference"));
80 d->actionCollection->setDefaultShortcut(d->previousDifference, QKeySequence(Qt::CTRL | Qt::Key_Up));
81 d->nextDifference = d->actionCollection->addAction(QStringLiteral("difference_next"), this, &ModelList::slotNextDifference);
82 d->nextDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down")));
83 d->nextDifference->setText(i18nc("@action", "&Next Difference"));
84 d->actionCollection->setDefaultShortcut(d->nextDifference, QKeySequence(Qt::CTRL | Qt::Key_Down));
85 d->previousDifference->setEnabled(false);
86 d->nextDifference->setEnabled(false);
87
88 if (supportReadWrite) {
89 d->save = KStandardAction::save(this, &ModelList::slotSaveDestination, d->actionCollection);
90 d->save->setEnabled(false);
91 } else {
92 d->save = nullptr;
93 }
94
95 d->updateModelListActions();
96}
97
98ModelList::~ModelList() = default;
99
100bool ModelList::compare()
101{
102 Q_D(ModelList);
103
104 bool result = false;
105
106 bool sourceIsDirectory = ModelListPrivate::isDirectory(d->info->localSource);
107 bool destinationIsDirectory = ModelListPrivate::isDirectory(d->info->localDestination);
108
109 if (sourceIsDirectory && destinationIsDirectory) {
110 d->info->mode = ComparingDirs;
111 result = compare(d->info->mode);
112 } else if (!sourceIsDirectory && !destinationIsDirectory) {
113 QFile sourceFile(d->info->localSource);
114 sourceFile.open(QIODevice::ReadOnly);
115 QMimeDatabase db;
116 QString sourceMimeType = (db.mimeTypeForData(sourceFile.readAll())).name();
117 sourceFile.close();
118 qCDebug(KOMPAREDIFF2_LOG) << "Mimetype source : " << sourceMimeType;
119
120 QFile destinationFile(d->info->localDestination);
121 destinationFile.open(QIODevice::ReadOnly);
122 QString destinationMimeType = (db.mimeTypeForData(destinationFile.readAll())).name();
123 destinationFile.close();
124 qCDebug(KOMPAREDIFF2_LOG) << "Mimetype destination: " << destinationMimeType;
125
126 // Not checking if it is a text file/something diff can even compare, we'll let diff handle that
127 if (!ModelListPrivate::isDiff(sourceMimeType) && ModelListPrivate::isDiff(destinationMimeType)) {
128 qCDebug(KOMPAREDIFF2_LOG) << "Blending destination into source...";
129 d->info->mode = BlendingFile;
130 result = openFileAndDiff();
131 } else if (ModelListPrivate::isDiff(sourceMimeType) && !ModelListPrivate::isDiff(destinationMimeType)) {
132 qCDebug(KOMPAREDIFF2_LOG) << "Blending source into destination...";
133 d->info->mode = BlendingFile;
134 // Swap source and destination before calling this
135 d->info->swapSourceWithDestination();
136 // Do we need to notify anyone we swapped source and destination?
137 // No we do not need to notify anyone about swapping source with destination
138 result = openFileAndDiff();
139 } else {
140 qCDebug(KOMPAREDIFF2_LOG) << "Comparing source with destination";
141 d->info->mode = ComparingFiles;
142 result = compare(d->info->mode);
143 }
144 } else if (sourceIsDirectory && !destinationIsDirectory) {
145 d->info->mode = BlendingDir;
146 result = openDirAndDiff();
147 } else {
148 d->info->mode = BlendingDir;
149 // Swap source and destination first in d->info
150 d->info->swapSourceWithDestination();
151 // Do we need to notify anyone we swapped source and destination?
152 // No we do not need to notify anyone about swapping source with destination
153 result = openDirAndDiff();
154 }
155
156 return result;
157}
158
159bool ModelList::compare(Mode mode)
160{
161 Q_D(ModelList);
162
163 clear(); // Destroy the old models...
164
165 d->diffProcess = std::make_unique<KompareProcess>(d->diffSettings, Custom, d->info->localSource, d->info->localDestination, QString(), mode);
166 d->diffProcess->setEncoding(d->encoding);
167
168 connect(d->diffProcess.get(), &KompareProcess::diffHasFinished, this, &ModelList::slotDiffProcessFinished);
169
170 Q_EMIT status(RunningDiff);
171 d->diffProcess->start();
172
173 return true;
174}
175
176bool ModelList::openFileAndDiff()
177{
178 Q_D(ModelList);
179
180 clear();
181
182 if (parseDiffOutput(d->readFile(d->info->localDestination)) != 0) {
183 Q_EMIT error(i18n("<qt>No models or no differences, this file: <b>%1</b>, is not a valid diff file.</qt>", d->info->destination.url()));
184 return false;
185 }
186
187 d->setDepthAndApplied();
188
189 if (!blendOriginalIntoModelList(d->info->localSource)) {
190 qCDebug(KOMPAREDIFF2_LOG) << "Oops cant blend original file into modellist : " << d->info->localSource;
191 Q_EMIT error(
192 i18n("<qt>There were problems applying the diff <b>%1</b> to the file <b>%2</b>.</qt>", d->info->destination.url(), d->info->source.url()));
193 return false;
194 }
195
196 d->updateModelListActions();
197 show();
198
199 return true;
200}
201
202bool ModelList::openDirAndDiff()
203{
204 Q_D(ModelList);
205
206 clear();
207
208 if (parseDiffOutput(d->readFile(d->info->localDestination)) != 0) {
209 Q_EMIT error(i18n("<qt>No models or no differences, this file: <b>%1</b>, is not a valid diff file.</qt>", d->info->destination.url()));
210 return false;
211 }
212
213 d->setDepthAndApplied();
214
215 // Do our thing :)
216 if (!blendOriginalIntoModelList(d->info->localSource)) {
217 // Trouble blending the original into the model
218 qCDebug(KOMPAREDIFF2_LOG) << "Oops cant blend original dir into modellist : " << d->info->localSource;
219 Q_EMIT error(
220 i18n("<qt>There were problems applying the diff <b>%1</b> to the folder <b>%2</b>.</qt>", d->info->destination.url(), d->info->source.url()));
221 return false;
222 }
223
224 d->updateModelListActions();
225 show();
226
227 return true;
228}
229
231{
232 Q_D(ModelList);
233
234 // Unnecessary safety check! We can now guarantee that saving is only possible when there is a model and there are unsaved changes
235 if (d->selectedModel) {
236 saveDestination(d->selectedModel);
237 if (d->save)
238 d->save->setEnabled(false);
239 Q_EMIT updateActions();
240 }
241}
242
243bool ModelList::saveDestination(DiffModel *model)
244{
245 Q_D(ModelList);
246
247 qCDebug(KOMPAREDIFF2_LOG) << "ModelList::saveDestination: ";
248
249 // Unnecessary safety check, we can guarantee there are unsaved changes!!!
250 if (!model->hasUnsavedChanges())
251 return true;
252
253 QTemporaryFile temp;
254
255 if (!temp.open()) {
256 Q_EMIT error(i18n("Could not open a temporary file."));
257 temp.remove();
258 return false;
259 }
260
261 QTextStream stream(&temp);
262 QStringList list;
263
264 const DiffHunkList *hunks = model->hunks();
265
266 for (const DiffHunk *hunk : *hunks) {
267 const DifferenceList differences = hunk->differences();
268
269 for (const Difference *diff : differences) {
270 if (!diff->applied()) {
271 const DifferenceStringList destinationLines = diff->destinationLines();
272 for (const DifferenceString *diffString : destinationLines) {
273 list.append(diffString->string());
274 }
275 } else {
276 const DifferenceStringList sourceLines = diff->sourceLines();
277 for (const DifferenceString *diffString : sourceLines) {
278 list.append(diffString->string());
279 }
280 }
281 }
282 }
283
284 // qCDebug(KOMPAREDIFF2_LOG) << "Everything: " << endl << list.join( "\n" );
285
286 if (list.count() > 0)
287 stream << list.join(QString());
288 if (temp.error() != QFile::NoError) {
289 Q_EMIT error(i18n("<qt>Could not write to the temporary file <b>%1</b>, deleting it.</qt>", temp.fileName()));
290 temp.remove();
291 return false;
292 }
293
294 temp.close();
295 if (temp.error() != QFile::NoError) {
296 Q_EMIT error(i18n("<qt>Could not write to the temporary file <b>%1</b>, deleting it.</qt>", temp.fileName()));
297 temp.remove();
298 return false;
299 }
300
301 bool result = false;
302
303 // Make sure the destination directory exists, it is possible when using -N to not have the destination dir/file available
304 if (d->info->mode == ComparingDirs) {
305 // Don't use destination which was used for creating the diff directly, use the original URL!!!
306 // FIXME!!! Wrong destination this way! Need to add the sub directory to the url!!!!
307 qCDebug(KOMPAREDIFF2_LOG) << "Tempfilename (save) : " << temp.fileName();
308 qCDebug(KOMPAREDIFF2_LOG) << "Model->path+file : " << model->destinationPath() << model->destinationFile();
309 qCDebug(KOMPAREDIFF2_LOG) << "info->localdest : " << d->info->localDestination;
310 QString tmp = model->destinationPath();
311 if (tmp.startsWith(d->info->localDestination)) // It should, if not serious trouble...
312 tmp.remove(0, d->info->localDestination.size());
313 qCDebug(KOMPAREDIFF2_LOG) << "DestinationURL : " << d->info->destination;
314 qCDebug(KOMPAREDIFF2_LOG) << "tmp : " << tmp;
315 KIO::UDSEntry entry;
316 QUrl fullDestinationPath = d->info->destination;
317 fullDestinationPath.setPath(fullDestinationPath.path() + tmp);
318 qCDebug(KOMPAREDIFF2_LOG) << "fullDestinationPath : " << fullDestinationPath;
319 KIO::StatJob *statJob = KIO::stat(fullDestinationPath);
320 if (!statJob->exec()) {
321 entry = statJob->statResult();
322 KIO::MkdirJob *mkdirJob = KIO::mkdir(fullDestinationPath);
323 if (!mkdirJob->exec()) {
324 Q_EMIT error(i18n("<qt>Could not create destination directory <b>%1</b>.\nThe file has not been saved.</qt>", fullDestinationPath.path()));
325 return false;
326 }
327 }
328 fullDestinationPath.setPath(fullDestinationPath.path() + model->destinationFile());
329 KIO::FileCopyJob *copyJob = KIO::file_copy(QUrl::fromLocalFile(temp.fileName()), fullDestinationPath, -1, KIO::Overwrite);
330 result = copyJob->exec();
331 } else {
332 qCDebug(KOMPAREDIFF2_LOG) << "Tempfilename : " << temp.fileName();
333 qCDebug(KOMPAREDIFF2_LOG) << "DestinationURL : " << d->info->destination;
334
335 // Get permissions of existing file and copy temporary file with the same permissions
336 int permissions = -1;
337 KIO::StatJob *statJob = KIO::stat(d->info->destination);
338 result = statJob->exec();
339 if (result)
340 permissions = statJob->statResult().numberValue(KIO::UDSEntry::UDS_ACCESS);
341
342 KIO::FileCopyJob *copyJob = KIO::file_copy(QUrl::fromLocalFile(temp.fileName()), d->info->destination, permissions, KIO::Overwrite);
343 result = copyJob->exec();
344 qCDebug(KOMPAREDIFF2_LOG) << "true or false?" << result;
345 }
346
347 if (!result) {
348 // FIXME: Wrong first argument given in case of comparing directories!
349 Q_EMIT error(
350 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 "
351 "can manually copy it to the right place.</qt>",
352 d->info->destination.url(),
353 temp.fileName()));
354 // Don't remove file when we delete temp and don't leak it.
355 temp.setAutoRemove(false);
356 } else {
357 temp.remove();
358 }
359
360 // If saving was fine set all differences to saved so we can start again with a clean slate
361 if (result) {
362 const DifferenceList *differences = model->differences();
363
364 for (Difference *diff : *differences) {
365 diff->setUnsaved(false);
366 }
367 }
368
369 return true;
370}
371
372bool ModelList::saveAll()
373{
374 Q_D(ModelList);
375
376 if (modelCount() == 0)
377 return false;
378
379 for (DiffModel *model : std::as_const(*d->models)) {
380 if (!saveDestination(model))
381 return false;
382 }
383
384 return true;
385}
386
387void ModelList::setEncoding(const QString &encoding)
388{
389 Q_D(ModelList);
390
391 d->encoding = encoding;
392 if (!encoding.compare(QLatin1String("default"), Qt::CaseInsensitive)) {
393 d->textDecoder = QStringDecoder(QStringDecoder::System);
394 } else {
395 qCDebug(KOMPAREDIFF2_LOG) << "Encoding : " << encoding;
396 d->textDecoder = QStringDecoder(encoding.toUtf8().constData());
397 qCDebug(KOMPAREDIFF2_LOG) << "TextCodec: " << d->textDecoder.name();
398 if (!d->textDecoder.isValid())
399 d->textDecoder = QStringDecoder(QStringDecoder::System);
400 }
401 qCDebug(KOMPAREDIFF2_LOG) << "TextCodec: " << d->textDecoder.name();
402}
403
404void ModelList::setReadWrite(bool isReadWrite)
405{
406 Q_D(ModelList);
407
408 if (d->isReadWrite == isReadWrite)
409 return;
410
411 d->isReadWrite = isReadWrite;
412 d->updateModelListActions();
413}
414
415bool ModelList::isReadWrite() const
416{
417 Q_D(const ModelList);
418
419 return d->isReadWrite;
420}
421
422void ModelList::slotDiffProcessFinished(bool success)
423{
424 Q_D(ModelList);
425
426 if (success) {
427 Q_EMIT status(Parsing);
428 if (parseDiffOutput(d->diffProcess->diffOutput()) != 0) {
429 Q_EMIT error(i18n("Could not parse diff output."));
430 } else {
431 if (d->info->mode != ShowingDiff) {
432 qCDebug(KOMPAREDIFF2_LOG) << "Blend this crap please and do not give me any conflicts...";
433 blendOriginalIntoModelList(d->info->localSource);
434 }
435 d->updateModelListActions();
436 show();
437 }
438 Q_EMIT status(FinishedParsing);
439 } else if (d->diffProcess->exitStatus() == 0) {
440 Q_EMIT error(i18n("The files are identical."));
441 } else {
442 Q_EMIT error(d->diffProcess->stdErr());
443 }
444
445 // delay deletion, see bug 182792
446 d->diffProcess.release()->deleteLater();
447}
448
449bool ModelList::openDiff(const QString &diffFile)
450{
451 Q_D(ModelList);
452
453 qCDebug(KOMPAREDIFF2_LOG) << "Stupid :) Url = " << diffFile;
454
455 if (diffFile.isEmpty())
456 return false;
457
458 QString diff = d->readFile(diffFile);
459
460 clear(); // Clear the current models
461
462 Q_EMIT status(Parsing);
463
464 if (parseDiffOutput(diff) != 0) {
465 Q_EMIT error(i18n("Could not parse diff output."));
466 return false;
467 }
468
469 d->updateModelListActions();
470 show();
471
472 Q_EMIT status(FinishedParsing);
473
474 return true;
475}
476
477bool ModelList::parseAndOpenDiff(const QString &diff)
478{
479 Q_D(ModelList);
480
481 clear(); // Clear the current models
482
483 Q_EMIT status(Parsing);
484
485 if (parseDiffOutput(diff) != 0) {
486 Q_EMIT error(i18n("Could not parse diff output."));
487 return false;
488 }
489
490 d->updateModelListActions();
491 show();
492
493 Q_EMIT status(FinishedParsing);
494 return true;
495}
496
497QString ModelList::recreateDiff() const
498{
499 Q_D(const ModelList);
500
501 QString diff;
502
503 for (const DiffModel *model : *d->models) {
504 diff += model->recreateDiff();
505 }
506
507 return diff;
508}
509
510bool ModelList::saveDiff(const QString &url, const QString &directory, DiffSettings *diffSettings)
511{
512 Q_D(ModelList);
513
514 qCDebug(KOMPAREDIFF2_LOG) << "ModelList::saveDiff: ";
515
516 d->diffTemp = std::make_unique<QTemporaryFile>();
517 d->diffURL = QUrl(url); // ### TODO the "url" argument should be a QUrl
518
519 if (!d->diffTemp->open()) {
520 Q_EMIT error(i18n("Could not open a temporary file."));
521 d->diffTemp->remove();
522 d->diffTemp.reset();
523 return false;
524 }
525
526 d->diffProcess = std::make_unique<KompareProcess>(diffSettings, Custom, d->info->localSource, d->info->localDestination, directory);
527 d->diffProcess->setEncoding(d->encoding);
528
529 connect(d->diffProcess.get(), &KompareProcess::diffHasFinished, this, &ModelList::slotWriteDiffOutput);
530
531 Q_EMIT status(RunningDiff);
532 d->diffProcess->start();
533 return true;
534}
535
536void ModelList::slotWriteDiffOutput(bool success)
537{
538 Q_D(ModelList);
539
540 qCDebug(KOMPAREDIFF2_LOG) << "Success = " << success;
541
542 if (success) {
543 QTextStream stream(d->diffTemp.get());
544
545 stream << d->diffProcess->diffOutput();
546
547 d->diffTemp->close();
548
549 if (false /*|| d->diffTemp->status() != 0 */) {
550 Q_EMIT error(i18n("Could not write to the temporary file."));
551 }
552
553 KIO::FileCopyJob *copyJob = KIO::file_copy(QUrl::fromLocalFile(d->diffTemp->fileName()), d->diffURL);
554 copyJob->exec();
555
556 Q_EMIT status(FinishedWritingDiff);
557 }
558
559 d->diffURL = QUrl();
560 d->diffTemp->remove();
561 d->diffTemp.reset();
562
563 d->diffProcess.reset();
564}
565
566void ModelList::slotSelectionChanged(const KompareDiff2::DiffModel *model, const KompareDiff2::Difference *diff)
567{
568 Q_D(ModelList);
569
570 // This method will signal all the other objects about a change in selection,
571 // it will emit setSelection( const DiffModel*, const Difference* ) to all who are connected
572 qCDebug(KOMPAREDIFF2_LOG) << "ModelList::slotSelectionChanged( " << model << ", " << diff << " )";
573 qCDebug(KOMPAREDIFF2_LOG) << "Sender is : " << sender()->metaObject()->className();
574// qCDebug(KOMPAREDIFF2_LOG) << kBacktrace();
575
576 d->selectedModel = const_cast<DiffModel *>(model);
577 d->modelIndex = d->models->indexOf(d->selectedModel);
578 qCDebug(KOMPAREDIFF2_LOG) << "d->modelIndex = " << d->modelIndex;
579 d->selectedDifference = const_cast<Difference *>(diff);
580
581 d->selectedModel->setSelectedDifference(d->selectedDifference);
582
583 // setSelected* search for the argument in the lists and return false if not found
584 // if found they return true and set the d->selected*
585 if (!d->setSelectedModel(d->selectedModel)) {
586 // Backup plan
587 d->selectedModel = d->firstModel();
588 d->selectedDifference = d->selectedModel->firstDifference();
589 } else if (!d->selectedModel->setSelectedDifference(d->selectedDifference)) {
590 // Another backup plan
591 d->selectedDifference = d->selectedModel->firstDifference();
592 }
593
594 Q_EMIT setSelection(model, diff);
595 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel),
596 d->selectedModel->findDifference(d->selectedDifference),
597 modelCount(),
598 differenceCount(),
599 d->selectedModel->appliedCount());
600
601 d->updateModelListActions();
602}
603
604void ModelList::slotSelectionChanged(const KompareDiff2::Difference *diff)
605{
606 Q_D(ModelList);
607
608 // This method will emit setSelection( const Difference* ) to whomever is listening
609 // when for instance in kompareview the selection has changed
610 qCDebug(KOMPAREDIFF2_LOG) << "ModelList::slotSelectionChanged( " << diff << " )";
611 qCDebug(KOMPAREDIFF2_LOG) << "Sender is : " << sender()->metaObject()->className();
612
613 d->selectedDifference = const_cast<Difference *>(diff);
614
615 if (!d->selectedModel->setSelectedDifference(d->selectedDifference)) {
616 // Backup plan
617 d->selectedDifference = d->selectedModel->firstDifference();
618 }
619
620 Q_EMIT setSelection(diff);
621 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel),
622 d->selectedModel->findDifference(d->selectedDifference),
623 modelCount(),
624 differenceCount(),
625 d->selectedModel->appliedCount());
626
627 d->updateModelListActions();
628}
629
630void ModelList::slotPreviousModel()
631{
632 Q_D(ModelList);
633
634 if ((d->selectedModel = d->prevModel()) != nullptr) {
635 d->selectedDifference = d->selectedModel->firstDifference();
636 } else {
637 d->selectedModel = d->firstModel();
638 d->selectedDifference = d->selectedModel->firstDifference();
639 }
640
641 Q_EMIT setSelection(d->selectedModel, d->selectedDifference);
642 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel),
643 d->selectedModel->findDifference(d->selectedDifference),
644 modelCount(),
645 differenceCount(),
646 d->selectedModel->appliedCount());
647 d->updateModelListActions();
648}
649
650void ModelList::slotNextModel()
651{
652 Q_D(ModelList);
653
654 if ((d->selectedModel = d->nextModel()) != nullptr) {
655 d->selectedDifference = d->selectedModel->firstDifference();
656 } else {
657 d->selectedModel = d->lastModel();
658 d->selectedDifference = d->selectedModel->firstDifference();
659 }
660
661 Q_EMIT setSelection(d->selectedModel, d->selectedDifference);
662 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel),
663 d->selectedModel->findDifference(d->selectedDifference),
664 modelCount(),
665 differenceCount(),
666 d->selectedModel->appliedCount());
667 d->updateModelListActions();
668}
669
670Mode ModelList::mode() const
671{
672 Q_D(const ModelList);
673
674 return d->info->mode;
675}
676
677const DiffModelList *ModelList::models() const
678{
679 Q_D(const ModelList);
680
681 return d->models.get();
682}
683
684KActionCollection *ModelList::actionCollection() const
685{
686 Q_D(const ModelList);
687
688 return d->actionCollection;
689}
690
691void ModelList::slotPreviousDifference()
692{
693 Q_D(ModelList);
694
695 qCDebug(KOMPAREDIFF2_LOG) << "slotPreviousDifference called";
696 if ((d->selectedDifference = d->selectedModel->prevDifference()) != nullptr) {
697 Q_EMIT setSelection(d->selectedDifference);
698 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel),
699 d->selectedModel->findDifference(d->selectedDifference),
700 modelCount(),
701 differenceCount(),
702 d->selectedModel->appliedCount());
703 d->updateModelListActions();
704 return;
705 }
706
707 qCDebug(KOMPAREDIFF2_LOG) << "**** no previous difference... ok lets find the previous model...";
708
709 if ((d->selectedModel = d->prevModel()) != nullptr) {
710 d->selectedDifference = d->selectedModel->lastDifference();
711
712 Q_EMIT setSelection(d->selectedModel, d->selectedDifference);
713 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel),
714 d->selectedModel->findDifference(d->selectedDifference),
715 modelCount(),
716 differenceCount(),
717 d->selectedModel->appliedCount());
718 d->updateModelListActions();
719 return;
720 }
721
722 qCDebug(KOMPAREDIFF2_LOG) << "**** !!! No previous model, ok backup plan activated...";
723
724 // Backup plan
725 d->selectedModel = d->firstModel();
726 d->selectedDifference = d->selectedModel->firstDifference();
727
728 Q_EMIT setSelection(d->selectedModel, d->selectedDifference);
729 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel),
730 d->selectedModel->findDifference(d->selectedDifference),
731 modelCount(),
732 differenceCount(),
733 d->selectedModel->appliedCount());
734 d->updateModelListActions();
735}
736
737void ModelList::slotNextDifference()
738{
739 Q_D(ModelList);
740
741 qCDebug(KOMPAREDIFF2_LOG) << "slotNextDifference called";
742 if ((d->selectedDifference = d->selectedModel->nextDifference()) != nullptr) {
743 Q_EMIT setSelection(d->selectedDifference);
744 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel),
745 d->selectedModel->findDifference(d->selectedDifference),
746 modelCount(),
747 differenceCount(),
748 d->selectedModel->appliedCount());
749 d->updateModelListActions();
750 return;
751 }
752
753 qCDebug(KOMPAREDIFF2_LOG) << "**** no next difference... ok lets find the next model...";
754
755 if ((d->selectedModel = d->nextModel()) != nullptr) {
756 d->selectedDifference = d->selectedModel->firstDifference();
757
758 Q_EMIT setSelection(d->selectedModel, d->selectedDifference);
759 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel),
760 d->selectedModel->findDifference(d->selectedDifference),
761 modelCount(),
762 differenceCount(),
763 d->selectedModel->appliedCount());
764 d->updateModelListActions();
765 return;
766 }
767
768 qCDebug(KOMPAREDIFF2_LOG) << "**** !!! No next model, ok backup plan activated...";
769
770 // Backup plan
771 d->selectedModel = d->lastModel();
772 d->selectedDifference = d->selectedModel->lastDifference();
773
774 Q_EMIT setSelection(d->selectedModel, d->selectedDifference);
775 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel),
776 d->selectedModel->findDifference(d->selectedDifference),
777 modelCount(),
778 differenceCount(),
779 d->selectedModel->appliedCount());
780 d->updateModelListActions();
781}
782
783void ModelList::slotApplyDifference(bool apply)
784{
785 Q_D(ModelList);
786
787 d->selectedModel->applyDifference(apply);
788 Q_EMIT applyDifference(apply);
789}
790
791void ModelList::slotApplyAllDifferences(bool apply)
792{
793 Q_D(ModelList);
794
795 d->selectedModel->applyAllDifferences(apply);
796 Q_EMIT applyAllDifferences(apply);
797}
798
799int ModelList::parseDiffOutput(const QString &diff)
800{
801 Q_D(ModelList);
802
803 qCDebug(KOMPAREDIFF2_LOG) << "ModelList::parseDiffOutput";
804 Q_EMIT diffString(diff);
805
806 QStringList diffLines = ModelListPrivate::split(diff);
807
808 std::unique_ptr<Parser> parser = std::make_unique<Parser>(this);
809 bool malformed = false;
810 d->models.reset(parser->parse(diffLines, &malformed));
811
812 d->info->generator = parser->generator();
813 d->info->format = parser->format();
814
815 parser.reset();
816
817 if (d->models) {
818 if (malformed) {
819 qCDebug(KOMPAREDIFF2_LOG) << "Malformed diff";
820 Q_EMIT error(i18n("The diff is malformed. Some lines could not be parsed and will not be displayed in the diff view."));
821 // proceed anyway with the lines which have been parsed
822 }
823 d->selectedModel = d->firstModel();
824 qCDebug(KOMPAREDIFF2_LOG) << "Ok there are differences...";
825 d->selectedDifference = d->selectedModel->firstDifference();
826 Q_EMIT setStatusBarModelInfo(0, 0, modelCount(), differenceCount(), 0);
827 } else {
828 // Wow trouble, no models, so no differences...
829 qCDebug(KOMPAREDIFF2_LOG) << "Now i'll be damned, there should be models here !!!";
830 return -1;
831 }
832
833 return 0;
834}
835
836bool ModelList::blendOriginalIntoModelList(const QString &localURL)
837{
838 Q_D(ModelList);
839
840 qCDebug(KOMPAREDIFF2_LOG) << "Hurrah we are blending...";
841 QFileInfo fi(localURL);
842
843 bool result = false;
844
845 QString fileContents;
846
847 if (fi.isDir()) { // is a dir
848 qCDebug(KOMPAREDIFF2_LOG) << "Blend Dir";
849// QDir dir( localURL, QString(), QDir::Name|QDir::DirsFirst, QDir::TypeMask );
850 for (DiffModel *model : std::as_const(*d->models)) {
851 qCDebug(KOMPAREDIFF2_LOG) << "Model : " << model;
852 QString filename = model->source();
853 if (!filename.startsWith(localURL))
854 filename = QDir(localURL).filePath(filename);
855 QFileInfo fi2(filename);
856 if (fi2.exists()) {
857 qCDebug(KOMPAREDIFF2_LOG) << "Reading from: " << filename;
858 fileContents = d->readFile(filename);
859 result = d->blendFile(model, fileContents);
860 } else {
861 qCDebug(KOMPAREDIFF2_LOG) << "File " << filename << " does not exist !";
862 qCDebug(KOMPAREDIFF2_LOG) << "Assume empty file !";
863 fileContents.truncate(0);
864 result = d->blendFile(model, fileContents);
865 }
866 }
867 qCDebug(KOMPAREDIFF2_LOG) << "End of Blend Dir";
868 } else if (fi.isFile()) { // is a file
869 qCDebug(KOMPAREDIFF2_LOG) << "Blend File";
870 qCDebug(KOMPAREDIFF2_LOG) << "Reading from: " << localURL;
871 fileContents = d->readFile(localURL);
872
873 result = d->blendFile((*d->models)[0], fileContents);
874 qCDebug(KOMPAREDIFF2_LOG) << "End of Blend File";
875 }
876
877 return result;
878}
879
880void ModelList::show()
881{
882 Q_D(ModelList);
883
884 qCDebug(KOMPAREDIFF2_LOG) << "ModelList::Show Number of models = " << d->models->count();
885 Q_EMIT modelsChanged(d->models.get());
886 Q_EMIT setSelection(d->selectedModel, d->selectedDifference);
887}
888
889const DiffModel *ModelList::modelAt(int i) const
890{
891 Q_D(const ModelList);
892
893 return d->models->at(i);
894}
895
896DiffModel *ModelList::modelAt(int i)
897{
898 Q_D(ModelList);
899
900 return d->models->at(i);
901}
902
903int ModelList::findModel(DiffModel *model) const
904{
905 Q_D(const ModelList);
906
907 return d->models->indexOf(model);
908}
909
910int ModelList::currentModel() const
911{
912 Q_D(const ModelList);
913
914 return d->models->indexOf(d->selectedModel);
915}
916
917int ModelList::currentDifference() const
918{
919 Q_D(const ModelList);
920
921 return d->selectedModel ? d->selectedModel->findDifference(d->selectedDifference) : -1;
922}
923
924const DiffModel *ModelList::selectedModel() const
925{
926 Q_D(const ModelList);
927
928 return d->selectedModel;
929}
930
931const Difference *ModelList::selectedDifference() const
932{
933 Q_D(const ModelList);
934
935 return d->selectedDifference;
936}
937
938void ModelList::clear()
939{
940 Q_D(ModelList);
941
942 if (d->models)
943 d->models->clear();
944
945 Q_EMIT modelsChanged(d->models.get());
946}
947
948void ModelList::refresh()
949{
950 Q_D(ModelList);
951
952 // FIXME: I can imagine blending also wants to be refreshed so make a switch case here
953 compare(d->info->mode);
954}
955
956void ModelList::swap()
957{
958 Q_D(ModelList);
959
960 // FIXME Not sure if any mode could be swapped
961 if (d->info->mode == ComparingFiles)
962 compare(d->info->mode);
963 else if (d->info->mode == ComparingDirs)
964 compare(d->info->mode);
965}
966
967bool ModelList::hasUnsavedChanges() const
968{
969 Q_D(const ModelList);
970
971 if (!d->models) {
972 return false;
973 }
974
975 return std::any_of(d->models->constBegin(), d->models->constEnd(), [] (const DiffModel *model) {
976 return model->hasUnsavedChanges();
977 });
978}
979
980int ModelList::modelCount() const
981{
982 Q_D(const ModelList);
983
984 return d->models ? d->models->count() : 0;
985}
986
987int ModelList::differenceCount() const
988{
989 Q_D(const ModelList);
990
991 return d->selectedModel ? d->selectedModel->differenceCount() : -1;
992}
993
994int ModelList::appliedCount() const
995{
996 Q_D(const ModelList);
997
998 return d->selectedModel ? d->selectedModel->appliedCount() : -1;
999}
1000
1001void ModelList::slotKompareInfo(Info *info)
1002{
1003 Q_D(ModelList);
1004
1005 d->info = info;
1006}
1007
1008void ModelList::slotActionApplyDifference()
1009{
1010 Q_D(ModelList);
1011
1012 if (!d->selectedDifference->applied())
1013 slotApplyDifference(true);
1014 slotNextDifference();
1015 d->updateModelListActions();
1016}
1017
1018void ModelList::slotActionUnApplyDifference()
1019{
1020 Q_D(ModelList);
1021
1022 if (d->selectedDifference->applied())
1023 slotApplyDifference(false);
1024 slotPreviousDifference();
1025 d->updateModelListActions();
1026}
1027
1028void ModelList::slotActionApplyAllDifferences()
1029{
1030 Q_D(ModelList);
1031
1032 slotApplyAllDifferences(true);
1033 d->updateModelListActions();
1034}
1035
1036void ModelList::slotActionUnapplyAllDifferences()
1037{
1038 Q_D(ModelList);
1039
1040 slotApplyAllDifferences(false);
1041 d->updateModelListActions();
1042}
1043
1044#include "moc_modellist.cpp"
const UDSEntry & statResult() const
long long numberValue(uint field, long long defaultValue=0) const
bool exec()
A list of DiffModel.
A model describing the differences between two files.
Definition diffmodel.h:31
The settings for a diff.
void slotSaveDestination()
Save the currently selected destination in a multi-file diff, or the single destination if a single f...
Q_SCRIPTABLE CaptureState status()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
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)
KompareDiff2 namespace.
Mode
Mode.
Definition global.h:46
@ ComparingFiles
compareFiles
Definition global.h:47
@ ComparingDirs
compareDirs
Definition global.h:50
@ BlendingFile
openFileAndDiff
Definition global.h:53
@ BlendingDir
openDirAndDiff
Definition global.h:52
@ ShowingDiff
openDiff
Definition global.h:51
const char * constData() const const
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)
qsizetype count() const const
const char * className() const const
QMimeType mimeTypeForData(QIODevice *device) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual const QMetaObject * metaObject() const const
QObject * sender() const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
bool isEmpty() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
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)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 22 2024 12:05:06 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.