File indexing completed on 2024-04-28 13:39:50
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 "komparemodellist.h" 0011 0012 #include <QAction> 0013 #include <QFile> 0014 #include <QDir> 0015 #include <QTextCodec> 0016 #include <QTextStream> 0017 #include <QList> 0018 #include <QTemporaryFile> 0019 #include <QMimeType> 0020 #include <QMimeDatabase> 0021 0022 #include <KActionCollection> 0023 #include <KDirWatch> 0024 #include <KIO/UDSEntry> 0025 #include <KIO/StatJob> 0026 #include <KIO/MkdirJob> 0027 #include <KIO/FileCopyJob> 0028 #include <KLocalizedString> 0029 #include <KStandardAction> 0030 0031 #include <komparediffdebug.h> 0032 #include "diffhunk.h" 0033 #include "kompareprocess.h" 0034 #include "parser.h" 0035 0036 using namespace Diff2; 0037 0038 #if KOMPAREDIFF2_BUILD_DEPRECATED_SINCE(5, 4) 0039 KompareModelList::KompareModelList(DiffSettings* diffSettings, QWidget* widgetForKIO, QObject* parent, const char* name, bool supportReadWrite) 0040 : KompareModelList(diffSettings, parent, name, supportReadWrite) 0041 { 0042 Q_UNUSED(widgetForKIO) 0043 } 0044 #endif 0045 0046 KompareModelList::KompareModelList(DiffSettings* diffSettings, QObject* parent, const char* name, bool supportReadWrite) 0047 : QObject(parent), 0048 m_diffProcess(nullptr), 0049 m_diffSettings(diffSettings), 0050 m_models(nullptr), 0051 m_selectedModel(nullptr), 0052 m_selectedDifference(nullptr), 0053 m_modelIndex(0), 0054 m_info(nullptr), 0055 m_textCodec(nullptr), 0056 m_isReadWrite(supportReadWrite) 0057 { 0058 qCDebug(LIBKOMPAREDIFF2) << "Show me the arguments: " << diffSettings << ", " << parent << ", " << name; 0059 m_actionCollection = new KActionCollection(this); 0060 if (supportReadWrite) { 0061 m_applyDifference = m_actionCollection->addAction(QStringLiteral("difference_apply"), this, &KompareModelList::slotActionApplyDifference); 0062 m_applyDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right"))); 0063 m_applyDifference->setText(i18nc("@action", "&Apply Difference")); 0064 m_actionCollection->setDefaultShortcut(m_applyDifference, QKeySequence(Qt::Key_Space)); 0065 m_unApplyDifference = m_actionCollection->addAction(QStringLiteral("difference_unapply"), this, &KompareModelList::slotActionUnApplyDifference); 0066 m_unApplyDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left"))); 0067 m_unApplyDifference->setText(i18nc("@action", "Un&apply Difference")); 0068 m_actionCollection->setDefaultShortcut(m_unApplyDifference, QKeySequence(Qt::Key_Backspace)); 0069 m_applyAll = m_actionCollection->addAction(QStringLiteral("difference_applyall"), this, &KompareModelList::slotActionApplyAllDifferences); 0070 m_applyAll->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double"))); 0071 m_applyAll->setText(i18nc("@action", "App&ly All")); 0072 m_actionCollection->setDefaultShortcut(m_applyAll, QKeySequence(Qt::CTRL | Qt::Key_A)); 0073 m_unapplyAll = m_actionCollection->addAction(QStringLiteral("difference_unapplyall"), this, &KompareModelList::slotActionUnapplyAllDifferences); 0074 m_unapplyAll->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left-double"))); 0075 m_unapplyAll->setText(i18nc("@action", "&Unapply All")); 0076 m_actionCollection->setDefaultShortcut(m_unapplyAll, QKeySequence(Qt::CTRL | Qt::Key_U)); 0077 } else { 0078 m_applyDifference = nullptr; 0079 m_unApplyDifference = nullptr; 0080 m_applyAll = nullptr; 0081 m_unapplyAll = nullptr; 0082 } 0083 m_previousFile = m_actionCollection->addAction(QStringLiteral("difference_previousfile"), this, &KompareModelList::slotPreviousModel); 0084 m_previousFile->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up-double"))); 0085 m_previousFile->setText(i18nc("@action", "P&revious File")); 0086 m_actionCollection->setDefaultShortcut(m_previousFile, QKeySequence(Qt::CTRL | Qt::Key_PageUp)); 0087 m_nextFile = m_actionCollection->addAction(QStringLiteral("difference_nextfile"), this, &KompareModelList::slotNextModel); 0088 m_nextFile->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down-double"))); 0089 m_nextFile->setText(i18nc("@action", "N&ext File")); 0090 m_actionCollection->setDefaultShortcut(m_nextFile, QKeySequence(Qt::CTRL | Qt::Key_PageDown)); 0091 m_previousDifference = m_actionCollection->addAction(QStringLiteral("difference_previous"), this, &KompareModelList::slotPreviousDifference); 0092 m_previousDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up"))); 0093 m_previousDifference->setText(i18nc("@action", "&Previous Difference")); 0094 m_actionCollection->setDefaultShortcut(m_previousDifference, QKeySequence(Qt::CTRL | Qt::Key_Up)); 0095 m_nextDifference = m_actionCollection->addAction(QStringLiteral("difference_next"), this, &KompareModelList::slotNextDifference); 0096 m_nextDifference->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down"))); 0097 m_nextDifference->setText(i18nc("@action", "&Next Difference")); 0098 m_actionCollection->setDefaultShortcut(m_nextDifference, QKeySequence(Qt::CTRL | Qt::Key_Down)); 0099 m_previousDifference->setEnabled(false); 0100 m_nextDifference->setEnabled(false); 0101 0102 if (supportReadWrite) { 0103 m_save = KStandardAction::save(this, &KompareModelList::slotSaveDestination, m_actionCollection); 0104 m_save->setEnabled(false); 0105 } else { 0106 m_save = nullptr; 0107 } 0108 0109 updateModelListActions(); 0110 } 0111 0112 KompareModelList::~KompareModelList() 0113 { 0114 m_selectedModel = nullptr; 0115 m_selectedDifference = nullptr; 0116 m_info = nullptr; 0117 delete m_models; 0118 } 0119 0120 bool KompareModelList::isDirectory(const QString& url) const 0121 { 0122 QFileInfo fi(url); 0123 if (fi.isDir()) 0124 return true; 0125 else 0126 return false; 0127 } 0128 0129 bool KompareModelList::isDiff(const QString& mimeType) const 0130 { 0131 if (mimeType == QLatin1String("text/x-patch")) 0132 return true; 0133 else 0134 return false; 0135 } 0136 0137 bool KompareModelList::compare() 0138 { 0139 bool result = false; 0140 0141 bool sourceIsDirectory = isDirectory(m_info->localSource); 0142 bool destinationIsDirectory = isDirectory(m_info->localDestination); 0143 0144 if (sourceIsDirectory && destinationIsDirectory) 0145 { 0146 m_info->mode = Kompare::ComparingDirs; 0147 result = compare(m_info->mode); 0148 } 0149 else if (!sourceIsDirectory && !destinationIsDirectory) 0150 { 0151 QFile sourceFile(m_info->localSource); 0152 sourceFile.open(QIODevice::ReadOnly); 0153 QMimeDatabase db; 0154 QString sourceMimeType = (db.mimeTypeForData(sourceFile.readAll())).name(); 0155 sourceFile.close(); 0156 qCDebug(LIBKOMPAREDIFF2) << "Mimetype source : " << sourceMimeType; 0157 0158 QFile destinationFile(m_info->localDestination); 0159 destinationFile.open(QIODevice::ReadOnly); 0160 QString destinationMimeType = (db.mimeTypeForData(destinationFile.readAll())).name(); 0161 destinationFile.close(); 0162 qCDebug(LIBKOMPAREDIFF2) << "Mimetype destination: " << destinationMimeType; 0163 0164 // Not checking if it is a text file/something diff can even compare, we'll let diff handle that 0165 if (!isDiff(sourceMimeType) && isDiff(destinationMimeType)) 0166 { 0167 qCDebug(LIBKOMPAREDIFF2) << "Blending destination into source..."; 0168 m_info->mode = Kompare::BlendingFile; 0169 result = openFileAndDiff(); 0170 } 0171 else if (isDiff(sourceMimeType) && !isDiff(destinationMimeType)) 0172 { 0173 qCDebug(LIBKOMPAREDIFF2) << "Blending source into destination..."; 0174 m_info->mode = Kompare::BlendingFile; 0175 // Swap source and destination before calling this 0176 m_info->swapSourceWithDestination(); 0177 // Do we need to notify anyone we swapped source and destination? 0178 // No we do not need to notify anyone about swapping source with destination 0179 result = openFileAndDiff(); 0180 } 0181 else 0182 { 0183 qCDebug(LIBKOMPAREDIFF2) << "Comparing source with destination"; 0184 m_info->mode = Kompare::ComparingFiles; 0185 result = compare(m_info->mode); 0186 } 0187 } 0188 else if (sourceIsDirectory && !destinationIsDirectory) 0189 { 0190 m_info->mode = Kompare::BlendingDir; 0191 result = openDirAndDiff(); 0192 } 0193 else 0194 { 0195 m_info->mode = Kompare::BlendingDir; 0196 // Swap source and destination first in m_info 0197 m_info->swapSourceWithDestination(); 0198 // Do we need to notify anyone we swapped source and destination? 0199 // No we do not need to notify anyone about swapping source with destination 0200 result = openDirAndDiff(); 0201 } 0202 0203 return result; 0204 } 0205 0206 bool KompareModelList::compare(Kompare::Mode mode) 0207 { 0208 clear(); // Destroy the old models... 0209 0210 m_diffProcess = new KompareProcess(m_diffSettings, Kompare::Custom, m_info->localSource, m_info->localDestination, QString(), mode); 0211 m_diffProcess->setEncoding(m_encoding); 0212 0213 connect(m_diffProcess, &KompareProcess::diffHasFinished, 0214 this, &KompareModelList::slotDiffProcessFinished); 0215 0216 Q_EMIT status(Kompare::RunningDiff); 0217 m_diffProcess->start(); 0218 0219 return true; 0220 } 0221 0222 static QString lstripSeparators(const QString& from, uint count) 0223 { 0224 int position = 0; 0225 for (uint i = 0; i < count; ++i) 0226 { 0227 position = from.indexOf(QLatin1Char('/'), position); 0228 if (position == -1) 0229 { 0230 break; 0231 } 0232 } 0233 if (position == -1) 0234 { 0235 return QString(); 0236 } 0237 0238 return from.mid(position + 1); 0239 } 0240 0241 void KompareModelList::setDepthAndApplied() 0242 { 0243 // Splice to avoid calling ~DiffModelList 0244 const QList<Diff2::DiffModel*> splicedModelList(*m_models); 0245 for (DiffModel* model : splicedModelList) { 0246 model->setSourceFile(lstripSeparators(model->source(), m_info->depth)); 0247 model->setDestinationFile(lstripSeparators(model->destination(), m_info->depth)); 0248 model->applyAllDifferences(m_info->applied); 0249 } 0250 } 0251 0252 bool KompareModelList::openFileAndDiff() 0253 { 0254 clear(); 0255 0256 if (parseDiffOutput(readFile(m_info->localDestination)) != 0) 0257 { 0258 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())); 0259 return false; 0260 } 0261 0262 setDepthAndApplied(); 0263 0264 if (!blendOriginalIntoModelList(m_info->localSource)) 0265 { 0266 qCDebug(LIBKOMPAREDIFF2) << "Oops cant blend original file into modellist : " << m_info->localSource; 0267 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())); 0268 return false; 0269 } 0270 0271 updateModelListActions(); 0272 show(); 0273 0274 return true; 0275 } 0276 0277 bool KompareModelList::openDirAndDiff() 0278 { 0279 clear(); 0280 0281 if (parseDiffOutput(readFile(m_info->localDestination)) != 0) 0282 { 0283 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())); 0284 return false; 0285 } 0286 0287 setDepthAndApplied(); 0288 0289 // Do our thing :) 0290 if (!blendOriginalIntoModelList(m_info->localSource)) 0291 { 0292 // Trouble blending the original into the model 0293 qCDebug(LIBKOMPAREDIFF2) << "Oops cant blend original dir into modellist : " << m_info->localSource; 0294 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())); 0295 return false; 0296 } 0297 0298 updateModelListActions(); 0299 show(); 0300 0301 return true; 0302 } 0303 0304 void KompareModelList::slotSaveDestination() 0305 { 0306 // Unnecessary safety check! We can now guarantee that saving is only possible when there is a model and there are unsaved changes 0307 if (m_selectedModel) 0308 { 0309 saveDestination(m_selectedModel); 0310 if (m_save) m_save->setEnabled(false); 0311 Q_EMIT updateActions(); 0312 } 0313 } 0314 0315 bool KompareModelList::saveDestination(DiffModel* model) 0316 { 0317 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::saveDestination: "; 0318 0319 // Unnecessary safety check, we can guarantee there are unsaved changes!!! 0320 if (!model->hasUnsavedChanges()) 0321 return true; 0322 0323 QTemporaryFile temp; 0324 0325 if (!temp.open()) { 0326 Q_EMIT error(i18n("Could not open a temporary file.")); 0327 temp.remove(); 0328 return false; 0329 } 0330 0331 QTextStream stream(&temp); 0332 QStringList list; 0333 0334 DiffHunkListConstIterator hunkIt = model->hunks()->constBegin(); 0335 DiffHunkListConstIterator hEnd = model->hunks()->constEnd(); 0336 0337 for (; hunkIt != hEnd; ++hunkIt) 0338 { 0339 DiffHunk* hunk = *hunkIt; 0340 0341 DifferenceListConstIterator diffIt = hunk->differences().constBegin(); 0342 DifferenceListConstIterator dEnd = hunk->differences().constEnd(); 0343 0344 Difference* diff; 0345 for (; diffIt != dEnd; ++diffIt) 0346 { 0347 diff = *diffIt; 0348 if (!diff->applied()) 0349 { 0350 DifferenceStringListConstIterator stringIt = diff->destinationLines().begin(); 0351 DifferenceStringListConstIterator sEnd = diff->destinationLines().end(); 0352 for (; stringIt != sEnd; ++stringIt) 0353 { 0354 list.append((*stringIt)->string()); 0355 } 0356 } 0357 else 0358 { 0359 DifferenceStringListConstIterator stringIt = diff->sourceLines().begin(); 0360 DifferenceStringListConstIterator sEnd = diff->sourceLines().end(); 0361 for (; stringIt != sEnd; ++stringIt) 0362 { 0363 list.append((*stringIt)->string()); 0364 } 0365 } 0366 } 0367 } 0368 0369 // qCDebug(LIBKOMPAREDIFF2) << "Everything: " << endl << list.join( "\n" ); 0370 0371 if (list.count() > 0) 0372 stream << list.join(QString()); 0373 if (temp.error() != QFile::NoError) { 0374 Q_EMIT error(i18n("<qt>Could not write to the temporary file <b>%1</b>, deleting it.</qt>", temp.fileName())); 0375 temp.remove(); 0376 return false; 0377 } 0378 0379 temp.close(); 0380 if (temp.error() != QFile::NoError) { 0381 Q_EMIT error(i18n("<qt>Could not write to the temporary file <b>%1</b>, deleting it.</qt>", temp.fileName())); 0382 temp.remove(); 0383 return false; 0384 } 0385 0386 bool result = false; 0387 0388 // Make sure the destination directory exists, it is possible when using -N to not have the destination dir/file available 0389 if (m_info->mode == Kompare::ComparingDirs) 0390 { 0391 // Don't use destination which was used for creating the diff directly, use the original URL!!! 0392 // FIXME!!! Wrong destination this way! Need to add the sub directory to the url!!!! 0393 qCDebug(LIBKOMPAREDIFF2) << "Tempfilename (save) : " << temp.fileName(); 0394 qCDebug(LIBKOMPAREDIFF2) << "Model->path+file : " << model->destinationPath() << model->destinationFile(); 0395 qCDebug(LIBKOMPAREDIFF2) << "info->localdest : " << m_info->localDestination; 0396 QString tmp = model->destinationPath(); 0397 if (tmp.startsWith(m_info->localDestination)) // It should, if not serious trouble... 0398 tmp.remove(0, m_info->localDestination.size()); 0399 qCDebug(LIBKOMPAREDIFF2) << "DestinationURL : " << m_info->destination; 0400 qCDebug(LIBKOMPAREDIFF2) << "tmp : " << tmp; 0401 KIO::UDSEntry entry; 0402 QUrl fullDestinationPath = m_info->destination; 0403 fullDestinationPath.setPath(fullDestinationPath.path() + tmp); 0404 qCDebug(LIBKOMPAREDIFF2) << "fullDestinationPath : " << fullDestinationPath; 0405 KIO::StatJob* statJob = KIO::stat(fullDestinationPath); 0406 if (!statJob->exec()) 0407 { 0408 entry = statJob->statResult(); 0409 KIO::MkdirJob* mkdirJob = KIO::mkdir(fullDestinationPath); 0410 if (!mkdirJob->exec()) 0411 { 0412 Q_EMIT error(i18n("<qt>Could not create destination directory <b>%1</b>.\nThe file has not been saved.</qt>", fullDestinationPath.path())); 0413 return false; 0414 } 0415 } 0416 fullDestinationPath.setPath(fullDestinationPath.path() + model->destinationFile()); 0417 KIO::FileCopyJob* copyJob = KIO::file_copy(QUrl::fromLocalFile(temp.fileName()), fullDestinationPath, -1, KIO::Overwrite); 0418 result = copyJob->exec(); 0419 } 0420 else 0421 { 0422 qCDebug(LIBKOMPAREDIFF2) << "Tempfilename : " << temp.fileName(); 0423 qCDebug(LIBKOMPAREDIFF2) << "DestinationURL : " << m_info->destination; 0424 0425 // Get permissions of existing file and copy temporary file with the same permissions 0426 int permissions = -1; 0427 KIO::StatJob* statJob = KIO::stat(m_info->destination); 0428 result = statJob->exec(); 0429 if (result) 0430 permissions = statJob->statResult().numberValue(KIO::UDSEntry::UDS_ACCESS); 0431 0432 KIO::FileCopyJob* copyJob = KIO::file_copy(QUrl::fromLocalFile(temp.fileName()), m_info->destination , permissions, KIO::Overwrite); 0433 result = copyJob->exec(); 0434 qCDebug(LIBKOMPAREDIFF2) << "true or false?" << result; 0435 } 0436 0437 if (!result) 0438 { 0439 // FIXME: Wrong first argument given in case of comparing directories! 0440 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())); 0441 //Don't remove file when we delete temp and don't leak it. 0442 temp.setAutoRemove(false); 0443 } 0444 else 0445 { 0446 temp.remove(); 0447 } 0448 0449 // If saving was fine set all differences to saved so we can start again with a clean slate 0450 if (result) 0451 { 0452 DifferenceListConstIterator diffIt = model->differences()->constBegin(); 0453 DifferenceListConstIterator endIt = model->differences()->constEnd(); 0454 0455 for (; diffIt != endIt; ++diffIt) 0456 { 0457 (*diffIt)->setUnsaved(false); 0458 } 0459 } 0460 0461 return true; 0462 } 0463 0464 bool KompareModelList::saveAll() 0465 { 0466 if (modelCount() == 0) 0467 return false; 0468 0469 DiffModelListIterator it = m_models->begin(); 0470 DiffModelListIterator end = m_models->end(); 0471 for (; it != end; ++it) 0472 { 0473 if (!saveDestination(*it)) 0474 return false; 0475 } 0476 0477 return true; 0478 } 0479 0480 void KompareModelList::setEncoding(const QString& encoding) 0481 { 0482 m_encoding = encoding; 0483 if (!encoding.compare(QLatin1String("default"), Qt::CaseInsensitive)) 0484 { 0485 m_textCodec = QTextCodec::codecForLocale(); 0486 } 0487 else 0488 { 0489 qCDebug(LIBKOMPAREDIFF2) << "Encoding : " << encoding; 0490 m_textCodec = QTextCodec::codecForName(encoding.toUtf8()); 0491 qCDebug(LIBKOMPAREDIFF2) << "TextCodec: " << m_textCodec; 0492 if (!m_textCodec) 0493 m_textCodec = QTextCodec::codecForLocale(); 0494 } 0495 qCDebug(LIBKOMPAREDIFF2) << "TextCodec: " << m_textCodec; 0496 } 0497 0498 void KompareModelList::setReadWrite(bool isReadWrite) 0499 { 0500 if (m_isReadWrite == isReadWrite) 0501 return; 0502 0503 m_isReadWrite = isReadWrite; 0504 updateModelListActions(); 0505 } 0506 0507 bool KompareModelList::isReadWrite() const 0508 { 0509 return m_isReadWrite; 0510 } 0511 0512 void KompareModelList::slotDiffProcessFinished(bool success) 0513 { 0514 if (success) 0515 { 0516 Q_EMIT status(Kompare::Parsing); 0517 if (parseDiffOutput(m_diffProcess->diffOutput()) != 0) 0518 { 0519 Q_EMIT error(i18n("Could not parse diff output.")); 0520 } 0521 else 0522 { 0523 if (m_info->mode != Kompare::ShowingDiff) 0524 { 0525 qCDebug(LIBKOMPAREDIFF2) << "Blend this crap please and do not give me any conflicts..."; 0526 blendOriginalIntoModelList(m_info->localSource); 0527 } 0528 updateModelListActions(); 0529 show(); 0530 } 0531 Q_EMIT status(Kompare::FinishedParsing); 0532 } 0533 else if (m_diffProcess->exitStatus() == 0) 0534 { 0535 Q_EMIT error(i18n("The files are identical.")); 0536 } 0537 else 0538 { 0539 Q_EMIT error(m_diffProcess->stdErr()); 0540 } 0541 0542 m_diffProcess->deleteLater(); 0543 m_diffProcess = nullptr; 0544 } 0545 0546 void KompareModelList::slotDirectoryChanged(const QString& /*dir*/) 0547 { 0548 // some debug output to see if watching works properly 0549 qCDebug(LIBKOMPAREDIFF2) << "Yippie directories are being watched !!! :)"; 0550 if (m_diffProcess) 0551 { 0552 Q_EMIT status(Kompare::ReRunningDiff); 0553 m_diffProcess->start(); 0554 } 0555 } 0556 0557 void KompareModelList::slotFileChanged(const QString& /*file*/) 0558 { 0559 // some debug output to see if watching works properly 0560 qCDebug(LIBKOMPAREDIFF2) << "Yippie files are being watched !!! :)"; 0561 if (m_diffProcess) 0562 { 0563 Q_EMIT status(Kompare::ReRunningDiff); 0564 m_diffProcess->start(); 0565 } 0566 } 0567 0568 QStringList KompareModelList::split(const QString& fileContents) 0569 { 0570 QString contents = fileContents; 0571 QStringList list; 0572 0573 int pos = 0; 0574 int oldpos = 0; 0575 // split that does not strip the split char 0576 #ifdef QT_OS_MAC 0577 const char split = '\r'; 0578 #else 0579 const char split = '\n'; 0580 #endif 0581 while ((pos = contents.indexOf(QLatin1Char(split), oldpos)) >= 0) 0582 { 0583 list.append(contents.mid(oldpos, pos - oldpos + 1)); 0584 oldpos = pos + 1; 0585 } 0586 0587 if (contents.length() > oldpos) 0588 { 0589 list.append(contents.right(contents.length() - oldpos)); 0590 } 0591 0592 return list; 0593 } 0594 0595 QString KompareModelList::readFile(const QString& fileName) 0596 { 0597 QStringList list; 0598 0599 QFile file(fileName); 0600 file.open(QIODevice::ReadOnly); 0601 0602 qCDebug(LIBKOMPAREDIFF2) << "Codec = " << m_textCodec; 0603 if (!m_textCodec) 0604 m_textCodec = QTextCodec::codecForLocale(); 0605 std::unique_ptr<QTextDecoder> decoder(m_textCodec->makeDecoder()); 0606 0607 QString contents; 0608 while (!file.atEnd()) { 0609 char buffer[4096]; 0610 const auto len = file.read(buffer, 4096); 0611 contents += decoder->toUnicode(buffer, len); 0612 } 0613 0614 file.close(); 0615 0616 return contents; 0617 } 0618 0619 bool KompareModelList::openDiff(const QString& diffFile) 0620 { 0621 qCDebug(LIBKOMPAREDIFF2) << "Stupid :) Url = " << diffFile; 0622 0623 if (diffFile.isEmpty()) 0624 return false; 0625 0626 QString diff = readFile(diffFile); 0627 0628 clear(); // Clear the current models 0629 0630 Q_EMIT status(Kompare::Parsing); 0631 0632 if (parseDiffOutput(diff) != 0) 0633 { 0634 Q_EMIT error(i18n("Could not parse diff output.")); 0635 return false; 0636 } 0637 0638 updateModelListActions(); 0639 show(); 0640 0641 Q_EMIT status(Kompare::FinishedParsing); 0642 0643 return true; 0644 } 0645 0646 bool KompareModelList::parseAndOpenDiff(const QString& diff) 0647 { 0648 clear(); // Clear the current models 0649 0650 Q_EMIT status(Kompare::Parsing); 0651 0652 if (parseDiffOutput(diff) != 0) 0653 { 0654 Q_EMIT error(i18n("Could not parse diff output.")); 0655 return false; 0656 } 0657 0658 updateModelListActions(); 0659 show(); 0660 0661 Q_EMIT status(Kompare::FinishedParsing); 0662 return true; 0663 } 0664 0665 QString KompareModelList::recreateDiff() const 0666 { 0667 QString diff; 0668 0669 DiffModelListConstIterator modelIt = m_models->constBegin(); 0670 DiffModelListConstIterator mEnd = m_models->constEnd(); 0671 0672 for (; modelIt != mEnd; ++modelIt) 0673 { 0674 diff += (*modelIt)->recreateDiff(); 0675 } 0676 return diff; 0677 } 0678 0679 bool KompareModelList::saveDiff(const QString& url, QString directory, DiffSettings* diffSettings) 0680 { 0681 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::saveDiff: "; 0682 0683 m_diffTemp = new QTemporaryFile(); 0684 m_diffURL = QUrl(url); // ### TODO the "url" argument should be a QUrl 0685 0686 if (!m_diffTemp->open()) { 0687 Q_EMIT error(i18n("Could not open a temporary file.")); 0688 m_diffTemp->remove(); 0689 delete m_diffTemp; 0690 m_diffTemp = nullptr; 0691 return false; 0692 } 0693 0694 m_diffProcess = new KompareProcess(diffSettings, Kompare::Custom, m_info->localSource, m_info->localDestination, directory); 0695 m_diffProcess->setEncoding(m_encoding); 0696 0697 connect(m_diffProcess, &KompareProcess::diffHasFinished, 0698 this, &KompareModelList::slotWriteDiffOutput); 0699 0700 Q_EMIT status(Kompare::RunningDiff); 0701 m_diffProcess->start(); 0702 return true; 0703 } 0704 0705 void KompareModelList::slotWriteDiffOutput(bool success) 0706 { 0707 qCDebug(LIBKOMPAREDIFF2) << "Success = " << success; 0708 0709 if (success) 0710 { 0711 QTextStream stream(m_diffTemp); 0712 0713 stream << m_diffProcess->diffOutput(); 0714 0715 m_diffTemp->close(); 0716 0717 if (false /*|| m_diffTemp->status() != 0 */) 0718 { 0719 Q_EMIT error(i18n("Could not write to the temporary file.")); 0720 } 0721 0722 KIO::FileCopyJob* copyJob = KIO::file_copy(QUrl::fromLocalFile(m_diffTemp->fileName()), m_diffURL); 0723 copyJob->exec(); 0724 0725 Q_EMIT status(Kompare::FinishedWritingDiff); 0726 } 0727 0728 m_diffURL = QUrl(); 0729 m_diffTemp->remove(); 0730 0731 delete m_diffTemp; 0732 m_diffTemp = nullptr; 0733 0734 delete m_diffProcess; 0735 m_diffProcess = nullptr; 0736 } 0737 0738 void KompareModelList::slotSelectionChanged(const Diff2::DiffModel* model, const Diff2::Difference* diff) 0739 { 0740 // This method will signal all the other objects about a change in selection, 0741 // it will Q_EMIT setSelection( const DiffModel*, const Difference* ) to all who are connected 0742 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::slotSelectionChanged( " << model << ", " << diff << " )"; 0743 qCDebug(LIBKOMPAREDIFF2) << "Sender is : " << sender()->metaObject()->className(); 0744 // qCDebug(LIBKOMPAREDIFF2) << kBacktrace(); 0745 0746 m_selectedModel = const_cast<DiffModel*>(model); 0747 m_modelIndex = m_models->indexOf(m_selectedModel); 0748 qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex; 0749 m_selectedDifference = const_cast<Difference*>(diff); 0750 0751 m_selectedModel->setSelectedDifference(m_selectedDifference); 0752 0753 // setSelected* search for the argument in the lists and return false if not found 0754 // if found they return true and set the m_selected* 0755 if (!setSelectedModel(m_selectedModel)) 0756 { 0757 // Backup plan 0758 m_selectedModel = firstModel(); 0759 m_selectedDifference = m_selectedModel->firstDifference(); 0760 } 0761 else if (!m_selectedModel->setSelectedDifference(m_selectedDifference)) 0762 { 0763 // Another backup plan 0764 m_selectedDifference = m_selectedModel->firstDifference(); 0765 } 0766 0767 Q_EMIT setSelection(model, diff); 0768 Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount()); 0769 0770 updateModelListActions(); 0771 } 0772 0773 void KompareModelList::slotSelectionChanged(const Diff2::Difference* diff) 0774 { 0775 // This method will Q_EMIT setSelection( const Difference* ) to whomever is listening 0776 // when for instance in kompareview the selection has changed 0777 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::slotSelectionChanged( " << diff << " )"; 0778 qCDebug(LIBKOMPAREDIFF2) << "Sender is : " << sender()->metaObject()->className(); 0779 0780 m_selectedDifference = const_cast<Difference*>(diff); 0781 0782 if (!m_selectedModel->setSelectedDifference(m_selectedDifference)) 0783 { 0784 // Backup plan 0785 m_selectedDifference = m_selectedModel->firstDifference(); 0786 } 0787 0788 Q_EMIT setSelection(diff); 0789 Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount()); 0790 0791 updateModelListActions(); 0792 } 0793 0794 void KompareModelList::slotPreviousModel() 0795 { 0796 if ((m_selectedModel = prevModel()) != nullptr) 0797 { 0798 m_selectedDifference = m_selectedModel->firstDifference(); 0799 } 0800 else 0801 { 0802 m_selectedModel = firstModel(); 0803 m_selectedDifference = m_selectedModel->firstDifference(); 0804 } 0805 0806 Q_EMIT setSelection(m_selectedModel, m_selectedDifference); 0807 Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount()); 0808 updateModelListActions(); 0809 } 0810 0811 void KompareModelList::slotNextModel() 0812 { 0813 if ((m_selectedModel = nextModel()) != nullptr) 0814 { 0815 m_selectedDifference = m_selectedModel->firstDifference(); 0816 } 0817 else 0818 { 0819 m_selectedModel = lastModel(); 0820 m_selectedDifference = m_selectedModel->firstDifference(); 0821 } 0822 0823 Q_EMIT setSelection(m_selectedModel, m_selectedDifference); 0824 Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount()); 0825 updateModelListActions(); 0826 } 0827 0828 DiffModel* KompareModelList::firstModel() 0829 { 0830 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::firstModel()"; 0831 m_modelIndex = 0; 0832 qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex; 0833 0834 m_selectedModel = m_models->first(); 0835 0836 return m_selectedModel; 0837 } 0838 0839 DiffModel* KompareModelList::lastModel() 0840 { 0841 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::lastModel()"; 0842 m_modelIndex = m_models->count() - 1; 0843 qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex; 0844 0845 m_selectedModel = m_models->last(); 0846 0847 return m_selectedModel; 0848 } 0849 0850 DiffModel* KompareModelList::prevModel() 0851 { 0852 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::prevModel()"; 0853 if (m_modelIndex > 0 && --m_modelIndex < m_models->count()) 0854 { 0855 qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex; 0856 m_selectedModel = (*m_models)[ m_modelIndex ]; 0857 } 0858 else 0859 { 0860 m_selectedModel = nullptr; 0861 m_modelIndex = 0; 0862 qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex; 0863 } 0864 0865 return m_selectedModel; 0866 } 0867 0868 DiffModel* KompareModelList::nextModel() 0869 { 0870 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::nextModel()"; 0871 if (++m_modelIndex < m_models->count()) 0872 { 0873 qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex; 0874 m_selectedModel = (*m_models)[ m_modelIndex ]; 0875 } 0876 else 0877 { 0878 m_selectedModel = nullptr; 0879 m_modelIndex = 0; 0880 qCDebug(LIBKOMPAREDIFF2) << "m_modelIndex = " << m_modelIndex; 0881 } 0882 0883 return m_selectedModel; 0884 } 0885 0886 KActionCollection* KompareModelList::actionCollection() const 0887 { 0888 return m_actionCollection; 0889 } 0890 0891 void KompareModelList::slotPreviousDifference() 0892 { 0893 qCDebug(LIBKOMPAREDIFF2) << "slotPreviousDifference called"; 0894 if ((m_selectedDifference = m_selectedModel->prevDifference()) != nullptr) 0895 { 0896 Q_EMIT setSelection(m_selectedDifference); 0897 Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount()); 0898 updateModelListActions(); 0899 return; 0900 } 0901 0902 qCDebug(LIBKOMPAREDIFF2) << "**** no previous difference... ok lets find the previous model..."; 0903 0904 if ((m_selectedModel = prevModel()) != nullptr) 0905 { 0906 m_selectedDifference = m_selectedModel->lastDifference(); 0907 0908 Q_EMIT setSelection(m_selectedModel, m_selectedDifference); 0909 Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount()); 0910 updateModelListActions(); 0911 return; 0912 } 0913 0914 0915 qCDebug(LIBKOMPAREDIFF2) << "**** !!! No previous model, ok backup plan activated..."; 0916 0917 // Backup plan 0918 m_selectedModel = firstModel(); 0919 m_selectedDifference = m_selectedModel->firstDifference(); 0920 0921 Q_EMIT setSelection(m_selectedModel, m_selectedDifference); 0922 Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount()); 0923 updateModelListActions(); 0924 } 0925 0926 void KompareModelList::slotNextDifference() 0927 { 0928 qCDebug(LIBKOMPAREDIFF2) << "slotNextDifference called"; 0929 if ((m_selectedDifference = m_selectedModel->nextDifference()) != nullptr) 0930 { 0931 Q_EMIT setSelection(m_selectedDifference); 0932 Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount()); 0933 updateModelListActions(); 0934 return; 0935 } 0936 0937 qCDebug(LIBKOMPAREDIFF2) << "**** no next difference... ok lets find the next model..."; 0938 0939 if ((m_selectedModel = nextModel()) != nullptr) 0940 { 0941 m_selectedDifference = m_selectedModel->firstDifference(); 0942 0943 Q_EMIT setSelection(m_selectedModel, m_selectedDifference); 0944 Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount()); 0945 updateModelListActions(); 0946 return; 0947 } 0948 0949 qCDebug(LIBKOMPAREDIFF2) << "**** !!! No next model, ok backup plan activated..."; 0950 0951 // Backup plan 0952 m_selectedModel = lastModel(); 0953 m_selectedDifference = m_selectedModel->lastDifference(); 0954 0955 Q_EMIT setSelection(m_selectedModel, m_selectedDifference); 0956 Q_EMIT setStatusBarModelInfo(findModel(m_selectedModel), m_selectedModel->findDifference(m_selectedDifference), modelCount(), differenceCount(), m_selectedModel->appliedCount()); 0957 updateModelListActions(); 0958 } 0959 0960 void KompareModelList::slotApplyDifference(bool apply) 0961 { 0962 m_selectedModel->applyDifference(apply); 0963 Q_EMIT applyDifference(apply); 0964 } 0965 0966 void KompareModelList::slotApplyAllDifferences(bool apply) 0967 { 0968 m_selectedModel->applyAllDifferences(apply); 0969 Q_EMIT applyAllDifferences(apply); 0970 } 0971 0972 int KompareModelList::parseDiffOutput(const QString& diff) 0973 { 0974 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::parseDiffOutput"; 0975 Q_EMIT diffString(diff); 0976 0977 QStringList diffLines = split(diff); 0978 0979 Parser* parser = new Parser(this); 0980 bool malformed = false; 0981 m_models = parser->parse(diffLines, &malformed); 0982 0983 m_info->generator = parser->generator(); 0984 m_info->format = parser->format(); 0985 0986 delete parser; 0987 0988 if (m_models) 0989 { 0990 if (malformed) 0991 { 0992 qCDebug(LIBKOMPAREDIFF2) << "Malformed diff"; 0993 Q_EMIT error(i18n("The diff is malformed. Some lines could not be parsed and will not be displayed in the diff view.")); 0994 // proceed anyway with the lines which have been parsed 0995 } 0996 m_selectedModel = firstModel(); 0997 qCDebug(LIBKOMPAREDIFF2) << "Ok there are differences..."; 0998 m_selectedDifference = m_selectedModel->firstDifference(); 0999 Q_EMIT setStatusBarModelInfo(0, 0, modelCount(), differenceCount(), 0); 1000 } 1001 else 1002 { 1003 // Wow trouble, no models, so no differences... 1004 qCDebug(LIBKOMPAREDIFF2) << "Now i'll be damned, there should be models here !!!"; 1005 return -1; 1006 } 1007 1008 return 0; 1009 } 1010 1011 bool KompareModelList::blendOriginalIntoModelList(const QString& localURL) 1012 { 1013 qCDebug(LIBKOMPAREDIFF2) << "Hurrah we are blending..."; 1014 QFileInfo fi(localURL); 1015 1016 bool result = false; 1017 DiffModel* model; 1018 1019 QString fileContents; 1020 1021 if (fi.isDir()) 1022 { // is a dir 1023 qCDebug(LIBKOMPAREDIFF2) << "Blend Dir"; 1024 // QDir dir( localURL, QString(), QDir::Name|QDir::DirsFirst, QDir::TypeMask ); 1025 DiffModelListIterator modelIt = m_models->begin(); 1026 DiffModelListIterator mEnd = m_models->end(); 1027 for (; modelIt != mEnd; ++modelIt) 1028 { 1029 model = *modelIt; 1030 qCDebug(LIBKOMPAREDIFF2) << "Model : " << model; 1031 QString filename = model->source(); 1032 if (!filename.startsWith(localURL)) 1033 filename = QDir(localURL).filePath(filename); 1034 QFileInfo fi2(filename); 1035 if (fi2.exists()) 1036 { 1037 qCDebug(LIBKOMPAREDIFF2) << "Reading from: " << filename; 1038 fileContents = readFile(filename); 1039 result = blendFile(model, fileContents); 1040 } 1041 else 1042 { 1043 qCDebug(LIBKOMPAREDIFF2) << "File " << filename << " does not exist !"; 1044 qCDebug(LIBKOMPAREDIFF2) << "Assume empty file !"; 1045 fileContents.truncate(0); 1046 result = blendFile(model, fileContents); 1047 } 1048 } 1049 qCDebug(LIBKOMPAREDIFF2) << "End of Blend Dir"; 1050 } 1051 else if (fi.isFile()) 1052 { // is a file 1053 qCDebug(LIBKOMPAREDIFF2) << "Blend File"; 1054 qCDebug(LIBKOMPAREDIFF2) << "Reading from: " << localURL; 1055 fileContents = readFile(localURL); 1056 1057 result = blendFile((*m_models)[ 0 ], fileContents); 1058 qCDebug(LIBKOMPAREDIFF2) << "End of Blend File"; 1059 } 1060 1061 return result; 1062 } 1063 1064 bool KompareModelList::blendFile(DiffModel* model, const QString& fileContents) 1065 { 1066 if (!model) 1067 { 1068 qCDebug(LIBKOMPAREDIFF2) << "**** model is null :("; 1069 return false; 1070 } 1071 1072 model->setBlended(true); 1073 1074 int srcLineNo = 1, destLineNo = 1; 1075 1076 const QStringList lines = split(fileContents); 1077 auto linesIt = lines.constBegin(), lEnd = lines.constEnd(); 1078 1079 DiffHunkList* hunks = model->hunks(); 1080 qCDebug(LIBKOMPAREDIFF2) << "Hunks in hunklist: " << hunks->count(); 1081 DiffHunkListIterator hunkIt = hunks->begin(); 1082 1083 DiffHunk* newHunk = nullptr; 1084 Difference* newDiff = nullptr; 1085 1086 // FIXME: this approach is not very good, we should first check if the hunk applies cleanly 1087 // and without offset and if not use that new linenumber with offset to compare against 1088 // This will only work for files we just diffed with kompare but not for blending where 1089 // file(s) to patch has/have potentially changed 1090 1091 for (; hunkIt != hunks->end(); ++hunkIt) 1092 { 1093 // Do we need to insert a new hunk before this one ? 1094 DiffHunk* hunk = *hunkIt; 1095 if (srcLineNo < hunk->sourceLineNumber()) 1096 { 1097 newHunk = new DiffHunk(srcLineNo, destLineNo, QString(), DiffHunk::AddedByBlend); 1098 1099 hunkIt = ++hunks->insert(hunkIt, newHunk); 1100 1101 newDiff = new Difference(srcLineNo, destLineNo, 1102 Difference::Unchanged); 1103 1104 newHunk->add(newDiff); 1105 1106 while (srcLineNo < hunk->sourceLineNumber() && linesIt != lEnd) 1107 { 1108 newDiff->addSourceLine(*linesIt); 1109 newDiff->addDestinationLine(*linesIt); 1110 ++srcLineNo; 1111 ++destLineNo; 1112 ++linesIt; 1113 } 1114 } 1115 1116 // Now we add the linecount difference for the hunk that follows 1117 int size = hunk->sourceLineCount(); 1118 1119 linesIt += size; 1120 if (linesIt > lEnd) 1121 { 1122 linesIt = lEnd; 1123 } 1124 1125 srcLineNo += size; 1126 destLineNo += hunk->destinationLineCount(); 1127 } 1128 1129 if (linesIt != lEnd) 1130 { 1131 newHunk = new DiffHunk(srcLineNo, destLineNo, QString(), DiffHunk::AddedByBlend); 1132 1133 model->addHunk(newHunk); 1134 1135 newDiff = new Difference(srcLineNo, destLineNo, Difference::Unchanged); 1136 1137 newHunk->add(newDiff); 1138 1139 while (linesIt != lEnd) 1140 { 1141 newDiff->addSourceLine(*linesIt); 1142 newDiff->addDestinationLine(*linesIt); 1143 ++linesIt; 1144 } 1145 } 1146 #if 0 1147 DifferenceList hunkDiffList = (*hunkIt)->differences(); 1148 DifferenceListIterator diffIt = hunkDiffList.begin(); 1149 DifferenceListIterator dEnd = hunkDiffList.end(); 1150 qCDebug(LIBKOMPAREDIFF2) << "Number of differences in hunkDiffList = " << diffList.count(); 1151 1152 DifferenceListIterator tempIt; 1153 Difference* diff; 1154 1155 for (; diffIt != dEnd; ++diffIt) 1156 { 1157 diff = *diffIt; 1158 qCDebug(LIBKOMPAREDIFF2) << "*(Diff it) = " << diff; 1159 // Check if there are lines in the original file before the difference 1160 // that are not yet in the diff. If so create new Unchanged diff 1161 if (srcLineNo < diff->sourceLineNumber()) 1162 { 1163 newDiff = new Difference(srcLineNo, destLineNo, 1164 Difference::Unchanged | Difference::AddedByBlend); 1165 newHunk->add(newDiff); 1166 while (srcLineNo < diff->sourceLineNumber() && linesIt != lEnd) 1167 { 1168 // qCDebug(LIBKOMPAREDIFF2) << "SourceLine = " << srcLineNo << ": " << *linesIt; 1169 newDiff->addSourceLine(*linesIt); 1170 newDiff->addDestinationLine(*linesIt); 1171 ++srcLineNo; 1172 ++destLineNo; 1173 ++linesIt; 1174 } 1175 } 1176 // Now i've got to add that diff 1177 switch (diff->type()) 1178 { 1179 case Difference::Unchanged: 1180 qCDebug(LIBKOMPAREDIFF2) << "Unchanged"; 1181 for (int i = 0; i < diff->sourceLineCount(); ++i) 1182 { 1183 if (linesIt != lEnd && *linesIt != diff->sourceLineAt(i)->string()) 1184 { 1185 qCDebug(LIBKOMPAREDIFF2) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt; 1186 qCDebug(LIBKOMPAREDIFF2) << "Conflict: DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt(i)->string(); 1187 1188 // Do conflict resolution (well sort of) 1189 diff->sourceLineAt(i)->setConflictString(*linesIt); 1190 diff->setConflict(true); 1191 } 1192 // qCDebug(LIBKOMPAREDIFF2) << "SourceLine = " << srcLineNo << ": " << *linesIt; 1193 // qCDebug(LIBKOMPAREDIFF2) << "DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string(); 1194 ++srcLineNo; 1195 ++destLineNo; 1196 ++linesIt; 1197 } 1198 1199 tempIt = diffIt; 1200 --diffIt; 1201 diffList.remove(tempIt); 1202 newHunk->add(diff); 1203 1204 break; 1205 case Difference::Change: 1206 qCDebug(LIBKOMPAREDIFF2) << "Change"; 1207 1208 //QStringListConstIterator saveIt = linesIt; 1209 1210 for (int i = 0; i < diff->sourceLineCount(); ++i) 1211 { 1212 if (linesIt != lEnd && *linesIt != diff->sourceLineAt(i)->string()) 1213 { 1214 qCDebug(LIBKOMPAREDIFF2) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt; 1215 qCDebug(LIBKOMPAREDIFF2) << "Conflict: DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt(i)->string(); 1216 1217 // Do conflict resolution (well sort of) 1218 diff->sourceLineAt(i)->setConflictString(*linesIt); 1219 diff->setConflict(true); 1220 } 1221 ++srcLineNo; 1222 ++destLineNo; 1223 ++linesIt; 1224 } 1225 1226 destLineNo += diff->destinationLineCount(); 1227 1228 tempIt = diffIt; 1229 --diffIt; 1230 diffList.remove(tempIt); 1231 newHunk->add(diff); 1232 newModel->addDiff(diff); 1233 1234 break; 1235 case Difference::Insert: 1236 qCDebug(LIBKOMPAREDIFF2) << "Insert"; 1237 destLineNo += diff->destinationLineCount(); 1238 tempIt = diffIt; 1239 --diffIt; 1240 diffList.remove(tempIt); 1241 newHunk->add(diff); 1242 newModel->addDiff(diff); 1243 break; 1244 case Difference::Delete: 1245 qCDebug(LIBKOMPAREDIFF2) << "Delete"; 1246 qCDebug(LIBKOMPAREDIFF2) << "Number of lines in Delete: " << diff->sourceLineCount(); 1247 for (int i = 0; i < diff->sourceLineCount(); ++i) 1248 { 1249 if (linesIt != lEnd && *linesIt != diff->sourceLineAt(i)->string()) 1250 { 1251 qCDebug(LIBKOMPAREDIFF2) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt; 1252 qCDebug(LIBKOMPAREDIFF2) << "Conflict: DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt(i)->string(); 1253 1254 // Do conflict resolution (well sort of) 1255 diff->sourceLineAt(i)->setConflictString(*linesIt); 1256 diff->setConflict(true); 1257 } 1258 1259 // qCDebug(LIBKOMPAREDIFF2) << "SourceLine = " << srcLineNo << ": " << *it; 1260 // qCDebug(LIBKOMPAREDIFF2) << "DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string(); 1261 ++srcLineNo; 1262 ++linesIt; 1263 } 1264 1265 tempIt = diffIt; 1266 --diffIt; 1267 diffList.remove(tempIt); 1268 newHunk->add(diff); 1269 newModel->addDiff(diff); 1270 break; 1271 default: 1272 qCDebug(LIBKOMPAREDIFF2) << "****, some diff type we do not know about ???"; 1273 } 1274 } 1275 } 1276 #endif 1277 1278 /* 1279 diffList = newModel->differences(); 1280 1281 diff = diffList.first(); 1282 qCDebug(LIBKOMPAREDIFF2) << "Count = " << diffList.count(); 1283 for ( diff = diffList.first(); diff; diff = diffList.next() ) 1284 { 1285 qCDebug(LIBKOMPAREDIFF2) << "sourcelinenumber = " << diff->sourceLineNumber(); 1286 } 1287 */ 1288 1289 m_selectedModel = firstModel(); 1290 1291 m_selectedDifference = m_selectedModel->firstDifference(); 1292 1293 return true; 1294 } 1295 1296 void KompareModelList::show() 1297 { 1298 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::Show Number of models = " << m_models->count(); 1299 Q_EMIT modelsChanged(m_models); 1300 Q_EMIT setSelection(m_selectedModel, m_selectedDifference); 1301 } 1302 1303 void KompareModelList::clear() 1304 { 1305 if (m_models) 1306 m_models->clear(); 1307 1308 Q_EMIT modelsChanged(m_models); 1309 } 1310 1311 void KompareModelList::refresh() 1312 { 1313 // FIXME: I can imagine blending also wants to be refreshed so make a switch case here 1314 compare(m_info->mode); 1315 } 1316 1317 void KompareModelList::swap() 1318 { 1319 //FIXME Not sure if any mode could be swapped 1320 if (m_info->mode == Kompare::ComparingFiles) 1321 compare(m_info->mode); 1322 else if (m_info->mode == Kompare::ComparingDirs) 1323 compare(m_info->mode); 1324 } 1325 1326 bool KompareModelList::hasUnsavedChanges() const 1327 { 1328 if (modelCount() == 0) 1329 return false; 1330 1331 DiffModelListConstIterator modelIt = m_models->constBegin(); 1332 DiffModelListConstIterator endIt = m_models->constEnd(); 1333 1334 for (; modelIt != endIt; ++modelIt) 1335 { 1336 if ((*modelIt)->hasUnsavedChanges()) 1337 return true; 1338 } 1339 return false; 1340 } 1341 1342 int KompareModelList::modelCount() const 1343 { 1344 return m_models ? m_models->count() : 0; 1345 } 1346 1347 int KompareModelList::differenceCount() const 1348 { 1349 return m_selectedModel ? m_selectedModel->differenceCount() : -1; 1350 } 1351 1352 int KompareModelList::appliedCount() const 1353 { 1354 return m_selectedModel ? m_selectedModel->appliedCount() : -1; 1355 } 1356 1357 void KompareModelList::slotKompareInfo(struct Kompare::Info* info) 1358 { 1359 m_info = info; 1360 } 1361 1362 bool KompareModelList::setSelectedModel(DiffModel* model) 1363 { 1364 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::setSelectedModel( " << model << " )"; 1365 1366 if (model != m_selectedModel) 1367 { 1368 if (!m_models->contains(model)) 1369 return false; 1370 qCDebug(LIBKOMPAREDIFF2) << "m_selectedModel (was) = " << m_selectedModel; 1371 m_modelIndex = m_models->indexOf(model); 1372 qCDebug(LIBKOMPAREDIFF2) << "m_selectedModel (is) = " << m_selectedModel; 1373 m_selectedModel = model; 1374 } 1375 1376 updateModelListActions(); 1377 1378 return true; 1379 } 1380 1381 void KompareModelList::updateModelListActions() 1382 { 1383 if (m_models && m_selectedModel && m_selectedDifference) 1384 { 1385 if (m_isReadWrite && m_save) 1386 { 1387 if (m_selectedModel->appliedCount() != m_selectedModel->differenceCount()) 1388 m_applyAll->setEnabled(true); 1389 else 1390 m_applyAll->setEnabled(false); 1391 1392 if (m_selectedModel->appliedCount() != 0) 1393 m_unapplyAll->setEnabled(true); 1394 else 1395 m_unapplyAll->setEnabled(false); 1396 1397 m_applyDifference->setEnabled(m_selectedDifference->applied() == false); 1398 m_unApplyDifference->setEnabled(m_selectedDifference->applied() == true); 1399 m_save->setEnabled(m_selectedModel->hasUnsavedChanges()); 1400 } 1401 else if (m_save) 1402 { 1403 m_applyDifference->setEnabled(false); 1404 m_unApplyDifference->setEnabled(false); 1405 m_applyAll->setEnabled(false); 1406 m_unapplyAll->setEnabled(false); 1407 m_save->setEnabled(false); 1408 } 1409 1410 m_previousFile->setEnabled(hasPrevModel()); 1411 m_nextFile->setEnabled(hasNextModel()); 1412 m_previousDifference->setEnabled(hasPrevDiff()); 1413 m_nextDifference->setEnabled(hasNextDiff()); 1414 } 1415 else 1416 { 1417 if (m_save) { 1418 m_applyDifference->setEnabled(false); 1419 m_unApplyDifference->setEnabled(false); 1420 m_applyAll->setEnabled(false); 1421 m_unapplyAll->setEnabled(false); 1422 m_save->setEnabled(false); 1423 } 1424 1425 m_previousFile->setEnabled(false); 1426 m_nextFile->setEnabled(false); 1427 m_previousDifference->setEnabled(false); 1428 m_nextDifference->setEnabled(false); 1429 } 1430 } 1431 1432 bool KompareModelList::hasPrevModel() const 1433 { 1434 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::hasPrevModel()"; 1435 1436 if (m_modelIndex > 0) 1437 { 1438 // qCDebug(LIBKOMPAREDIFF2) << "has prev model"; 1439 return true; 1440 } 1441 1442 // qCDebug(LIBKOMPAREDIFF2) << "doesn't have a prev model, this is the first one..."; 1443 1444 return false; 1445 } 1446 1447 bool KompareModelList::hasNextModel() const 1448 { 1449 qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::hasNextModel()"; 1450 1451 if (m_modelIndex < (m_models->count() - 1)) 1452 { 1453 // qCDebug(LIBKOMPAREDIFF2) << "has next model"; 1454 return true; 1455 } 1456 1457 // qCDebug(LIBKOMPAREDIFF2) << "doesn't have a next model, this is the last one..."; 1458 return false; 1459 } 1460 1461 bool KompareModelList::hasPrevDiff() const 1462 { 1463 // qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::hasPrevDiff()"; 1464 int index = m_selectedModel->diffIndex(); 1465 1466 if (index > 0) 1467 { 1468 // qCDebug(LIBKOMPAREDIFF2) << "has prev difference in same model"; 1469 return true; 1470 } 1471 1472 if (hasPrevModel()) 1473 { 1474 // qCDebug(LIBKOMPAREDIFF2) << "has prev difference but in prev model"; 1475 return true; 1476 } 1477 1478 // qCDebug(LIBKOMPAREDIFF2) << "doesn't have a prev difference, not even in the previous model because there is no previous model"; 1479 1480 return false; 1481 } 1482 1483 bool KompareModelList::hasNextDiff() const 1484 { 1485 // qCDebug(LIBKOMPAREDIFF2) << "KompareModelList::hasNextDiff()"; 1486 int index = m_selectedModel->diffIndex(); 1487 1488 if (index < (m_selectedModel->differenceCount() - 1)) 1489 { 1490 // qCDebug(LIBKOMPAREDIFF2) << "has next difference in same model"; 1491 return true; 1492 } 1493 1494 if (hasNextModel()) 1495 { 1496 // qCDebug(LIBKOMPAREDIFF2) << "has next difference but in next model"; 1497 return true; 1498 } 1499 1500 // qCDebug(LIBKOMPAREDIFF2) << "doesn't have a next difference, not even in next model because there is no next model"; 1501 1502 return false; 1503 } 1504 1505 void KompareModelList::slotActionApplyDifference() 1506 { 1507 if (!m_selectedDifference->applied()) 1508 slotApplyDifference(true); 1509 slotNextDifference(); 1510 updateModelListActions(); 1511 } 1512 1513 void KompareModelList::slotActionUnApplyDifference() 1514 { 1515 if (m_selectedDifference->applied()) 1516 slotApplyDifference(false); 1517 slotPreviousDifference(); 1518 updateModelListActions(); 1519 } 1520 1521 void KompareModelList::slotActionApplyAllDifferences() 1522 { 1523 slotApplyAllDifferences(true); 1524 updateModelListActions(); 1525 } 1526 1527 void KompareModelList::slotActionUnapplyAllDifferences() 1528 { 1529 slotApplyAllDifferences(false); 1530 updateModelListActions(); 1531 } 1532 1533 #include "moc_komparemodellist.cpp" 1534 1535 /* vim: set ts=4 sw=4 noet: */