OSDN Git Service

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