File indexing completed on 2024-04-28 17:01:41

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: */