File indexing completed on 2024-12-01 05:11:57

0001 // clang-format off
0002 /*
0003  * KDiff3 - Text Diff And Merge Tool
0004  *
0005  * SPDX-FileCopyrightText: 2002-2011 Joachim Eibl, joachim.eibl at gmx.de
0006  * SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com
0007  * SPDX-License-Identifier: GPL-2.0-or-later
0008  */
0009 // clang-format on
0010 #ifndef MERGEFILEINFO_H
0011 #define MERGEFILEINFO_H
0012 
0013 #include "DirectoryInfo.h"
0014 #include "diff.h"
0015 #include "fileaccess.h"
0016 
0017 #include <QSharedPointer>
0018 #include <QString>
0019 
0020 enum e_MergeOperation
0021 {
0022     eTitleId,
0023     eNoOperation,
0024     // Operations in sync mode (with only two directories):
0025     eCopyAToB,
0026     eCopyBToA,
0027     eDeleteA,
0028     eDeleteB,
0029     eDeleteAB,
0030     eMergeToA,
0031     eMergeToB,
0032     eMergeToAB,
0033 
0034     // Operations in merge mode (with two or three directories)
0035     eCopyAToDest,
0036     eCopyBToDest,
0037     eCopyCToDest,
0038     eDeleteFromDest,
0039     eMergeABCToDest,
0040     eMergeABToDest,
0041     eConflictingFileTypes, // Error
0042     eChangedAndDeleted,    // Error
0043     eConflictingAges       // Equal age but files are not!
0044 };
0045 
0046 enum e_Age
0047 {
0048     eNew,
0049     eMiddle,
0050     eOld,
0051     eNotThere,
0052     eAgeEnd
0053 };
0054 
0055 enum e_OperationStatus
0056 {
0057     eOpStatusNone,
0058     eOpStatusDone,
0059     eOpStatusError,
0060     eOpStatusSkipped,
0061     eOpStatusNotSaved,
0062     eOpStatusInProgress,
0063     eOpStatusToDo
0064 };
0065 
0066 class DirectoryMergeWindow;
0067 
0068 class MergeFileInfos
0069 {
0070   public:
0071     MergeFileInfos();
0072     ~MergeFileInfos();
0073 
0074     [[nodiscard]] QString subPath() const;
0075     [[nodiscard]] QString fileName() const;
0076 
0077     [[nodiscard]] bool isDirA() const { return m_pFileInfoA != nullptr ? m_pFileInfoA->isDir() : false; }
0078     [[nodiscard]] bool isDirB() const { return m_pFileInfoB != nullptr ? m_pFileInfoB->isDir() : false; }
0079     [[nodiscard]] bool isDirC() const { return m_pFileInfoC != nullptr ? m_pFileInfoC->isDir() : false; }
0080     [[nodiscard]] bool hasDir() const { return isDirA() || isDirB() || isDirC(); }
0081 
0082     [[nodiscard]] bool isLinkA() const { return m_pFileInfoA != nullptr ? m_pFileInfoA->isSymLink() : false; }
0083     [[nodiscard]] bool isLinkB() const { return m_pFileInfoB != nullptr ? m_pFileInfoB->isSymLink() : false; }
0084     [[nodiscard]] bool isLinkC() const { return m_pFileInfoC != nullptr ? m_pFileInfoC->isSymLink() : false; }
0085     [[nodiscard]] bool hasLink() const { return isLinkA() || isLinkB() || isLinkC(); }
0086 
0087     [[nodiscard]] bool existsInA() const { return m_pFileInfoA != nullptr; }
0088     [[nodiscard]] bool existsInB() const { return m_pFileInfoB != nullptr; }
0089     [[nodiscard]] bool existsInC() const { return m_pFileInfoC != nullptr; }
0090 
0091     [[nodiscard]] bool conflictingFileTypes() const;
0092 
0093     void sort(Qt::SortOrder order);
0094     [[nodiscard]] inline MergeFileInfos* parent() const { return m_pParent; }
0095     inline void setParent(MergeFileInfos* inParent) { m_pParent = inParent; }
0096     [[nodiscard]] inline const QList<MergeFileInfos*>& children() const { return m_children; }
0097     inline void addChild(MergeFileInfos* child) { m_children.push_back(child); }
0098     inline void clear() { m_children.clear(); }
0099 
0100     [[nodiscard]] FileAccess* getFileInfoA() const { return m_pFileInfoA; }
0101     [[nodiscard]] FileAccess* getFileInfoB() const { return m_pFileInfoB; }
0102     [[nodiscard]] FileAccess* getFileInfoC() const { return m_pFileInfoC; }
0103 
0104     void setFileInfoA(FileAccess* newInfo) { m_pFileInfoA = newInfo; }
0105     void setFileInfoB(FileAccess* newInfo) { m_pFileInfoB = newInfo; }
0106     void setFileInfoC(FileAccess* newInfo) { m_pFileInfoC = newInfo; }
0107 
0108     [[nodiscard]] QString fullNameA() const;
0109     [[nodiscard]] QString fullNameB() const;
0110     [[nodiscard]] QString fullNameC() const;
0111     [[nodiscard]] QString fullNameDest() const;
0112 
0113     [[nodiscard]] inline QString getDirNameA() const { return gDirInfo->dirA().prettyAbsPath(); }
0114     [[nodiscard]] inline QString getDirNameB() const { return gDirInfo->dirB().prettyAbsPath(); }
0115     [[nodiscard]] inline QString getDirNameC() const { return gDirInfo->dirC().prettyAbsPath(); }
0116     [[nodiscard]] inline QString getDirNameDest() const { return gDirInfo->destDir().prettyAbsPath(); }
0117 
0118     [[nodiscard]] inline TotalDiffStatus& diffStatus() { return m_totalDiffStatus; }
0119 
0120     [[nodiscard]] inline e_MergeOperation getOperation() const { return m_eMergeOperation; }
0121     inline void setOperation(const e_MergeOperation op) { m_eMergeOperation = op; }
0122 
0123     [[nodiscard]] inline e_OperationStatus getOpStatus() const { return m_eOpStatus; }
0124     inline void setOpStatus(const e_OperationStatus eOpStatus) { m_eOpStatus = eOpStatus; }
0125 
0126     [[nodiscard]] inline e_Age getAgeA() const { return m_ageA; }
0127     [[nodiscard]] inline e_Age getAgeB() const { return m_ageB; }
0128     [[nodiscard]] inline e_Age getAgeC() const { return m_ageC; }
0129 
0130     [[nodiscard]] inline bool isEqualAB() const { return m_bEqualAB; }
0131     [[nodiscard]] inline bool isEqualAC() const { return m_bEqualAC; }
0132     [[nodiscard]] inline bool isEqualBC() const { return m_bEqualBC; }
0133     bool compareFilesAndCalcAges(QStringList& errors, DirectoryMergeWindow* pDMW);
0134 
0135     void updateAge();
0136 
0137     void updateParents();
0138 
0139     void updateDirectoryOrLink();
0140     inline void startSimOp() { m_bSimOpComplete = false; }
0141     [[nodiscard]] inline bool isSimOpRunning() const { return !m_bOperationComplete; }
0142     inline void endSimOp() { m_bSimOpComplete = true; }
0143 
0144     inline void startOperation() { m_bOperationComplete = false; };
0145     [[nodiscard]] inline bool isOperationRunning() const { return !m_bOperationComplete; }
0146     inline void endOperation() { m_bOperationComplete = true; };
0147 
0148     [[nodiscard]] inline static bool isThreeWay()
0149     {
0150         if(gDirInfo == nullptr) return false;
0151         return gDirInfo->dirC().isValid();
0152     }
0153     [[nodiscard]] inline bool existsEveryWhere() const { return existsInA() && existsInB() && (existsInC() || !isThreeWay()); }
0154 
0155     [[nodiscard]] inline qint32 existsCount() const { return (existsInA() ? 1 : 0) + (existsInB() ? 1 : 0) + (existsInC() ? 1 : 0); }
0156 
0157     [[nodiscard]] inline bool onlyInA() const { return existsInA() && !existsInB() && !existsInC(); }
0158     [[nodiscard]] inline bool onlyInB() const { return !existsInA() && existsInB() && !existsInC(); }
0159     [[nodiscard]] inline bool onlyInC() const { return !existsInA() && !existsInB() && existsInC(); }
0160 
0161     [[nodiscard]] bool conflictingAges() const { return m_bConflictingAges; }
0162 
0163   private:
0164     [[nodiscard]] e_Age nextAgeValue(e_Age age)
0165     {
0166         switch(age)
0167         {
0168             case eNew:
0169                 return eMiddle;
0170             case eMiddle:
0171                 return eOld;
0172             case eOld:
0173                 return eNotThere;
0174             case eNotThere:
0175                 return eAgeEnd;
0176             case eAgeEnd:
0177                 assert(false);
0178         }
0179         return age;
0180     }
0181 
0182     bool fastFileComparison(FileAccess& fi1, FileAccess& fi2, bool& bError, QString& status);
0183     inline void setAgeA(const e_Age inAge) { m_ageA = inAge; }
0184     inline void setAgeB(const e_Age inAge) { m_ageB = inAge; }
0185     inline void setAgeC(const e_Age inAge) { m_ageC = inAge; }
0186 
0187     MergeFileInfos* m_pParent = nullptr;
0188     QList<MergeFileInfos*> m_children;
0189 
0190     FileAccess* m_pFileInfoA = nullptr;
0191     FileAccess* m_pFileInfoB = nullptr;
0192     FileAccess* m_pFileInfoC = nullptr;
0193 
0194     TotalDiffStatus m_totalDiffStatus;
0195 
0196     e_MergeOperation m_eMergeOperation = eNoOperation;
0197     e_OperationStatus m_eOpStatus = eOpStatusNone;
0198     e_Age m_ageA = eNotThere;
0199     e_Age m_ageB = eNotThere;
0200     e_Age m_ageC = eNotThere;
0201 
0202     bool m_bOperationComplete = false;
0203     bool m_bSimOpComplete = false;
0204 
0205     bool m_bEqualAB = false;
0206     bool m_bEqualAC = false;
0207     bool m_bEqualBC = false;
0208     bool m_bConflictingAges = false; // Equal age but files are not!
0209 };
0210 
0211 QTextStream& operator<<(QTextStream& ts, MergeFileInfos& mfi);
0212 
0213 class MfiCompare
0214 {
0215     Qt::SortOrder mOrder;
0216 
0217   public:
0218     explicit MfiCompare(Qt::SortOrder order)
0219     {
0220         mOrder = order;
0221     }
0222     bool operator()(MergeFileInfos* pMFI1, MergeFileInfos* pMFI2)
0223     {
0224         bool bDir1 = pMFI1->isDirA() || pMFI1->isDirB() || pMFI1->isDirC();
0225         bool bDir2 = pMFI2->isDirA() || pMFI2->isDirB() || pMFI2->isDirC();
0226         if(bDir1 == bDir2)
0227         {
0228             if(mOrder == Qt::AscendingOrder)
0229             {
0230                 return pMFI1->fileName().compare(pMFI2->fileName(), Qt::CaseInsensitive) < 0;
0231             }
0232             else
0233             {
0234                 return pMFI1->fileName().compare(pMFI2->fileName(), Qt::CaseInsensitive) > 0;
0235             }
0236         }
0237         else
0238             return bDir1;
0239     }
0240 };
0241 
0242 #endif // !MERGEFILEINFO_H