OSDN Git Service

Implement issue #1413: "Move" needs options "Left to Right" or "Right… (#1732)
[winmerge-jp/winmerge-jp.git] / Src / DirActions.h
1 #pragma once
2
3 #include "UnicodeString.h"
4 #include "DiffContext.h"
5 #include "FileActionScript.h"
6 #include "paths.h"
7 #include "IntToIntMap.h"
8 #include <algorithm>
9 #include "FileTransform.h"
10
11 class CDiffContext;
12 class PathContext;
13 class PluginManager;
14 class FileActionScript;
15 class CTempPathContext;
16
17 /**
18  * @brief Folder compare icon indexes.
19  * This `enum` defines indexes for the imagelist used for folder/file compare icons.
20  * Note that this enum must be in synch with code in CDirView::OnInitialUpdate() and
21  * GetColImage(). Also remember that icons are in resource file...
22  */
23 enum
24 {
25         DIFFIMG_LUNIQUE,
26         DIFFIMG_MUNIQUE,
27         DIFFIMG_RUNIQUE,
28         DIFFIMG_LMISSING,
29         DIFFIMG_MMISSING,
30         DIFFIMG_RMISSING,
31         DIFFIMG_DIFF,
32         DIFFIMG_SAME,
33         DIFFIMG_FILE,
34         DIFFIMG_BINSAME,
35         DIFFIMG_BINDIFF,
36         DIFFIMG_LDIRUNIQUE,
37         DIFFIMG_MDIRUNIQUE,
38         DIFFIMG_RDIRUNIQUE,
39         DIFFIMG_LDIRMISSING,
40         DIFFIMG_MDIRMISSING,
41         DIFFIMG_RDIRMISSING,
42         DIFFIMG_SKIP,
43         DIFFIMG_DIRSKIP,
44         DIFFIMG_DIRDIFF,
45         DIFFIMG_DIRSAME,
46         DIFFIMG_DIR,
47         DIFFIMG_ERROR,
48         DIFFIMG_DIRUP,
49         DIFFIMG_DIRUP_DISABLE,
50         DIFFIMG_ABORT,
51         DIFFIMG_TEXTDIFF,
52         DIFFIMG_TEXTSAME,
53         DIFFIMG_IMAGEDIFF,
54         DIFFIMG_IMAGESAME,
55 };
56
57 typedef enum {
58         SIDE_LEFT = 1,
59         SIDE_MIDDLE,
60         SIDE_RIGHT
61 } SIDE_TYPE;
62
63 typedef enum {
64         SELECTIONTYPE_NORMAL,
65         SELECTIONTYPE_LEFT1LEFT2,
66         SELECTIONTYPE_RIGHT1RIGHT2,
67         SELECTIONTYPE_LEFT1RIGHT2,
68         SELECTIONTYPE_LEFT2RIGHT1
69 } SELECTIONTYPE;
70
71 typedef enum {
72         UPDATEITEM_NONE,
73         UPDATEITEM_UPDATE,
74         UPDATEITEM_REMOVE
75 } UPDATEITEM_TYPE;
76
77 struct AllowUpwardDirectory
78 {
79         enum ReturnCode
80         {
81                 Never,
82                 No,
83                 ParentIsRegularPath,
84                 ParentIsTempPath
85         };
86 };
87
88
89 struct DirViewFilterSettings
90 {
91         template<class GetOptionBool>
92         explicit DirViewFilterSettings(GetOptionBool getoptbool)
93         {
94                 show_skipped = getoptbool(OPT_SHOW_SKIPPED);
95                 show_unique_left = getoptbool(OPT_SHOW_UNIQUE_LEFT);
96                 show_unique_middle = getoptbool(OPT_SHOW_UNIQUE_MIDDLE);
97                 show_unique_right = getoptbool(OPT_SHOW_UNIQUE_RIGHT);
98                 show_binaries = getoptbool(OPT_SHOW_BINARIES);
99                 show_identical = getoptbool(OPT_SHOW_IDENTICAL);
100                 show_different = getoptbool(OPT_SHOW_DIFFERENT);
101                 show_different_left_only = getoptbool(OPT_SHOW_DIFFERENT_LEFT_ONLY);
102                 show_different_middle_only = getoptbool(OPT_SHOW_DIFFERENT_MIDDLE_ONLY);
103                 show_different_right_only = getoptbool(OPT_SHOW_DIFFERENT_RIGHT_ONLY);
104                 show_missing_left_only = getoptbool(OPT_SHOW_MISSING_LEFT_ONLY);
105                 show_missing_middle_only = getoptbool(OPT_SHOW_MISSING_MIDDLE_ONLY);
106                 show_missing_right_only = getoptbool(OPT_SHOW_MISSING_RIGHT_ONLY);
107                 tree_mode = getoptbool(OPT_TREE_MODE);
108         };
109         bool show_skipped;
110         bool show_unique_left;
111         bool show_unique_middle;
112         bool show_unique_right;
113         bool show_binaries;
114         bool show_identical;
115         bool show_different;
116         bool show_different_left_only;
117         bool show_different_middle_only;
118         bool show_different_right_only;
119         bool show_missing_left_only;
120         bool show_missing_middle_only;
121         bool show_missing_right_only;
122         bool tree_mode;
123 };
124
125 typedef std::map<String, bool> DirViewTreeState;
126
127 String NumToStr(int n);
128 String FormatFilesAffectedString(int nFilesAffected, int nFilesTotal);
129 String FormatMenuItemString(SIDE_TYPE src, int count, int total);
130 String FormatMenuItemString(SIDE_TYPE src, SIDE_TYPE dst, int count, int total);
131 String FormatMenuItemStringAll(int nDirs, int count, int total);
132 String FormatMenuItemString(const String& fmt1, const String& fmt2, int count, int total);
133 String FormatMenuItemStringTo(SIDE_TYPE src, int count, int total);
134 String FormatMenuItemStringAllTo(int nDirs, int count, int total);
135 String FormatMenuItemStringDifferencesTo(int count, int total);
136
137 void ConfirmActionList(const CDiffContext& ctxt, const FileActionScript & actionList);
138 UPDATEITEM_TYPE UpdateDiffAfterOperation(const FileActionItem & act, CDiffContext& ctxt, DIFFITEM &di);
139
140 DIFFITEM *FindItemFromPaths(const CDiffContext& ctxt, const PathContext& paths);
141
142 bool IsItemCopyable(const DIFFITEM &di, int index);
143 bool IsItemMovable(const DIFFITEM &di, int index);
144 bool IsItemDeletable(const DIFFITEM &di, int index);
145 bool IsItemDeletableOnBoth(const CDiffContext& ctxt, const DIFFITEM &di);
146 bool AreItemsOpenable(const CDiffContext& ctxt, SELECTIONTYPE selectionType, const DIFFITEM &di1, const DIFFITEM &di2, bool openableForDir = true);
147 bool AreItemsOpenable(const CDiffContext& ctxt, const DIFFITEM &di1, const DIFFITEM &di2, const DIFFITEM &di3, bool openableForDir = true);
148 bool IsItemOpenableOn(const DIFFITEM &di, int index);
149 bool IsItemOpenableOnWith(const DIFFITEM &di, int index);
150 bool IsItemCopyableToOn(const DIFFITEM &di, int index);
151 bool IsItemNavigableDiff(const CDiffContext& ctxt, const DIFFITEM &di);
152 bool IsItemExistAll(const CDiffContext& ctxt, const DIFFITEM &di);
153 bool IsShowable(const CDiffContext& ctxt, const DIFFITEM &di, const DirViewFilterSettings& filter);
154
155 bool GetOpenOneItem(const CDiffContext& ctxt, DIFFITEM *pos1, const DIFFITEM *pdi[3],
156                 PathContext &paths, int & sel1, bool & isDir, int nPane[3], FileTextEncoding encoding[3], String& errmsg, bool openableForDir = true);
157 bool GetOpenTwoItems(const CDiffContext& ctxt, SELECTIONTYPE selectionType, DIFFITEM *pos1, DIFFITEM *pos2, const DIFFITEM *pdi[3],
158                 PathContext &paths, int & sel1, int & sel2, bool & isDir, int nPane[3], FileTextEncoding encoding[3], String& errmsg, bool openableForDir = true);
159 bool GetOpenThreeItems(const CDiffContext& ctxt, DIFFITEM *pos1, DIFFITEM *pos2, DIFFITEM *pos3, const DIFFITEM *pdi[3],
160                 PathContext &paths, int & sel1, int & sel2, int & sel3, bool & isDir, int nPane[3], FileTextEncoding encoding[3], String& errmsg, bool openableForDir = true);
161
162 void GetItemFileNames(const CDiffContext& ctxt, const DIFFITEM& di, String& strLeft, String& strRight);
163 PathContext GetItemFileNames(const CDiffContext& ctxt, const DIFFITEM& di);
164 String GetItemFileName(const CDiffContext& ctx, const DIFFITEM &di, int index);
165 int GetColImage(const DIFFITEM &di);
166
167 void SetDiffStatus(DIFFITEM& di, unsigned  diffcode, unsigned mask);
168 void SetDiffCompare(DIFFITEM& di, unsigned diffcode);
169 void CopyDiffSideAndProperties(DIFFITEM& di, int src, int dst);
170 void UnsetDiffSide(DIFFITEM& di, int index);
171 void UpdateStatusFromDisk(CDiffContext& ctxt, DIFFITEM& di, int index);
172 int UpdateCompareFlagsAfterSync(DIFFITEM& di, bool bRecursive);
173 void UpdatePaths(int nDirs, DIFFITEM& di);
174 void SetDiffCounts(DIFFITEM& di, unsigned diffs, unsigned ignored);
175 void SetItemViewFlag(DIFFITEM& di, unsigned flag, unsigned mask);
176 void SetItemViewFlag(CDiffContext& ctxt, unsigned flag, unsigned mask);
177 void MarkForRescan(DIFFITEM& di);
178
179 bool RenameOnSameDir(const String& szOldFileName, const String& szNewFileName);
180
181 void ExpandSubdirs(CDiffContext& ctxt, DIFFITEM& dip);
182 void ExpandAllSubdirs(CDiffContext &ctxt);
183 void CollapseAllSubdirs(CDiffContext &ctxt);
184 DirViewTreeState *SaveTreeState(const CDiffContext& ctxt);
185 void RestoreTreeState(CDiffContext &ctxt, DirViewTreeState *pTreeState);
186
187 AllowUpwardDirectory::ReturnCode
188 CheckAllowUpwardDirectory(const CDiffContext& ctxt, const CTempPathContext *pTempPathContext, PathContext &pathsParent);
189
190 inline int SideToIndex(const CDiffContext& ctxt, SIDE_TYPE stype)
191 {
192         switch (stype)
193         {
194         case SIDE_MIDDLE: return ctxt.GetCompareDirs() == 3 ? 1 : -1;
195         case SIDE_RIGHT: return ctxt.GetCompareDirs() - 1;
196         default: return 0;
197         }
198 }
199
200 struct ConfirmationNeededException
201 {
202         String m_caption;
203         String m_question;
204         String m_fromText;
205         String m_toText;
206         String m_fromPath;
207         String m_toPath;
208 };
209
210 struct ContentsChangedException
211 {
212         explicit ContentsChangedException(const String& failpath);
213         String m_msg;
214 };
215
216 struct FileOperationException
217 {
218         explicit FileOperationException(const String& msg);
219         String m_msg;
220 };
221
222 struct DirActions
223 {
224         typedef bool (DirActions::*method_type2)(const DIFFITEM& di) const;
225         typedef FileActionScript *(DirActions::*method_type)(FileActionScript *, const std::pair<int, const DIFFITEM *>& it) const;
226
227         DirActions(const CDiffContext& ctxt, const bool RO[], method_type func = nullptr, method_type2 func2 = nullptr) : 
228                 m_ctxt(ctxt), m_RO(RO), m_cur_method(func), m_cur_method2(func2) {}
229
230         template <SIDE_TYPE src, SIDE_TYPE dst>
231         bool IsItemCopyableOnTo(const DIFFITEM& di) const
232         {
233                 return (di.diffcode.diffcode != 0 && !m_RO[SideToIndex(m_ctxt, dst)] && ::IsItemCopyable(di, SideToIndex(m_ctxt, src)));
234         }
235
236         template <SIDE_TYPE src>
237         bool IsItemCopyableToOn(const DIFFITEM& di) const
238         {
239                 return (di.diffcode.diffcode != 0 && ::IsItemCopyableToOn(di, SideToIndex(m_ctxt, src)));
240         }
241
242         bool IsItemCopyableBothToOn(const DIFFITEM& di) const
243         {
244                 if (di.diffcode.diffcode != 0)
245                 {
246                         int i;
247                         for (i = 0; i < m_ctxt.GetCompareDirs(); ++i)
248                         {
249                                 if (!::IsItemCopyableToOn(di, i))
250                                         break;
251                         }
252                         return (i == m_ctxt.GetCompareDirs());
253                 }
254                 return false;
255         }
256
257         /**
258          * @brief Return whether the specified diff item is renamable.
259          * @param [in] di Diff item to check
260          * @return true if the specified diff item is renamable.
261          */
262         bool IsItemRenamable(const DIFFITEM& di) const
263         {
264                 int nDirs = m_ctxt.GetCompareDirs();
265                 for (int i = 0; i < nDirs; i++)
266                         if (di.diffcode.exists(i) && m_RO[i])
267                                 return false;
268                 return true;
269         }
270
271         template <SIDE_TYPE src, SIDE_TYPE dst>
272         bool IsItemMovableOnTo(const DIFFITEM& di) const
273         {
274                 const int idx = SideToIndex(m_ctxt, src);
275                 return (di.diffcode.diffcode != 0 && !m_RO[idx] && !m_RO[SideToIndex(m_ctxt, dst)] && ::IsItemMovable(di, idx));
276         }
277
278         template <SIDE_TYPE src>
279         bool IsItemMovableToOn(const DIFFITEM& di) const
280         {
281                 const int idx = SideToIndex(m_ctxt, src);
282                 return (di.diffcode.diffcode != 0 && !m_RO[idx] && IsItemDeletable(di, idx) && ::IsItemCopyableToOn(di, idx));
283         }
284
285         template <SIDE_TYPE src>
286         bool IsItemDeletableOn(const DIFFITEM& di) const
287         { 
288                 const int idx = SideToIndex(m_ctxt, src);
289                 return (di.diffcode.diffcode != 0 && !m_RO[idx] && IsItemDeletable(di, idx));
290         }
291
292         bool IsItemDeletableOnBoth(const DIFFITEM& di) const
293         {
294                 if (di.diffcode.diffcode != 0)
295                 {
296                         int i;
297                         for (i = 0; i < m_ctxt.GetCompareDirs(); ++i)
298                         {
299                                 if (m_RO[i] || !IsItemDeletable(di, i))
300                                         break;
301                         }
302                         return (i == m_ctxt.GetCompareDirs());
303                 }
304                 return false;
305         }
306         bool IsItemDeletableOnEitherOrBoth(const DIFFITEM& di) const
307         {
308                 if (di.diffcode.diffcode != 0)
309                 {
310                         int i;
311                         for (i = 0; i < m_ctxt.GetCompareDirs(); ++i)
312                         {
313                                 if (!m_RO[i] && IsItemDeletable(di, i))
314                                         break;
315                         }
316                         return (i < m_ctxt.GetCompareDirs());
317                 }
318                 return false;
319         }
320
321         template <SIDE_TYPE src>
322         bool IsItemOpenableOn(const DIFFITEM& di) const
323         {
324                 return (di.diffcode.diffcode != 0 && ::IsItemOpenableOn(di, SideToIndex(m_ctxt, src)));
325         }
326
327         template <SIDE_TYPE src>
328         bool IsItemOpenableOnWith(const DIFFITEM& di) const
329         {
330                 return (di.diffcode.diffcode != 0 && ::IsItemOpenableOnWith(di, SideToIndex(m_ctxt, src)));
331         }
332
333         template <SIDE_TYPE src>
334         bool IsParentFolderOpenable(const DIFFITEM& di) const
335         {
336                 return (di.diffcode.diffcode != 0 && di.diffcode.exists(SideToIndex(m_ctxt, src)));
337         }
338
339         bool IsItemFile(const DIFFITEM& di) const
340         {
341                 return (di.diffcode.diffcode != 0 && !di.diffcode.isDirectory());
342         }
343
344         template <SIDE_TYPE src>
345         bool IsItemExist(const DIFFITEM& di) const
346         {
347                 return (di.diffcode.diffcode != 0 && di.diffcode.exists(SideToIndex(m_ctxt, src)));
348         }
349
350         template <SIDE_TYPE src>
351         bool IsItemEditableEncoding(const DIFFITEM& di) const
352         {
353                 const int index = SideToIndex(m_ctxt, src);
354                 return (di.diffcode.diffcode != 0 && di.diffcode.exists(index) && di.diffFileInfo[index].IsEditableEncoding());
355         }
356
357         bool IsItemNavigableDiff(const DIFFITEM& di) const
358         {
359                 return ::IsItemNavigableDiff(m_ctxt, di);
360         }
361
362         FileActionScript *CopyItem(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it, SIDE_TYPE src, SIDE_TYPE dst) const
363         {
364                 const DIFFITEM& di = *it.second;
365                 const int srcidx = SideToIndex(m_ctxt, src);
366                 const int dstidx = SideToIndex(m_ctxt, dst);
367                 if (di.diffcode.diffcode != 0 && !m_RO[dstidx] && IsItemCopyable(di, srcidx))
368                 {
369                         FileActionItem act;
370                         act.src  = GetItemFileName(m_ctxt, di, srcidx);
371                         act.dest = GetItemFileName(m_ctxt, di, dstidx);
372                         
373                         // We must check that paths still exists
374                         if (paths::DoesPathExist(act.src) == paths::DOES_NOT_EXIST)
375                                 throw ContentsChangedException(act.src);
376
377                         act.context = it.first;
378                         act.dirflag = di.diffcode.isDirectory();
379                         act.atype = FileAction::ACT_COPY;
380                         act.UIResult = FileActionItem::UI_SYNC;
381                         act.UIOrigin = srcidx;
382                         act.UIDestination = dstidx;
383                         pscript->AddActionItem(act);
384                 }
385                 return pscript;
386         }
387
388         template<SIDE_TYPE src, SIDE_TYPE to>
389         FileActionScript *Copy(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it) const
390         {
391                 return CopyItem(pscript, it, src, to);
392         }
393
394         FileActionScript *MoveItem(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it, SIDE_TYPE src, SIDE_TYPE dst) const
395         {
396                 const DIFFITEM& di = *it.second;
397                 const int srcidx = SideToIndex(m_ctxt, src);
398                 const int dstidx = SideToIndex(m_ctxt, dst);
399                 if (di.diffcode.diffcode != 0 && !m_RO[dstidx] && IsItemMovable(di, srcidx))
400                 {
401                         FileActionItem act;
402                         act.src  = GetItemFileName(m_ctxt, di, srcidx);
403                         act.dest = GetItemFileName(m_ctxt, di, dstidx);
404                         
405                         // We must check that paths still exists
406                         if (paths::DoesPathExist(act.src) == paths::DOES_NOT_EXIST)
407                                 throw ContentsChangedException(act.src);
408
409                         act.context = it.first;
410                         act.dirflag = di.diffcode.isDirectory();
411                         act.atype = FileAction::ACT_MOVE;
412                         act.UIResult = FileActionItem::UI_MOVE;
413                         act.UIOrigin = srcidx;
414                         act.UIDestination = dstidx;
415                         pscript->AddActionItem(act);
416                 }
417                 return pscript;
418         }
419
420         template<SIDE_TYPE src, SIDE_TYPE to>
421         FileActionScript *Move(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it) const
422         {
423                 return MoveItem(pscript, it, src, to);
424         }
425
426         FileActionScript *DeleteItem(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it, SIDE_TYPE src) const
427         {
428                 const DIFFITEM& di = *it.second;
429                 const int index = SideToIndex(m_ctxt, src);
430                 if (di.diffcode.diffcode != 0 && !m_RO[index] && IsItemDeletable(di, index))
431                 {
432                         FileActionItem act;
433                         act.src = GetItemFileName(m_ctxt, di, index);
434
435                         // We must check that path still exists
436                         if (paths::DoesPathExist(act.src) == paths::DOES_NOT_EXIST)
437                                 throw ContentsChangedException(act.src);
438
439                         act.context = it.first;
440                         act.dirflag = di.diffcode.isDirectory();
441                         act.atype = FileAction::ACT_DEL;
442                         act.UIOrigin = index;
443                         act.UIResult = FileActionItem::UI_DEL;
444                         pscript->AddActionItem(act);
445                 }
446                 return pscript;
447         }
448
449         template<SIDE_TYPE src>
450         FileActionScript *DeleteOn(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it) const
451         {
452                 return DeleteItem(pscript, it, src);
453         }
454
455         FileActionScript *DeleteOnBoth(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it) const
456         {
457                 const DIFFITEM& di = *it.second;
458
459                 if (di.diffcode.diffcode != 0 && IsItemDeletableOnBoth(di) && 
460                         (std::count(m_RO, m_RO + m_ctxt.GetCompareDirs(), true) == 0))
461                 {
462                         for (int i = 0; i < m_ctxt.GetCompareDirs(); ++i)
463                         {
464                                 FileActionItem act;
465                                 act.src = GetItemFileName(m_ctxt, di, i);
466                                 // We must first check that paths still exists
467                                 if (paths::DoesPathExist(act.src) == paths::DOES_NOT_EXIST)
468                                         throw ContentsChangedException(act.src);
469                                 act.context = it.first;
470                                 act.dirflag = di.diffcode.isDirectory();
471                                 act.atype = FileAction::ACT_DEL;
472                                 act.UIOrigin = i;
473                                 act.UIResult = FileActionItem::UI_DEL;
474                                 pscript->AddActionItem(act);
475                         }
476                 }
477                 return pscript;
478         }
479
480         FileActionScript *DeleteOnEitherOrBoth(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it) const
481         {
482                 const DIFFITEM& di = *it.second;
483                 if (di.diffcode.diffcode != 0)
484                 {
485                         for (int i = 0; i < m_ctxt.GetCompareDirs(); ++i)
486                         {
487                                 if (IsItemDeletable(di, i) && !m_RO[i])
488                                 {
489                                         FileActionItem act;
490                                         act.src = GetItemFileName(m_ctxt, di, i);
491                                         // We must first check that paths still exists
492                                         if (paths::DoesPathExist(act.src) == paths::DOES_NOT_EXIST)
493                                                 throw ContentsChangedException(act.src);
494                                         act.UIResult = FileActionItem::UI_DEL;
495                                         act.dirflag = di.diffcode.isDirectory();
496                                         act.context = it.first;
497                                         act.UIOrigin = i;
498                                         act.atype = FileAction::ACT_DEL;
499                                         pscript->AddActionItem(act);
500                                 }
501                         }
502                 }
503                 return pscript;
504         }
505
506         FileActionScript *CopyOrMoveItemTo(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it, FileAction::ACT_TYPE atype, SIDE_TYPE src) const
507         {
508                 const int index = SideToIndex(m_ctxt, src);
509                 const DIFFITEM& di = *it.second;
510
511                 if (di.diffcode.diffcode != 0 && di.diffcode.exists(index) &&
512                         (atype == FileAction::ACT_MOVE ? (!m_RO[index] && IsItemDeletable(di, index)) : true))
513                 {
514                         FileActionItem act;
515                         act.src = GetItemFileName(m_ctxt, di, index);
516                          
517                         // We must check that path still exists
518                         if (paths::DoesPathExist(act.src) == paths::DOES_NOT_EXIST)
519                                 throw ContentsChangedException(act.src);
520
521                         act.dest = paths::ConcatPath(pscript->m_destBase, di.diffFileInfo[index].GetFile());
522                         act.dirflag = di.diffcode.isDirectory();
523                         act.context = it.first;
524                         act.atype = atype;
525                         act.UIResult = (atype == FileAction::ACT_COPY) ? FileActionItem::UI_DONT_CARE : FileActionItem::UI_DEL;
526                         act.UIOrigin = index;
527                         pscript->AddActionItem(act);
528                 }
529                 return pscript;
530         }
531
532         template<SIDE_TYPE src>
533         FileActionScript *CopyTo(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it) const
534         {
535                 return CopyOrMoveItemTo(pscript, it, FileAction::ACT_COPY, src);
536         }
537
538         template<SIDE_TYPE src>
539         FileActionScript *MoveTo(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it) const
540         {
541                 return CopyOrMoveItemTo(pscript, it, FileAction::ACT_MOVE, src);
542         }
543
544         FileActionScript *operator()(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it) const
545         {
546                 return ((*this).*m_cur_method)(pscript, it);
547         }
548
549         bool operator()(const DIFFITEM &di) const
550         {
551                 return ((*this).*m_cur_method2)(di);
552         }
553
554         method_type m_cur_method;
555         method_type2 m_cur_method2;
556         const CDiffContext& m_ctxt;
557         const bool *m_RO;
558 };
559
560 struct Counts {
561         Counts() : count(0), total(0) {}
562         Counts(int c, int t): count(c), total(t) {}
563         int count;
564         int total;
565 };
566
567 template<class InputIterator, class Predicate>
568 Counts Count(const InputIterator& begin, const InputIterator& end, const Predicate& pred) 
569 {
570         int count = 0, total = 0;
571         for (InputIterator it = begin; it != end; ++it)
572         {
573                 if (pred(*it))
574                         ++count;
575                 ++total;
576         }
577         return Counts(count, total);
578 }
579
580 /**
581  * @brief Rename selected item on both left and right sides.
582  *
583  * @param szNewItemName [in] New item name.
584  *
585  * @return true if at least one file was renamed successfully.
586  */
587 template<class InputIterator>
588 bool DoItemRename(InputIterator& it, const CDiffContext& ctxt, const String& szNewItemName)
589 {
590         PathContext paths;
591         int nDirs = ctxt.GetCompareDirs();
592
593         assert(it != InputIterator());
594
595         // We must check that paths still exists
596         DIFFITEM &di = *it;
597         paths = ::GetItemFileNames(ctxt, di);
598         for (int index = 0; index < nDirs; index++)
599         {
600                 if (di.diffcode.exists(index) && paths::DoesPathExist(paths[index]) == paths::DOES_NOT_EXIST)
601                         throw ContentsChangedException(paths[index]);
602         }
603
604         bool bRename[3] = {false};
605         for (int index = 0; index < nDirs; index++)
606         {
607                 if (di.diffcode.exists(index))
608                         bRename[index] = RenameOnSameDir(paths[index], szNewItemName);
609         }
610
611         if (std::count(bRename, bRename + nDirs, true) == 0)
612                 return false;
613         
614         di.diffcode.setSideNone();
615         for (int index = 0; index < nDirs; index++)
616         {
617                 di.diffFileInfo[index].filename = szNewItemName;
618                 bool bSetSideFlag = bRename[index];
619                 if (!bSetSideFlag)
620                 {
621                         paths::PATH_EXISTENCE pathExist = paths::DoesPathExist(GetItemFileName(ctxt, di, index));
622                         bool bIsDirectory = di.diffcode.isDirectory();
623                         if (((pathExist == paths::IS_EXISTING_DIR) && bIsDirectory) || ((pathExist == paths::IS_EXISTING_FILE) && !bIsDirectory))
624                                 bSetSideFlag = true;
625                 }
626                 if (bSetSideFlag)
627                         di.diffcode.setSideFlag(index);
628         }
629         return true;
630 }
631
632 template<class InputIterator, class OutputIterator>
633 OutputIterator CopyPathnames(const InputIterator& begin, const InputIterator& end, OutputIterator result, SIDE_TYPE stype, const CDiffContext& ctxt)
634 {
635         const int index = SideToIndex(ctxt, stype);
636         for (InputIterator it = begin; it != end; ++it)
637         {
638                 const DIFFITEM& di = *it;
639                 if (di.diffcode.exists(index))
640                 {
641                         *result = GetItemFileName(ctxt, di, index);
642                         ++result;
643                 }
644         }
645         return result;
646 }
647
648 template<class InputIterator, class OutputIterator>
649 OutputIterator CopyBothPathnames(const InputIterator& begin, const InputIterator& end, OutputIterator result, const CDiffContext& ctxt)
650 {
651         for (InputIterator it = begin; it != end; ++it)
652         {
653                 const DIFFITEM& di = *it;
654                 for (int i = 0; i < ctxt.GetCompareDirs(); ++i)
655                 {
656                         if (di.diffcode.exists(i))
657                         {
658                                 *result = GetItemFileName(ctxt, di, i);
659                                 ++result;
660                         }
661                 }
662         }
663         return result;
664 }
665
666 template<class InputIterator, class OutputIterator>
667 OutputIterator CopyFilenames(const InputIterator& begin, const InputIterator& end, OutputIterator result)
668 {
669         for (InputIterator it = begin; it != end; ++it)
670         {
671                 const DIFFITEM& di = *it;
672                 if (!di.diffcode.isDirectory())
673                 {
674                         *result = di.diffFileInfo[0].filename;
675                         ++result;
676                 }
677         }
678         return result;
679 }
680
681 template<class InputIterator, class OutputIterator>
682 OutputIterator CopyPathnamesForDragAndDrop(const InputIterator& begin, const InputIterator& end, OutputIterator result, const CDiffContext& ctxt)
683 {
684         for (InputIterator it = begin; it != end; ++it)
685         {
686                 const DIFFITEM& di = *it;
687
688                 // check for special items (e.g not "..")
689                 if (di.diffcode.diffcode == 0)
690                         continue;
691
692                 for (int i = 0; i < ctxt.GetCompareDirs(); ++i)
693                 {
694                         if (di.diffcode.exists(i))
695                         {
696                                 *result = GetItemFileName(ctxt, di, i);
697                                 ++result;
698                         }
699                 }
700         }
701         return result;
702 }
703
704 template<class InputIterator, class BinaryFunction>
705 void ApplyFolderNameAndFileName(const InputIterator& begin, const InputIterator& end, SIDE_TYPE stype,
706         const CDiffContext& ctxt, BinaryFunction func)
707 {
708         int index = SideToIndex(ctxt, stype);
709         for (InputIterator it = begin; it != end; ++it)
710         {
711                 const DIFFITEM& di = *it;
712                 if (di.diffcode.diffcode == 0) // Invalid value, this must be special item
713                         continue;
714                 String filename = di.diffFileInfo[index].filename;
715                 String currentDir = di.getFilepath(index, ctxt.GetNormalizedPath(index));
716                 func(currentDir, filename);
717         }
718 }
719
720 /**
721  * @brief Apply specified setting for prediffing to all selected items
722  */
723 template<class InputIterator>
724 void ApplyPluginPipeline(const InputIterator& begin, const InputIterator& end, const CDiffContext& ctxt, bool unpacker, const String& pluginPipeline)
725 {
726         // Unlike other group actions, here we don't build an action list
727         // to execute; we just apply this change directly
728         if( !ctxt.m_bPluginsEnabled || ctxt.m_piPluginInfos == nullptr )
729                 return;
730         for (InputIterator it = begin; it != end; ++it)
731         {
732                 const DIFFITEM& di = *it;
733                 if (!di.diffcode.isDirectory())
734                 {
735                         PackingInfo * infoUnpacker = nullptr;
736                         PrediffingInfo * infoPrediffer = nullptr;
737                         String filteredFilenames = ctxt.GetFilteredFilenames(di);
738                         const_cast<CDiffContext&>(ctxt).FetchPluginInfos(filteredFilenames, &infoUnpacker, &infoPrediffer);
739                         if (unpacker)
740                                 infoUnpacker->SetPluginPipeline(pluginPipeline);
741                         else
742                                 infoPrediffer->SetPluginPipeline(pluginPipeline);
743                 }
744         }
745 }
746
747 /**
748  * @brief Updates just before displaying plugin context view in list
749  */
750 template<class InputIterator>
751 std::tuple<int, int, int> CountPluginNoneAutoOthers(const InputIterator& begin, const InputIterator& end, const CDiffContext& ctxt, bool unpacker)
752 {
753         int nNone = 0;
754         int nAuto = 0;
755         int nOthers = 0;
756         if( !ctxt.m_bPluginsEnabled || ctxt.m_piPluginInfos == nullptr ) 
757                 return std::make_tuple(nNone, nAuto, nOthers);
758
759         for (InputIterator it = begin; it != end; ++it)
760         {
761                 const DIFFITEM& di = *it;
762                 if (di.diffcode.diffcode == 0) // Invalid value, this must be special item
763                         continue;
764
765                 // note the prediffer flag for 'files present on both sides and not skipped'
766                 if (!di.diffcode.isDirectory() && !di.diffcode.isBin() && IsItemExistAll(ctxt, di)
767                         && !di.diffcode.isResultFiltered())
768                 {
769                         PackingInfo * infoUnpacker;
770                         PrediffingInfo * infoPrediffer;
771                         String filteredFilenames = ctxt.GetFilteredFilenames(di);
772                         const_cast<CDiffContext&>(ctxt).FetchPluginInfos(filteredFilenames, &infoUnpacker, &infoPrediffer);
773                         String pluginPipeline = unpacker ? infoUnpacker->GetPluginPipeline() : infoPrediffer->GetPluginPipeline();
774                         if (pluginPipeline.empty())
775                                 nNone++;
776                         else if (pluginPipeline == _T("<Automatic>"))
777                                 nAuto++;
778                         else
779                                 nOthers++;
780                 }
781         }
782         return std::make_tuple(nNone, nAuto, nOthers);
783 }
784
785 template<class InputIterator>
786 IntToIntMap CountCodepages(const InputIterator& begin, const InputIterator& end, const CDiffContext& ctxt)
787 {
788         IntToIntMap map;
789         for (InputIterator it = begin; it != end; ++it)
790         {
791                 const DIFFITEM& di = *it;
792                 for (int i = 0; i < ctxt.GetCompareDirs(); ++i)
793                 {
794                         if (di.diffcode.diffcode != 0 && di.diffcode.exists(i))
795                                 map.Increment(di.diffFileInfo[i].encoding.m_codepage);
796                 }
797         }
798         return map;
799 }
800
801 template<class InputIterator>
802 void ApplyCodepage(const InputIterator& begin, const InputIterator& end, CDiffContext& ctxt, const bool affect[3], int nCodepage)
803 {
804         for (InputIterator it = begin; it != end; ++it)
805         {
806                 DIFFITEM& di = *it;
807                 if (di.diffcode.diffcode == 0) // Invalid value, this must be special item
808                         continue;
809                 if (di.diffcode.isDirectory())
810                         continue;
811
812                 for (int i = 0; i < ctxt.GetCompareDirs(); ++i)
813                 {
814                         // Does it exist on left? (ie, right or both)
815                         if (affect[i] && di.diffcode.exists(i) && di.diffFileInfo[i].IsEditableEncoding())
816                         {
817                                 di.diffFileInfo[i].encoding.SetCodepage(nCodepage);
818                         }
819                 }
820         }
821 }
822
823 /// get file name on specified side for first selected item
824 template<class InputIterator>
825 String GetSelectedFileName(InputIterator& it, SIDE_TYPE stype, const CDiffContext& ctxt)
826 {
827         if (it == InputIterator())
828                 return _T("");
829         return GetItemFileName(ctxt, *it, SideToIndex(ctxt, stype));
830 }