File indexing completed on 2024-11-17 05:10:23
0001 /* 0002 SPDX-FileCopyrightText: 2001-2005,2009 Otto Bruggeman <otto.bruggeman@home.nl> 0003 SPDX-FileCopyrightText: 2001-2003 John Firebaugh <jfirebaugh@kde.org> 0004 SPDX-FileCopyrightText: 2007-2010 Kevin Kofler <kevin.kofler@chello.at> 0005 SPDX-FileCopyrightText: 2012 Jean -Nicolas Artaud <jeannicolasartaud@gmail.com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "modellist.h" 0011 #include "modellist_p.h" 0012 0013 // lib 0014 #include "diffhunk.h" 0015 #include "parser.h" 0016 #include <komparediff2_logging.h> 0017 // KF 0018 #include <KActionCollection> 0019 #include <KDirWatch> 0020 #include <KIO/FileCopyJob> 0021 #include <KIO/MkdirJob> 0022 #include <KIO/StatJob> 0023 #include <KIO/UDSEntry> 0024 #include <KLocalizedString> 0025 #include <KStandardAction> 0026 // Qt 0027 #include <QDir> 0028 #include <QFile> 0029 #include <QList> 0030 #include <QMimeDatabase> 0031 #include <QMimeType> 0032 #include <QTextCodec> 0033 #include <QTextStream> 0034 // Std 0035 #include <algorithm> 0036 0037 using namespace KompareDiff2; 0038 0039 ModelList::ModelList(DiffSettings *diffSettings, QObject *parent, bool supportReadWrite) 0040 : QObject(parent) 0041 , d_ptr(new ModelListPrivate(diffSettings, supportReadWrite)) 0042 { 0043 Q_D(ModelList); 0044 0045 qCDebug(KOMPAREDIFF2_LOG) << "Show me the arguments: " << diffSettings << ", " << parent; 0046 d->actionCollection = new KActionCollection(this); 0047 if (supportReadWrite) { 0048 d->applyDifference = d->actionCollection->addAction(QStringLiteral("difference_apply"), this, &ModelList::slotActionApplyDifference); 0049 d->applyDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right"))); 0050 d->applyDifference->setText(i18nc("@action", "&Apply Difference")); 0051 d->actionCollection->setDefaultShortcut(d->applyDifference, QKeySequence(Qt::Key_Space)); 0052 d->unApplyDifference = d->actionCollection->addAction(QStringLiteral("difference_unapply"), this, &ModelList::slotActionUnApplyDifference); 0053 d->unApplyDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left"))); 0054 d->unApplyDifference->setText(i18nc("@action", "Un&apply Difference")); 0055 d->actionCollection->setDefaultShortcut(d->unApplyDifference, QKeySequence(Qt::Key_Backspace)); 0056 d->applyAll = d->actionCollection->addAction(QStringLiteral("difference_applyall"), this, &ModelList::slotActionApplyAllDifferences); 0057 d->applyAll->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double"))); 0058 d->applyAll->setText(i18nc("@action", "App&ly All")); 0059 d->actionCollection->setDefaultShortcut(d->applyAll, QKeySequence(Qt::CTRL | Qt::Key_A)); 0060 d->unapplyAll = d->actionCollection->addAction(QStringLiteral("difference_unapplyall"), this, &ModelList::slotActionUnapplyAllDifferences); 0061 d->unapplyAll->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left-double"))); 0062 d->unapplyAll->setText(i18nc("@action", "&Unapply All")); 0063 d->actionCollection->setDefaultShortcut(d->unapplyAll, QKeySequence(Qt::CTRL | Qt::Key_U)); 0064 } else { 0065 d->applyDifference = nullptr; 0066 d->unApplyDifference = nullptr; 0067 d->applyAll = nullptr; 0068 d->unapplyAll = nullptr; 0069 } 0070 d->previousFile = d->actionCollection->addAction(QStringLiteral("difference_previousfile"), this, &ModelList::slotPreviousModel); 0071 d->previousFile->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up-double"))); 0072 d->previousFile->setText(i18nc("@action", "P&revious File")); 0073 d->actionCollection->setDefaultShortcut(d->previousFile, QKeySequence(Qt::CTRL | Qt::Key_PageUp)); 0074 d->nextFile = d->actionCollection->addAction(QStringLiteral("difference_nextfile"), this, &ModelList::slotNextModel); 0075 d->nextFile->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down-double"))); 0076 d->nextFile->setText(i18nc("@action", "N&ext File")); 0077 d->actionCollection->setDefaultShortcut(d->nextFile, QKeySequence(Qt::CTRL | Qt::Key_PageDown)); 0078 d->previousDifference = d->actionCollection->addAction(QStringLiteral("difference_previous"), this, &ModelList::slotPreviousDifference); 0079 d->previousDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up"))); 0080 d->previousDifference->setText(i18nc("@action", "&Previous Difference")); 0081 d->actionCollection->setDefaultShortcut(d->previousDifference, QKeySequence(Qt::CTRL | Qt::Key_Up)); 0082 d->nextDifference = d->actionCollection->addAction(QStringLiteral("difference_next"), this, &ModelList::slotNextDifference); 0083 d->nextDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down"))); 0084 d->nextDifference->setText(i18nc("@action", "&Next Difference")); 0085 d->actionCollection->setDefaultShortcut(d->nextDifference, QKeySequence(Qt::CTRL | Qt::Key_Down)); 0086 d->previousDifference->setEnabled(false); 0087 d->nextDifference->setEnabled(false); 0088 0089 if (supportReadWrite) { 0090 d->save = KStandardAction::save(this, &ModelList::slotSaveDestination, d->actionCollection); 0091 d->save->setEnabled(false); 0092 } else { 0093 d->save = nullptr; 0094 } 0095 0096 d->updateModelListActions(); 0097 } 0098 0099 ModelList::~ModelList() = default; 0100 0101 bool ModelList::compare() 0102 { 0103 Q_D(ModelList); 0104 0105 bool result = false; 0106 0107 bool sourceIsDirectory = ModelListPrivate::isDirectory(d->info->localSource); 0108 bool destinationIsDirectory = ModelListPrivate::isDirectory(d->info->localDestination); 0109 0110 if (sourceIsDirectory && destinationIsDirectory) { 0111 d->info->mode = ComparingDirs; 0112 result = compare(d->info->mode); 0113 } else if (!sourceIsDirectory && !destinationIsDirectory) { 0114 QFile sourceFile(d->info->localSource); 0115 sourceFile.open(QIODevice::ReadOnly); 0116 QMimeDatabase db; 0117 QString sourceMimeType = (db.mimeTypeForData(sourceFile.readAll())).name(); 0118 sourceFile.close(); 0119 qCDebug(KOMPAREDIFF2_LOG) << "Mimetype source : " << sourceMimeType; 0120 0121 QFile destinationFile(d->info->localDestination); 0122 destinationFile.open(QIODevice::ReadOnly); 0123 QString destinationMimeType = (db.mimeTypeForData(destinationFile.readAll())).name(); 0124 destinationFile.close(); 0125 qCDebug(KOMPAREDIFF2_LOG) << "Mimetype destination: " << destinationMimeType; 0126 0127 // Not checking if it is a text file/something diff can even compare, we'll let diff handle that 0128 if (!ModelListPrivate::isDiff(sourceMimeType) && ModelListPrivate::isDiff(destinationMimeType)) { 0129 qCDebug(KOMPAREDIFF2_LOG) << "Blending destination into source..."; 0130 d->info->mode = BlendingFile; 0131 result = openFileAndDiff(); 0132 } else if (ModelListPrivate::isDiff(sourceMimeType) && !ModelListPrivate::isDiff(destinationMimeType)) { 0133 qCDebug(KOMPAREDIFF2_LOG) << "Blending source into destination..."; 0134 d->info->mode = BlendingFile; 0135 // Swap source and destination before calling this 0136 d->info->swapSourceWithDestination(); 0137 // Do we need to notify anyone we swapped source and destination? 0138 // No we do not need to notify anyone about swapping source with destination 0139 result = openFileAndDiff(); 0140 } else { 0141 qCDebug(KOMPAREDIFF2_LOG) << "Comparing source with destination"; 0142 d->info->mode = ComparingFiles; 0143 result = compare(d->info->mode); 0144 } 0145 } else if (sourceIsDirectory && !destinationIsDirectory) { 0146 d->info->mode = BlendingDir; 0147 result = openDirAndDiff(); 0148 } else { 0149 d->info->mode = BlendingDir; 0150 // Swap source and destination first in d->info 0151 d->info->swapSourceWithDestination(); 0152 // Do we need to notify anyone we swapped source and destination? 0153 // No we do not need to notify anyone about swapping source with destination 0154 result = openDirAndDiff(); 0155 } 0156 0157 return result; 0158 } 0159 0160 bool ModelList::compare(Mode mode) 0161 { 0162 Q_D(ModelList); 0163 0164 clear(); // Destroy the old models... 0165 0166 d->diffProcess = std::make_unique<KompareProcess>(d->diffSettings, Custom, d->info->localSource, d->info->localDestination, QString(), mode); 0167 d->diffProcess->setEncoding(d->encoding); 0168 0169 connect(d->diffProcess.get(), &KompareProcess::diffHasFinished, this, &ModelList::slotDiffProcessFinished); 0170 0171 Q_EMIT status(RunningDiff); 0172 d->diffProcess->start(); 0173 0174 return true; 0175 } 0176 0177 bool ModelList::openFileAndDiff() 0178 { 0179 Q_D(ModelList); 0180 0181 clear(); 0182 0183 if (parseDiffOutput(d->readFile(d->info->localDestination)) != 0) { 0184 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())); 0185 return false; 0186 } 0187 0188 d->setDepthAndApplied(); 0189 0190 if (!blendOriginalIntoModelList(d->info->localSource)) { 0191 qCDebug(KOMPAREDIFF2_LOG) << "Oops cant blend original file into modellist : " << d->info->localSource; 0192 Q_EMIT error( 0193 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())); 0194 return false; 0195 } 0196 0197 d->updateModelListActions(); 0198 show(); 0199 0200 return true; 0201 } 0202 0203 bool ModelList::openDirAndDiff() 0204 { 0205 Q_D(ModelList); 0206 0207 clear(); 0208 0209 if (parseDiffOutput(d->readFile(d->info->localDestination)) != 0) { 0210 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())); 0211 return false; 0212 } 0213 0214 d->setDepthAndApplied(); 0215 0216 // Do our thing :) 0217 if (!blendOriginalIntoModelList(d->info->localSource)) { 0218 // Trouble blending the original into the model 0219 qCDebug(KOMPAREDIFF2_LOG) << "Oops cant blend original dir into modellist : " << d->info->localSource; 0220 Q_EMIT error( 0221 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())); 0222 return false; 0223 } 0224 0225 d->updateModelListActions(); 0226 show(); 0227 0228 return true; 0229 } 0230 0231 void ModelList::slotSaveDestination() 0232 { 0233 Q_D(ModelList); 0234 0235 // Unnecessary safety check! We can now guarantee that saving is only possible when there is a model and there are unsaved changes 0236 if (d->selectedModel) { 0237 saveDestination(d->selectedModel); 0238 if (d->save) 0239 d->save->setEnabled(false); 0240 Q_EMIT updateActions(); 0241 } 0242 } 0243 0244 bool ModelList::saveDestination(DiffModel *model) 0245 { 0246 Q_D(ModelList); 0247 0248 qCDebug(KOMPAREDIFF2_LOG) << "ModelList::saveDestination: "; 0249 0250 // Unnecessary safety check, we can guarantee there are unsaved changes!!! 0251 if (!model->hasUnsavedChanges()) 0252 return true; 0253 0254 QTemporaryFile temp; 0255 0256 if (!temp.open()) { 0257 Q_EMIT error(i18n("Could not open a temporary file.")); 0258 temp.remove(); 0259 return false; 0260 } 0261 0262 QTextStream stream(&temp); 0263 QStringList list; 0264 0265 const DiffHunkList *hunks = model->hunks(); 0266 0267 for (const DiffHunk *hunk : *hunks) { 0268 const DifferenceList differences = hunk->differences(); 0269 0270 for (const Difference *diff : differences) { 0271 if (!diff->applied()) { 0272 const DifferenceStringList destinationLines = diff->destinationLines(); 0273 for (const DifferenceString *diffString : destinationLines) { 0274 list.append(diffString->string()); 0275 } 0276 } else { 0277 const DifferenceStringList sourceLines = diff->sourceLines(); 0278 for (const DifferenceString *diffString : sourceLines) { 0279 list.append(diffString->string()); 0280 } 0281 } 0282 } 0283 } 0284 0285 // qCDebug(KOMPAREDIFF2_LOG) << "Everything: " << endl << list.join( "\n" ); 0286 0287 if (list.count() > 0) 0288 stream << list.join(QString()); 0289 if (temp.error() != QFile::NoError) { 0290 Q_EMIT error(i18n("<qt>Could not write to the temporary file <b>%1</b>, deleting it.</qt>", temp.fileName())); 0291 temp.remove(); 0292 return false; 0293 } 0294 0295 temp.close(); 0296 if (temp.error() != QFile::NoError) { 0297 Q_EMIT error(i18n("<qt>Could not write to the temporary file <b>%1</b>, deleting it.</qt>", temp.fileName())); 0298 temp.remove(); 0299 return false; 0300 } 0301 0302 bool result = false; 0303 0304 // Make sure the destination directory exists, it is possible when using -N to not have the destination dir/file available 0305 if (d->info->mode == ComparingDirs) { 0306 // Don't use destination which was used for creating the diff directly, use the original URL!!! 0307 // FIXME!!! Wrong destination this way! Need to add the sub directory to the url!!!! 0308 qCDebug(KOMPAREDIFF2_LOG) << "Tempfilename (save) : " << temp.fileName(); 0309 qCDebug(KOMPAREDIFF2_LOG) << "Model->path+file : " << model->destinationPath() << model->destinationFile(); 0310 qCDebug(KOMPAREDIFF2_LOG) << "info->localdest : " << d->info->localDestination; 0311 QString tmp = model->destinationPath(); 0312 if (tmp.startsWith(d->info->localDestination)) // It should, if not serious trouble... 0313 tmp.remove(0, d->info->localDestination.size()); 0314 qCDebug(KOMPAREDIFF2_LOG) << "DestinationURL : " << d->info->destination; 0315 qCDebug(KOMPAREDIFF2_LOG) << "tmp : " << tmp; 0316 KIO::UDSEntry entry; 0317 QUrl fullDestinationPath = d->info->destination; 0318 fullDestinationPath.setPath(fullDestinationPath.path() + tmp); 0319 qCDebug(KOMPAREDIFF2_LOG) << "fullDestinationPath : " << fullDestinationPath; 0320 KIO::StatJob *statJob = KIO::stat(fullDestinationPath); 0321 if (!statJob->exec()) { 0322 entry = statJob->statResult(); 0323 KIO::MkdirJob *mkdirJob = KIO::mkdir(fullDestinationPath); 0324 if (!mkdirJob->exec()) { 0325 Q_EMIT error(i18n("<qt>Could not create destination directory <b>%1</b>.\nThe file has not been saved.</qt>", fullDestinationPath.path())); 0326 return false; 0327 } 0328 } 0329 fullDestinationPath.setPath(fullDestinationPath.path() + model->destinationFile()); 0330 KIO::FileCopyJob *copyJob = KIO::file_copy(QUrl::fromLocalFile(temp.fileName()), fullDestinationPath, -1, KIO::Overwrite); 0331 result = copyJob->exec(); 0332 } else { 0333 qCDebug(KOMPAREDIFF2_LOG) << "Tempfilename : " << temp.fileName(); 0334 qCDebug(KOMPAREDIFF2_LOG) << "DestinationURL : " << d->info->destination; 0335 0336 // Get permissions of existing file and copy temporary file with the same permissions 0337 int permissions = -1; 0338 KIO::StatJob *statJob = KIO::stat(d->info->destination); 0339 result = statJob->exec(); 0340 if (result) 0341 permissions = statJob->statResult().numberValue(KIO::UDSEntry::UDS_ACCESS); 0342 0343 KIO::FileCopyJob *copyJob = KIO::file_copy(QUrl::fromLocalFile(temp.fileName()), d->info->destination, permissions, KIO::Overwrite); 0344 result = copyJob->exec(); 0345 qCDebug(KOMPAREDIFF2_LOG) << "true or false?" << result; 0346 } 0347 0348 if (!result) { 0349 // FIXME: Wrong first argument given in case of comparing directories! 0350 Q_EMIT error( 0351 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 " 0352 "can manually copy it to the right place.</qt>", 0353 d->info->destination.url(), 0354 temp.fileName())); 0355 // Don't remove file when we delete temp and don't leak it. 0356 temp.setAutoRemove(false); 0357 } else { 0358 temp.remove(); 0359 } 0360 0361 // If saving was fine set all differences to saved so we can start again with a clean slate 0362 if (result) { 0363 const DifferenceList *differences = model->differences(); 0364 0365 for (Difference *diff : *differences) { 0366 diff->setUnsaved(false); 0367 } 0368 } 0369 0370 return true; 0371 } 0372 0373 bool ModelList::saveAll() 0374 { 0375 Q_D(ModelList); 0376 0377 if (modelCount() == 0) 0378 return false; 0379 0380 for (DiffModel *model : std::as_const(*d->models)) { 0381 if (!saveDestination(model)) 0382 return false; 0383 } 0384 0385 return true; 0386 } 0387 0388 void ModelList::setEncoding(const QString &encoding) 0389 { 0390 Q_D(ModelList); 0391 0392 d->encoding = encoding; 0393 if (!encoding.compare(QLatin1String("default"), Qt::CaseInsensitive)) { 0394 d->textCodec = QTextCodec::codecForLocale(); 0395 } else { 0396 qCDebug(KOMPAREDIFF2_LOG) << "Encoding : " << encoding; 0397 d->textCodec = QTextCodec::codecForName(encoding.toUtf8()); 0398 qCDebug(KOMPAREDIFF2_LOG) << "TextCodec: " << d->textCodec; 0399 if (!d->textCodec) 0400 d->textCodec = QTextCodec::codecForLocale(); 0401 } 0402 qCDebug(KOMPAREDIFF2_LOG) << "TextCodec: " << d->textCodec; 0403 } 0404 0405 void ModelList::setReadWrite(bool isReadWrite) 0406 { 0407 Q_D(ModelList); 0408 0409 if (d->isReadWrite == isReadWrite) 0410 return; 0411 0412 d->isReadWrite = isReadWrite; 0413 d->updateModelListActions(); 0414 } 0415 0416 bool ModelList::isReadWrite() const 0417 { 0418 Q_D(const ModelList); 0419 0420 return d->isReadWrite; 0421 } 0422 0423 void ModelList::slotDiffProcessFinished(bool success) 0424 { 0425 Q_D(ModelList); 0426 0427 if (success) { 0428 Q_EMIT status(Parsing); 0429 if (parseDiffOutput(d->diffProcess->diffOutput()) != 0) { 0430 Q_EMIT error(i18n("Could not parse diff output.")); 0431 } else { 0432 if (d->info->mode != ShowingDiff) { 0433 qCDebug(KOMPAREDIFF2_LOG) << "Blend this crap please and do not give me any conflicts..."; 0434 blendOriginalIntoModelList(d->info->localSource); 0435 } 0436 d->updateModelListActions(); 0437 show(); 0438 } 0439 Q_EMIT status(FinishedParsing); 0440 } else if (d->diffProcess->exitStatus() == 0) { 0441 Q_EMIT error(i18n("The files are identical.")); 0442 } else { 0443 Q_EMIT error(d->diffProcess->stdErr()); 0444 } 0445 0446 // delay deletion, see bug 182792 0447 d->diffProcess.release()->deleteLater(); 0448 } 0449 0450 bool ModelList::openDiff(const QString &diffFile) 0451 { 0452 Q_D(ModelList); 0453 0454 qCDebug(KOMPAREDIFF2_LOG) << "Stupid :) Url = " << diffFile; 0455 0456 if (diffFile.isEmpty()) 0457 return false; 0458 0459 QString diff = d->readFile(diffFile); 0460 0461 clear(); // Clear the current models 0462 0463 Q_EMIT status(Parsing); 0464 0465 if (parseDiffOutput(diff) != 0) { 0466 Q_EMIT error(i18n("Could not parse diff output.")); 0467 return false; 0468 } 0469 0470 d->updateModelListActions(); 0471 show(); 0472 0473 Q_EMIT status(FinishedParsing); 0474 0475 return true; 0476 } 0477 0478 bool ModelList::parseAndOpenDiff(const QString &diff) 0479 { 0480 Q_D(ModelList); 0481 0482 clear(); // Clear the current models 0483 0484 Q_EMIT status(Parsing); 0485 0486 if (parseDiffOutput(diff) != 0) { 0487 Q_EMIT error(i18n("Could not parse diff output.")); 0488 return false; 0489 } 0490 0491 d->updateModelListActions(); 0492 show(); 0493 0494 Q_EMIT status(FinishedParsing); 0495 return true; 0496 } 0497 0498 QString ModelList::recreateDiff() const 0499 { 0500 Q_D(const ModelList); 0501 0502 QString diff; 0503 0504 for (const DiffModel *model : *d->models) { 0505 diff += model->recreateDiff(); 0506 } 0507 0508 return diff; 0509 } 0510 0511 bool ModelList::saveDiff(const QString &url, const QString &directory, DiffSettings *diffSettings) 0512 { 0513 Q_D(ModelList); 0514 0515 qCDebug(KOMPAREDIFF2_LOG) << "ModelList::saveDiff: "; 0516 0517 d->diffTemp = std::make_unique<QTemporaryFile>(); 0518 d->diffURL = QUrl(url); // ### TODO the "url" argument should be a QUrl 0519 0520 if (!d->diffTemp->open()) { 0521 Q_EMIT error(i18n("Could not open a temporary file.")); 0522 d->diffTemp->remove(); 0523 d->diffTemp.reset(); 0524 return false; 0525 } 0526 0527 d->diffProcess = std::make_unique<KompareProcess>(diffSettings, Custom, d->info->localSource, d->info->localDestination, directory); 0528 d->diffProcess->setEncoding(d->encoding); 0529 0530 connect(d->diffProcess.get(), &KompareProcess::diffHasFinished, this, &ModelList::slotWriteDiffOutput); 0531 0532 Q_EMIT status(RunningDiff); 0533 d->diffProcess->start(); 0534 return true; 0535 } 0536 0537 void ModelList::slotWriteDiffOutput(bool success) 0538 { 0539 Q_D(ModelList); 0540 0541 qCDebug(KOMPAREDIFF2_LOG) << "Success = " << success; 0542 0543 if (success) { 0544 QTextStream stream(d->diffTemp.get()); 0545 0546 stream << d->diffProcess->diffOutput(); 0547 0548 d->diffTemp->close(); 0549 0550 if (false /*|| d->diffTemp->status() != 0 */) { 0551 Q_EMIT error(i18n("Could not write to the temporary file.")); 0552 } 0553 0554 KIO::FileCopyJob *copyJob = KIO::file_copy(QUrl::fromLocalFile(d->diffTemp->fileName()), d->diffURL); 0555 copyJob->exec(); 0556 0557 Q_EMIT status(FinishedWritingDiff); 0558 } 0559 0560 d->diffURL = QUrl(); 0561 d->diffTemp->remove(); 0562 d->diffTemp.reset(); 0563 0564 d->diffProcess.reset(); 0565 } 0566 0567 void ModelList::slotSelectionChanged(const KompareDiff2::DiffModel *model, const KompareDiff2::Difference *diff) 0568 { 0569 Q_D(ModelList); 0570 0571 // This method will signal all the other objects about a change in selection, 0572 // it will emit setSelection( const DiffModel*, const Difference* ) to all who are connected 0573 qCDebug(KOMPAREDIFF2_LOG) << "ModelList::slotSelectionChanged( " << model << ", " << diff << " )"; 0574 qCDebug(KOMPAREDIFF2_LOG) << "Sender is : " << sender()->metaObject()->className(); 0575 // qCDebug(KOMPAREDIFF2_LOG) << kBacktrace(); 0576 0577 d->selectedModel = const_cast<DiffModel *>(model); 0578 d->modelIndex = d->models->indexOf(d->selectedModel); 0579 qCDebug(KOMPAREDIFF2_LOG) << "d->modelIndex = " << d->modelIndex; 0580 d->selectedDifference = const_cast<Difference *>(diff); 0581 0582 d->selectedModel->setSelectedDifference(d->selectedDifference); 0583 0584 // setSelected* search for the argument in the lists and return false if not found 0585 // if found they return true and set the d->selected* 0586 if (!d->setSelectedModel(d->selectedModel)) { 0587 // Backup plan 0588 d->selectedModel = d->firstModel(); 0589 d->selectedDifference = d->selectedModel->firstDifference(); 0590 } else if (!d->selectedModel->setSelectedDifference(d->selectedDifference)) { 0591 // Another backup plan 0592 d->selectedDifference = d->selectedModel->firstDifference(); 0593 } 0594 0595 Q_EMIT setSelection(model, diff); 0596 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel), 0597 d->selectedModel->findDifference(d->selectedDifference), 0598 modelCount(), 0599 differenceCount(), 0600 d->selectedModel->appliedCount()); 0601 0602 d->updateModelListActions(); 0603 } 0604 0605 void ModelList::slotSelectionChanged(const KompareDiff2::Difference *diff) 0606 { 0607 Q_D(ModelList); 0608 0609 // This method will emit setSelection( const Difference* ) to whomever is listening 0610 // when for instance in kompareview the selection has changed 0611 qCDebug(KOMPAREDIFF2_LOG) << "ModelList::slotSelectionChanged( " << diff << " )"; 0612 qCDebug(KOMPAREDIFF2_LOG) << "Sender is : " << sender()->metaObject()->className(); 0613 0614 d->selectedDifference = const_cast<Difference *>(diff); 0615 0616 if (!d->selectedModel->setSelectedDifference(d->selectedDifference)) { 0617 // Backup plan 0618 d->selectedDifference = d->selectedModel->firstDifference(); 0619 } 0620 0621 Q_EMIT setSelection(diff); 0622 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel), 0623 d->selectedModel->findDifference(d->selectedDifference), 0624 modelCount(), 0625 differenceCount(), 0626 d->selectedModel->appliedCount()); 0627 0628 d->updateModelListActions(); 0629 } 0630 0631 void ModelList::slotPreviousModel() 0632 { 0633 Q_D(ModelList); 0634 0635 if ((d->selectedModel = d->prevModel()) != nullptr) { 0636 d->selectedDifference = d->selectedModel->firstDifference(); 0637 } else { 0638 d->selectedModel = d->firstModel(); 0639 d->selectedDifference = d->selectedModel->firstDifference(); 0640 } 0641 0642 Q_EMIT setSelection(d->selectedModel, d->selectedDifference); 0643 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel), 0644 d->selectedModel->findDifference(d->selectedDifference), 0645 modelCount(), 0646 differenceCount(), 0647 d->selectedModel->appliedCount()); 0648 d->updateModelListActions(); 0649 } 0650 0651 void ModelList::slotNextModel() 0652 { 0653 Q_D(ModelList); 0654 0655 if ((d->selectedModel = d->nextModel()) != nullptr) { 0656 d->selectedDifference = d->selectedModel->firstDifference(); 0657 } else { 0658 d->selectedModel = d->lastModel(); 0659 d->selectedDifference = d->selectedModel->firstDifference(); 0660 } 0661 0662 Q_EMIT setSelection(d->selectedModel, d->selectedDifference); 0663 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel), 0664 d->selectedModel->findDifference(d->selectedDifference), 0665 modelCount(), 0666 differenceCount(), 0667 d->selectedModel->appliedCount()); 0668 d->updateModelListActions(); 0669 } 0670 0671 Mode ModelList::mode() const 0672 { 0673 Q_D(const ModelList); 0674 0675 return d->info->mode; 0676 } 0677 0678 const DiffModelList *ModelList::models() const 0679 { 0680 Q_D(const ModelList); 0681 0682 return d->models.get(); 0683 } 0684 0685 KActionCollection *ModelList::actionCollection() const 0686 { 0687 Q_D(const ModelList); 0688 0689 return d->actionCollection; 0690 } 0691 0692 void ModelList::slotPreviousDifference() 0693 { 0694 Q_D(ModelList); 0695 0696 qCDebug(KOMPAREDIFF2_LOG) << "slotPreviousDifference called"; 0697 if ((d->selectedDifference = d->selectedModel->prevDifference()) != nullptr) { 0698 Q_EMIT setSelection(d->selectedDifference); 0699 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel), 0700 d->selectedModel->findDifference(d->selectedDifference), 0701 modelCount(), 0702 differenceCount(), 0703 d->selectedModel->appliedCount()); 0704 d->updateModelListActions(); 0705 return; 0706 } 0707 0708 qCDebug(KOMPAREDIFF2_LOG) << "**** no previous difference... ok lets find the previous model..."; 0709 0710 if ((d->selectedModel = d->prevModel()) != nullptr) { 0711 d->selectedDifference = d->selectedModel->lastDifference(); 0712 0713 Q_EMIT setSelection(d->selectedModel, d->selectedDifference); 0714 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel), 0715 d->selectedModel->findDifference(d->selectedDifference), 0716 modelCount(), 0717 differenceCount(), 0718 d->selectedModel->appliedCount()); 0719 d->updateModelListActions(); 0720 return; 0721 } 0722 0723 qCDebug(KOMPAREDIFF2_LOG) << "**** !!! No previous model, ok backup plan activated..."; 0724 0725 // Backup plan 0726 d->selectedModel = d->firstModel(); 0727 d->selectedDifference = d->selectedModel->firstDifference(); 0728 0729 Q_EMIT setSelection(d->selectedModel, d->selectedDifference); 0730 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel), 0731 d->selectedModel->findDifference(d->selectedDifference), 0732 modelCount(), 0733 differenceCount(), 0734 d->selectedModel->appliedCount()); 0735 d->updateModelListActions(); 0736 } 0737 0738 void ModelList::slotNextDifference() 0739 { 0740 Q_D(ModelList); 0741 0742 qCDebug(KOMPAREDIFF2_LOG) << "slotNextDifference called"; 0743 if ((d->selectedDifference = d->selectedModel->nextDifference()) != nullptr) { 0744 Q_EMIT setSelection(d->selectedDifference); 0745 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel), 0746 d->selectedModel->findDifference(d->selectedDifference), 0747 modelCount(), 0748 differenceCount(), 0749 d->selectedModel->appliedCount()); 0750 d->updateModelListActions(); 0751 return; 0752 } 0753 0754 qCDebug(KOMPAREDIFF2_LOG) << "**** no next difference... ok lets find the next model..."; 0755 0756 if ((d->selectedModel = d->nextModel()) != nullptr) { 0757 d->selectedDifference = d->selectedModel->firstDifference(); 0758 0759 Q_EMIT setSelection(d->selectedModel, d->selectedDifference); 0760 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel), 0761 d->selectedModel->findDifference(d->selectedDifference), 0762 modelCount(), 0763 differenceCount(), 0764 d->selectedModel->appliedCount()); 0765 d->updateModelListActions(); 0766 return; 0767 } 0768 0769 qCDebug(KOMPAREDIFF2_LOG) << "**** !!! No next model, ok backup plan activated..."; 0770 0771 // Backup plan 0772 d->selectedModel = d->lastModel(); 0773 d->selectedDifference = d->selectedModel->lastDifference(); 0774 0775 Q_EMIT setSelection(d->selectedModel, d->selectedDifference); 0776 Q_EMIT setStatusBarModelInfo(findModel(d->selectedModel), 0777 d->selectedModel->findDifference(d->selectedDifference), 0778 modelCount(), 0779 differenceCount(), 0780 d->selectedModel->appliedCount()); 0781 d->updateModelListActions(); 0782 } 0783 0784 void ModelList::slotApplyDifference(bool apply) 0785 { 0786 Q_D(ModelList); 0787 0788 d->selectedModel->applyDifference(apply); 0789 Q_EMIT applyDifference(apply); 0790 } 0791 0792 void ModelList::slotApplyAllDifferences(bool apply) 0793 { 0794 Q_D(ModelList); 0795 0796 d->selectedModel->applyAllDifferences(apply); 0797 Q_EMIT applyAllDifferences(apply); 0798 } 0799 0800 int ModelList::parseDiffOutput(const QString &diff) 0801 { 0802 Q_D(ModelList); 0803 0804 qCDebug(KOMPAREDIFF2_LOG) << "ModelList::parseDiffOutput"; 0805 Q_EMIT diffString(diff); 0806 0807 QStringList diffLines = ModelListPrivate::split(diff); 0808 0809 std::unique_ptr<Parser> parser = std::make_unique<Parser>(this); 0810 bool malformed = false; 0811 d->models.reset(parser->parse(diffLines, &malformed)); 0812 0813 d->info->generator = parser->generator(); 0814 d->info->format = parser->format(); 0815 0816 parser.reset(); 0817 0818 if (d->models) { 0819 if (malformed) { 0820 qCDebug(KOMPAREDIFF2_LOG) << "Malformed diff"; 0821 Q_EMIT error(i18n("The diff is malformed. Some lines could not be parsed and will not be displayed in the diff view.")); 0822 // proceed anyway with the lines which have been parsed 0823 } 0824 d->selectedModel = d->firstModel(); 0825 qCDebug(KOMPAREDIFF2_LOG) << "Ok there are differences..."; 0826 d->selectedDifference = d->selectedModel->firstDifference(); 0827 Q_EMIT setStatusBarModelInfo(0, 0, modelCount(), differenceCount(), 0); 0828 } else { 0829 // Wow trouble, no models, so no differences... 0830 qCDebug(KOMPAREDIFF2_LOG) << "Now i'll be damned, there should be models here !!!"; 0831 return -1; 0832 } 0833 0834 return 0; 0835 } 0836 0837 bool ModelList::blendOriginalIntoModelList(const QString &localURL) 0838 { 0839 Q_D(ModelList); 0840 0841 qCDebug(KOMPAREDIFF2_LOG) << "Hurrah we are blending..."; 0842 QFileInfo fi(localURL); 0843 0844 bool result = false; 0845 0846 QString fileContents; 0847 0848 if (fi.isDir()) { // is a dir 0849 qCDebug(KOMPAREDIFF2_LOG) << "Blend Dir"; 0850 // QDir dir( localURL, QString(), QDir::Name|QDir::DirsFirst, QDir::TypeMask ); 0851 for (DiffModel *model : std::as_const(*d->models)) { 0852 qCDebug(KOMPAREDIFF2_LOG) << "Model : " << model; 0853 QString filename = model->source(); 0854 if (!filename.startsWith(localURL)) 0855 filename = QDir(localURL).filePath(filename); 0856 QFileInfo fi2(filename); 0857 if (fi2.exists()) { 0858 qCDebug(KOMPAREDIFF2_LOG) << "Reading from: " << filename; 0859 fileContents = d->readFile(filename); 0860 result = d->blendFile(model, fileContents); 0861 } else { 0862 qCDebug(KOMPAREDIFF2_LOG) << "File " << filename << " does not exist !"; 0863 qCDebug(KOMPAREDIFF2_LOG) << "Assume empty file !"; 0864 fileContents.truncate(0); 0865 result = d->blendFile(model, fileContents); 0866 } 0867 } 0868 qCDebug(KOMPAREDIFF2_LOG) << "End of Blend Dir"; 0869 } else if (fi.isFile()) { // is a file 0870 qCDebug(KOMPAREDIFF2_LOG) << "Blend File"; 0871 qCDebug(KOMPAREDIFF2_LOG) << "Reading from: " << localURL; 0872 fileContents = d->readFile(localURL); 0873 0874 result = d->blendFile((*d->models)[0], fileContents); 0875 qCDebug(KOMPAREDIFF2_LOG) << "End of Blend File"; 0876 } 0877 0878 return result; 0879 } 0880 0881 void ModelList::show() 0882 { 0883 Q_D(ModelList); 0884 0885 qCDebug(KOMPAREDIFF2_LOG) << "ModelList::Show Number of models = " << d->models->count(); 0886 Q_EMIT modelsChanged(d->models.get()); 0887 Q_EMIT setSelection(d->selectedModel, d->selectedDifference); 0888 } 0889 0890 const DiffModel *ModelList::modelAt(int i) const 0891 { 0892 Q_D(const ModelList); 0893 0894 return d->models->at(i); 0895 } 0896 0897 DiffModel *ModelList::modelAt(int i) 0898 { 0899 Q_D(ModelList); 0900 0901 return d->models->at(i); 0902 } 0903 0904 int ModelList::findModel(DiffModel *model) const 0905 { 0906 Q_D(const ModelList); 0907 0908 return d->models->indexOf(model); 0909 } 0910 0911 int ModelList::currentModel() const 0912 { 0913 Q_D(const ModelList); 0914 0915 return d->models->indexOf(d->selectedModel); 0916 } 0917 0918 int ModelList::currentDifference() const 0919 { 0920 Q_D(const ModelList); 0921 0922 return d->selectedModel ? d->selectedModel->findDifference(d->selectedDifference) : -1; 0923 } 0924 0925 const DiffModel *ModelList::selectedModel() const 0926 { 0927 Q_D(const ModelList); 0928 0929 return d->selectedModel; 0930 } 0931 0932 const Difference *ModelList::selectedDifference() const 0933 { 0934 Q_D(const ModelList); 0935 0936 return d->selectedDifference; 0937 } 0938 0939 void ModelList::clear() 0940 { 0941 Q_D(ModelList); 0942 0943 if (d->models) 0944 d->models->clear(); 0945 0946 Q_EMIT modelsChanged(d->models.get()); 0947 } 0948 0949 void ModelList::refresh() 0950 { 0951 Q_D(ModelList); 0952 0953 // FIXME: I can imagine blending also wants to be refreshed so make a switch case here 0954 compare(d->info->mode); 0955 } 0956 0957 void ModelList::swap() 0958 { 0959 Q_D(ModelList); 0960 0961 // FIXME Not sure if any mode could be swapped 0962 if (d->info->mode == ComparingFiles) 0963 compare(d->info->mode); 0964 else if (d->info->mode == ComparingDirs) 0965 compare(d->info->mode); 0966 } 0967 0968 bool ModelList::hasUnsavedChanges() const 0969 { 0970 Q_D(const ModelList); 0971 0972 if (!d->models) { 0973 return false; 0974 } 0975 0976 return std::any_of(d->models->constBegin(), d->models->constEnd(), [] (const DiffModel *model) { 0977 return model->hasUnsavedChanges(); 0978 }); 0979 } 0980 0981 int ModelList::modelCount() const 0982 { 0983 Q_D(const ModelList); 0984 0985 return d->models ? d->models->count() : 0; 0986 } 0987 0988 int ModelList::differenceCount() const 0989 { 0990 Q_D(const ModelList); 0991 0992 return d->selectedModel ? d->selectedModel->differenceCount() : -1; 0993 } 0994 0995 int ModelList::appliedCount() const 0996 { 0997 Q_D(const ModelList); 0998 0999 return d->selectedModel ? d->selectedModel->appliedCount() : -1; 1000 } 1001 1002 void ModelList::slotKompareInfo(Info *info) 1003 { 1004 Q_D(ModelList); 1005 1006 d->info = info; 1007 } 1008 1009 void ModelList::slotActionApplyDifference() 1010 { 1011 Q_D(ModelList); 1012 1013 if (!d->selectedDifference->applied()) 1014 slotApplyDifference(true); 1015 slotNextDifference(); 1016 d->updateModelListActions(); 1017 } 1018 1019 void ModelList::slotActionUnApplyDifference() 1020 { 1021 Q_D(ModelList); 1022 1023 if (d->selectedDifference->applied()) 1024 slotApplyDifference(false); 1025 slotPreviousDifference(); 1026 d->updateModelListActions(); 1027 } 1028 1029 void ModelList::slotActionApplyAllDifferences() 1030 { 1031 Q_D(ModelList); 1032 1033 slotApplyAllDifferences(true); 1034 d->updateModelListActions(); 1035 } 1036 1037 void ModelList::slotActionUnapplyAllDifferences() 1038 { 1039 Q_D(ModelList); 1040 1041 slotApplyAllDifferences(false); 1042 d->updateModelListActions(); 1043 } 1044 1045 #include "moc_modellist.cpp"