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