4 * @brief Declaration of DIFFITEM
8 #include "DiffFileInfo.h"
10 // Uncomment this to show debug information in the folder comparison window.
11 // We don't use _DEBUG since the mapping of the setting (OPT_DIRVIEW_COLUMN_ORDERS or OPT_DIRVIEW3_COLUMN_ORDERS) shifts if this feature is enabled.
12 //#define SHOW_DIFFITEM_DEBUG_INFO
15 * @brief Bitfield values for binary file sides.
16 * These values are used as bitfield values when determining which file(s)
17 * are binary files. There is no "both" -value since in bitfield "both" is
18 * when side1- and side2- bits are set (BINFILE_SIDE1 | BINFILE_SIDE2).
22 BINFILE_NONE = 0, /**< No binary files detected. */
23 BINFILE_SIDE1, /**< First file was detected as binary file. */
24 BINFILE_SIDE2, /**< Second file was detected as binart file. */
28 * @brief Status of one item comparison, stored as bitfields
30 * Bitmask can be seen as a 4 dimensional space; that is, there are four
31 * different attributes, and each entry picks one of each attribute
34 * One dimension is how the compare went: same or different or
37 * One dimension is file mode: text or binary (text is only if
38 * both sides were text)
40 * One dimension is existence: both sides, left only, or right only
42 * One dimension is type: directory, or file
47 * @brief values for DIFFITEM.diffcode
51 // We use extra bits so that no valid values are 0
52 // and each set of flags is in a different hex digit
53 // to make debugging easier
54 // These can always be packed down in the future
55 TEXTFLAGS=0x3FU, TEXT=0x1U, BIN=0x2U, BINSIDE1=0x4U, BINSIDE2=0x8U, BINSIDE3=0x10U, IMAGE=0x20U,
56 TYPEFLAGS=0xC0U, FILE=0x40U, DIR=0x80U,
57 COMPAREFLAGS=0x7000U, NOCMP=0x0000U, DIFF=0x1000U, SAME=0x2000U, CMPERR=0x3000U, CMPABORT=0x4000U,
58 COMPAREFLAGS3WAY=0x18000U, DIFFALL=0x0000U, DIFF1STONLY=0x8000U, DIFF2NDONLY=0x10000U, DIFF3RDONLY=0x18000U,
59 FILTERFLAGS=0x20000U, INCLUDED=0x00000U, SKIPPED=0x20000U,
60 SCANFLAGS=0x100000U, NEEDSCAN=0x100000U,
61 THREEWAYFLAGS=0x200000U, THREEWAY=0x200000U,
62 SIDEFLAGS=0x70000000U, FIRST=0x10000000U, SECOND=0x20000000U, THIRD=0x40000000U, BOTH=0x30000000U, ALL=0x70000000U,
67 explicit DIFFCODE(unsigned diffcode = 0) : diffcode(diffcode) { }
70 /// Worker function, to check one area (mask) of code for a particular value (result)
71 static bool Check(unsigned code, unsigned mask, unsigned result) { return ((code & mask) == result); }
72 /// Convenience function to check the part of the code for comparison results
73 static bool CheckCompare(unsigned code, unsigned result) { return Check(code, DIFFCODE::COMPAREFLAGS, result); }
74 /// Convenience function to check the part of the code for filter status
75 static bool CheckFilter(unsigned code, unsigned result) { return Check(code, DIFFCODE::FILTERFLAGS, result); }
76 /// Convenience function to check the part of the code for side status (eg, left-only)
77 static bool CheckSide(unsigned code, unsigned result) { return Check(code, DIFFCODE::SIDEFLAGS, result); }
79 /// Worker function to set the area indicated by mask to specified result
80 void Set(int mask, unsigned result) { diffcode &= (~mask); diffcode |= result; }
81 /// Convenience function to set the side status, eg, SetSide(DIFFCODE::LEFT)
82 void SetSide(unsigned result) { Set(DIFFCODE::SIDEFLAGS, result); }
86 bool isDirectory() const { return Check(diffcode, DIFFCODE::TYPEFLAGS, DIFFCODE::DIR); }
88 bool isSideFirstOnly() const { return CheckSide(diffcode, DIFFCODE::FIRST); }
89 bool isSideSecondOnly() const { return CheckSide(diffcode, DIFFCODE::SECOND); }
90 bool isSideThirdOnly() const { return CheckSide(diffcode, DIFFCODE::THIRD); }
91 bool isMissingFirstOnly() const { return CheckSide(diffcode, DIFFCODE::SECOND | DIFFCODE::THIRD); }
92 bool isMissingSecondOnly() const { return CheckSide(diffcode, DIFFCODE::FIRST | DIFFCODE::THIRD); }
93 bool isMissingThirdOnly() const { return CheckSide(diffcode, DIFFCODE::FIRST | DIFFCODE::SECOND); }
94 bool isSideBoth() const { return CheckSide(diffcode, DIFFCODE::BOTH); }
95 bool isSideAll() const { return CheckSide(diffcode, DIFFCODE::ALL); }
96 void setSideNone() { SetSide(0); }
97 void setSideFlag(int nIndex)
101 case 0: SetSide(diffcode | DIFFCODE::FIRST); return;
102 case 1: SetSide(diffcode | DIFFCODE::SECOND); return;
103 case 2: SetSide(diffcode | DIFFCODE::THIRD); return;
106 void unsetSideFlag(int nIndex)
110 case 0: SetSide(diffcode & ~DIFFCODE::FIRST); return;
111 case 1: SetSide(diffcode & ~DIFFCODE::SECOND); return;
112 case 2: SetSide(diffcode & ~DIFFCODE::THIRD); return;
115 bool isSideOnly(int nIndex) const
119 case 0: return isSideFirstOnly();
120 case 1: return isSideSecondOnly();
121 case 2: return isSideThirdOnly();
125 bool existsFirst() const { return !!(diffcode & DIFFCODE::FIRST); }
126 bool existsSecond() const { return !!(diffcode & DIFFCODE::SECOND); }
127 bool existsThird() const { return !!(diffcode & DIFFCODE::THIRD); }
128 bool exists(int nIndex) const
132 case 0: return !!(diffcode & DIFFCODE::FIRST);
133 case 1: return !!(diffcode & DIFFCODE::SECOND);
134 case 2: return !!(diffcode & DIFFCODE::THIRD);
138 bool existAll() const
140 return ((diffcode & DIFFCODE::THREEWAY) ? DIFFCODE::ALL : DIFFCODE::BOTH) == (diffcode & DIFFCODE::ALL);
144 bool isResultNone() const { return CheckCompare(diffcode, 0); }
145 bool isResultSame() const { return CheckCompare(diffcode, DIFFCODE::SAME); }
146 bool isResultDiff() const { return (CheckCompare(diffcode, DIFFCODE::DIFF) && !isResultFiltered() &&
148 static bool isResultError(unsigned code) { return CheckCompare(code, DIFFCODE::CMPERR); }
149 bool isResultError() const { return isResultError(diffcode); }
150 static bool isResultAbort(unsigned code) { return CheckCompare(code, DIFFCODE::CMPABORT); }
151 bool isResultAbort() const { return isResultAbort(diffcode); }
153 bool isResultFiltered() const { return CheckFilter(diffcode, DIFFCODE::SKIPPED); }
155 bool isText() const { return Check(diffcode, DIFFCODE::TEXTFLAGS, DIFFCODE::TEXT); }
156 bool isBin() const { return (diffcode & DIFFCODE::BIN) != 0; }
157 bool isImage() const { return (diffcode & DIFFCODE::IMAGE) != 0; }
159 bool isScanNeeded() const { return ((diffcode & DIFFCODE::SCANFLAGS) == DIFFCODE::NEEDSCAN); }
161 void swap(int idx1, int idx2);
166 // No valid values are 0
168 VISIBILITY = 0x3U, VISIBLE = 0x1U, HIDDEN = 0x2U, EXPANDED = 0x4U
172 * @brief information about one file/folder item.
173 * This class holds information about one compared "item" in the folder comparison tree.
174 * The item can be a file item or folder item. The item can have data from
175 * both (or all three) compare sides (file/folder exists in all sides) or from
176 * fewer sides (file/folder is missing in one/two sides).
178 * The basic structure consists of linkage to the `parent` folder and possible "sibling"
179 * file/folder items that share the identical `parent` folder item. Folder items also
180 * have a `children` link indicating the head of another DIFFITEM list of contained
183 * This class is for backend differences processing, representing physical
184 * files and folders. This class is not for GUI data like selection or
185 * visibility statuses. So do not include any GUI-dependent data here.
188 // See http://web.eecs.utk.edu/~bvz/teaching/cs140Fa09/notes/Dllists for a discussion of
189 // "doubly linked lists". The "sibling" linkage used here is basically a doubly linked
190 // list (similar to what is described in that article), with the notes ...
191 // A. Items may be deleted anywhere within the tree (e.g. via "Refresh Selected (ctrl-F5)").
192 // This justifies the usage of a double link list.
193 // B. Lists are only traversed in the forward direction (using `Flink`). Using `nullptr`
194 // to stop this traversal is both obvious and convenient.
195 // C. Items are only inserted at the end, so storing the end's link (for a future insertion)
196 // into the head's `Blink` serves the function of the "sentinel node" without consuming
198 // D. A "sentinel" structure is not necessary because ...
199 // D1. The head of a sibling list can always be found directly from the parent folder
200 // item by `this->children`. From an arbitrary sibling item, the head is at
201 // `this->parent->children` .
202 // D2. Similarily, the end of a sibling list is found (from the parent folder item) at
203 // `this->children->Blink` or (from a sibling item) at `this->parent->children->Blink`.
204 // E. The "root" item for a DIFFITEM tree is `m_pRoot`, declared in and managed by the
205 // DiffItemList class. There is one "root" for each active folder comparison
206 // (i.e. each folder comparison window in the GUI). The "root" item exists to anchor the
207 // tree, as well as to guarantee that the `parent` and `children` linkage is correct at the
208 // top folder comparison level.
211 // Sometimes a picture is worth many words...
215 // | parent -------|---> P0
217 // | children -----|->\
218 // |---------------| |
220 // | /--------------------------------------------------------------------------->\
222 // | | |---P1.C0-----| |---P1.S1-----| |---P1.S2-----| |
223 // | | | data values | | data values | | data values | |
224 // | | |-------------| |-------------| |-------------| |
225 // | \<--|--Blink |<----------|--Blink |<----------|--Blink |<-----/
226 // | | Flink -----|---------->| Flink -----|---------->| Flink -----|--------> `nullptr`
227 // \----->| children---|->\ | children---|->\ | children---|->\
228 // P1<------------|--parent | | P1<--|--parent | | P1<--|--parent | |
229 // |-------------| | |-------------| | |-------------| |
231 // \--> P1.C0.C0 \--> P1.S1.C0 \--> P1.S2.C0
232 // \--> `nullptr` \--> `nullptr` \--> `nullptr`
237 //**** DIFFITEM data values
240 DiffFileInfo diffFileInfo[3]; /**< Fileinfo for left/middle/right file. */
241 int nsdiffs; /**< Amount of non-ignored differences */
242 int nidiffs; /**< Amount of ignored differences */
243 // Note: Keep `diffFileInfo[]`, `nsdiffs` and `nidiffs`
244 // near front of class for small offsets.
245 // (see `DirColInfo` arrays in `DirViewColItems.cpp`) *>
246 DIFFCODE diffcode; /**< Compare result */
247 unsigned customFlags; /**< ViewCustomFlags flags */
249 String getFilepath(int nIndex, const String &sRoot) const;
250 String getItemRelativePath() const;
252 void Swap(int idx1, int idx2);
253 void ClearAllAdditionalProperties();
255 //**** Child, Parent, Sibling linkage
256 private: // Don't allow direct external manipulation of link values
257 DIFFITEM *parent; /**< Parent of current item */
258 DIFFITEM *children; /**< Link to first child of this item */
259 DIFFITEM *Flink; /**< Forward "sibling" link. The forward linkage ends with
260 a `nullptr` in the final (newest) item. */
261 DIFFITEM *Blink; /**< Backward "sibling" link. The backward linkage is circular,
262 with the first (oldest) item (pointed to by `this->parent->children`)
263 pointing to the last (newest) item. This is for easy insertion. */
264 void AppendSibling(DIFFITEM *p);
267 void DelinkFromSiblings();
268 void AddChildToParent(DIFFITEM *p);
269 void RemoveChildren();
270 int GetDepth() const;
271 bool IsAncestor(const DIFFITEM *pdi) const;
272 std::vector<const DIFFITEM*> GetAncestors() const;
273 inline DIFFITEM *GetFwdSiblingLink() const { return Flink; }
274 inline DIFFITEM *GetFirstChild() const { return children; }
275 inline DIFFITEM *GetParentLink() const { return parent; }
276 #ifdef SHOW_DIFFITEM_DEBUG_INFO
277 inline DIFFITEM *GetBackwardSiblingLink() const { return Blink; }
278 #endif // SHOW_DIFFITEM_DEBUG_INFO
280 /** @brief Return whether the current DIFFITEM has children */
281 inline bool DIFFITEM::HasChildren() const { return (children != nullptr); }
282 /** @brief Return whether the current DIFFITEM has children */
283 inline bool DIFFITEM::HasParent() const { return (parent != nullptr); }
285 //**** The `emptyitem` and its access procedures
287 static DIFFITEM emptyitem; /**< Singleton to represent a DIFFITEM that doesn't have any data */
290 static DIFFITEM *GetEmptyItem();
291 inline bool isEmpty() const { return this == GetEmptyItem(); /* to invoke "emptiness" checking */ }
295 DIFFITEM() : parent(nullptr), children(nullptr), Flink(nullptr), Blink(nullptr),
296 nidiffs(-1), nsdiffs(-1), customFlags(ViewCustomFlags::INVALID_CODE)
297 // `DiffFileInfo` and `DIFFCODE` have their own initializers.