2 * @file DirViewColItems.cpp
4 * @brief Code for individual columns in the DirView
6 * @date Created: 2003-08-19
10 #include "DirViewColItems.h"
11 #include <Poco/Timestamp.h>
13 #include "UnicodeString.h"
15 #include "DiffContext.h"
19 #include "FileTransform.h"
20 #include "PropertySystem.h"
23 using Poco::Timestamp;
29 const char *COLHDR_FILENAME = N_("Filename");
30 const char *COLHDR_DIR = NC_("DirView|ColumnHeader", "Folder");
31 const char *COLHDR_RESULT = N_("Comparison result");
32 const char *COLHDR_LTIMEM = N_("Left Date");
33 const char *COLHDR_RTIMEM = N_("Right Date");
34 const char *COLHDR_MTIMEM = N_("Middle Date");
35 const char *COLHDR_EXTENSION = N_("Extension");
36 const char *COLHDR_LSIZE = N_("Left Size");
37 const char *COLHDR_RSIZE = N_("Right Size");
38 const char *COLHDR_MSIZE = N_("Middle Size");
39 const char *COLHDR_RSIZE_SHORT = N_("Right Size (Short)");
40 const char *COLHDR_LSIZE_SHORT = N_("Left Size (Short)");
41 const char *COLHDR_MSIZE_SHORT = N_("Middle Size (Short)");
42 const char *COLHDR_LTIMEC = N_("Left Creation Time");
43 const char *COLHDR_RTIMEC = N_("Right Creation Time");
44 const char *COLHDR_MTIMEC = N_("Middle Creation Time");
45 const char *COLHDR_NEWER = N_("Newer File");
46 const char *COLHDR_LVERSION = N_("Left File Version");
47 const char *COLHDR_RVERSION = N_("Right File Version");
48 const char *COLHDR_MVERSION = N_("Middle File Version");
49 const char *COLHDR_RESULT_ABBR = N_("Short Result");
50 const char *COLHDR_LATTRIBUTES = N_("Left Attributes");
51 const char *COLHDR_RATTRIBUTES = N_("Right Attributes");
52 const char *COLHDR_MATTRIBUTES = N_("Middle Attributes");
53 const char *COLHDR_LEOL_TYPE = N_("Left EOL");
54 const char *COLHDR_MEOL_TYPE = N_("Middle EOL");
55 const char *COLHDR_REOL_TYPE = N_("Right EOL");
56 const char *COLHDR_LENCODING = N_("Left Encoding");
57 const char *COLHDR_RENCODING = N_("Right Encoding");
58 const char *COLHDR_MENCODING = N_("Middle Encoding");
59 const char *COLHDR_NIDIFFS = N_("Ignored Diff");
60 const char *COLHDR_NSDIFFS = N_("Differences");
61 const char *COLHDR_BINARY = NC_("DirView|ColumnHeader", "Binary");
62 const char *COLHDR_UNPACKER = N_("Unpacker");
63 const char *COLHDR_PREDIFFER = N_("Prediffer");
64 #ifdef SHOW_DIFFITEM_DEBUG_INFO
65 const char *COLHDR_DEBUG_DIFFCODE = N_("[Debug]diffcode");
66 const char *COLHDR_DEBUG_CUSTOMFLAGS = N_("[Debug]customFlags");
67 const char *COLHDR_DEBUG_THIS = N_("[Debug]this");
68 const char *COLHDR_DEBUG_PARENT = N_("[Debug]parent");
69 const char *COLHDR_DEBUG_CHILDREN = N_("[Debug]children");
70 const char *COLHDR_DEBUG_FLINK = N_("[Debug]Flink");
71 const char *COLHDR_DEBUG_BLINK = N_("[Debug]Blink");
72 #endif // SHOW_DIFFITEM_DEBUG_INFO
74 const char *COLDESC_FILENAME = N_("Filename or folder name.");
75 const char *COLDESC_DIR = N_("Subfolder name when subfolders are included.");
76 const char *COLDESC_RESULT = N_("Comparison result, long form.");
77 const char *COLDESC_LTIMEM = N_("Left side modification date.");
78 const char *COLDESC_RTIMEM = N_("Right side modification date.");
79 const char *COLDESC_MTIMEM = N_("Middle side modification date.");
80 const char *COLDESC_EXTENSION = N_("File's extension.");
81 const char *COLDESC_LSIZE = N_("Left file size in bytes.");
82 const char *COLDESC_RSIZE = N_("Right file size in bytes.");
83 const char *COLDESC_MSIZE = N_("Middle file size in bytes.");
84 const char *COLDESC_LSIZE_SHORT = N_("Left file size abbreviated.");
85 const char *COLDESC_RSIZE_SHORT = N_("Right file size abbreviated.");
86 const char *COLDESC_MSIZE_SHORT = N_("Middle file size abbreviated.");
87 const char *COLDESC_LTIMEC = N_("Left side creation time.");
88 const char *COLDESC_RTIMEC = N_("Right side creation time.");
89 const char *COLDESC_MTIMEC = N_("Middle side creation time.");
90 const char *COLDESC_NEWER = N_("Tells which side has newer modification date.");
91 const char *COLDESC_LVERSION = N_("Left side file version, only for some file types.");
92 const char *COLDESC_RVERSION = N_("Right side file version, only for some file types.");
93 const char *COLDESC_MVERSION = N_("Middle side file version, only for some file types.");
94 const char *COLDESC_RESULT_ABBR = N_("Short comparison result.");
95 const char *COLDESC_LATTRIBUTES = N_("Left side attributes.");
96 const char *COLDESC_RATTRIBUTES = N_("Right side attributes.");
97 const char *COLDESC_MATTRIBUTES = N_("Middle side attributes.");
98 const char *COLDESC_LEOL_TYPE = N_("Left side file EOL type.");
99 const char *COLDESC_REOL_TYPE = N_("Right side file EOL type.");
100 const char *COLDESC_MEOL_TYPE = N_("Middle side file EOL type.");
101 const char *COLDESC_LENCODING = N_("Left side encoding.");
102 const char *COLDESC_RENCODING = N_("Right side encoding.");
103 const char *COLDESC_MENCODING = N_("Middle side encoding.");
104 const char *COLDESC_NIDIFFS = N_("Number of ignored differences in file. These differences are ignored by WinMerge and cannot be merged.");
105 const char *COLDESC_NSDIFFS = N_("Number of differences in file. This number does not include ignored differences.");
106 const char *COLDESC_BINARY = N_("Shows an asterisk (*) if the file is binary.");
107 const char *COLDESC_UNPACKER = N_("Unpacker plugin name or pipeline.");
108 const char *COLDESC_PREDIFFER = N_("Prediffer plugin name or pipeline.");
109 #ifdef SHOW_DIFFITEM_DEBUG_INFO
110 const char *COLDESC_DEBUG_DIFFCODE = N_("Compare result");
111 const char *COLDESC_DEBUG_CUSTOMFLAGS = N_("ViewCustomFlags flags");
112 const char *COLDESC_DEBUG_THIS = N_("This item");
113 const char *COLDESC_DEBUG_PARENT = N_("Parent of current item");
114 const char *COLDESC_DEBUG_CHILDREN = N_("Link to first child of this item");
115 const char *COLDESC_DEBUG_FLINK = N_("Forward \"sibling\" link");
116 const char *COLDESC_DEBUG_BLINK = N_("Backward \"sibling\" link");
117 #endif // SHOW_DIFFITEM_DEBUG_INFO
121 * @brief Function to compare two int64_t's for a sort
123 static int cmp64(int64_t i1, int64_t i2)
125 if (i1==i2) return 0;
126 return i1>i2 ? 1 : -1;
130 * @brief Function to compare two uint64_t's for a sort
132 static int cmpu64(uint64_t i1, uint64_t i2)
134 if (i1==i2) return 0;
135 return i1>i2 ? 1 : -1;
138 * @brief Function to compare two diffcodes for a sort
139 * @todo How shall we order diff statuses?
141 static int cmpdiffcode(unsigned diffcode1, unsigned diffcode2)
143 bool same1 = (diffcode1 & DIFFCODE::COMPAREFLAGS) == DIFFCODE::SAME;
144 bool same2 = (diffcode2 & DIFFCODE::COMPAREFLAGS) == DIFFCODE::SAME;
149 return diffcode1 - diffcode2;
152 * @brief Formats a size as a short string.
154 * MakeShortSize(500) = "500b"
155 * MakeShortSize(1024) = "1Kb"
156 * MakeShortSize(12000) = "1.7Kb"
157 * MakeShortSize(200000) = "195Kb"
158 * @param [in] size File's size to convert.
159 * @return Size string with localized suffix.
160 * @note Localized suffix strings are read from resource.
161 * @todo Can't handle > terabyte filesizes.
163 static String MakeShortSize(int64_t size)
167 return strutils::format(_T("%d B"), static_cast<int>(size));
169 StrFormatByteSize64(size, buffer, static_cast<unsigned>(std::size(buffer)));
174 * @name Functions to format content of each type of column.
175 * These functions all receive two parameters, a pointer to CDiffContext.
176 * which contains general compare information. And a void pointer whose type
177 * depends on column to format. Function to call for each column, and
178 * parameter for the function are defined in static DirColInfo f_cols table.
182 * @brief Format Filename column data.
183 * @param [in] pCtxt Pointer to compare context.
184 * @param [in] p Pointer to DIFFITEM.
185 * @return String to show in the column.
188 static Type ColFileNameGet(const CDiffContext * pCtxt, const void *p, int) //sfilename
190 assert(pCtxt != nullptr && p != nullptr);
192 int nDirs = pCtxt->GetCompareDirs();
194 const DIFFITEM* pDiffItem = static_cast<const DIFFITEM*>(p);
195 const DiffFileInfo* pDiffFileInfo = pDiffItem->diffFileInfo;
198 for (int i = 0; i < nDirs; i++)
199 bExist[i] = (pDiffItem->diffcode.exists(i) && (!pDiffFileInfo[i].filename.get().empty()));
201 bool bIsSameName = true;
203 for (int i = 0; i < nDirs; i++)
209 else if (pDiffFileInfo[i].filename != pDiffFileInfo[index].filename)
221 return pDiffFileInfo[index].filename;
226 return static_cast<Type>(pDiffFileInfo[0].filename.get() + _T("|") + pDiffFileInfo[1].filename.get());
229 String none = _("<None>");
230 return static_cast<Type>((bExist[0] ? pDiffFileInfo[0].filename.get() : none) + _T("|")
231 + (bExist[1] ? pDiffFileInfo[1].filename.get() : none) + _T("|")
232 + (bExist[2] ? pDiffFileInfo[2].filename.get() : none));
238 * @brief Format Extension column data.
239 * @param [in] p Pointer to DIFFITEM.
240 * @return String to show in the column.
242 static String ColExtGet(const CDiffContext *, const void *p, int) //sfilename
244 const DIFFITEM &di = *static_cast<const DIFFITEM*>(p);
245 // We don't show extension for folder names
246 if (di.diffcode.isDirectory())
248 const String &r = di.diffFileInfo[0].filename;
249 String s = paths::FindExtension(r);
250 return s.c_str() + tc::tcsspn(s.c_str(), _T("."));
254 * @brief Format Folder column data.
255 * @param [in] pCtxt Pointer to compare context.
256 * @param [in] p Pointer to DIFFITEM.
257 * @return String to show in the column.
259 static String ColPathGet(const CDiffContext * pCtxt, const void *p, int)
261 assert(pCtxt != nullptr && p != nullptr);
263 int nDirs = pCtxt->GetCompareDirs();
265 const DIFFITEM &di = *static_cast<const DIFFITEM*>(p);
269 String s = di.diffFileInfo[1].path;
270 const String& t = di.diffFileInfo[0].path;
272 // If we have unique path, just print the existing path name
273 if (s.length() == 0 || t.length() == 0)
284 const tchar_t* pi = tc::tcschr(s.c_str() + i, '\\');
285 const tchar_t* pj = tc::tcschr(t.c_str() + j, '\\');
286 size_t i_ahead = (pi != nullptr ? pi - s.c_str() : std::string::npos);
287 size_t j_ahead = (pj != nullptr ? pj - t.c_str() : std::string::npos);
288 size_t length_s = ((i_ahead != std::string::npos ? i_ahead : s.length()) - i);
289 size_t length_t = ((j_ahead != std::string::npos ? j_ahead : t.length()) - j);
290 if (length_s != length_t ||
291 memcmp(s.c_str() + i, t.c_str() + j, length_s) != 0)
293 String u(t.c_str() + j, length_t + 1);
296 i_ahead += u.length();
307 // If we have unique path, just print the existing path name
308 const DiffFileInfo* pDiffFileInfo = di.diffFileInfo;
309 if (pDiffFileInfo[0].path == pDiffFileInfo[1].path && pDiffFileInfo[0].path == pDiffFileInfo[2].path)
310 return pDiffFileInfo[0].path;
313 const std::vector<const DIFFITEM*> ancestors = di.GetAncestors();
314 size_t depth = ancestors.size();
315 for (int i = 0; i < depth; i++)
319 s += ColFileNameGet<String>(pCtxt, ancestors[i], 0);
326 * @brief Format Result column data.
327 * @param [in] pCtxt Pointer to compare context.
328 * @param [in] p Pointer to DIFFITEM.
329 * @return String to show in the column.
331 static String ColStatusGet(const CDiffContext *pCtxt, const void *p, int)
333 const DIFFITEM &di = *static_cast<const DIFFITEM*>(p);
334 int nDirs = pCtxt->GetCompareDirs();
335 // Note that order of items does matter. We must check for
336 // skipped items before unique items, for example, so that
337 // skipped unique items are labeled as skipped, not unique.
339 bool bAddCompareFlags3WayString = false;
340 if (di.diffcode.isResultError())
342 s = _("Unable to compare files");
344 else if (di.diffcode.isResultAbort())
346 s = _("Item aborted");
348 else if (di.diffcode.isResultFiltered())
350 if (di.diffcode.isDirectory())
351 s = _("Folder skipped");
353 s = _("File skipped");
355 else if (di.diffcode.isSideFirstOnly())
357 s = strutils::format_string1(_("Left only: %1"),
358 di.getFilepath(0, pCtxt->GetNormalizedLeft()));
360 else if (di.diffcode.isSideSecondOnly())
364 s = strutils::format_string1(_("Right only: %1"),
365 di.getFilepath(1, pCtxt->GetNormalizedRight()));
369 s = strutils::format_string1(_("Middle only: %1"),
370 di.getFilepath(1, pCtxt->GetNormalizedMiddle()));
373 else if (di.diffcode.isSideThirdOnly())
375 s = strutils::format_string1(_("Right only: %1"),
376 di.getFilepath(2, pCtxt->GetNormalizedRight()));
378 else if (nDirs > 2 && !di.diffcode.existsFirst())
380 s = strutils::format_string1(_("Does not exist in %1"),
381 pCtxt->GetNormalizedLeft());
382 bAddCompareFlags3WayString = true;
384 else if (nDirs > 2 && !di.diffcode.existsSecond())
386 s = strutils::format_string1(_("Does not exist in %1"),
387 pCtxt->GetNormalizedMiddle());
388 bAddCompareFlags3WayString = true;
390 else if (nDirs > 2 && !di.diffcode.existsThird())
392 s = strutils::format_string1(_("Does not exist in %1"),
393 pCtxt->GetNormalizedRight());
394 bAddCompareFlags3WayString = true;
396 else if (di.diffcode.isResultSame())
398 if (di.diffcode.isText())
399 s = _("Text files are identical");
400 else if (di.diffcode.isBin())
401 s = _("Binary files are identical");
402 else if (di.diffcode.isImage())
403 s = _("Image files are identical");
407 else if (di.diffcode.isResultDiff()) // diff
409 if (di.diffcode.isText())
410 s = _("Text files are different");
411 else if (di.diffcode.isBin())
412 s = _("Binary files are different");
413 else if (di.diffcode.isImage())
414 s = _("Image files are different");
415 else if (di.diffcode.isDirectory())
416 s = _("Folders are different");
418 s = _("Files are different");
420 bAddCompareFlags3WayString = true;
422 if (bAddCompareFlags3WayString)
424 switch (di.diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY)
426 case DIFFCODE::DIFF1STONLY: s += _(" (Middle and right are identical)"); break;
427 case DIFFCODE::DIFF2NDONLY: s += _(" (Left and right are identical)"); break;
428 case DIFFCODE::DIFF3RDONLY: s += _(" (Left and middle are identical)"); break;
435 * @brief Format Date column data.
436 * @param [in] p Pointer to integer (seconds since 1.1.1970).
437 * @return String to show in the column.
439 static String ColTimeGet(const CDiffContext *, const void *p, int)
441 const int64_t r = *static_cast<const int64_t*>(p) / Timestamp::resolution();
443 return locality::TimeString(&r);
449 * @brief Format Sizw column data.
450 * @param [in] p Pointer to integer containing size in bytes.
451 * @return String to show in the column.
453 static String ColSizeGet(const CDiffContext *, const void *p, int)
455 const int64_t &r = *static_cast<const int64_t*>(p);
459 s = locality::NumToLocaleStr(r);
465 * @brief Format Folder column data.
466 * @param [in] p Pointer to DIFFITEM.
467 * @return String to show in the column.
469 static String ColSizeShortGet(const CDiffContext *, const void *p, int)
471 const int64_t &r = *static_cast<const int64_t*>(p);
475 s = MakeShortSize(r);
481 * @brief Format Difference cout column data.
482 * @param [in] p Pointer to integer having count of differences.
483 * @return String to show in the column.
485 static String ColDiffsGet(const CDiffContext *, const void *p, int)
487 const int &r = *static_cast<const int*>(p);
489 if (r == CDiffContext::DIFFS_UNKNOWN_QUICKCOMPARE)
490 { // QuickCompare, unknown
493 else if (r == CDiffContext::DIFFS_UNKNOWN)
499 s = locality::NumToLocaleStr(r);
505 * @brief Format Newer/Older column data.
506 * @param [in] p Pointer to DIFFITEM.
507 * @return String to show in the column.
509 static String ColNewerGet(const CDiffContext *pCtxt, const void *p, int)
511 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
512 if (pCtxt->GetCompareDirs() < 3)
514 if (di.diffcode.isSideFirstOnly())
518 if (di.diffcode.isSideSecondOnly())
522 if (di.diffFileInfo[0].mtime != 0 && di.diffFileInfo[1].mtime != 0)
524 if (di.diffFileInfo[0].mtime > di.diffFileInfo[1].mtime)
528 if (di.diffFileInfo[0].mtime < di.diffFileInfo[1].mtime)
539 int sortno[3] = {0, 1, 2};
540 Timestamp sorttime[3] = {di.diffFileInfo[0].mtime, di.diffFileInfo[1].mtime, di.diffFileInfo[2].mtime};
541 for (int i = 0; i < 3; i++)
543 for (int j = i; j < 3; j++)
545 if (sorttime[i] < sorttime[j])
547 swap(sorttime[i], sorttime[j]);
548 swap(sortno[i], sortno[j]);
552 res = _T("LMR")[sortno[0]];
553 res += sorttime[0] == sorttime[1] ? _T("==") : _T("<-");
554 res += _T("LMR")[sortno[1]];
555 res += sorttime[1] == sorttime[2] ? _T("==") : _T("<-");
556 res += _T("LMR")[sortno[2]];
562 * @brief Format Version info to string.
563 * @param [in] pCtxt Pointer to compare context.
564 * @param [in] pdi Pointer to DIFFITEM.
565 * @param [in] bLeft Is the item left-size item?
566 * @return String proper to show in the GUI.
568 static String GetVersion(const CDiffContext * pCtxt, const DIFFITEM *pdi, int nIndex)
570 DIFFITEM &di = const_cast<DIFFITEM &>(*pdi);
571 DiffFileInfo & dfi = di.diffFileInfo[nIndex];
572 if (dfi.version.IsCleared())
574 pCtxt->UpdateVersion(di, nIndex);
576 return dfi.version.GetFileVersionString();
579 static uint64_t GetVersionQWORD(const CDiffContext * pCtxt, const DIFFITEM *pdi, int nIndex)
581 DIFFITEM &di = const_cast<DIFFITEM &>(*pdi);
582 DiffFileInfo & dfi = di.diffFileInfo[nIndex];
583 if (dfi.version.IsCleared())
585 pCtxt->UpdateVersion(di, nIndex);
587 return dfi.version.GetFileVersionQWORD();
591 * @brief Format Version column data
592 * @param [in] pCtxt Pointer to compare context.
593 * @param [in] p Pointer to DIFFITEM.
594 * @return String to show in the column.
596 static String ColVersionGet(const CDiffContext * pCtxt, const void *p, int opt)
598 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
599 return GetVersion(pCtxt, &di, opt);
603 * @brief Format Short Result column data.
604 * @param [in] p Pointer to DIFFITEM.
605 * @return String to show in the column.
607 static String ColStatusAbbrGet(const CDiffContext *pCtxt, const void *p, int opt)
609 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
611 int nDirs = pCtxt->GetCompareDirs();
613 // Note that order of items does matter. We must check for
614 // skipped items before unique items, for example, so that
615 // skipped unique items are labeled as skipped, not unique.
616 if (di.diffcode.isResultError())
620 else if (di.diffcode.isResultAbort())
622 id = N_("Item aborted");
624 else if (di.diffcode.isResultFiltered())
626 if (di.diffcode.isDirectory())
627 id = N_("Folder skipped");
629 id = N_("File skipped");
631 else if (di.diffcode.isSideFirstOnly())
633 id = N_("Left Only");
635 else if (di.diffcode.isSideSecondOnly())
638 id = N_("Right Only");
640 id = N_("Middle Only");
642 else if (di.diffcode.isSideThirdOnly())
644 id = N_("Right Only");
646 else if (nDirs > 2 && !di.diffcode.existsFirst())
648 id = N_("No item in left");
650 else if (nDirs > 2 && !di.diffcode.existsSecond())
652 id = N_("No item in middle");
654 else if (nDirs > 2 && !di.diffcode.existsThird())
656 id = N_("No item in right");
658 else if (di.diffcode.isResultSame())
660 id = N_("Identical");
662 else if (di.diffcode.isResultDiff())
664 id = N_("Different");
667 return id ? tr(id) : _T("");
671 * @brief Format Binary column data.
672 * @param [in] p Pointer to DIFFITEM.
673 * @return String to show in the column.
675 static String ColBinGet(const CDiffContext *, const void *p, int)
677 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
679 if (di.diffcode.isBin())
686 * @brief Format File Attributes column data.
687 * @param [in] p Pointer to file flags class.
688 * @return String to show in the column.
690 static String ColAttrGet(const CDiffContext *, const void *p, int)
692 const FileFlags &r = *static_cast<const FileFlags *>(p);
697 * @brief Format File Encoding column data.
698 * @param [in] p Pointer to file information.
699 * @return String to show in the column.
701 static String ColEncodingGet(const CDiffContext *, const void *p, int)
703 const DiffFileInfo &r = *static_cast<const DiffFileInfo *>(p);
704 return r.encoding.GetName();
708 * @brief Format EOL type to string.
709 * @param [in] p Pointer to DIFFITEM.
710 * @param [in] bLeft Are we formatting left-side file's data?
711 * @return EOL type as as string.
713 static String GetEOLType(const CDiffContext *, const void *p, int index)
715 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
716 const DiffFileInfo & dfi = di.diffFileInfo[index];
717 const FileTextStats &stats = dfi.m_textStats;
719 if (stats.ncrlfs == 0 && stats.ncrs == 0 && stats.nlfs == 0)
723 if (di.diffcode.isBin())
725 return tr("EOL Type", "Binary");
729 if (stats.ncrlfs > 0 && stats.ncrs == 0 && stats.nlfs == 0)
733 else if (stats.ncrlfs == 0 && stats.ncrs > 0 && stats.nlfs == 0)
737 else if (stats.ncrlfs == 0 && stats.ncrs == 0 && stats.nlfs > 0)
743 return strutils::format(_T("%s:%d/%d/%d"),
745 stats.ncrlfs, stats.ncrs, stats.nlfs);
752 * @brief Format EOL type column data
753 * @param [in] pCtxt Pointer to compare context.
754 * @param [in] p Pointer to DIFFITEM.
755 * @return String to show in the column.
757 static String ColEOLTypeGet(const CDiffContext * pCtxt, const void *p, int opt)
759 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
760 return GetEOLType(pCtxt, &di, opt);
763 static String ColPluginPipelineGet(const CDiffContext* pCtxt, const void *p, int opt)
765 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
766 if (di.diffcode.isDirectory())
768 PackingInfo* pInfoUnpacker = nullptr;
769 PrediffingInfo * pInfoPrediffer = nullptr;
770 String filteredFilenames = pCtxt->GetFilteredFilenames(di);
771 const_cast<CDiffContext *>(pCtxt)->FetchPluginInfos(filteredFilenames, &pInfoUnpacker, &pInfoPrediffer);
773 return pInfoUnpacker ? pInfoUnpacker->GetPluginPipeline() : _T("");
775 return pInfoPrediffer ? pInfoPrediffer->GetPluginPipeline() : _T("");
778 #ifdef SHOW_DIFFITEM_DEBUG_INFO
780 * @brief Format "[Debug]diffcode" column data.
781 * @param [in] p Pointer to DIFFITEM.
782 * @return String to show in the column.
784 static String ColDebugDiffCodeGet(const CDiffContext *, const void* p, int)
786 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
787 unsigned int diffcode = di.diffcode.diffcode;
789 String s = strutils::format(_T("0x%08x"), diffcode);
791 std::vector<String> flags;
792 if (diffcode & DIFFCODE::FIRST)
793 flags.push_back(_T("FIRST"));
794 if (diffcode & DIFFCODE::SECOND)
795 flags.push_back(_T("SECOND"));
796 if (diffcode & DIFFCODE::THIRD)
797 flags.push_back(_T("THIRD"));
798 if (diffcode & DIFFCODE::THREEWAY)
799 flags.push_back(_T("THREEWAY"));
800 if (diffcode & DIFFCODE::NEEDSCAN)
801 flags.push_back(_T("NEEDSCAN"));
802 if (diffcode & DIFFCODE::SKIPPED)
803 flags.push_back(_T("SKIPPED"));
804 switch (diffcode & DIFFCODE::COMPAREFLAGS3WAY)
806 case DIFFCODE::DIFF1STONLY: flags.push_back(_T("DIFF1STONLY")); break;
807 case DIFFCODE::DIFF2NDONLY: flags.push_back(_T("DIFF2NDONLY")); break;
808 case DIFFCODE::DIFF3RDONLY: flags.push_back(_T("DIFF3RDONLY")); break;
810 switch (diffcode & DIFFCODE::COMPAREFLAGS)
812 case DIFFCODE::DIFF: flags.push_back(_T("DIFF")); break;
813 case DIFFCODE::SAME: flags.push_back(_T("SAME")); break;
814 case DIFFCODE::CMPERR: flags.push_back(_T("CMPERR")); break;
815 case DIFFCODE::CMPABORT: flags.push_back(_T("CMPABORT")); break;
817 if (diffcode & DIFFCODE::FILE)
818 flags.push_back(_T("FILE"));
819 if (diffcode & DIFFCODE::DIR)
820 flags.push_back(_T("DIR"));
821 if (diffcode & DIFFCODE::TEXT)
822 flags.push_back(_T("TEXT"));
823 if (diffcode & DIFFCODE::BIN)
824 flags.push_back(_T("BIN"));
825 if (diffcode & DIFFCODE::BINSIDE1)
826 flags.push_back(_T("BINSIDE1"));
827 if (diffcode & DIFFCODE::BINSIDE2)
828 flags.push_back(_T("BINSIDE2"));
829 if (diffcode & DIFFCODE::BINSIDE3)
830 flags.push_back(_T("BINSIDE3"));
831 if (diffcode & DIFFCODE::IMAGE)
832 flags.push_back(_T("IMAGE"));
837 for (size_t i = 0; i < flags.size(); i++)
840 if (i < flags.size() - 1)
848 #endif // SHOW_DIFFITEM_DEBUG_INFO
850 #ifdef SHOW_DIFFITEM_DEBUG_INFO
852 * @brief Format "[Debug]customFlags" column data.
853 * @param [in] p Pointer to DIFFITEM.
854 * @return String to show in the column.
856 static String ColDebugCustomFlagsGet(const CDiffContext *, const void* p, int)
858 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
859 unsigned customFlags = di.customFlags;
861 String s = strutils::format(_T("0x%08x"), customFlags);
863 std::vector<String> flags;
864 if (customFlags & ViewCustomFlags::EXPANDED)
865 flags.push_back(_T("EXPANDED"));
866 if (customFlags & ViewCustomFlags::HIDDEN)
867 flags.push_back(_T("HIDDEN"));
868 if (customFlags & ViewCustomFlags::VISIBLE)
869 flags.push_back(_T("VISIBLE"));
874 for (size_t i = 0; i < flags.size(); i++)
877 if (i < flags.size() - 1)
885 #endif // SHOW_DIFFITEM_DEBUG_INFO
887 #ifdef SHOW_DIFFITEM_DEBUG_INFO
889 * @brief Format "[Debug]this", "[Debug]parent", "[Debug]children", "[Debug]Flink" and "[Debug]Blink" column data.
890 * @param [in] p Pointer to DIFFITEM.
891 * @param [in] opt Type of data to show (0:"[Debug]this", 1:"[Debug]parent", 2:"[Debug]children", 3:"[Debug]Flink", 4:"[Debug]Blink")
892 * @return String to show in the column.
894 static String ColDebugNodeGet(const CDiffContext *, const void* p, int opt)
896 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
898 const DIFFITEM* pdi = nullptr;
901 case 0: pdi = &di; break;
902 case 1: pdi = di.GetParentLink(); break;
903 case 2: pdi = di.GetFirstChild(); break;
904 case 3: pdi = di.GetFwdSiblingLink(); break;
905 case 4: pdi = di.GetBackwardSiblingLink(); break;
908 return strutils::format(_T("%p"), pdi);
910 #endif // SHOW_DIFFITEM_DEBUG_INFO
912 static String ColPropertyGet(const CDiffContext *pCtxt, const void *p, int opt)
914 const DiffFileInfo &dfi = *static_cast<const DiffFileInfo *>(p);
915 PropertyValues* pprops = dfi.m_pAdditionalProperties.get();
916 return (pprops != nullptr && opt < pprops->GetSize()) ? pCtxt->m_pPropertySystem->FormatPropertyValue(*pprops, opt) : _T("");
919 static const DuplicateInfo *GetDuplicateInfo(const CDiffContext* pCtxt, const DiffFileInfo& dfi, int index)
921 PropertyValues* pprops = dfi.m_pAdditionalProperties.get();
922 if (!pprops || index >= pprops->GetSize() || !pprops->IsHashValue(index) || pCtxt->m_duplicateValues.empty())
924 auto it = pCtxt->m_duplicateValues[index].find(pprops->GetHashValue(index));
925 if (it == pCtxt->m_duplicateValues[index].end())
927 return &(it->second);
930 static String ColPropertyDuplicateCountGet(const CDiffContext *pCtxt, const void *p, int opt)
932 const int index = opt & 0xffff;
933 const int pane = opt >> 16;
934 const DiffFileInfo &dfi = *static_cast<const DiffFileInfo *>(p);
935 const DuplicateInfo *info = GetDuplicateInfo(pCtxt, dfi, index);
936 return (!info || info->count[pane] <= 1) ? _T("") :
937 strutils::format(_("%s: %d"), strutils::format(_("Group%d"), info->groupid), info->count[pane] - 1);
940 static String ColAllPropertyGet(const CDiffContext *pCtxt, const void *p, int opt)
942 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
944 PropertyValues* pFirstProps = di.diffFileInfo[0].m_pAdditionalProperties.get();
945 for (int i = 1; i < pCtxt->GetCompareDirs(); ++i)
947 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
948 if (pFirstProps && pprops)
950 if (PropertyValues::CompareValues(*pFirstProps, *pprops, opt) != 0)
955 return pFirstProps ? pCtxt->m_pPropertySystem->FormatPropertyValue(*pFirstProps, opt) : _T("");
957 std::vector<String> values;
958 for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
960 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
961 if (pCtxt->GetCompareDirs() == 3 || di.diffcode.exists(i))
962 values.push_back(pprops ? pCtxt->m_pPropertySystem->FormatPropertyValue(*pprops, opt) : _T(""));
964 return strutils::join(values.begin(), values.end(), _T("|"));
967 static String ColPropertyDiffGetEx(const CDiffContext *pCtxt, const void *p, int opt, bool addNumDiff, bool& numeric, int64_t *pnumdiff)
969 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
970 if (di.diffcode.isDirectory())
972 if (di.diffcode.isResultError())
974 if (di.diffcode.isResultAbort())
975 return _("Item aborted");
976 if (di.diffcode.isResultFiltered())
977 return _("File skipped");
978 PropertyValues* pFirstProps = di.diffFileInfo[0].m_pAdditionalProperties.get();
984 const int nDirs = pCtxt->GetCompareDirs();
985 for (int i = 1; i < nDirs; ++i)
987 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
988 if (pFirstProps && pprops)
990 diff = PropertyValues::DiffValues(*pFirstProps, *pprops, opt, numeric);
998 if (!di.diffcode.existAll())
1000 bool allempty = true;
1001 for (int i = 0; i < nDirs; ++i)
1003 if (di.diffcode.exists(i))
1005 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
1006 if (pprops && !pprops->IsEmptyValue(opt))
1012 if (di.diffcode.isSideFirstOnly())
1013 result = _("Left Only");
1014 else if (di.diffcode.isSideSecondOnly())
1017 result = _("Right Only");
1019 result = _("Middle Only");
1021 else if (di.diffcode.isSideThirdOnly())
1022 result = _("Right Only");
1023 else if (nDirs > 2 && !di.diffcode.existsFirst())
1024 result = _("No item in left");
1025 else if (nDirs > 2 && !di.diffcode.existsSecond())
1026 result = _("No item in middle");
1027 else if (nDirs > 2 && !di.diffcode.existsThird())
1028 result = _("No item in right");
1033 result = !equal ? _("Different") :
1034 (!pFirstProps->IsEmptyValue(opt) ? _("Identical") : _T(""));
1036 if (nDirs == 2 && numeric && addNumDiff)
1037 result += strutils::format(_T(" (%+ld)"), diff);
1041 static String ColPropertyDiffGet(const CDiffContext* pCtxt, const void* p, int opt)
1043 bool numeric = false;
1044 return ColPropertyDiffGetEx(pCtxt, p, opt, true, numeric, nullptr);
1047 static String ColPropertyMoveGet(const CDiffContext *pCtxt, const void *p, int opt)
1049 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
1050 if (di.diffcode.isDirectory())
1052 std::vector<String> list;
1053 const int nDirs = pCtxt->GetCompareDirs();
1054 for (int i = 0; i < nDirs; ++i)
1056 if (di.diffcode.exists(i))
1058 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
1059 if (pprops && !pCtxt->m_duplicateValues.empty())
1061 auto it = pCtxt->m_duplicateValues[opt].find(pprops->GetHashValue(opt));
1062 if (it != pCtxt->m_duplicateValues[opt].end())
1064 if (it->second.groupid != 0 && it->second.nonpaired)
1067 for (int j = 0; j < nDirs; ++j)
1069 if (it->second.count[j] > 0)
1073 list.push_back(strutils::format(_("Group%d"), it->second.groupid));
1079 return strutils::join(list.begin(), list.end(), _T("|"));
1087 * @name Functions to sort each type of column info.
1088 * These functions are used to sort information in folder compare GUI. Each
1089 * column info (type) has its own function to compare the data. Each
1090 * function receives three parameters:
1091 * - pointer to compare context
1092 * - two parameters for data to compare (type varies)
1093 * Return value is -1, 0, or 1, where 0 means datas are identical.
1097 * @brief Compare file names.
1098 * @param [in] pCtxt Pointer to compare context.
1099 * @param [in] p Pointer to DIFFITEM having first name to compare.
1100 * @param [in] q Pointer to DIFFITEM having second name to compare.
1101 * @return Compare result.
1103 static int ColFileNameSort(const CDiffContext *pCtxt, const void *p, const void *q, int)
1105 const DIFFITEM &ldi = *static_cast<const DIFFITEM *>(p);
1106 const DIFFITEM &rdi = *static_cast<const DIFFITEM *>(q);
1107 if (ldi.diffcode.isDirectory() && !rdi.diffcode.isDirectory())
1109 if (!ldi.diffcode.isDirectory() && rdi.diffcode.isDirectory())
1111 return strutils::compare_logical(ColFileNameGet<boost::flyweight<String>>(pCtxt, p, 0), ColFileNameGet<boost::flyweight<String>>(pCtxt, q, 0));
1115 * @brief Compare file name extensions.
1116 * @param [in] pCtxt Pointer to compare context.
1117 * @param [in] p First file name extension to compare.
1118 * @param [in] q Second file name extension to compare.
1119 * @return Compare result.
1121 static int ColExtSort(const CDiffContext *pCtxt, const void *p, const void *q, int)
1123 const DIFFITEM &ldi = *static_cast<const DIFFITEM *>(p);
1124 const DIFFITEM &rdi = *static_cast<const DIFFITEM *>(q);
1125 if (ldi.diffcode.isDirectory() && !rdi.diffcode.isDirectory())
1127 if (!ldi.diffcode.isDirectory() && rdi.diffcode.isDirectory())
1129 return strutils::compare_logical(ColExtGet(pCtxt, p, 0), ColExtGet(pCtxt, q, 0));
1133 * @brief Compare folder names.
1134 * @param [in] pCtxt Pointer to compare context.
1135 * @param [in] p Pointer to DIFFITEM having first folder name to compare.
1136 * @param [in] q Pointer to DIFFITEM having second folder name to compare.
1137 * @return Compare result.
1139 static int ColPathSort(const CDiffContext *pCtxt, const void *p, const void *q, int)
1141 return strutils::compare_logical(ColPathGet(pCtxt, p, 0), ColPathGet(pCtxt, q, 0));
1145 * @brief Compare compare results.
1146 * @param [in] p Pointer to DIFFITEM having first result to compare.
1147 * @param [in] q Pointer to DIFFITEM having second result to compare.
1148 * @return Compare result.
1150 static int ColStatusSort(const CDiffContext *, const void *p, const void *q, int)
1152 const DIFFITEM &ldi = *static_cast<const DIFFITEM *>(p);
1153 const DIFFITEM &rdi = *static_cast<const DIFFITEM *>(q);
1154 return cmpdiffcode(rdi.diffcode.diffcode, ldi.diffcode.diffcode);
1158 * @brief Compare file times.
1159 * @param [in] p First time to compare.
1160 * @param [in] q Second time to compare.
1161 * @return Compare result.
1163 static int ColTimeSort(const CDiffContext *, const void *p, const void *q, int)
1165 const int64_t &r = *static_cast<const int64_t*>(p);
1166 const int64_t &s = *static_cast<const int64_t*>(q);
1171 * @brief Compare file sizes.
1172 * @param [in] p First size to compare.
1173 * @param [in] q Second size to compare.
1174 * @return Compare result.
1176 static int ColSizeSort(const CDiffContext *, const void *p, const void *q, int)
1178 const int64_t &r = *static_cast<const int64_t*>(p);
1179 const int64_t &s = *static_cast<const int64_t*>(q);
1184 * @brief Compare difference counts.
1185 * @param [in] p First count to compare.
1186 * @param [in] q Second count to compare.
1187 * @return Compare result.
1189 static int ColDiffsSort(const CDiffContext *, const void *p, const void *q, int)
1191 const int &r = *static_cast<const int*>(p);
1192 const int &s = *static_cast<const int*>(q);
1197 * @brief Compare newer/older statuses.
1198 * @param [in] pCtxt Pointer to compare context.
1199 * @param [in] p Pointer to DIFFITEM having first status to compare.
1200 * @param [in] q Pointer to DIFFITEM having second status to compare.
1201 * @return Compare result.
1203 static int ColNewerSort(const CDiffContext *pCtxt, const void *p, const void *q, int)
1205 return ColNewerGet(pCtxt, p, 0).compare(ColNewerGet(pCtxt, q, 0));
1209 * @brief Compare file versions.
1210 * @param [in] pCtxt Pointer to compare context.
1211 * @param [in] p Pointer to DIFFITEM having first version to compare.
1212 * @param [in] q Pointer to DIFFITEM having second version to compare.
1213 * @return Compare result.
1215 static int ColVersionSort(const CDiffContext *pCtxt, const void *p, const void *q, int opt)
1217 return cmpu64(GetVersionQWORD(pCtxt, reinterpret_cast<const DIFFITEM *>(p), opt), GetVersionQWORD(pCtxt, reinterpret_cast<const DIFFITEM *>(q), opt));
1221 * @brief Compare binary statuses.
1222 * This function returns a comparison of binary results.
1223 * @param [in] p Pointer to DIFFITEM having first status to compare.
1224 * @param [in] q Pointer to DIFFITEM having second status to compare.
1225 * @return Compare result:
1226 * - if both items are text files or binary files: 0
1227 * - if left is text and right is binary: -1
1228 * - if left is binary and right is text: 1
1230 static int ColBinSort(const CDiffContext *, const void *p, const void *q, int)
1232 const DIFFITEM &ldi = *static_cast<const DIFFITEM *>(p);
1233 const DIFFITEM &rdi = *static_cast<const DIFFITEM *>(q);
1234 const bool i = ldi.diffcode.isBin();
1235 const bool j = rdi.diffcode.isBin();
1248 * @brief Compare file flags.
1249 * @param [in] p Pointer to first flag structure to compare.
1250 * @param [in] q Pointer to second flag structure to compare.
1251 * @return Compare result.
1253 static int ColAttrSort(const CDiffContext *, const void *p, const void *q, int)
1255 const FileFlags &r = *static_cast<const FileFlags *>(p);
1256 const FileFlags &s = *static_cast<const FileFlags *>(q);
1257 if (r.attributes == s.attributes)
1259 return r.attributes < s.attributes ? -1 : 1;
1263 * @brief Compare file encodings.
1264 * @param [in] p Pointer to first structure to compare.
1265 * @param [in] q Pointer to second structure to compare.
1266 * @return Compare result.
1268 static int ColEncodingSort(const CDiffContext *, const void *p, const void *q, int)
1270 const DiffFileInfo &r = *static_cast<const DiffFileInfo *>(p);
1271 const DiffFileInfo &s = *static_cast<const DiffFileInfo *>(q);
1272 return FileTextEncoding::Collate(r.encoding, s.encoding);
1276 * @brief Compare property values
1277 * @param [in] p Pointer to first structure to compare.
1278 * @param [in] q Pointer to second structure to compare.
1279 * @return Compare result.
1281 static int ColPropertySort(const CDiffContext *, const void *p, const void *q, int opt)
1283 const DiffFileInfo &r = *static_cast<const DiffFileInfo *>(p);
1284 const DiffFileInfo &s = *static_cast<const DiffFileInfo *>(q);
1285 if (!r.m_pAdditionalProperties && s.m_pAdditionalProperties)
1287 if (r.m_pAdditionalProperties && !s.m_pAdditionalProperties)
1289 if (!r.m_pAdditionalProperties && !s.m_pAdditionalProperties)
1291 return PropertyValues::CompareValues(*r.m_pAdditionalProperties, *s.m_pAdditionalProperties, opt);
1295 * @brief Compare duplicate count
1296 * @param [in] p Pointer to first structure to compare.
1297 * @param [in] q Pointer to second structure to compare.
1298 * @return Compare result.
1300 static int ColPropertyDuplicateCountSort(const CDiffContext* pCtxt, const void* p, const void* q, int opt)
1302 const int index = opt & 0xffff;
1303 const int pane = opt >> 16;
1304 const DiffFileInfo& r = *static_cast<const DiffFileInfo*>(p);
1305 const DiffFileInfo& s = *static_cast<const DiffFileInfo*>(q);
1306 const DuplicateInfo* rinfo = GetDuplicateInfo(pCtxt, r, index);
1307 const DuplicateInfo* sinfo = GetDuplicateInfo(pCtxt, s, index);
1308 if (!rinfo && !sinfo)
1310 if (!rinfo && sinfo)
1312 if (rinfo && !sinfo)
1314 if (rinfo->count[pane] <= 1 && sinfo->count[pane] <= 1)
1316 if (rinfo->count[pane] <= 1 && sinfo->count[pane] > 1)
1318 if (rinfo->count[pane] > 1 && sinfo->count[pane] <= 1)
1320 if (rinfo->count[pane] == sinfo->count[pane])
1322 if (rinfo->groupid == sinfo->groupid)
1324 return rinfo->groupid < sinfo->groupid ? -1 : 1;
1326 return rinfo->count[pane] < sinfo->count[pane] ? -1 : 1;
1330 * @brief Compare all property values
1331 * @param [in] p Pointer to first structure to compare.
1332 * @param [in] q Pointer to second structure to compare.
1333 * @return Compare result.
1335 static int ColAllPropertySort(const CDiffContext *pCtxt, const void *p, const void *q, int opt)
1337 const DIFFITEM &r = *static_cast<const DIFFITEM *>(p);
1338 const DIFFITEM &s = *static_cast<const DIFFITEM *>(q);
1339 for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
1341 if (r.diffcode.exists(i))
1343 for (int j = 0; j < pCtxt->GetCompareDirs(); ++j)
1345 if (s.diffcode.exists(j))
1347 if (!r.diffFileInfo[i].m_pAdditionalProperties && s.diffFileInfo[j].m_pAdditionalProperties)
1349 if (r.diffFileInfo[i].m_pAdditionalProperties && !s.diffFileInfo[j].m_pAdditionalProperties)
1351 if (!r.diffFileInfo[i].m_pAdditionalProperties && !s.diffFileInfo[j].m_pAdditionalProperties)
1353 int result = PropertyValues::CompareValues(*r.diffFileInfo[i].m_pAdditionalProperties, *s.diffFileInfo[j].m_pAdditionalProperties, opt);
1364 * @brief Compare Hash(Diff) column values
1365 * @param [in] p Pointer to first structure to compare.
1366 * @param [in] q Pointer to second structure to compare.
1367 * @return Compare result.
1369 static int ColPropertyDiffSort(const CDiffContext *pCtxt, const void *p, const void *q, int opt)
1371 int64_t rnumdiff = 0;
1372 int64_t snumdiff = 0;
1373 bool rnumeric = false;
1374 bool snumeric = false;
1375 String r2 = ColPropertyDiffGetEx(pCtxt, p, opt, false, rnumeric, &rnumdiff);
1376 String s2 = ColPropertyDiffGetEx(pCtxt, q, opt, false, snumeric, &snumdiff);
1377 int result = strutils::compare_logical(r2, s2);
1378 if (pCtxt->GetCompareDirs() == 2 && result == 0 && rnumeric && snumeric)
1380 if (rnumdiff == snumdiff)
1382 return rnumdiff < snumdiff ? -1 : 1;
1388 * @brief Compare Hash(Move) column values
1389 * @param [in] p Pointer to first structure to compare.
1390 * @param [in] q Pointer to second structure to compare.
1391 * @return Compare result.
1393 static int ColPropertyMoveSort(const CDiffContext *pCtxt, const void *p, const void *q, int opt)
1395 String r2 = ColPropertyMoveGet(pCtxt, p, opt);
1396 String s2 = ColPropertyMoveGet(pCtxt, q, opt);
1397 return strutils::compare_logical(r2, s2);
1402 #undef FIELD_OFFSET // incorrect for Win32 as defined in WinNT.h
1403 #define FIELD_OFFSET(type, field) ((size_t)(LONG_PTR)&(((type *)nullptr)->field))
1406 * @brief All existing folder compare columns.
1408 * This table has information for all folder compare columns. Fields are
1411 * - name resource ID: column's name shown in header
1412 * - description resource ID: columns description text
1413 * - custom function for getting column data
1414 * - custom function for sorting column data
1415 * - parameter for custom functions: DIFFITEM (if `nullptr`) or one of its fields
1416 * - default column order number, -1 if not shown by default
1417 * - ascending (`true`) or descending (`false`) default sort order
1418 * - alignment of column contents: numbers are usually right-aligned
1420 static DirColInfo f_cols[] =
1422 { _T("Name"), nullptr, COLHDR_FILENAME, COLDESC_FILENAME, &ColFileNameGet<String>, &ColFileNameSort, 0, 0, true, DirColInfo::ALIGN_LEFT },
1423 { _T("Path"), "DirView|ColumnHeader", COLHDR_DIR, COLDESC_DIR, &ColPathGet, &ColPathSort, 0, 1, true, DirColInfo::ALIGN_LEFT },
1424 { _T("Status"), nullptr, COLHDR_RESULT, COLDESC_RESULT, &ColStatusGet, &ColStatusSort, 0, 2, true, DirColInfo::ALIGN_LEFT },
1425 { _T("Lmtime"), nullptr, COLHDR_LTIMEM, COLDESC_LTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].mtime), 3, false, DirColInfo::ALIGN_LEFT, 0 },
1426 { _T("Rmtime"), nullptr, COLHDR_RTIMEM, COLDESC_RTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].mtime), 4, false, DirColInfo::ALIGN_LEFT, 1 },
1427 { _T("Lctime"), nullptr, COLHDR_LTIMEC, COLDESC_LTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].ctime), -1, false, DirColInfo::ALIGN_LEFT, 0 },
1428 { _T("Rctime"), nullptr, COLHDR_RTIMEC, COLDESC_RTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].ctime), -1, false, DirColInfo::ALIGN_LEFT, 1 },
1429 { _T("Ext"), nullptr, COLHDR_EXTENSION, COLDESC_EXTENSION, &ColExtGet, &ColExtSort, 0, 5, true, DirColInfo::ALIGN_LEFT },
1430 { _T("Lsize"), nullptr, COLHDR_LSIZE, COLDESC_LSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].size), -1, false, DirColInfo::ALIGN_RIGHT, 0 },
1431 { _T("Rsize"), nullptr, COLHDR_RSIZE, COLDESC_RSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].size), -1, false, DirColInfo::ALIGN_RIGHT, 1 },
1432 { _T("LsizeShort"), nullptr, COLHDR_LSIZE_SHORT, COLDESC_LSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].size), -1, false, DirColInfo::ALIGN_RIGHT, 0 },
1433 { _T("RsizeShort"), nullptr, COLHDR_RSIZE_SHORT, COLDESC_RSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].size), -1, false, DirColInfo::ALIGN_RIGHT, 1 },
1434 { _T("Newer"), nullptr, COLHDR_NEWER, COLDESC_NEWER, &ColNewerGet, &ColNewerSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1435 { _T("Lversion"), nullptr, COLHDR_LVERSION, COLDESC_LVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1436 { _T("Rversion"), nullptr, COLHDR_RVERSION, COLDESC_RVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1437 { _T("StatusAbbr"), nullptr, COLHDR_RESULT_ABBR, COLDESC_RESULT_ABBR, &ColStatusAbbrGet, &ColStatusSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1438 { _T("Binary"), "DirView|ColumnHeader", COLHDR_BINARY, COLDESC_BINARY, &ColBinGet, &ColBinSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1439 { _T("Lattr"), nullptr, COLHDR_LATTRIBUTES, COLDESC_LATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].flags), -1, true, DirColInfo::ALIGN_LEFT, 0 },
1440 { _T("Rattr"), nullptr, COLHDR_RATTRIBUTES, COLDESC_RATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].flags), -1, true, DirColInfo::ALIGN_LEFT, 1 },
1441 { _T("Lencoding"), nullptr, COLHDR_LENCODING, COLDESC_LENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0]), -1, true, DirColInfo::ALIGN_LEFT, 0 },
1442 { _T("Rencoding"), nullptr, COLHDR_RENCODING, COLDESC_RENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1]), -1, true, DirColInfo::ALIGN_LEFT, 1 },
1443 { _T("Snsdiffs"), nullptr, COLHDR_NSDIFFS, COLDESC_NSDIFFS, ColDiffsGet, ColDiffsSort, FIELD_OFFSET(DIFFITEM, nsdiffs), -1, false, DirColInfo::ALIGN_RIGHT },
1444 { _T("Snidiffs"), nullptr, COLHDR_NIDIFFS, COLDESC_NIDIFFS, ColDiffsGet, ColDiffsSort, FIELD_OFFSET(DIFFITEM, nidiffs), -1, false, DirColInfo::ALIGN_RIGHT },
1445 { _T("Leoltype"), nullptr, COLHDR_LEOL_TYPE, COLDESC_LEOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1446 { _T("Reoltype"), nullptr, COLHDR_REOL_TYPE, COLDESC_REOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1447 { _T("Unpacker"), nullptr, COLHDR_UNPACKER, COLDESC_UNPACKER, &ColPluginPipelineGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1448 { _T("Prediffer"), nullptr, COLHDR_PREDIFFER, COLDESC_PREDIFFER, &ColPluginPipelineGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1449 #ifdef SHOW_DIFFITEM_DEBUG_INFO
1450 { _T("diffcode"), nullptr, COLHDR_DEBUG_DIFFCODE, COLDESC_DEBUG_DIFFCODE, &ColDebugDiffCodeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT },
1451 { _T("customFlags"), nullptr, COLHDR_DEBUG_CUSTOMFLAGS, COLDESC_DEBUG_CUSTOMFLAGS, &ColDebugCustomFlagsGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT },
1452 { _T("this"), nullptr, COLHDR_DEBUG_THIS, COLDESC_DEBUG_THIS, &ColDebugNodeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1453 { _T("parent"), nullptr, COLHDR_DEBUG_PARENT, COLDESC_DEBUG_PARENT, &ColDebugNodeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1454 { _T("children"), nullptr, COLHDR_DEBUG_CHILDREN, COLDESC_DEBUG_CHILDREN, &ColDebugNodeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 2 },
1455 { _T("Flink"), nullptr, COLHDR_DEBUG_FLINK, COLDESC_DEBUG_FLINK, &ColDebugNodeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 3 },
1456 { _T("Blink"), nullptr, COLHDR_DEBUG_BLINK, COLDESC_DEBUG_BLINK, &ColDebugNodeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 4 },
1457 #endif // SHOW_DIFFITEM_DEBUG_INFO
1459 static DirColInfo f_cols3[] =
1461 { _T("Name"), nullptr, COLHDR_FILENAME, COLDESC_FILENAME, &ColFileNameGet<String>, &ColFileNameSort, 0, 0, true, DirColInfo::ALIGN_LEFT },
1462 { _T("Path"), "DirView|ColumnHeader", COLHDR_DIR, COLDESC_DIR, &ColPathGet, &ColPathSort, 0, 1, true, DirColInfo::ALIGN_LEFT },
1463 { _T("Status"), nullptr, COLHDR_RESULT, COLDESC_RESULT, &ColStatusGet, &ColStatusSort, 0, 2, true, DirColInfo::ALIGN_LEFT },
1464 { _T("Lmtime"), nullptr, COLHDR_LTIMEM, COLDESC_LTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].mtime), 3, false, DirColInfo::ALIGN_LEFT, 0 },
1465 { _T("Mmtime"), nullptr, COLHDR_MTIMEM, COLDESC_MTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].mtime), 4, false, DirColInfo::ALIGN_LEFT, 1 },
1466 { _T("Rmtime"), nullptr, COLHDR_RTIMEM, COLDESC_RTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].mtime), 5, false, DirColInfo::ALIGN_LEFT, 2 },
1467 { _T("Lctime"), nullptr, COLHDR_LTIMEC, COLDESC_LTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].ctime), -1, false, DirColInfo::ALIGN_LEFT, 0 },
1468 { _T("Mctime"), nullptr, COLHDR_MTIMEC, COLDESC_MTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].ctime), -1, false, DirColInfo::ALIGN_LEFT, 1 },
1469 { _T("Rctime"), nullptr, COLHDR_RTIMEC, COLDESC_RTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].ctime), -1, false, DirColInfo::ALIGN_LEFT, 2 },
1470 { _T("Ext"), nullptr, COLHDR_EXTENSION, COLDESC_EXTENSION, &ColExtGet, &ColExtSort, 0, 6, true, DirColInfo::ALIGN_LEFT },
1471 { _T("Lsize"), nullptr, COLHDR_LSIZE, COLDESC_LSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].size), -1, false, DirColInfo::ALIGN_RIGHT, 0 },
1472 { _T("Msize"), nullptr, COLHDR_MSIZE, COLDESC_MSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].size), -1, false, DirColInfo::ALIGN_RIGHT, 1 },
1473 { _T("Rsize"), nullptr, COLHDR_RSIZE, COLDESC_RSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].size), -1, false, DirColInfo::ALIGN_RIGHT, 2 },
1474 { _T("LsizeShort"), nullptr, COLHDR_LSIZE_SHORT, COLDESC_LSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].size), -1, false, DirColInfo::ALIGN_RIGHT, 0 },
1475 { _T("MsizeShort"), nullptr, COLHDR_MSIZE_SHORT, COLDESC_MSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].size), -1, false, DirColInfo::ALIGN_RIGHT, 1 },
1476 { _T("RsizeShort"), nullptr, COLHDR_RSIZE_SHORT, COLDESC_RSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].size), -1, false, DirColInfo::ALIGN_RIGHT, 2 },
1477 { _T("Newer"), nullptr, COLHDR_NEWER, COLDESC_NEWER, &ColNewerGet, &ColNewerSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1478 { _T("Lversion"), nullptr, COLHDR_LVERSION, COLDESC_LVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1479 { _T("Mversion"), nullptr, COLHDR_MVERSION, COLDESC_MVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1480 { _T("Rversion"), nullptr, COLHDR_RVERSION, COLDESC_RVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 2 },
1481 { _T("StatusAbbr"), nullptr, COLHDR_RESULT_ABBR, COLDESC_RESULT_ABBR, &ColStatusAbbrGet, &ColStatusSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1482 { _T("Binary"), "DirView|ColumnHeader", COLHDR_BINARY, COLDESC_BINARY, &ColBinGet, &ColBinSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1483 { _T("Lattr"), nullptr, COLHDR_LATTRIBUTES, COLDESC_LATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].flags), -1, true, DirColInfo::ALIGN_LEFT, 0 },
1484 { _T("Mattr"), nullptr, COLHDR_MATTRIBUTES, COLDESC_MATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].flags), -1, true, DirColInfo::ALIGN_LEFT, 1 },
1485 { _T("Rattr"), nullptr, COLHDR_RATTRIBUTES, COLDESC_RATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].flags), -1, true, DirColInfo::ALIGN_LEFT, 2 },
1486 { _T("Lencoding"), nullptr, COLHDR_LENCODING, COLDESC_LENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0]), -1, true, DirColInfo::ALIGN_LEFT, 0 },
1487 { _T("Mencoding"), nullptr, COLHDR_MENCODING, COLDESC_MENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1]), -1, true, DirColInfo::ALIGN_LEFT, 1 },
1488 { _T("Rencoding"), nullptr, COLHDR_RENCODING, COLDESC_RENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2]), -1, true, DirColInfo::ALIGN_LEFT, 2 },
1489 { _T("Snsdiffs"), nullptr, COLHDR_NSDIFFS, COLDESC_NSDIFFS, ColDiffsGet, ColDiffsSort, FIELD_OFFSET(DIFFITEM, nsdiffs), -1, false, DirColInfo::ALIGN_RIGHT },
1490 { _T("Snidiffs"), nullptr, COLHDR_NIDIFFS, COLDESC_NIDIFFS, ColDiffsGet, ColDiffsSort, FIELD_OFFSET(DIFFITEM, nidiffs), -1, false, DirColInfo::ALIGN_RIGHT },
1491 { _T("Leoltype"), nullptr, COLHDR_LEOL_TYPE, COLDESC_LEOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1492 { _T("Meoltype"), nullptr, COLHDR_MEOL_TYPE, COLDESC_MEOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1493 { _T("Reoltype"), nullptr, COLHDR_REOL_TYPE, COLDESC_REOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 2 },
1494 { _T("Unpacker"), nullptr, COLHDR_UNPACKER, COLDESC_UNPACKER, &ColPluginPipelineGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1495 { _T("Prediffer"), nullptr, COLHDR_PREDIFFER, COLDESC_PREDIFFER, &ColPluginPipelineGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1496 #ifdef SHOW_DIFFITEM_DEBUG_INFO
1497 { _T("diffcode"), nullptr, COLHDR_DEBUG_DIFFCODE, COLDESC_DEBUG_DIFFCODE, &ColDebugDiffCodeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT },
1498 { _T("customFlags"), nullptr, COLHDR_DEBUG_CUSTOMFLAGS, COLDESC_DEBUG_CUSTOMFLAGS, &ColDebugCustomFlagsGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT },
1499 { _T("this"), nullptr, COLHDR_DEBUG_THIS, COLDESC_DEBUG_THIS, &ColDebugNodeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1500 { _T("parent"), nullptr, COLHDR_DEBUG_PARENT, COLDESC_DEBUG_PARENT, &ColDebugNodeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1501 { _T("children"), nullptr, COLHDR_DEBUG_CHILDREN, COLDESC_DEBUG_CHILDREN, &ColDebugNodeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 2 },
1502 { _T("Flink"), nullptr, COLHDR_DEBUG_FLINK, COLDESC_DEBUG_FLINK, &ColDebugNodeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 3 },
1503 { _T("Blink"), nullptr, COLHDR_DEBUG_BLINK, COLDESC_DEBUG_BLINK, &ColDebugNodeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 4 },
1504 #endif // SHOW_DIFFITEM_DEBUG_INFO
1507 String DirColInfo::GetDisplayName() const
1510 return tr(idNameContext, idName);
1511 PropertySystem ps({ regName + 1 });
1512 std::vector<String> names;
1513 ps.GetDisplayNames(names);
1514 String name = names[0];
1515 if (regName[0] != 'A')
1519 (regName[0] == 'L') ? _("Left") :
1520 (regName[0] == 'R') ? _("Right") :
1521 (regName[0] == 'M') ? _("Middle") :
1522 (regName[0] == 'D') ? _("Diff") :
1523 (regName[0] == 'l') ? _("Left Duplicate Count") :
1524 (regName[0] == 'r') ? _("Right Duplicate Count") :
1525 (regName[0] == 'm') ? _("Middle Duplicate Count") :
1526 (regName[0] == 'N') ? _("Move") : _("");
1532 String DirColInfo::GetDescription() const
1536 return GetDisplayName();
1539 DirViewColItems::DirViewColItems(int nDirs, const std::vector<String>& additionalPropertyNames) :
1540 m_nDirs(nDirs), m_dispcols(-1)
1542 m_numcols = static_cast<int>(nDirs < 3 ? std::size(f_cols) : std::size(f_cols3));
1543 DirColInfo *pcol = nDirs < 3 ? f_cols : f_cols3;
1544 for (size_t i = 0; i < m_numcols; ++i)
1545 m_cols.push_back(pcol[i]);
1546 ResetColumnOrdering();
1547 PropertySystem ps(additionalPropertyNames);
1548 for (const auto& propertyName : ps.GetCanonicalNames())
1549 AddAdditionalPropertyName(propertyName);
1553 DirViewColItems::AddAdditionalPropertyName(const String& propertyName)
1555 const int nfixedcols = static_cast<int>(m_nDirs < 3 ? std::size(f_cols) : std::size(f_cols3));
1556 int fixedColPhysicalIndexMax = 0;
1557 for (int i = 0; i < nfixedcols; ++i)
1558 fixedColPhysicalIndexMax = (std::max)(fixedColPhysicalIndexMax, m_cols[i].physicalIndex);
1560 String cList = (m_nDirs < 3) ? String(_T("ADLR")) : String(_T("ADLMR"));
1561 if (propertyName.substr(0, 5) == _T("Hash."))
1562 cList += (m_nDirs < 3) ? String(_T("Nlr")) : String(_T("lmr"));
1563 const size_t count = cList.size();
1564 m_cols.reserve(count);
1565 m_invcolorder.reserve(count);
1566 m_colorder.reserve(count);
1567 for (auto c : cList)
1569 m_cols.emplace_back(DirColInfo{});
1570 m_strpool.push_back(c + propertyName);
1571 auto& col = m_cols.back();
1572 col.regName = m_strpool.back().c_str();
1573 col.opt = static_cast<int>(m_additionalPropertyNames.size());
1576 col.physicalIndex = fixedColPhysicalIndexMax + 1 + col.opt;
1578 col.getfnc = ColAllPropertyGet;
1579 col.sortfnc = ColAllPropertySort;
1583 col.physicalIndex = -1;
1585 col.getfnc = ColPropertyDiffGet;
1586 col.sortfnc = ColPropertyDiffSort;
1590 col.physicalIndex = -1;
1592 col.getfnc = ColPropertyMoveGet;
1593 col.sortfnc = ColPropertyMoveSort;
1597 col.physicalIndex = -1;
1598 col.offset = FIELD_OFFSET(DIFFITEM, diffFileInfo[pane]);
1599 if (c == 'L' || c == 'M' || c == 'R')
1601 col.getfnc = ColPropertyGet;
1602 col.sortfnc = ColPropertySort;
1606 col.opt |= pane << 16;
1607 col.getfnc = ColPropertyDuplicateCountGet;
1608 col.sortfnc = ColPropertyDuplicateCountSort;
1610 pane = (pane + 1) % m_nDirs;
1612 m_invcolorder.push_back(-1);
1614 m_invcolorder[m_dispcols] = m_numcols;
1615 m_colorder.push_back(c == 'A' ? m_dispcols++ : -1);
1618 m_additionalPropertyNames.push_back(propertyName);
1622 DirViewColItems::RemoveAdditionalPropertyName(const String& propertyName)
1624 const int nfixedcols = static_cast<int>(m_nDirs < 3 ? std::size(f_cols) : std::size(f_cols3));
1625 std::vector<int> deletedPhysicalIndexes;
1626 std::vector<int> deletedLogicalIndexes;
1627 int deletedOpt = -1;
1628 for (int i = static_cast<int>(m_cols.size()) - 1; i >= nfixedcols; --i)
1630 if (m_cols[i].regName + 1 == propertyName)
1632 deletedLogicalIndexes.push_back(static_cast<int>(i));
1633 if (m_colorder[i] >= 0)
1635 deletedPhysicalIndexes.push_back(m_colorder[i]);
1638 deletedOpt = m_cols[i].opt;
1639 m_cols.erase(m_cols.begin() + i);
1643 for (int i = static_cast<int>(m_invcolorder.size()) - 1; i >= 0; --i)
1645 auto it = std::find(deletedLogicalIndexes.begin(), deletedLogicalIndexes.end(), m_invcolorder[i]);
1646 if (it != deletedLogicalIndexes.end())
1647 m_invcolorder.erase(m_invcolorder.begin() + i);
1650 int logicalIndex = m_invcolorder[i];
1651 for (auto deletedLogicalIndex : deletedLogicalIndexes)
1653 if (deletedLogicalIndex < logicalIndex)
1658 for (int i = static_cast<int>(m_colorder.size()) - 1; i >= 0; --i)
1660 auto it = std::find(deletedLogicalIndexes.begin(), deletedLogicalIndexes.end(), i);
1661 if (it != deletedLogicalIndexes.end())
1662 m_colorder.erase(m_colorder.begin() + i);
1665 int physicalIndex = m_colorder[i];
1666 for (auto deletedPhysicalIndex : deletedPhysicalIndexes)
1668 if (deletedPhysicalIndex < physicalIndex)
1673 for (int i = static_cast<int>(m_cols.size()) - 1; i >= nfixedcols; --i)
1675 if (deletedOpt < m_cols[i].opt)
1678 for (size_t i = 0; i < m_additionalPropertyNames.size(); ++i)
1680 if (m_additionalPropertyNames[i] == propertyName)
1681 m_additionalPropertyNames.erase(m_additionalPropertyNames.begin() + i);
1683 m_colorder.resize(m_numcols);
1684 m_invcolorder.resize(m_numcols);
1685 if (m_dispcols <= 0)
1686 ResetColumnOrdering();
1690 DirViewColItems::SetAdditionalPropertyNames(const std::vector<String>& additionalPropertyNames)
1692 for (int i = static_cast<int>(m_additionalPropertyNames.size()) - 1; i >= 0; i--)
1694 auto it = std::find(additionalPropertyNames.begin(), additionalPropertyNames.end(), m_additionalPropertyNames[i]);
1695 if (it == additionalPropertyNames.end())
1696 RemoveAdditionalPropertyName(m_additionalPropertyNames[i]);
1698 for (const auto& propertyName : additionalPropertyNames)
1700 auto it = std::find(m_additionalPropertyNames.begin(), m_additionalPropertyNames.end(), propertyName);
1701 if (it == m_additionalPropertyNames.end())
1702 AddAdditionalPropertyName(propertyName);
1707 * @brief Registry base value name for saving/loading info for this column
1710 DirViewColItems::GetColRegValueNameBase(int col) const
1712 assert(col>=0 && col<m_numcols);
1713 return strutils::format(_T("WDirHdr_%s"), m_cols[col].regName);
1717 * @brief Get default physical order for specified logical column
1720 DirViewColItems::GetColDefaultOrder(int col) const
1722 assert(col>=0 && col<m_numcols);
1723 return m_cols[col].physicalIndex;
1727 * @brief Return the info about the specified physical column
1730 DirViewColItems::GetDirColInfo(int col) const
1732 if (col < 0 || col >= m_numcols)
1734 assert(false); // fix caller, should not ask for nonexistent columns
1737 return &m_cols[col];
1741 * @brief Check if specified physical column has specified resource id name
1744 DirViewColItems::IsColById(int col, const char *idname) const
1746 if (col < 0 || col >= m_numcols)
1748 assert(false); // fix caller, should not ask for nonexistent columns
1751 return m_cols[col].idName == idname;
1755 * @brief Is specified physical column the name column?
1758 DirViewColItems::IsColName(int col) const
1760 return IsColById(col, COLHDR_FILENAME);
1763 * @brief Is specified physical column the left modification time column?
1766 DirViewColItems::IsColLmTime(int col) const
1768 return IsColById(col, COLHDR_LTIMEM);
1771 * @brief Is specified physical column the middle modification time column?
1774 DirViewColItems::IsColMmTime(int col) const
1776 return IsColById(col, COLHDR_MTIMEM);
1779 * @brief Is specified physical column the right modification time column?
1782 DirViewColItems::IsColRmTime(int col) const
1784 return IsColById(col, COLHDR_RTIMEM);
1787 * @brief Is specified physical column the full status (result) column?
1790 DirViewColItems::IsColStatus(int col) const
1792 return IsColById(col, COLHDR_RESULT);
1795 * @brief Is specified physical column the full status (result) column?
1798 DirViewColItems::IsColStatusAbbr(int col) const
1800 return IsColById(col, COLHDR_RESULT_ABBR);
1804 * @brief return whether column normally sorts ascending (dates do not)
1807 DirViewColItems::IsDefaultSortAscending(int col) const
1809 const DirColInfo * pColInfo = GetDirColInfo(col);
1810 if (pColInfo == nullptr)
1812 assert(false); // fix caller, should not ask for nonexistent columns
1815 return pColInfo->defSortUp;
1819 * @brief Return display name of column
1822 DirViewColItems::GetColDisplayName(int col) const
1824 const DirColInfo * colinfo = GetDirColInfo(col);
1825 return colinfo ? colinfo->GetDisplayName() : _T("");
1829 * @brief Return description of column
1832 DirViewColItems::GetColDescription(int col) const
1834 const DirColInfo * colinfo = GetDirColInfo(col);
1835 return colinfo ? colinfo->GetDescription() : _T("");
1839 * @brief Get text for specified column.
1840 * This function retrieves the text for the specified colum. Text is
1841 * retrieved by using column-specific handler functions.
1842 * @param [in] pCtxt Compare context.
1843 * @param [in] col Column number.
1844 * @param [in] di Difference data.
1845 * @return Text for the specified column.
1848 DirViewColItems::ColGetTextToDisplay(const CDiffContext *pCtxt, int col,
1849 const DIFFITEM &di) const
1851 // Custom properties have custom get functions
1852 const DirColInfo * pColInfo = GetDirColInfo(col);
1853 if (pColInfo == nullptr)
1855 assert(false); // fix caller, should not ask for nonexistent columns
1858 ColGetFncPtrType fnc = pColInfo->getfnc;
1859 size_t offset = pColInfo->offset;
1860 String s = (*fnc)(pCtxt, reinterpret_cast<const char *>(&di) + offset, pColInfo->opt);
1862 // Add '*' to newer time field
1863 if (IsColLmTime(col) || IsColMmTime(col) || IsColRmTime(col))
1867 if (di.diffFileInfo[0].mtime != 0 || di.diffFileInfo[1].mtime != 0)
1871 IsColLmTime(col) && di.diffFileInfo[0].mtime > di.diffFileInfo[1].mtime // Left modification time
1872 || IsColRmTime(col) && di.diffFileInfo[0].mtime < di.diffFileInfo[1].mtime // Right modification time
1875 s.insert(0, _T("* "));
1879 s.insert(0, _T(" ")); // Looks best with a fixed-font, but not too bad otherwise
1881 // GreyMerlin (14 Nov 2009) - the flagging of Date needs to be done with
1882 // something not involving extra characters. Perhaps <red> for oldest,
1883 // <green> for newest. Note (20 March 2017): the introduction of 3-Way
1884 // Merge and the yellow difference highlighting adds to the design
1885 // difficulty of any changes. So maybe this "* "/" " scheme is good enough.
1891 if (di.diffFileInfo[0].mtime != 0 || di.diffFileInfo[1].mtime != 0 || di.diffFileInfo[2].mtime != 0)
1895 IsColLmTime(col) && di.diffFileInfo[0].mtime > di.diffFileInfo[1].mtime && di.diffFileInfo[0].mtime > di.diffFileInfo[2].mtime // Left modification time
1896 || IsColMmTime(col) && di.diffFileInfo[1].mtime > di.diffFileInfo[0].mtime && di.diffFileInfo[1].mtime > di.diffFileInfo[2].mtime // Middle modification time
1897 || IsColRmTime(col) && di.diffFileInfo[2].mtime > di.diffFileInfo[0].mtime && di.diffFileInfo[2].mtime > di.diffFileInfo[1].mtime // Right modification time
1900 s.insert(0, _T("* "));
1904 s.insert(0, _T(" ")); // Looks best with a fixed-font, but not too bad otherwise
1906 // GreyMerlin (14 Nov 2009) - See note above.
1917 * @brief Sort two items on specified column.
1918 * This function determines order of two items in specified column. Order
1919 * is determined by column-specific functions.
1920 * @param [in] pCtxt Compare context.
1921 * @param [in] col Column number to sort.
1922 * @param [in] ldi Left difference item data.
1923 * @param [in] rdi Right difference item data.
1924 * @return Order of items.
1927 DirViewColItems::ColSort(const CDiffContext *pCtxt, int col, const DIFFITEM &ldi,
1928 const DIFFITEM &rdi, bool bTreeMode) const
1930 // Custom properties have custom sort functions
1931 const DirColInfo * pColInfo = GetDirColInfo(col);
1932 if (pColInfo == nullptr)
1934 assert(false); // fix caller, should not ask for nonexistent columns
1937 size_t offset = pColInfo->offset;
1938 int opt = pColInfo->opt;
1943 int lLevel = ldi.GetDepth();
1944 int rLevel = rdi.GetDepth();
1945 const DIFFITEM *lcur = &ldi, *rcur = &rdi;
1946 if (lLevel < rLevel)
1948 for (; lLevel != rLevel; rLevel--)
1949 rcur = rcur->GetParentLink();
1951 else if (rLevel < lLevel)
1953 for (; lLevel != rLevel; lLevel--)
1954 lcur = lcur->GetParentLink();
1956 while (lcur->GetParentLink() != rcur->GetParentLink())
1958 lcur = lcur->GetParentLink();
1959 rcur = rcur->GetParentLink();
1961 arg1 = reinterpret_cast<const char *>(lcur) + offset;
1962 arg2 = reinterpret_cast<const char *>(rcur) + offset;
1966 arg1 = reinterpret_cast<const char *>(&ldi) + offset;
1967 arg2 = reinterpret_cast<const char *>(&rdi) + offset;
1969 if (ColSortFncPtrType fnc = pColInfo->sortfnc)
1971 return (*fnc)(pCtxt, arg1, arg2, opt);
1973 if (ColGetFncPtrType fnc = pColInfo->getfnc)
1975 String p = (*fnc)(pCtxt, arg1, opt);
1976 String q = (*fnc)(pCtxt, arg2, opt);
1977 return strutils::compare_logical(p, q);
1982 void DirViewColItems::SetColumnOrdering(const int colorder[])
1985 for (int i = 0; i < m_numcols; ++i)
1987 m_colorder[i] = colorder[i];
1988 int phy = m_colorder[i];
1992 m_invcolorder[phy] = i;
1998 * @brief Set column ordering to default initial order
2000 void DirViewColItems::ResetColumnOrdering()
2002 ClearColumnOrders();
2004 for (int i=0; i<m_numcols; ++i)
2006 int phy = GetColDefaultOrder(i);
2007 m_colorder[i] = phy;
2010 m_invcolorder[phy] = i;
2017 * @brief Reset all current column ordering information
2019 void DirViewColItems::ClearColumnOrders()
2021 m_colorder.resize(m_numcols);
2022 m_invcolorder.resize(m_numcols);
2023 for (int i=0; i<m_numcols; ++i)
2026 m_invcolorder[i] = -1;
2031 * @brief Remove any windows reordering of columns (params are physical columns)
2033 void DirViewColItems::MoveColumn(int psrc, int pdest)
2035 // actually moved column
2036 m_colorder[m_invcolorder[psrc]] = pdest;
2037 // shift all other affected columns
2038 int dir = psrc > pdest ? +1 : -1;
2040 for (i=pdest; i!=psrc; i += dir)
2042 m_colorder[m_invcolorder[i]] = i+dir;
2044 // fix inverse mapping
2045 for (i=0; i<m_numcols; ++i)
2047 if (m_colorder[i] >= 0)
2048 m_invcolorder[m_colorder[i]] = i;
2053 * @brief Resets column widths to defaults.
2055 String DirViewColItems::ResetColumnWidths(int defcolwidth)
2058 for (int i = 0; i < m_numcols; i++)
2060 if (!result.empty()) result += ' ';
2061 result += strutils::to_str(defcolwidth);
2067 * @brief Load column orders from registry
2069 void DirViewColItems::LoadColumnOrders(const String& colOrders)
2071 ClearColumnOrders();
2073 std::basic_istringstream<tchar_t> ss(colOrders);
2075 // Load column orders
2076 // Break out if one is missing
2077 // Break out & mark failure (m_dispcols == -1) if one is invalid
2079 for (i=0; i<m_numcols; ++i)
2083 if (ord<-1 || ord >= m_numcols)
2085 m_colorder[i] = ord;
2089 if (m_invcolorder[ord] != -1)
2094 m_invcolorder[ord] = i;
2097 // Check that a contiguous range was set
2098 for (i=0; i<m_dispcols; ++i)
2100 if (m_invcolorder[i] < 0)
2106 // Must have at least one column
2109 ResetColumnOrdering();
2113 /// store current column orders into registry
2114 String DirViewColItems::SaveColumnOrders()
2116 assert(static_cast<int>(m_colorder.size()) == m_numcols);
2117 assert(static_cast<int>(m_invcolorder.size()) == m_numcols);
2118 return strutils::join<String (*)(int)>(m_colorder.begin(), m_colorder.end(), _T(" "), strutils::to_str);