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");
65 const char *COLDESC_FILENAME = N_("Filename or folder name.");
66 const char *COLDESC_DIR = N_("Subfolder name when subfolders are included.");
67 const char *COLDESC_RESULT = N_("Comparison result, long form.");
68 const char *COLDESC_LTIMEM = N_("Left side modification date.");
69 const char *COLDESC_RTIMEM = N_("Right side modification date.");
70 const char *COLDESC_MTIMEM = N_("Middle side modification date.");
71 const char *COLDESC_EXTENSION = N_("File's extension.");
72 const char *COLDESC_LSIZE = N_("Left file size in bytes.");
73 const char *COLDESC_RSIZE = N_("Right file size in bytes.");
74 const char *COLDESC_MSIZE = N_("Middle file size in bytes.");
75 const char *COLDESC_LSIZE_SHORT = N_("Left file size abbreviated.");
76 const char *COLDESC_RSIZE_SHORT = N_("Right file size abbreviated.");
77 const char *COLDESC_MSIZE_SHORT = N_("Middle file size abbreviated.");
78 const char *COLDESC_LTIMEC = N_("Left side creation time.");
79 const char *COLDESC_RTIMEC = N_("Right side creation time.");
80 const char *COLDESC_MTIMEC = N_("Middle side creation time.");
81 const char *COLDESC_NEWER = N_("Tells which side has newer modification date.");
82 const char *COLDESC_LVERSION = N_("Left side file version, only for some filetypes.");
83 const char *COLDESC_RVERSION = N_("Right side file version, only for some filetypes.");
84 const char *COLDESC_MVERSION = N_("Middle side file version, only for some filetypes.");
85 const char *COLDESC_RESULT_ABBR = N_("Short comparison result.");
86 const char *COLDESC_LATTRIBUTES = N_("Left side attributes.");
87 const char *COLDESC_RATTRIBUTES = N_("Right side attributes.");
88 const char *COLDESC_MATTRIBUTES = N_("Middle side attributes.");
89 const char *COLDESC_LEOL_TYPE = N_("Left side file EOL type.");
90 const char *COLDESC_REOL_TYPE = N_("Right side file EOL type.");
91 const char *COLDESC_MEOL_TYPE = N_("Middle side file EOL type.");
92 const char *COLDESC_LENCODING = N_("Left side encoding.");
93 const char *COLDESC_RENCODING = N_("Right side encoding.");
94 const char *COLDESC_MENCODING = N_("Middle side encoding.");
95 const char *COLDESC_NIDIFFS = N_("Number of ignored differences in file. These differences are ignored by WinMerge and cannot be merged.");
96 const char *COLDESC_NSDIFFS = N_("Number of differences in file. This number does not include ignored differences.");
97 const char *COLDESC_BINARY = N_("Shows an asterisk (*) if the file is binary.");
98 const char *COLDESC_UNPACKER = N_("Unpacker plugin name or pipeline.");
99 const char *COLDESC_PREDIFFER = N_("Prediffer plugin name or pipeline.");
103 * @brief Function to compare two int64_t's for a sort
105 static int cmp64(int64_t i1, int64_t i2)
107 if (i1==i2) return 0;
108 return i1>i2 ? 1 : -1;
112 * @brief Function to compare two uint64_t's for a sort
114 static int cmpu64(uint64_t i1, uint64_t i2)
116 if (i1==i2) return 0;
117 return i1>i2 ? 1 : -1;
120 * @brief Convert int64_t to int sign
122 static int sign64(int64_t val)
125 if (val<0) return -1;
129 * @brief Function to compare two diffcodes for a sort
130 * @todo How shall we order diff statuses?
132 static int cmpdiffcode(unsigned diffcode1, unsigned diffcode2)
134 bool same1 = (diffcode1 & DIFFCODE::COMPAREFLAGS) == DIFFCODE::SAME;
135 bool same2 = (diffcode2 & DIFFCODE::COMPAREFLAGS) == DIFFCODE::SAME;
140 return diffcode1 - diffcode2;
143 * @brief Function to compare two doubles for a sort
145 static int cmpfloat(double v1, double v2)
154 * @brief Formats a size as a short string.
156 * MakeShortSize(500) = "500b"
157 * MakeShortSize(1024) = "1Kb"
158 * MakeShortSize(12000) = "1.7Kb"
159 * MakeShortSize(200000) = "195Kb"
160 * @param [in] size File's size to convert.
161 * @return Size string with localized suffix.
162 * @note Localized suffix strings are read from resource.
163 * @todo Can't handle > terabyte filesizes.
165 static String MakeShortSize(int64_t size)
169 return strutils::format(_T("%d B"), static_cast<int>(size));
171 StrFormatByteSize64(size, buffer, static_cast<unsigned>(std::size(buffer)));
176 * @name Functions to format content of each type of column.
177 * These functions all receive two parameters, a pointer to CDiffContext.
178 * which contains general compare information. And a void pointer whose type
179 * depends on column to format. Function to call for each column, and
180 * parameter for the function are defined in static DirColInfo f_cols table.
184 * @brief Format Filename column data.
185 * @param [in] pCtxt Pointer to compare context.
186 * @param [in] p Pointer to DIFFITEM.
187 * @return String to show in the column.
190 static Type ColFileNameGet(const CDiffContext * pCtxt, const void *p, int) //sfilename
192 assert(pCtxt != nullptr && p != nullptr);
194 int nDirs = pCtxt->GetCompareDirs();
196 const DIFFITEM* pDiffItem = static_cast<const DIFFITEM*>(p);
197 const DiffFileInfo* pDiffFileInfo = pDiffItem->diffFileInfo;
200 for (int i = 0; i < nDirs; i++)
201 bExist[i] = (pDiffItem->diffcode.exists(i) && (!pDiffFileInfo[i].filename.get().empty()));
203 bool bIsSameName = true;
205 for (int i = 0; i < nDirs; i++)
211 else if (pDiffFileInfo[i].filename != pDiffFileInfo[index].filename)
223 return pDiffFileInfo[index].filename;
228 return static_cast<Type>(pDiffFileInfo[0].filename.get() + _T("|") + pDiffFileInfo[1].filename.get());
231 String none = _("<None>");
232 return static_cast<Type>((bExist[0] ? pDiffFileInfo[0].filename.get() : none) + _T("|")
233 + (bExist[1] ? pDiffFileInfo[1].filename.get() : none) + _T("|")
234 + (bExist[2] ? pDiffFileInfo[2].filename.get() : none));
240 * @brief Format Extension column data.
241 * @param [in] p Pointer to DIFFITEM.
242 * @return String to show in the column.
244 static String ColExtGet(const CDiffContext *, const void *p, int) //sfilename
246 const DIFFITEM &di = *static_cast<const DIFFITEM*>(p);
247 // We don't show extension for folder names
248 if (di.diffcode.isDirectory())
250 const String &r = di.diffFileInfo[0].filename;
251 String s = paths::FindExtension(r);
252 return s.c_str() + _tcsspn(s.c_str(), _T("."));
256 * @brief Format Folder column data.
257 * @param [in] p Pointer to DIFFITEM.
258 * @return String to show in the column.
260 static String ColPathGet(const CDiffContext *, const void *p, int)
262 const DIFFITEM &di = *static_cast<const DIFFITEM*>(p);
263 String s = di.diffFileInfo[1].path;
264 const String &t = di.diffFileInfo[0].path;
266 // If we have unique path, just print the existing path name
267 if (s.length() == 0 || t.length() == 0)
278 const TCHAR *pi = _tcschr(s.c_str() + i, '\\');
279 const TCHAR *pj = _tcschr(t.c_str() + j, '\\');
280 size_t i_ahead = (pi != nullptr ? pi - s.c_str() : std::string::npos);
281 size_t j_ahead = (pj != nullptr ? pj - t.c_str() : std::string::npos);
282 size_t length_s = ((i_ahead != std::string::npos ? i_ahead : s.length()) - i);
283 size_t length_t = ((j_ahead != std::string::npos ? j_ahead : t.length()) - j);
284 if (length_s != length_t ||
285 memcmp(s.c_str() + i, t.c_str() + j, length_s) != 0)
287 String u(t.c_str() + j, length_t + 1);
290 i_ahead += u.length();
301 * @brief Format Result column data.
302 * @param [in] pCtxt Pointer to compare context.
303 * @param [in] p Pointer to DIFFITEM.
304 * @return String to show in the column.
306 static String ColStatusGet(const CDiffContext *pCtxt, const void *p, int)
308 const DIFFITEM &di = *static_cast<const DIFFITEM*>(p);
309 int nDirs = pCtxt->GetCompareDirs();
310 // Note that order of items does matter. We must check for
311 // skipped items before unique items, for example, so that
312 // skipped unique items are labeled as skipped, not unique.
314 if (di.diffcode.isResultError())
316 s = _("Unable to compare files");
318 else if (di.diffcode.isResultAbort())
320 s = _("Item aborted");
322 else if (di.diffcode.isResultFiltered())
324 if (di.diffcode.isDirectory())
325 s = _("Folder skipped");
327 s = _("File skipped");
329 else if (di.diffcode.isSideFirstOnly())
331 s = strutils::format_string1(_("Left only: %1"),
332 di.getFilepath(0, pCtxt->GetNormalizedLeft()));
334 else if (di.diffcode.isSideSecondOnly())
338 s = strutils::format_string1(_("Right only: %1"),
339 di.getFilepath(1, pCtxt->GetNormalizedRight()));
343 s = strutils::format_string1(_("Middle only: %1"),
344 di.getFilepath(1, pCtxt->GetNormalizedMiddle()));
347 else if (di.diffcode.isSideThirdOnly())
349 s = strutils::format_string1(_("Right only: %1"),
350 di.getFilepath(2, pCtxt->GetNormalizedRight()));
352 else if (nDirs > 2 && !di.diffcode.existsFirst())
354 s = strutils::format_string1(_("Does not exist in %1"),
355 pCtxt->GetNormalizedLeft());
357 else if (nDirs > 2 && !di.diffcode.existsSecond())
359 s = strutils::format_string1(_("Does not exist in %1"),
360 pCtxt->GetNormalizedMiddle());
362 else if (nDirs > 2 && !di.diffcode.existsThird())
364 s = strutils::format_string1(_("Does not exist in %1"),
365 pCtxt->GetNormalizedRight());
367 else if (di.diffcode.isResultSame())
369 if (di.diffcode.isText())
370 s = _("Text files are identical");
371 else if (di.diffcode.isBin())
372 s = _("Binary files are identical");
373 else if (di.diffcode.isImage())
374 s = _("Image files are identical");
378 else if (di.diffcode.isResultDiff()) // diff
380 if (di.diffcode.isText())
381 s = _("Text files are different");
382 else if (di.diffcode.isBin())
383 s = _("Binary files are different");
384 else if (di.diffcode.isImage())
385 s = _("Image files are different");
386 else if (di.diffcode.isDirectory())
387 s = _("Folders are different");
389 s = _("Files are different");
392 switch (di.diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY)
394 case DIFFCODE::DIFF1STONLY: s += _("(Middle and right are identical)"); break;
395 case DIFFCODE::DIFF2NDONLY: s += _("(Left and right are identical)"); break;
396 case DIFFCODE::DIFF3RDONLY: s += _("(Left and middle are identical)"); break;
404 * @brief Format Date column data.
405 * @param [in] p Pointer to integer (seconds since 1.1.1970).
406 * @return String to show in the column.
408 static String ColTimeGet(const CDiffContext *, const void *p, int)
410 const int64_t r = *static_cast<const int64_t*>(p) / Timestamp::resolution();
412 return locality::TimeString(&r);
418 * @brief Format Sizw column data.
419 * @param [in] p Pointer to integer containing size in bytes.
420 * @return String to show in the column.
422 static String ColSizeGet(const CDiffContext *, const void *p, int)
424 const int64_t &r = *static_cast<const int64_t*>(p);
428 s = locality::NumToLocaleStr(r);
434 * @brief Format Folder column data.
435 * @param [in] p Pointer to DIFFITEM.
436 * @return String to show in the column.
438 static String ColSizeShortGet(const CDiffContext *, const void *p, int)
440 const int64_t &r = *static_cast<const int64_t*>(p);
444 s = MakeShortSize(r);
450 * @brief Format Difference cout column data.
451 * @param [in] p Pointer to integer having count of differences.
452 * @return String to show in the column.
454 static String ColDiffsGet(const CDiffContext *, const void *p, int)
456 const int &r = *static_cast<const int*>(p);
458 if (r == CDiffContext::DIFFS_UNKNOWN_QUICKCOMPARE)
459 { // QuickCompare, unknown
462 else if (r == CDiffContext::DIFFS_UNKNOWN)
468 s = locality::NumToLocaleStr(r);
474 * @brief Format Newer/Older column data.
475 * @param [in] p Pointer to DIFFITEM.
476 * @return String to show in the column.
478 static String ColNewerGet(const CDiffContext *pCtxt, const void *p, int)
480 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
481 if (pCtxt->GetCompareDirs() < 3)
483 if (di.diffcode.isSideFirstOnly())
487 if (di.diffcode.isSideSecondOnly())
491 if (di.diffFileInfo[0].mtime != 0 && di.diffFileInfo[1].mtime != 0)
493 if (di.diffFileInfo[0].mtime > di.diffFileInfo[1].mtime)
497 if (di.diffFileInfo[0].mtime < di.diffFileInfo[1].mtime)
508 int sortno[3] = {0, 1, 2};
509 Timestamp sorttime[3] = {di.diffFileInfo[0].mtime, di.diffFileInfo[1].mtime, di.diffFileInfo[2].mtime};
510 for (int i = 0; i < 3; i++)
512 for (int j = i; j < 3; j++)
514 if (sorttime[i] < sorttime[j])
516 swap(sorttime[i], sorttime[j]);
517 swap(sortno[i], sortno[j]);
521 res = _T("LMR")[sortno[0]];
522 res += sorttime[0] == sorttime[1] ? _T("==") : _T("<-");
523 res += _T("LMR")[sortno[1]];
524 res += sorttime[1] == sorttime[2] ? _T("==") : _T("<-");
525 res += _T("LMR")[sortno[2]];
531 * @brief Format Version info to string.
532 * @param [in] pCtxt Pointer to compare context.
533 * @param [in] pdi Pointer to DIFFITEM.
534 * @param [in] bLeft Is the item left-size item?
535 * @return String proper to show in the GUI.
537 static String GetVersion(const CDiffContext * pCtxt, const DIFFITEM *pdi, int nIndex)
539 DIFFITEM &di = const_cast<DIFFITEM &>(*pdi);
540 DiffFileInfo & dfi = di.diffFileInfo[nIndex];
541 if (dfi.version.IsCleared())
543 pCtxt->UpdateVersion(di, nIndex);
545 return dfi.version.GetFileVersionString();
548 static uint64_t GetVersionQWORD(const CDiffContext * pCtxt, const DIFFITEM *pdi, int nIndex)
550 DIFFITEM &di = const_cast<DIFFITEM &>(*pdi);
551 DiffFileInfo & dfi = di.diffFileInfo[nIndex];
552 if (dfi.version.IsCleared())
554 pCtxt->UpdateVersion(di, nIndex);
556 return dfi.version.GetFileVersionQWORD();
560 * @brief Format Version column data
561 * @param [in] pCtxt Pointer to compare context.
562 * @param [in] p Pointer to DIFFITEM.
563 * @return String to show in the column.
565 static String ColVersionGet(const CDiffContext * pCtxt, const void *p, int opt)
567 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
568 return GetVersion(pCtxt, &di, opt);
572 * @brief Format Short Result column data.
573 * @param [in] p Pointer to DIFFITEM.
574 * @return String to show in the column.
576 static String ColStatusAbbrGet(const CDiffContext *pCtxt, const void *p, int opt)
578 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
580 int nDirs = pCtxt->GetCompareDirs();
582 // Note that order of items does matter. We must check for
583 // skipped items before unique items, for example, so that
584 // skipped unique items are labeled as skipped, not unique.
585 if (di.diffcode.isResultError())
589 else if (di.diffcode.isResultAbort())
591 id = N_("Item aborted");
593 else if (di.diffcode.isResultFiltered())
595 if (di.diffcode.isDirectory())
596 id = N_("Folder skipped");
598 id = N_("File skipped");
600 else if (di.diffcode.isSideFirstOnly())
602 id = N_("Left Only");
604 else if (di.diffcode.isSideSecondOnly())
607 id = N_("Right Only");
609 id = N_("Middle Only");
611 else if (di.diffcode.isSideThirdOnly())
613 id = N_("Right Only");
615 else if (nDirs > 2 && !di.diffcode.existsFirst())
617 id = N_("No item in left");
619 else if (nDirs > 2 && !di.diffcode.existsSecond())
621 id = N_("No item in middle");
623 else if (nDirs > 2 && !di.diffcode.existsThird())
625 id = N_("No item in right");
627 else if (di.diffcode.isResultSame())
629 id = N_("Identical");
631 else if (di.diffcode.isResultDiff())
633 id = N_("Different");
636 return id ? tr(id) : _T("");
640 * @brief Format Binary column data.
641 * @param [in] p Pointer to DIFFITEM.
642 * @return String to show in the column.
644 static String ColBinGet(const CDiffContext *, const void *p, int)
646 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
648 if (di.diffcode.isBin())
655 * @brief Format File Attributes column data.
656 * @param [in] p Pointer to file flags class.
657 * @return String to show in the column.
659 static String ColAttrGet(const CDiffContext *, const void *p, int)
661 const FileFlags &r = *static_cast<const FileFlags *>(p);
666 * @brief Format File Encoding column data.
667 * @param [in] p Pointer to file information.
668 * @return String to show in the column.
670 static String ColEncodingGet(const CDiffContext *, const void *p, int)
672 const DiffFileInfo &r = *static_cast<const DiffFileInfo *>(p);
673 return r.encoding.GetName();
677 * @brief Format EOL type to string.
678 * @param [in] p Pointer to DIFFITEM.
679 * @param [in] bLeft Are we formatting left-side file's data?
680 * @return EOL type as as string.
682 static String GetEOLType(const CDiffContext *, const void *p, int index)
684 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
685 const DiffFileInfo & dfi = di.diffFileInfo[index];
686 const FileTextStats &stats = dfi.m_textStats;
688 if (stats.ncrlfs == 0 && stats.ncrs == 0 && stats.nlfs == 0)
692 if (di.diffcode.isBin())
694 return tr("EOL Type", "Binary");
698 if (stats.ncrlfs > 0 && stats.ncrs == 0 && stats.nlfs == 0)
702 else if (stats.ncrlfs == 0 && stats.ncrs > 0 && stats.nlfs == 0)
706 else if (stats.ncrlfs == 0 && stats.ncrs == 0 && stats.nlfs > 0)
712 return strutils::format(_T("%s:%d/%d/%d"),
714 stats.ncrlfs, stats.ncrs, stats.nlfs);
721 * @brief Format EOL type column data
722 * @param [in] pCtxt Pointer to compare context.
723 * @param [in] p Pointer to DIFFITEM.
724 * @return String to show in the column.
726 static String ColEOLTypeGet(const CDiffContext * pCtxt, const void *p, int opt)
728 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
729 return GetEOLType(pCtxt, &di, opt);
732 static String ColPluginPipelineGet(const CDiffContext* pCtxt, const void *p, int opt)
734 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
735 if (di.diffcode.isDirectory())
737 PackingInfo* pInfoUnpacker = nullptr;
738 PrediffingInfo * pInfoPrediffer = nullptr;
739 String filteredFilenames = pCtxt->GetFilteredFilenames(di);
740 const_cast<CDiffContext *>(pCtxt)->FetchPluginInfos(filteredFilenames, &pInfoUnpacker, &pInfoPrediffer);
742 return pInfoUnpacker ? pInfoUnpacker->GetPluginPipeline() : _T("");
744 return pInfoPrediffer ? pInfoPrediffer->GetPluginPipeline() : _T("");
747 static String ColPropertyGet(const CDiffContext *pCtxt, const void *p, int opt)
749 const DiffFileInfo &dfi = *static_cast<const DiffFileInfo *>(p);
750 PropertyValues* pprops = dfi.m_pAdditionalProperties.get();
751 return (pprops != nullptr && opt < pprops->GetSize()) ? pCtxt->m_pPropertySystem->FormatPropertyValue(*pprops, opt) : _T("");
754 static const DuplicateInfo *GetDuplicateInfo(const CDiffContext* pCtxt, const DiffFileInfo& dfi, int index)
756 PropertyValues* pprops = dfi.m_pAdditionalProperties.get();
757 if (!pprops || index >= pprops->GetSize() || !pprops->IsHashValue(index) || pCtxt->m_duplicateValues.empty())
759 auto it = pCtxt->m_duplicateValues[index].find(pprops->GetHashValue(index));
760 if (it == pCtxt->m_duplicateValues[index].end())
762 return &(it->second);
765 static String ColPropertyDuplicateCountGet(const CDiffContext *pCtxt, const void *p, int opt)
767 const int index = opt & 0xffff;
768 const int pane = opt >> 16;
769 const DiffFileInfo &dfi = *static_cast<const DiffFileInfo *>(p);
770 const DuplicateInfo *info = GetDuplicateInfo(pCtxt, dfi, index);
771 return (!info || info->count[pane] <= 1) ? _T("") :
772 strutils::format(_("%s: %d"), strutils::format(_("Group%d"), info->groupid), info->count[pane] - 1);
775 static String ColAllPropertyGet(const CDiffContext *pCtxt, const void *p, int opt)
777 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
779 PropertyValues* pFirstProps = di.diffFileInfo[0].m_pAdditionalProperties.get();
780 for (int i = 1; i < pCtxt->GetCompareDirs(); ++i)
782 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
783 if (pFirstProps && pprops)
785 if (PropertyValues::CompareValues(*pFirstProps, *pprops, opt) != 0)
790 return pFirstProps ? pCtxt->m_pPropertySystem->FormatPropertyValue(*pFirstProps, opt) : _T("");
792 std::vector<String> values;
793 for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
795 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
796 if (pCtxt->GetCompareDirs() == 3 || di.diffcode.exists(i))
797 values.push_back(pprops ? pCtxt->m_pPropertySystem->FormatPropertyValue(*pprops, opt) : _T(""));
799 return strutils::join(values.begin(), values.end(), _T("|"));
802 static String ColPropertyDiffGetEx(const CDiffContext *pCtxt, const void *p, int opt, bool addNumDiff, bool& numeric, int64_t *pnumdiff)
804 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
805 if (di.diffcode.isDirectory())
807 if (di.diffcode.isResultError())
809 if (di.diffcode.isResultAbort())
810 return _("Item aborted");
811 if (di.diffcode.isResultFiltered())
812 return _("File skipped");
813 PropertyValues* pFirstProps = di.diffFileInfo[0].m_pAdditionalProperties.get();
819 const int nDirs = pCtxt->GetCompareDirs();
820 for (int i = 1; i < nDirs; ++i)
822 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
823 if (pFirstProps && pprops)
825 diff = PropertyValues::DiffValues(*pFirstProps, *pprops, opt, numeric);
833 if (!di.diffcode.existAll())
835 bool allempty = true;
836 for (int i = 0; i < nDirs; ++i)
838 if (di.diffcode.exists(i))
840 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
841 if (pprops && !pprops->IsEmptyValue(opt))
847 if (di.diffcode.isSideFirstOnly())
848 result = _("Left Only");
849 else if (di.diffcode.isSideSecondOnly())
852 result = _("Right Only");
854 result = _("Middle Only");
856 else if (di.diffcode.isSideThirdOnly())
857 result = _("Right Only");
858 else if (nDirs > 2 && !di.diffcode.existsFirst())
859 result = _("No item in left");
860 else if (nDirs > 2 && !di.diffcode.existsSecond())
861 result = _("No item in middle");
862 else if (nDirs > 2 && !di.diffcode.existsThird())
863 result = _("No item in right");
868 result = !equal ? _("Different") :
869 (!pFirstProps->IsEmptyValue(opt) ? _("Identical") : _T(""));
871 if (nDirs == 2 && numeric && addNumDiff)
872 result += strutils::format(_T(" (%+ld)"), diff);
876 static String ColPropertyDiffGet(const CDiffContext* pCtxt, const void* p, int opt)
878 bool numeric = false;
879 return ColPropertyDiffGetEx(pCtxt, p, opt, true, numeric, nullptr);
882 static String ColPropertyMoveGet(const CDiffContext *pCtxt, const void *p, int opt)
884 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
885 if (di.diffcode.isDirectory())
887 std::vector<String> list;
888 const int nDirs = pCtxt->GetCompareDirs();
889 for (int i = 0; i < nDirs; ++i)
891 if (di.diffcode.exists(i))
893 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
894 if (pprops && !pCtxt->m_duplicateValues.empty())
896 auto it = pCtxt->m_duplicateValues[opt].find(pprops->GetHashValue(opt));
897 if (it != pCtxt->m_duplicateValues[opt].end())
899 if (it->second.groupid != 0 && it->second.nonpaired)
902 for (int j = 0; j < nDirs; ++j)
904 if (it->second.count[j] > 0)
908 list.push_back(strutils::format(_("Group%d"), it->second.groupid));
914 return strutils::join(list.begin(), list.end(), _T("|"));
922 * @name Functions to sort each type of column info.
923 * These functions are used to sort information in folder compare GUI. Each
924 * column info (type) has its own function to compare the data. Each
925 * function receives three parameters:
926 * - pointer to compare context
927 * - two parameters for data to compare (type varies)
928 * Return value is -1, 0, or 1, where 0 means datas are identical.
932 * @brief Compare file names.
933 * @param [in] pCtxt Pointer to compare context.
934 * @param [in] p Pointer to DIFFITEM having first name to compare.
935 * @param [in] q Pointer to DIFFITEM having second name to compare.
936 * @return Compare result.
938 static int ColFileNameSort(const CDiffContext *pCtxt, const void *p, const void *q, int)
940 const DIFFITEM &ldi = *static_cast<const DIFFITEM *>(p);
941 const DIFFITEM &rdi = *static_cast<const DIFFITEM *>(q);
942 if (ldi.diffcode.isDirectory() && !rdi.diffcode.isDirectory())
944 if (!ldi.diffcode.isDirectory() && rdi.diffcode.isDirectory())
946 return strutils::compare_nocase(ColFileNameGet<boost::flyweight<String> >(pCtxt, p, 0), ColFileNameGet<boost::flyweight<String> >(pCtxt, q, 0));
950 * @brief Compare file name extensions.
951 * @param [in] pCtxt Pointer to compare context.
952 * @param [in] p First file name extension to compare.
953 * @param [in] q Second file name extension to compare.
954 * @return Compare result.
956 static int ColExtSort(const CDiffContext *pCtxt, const void *p, const void *q, int)
958 const DIFFITEM &ldi = *static_cast<const DIFFITEM *>(p);
959 const DIFFITEM &rdi = *static_cast<const DIFFITEM *>(q);
960 if (ldi.diffcode.isDirectory() && !rdi.diffcode.isDirectory())
962 if (!ldi.diffcode.isDirectory() && rdi.diffcode.isDirectory())
964 return strutils::compare_nocase(ColExtGet(pCtxt, p, 0), ColExtGet(pCtxt, q, 0));
968 * @brief Compare folder names.
969 * @param [in] pCtxt Pointer to compare context.
970 * @param [in] p Pointer to DIFFITEM having first folder name to compare.
971 * @param [in] q Pointer to DIFFITEM having second folder name to compare.
972 * @return Compare result.
974 static int ColPathSort(const CDiffContext *pCtxt, const void *p, const void *q, int)
976 return strutils::compare_nocase(ColPathGet(pCtxt, p, 0), ColPathGet(pCtxt, q, 0));
980 * @brief Compare compare results.
981 * @param [in] p Pointer to DIFFITEM having first result to compare.
982 * @param [in] q Pointer to DIFFITEM having second result to compare.
983 * @return Compare result.
985 static int ColStatusSort(const CDiffContext *, const void *p, const void *q, int)
987 const DIFFITEM &ldi = *static_cast<const DIFFITEM *>(p);
988 const DIFFITEM &rdi = *static_cast<const DIFFITEM *>(q);
989 return cmpdiffcode(rdi.diffcode.diffcode, ldi.diffcode.diffcode);
993 * @brief Compare file times.
994 * @param [in] p First time to compare.
995 * @param [in] q Second time to compare.
996 * @return Compare result.
998 static int ColTimeSort(const CDiffContext *, const void *p, const void *q, int)
1000 const int64_t &r = *static_cast<const int64_t*>(p);
1001 const int64_t &s = *static_cast<const int64_t*>(q);
1006 * @brief Compare file sizes.
1007 * @param [in] p First size to compare.
1008 * @param [in] q Second size to compare.
1009 * @return Compare result.
1011 static int ColSizeSort(const CDiffContext *, const void *p, const void *q, int)
1013 const int64_t &r = *static_cast<const int64_t*>(p);
1014 const int64_t &s = *static_cast<const int64_t*>(q);
1019 * @brief Compare difference counts.
1020 * @param [in] p First count to compare.
1021 * @param [in] q Second count to compare.
1022 * @return Compare result.
1024 static int ColDiffsSort(const CDiffContext *, const void *p, const void *q, int)
1026 const int &r = *static_cast<const int*>(p);
1027 const int &s = *static_cast<const int*>(q);
1032 * @brief Compare newer/older statuses.
1033 * @param [in] pCtxt Pointer to compare context.
1034 * @param [in] p Pointer to DIFFITEM having first status to compare.
1035 * @param [in] q Pointer to DIFFITEM having second status to compare.
1036 * @return Compare result.
1038 static int ColNewerSort(const CDiffContext *pCtxt, const void *p, const void *q, int)
1040 return ColNewerGet(pCtxt, p, 0).compare(ColNewerGet(pCtxt, q, 0));
1044 * @brief Compare file versions.
1045 * @param [in] pCtxt Pointer to compare context.
1046 * @param [in] p Pointer to DIFFITEM having first version to compare.
1047 * @param [in] q Pointer to DIFFITEM having second version to compare.
1048 * @return Compare result.
1050 static int ColVersionSort(const CDiffContext *pCtxt, const void *p, const void *q, int opt)
1052 return cmpu64(GetVersionQWORD(pCtxt, reinterpret_cast<const DIFFITEM *>(p), opt), GetVersionQWORD(pCtxt, reinterpret_cast<const DIFFITEM *>(q), opt));
1056 * @brief Compare binary statuses.
1057 * This function returns a comparison of binary results.
1058 * @param [in] p Pointer to DIFFITEM having first status to compare.
1059 * @param [in] q Pointer to DIFFITEM having second status to compare.
1060 * @return Compare result:
1061 * - if both items are text files or binary files: 0
1062 * - if left is text and right is binary: -1
1063 * - if left is binary and right is text: 1
1065 static int ColBinSort(const CDiffContext *, const void *p, const void *q, int)
1067 const DIFFITEM &ldi = *static_cast<const DIFFITEM *>(p);
1068 const DIFFITEM &rdi = *static_cast<const DIFFITEM *>(q);
1069 const bool i = ldi.diffcode.isBin();
1070 const bool j = rdi.diffcode.isBin();
1083 * @brief Compare file flags.
1084 * @param [in] p Pointer to first flag structure to compare.
1085 * @param [in] q Pointer to second flag structure to compare.
1086 * @return Compare result.
1088 static int ColAttrSort(const CDiffContext *, const void *p, const void *q, int)
1090 const FileFlags &r = *static_cast<const FileFlags *>(p);
1091 const FileFlags &s = *static_cast<const FileFlags *>(q);
1092 if (r.attributes == s.attributes)
1094 return r.attributes < s.attributes ? -1 : 1;
1098 * @brief Compare file encodings.
1099 * @param [in] p Pointer to first structure to compare.
1100 * @param [in] q Pointer to second structure to compare.
1101 * @return Compare result.
1103 static int ColEncodingSort(const CDiffContext *, const void *p, const void *q, int)
1105 const DiffFileInfo &r = *static_cast<const DiffFileInfo *>(p);
1106 const DiffFileInfo &s = *static_cast<const DiffFileInfo *>(q);
1107 return FileTextEncoding::Collate(r.encoding, s.encoding);
1111 * @brief Compare property values
1112 * @param [in] p Pointer to first structure to compare.
1113 * @param [in] q Pointer to second structure to compare.
1114 * @return Compare result.
1116 static int ColPropertySort(const CDiffContext *, const void *p, const void *q, int opt)
1118 const DiffFileInfo &r = *static_cast<const DiffFileInfo *>(p);
1119 const DiffFileInfo &s = *static_cast<const DiffFileInfo *>(q);
1120 if (!r.m_pAdditionalProperties && s.m_pAdditionalProperties)
1122 if (r.m_pAdditionalProperties && !s.m_pAdditionalProperties)
1124 if (!r.m_pAdditionalProperties && !s.m_pAdditionalProperties)
1126 return PropertyValues::CompareValues(*r.m_pAdditionalProperties, *s.m_pAdditionalProperties, opt);
1130 * @brief Compare duplicate count
1131 * @param [in] p Pointer to first structure to compare.
1132 * @param [in] q Pointer to second structure to compare.
1133 * @return Compare result.
1135 static int ColPropertyDuplicateCountSort(const CDiffContext* pCtxt, const void* p, const void* q, int opt)
1137 const int index = opt & 0xffff;
1138 const int pane = opt >> 16;
1139 const DiffFileInfo& r = *static_cast<const DiffFileInfo*>(p);
1140 const DiffFileInfo& s = *static_cast<const DiffFileInfo*>(q);
1141 const DuplicateInfo* rinfo = GetDuplicateInfo(pCtxt, r, index);
1142 const DuplicateInfo* sinfo = GetDuplicateInfo(pCtxt, s, index);
1143 if (!rinfo && !sinfo)
1145 if (!rinfo && sinfo)
1147 if (rinfo && !sinfo)
1149 if (rinfo->count[pane] <= 1 && sinfo->count[pane] <= 1)
1151 if (rinfo->count[pane] <= 1 && sinfo->count[pane] > 1)
1153 if (rinfo->count[pane] > 1 && sinfo->count[pane] <= 1)
1155 if (rinfo->count[pane] == sinfo->count[pane])
1157 if (rinfo->groupid == sinfo->groupid)
1159 return rinfo->groupid < sinfo->groupid ? -1 : 1;
1161 return rinfo->count[pane] < sinfo->count[pane] ? -1 : 1;
1165 * @brief Compare all property values
1166 * @param [in] p Pointer to first structure to compare.
1167 * @param [in] q Pointer to second structure to compare.
1168 * @return Compare result.
1170 static int ColAllPropertySort(const CDiffContext *pCtxt, const void *p, const void *q, int opt)
1172 const DIFFITEM &r = *static_cast<const DIFFITEM *>(p);
1173 const DIFFITEM &s = *static_cast<const DIFFITEM *>(q);
1174 for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
1176 if (r.diffcode.exists(i))
1178 for (int j = 0; j < pCtxt->GetCompareDirs(); ++j)
1180 if (s.diffcode.exists(j))
1182 if (!r.diffFileInfo[i].m_pAdditionalProperties && s.diffFileInfo[j].m_pAdditionalProperties)
1184 if (r.diffFileInfo[i].m_pAdditionalProperties && !s.diffFileInfo[j].m_pAdditionalProperties)
1186 if (!r.diffFileInfo[i].m_pAdditionalProperties && !s.diffFileInfo[j].m_pAdditionalProperties)
1188 int result = PropertyValues::CompareValues(*r.diffFileInfo[i].m_pAdditionalProperties, *s.diffFileInfo[j].m_pAdditionalProperties, opt);
1199 * @brief Compare Hash(Diff) column values
1200 * @param [in] p Pointer to first structure to compare.
1201 * @param [in] q Pointer to second structure to compare.
1202 * @return Compare result.
1204 static int ColPropertyDiffSort(const CDiffContext *pCtxt, const void *p, const void *q, int opt)
1206 int64_t rnumdiff = 0;
1207 int64_t snumdiff = 0;
1208 bool rnumeric = false;
1209 bool snumeric = false;
1210 String r2 = ColPropertyDiffGetEx(pCtxt, p, opt, false, rnumeric, &rnumdiff);
1211 String s2 = ColPropertyDiffGetEx(pCtxt, q, opt, false, snumeric, &snumdiff);
1212 int result = strutils::compare_nocase(r2, s2);
1213 if (pCtxt->GetCompareDirs() == 2 && result == 0 && rnumeric && snumeric)
1215 if (rnumdiff == snumdiff)
1217 return rnumdiff < snumdiff ? -1 : 1;
1223 * @brief Compare Hash(Move) column values
1224 * @param [in] p Pointer to first structure to compare.
1225 * @param [in] q Pointer to second structure to compare.
1226 * @return Compare result.
1228 static int ColPropertyMoveSort(const CDiffContext *pCtxt, const void *p, const void *q, int opt)
1230 String r2 = ColPropertyMoveGet(pCtxt, p, opt);
1231 String s2 = ColPropertyMoveGet(pCtxt, q, opt);
1232 return strutils::compare_nocase(r2, s2);
1237 #undef FIELD_OFFSET // incorrect for Win32 as defined in WinNT.h
1238 #define FIELD_OFFSET(type, field) ((size_t)(LONG_PTR)&(((type *)nullptr)->field))
1241 * @brief All existing folder compare columns.
1243 * This table has information for all folder compare columns. Fields are
1246 * - name resource ID: column's name shown in header
1247 * - description resource ID: columns description text
1248 * - custom function for getting column data
1249 * - custom function for sorting column data
1250 * - parameter for custom functions: DIFFITEM (if `nullptr`) or one of its fields
1251 * - default column order number, -1 if not shown by default
1252 * - ascending (`true`) or descending (`false`) default sort order
1253 * - alignment of column contents: numbers are usually right-aligned
1255 static DirColInfo f_cols[] =
1257 { _T("Name"), nullptr, COLHDR_FILENAME, COLDESC_FILENAME, &ColFileNameGet<String>, &ColFileNameSort, 0, 0, true, DirColInfo::ALIGN_LEFT },
1258 { _T("Path"), "DirView|ColumnHeader", COLHDR_DIR, COLDESC_DIR, &ColPathGet, &ColPathSort, 0, 1, true, DirColInfo::ALIGN_LEFT },
1259 { _T("Status"), nullptr, COLHDR_RESULT, COLDESC_RESULT, &ColStatusGet, &ColStatusSort, 0, 2, true, DirColInfo::ALIGN_LEFT },
1260 { _T("Lmtime"), nullptr, COLHDR_LTIMEM, COLDESC_LTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].mtime), 3, false, DirColInfo::ALIGN_LEFT, 0 },
1261 { _T("Rmtime"), nullptr, COLHDR_RTIMEM, COLDESC_RTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].mtime), 4, false, DirColInfo::ALIGN_LEFT, 1 },
1262 { _T("Lctime"), nullptr, COLHDR_LTIMEC, COLDESC_LTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].ctime), -1, false, DirColInfo::ALIGN_LEFT, 0 },
1263 { _T("Rctime"), nullptr, COLHDR_RTIMEC, COLDESC_RTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].ctime), -1, false, DirColInfo::ALIGN_LEFT, 1 },
1264 { _T("Ext"), nullptr, COLHDR_EXTENSION, COLDESC_EXTENSION, &ColExtGet, &ColExtSort, 0, 5, true, DirColInfo::ALIGN_LEFT },
1265 { _T("Lsize"), nullptr, COLHDR_LSIZE, COLDESC_LSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].size), -1, false, DirColInfo::ALIGN_RIGHT, 0 },
1266 { _T("Rsize"), nullptr, COLHDR_RSIZE, COLDESC_RSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].size), -1, false, DirColInfo::ALIGN_RIGHT, 1 },
1267 { _T("LsizeShort"), nullptr, COLHDR_LSIZE_SHORT, COLDESC_LSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].size), -1, false, DirColInfo::ALIGN_RIGHT, 0 },
1268 { _T("RsizeShort"), nullptr, COLHDR_RSIZE_SHORT, COLDESC_RSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].size), -1, false, DirColInfo::ALIGN_RIGHT, 1 },
1269 { _T("Newer"), nullptr, COLHDR_NEWER, COLDESC_NEWER, &ColNewerGet, &ColNewerSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1270 { _T("Lversion"), nullptr, COLHDR_LVERSION, COLDESC_LVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1271 { _T("Rversion"), nullptr, COLHDR_RVERSION, COLDESC_RVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1272 { _T("StatusAbbr"), nullptr, COLHDR_RESULT_ABBR, COLDESC_RESULT_ABBR, &ColStatusAbbrGet, &ColStatusSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1273 { _T("Binary"), "DirView|ColumnHeader", COLHDR_BINARY, COLDESC_BINARY, &ColBinGet, &ColBinSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1274 { _T("Lattr"), nullptr, COLHDR_LATTRIBUTES, COLDESC_LATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].flags), -1, true, DirColInfo::ALIGN_LEFT, 0 },
1275 { _T("Rattr"), nullptr, COLHDR_RATTRIBUTES, COLDESC_RATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].flags), -1, true, DirColInfo::ALIGN_LEFT, 1 },
1276 { _T("Lencoding"), nullptr, COLHDR_LENCODING, COLDESC_LENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0]), -1, true, DirColInfo::ALIGN_LEFT, 0 },
1277 { _T("Rencoding"), nullptr, COLHDR_RENCODING, COLDESC_RENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1]), -1, true, DirColInfo::ALIGN_LEFT, 1 },
1278 { _T("Snsdiffs"), nullptr, COLHDR_NSDIFFS, COLDESC_NSDIFFS, ColDiffsGet, ColDiffsSort, FIELD_OFFSET(DIFFITEM, nsdiffs), -1, false, DirColInfo::ALIGN_RIGHT },
1279 { _T("Snidiffs"), nullptr, COLHDR_NIDIFFS, COLDESC_NIDIFFS, ColDiffsGet, ColDiffsSort, FIELD_OFFSET(DIFFITEM, nidiffs), -1, false, DirColInfo::ALIGN_RIGHT },
1280 { _T("Leoltype"), nullptr, COLHDR_LEOL_TYPE, COLDESC_LEOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1281 { _T("Reoltype"), nullptr, COLHDR_REOL_TYPE, COLDESC_REOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1282 { _T("Unpacker"), nullptr, COLHDR_UNPACKER, COLDESC_UNPACKER, &ColPluginPipelineGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1283 { _T("Prediffer"), nullptr, COLHDR_PREDIFFER, COLDESC_PREDIFFER, &ColPluginPipelineGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1285 static DirColInfo f_cols3[] =
1287 { _T("Name"), nullptr, COLHDR_FILENAME, COLDESC_FILENAME, &ColFileNameGet<String>, &ColFileNameSort, 0, 0, true, DirColInfo::ALIGN_LEFT },
1288 { _T("Path"), "DirView|ColumnHeader", COLHDR_DIR, COLDESC_DIR, &ColPathGet, &ColPathSort, 0, 1, true, DirColInfo::ALIGN_LEFT },
1289 { _T("Status"), nullptr, COLHDR_RESULT, COLDESC_RESULT, &ColStatusGet, &ColStatusSort, 0, 2, true, DirColInfo::ALIGN_LEFT },
1290 { _T("Lmtime"), nullptr, COLHDR_LTIMEM, COLDESC_LTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].mtime), 3, false, DirColInfo::ALIGN_LEFT, 0 },
1291 { _T("Mmtime"), nullptr, COLHDR_MTIMEM, COLDESC_MTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].mtime), 4, false, DirColInfo::ALIGN_LEFT, 1 },
1292 { _T("Rmtime"), nullptr, COLHDR_RTIMEM, COLDESC_RTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].mtime), 5, false, DirColInfo::ALIGN_LEFT, 2 },
1293 { _T("Lctime"), nullptr, COLHDR_LTIMEC, COLDESC_LTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].ctime), -1, false, DirColInfo::ALIGN_LEFT, 0 },
1294 { _T("Mctime"), nullptr, COLHDR_MTIMEC, COLDESC_MTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].ctime), -1, false, DirColInfo::ALIGN_LEFT, 1 },
1295 { _T("Rctime"), nullptr, COLHDR_RTIMEC, COLDESC_RTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].ctime), -1, false, DirColInfo::ALIGN_LEFT, 2 },
1296 { _T("Ext"), nullptr, COLHDR_EXTENSION, COLDESC_EXTENSION, &ColExtGet, &ColExtSort, 0, 6, true, DirColInfo::ALIGN_LEFT },
1297 { _T("Lsize"), nullptr, COLHDR_LSIZE, COLDESC_LSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].size), -1, false, DirColInfo::ALIGN_RIGHT, 0 },
1298 { _T("Msize"), nullptr, COLHDR_MSIZE, COLDESC_MSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].size), -1, false, DirColInfo::ALIGN_RIGHT, 1 },
1299 { _T("Rsize"), nullptr, COLHDR_RSIZE, COLDESC_RSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].size), -1, false, DirColInfo::ALIGN_RIGHT, 2 },
1300 { _T("LsizeShort"), nullptr, COLHDR_LSIZE_SHORT, COLDESC_LSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].size), -1, false, DirColInfo::ALIGN_RIGHT, 0 },
1301 { _T("MsizeShort"), nullptr, COLHDR_MSIZE_SHORT, COLDESC_MSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].size), -1, false, DirColInfo::ALIGN_RIGHT, 1 },
1302 { _T("RsizeShort"), nullptr, COLHDR_RSIZE_SHORT, COLDESC_RSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].size), -1, false, DirColInfo::ALIGN_RIGHT, 2 },
1303 { _T("Newer"), nullptr, COLHDR_NEWER, COLDESC_NEWER, &ColNewerGet, &ColNewerSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1304 { _T("Lversion"), nullptr, COLHDR_LVERSION, COLDESC_LVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1305 { _T("Mversion"), nullptr, COLHDR_MVERSION, COLDESC_MVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1306 { _T("Rversion"), nullptr, COLHDR_RVERSION, COLDESC_RVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 2 },
1307 { _T("StatusAbbr"), nullptr, COLHDR_RESULT_ABBR, COLDESC_RESULT_ABBR, &ColStatusAbbrGet, &ColStatusSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1308 { _T("Binary"), "DirView|ColumnHeader", COLHDR_BINARY, COLDESC_BINARY, &ColBinGet, &ColBinSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1309 { _T("Lattr"), nullptr, COLHDR_LATTRIBUTES, COLDESC_LATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].flags), -1, true, DirColInfo::ALIGN_LEFT, 0 },
1310 { _T("Mattr"), nullptr, COLHDR_MATTRIBUTES, COLDESC_MATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].flags), -1, true, DirColInfo::ALIGN_LEFT, 1 },
1311 { _T("Rattr"), nullptr, COLHDR_RATTRIBUTES, COLDESC_RATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].flags), -1, true, DirColInfo::ALIGN_LEFT, 2 },
1312 { _T("Lencoding"), nullptr, COLHDR_LENCODING, COLDESC_LENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0]), -1, true, DirColInfo::ALIGN_LEFT, 0 },
1313 { _T("Mencoding"), nullptr, COLHDR_MENCODING, COLDESC_MENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1]), -1, true, DirColInfo::ALIGN_LEFT, 1 },
1314 { _T("Rencoding"), nullptr, COLHDR_RENCODING, COLDESC_RENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2]), -1, true, DirColInfo::ALIGN_LEFT, 2 },
1315 { _T("Snsdiffs"), nullptr, COLHDR_NSDIFFS, COLDESC_NSDIFFS, ColDiffsGet, ColDiffsSort, FIELD_OFFSET(DIFFITEM, nsdiffs), -1, false, DirColInfo::ALIGN_RIGHT },
1316 { _T("Snidiffs"), nullptr, COLHDR_NIDIFFS, COLDESC_NIDIFFS, ColDiffsGet, ColDiffsSort, FIELD_OFFSET(DIFFITEM, nidiffs), -1, false, DirColInfo::ALIGN_RIGHT },
1317 { _T("Leoltype"), nullptr, COLHDR_LEOL_TYPE, COLDESC_LEOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1318 { _T("Meoltype"), nullptr, COLHDR_MEOL_TYPE, COLDESC_MEOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1319 { _T("Reoltype"), nullptr, COLHDR_REOL_TYPE, COLDESC_REOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 2 },
1320 { _T("Unpacker"), nullptr, COLHDR_UNPACKER, COLDESC_UNPACKER, &ColPluginPipelineGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1321 { _T("Prediffer"), nullptr, COLHDR_PREDIFFER, COLDESC_PREDIFFER, &ColPluginPipelineGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1324 String DirColInfo::GetDisplayName() const
1327 return tr(idNameContext, idName);
1328 PropertySystem ps({ regName + 1 });
1329 std::vector<String> names;
1330 ps.GetDisplayNames(names);
1331 String name = names[0];
1332 if (regName[0] != 'A')
1336 (regName[0] == 'L') ? _("Left") :
1337 (regName[0] == 'R') ? _("Right") :
1338 (regName[0] == 'M') ? _("Middle") :
1339 (regName[0] == 'D') ? _("Diff") :
1340 (regName[0] == 'l') ? _("Left Duplicate Count") :
1341 (regName[0] == 'r') ? _("Right Duplicate Count") :
1342 (regName[0] == 'm') ? _("Middle Duplicate Count") :
1343 (regName[0] == 'N') ? _("Move") : _("");
1349 String DirColInfo::GetDescription() const
1353 return GetDisplayName();
1356 DirViewColItems::DirViewColItems(int nDirs, const std::vector<String>& additionalPropertyNames) :
1357 m_nDirs(nDirs), m_dispcols(-1)
1359 m_numcols = static_cast<int>(nDirs < 3 ? std::size(f_cols) : std::size(f_cols3));
1360 DirColInfo *pcol = nDirs < 3 ? f_cols : f_cols3;
1361 for (size_t i = 0; i < m_numcols; ++i)
1362 m_cols.push_back(pcol[i]);
1363 ResetColumnOrdering();
1364 PropertySystem ps(additionalPropertyNames);
1365 for (const auto& propertyName : ps.GetCanonicalNames())
1366 AddAdditionalPropertyName(propertyName);
1370 DirViewColItems::AddAdditionalPropertyName(const String& propertyName)
1372 const int nfixedcols = static_cast<int>(m_nDirs < 3 ? std::size(f_cols) : std::size(f_cols3));
1373 int fixedColPhysicalIndexMax = 0;
1374 for (int i = 0; i < nfixedcols; ++i)
1375 fixedColPhysicalIndexMax = (std::max)(fixedColPhysicalIndexMax, m_cols[i].physicalIndex);
1377 String cList = (m_nDirs < 3) ? String(_T("ADLR")) : String(_T("ADLMR"));
1378 if (propertyName.substr(0, 5) == _T("Hash."))
1379 cList += (m_nDirs < 3) ? String(_T("Nlr")) : String(_T("lmr"));
1380 for (auto c : cList)
1382 m_cols.emplace_back(DirColInfo{});
1383 m_strpool.push_back(c + propertyName);
1384 auto& col = m_cols.back();
1385 col.regName = m_strpool.back().c_str();
1386 col.opt = static_cast<int>(m_additionalPropertyNames.size());
1389 col.physicalIndex = fixedColPhysicalIndexMax + 1 + col.opt;
1391 col.getfnc = ColAllPropertyGet;
1392 col.sortfnc = ColAllPropertySort;
1396 col.physicalIndex = -1;
1398 col.getfnc = ColPropertyDiffGet;
1399 col.sortfnc = ColPropertyDiffSort;
1403 col.physicalIndex = -1;
1405 col.getfnc = ColPropertyMoveGet;
1406 col.sortfnc = ColPropertyMoveSort;
1410 col.physicalIndex = -1;
1411 col.offset = FIELD_OFFSET(DIFFITEM, diffFileInfo[pane]);
1412 if (c == 'L' || c == 'M' || c == 'R')
1414 col.getfnc = ColPropertyGet;
1415 col.sortfnc = ColPropertySort;
1419 col.opt |= pane << 16;
1420 col.getfnc = ColPropertyDuplicateCountGet;
1421 col.sortfnc = ColPropertyDuplicateCountSort;
1423 pane = (pane + 1) % m_nDirs;
1425 m_invcolorder.push_back(-1);
1427 m_invcolorder[m_dispcols] = m_numcols;
1428 m_colorder.push_back(c == 'A' ? m_dispcols++ : -1);
1431 m_additionalPropertyNames.push_back(propertyName);
1435 DirViewColItems::RemoveAdditionalPropertyName(const String& propertyName)
1437 const int nfixedcols = static_cast<int>(m_nDirs < 3 ? std::size(f_cols) : std::size(f_cols3));
1438 std::vector<int> deletedPhysicalIndexes;
1439 std::vector<int> deletedLogicalIndexes;
1440 int deletedOpt = -1;
1441 for (int i = static_cast<int>(m_cols.size()) - 1; i >= nfixedcols; --i)
1443 if (m_cols[i].regName + 1 == propertyName)
1445 deletedLogicalIndexes.push_back(static_cast<int>(i));
1446 if (m_colorder[i] >= 0)
1448 deletedPhysicalIndexes.push_back(m_colorder[i]);
1451 deletedOpt = m_cols[i].opt;
1452 m_cols.erase(m_cols.begin() + i);
1456 for (int i = static_cast<int>(m_invcolorder.size()) - 1; i >= 0; --i)
1458 auto it = std::find(deletedLogicalIndexes.begin(), deletedLogicalIndexes.end(), m_invcolorder[i]);
1459 if (it != deletedLogicalIndexes.end())
1460 m_invcolorder.erase(m_invcolorder.begin() + i);
1463 int logicalIndex = m_invcolorder[i];
1464 for (auto deletedLogicalIndex : deletedLogicalIndexes)
1466 if (deletedLogicalIndex < logicalIndex)
1471 for (int i = static_cast<int>(m_colorder.size()) - 1; i >= 0; --i)
1473 auto it = std::find(deletedLogicalIndexes.begin(), deletedLogicalIndexes.end(), i);
1474 if (it != deletedLogicalIndexes.end())
1475 m_colorder.erase(m_colorder.begin() + i);
1478 int physicalIndex = m_colorder[i];
1479 for (auto deletedPhysicalIndex : deletedPhysicalIndexes)
1481 if (deletedPhysicalIndex < physicalIndex)
1486 for (int i = static_cast<int>(m_cols.size()) - 1; i >= nfixedcols; --i)
1488 if (deletedOpt < m_cols[i].opt)
1491 for (size_t i = 0; i < m_additionalPropertyNames.size(); ++i)
1493 if (m_additionalPropertyNames[i] == propertyName)
1494 m_additionalPropertyNames.erase(m_additionalPropertyNames.begin() + i);
1496 m_colorder.resize(m_numcols);
1497 m_invcolorder.resize(m_numcols);
1498 if (m_dispcols <= 0)
1499 ResetColumnOrdering();
1503 DirViewColItems::SetAdditionalPropertyNames(const std::vector<String>& additionalPropertyNames)
1505 for (int i = static_cast<int>(m_additionalPropertyNames.size()) - 1; i >= 0; i--)
1507 auto it = std::find(additionalPropertyNames.begin(), additionalPropertyNames.end(), m_additionalPropertyNames[i]);
1508 if (it == additionalPropertyNames.end())
1509 RemoveAdditionalPropertyName(m_additionalPropertyNames[i]);
1511 for (const auto& propertyName : additionalPropertyNames)
1513 auto it = std::find(m_additionalPropertyNames.begin(), m_additionalPropertyNames.end(), propertyName);
1514 if (it == m_additionalPropertyNames.end())
1515 AddAdditionalPropertyName(propertyName);
1520 * @brief Registry base value name for saving/loading info for this column
1523 DirViewColItems::GetColRegValueNameBase(int col) const
1525 assert(col>=0 && col<m_numcols);
1526 return strutils::format(_T("WDirHdr_%s"), m_cols[col].regName);
1530 * @brief Get default physical order for specified logical column
1533 DirViewColItems::GetColDefaultOrder(int col) const
1535 assert(col>=0 && col<m_numcols);
1536 return m_cols[col].physicalIndex;
1540 * @brief Return the info about the specified physical column
1543 DirViewColItems::GetDirColInfo(int col) const
1545 if (col < 0 || col >= m_numcols)
1547 assert(false); // fix caller, should not ask for nonexistent columns
1550 return &m_cols[col];
1554 * @brief Check if specified physical column has specified resource id name
1557 DirViewColItems::IsColById(int col, const char *idname) const
1559 if (col < 0 || col >= m_numcols)
1561 assert(false); // fix caller, should not ask for nonexistent columns
1564 return m_cols[col].idName == idname;
1568 * @brief Is specified physical column the name column?
1571 DirViewColItems::IsColName(int col) const
1573 return IsColById(col, COLHDR_FILENAME);
1576 * @brief Is specified physical column the left modification time column?
1579 DirViewColItems::IsColLmTime(int col) const
1581 return IsColById(col, COLHDR_LTIMEM);
1584 * @brief Is specified physical column the middle modification time column?
1587 DirViewColItems::IsColMmTime(int col) const
1589 return IsColById(col, COLHDR_MTIMEM);
1592 * @brief Is specified physical column the right modification time column?
1595 DirViewColItems::IsColRmTime(int col) const
1597 return IsColById(col, COLHDR_RTIMEM);
1600 * @brief Is specified physical column the full status (result) column?
1603 DirViewColItems::IsColStatus(int col) const
1605 return IsColById(col, COLHDR_RESULT);
1608 * @brief Is specified physical column the full status (result) column?
1611 DirViewColItems::IsColStatusAbbr(int col) const
1613 return IsColById(col, COLHDR_RESULT_ABBR);
1617 * @brief return whether column normally sorts ascending (dates do not)
1620 DirViewColItems::IsDefaultSortAscending(int col) const
1622 const DirColInfo * pColInfo = GetDirColInfo(col);
1623 if (pColInfo == nullptr)
1625 assert(false); // fix caller, should not ask for nonexistent columns
1628 return pColInfo->defSortUp;
1632 * @brief Return display name of column
1635 DirViewColItems::GetColDisplayName(int col) const
1637 const DirColInfo * colinfo = GetDirColInfo(col);
1638 return colinfo ? colinfo->GetDisplayName() : _T("");
1642 * @brief Return description of column
1645 DirViewColItems::GetColDescription(int col) const
1647 const DirColInfo * colinfo = GetDirColInfo(col);
1648 return colinfo ? colinfo->GetDescription() : _T("");
1652 * @brief Get text for specified column.
1653 * This function retrieves the text for the specified colum. Text is
1654 * retrieved by using column-specific handler functions.
1655 * @param [in] pCtxt Compare context.
1656 * @param [in] col Column number.
1657 * @param [in] di Difference data.
1658 * @return Text for the specified column.
1661 DirViewColItems::ColGetTextToDisplay(const CDiffContext *pCtxt, int col,
1662 const DIFFITEM &di) const
1664 // Custom properties have custom get functions
1665 const DirColInfo * pColInfo = GetDirColInfo(col);
1666 if (pColInfo == nullptr)
1668 assert(false); // fix caller, should not ask for nonexistent columns
1671 ColGetFncPtrType fnc = pColInfo->getfnc;
1672 size_t offset = pColInfo->offset;
1673 String s = (*fnc)(pCtxt, reinterpret_cast<const char *>(&di) + offset, pColInfo->opt);
1675 // Add '*' to newer time field
1676 if (IsColLmTime(col) || IsColMmTime(col) || IsColRmTime(col))
1680 if (di.diffFileInfo[0].mtime != 0 || di.diffFileInfo[1].mtime != 0)
1684 IsColLmTime(col) && di.diffFileInfo[0].mtime > di.diffFileInfo[1].mtime // Left modification time
1685 || IsColRmTime(col) && di.diffFileInfo[0].mtime < di.diffFileInfo[1].mtime // Right modification time
1688 s.insert(0, _T("* "));
1692 s.insert(0, _T(" ")); // Looks best with a fixed-font, but not too bad otherwise
1694 // GreyMerlin (14 Nov 2009) - the flagging of Date needs to be done with
1695 // something not involving extra characters. Perhaps <red> for oldest,
1696 // <green> for newest. Note (20 March 2017): the introduction of 3-Way
1697 // Merge and the yellow difference highlighting adds to the design
1698 // difficulty of any changes. So maybe this "* "/" " scheme is good enough.
1704 if (di.diffFileInfo[0].mtime != 0 || di.diffFileInfo[1].mtime != 0 || di.diffFileInfo[2].mtime != 0)
1708 IsColLmTime(col) && di.diffFileInfo[0].mtime > di.diffFileInfo[1].mtime && di.diffFileInfo[0].mtime > di.diffFileInfo[2].mtime // Left modification time
1709 || IsColMmTime(col) && di.diffFileInfo[1].mtime > di.diffFileInfo[0].mtime && di.diffFileInfo[1].mtime > di.diffFileInfo[2].mtime // Middle modification time
1710 || IsColRmTime(col) && di.diffFileInfo[2].mtime > di.diffFileInfo[0].mtime && di.diffFileInfo[2].mtime > di.diffFileInfo[1].mtime // Right modification time
1713 s.insert(0, _T("* "));
1717 s.insert(0, _T(" ")); // Looks best with a fixed-font, but not too bad otherwise
1719 // GreyMerlin (14 Nov 2009) - See note above.
1730 * @brief Sort two items on specified column.
1731 * This function determines order of two items in specified column. Order
1732 * is determined by column-specific functions.
1733 * @param [in] pCtxt Compare context.
1734 * @param [in] col Column number to sort.
1735 * @param [in] ldi Left difference item data.
1736 * @param [in] rdi Right difference item data.
1737 * @return Order of items.
1740 DirViewColItems::ColSort(const CDiffContext *pCtxt, int col, const DIFFITEM &ldi,
1741 const DIFFITEM &rdi, bool bTreeMode) const
1743 // Custom properties have custom sort functions
1744 const DirColInfo * pColInfo = GetDirColInfo(col);
1745 if (pColInfo == nullptr)
1747 assert(false); // fix caller, should not ask for nonexistent columns
1750 size_t offset = pColInfo->offset;
1751 int opt = pColInfo->opt;
1756 int lLevel = ldi.GetDepth();
1757 int rLevel = rdi.GetDepth();
1758 const DIFFITEM *lcur = &ldi, *rcur = &rdi;
1759 if (lLevel < rLevel)
1761 for (; lLevel != rLevel; rLevel--)
1762 rcur = rcur->GetParentLink();
1764 else if (rLevel < lLevel)
1766 for (; lLevel != rLevel; lLevel--)
1767 lcur = lcur->GetParentLink();
1769 while (lcur->GetParentLink() != rcur->GetParentLink())
1771 lcur = lcur->GetParentLink();
1772 rcur = rcur->GetParentLink();
1774 arg1 = reinterpret_cast<const char *>(lcur) + offset;
1775 arg2 = reinterpret_cast<const char *>(rcur) + offset;
1779 arg1 = reinterpret_cast<const char *>(&ldi) + offset;
1780 arg2 = reinterpret_cast<const char *>(&rdi) + offset;
1782 if (ColSortFncPtrType fnc = pColInfo->sortfnc)
1784 return (*fnc)(pCtxt, arg1, arg2, opt);
1786 if (ColGetFncPtrType fnc = pColInfo->getfnc)
1788 String p = (*fnc)(pCtxt, arg1, opt);
1789 String q = (*fnc)(pCtxt, arg2, opt);
1790 return strutils::compare_nocase(p, q);
1795 void DirViewColItems::SetColumnOrdering(const int colorder[])
1798 for (int i = 0; i < m_numcols; ++i)
1800 m_colorder[i] = colorder[i];
1801 int phy = m_colorder[i];
1805 m_invcolorder[phy] = i;
1811 * @brief Set column ordering to default initial order
1813 void DirViewColItems::ResetColumnOrdering()
1815 ClearColumnOrders();
1817 for (int i=0; i<m_numcols; ++i)
1819 int phy = GetColDefaultOrder(i);
1820 m_colorder[i] = phy;
1823 m_invcolorder[phy] = i;
1830 * @brief Reset all current column ordering information
1832 void DirViewColItems::ClearColumnOrders()
1834 m_colorder.resize(m_numcols);
1835 m_invcolorder.resize(m_numcols);
1836 for (int i=0; i<m_numcols; ++i)
1839 m_invcolorder[i] = -1;
1844 * @brief Remove any windows reordering of columns (params are physical columns)
1846 void DirViewColItems::MoveColumn(int psrc, int pdest)
1848 // actually moved column
1849 m_colorder[m_invcolorder[psrc]] = pdest;
1850 // shift all other affected columns
1851 int dir = psrc > pdest ? +1 : -1;
1853 for (i=pdest; i!=psrc; i += dir)
1855 m_colorder[m_invcolorder[i]] = i+dir;
1857 // fix inverse mapping
1858 for (i=0; i<m_numcols; ++i)
1860 if (m_colorder[i] >= 0)
1861 m_invcolorder[m_colorder[i]] = i;
1866 * @brief Resets column widths to defaults.
1868 String DirViewColItems::ResetColumnWidths(int defcolwidth)
1871 for (int i = 0; i < m_numcols; i++)
1873 if (!result.empty()) result += ' ';
1874 result += strutils::to_str(defcolwidth);
1880 * @brief Load column orders from registry
1882 void DirViewColItems::LoadColumnOrders(const String& colOrders)
1884 ClearColumnOrders();
1886 std::basic_istringstream<TCHAR> ss(colOrders);
1888 // Load column orders
1889 // Break out if one is missing
1890 // Break out & mark failure (m_dispcols == -1) if one is invalid
1892 for (i=0; i<m_numcols; ++i)
1896 if (ord<-1 || ord >= m_numcols)
1898 m_colorder[i] = ord;
1902 if (m_invcolorder[ord] != -1)
1907 m_invcolorder[ord] = i;
1910 // Check that a contiguous range was set
1911 for (i=0; i<m_dispcols; ++i)
1913 if (m_invcolorder[i] < 0)
1919 // Must have at least one column
1922 ResetColumnOrdering();
1926 /// store current column orders into registry
1927 String DirViewColItems::SaveColumnOrders()
1929 assert(static_cast<int>(m_colorder.size()) == m_numcols);
1930 assert(static_cast<int>(m_invcolorder.size()) == m_numcols);
1931 return strutils::join<String (*)(int)>(m_colorder.begin(), m_colorder.end(), _T(" "), strutils::to_str);