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] p Pointer to DIFFITEM.
186 * @return String to show in the column.
189 static Type ColFileNameGet(const CDiffContext *, const void *p, int) //sfilename
191 const boost::flyweight<String> &lfilename = static_cast<const DIFFITEM*>(p)->diffFileInfo[0].filename;
192 const boost::flyweight<String> &rfilename = static_cast<const DIFFITEM*>(p)->diffFileInfo[1].filename;
193 if (lfilename.get().empty())
195 else if (rfilename.get().empty() || lfilename == rfilename)
198 return static_cast<Type>(lfilename.get() + _T("|") + rfilename.get());
202 * @brief Format Extension column data.
203 * @param [in] p Pointer to DIFFITEM.
204 * @return String to show in the column.
206 static String ColExtGet(const CDiffContext *, const void *p, int) //sfilename
208 const DIFFITEM &di = *static_cast<const DIFFITEM*>(p);
209 // We don't show extension for folder names
210 if (di.diffcode.isDirectory())
212 const String &r = di.diffFileInfo[0].filename;
213 String s = paths::FindExtension(r);
214 return s.c_str() + _tcsspn(s.c_str(), _T("."));
218 * @brief Format Folder column data.
219 * @param [in] p Pointer to DIFFITEM.
220 * @return String to show in the column.
222 static String ColPathGet(const CDiffContext *, const void *p, int)
224 const DIFFITEM &di = *static_cast<const DIFFITEM*>(p);
225 String s = di.diffFileInfo[1].path;
226 const String &t = di.diffFileInfo[0].path;
228 // If we have unique path, just print the existing path name
229 if (s.length() == 0 || t.length() == 0)
240 const TCHAR *pi = _tcschr(s.c_str() + i, '\\');
241 const TCHAR *pj = _tcschr(t.c_str() + j, '\\');
242 size_t i_ahead = (pi != nullptr ? pi - s.c_str() : std::string::npos);
243 size_t j_ahead = (pj != nullptr ? pj - t.c_str() : std::string::npos);
244 size_t length_s = ((i_ahead != std::string::npos ? i_ahead : s.length()) - i);
245 size_t length_t = ((j_ahead != std::string::npos ? j_ahead : t.length()) - j);
246 if (length_s != length_t ||
247 memcmp(s.c_str() + i, t.c_str() + j, length_s) != 0)
249 String u(t.c_str() + j, length_t + 1);
252 i_ahead += u.length();
263 * @brief Format Result column data.
264 * @param [in] pCtxt Pointer to compare context.
265 * @param [in] p Pointer to DIFFITEM.
266 * @return String to show in the column.
268 static String ColStatusGet(const CDiffContext *pCtxt, const void *p, int)
270 const DIFFITEM &di = *static_cast<const DIFFITEM*>(p);
271 int nDirs = pCtxt->GetCompareDirs();
272 // Note that order of items does matter. We must check for
273 // skipped items before unique items, for example, so that
274 // skipped unique items are labeled as skipped, not unique.
276 if (di.diffcode.isResultError())
278 s = _("Unable to compare files");
280 else if (di.diffcode.isResultAbort())
282 s = _("Item aborted");
284 else if (di.diffcode.isResultFiltered())
286 if (di.diffcode.isDirectory())
287 s = _("Folder skipped");
289 s = _("File skipped");
291 else if (di.diffcode.isSideFirstOnly())
293 s = strutils::format_string1(_("Left only: %1"),
294 di.getFilepath(0, pCtxt->GetNormalizedLeft()));
296 else if (di.diffcode.isSideSecondOnly())
300 s = strutils::format_string1(_("Right only: %1"),
301 di.getFilepath(1, pCtxt->GetNormalizedRight()));
305 s = strutils::format_string1(_("Middle only: %1"),
306 di.getFilepath(1, pCtxt->GetNormalizedMiddle()));
309 else if (di.diffcode.isSideThirdOnly())
311 s = strutils::format_string1(_("Right only: %1"),
312 di.getFilepath(2, pCtxt->GetNormalizedRight()));
314 else if (nDirs > 2 && !di.diffcode.existsFirst())
316 s = strutils::format_string1(_("Does not exist in %1"),
317 pCtxt->GetNormalizedLeft());
319 else if (nDirs > 2 && !di.diffcode.existsSecond())
321 s = strutils::format_string1(_("Does not exist in %1"),
322 pCtxt->GetNormalizedMiddle());
324 else if (nDirs > 2 && !di.diffcode.existsThird())
326 s = strutils::format_string1(_("Does not exist in %1"),
327 pCtxt->GetNormalizedRight());
329 else if (di.diffcode.isResultSame())
331 if (di.diffcode.isText())
332 s = _("Text files are identical");
333 else if (di.diffcode.isBin())
334 s = _("Binary files are identical");
335 else if (di.diffcode.isImage())
336 s = _("Image files are identical");
340 else if (di.diffcode.isResultDiff()) // diff
342 if (di.diffcode.isText())
343 s = _("Text files are different");
344 else if (di.diffcode.isBin())
345 s = _("Binary files are different");
346 else if (di.diffcode.isImage())
347 s = _("Image files are different");
348 else if (di.diffcode.isDirectory())
349 s = _("Folders are different");
351 s = _("Files are different");
354 switch (di.diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY)
356 case DIFFCODE::DIFF1STONLY: s += _("(Middle and right are identical)"); break;
357 case DIFFCODE::DIFF2NDONLY: s += _("(Left and right are identical)"); break;
358 case DIFFCODE::DIFF3RDONLY: s += _("(Left and middle are identical)"); break;
366 * @brief Format Date column data.
367 * @param [in] p Pointer to integer (seconds since 1.1.1970).
368 * @return String to show in the column.
370 static String ColTimeGet(const CDiffContext *, const void *p, int)
372 const int64_t r = *static_cast<const int64_t*>(p) / Timestamp::resolution();
374 return locality::TimeString(&r);
380 * @brief Format Sizw column data.
381 * @param [in] p Pointer to integer containing size in bytes.
382 * @return String to show in the column.
384 static String ColSizeGet(const CDiffContext *, const void *p, int)
386 const int64_t &r = *static_cast<const int64_t*>(p);
390 s = locality::NumToLocaleStr(r);
396 * @brief Format Folder column data.
397 * @param [in] p Pointer to DIFFITEM.
398 * @return String to show in the column.
400 static String ColSizeShortGet(const CDiffContext *, const void *p, int)
402 const int64_t &r = *static_cast<const int64_t*>(p);
406 s = MakeShortSize(r);
412 * @brief Format Difference cout column data.
413 * @param [in] p Pointer to integer having count of differences.
414 * @return String to show in the column.
416 static String ColDiffsGet(const CDiffContext *, const void *p, int)
418 const int &r = *static_cast<const int*>(p);
420 if (r == CDiffContext::DIFFS_UNKNOWN_QUICKCOMPARE)
421 { // QuickCompare, unknown
424 else if (r == CDiffContext::DIFFS_UNKNOWN)
430 s = locality::NumToLocaleStr(r);
436 * @brief Format Newer/Older column data.
437 * @param [in] p Pointer to DIFFITEM.
438 * @return String to show in the column.
440 static String ColNewerGet(const CDiffContext *pCtxt, const void *p, int)
442 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
443 if (pCtxt->GetCompareDirs() < 3)
445 if (di.diffcode.isSideFirstOnly())
449 if (di.diffcode.isSideSecondOnly())
453 if (di.diffFileInfo[0].mtime != 0 && di.diffFileInfo[1].mtime != 0)
455 if (di.diffFileInfo[0].mtime > di.diffFileInfo[1].mtime)
459 if (di.diffFileInfo[0].mtime < di.diffFileInfo[1].mtime)
470 int sortno[3] = {0, 1, 2};
471 Timestamp sorttime[3] = {di.diffFileInfo[0].mtime, di.diffFileInfo[1].mtime, di.diffFileInfo[2].mtime};
472 for (int i = 0; i < 3; i++)
474 for (int j = i; j < 3; j++)
476 if (sorttime[i] < sorttime[j])
478 swap(sorttime[i], sorttime[j]);
479 swap(sortno[i], sortno[j]);
483 res = _T("LMR")[sortno[0]];
484 res += sorttime[0] == sorttime[1] ? _T("==") : _T("<-");
485 res += _T("LMR")[sortno[1]];
486 res += sorttime[1] == sorttime[2] ? _T("==") : _T("<-");
487 res += _T("LMR")[sortno[2]];
493 * @brief Format Version info to string.
494 * @param [in] pCtxt Pointer to compare context.
495 * @param [in] pdi Pointer to DIFFITEM.
496 * @param [in] bLeft Is the item left-size item?
497 * @return String proper to show in the GUI.
499 static String GetVersion(const CDiffContext * pCtxt, const DIFFITEM *pdi, int nIndex)
501 DIFFITEM &di = const_cast<DIFFITEM &>(*pdi);
502 DiffFileInfo & dfi = di.diffFileInfo[nIndex];
503 if (dfi.version.IsCleared())
505 pCtxt->UpdateVersion(di, nIndex);
507 return dfi.version.GetFileVersionString();
510 static uint64_t GetVersionQWORD(const CDiffContext * pCtxt, const DIFFITEM *pdi, int nIndex)
512 DIFFITEM &di = const_cast<DIFFITEM &>(*pdi);
513 DiffFileInfo & dfi = di.diffFileInfo[nIndex];
514 if (dfi.version.IsCleared())
516 pCtxt->UpdateVersion(di, nIndex);
518 return dfi.version.GetFileVersionQWORD();
522 * @brief Format Version column data
523 * @param [in] pCtxt Pointer to compare context.
524 * @param [in] p Pointer to DIFFITEM.
525 * @return String to show in the column.
527 static String ColVersionGet(const CDiffContext * pCtxt, const void *p, int opt)
529 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
530 return GetVersion(pCtxt, &di, opt);
534 * @brief Format Short Result column data.
535 * @param [in] p Pointer to DIFFITEM.
536 * @return String to show in the column.
538 static String ColStatusAbbrGet(const CDiffContext *pCtxt, const void *p, int opt)
540 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
542 int nDirs = pCtxt->GetCompareDirs();
544 // Note that order of items does matter. We must check for
545 // skipped items before unique items, for example, so that
546 // skipped unique items are labeled as skipped, not unique.
547 if (di.diffcode.isResultError())
551 else if (di.diffcode.isResultAbort())
553 id = N_("Item aborted");
555 else if (di.diffcode.isResultFiltered())
557 if (di.diffcode.isDirectory())
558 id = N_("Folder skipped");
560 id = N_("File skipped");
562 else if (di.diffcode.isSideFirstOnly())
564 id = N_("Left Only");
566 else if (di.diffcode.isSideSecondOnly())
569 id = N_("Right Only");
571 id = N_("Middle Only");
573 else if (di.diffcode.isSideThirdOnly())
575 id = N_("Right Only");
577 else if (nDirs > 2 && !di.diffcode.existsFirst())
579 id = N_("No item in left");
581 else if (nDirs > 2 && !di.diffcode.existsSecond())
583 id = N_("No item in middle");
585 else if (nDirs > 2 && !di.diffcode.existsThird())
587 id = N_("No item in right");
589 else if (di.diffcode.isResultSame())
591 id = N_("Identical");
593 else if (di.diffcode.isResultDiff())
595 id = N_("Different");
598 return id ? tr(id) : _T("");
602 * @brief Format Binary column data.
603 * @param [in] p Pointer to DIFFITEM.
604 * @return String to show in the column.
606 static String ColBinGet(const CDiffContext *, const void *p, int)
608 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
610 if (di.diffcode.isBin())
617 * @brief Format File Attributes column data.
618 * @param [in] p Pointer to file flags class.
619 * @return String to show in the column.
621 static String ColAttrGet(const CDiffContext *, const void *p, int)
623 const FileFlags &r = *static_cast<const FileFlags *>(p);
628 * @brief Format File Encoding column data.
629 * @param [in] p Pointer to file information.
630 * @return String to show in the column.
632 static String ColEncodingGet(const CDiffContext *, const void *p, int)
634 const DiffFileInfo &r = *static_cast<const DiffFileInfo *>(p);
635 return r.encoding.GetName();
639 * @brief Format EOL type to string.
640 * @param [in] p Pointer to DIFFITEM.
641 * @param [in] bLeft Are we formatting left-side file's data?
642 * @return EOL type as as string.
644 static String GetEOLType(const CDiffContext *, const void *p, int index)
646 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
647 const DiffFileInfo & dfi = di.diffFileInfo[index];
648 const FileTextStats &stats = dfi.m_textStats;
650 if (stats.ncrlfs == 0 && stats.ncrs == 0 && stats.nlfs == 0)
654 if (di.diffcode.isBin())
656 return tr("EOL Type", "Binary");
660 if (stats.ncrlfs > 0 && stats.ncrs == 0 && stats.nlfs == 0)
664 else if (stats.ncrlfs == 0 && stats.ncrs > 0 && stats.nlfs == 0)
668 else if (stats.ncrlfs == 0 && stats.ncrs == 0 && stats.nlfs > 0)
674 return strutils::format(_T("%s:%d/%d/%d"),
676 stats.ncrlfs, stats.ncrs, stats.nlfs);
683 * @brief Format EOL type column data
684 * @param [in] pCtxt Pointer to compare context.
685 * @param [in] p Pointer to DIFFITEM.
686 * @return String to show in the column.
688 static String ColEOLTypeGet(const CDiffContext * pCtxt, const void *p, int opt)
690 const DIFFITEM &di = *static_cast<const DIFFITEM *>(p);
691 return GetEOLType(pCtxt, &di, opt);
694 static String ColPluginPipelineGet(const CDiffContext* pCtxt, const void *p, int opt)
696 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
697 if (di.diffcode.isDirectory())
699 PackingInfo* pInfoUnpacker = nullptr;
700 PrediffingInfo * pInfoPrediffer = nullptr;
701 String filteredFilenames = pCtxt->GetFilteredFilenames(di);
702 const_cast<CDiffContext *>(pCtxt)->FetchPluginInfos(filteredFilenames, &pInfoUnpacker, &pInfoPrediffer);
704 return pInfoUnpacker ? pInfoUnpacker->GetPluginPipeline() : _T("");
706 return pInfoPrediffer ? pInfoPrediffer->GetPluginPipeline() : _T("");
709 static String ColPropertyGet(const CDiffContext *pCtxt, const void *p, int opt)
711 const DiffFileInfo &dfi = *static_cast<const DiffFileInfo *>(p);
712 PropertyValues* pprops = dfi.m_pAdditionalProperties.get();
713 return (pprops != nullptr && opt < pprops->GetSize()) ? pCtxt->m_pPropertySystem->FormatPropertyValue(*pprops, opt) : _T("");
716 static const DuplicateInfo *GetDuplicateInfo(const CDiffContext* pCtxt, const DiffFileInfo& dfi, int index)
718 PropertyValues* pprops = dfi.m_pAdditionalProperties.get();
719 if (!pprops || index >= pprops->GetSize() || !pprops->IsHashValue(index) || pCtxt->m_duplicateValues.empty())
721 auto it = pCtxt->m_duplicateValues[index].find(pprops->GetHashValue(index));
722 if (it == pCtxt->m_duplicateValues[index].end())
724 return &(it->second);
727 static String ColPropertyDuplicateCountGet(const CDiffContext *pCtxt, const void *p, int opt)
729 const int index = opt & 0xffff;
730 const int pane = opt >> 16;
731 const DiffFileInfo &dfi = *static_cast<const DiffFileInfo *>(p);
732 const DuplicateInfo *info = GetDuplicateInfo(pCtxt, dfi, index);
733 return (!info || info->count[pane] <= 1) ? _T("") :
734 strutils::format(_("%s: %d"), strutils::format(_("Group%d"), info->groupid), info->count[pane] - 1);
737 static String ColAllPropertyGet(const CDiffContext *pCtxt, const void *p, int opt)
739 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
741 PropertyValues* pFirstProps = di.diffFileInfo[0].m_pAdditionalProperties.get();
742 for (int i = 1; i < pCtxt->GetCompareDirs(); ++i)
744 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
745 if (pFirstProps && pprops)
747 if (PropertyValues::CompareValues(*pFirstProps, *pprops, opt) != 0)
752 return pFirstProps ? pCtxt->m_pPropertySystem->FormatPropertyValue(*pFirstProps, opt) : _T("");
754 std::vector<String> values;
755 for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
757 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
758 if (pCtxt->GetCompareDirs() == 3 || di.diffcode.exists(i))
759 values.push_back(pprops ? pCtxt->m_pPropertySystem->FormatPropertyValue(*pprops, opt) : _T(""));
761 return strutils::join(values.begin(), values.end(), _T("|"));
764 static String ColPropertyDiffGetEx(const CDiffContext *pCtxt, const void *p, int opt, bool addNumDiff, bool& numeric, int64_t *pnumdiff)
766 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
767 if (di.diffcode.isDirectory())
769 if (di.diffcode.isResultError())
771 if (di.diffcode.isResultAbort())
772 return _("Item aborted");
773 if (di.diffcode.isResultFiltered())
774 return _("File skipped");
775 PropertyValues* pFirstProps = di.diffFileInfo[0].m_pAdditionalProperties.get();
781 const int nDirs = pCtxt->GetCompareDirs();
782 for (int i = 1; i < nDirs; ++i)
784 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
785 if (pFirstProps && pprops)
787 diff = PropertyValues::DiffValues(*pFirstProps, *pprops, opt, numeric);
795 if (!di.diffcode.existAll())
797 bool allempty = true;
798 for (int i = 0; i < nDirs; ++i)
800 if (di.diffcode.exists(i))
802 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
803 if (pprops && !pprops->IsEmptyValue(opt))
809 if (di.diffcode.isSideFirstOnly())
810 result = _("Left Only");
811 else if (di.diffcode.isSideSecondOnly())
814 result = _("Right Only");
816 result = _("Middle Only");
818 else if (di.diffcode.isSideThirdOnly())
819 result = _("Right Only");
820 else if (nDirs > 2 && !di.diffcode.existsFirst())
821 result = _("No item in left");
822 else if (nDirs > 2 && !di.diffcode.existsSecond())
823 result = _("No item in middle");
824 else if (nDirs > 2 && !di.diffcode.existsThird())
825 result = _("No item in right");
830 result = !equal ? _("Different") :
831 (!pFirstProps->IsEmptyValue(opt) ? _("Identical") : _T(""));
833 if (nDirs == 2 && numeric && addNumDiff)
834 result += strutils::format(_T(" (%+ld)"), diff);
838 static String ColPropertyDiffGet(const CDiffContext* pCtxt, const void* p, int opt)
840 bool numeric = false;
841 return ColPropertyDiffGetEx(pCtxt, p, opt, true, numeric, nullptr);
844 static String ColPropertyMoveGet(const CDiffContext *pCtxt, const void *p, int opt)
846 const DIFFITEM& di = *static_cast<const DIFFITEM*>(p);
847 if (di.diffcode.isDirectory())
849 std::vector<String> list;
850 const int nDirs = pCtxt->GetCompareDirs();
851 for (int i = 0; i < nDirs; ++i)
853 if (di.diffcode.exists(i))
855 PropertyValues* pprops = di.diffFileInfo[i].m_pAdditionalProperties.get();
856 if (pprops && !pCtxt->m_duplicateValues.empty())
858 auto it = pCtxt->m_duplicateValues[opt].find(pprops->GetHashValue(opt));
859 if (it != pCtxt->m_duplicateValues[opt].end())
861 if (it->second.groupid != 0 && it->second.nonpaired)
864 for (int j = 0; j < nDirs; ++j)
866 if (it->second.count[j] > 0)
870 list.push_back(strutils::format(_("Group%d"), it->second.groupid));
876 return strutils::join(list.begin(), list.end(), _T("|"));
884 * @name Functions to sort each type of column info.
885 * These functions are used to sort information in folder compare GUI. Each
886 * column info (type) has its own function to compare the data. Each
887 * function receives three parameters:
888 * - pointer to compare context
889 * - two parameters for data to compare (type varies)
890 * Return value is -1, 0, or 1, where 0 means datas are identical.
894 * @brief Compare file names.
895 * @param [in] pCtxt Pointer to compare context.
896 * @param [in] p Pointer to DIFFITEM having first name to compare.
897 * @param [in] q Pointer to DIFFITEM having second name to compare.
898 * @return Compare result.
900 static int ColFileNameSort(const CDiffContext *pCtxt, const void *p, const void *q, int)
902 const DIFFITEM &ldi = *static_cast<const DIFFITEM *>(p);
903 const DIFFITEM &rdi = *static_cast<const DIFFITEM *>(q);
904 if (ldi.diffcode.isDirectory() && !rdi.diffcode.isDirectory())
906 if (!ldi.diffcode.isDirectory() && rdi.diffcode.isDirectory())
908 return strutils::compare_nocase(ColFileNameGet<boost::flyweight<String> >(pCtxt, p, 0), ColFileNameGet<boost::flyweight<String> >(pCtxt, q, 0));
912 * @brief Compare file name extensions.
913 * @param [in] pCtxt Pointer to compare context.
914 * @param [in] p First file name extension to compare.
915 * @param [in] q Second file name extension to compare.
916 * @return Compare result.
918 static int ColExtSort(const CDiffContext *pCtxt, const void *p, const void *q, int)
920 const DIFFITEM &ldi = *static_cast<const DIFFITEM *>(p);
921 const DIFFITEM &rdi = *static_cast<const DIFFITEM *>(q);
922 if (ldi.diffcode.isDirectory() && !rdi.diffcode.isDirectory())
924 if (!ldi.diffcode.isDirectory() && rdi.diffcode.isDirectory())
926 return strutils::compare_nocase(ColExtGet(pCtxt, p, 0), ColExtGet(pCtxt, q, 0));
930 * @brief Compare folder names.
931 * @param [in] pCtxt Pointer to compare context.
932 * @param [in] p Pointer to DIFFITEM having first folder name to compare.
933 * @param [in] q Pointer to DIFFITEM having second folder name to compare.
934 * @return Compare result.
936 static int ColPathSort(const CDiffContext *pCtxt, const void *p, const void *q, int)
938 return strutils::compare_nocase(ColPathGet(pCtxt, p, 0), ColPathGet(pCtxt, q, 0));
942 * @brief Compare compare results.
943 * @param [in] p Pointer to DIFFITEM having first result to compare.
944 * @param [in] q Pointer to DIFFITEM having second result to compare.
945 * @return Compare result.
947 static int ColStatusSort(const CDiffContext *, const void *p, const void *q, int)
949 const DIFFITEM &ldi = *static_cast<const DIFFITEM *>(p);
950 const DIFFITEM &rdi = *static_cast<const DIFFITEM *>(q);
951 return cmpdiffcode(rdi.diffcode.diffcode, ldi.diffcode.diffcode);
955 * @brief Compare file times.
956 * @param [in] p First time to compare.
957 * @param [in] q Second time to compare.
958 * @return Compare result.
960 static int ColTimeSort(const CDiffContext *, const void *p, const void *q, int)
962 const int64_t &r = *static_cast<const int64_t*>(p);
963 const int64_t &s = *static_cast<const int64_t*>(q);
968 * @brief Compare file sizes.
969 * @param [in] p First size to compare.
970 * @param [in] q Second size to compare.
971 * @return Compare result.
973 static int ColSizeSort(const CDiffContext *, const void *p, const void *q, int)
975 const int64_t &r = *static_cast<const int64_t*>(p);
976 const int64_t &s = *static_cast<const int64_t*>(q);
981 * @brief Compare difference counts.
982 * @param [in] p First count to compare.
983 * @param [in] q Second count to compare.
984 * @return Compare result.
986 static int ColDiffsSort(const CDiffContext *, const void *p, const void *q, int)
988 const int &r = *static_cast<const int*>(p);
989 const int &s = *static_cast<const int*>(q);
994 * @brief Compare newer/older statuses.
995 * @param [in] pCtxt Pointer to compare context.
996 * @param [in] p Pointer to DIFFITEM having first status to compare.
997 * @param [in] q Pointer to DIFFITEM having second status to compare.
998 * @return Compare result.
1000 static int ColNewerSort(const CDiffContext *pCtxt, const void *p, const void *q, int)
1002 return ColNewerGet(pCtxt, p, 0).compare(ColNewerGet(pCtxt, q, 0));
1006 * @brief Compare file versions.
1007 * @param [in] pCtxt Pointer to compare context.
1008 * @param [in] p Pointer to DIFFITEM having first version to compare.
1009 * @param [in] q Pointer to DIFFITEM having second version to compare.
1010 * @return Compare result.
1012 static int ColVersionSort(const CDiffContext *pCtxt, const void *p, const void *q, int opt)
1014 return cmpu64(GetVersionQWORD(pCtxt, reinterpret_cast<const DIFFITEM *>(p), opt), GetVersionQWORD(pCtxt, reinterpret_cast<const DIFFITEM *>(q), opt));
1018 * @brief Compare binary statuses.
1019 * This function returns a comparison of binary results.
1020 * @param [in] p Pointer to DIFFITEM having first status to compare.
1021 * @param [in] q Pointer to DIFFITEM having second status to compare.
1022 * @return Compare result:
1023 * - if both items are text files or binary files: 0
1024 * - if left is text and right is binary: -1
1025 * - if left is binary and right is text: 1
1027 static int ColBinSort(const CDiffContext *, const void *p, const void *q, int)
1029 const DIFFITEM &ldi = *static_cast<const DIFFITEM *>(p);
1030 const DIFFITEM &rdi = *static_cast<const DIFFITEM *>(q);
1031 const bool i = ldi.diffcode.isBin();
1032 const bool j = rdi.diffcode.isBin();
1045 * @brief Compare file flags.
1046 * @param [in] p Pointer to first flag structure to compare.
1047 * @param [in] q Pointer to second flag structure to compare.
1048 * @return Compare result.
1050 static int ColAttrSort(const CDiffContext *, const void *p, const void *q, int)
1052 const FileFlags &r = *static_cast<const FileFlags *>(p);
1053 const FileFlags &s = *static_cast<const FileFlags *>(q);
1054 if (r.attributes == s.attributes)
1056 return r.attributes < s.attributes ? -1 : 1;
1060 * @brief Compare file encodings.
1061 * @param [in] p Pointer to first structure to compare.
1062 * @param [in] q Pointer to second structure to compare.
1063 * @return Compare result.
1065 static int ColEncodingSort(const CDiffContext *, const void *p, const void *q, int)
1067 const DiffFileInfo &r = *static_cast<const DiffFileInfo *>(p);
1068 const DiffFileInfo &s = *static_cast<const DiffFileInfo *>(q);
1069 return FileTextEncoding::Collate(r.encoding, s.encoding);
1073 * @brief Compare property values
1074 * @param [in] p Pointer to first structure to compare.
1075 * @param [in] q Pointer to second structure to compare.
1076 * @return Compare result.
1078 static int ColPropertySort(const CDiffContext *, const void *p, const void *q, int opt)
1080 const DiffFileInfo &r = *static_cast<const DiffFileInfo *>(p);
1081 const DiffFileInfo &s = *static_cast<const DiffFileInfo *>(q);
1082 if (!r.m_pAdditionalProperties && s.m_pAdditionalProperties)
1084 if (r.m_pAdditionalProperties && !s.m_pAdditionalProperties)
1086 if (!r.m_pAdditionalProperties && !s.m_pAdditionalProperties)
1088 return PropertyValues::CompareValues(*r.m_pAdditionalProperties, *s.m_pAdditionalProperties, opt);
1092 * @brief Compare duplicate count
1093 * @param [in] p Pointer to first structure to compare.
1094 * @param [in] q Pointer to second structure to compare.
1095 * @return Compare result.
1097 static int ColPropertyDuplicateCountSort(const CDiffContext* pCtxt, const void* p, const void* q, int opt)
1099 const int index = opt & 0xffff;
1100 const int pane = opt >> 16;
1101 const DiffFileInfo& r = *static_cast<const DiffFileInfo*>(p);
1102 const DiffFileInfo& s = *static_cast<const DiffFileInfo*>(q);
1103 const DuplicateInfo* rinfo = GetDuplicateInfo(pCtxt, r, index);
1104 const DuplicateInfo* sinfo = GetDuplicateInfo(pCtxt, s, index);
1105 if (!rinfo && !sinfo)
1107 if (!rinfo && sinfo)
1109 if (rinfo && !sinfo)
1111 if (rinfo->count[pane] <= 1 && sinfo->count[pane] <= 1)
1113 if (rinfo->count[pane] <= 1 && sinfo->count[pane] > 1)
1115 if (rinfo->count[pane] > 1 && sinfo->count[pane] <= 1)
1117 if (rinfo->count[pane] == sinfo->count[pane])
1119 if (rinfo->groupid == sinfo->groupid)
1121 return rinfo->groupid < sinfo->groupid ? -1 : 1;
1123 return rinfo->count[pane] < sinfo->count[pane] ? -1 : 1;
1127 * @brief Compare all property values
1128 * @param [in] p Pointer to first structure to compare.
1129 * @param [in] q Pointer to second structure to compare.
1130 * @return Compare result.
1132 static int ColAllPropertySort(const CDiffContext *pCtxt, const void *p, const void *q, int opt)
1134 const DIFFITEM &r = *static_cast<const DIFFITEM *>(p);
1135 const DIFFITEM &s = *static_cast<const DIFFITEM *>(q);
1136 for (int i = 0; i < pCtxt->GetCompareDirs(); ++i)
1138 if (r.diffcode.exists(i))
1140 for (int j = 0; j < pCtxt->GetCompareDirs(); ++j)
1142 if (s.diffcode.exists(j))
1144 if (!r.diffFileInfo[i].m_pAdditionalProperties && s.diffFileInfo[j].m_pAdditionalProperties)
1146 if (r.diffFileInfo[i].m_pAdditionalProperties && !s.diffFileInfo[j].m_pAdditionalProperties)
1148 if (!r.diffFileInfo[i].m_pAdditionalProperties && !s.diffFileInfo[j].m_pAdditionalProperties)
1150 int result = PropertyValues::CompareValues(*r.diffFileInfo[i].m_pAdditionalProperties, *s.diffFileInfo[j].m_pAdditionalProperties, opt);
1161 * @brief Compare Hash(Diff) column values
1162 * @param [in] p Pointer to first structure to compare.
1163 * @param [in] q Pointer to second structure to compare.
1164 * @return Compare result.
1166 static int ColPropertyDiffSort(const CDiffContext *pCtxt, const void *p, const void *q, int opt)
1168 int64_t rnumdiff = 0;
1169 int64_t snumdiff = 0;
1170 bool rnumeric = false;
1171 bool snumeric = false;
1172 String r2 = ColPropertyDiffGetEx(pCtxt, p, opt, false, rnumeric, &rnumdiff);
1173 String s2 = ColPropertyDiffGetEx(pCtxt, q, opt, false, snumeric, &snumdiff);
1174 int result = strutils::compare_nocase(r2, s2);
1175 if (pCtxt->GetCompareDirs() == 2 && result == 0 && rnumeric && snumeric)
1177 if (rnumdiff == snumdiff)
1179 return rnumdiff < snumdiff ? -1 : 1;
1185 * @brief Compare Hash(Move) column values
1186 * @param [in] p Pointer to first structure to compare.
1187 * @param [in] q Pointer to second structure to compare.
1188 * @return Compare result.
1190 static int ColPropertyMoveSort(const CDiffContext *pCtxt, const void *p, const void *q, int opt)
1192 String r2 = ColPropertyMoveGet(pCtxt, p, opt);
1193 String s2 = ColPropertyMoveGet(pCtxt, q, opt);
1194 return strutils::compare_nocase(r2, s2);
1199 #undef FIELD_OFFSET // incorrect for Win32 as defined in WinNT.h
1200 #define FIELD_OFFSET(type, field) ((size_t)(LONG_PTR)&(((type *)nullptr)->field))
1203 * @brief All existing folder compare columns.
1205 * This table has information for all folder compare columns. Fields are
1208 * - name resource ID: column's name shown in header
1209 * - description resource ID: columns description text
1210 * - custom function for getting column data
1211 * - custom function for sorting column data
1212 * - parameter for custom functions: DIFFITEM (if `nullptr`) or one of its fields
1213 * - default column order number, -1 if not shown by default
1214 * - ascending (`true`) or descending (`false`) default sort order
1215 * - alignment of column contents: numbers are usually right-aligned
1217 static DirColInfo f_cols[] =
1219 { _T("Name"), nullptr, COLHDR_FILENAME, COLDESC_FILENAME, &ColFileNameGet<String>, &ColFileNameSort, 0, 0, true, DirColInfo::ALIGN_LEFT },
1220 { _T("Path"), "DirView|ColumnHeader", COLHDR_DIR, COLDESC_DIR, &ColPathGet, &ColPathSort, 0, 1, true, DirColInfo::ALIGN_LEFT },
1221 { _T("Status"), nullptr, COLHDR_RESULT, COLDESC_RESULT, &ColStatusGet, &ColStatusSort, 0, 2, true, DirColInfo::ALIGN_LEFT },
1222 { _T("Lmtime"), nullptr, COLHDR_LTIMEM, COLDESC_LTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].mtime), 3, false, DirColInfo::ALIGN_LEFT, 0 },
1223 { _T("Rmtime"), nullptr, COLHDR_RTIMEM, COLDESC_RTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].mtime), 4, false, DirColInfo::ALIGN_LEFT, 1 },
1224 { _T("Lctime"), nullptr, COLHDR_LTIMEC, COLDESC_LTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].ctime), -1, false, DirColInfo::ALIGN_LEFT, 0 },
1225 { _T("Rctime"), nullptr, COLHDR_RTIMEC, COLDESC_RTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].ctime), -1, false, DirColInfo::ALIGN_LEFT, 1 },
1226 { _T("Ext"), nullptr, COLHDR_EXTENSION, COLDESC_EXTENSION, &ColExtGet, &ColExtSort, 0, 5, true, DirColInfo::ALIGN_LEFT },
1227 { _T("Lsize"), nullptr, COLHDR_LSIZE, COLDESC_LSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].size), -1, false, DirColInfo::ALIGN_RIGHT, 0 },
1228 { _T("Rsize"), nullptr, COLHDR_RSIZE, COLDESC_RSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].size), -1, false, DirColInfo::ALIGN_RIGHT, 1 },
1229 { _T("LsizeShort"), nullptr, COLHDR_LSIZE_SHORT, COLDESC_LSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].size), -1, false, DirColInfo::ALIGN_RIGHT, 0 },
1230 { _T("RsizeShort"), nullptr, COLHDR_RSIZE_SHORT, COLDESC_RSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].size), -1, false, DirColInfo::ALIGN_RIGHT, 1 },
1231 { _T("Newer"), nullptr, COLHDR_NEWER, COLDESC_NEWER, &ColNewerGet, &ColNewerSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1232 { _T("Lversion"), nullptr, COLHDR_LVERSION, COLDESC_LVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1233 { _T("Rversion"), nullptr, COLHDR_RVERSION, COLDESC_RVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1234 { _T("StatusAbbr"), nullptr, COLHDR_RESULT_ABBR, COLDESC_RESULT_ABBR, &ColStatusAbbrGet, &ColStatusSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1235 { _T("Binary"), "DirView|ColumnHeader", COLHDR_BINARY, COLDESC_BINARY, &ColBinGet, &ColBinSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1236 { _T("Lattr"), nullptr, COLHDR_LATTRIBUTES, COLDESC_LATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].flags), -1, true, DirColInfo::ALIGN_LEFT, 0 },
1237 { _T("Rattr"), nullptr, COLHDR_RATTRIBUTES, COLDESC_RATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].flags), -1, true, DirColInfo::ALIGN_LEFT, 1 },
1238 { _T("Lencoding"), nullptr, COLHDR_LENCODING, COLDESC_LENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0]), -1, true, DirColInfo::ALIGN_LEFT, 0 },
1239 { _T("Rencoding"), nullptr, COLHDR_RENCODING, COLDESC_RENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1]), -1, true, DirColInfo::ALIGN_LEFT, 1 },
1240 { _T("Snsdiffs"), nullptr, COLHDR_NSDIFFS, COLDESC_NSDIFFS, ColDiffsGet, ColDiffsSort, FIELD_OFFSET(DIFFITEM, nsdiffs), -1, false, DirColInfo::ALIGN_RIGHT },
1241 { _T("Snidiffs"), nullptr, COLHDR_NIDIFFS, COLDESC_NIDIFFS, ColDiffsGet, ColDiffsSort, FIELD_OFFSET(DIFFITEM, nidiffs), -1, false, DirColInfo::ALIGN_RIGHT },
1242 { _T("Leoltype"), nullptr, COLHDR_LEOL_TYPE, COLDESC_LEOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1243 { _T("Reoltype"), nullptr, COLHDR_REOL_TYPE, COLDESC_REOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1244 { _T("Unpacker"), nullptr, COLHDR_UNPACKER, COLDESC_UNPACKER, &ColPluginPipelineGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1245 { _T("Prediffer"), nullptr, COLHDR_PREDIFFER, COLDESC_PREDIFFER, &ColPluginPipelineGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1247 static DirColInfo f_cols3[] =
1249 { _T("Name"), nullptr, COLHDR_FILENAME, COLDESC_FILENAME, &ColFileNameGet<String>, &ColFileNameSort, 0, 0, true, DirColInfo::ALIGN_LEFT },
1250 { _T("Path"), "DirView|ColumnHeader", COLHDR_DIR, COLDESC_DIR, &ColPathGet, &ColPathSort, 0, 1, true, DirColInfo::ALIGN_LEFT },
1251 { _T("Status"), nullptr, COLHDR_RESULT, COLDESC_RESULT, &ColStatusGet, &ColStatusSort, 0, 2, true, DirColInfo::ALIGN_LEFT },
1252 { _T("Lmtime"), nullptr, COLHDR_LTIMEM, COLDESC_LTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].mtime), 3, false, DirColInfo::ALIGN_LEFT, 0 },
1253 { _T("Mmtime"), nullptr, COLHDR_MTIMEM, COLDESC_MTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].mtime), 4, false, DirColInfo::ALIGN_LEFT, 1 },
1254 { _T("Rmtime"), nullptr, COLHDR_RTIMEM, COLDESC_RTIMEM, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].mtime), 5, false, DirColInfo::ALIGN_LEFT, 2 },
1255 { _T("Lctime"), nullptr, COLHDR_LTIMEC, COLDESC_LTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].ctime), -1, false, DirColInfo::ALIGN_LEFT, 0 },
1256 { _T("Mctime"), nullptr, COLHDR_MTIMEC, COLDESC_MTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].ctime), -1, false, DirColInfo::ALIGN_LEFT, 1 },
1257 { _T("Rctime"), nullptr, COLHDR_RTIMEC, COLDESC_RTIMEC, &ColTimeGet, &ColTimeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].ctime), -1, false, DirColInfo::ALIGN_LEFT, 2 },
1258 { _T("Ext"), nullptr, COLHDR_EXTENSION, COLDESC_EXTENSION, &ColExtGet, &ColExtSort, 0, 6, true, DirColInfo::ALIGN_LEFT },
1259 { _T("Lsize"), nullptr, COLHDR_LSIZE, COLDESC_LSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].size), -1, false, DirColInfo::ALIGN_RIGHT, 0 },
1260 { _T("Msize"), nullptr, COLHDR_MSIZE, COLDESC_MSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].size), -1, false, DirColInfo::ALIGN_RIGHT, 1 },
1261 { _T("Rsize"), nullptr, COLHDR_RSIZE, COLDESC_RSIZE, &ColSizeGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].size), -1, false, DirColInfo::ALIGN_RIGHT, 2 },
1262 { _T("LsizeShort"), nullptr, COLHDR_LSIZE_SHORT, COLDESC_LSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].size), -1, false, DirColInfo::ALIGN_RIGHT, 0 },
1263 { _T("MsizeShort"), nullptr, COLHDR_MSIZE_SHORT, COLDESC_MSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].size), -1, false, DirColInfo::ALIGN_RIGHT, 1 },
1264 { _T("RsizeShort"), nullptr, COLHDR_RSIZE_SHORT, COLDESC_RSIZE_SHORT, &ColSizeShortGet, &ColSizeSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].size), -1, false, DirColInfo::ALIGN_RIGHT, 2 },
1265 { _T("Newer"), nullptr, COLHDR_NEWER, COLDESC_NEWER, &ColNewerGet, &ColNewerSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1266 { _T("Lversion"), nullptr, COLHDR_LVERSION, COLDESC_LVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1267 { _T("Mversion"), nullptr, COLHDR_MVERSION, COLDESC_MVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1268 { _T("Rversion"), nullptr, COLHDR_RVERSION, COLDESC_RVERSION, &ColVersionGet, &ColVersionSort, 0, -1, true, DirColInfo::ALIGN_LEFT, 2 },
1269 { _T("StatusAbbr"), nullptr, COLHDR_RESULT_ABBR, COLDESC_RESULT_ABBR, &ColStatusAbbrGet, &ColStatusSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1270 { _T("Binary"), "DirView|ColumnHeader", COLHDR_BINARY, COLDESC_BINARY, &ColBinGet, &ColBinSort, 0, -1, true, DirColInfo::ALIGN_LEFT },
1271 { _T("Lattr"), nullptr, COLHDR_LATTRIBUTES, COLDESC_LATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0].flags), -1, true, DirColInfo::ALIGN_LEFT, 0 },
1272 { _T("Mattr"), nullptr, COLHDR_MATTRIBUTES, COLDESC_MATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1].flags), -1, true, DirColInfo::ALIGN_LEFT, 1 },
1273 { _T("Rattr"), nullptr, COLHDR_RATTRIBUTES, COLDESC_RATTRIBUTES, &ColAttrGet, &ColAttrSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2].flags), -1, true, DirColInfo::ALIGN_LEFT, 2 },
1274 { _T("Lencoding"), nullptr, COLHDR_LENCODING, COLDESC_LENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[0]), -1, true, DirColInfo::ALIGN_LEFT, 0 },
1275 { _T("Mencoding"), nullptr, COLHDR_MENCODING, COLDESC_MENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[1]), -1, true, DirColInfo::ALIGN_LEFT, 1 },
1276 { _T("Rencoding"), nullptr, COLHDR_RENCODING, COLDESC_RENCODING, &ColEncodingGet, &ColEncodingSort, FIELD_OFFSET(DIFFITEM, diffFileInfo[2]), -1, true, DirColInfo::ALIGN_LEFT, 2 },
1277 { _T("Snsdiffs"), nullptr, COLHDR_NSDIFFS, COLDESC_NSDIFFS, ColDiffsGet, ColDiffsSort, FIELD_OFFSET(DIFFITEM, nsdiffs), -1, false, DirColInfo::ALIGN_RIGHT },
1278 { _T("Snidiffs"), nullptr, COLHDR_NIDIFFS, COLDESC_NIDIFFS, ColDiffsGet, ColDiffsSort, FIELD_OFFSET(DIFFITEM, nidiffs), -1, false, DirColInfo::ALIGN_RIGHT },
1279 { _T("Leoltype"), nullptr, COLHDR_LEOL_TYPE, COLDESC_LEOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 0 },
1280 { _T("Meoltype"), nullptr, COLHDR_MEOL_TYPE, COLDESC_MEOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 1 },
1281 { _T("Reoltype"), nullptr, COLHDR_REOL_TYPE, COLDESC_REOL_TYPE, &ColEOLTypeGet, 0, 0, -1, true, DirColInfo::ALIGN_LEFT, 2 },
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 },
1286 String DirColInfo::GetDisplayName() const
1289 return tr(idNameContext, idName);
1290 PropertySystem ps({ regName + 1 });
1291 std::vector<String> names;
1292 ps.GetDisplayNames(names);
1293 String name = names[0];
1294 if (regName[0] != 'A')
1298 (regName[0] == 'L') ? _("Left") :
1299 (regName[0] == 'R') ? _("Right") :
1300 (regName[0] == 'M') ? _("Middle") :
1301 (regName[0] == 'D') ? _("Diff") :
1302 (regName[0] == 'l') ? _("Left Duplicate Count") :
1303 (regName[0] == 'r') ? _("Right Duplicate Count") :
1304 (regName[0] == 'm') ? _("Middle Duplicate Count") :
1305 (regName[0] == 'N') ? _("Move") : _("");
1311 String DirColInfo::GetDescription() const
1315 return GetDisplayName();
1318 DirViewColItems::DirViewColItems(int nDirs, const std::vector<String>& additionalPropertyNames) :
1319 m_nDirs(nDirs), m_dispcols(-1)
1321 m_numcols = static_cast<int>(nDirs < 3 ? std::size(f_cols) : std::size(f_cols3));
1322 DirColInfo *pcol = nDirs < 3 ? f_cols : f_cols3;
1323 for (size_t i = 0; i < m_numcols; ++i)
1324 m_cols.push_back(pcol[i]);
1325 ResetColumnOrdering();
1326 PropertySystem ps(additionalPropertyNames);
1327 for (const auto& propertyName : ps.GetCanonicalNames())
1328 AddAdditionalPropertyName(propertyName);
1332 DirViewColItems::AddAdditionalPropertyName(const String& propertyName)
1334 const int nfixedcols = static_cast<int>(m_nDirs < 3 ? std::size(f_cols) : std::size(f_cols3));
1335 int fixedColPhysicalIndexMax = 0;
1336 for (int i = 0; i < nfixedcols; ++i)
1337 fixedColPhysicalIndexMax = (std::max)(fixedColPhysicalIndexMax, m_cols[i].physicalIndex);
1339 String cList = (m_nDirs < 3) ? String(_T("ADLR")) : String(_T("ADLMR"));
1340 if (propertyName.substr(0, 5) == _T("Hash."))
1341 cList += (m_nDirs < 3) ? String(_T("Nlr")) : String(_T("lmr"));
1342 for (auto c : cList)
1344 m_cols.emplace_back(DirColInfo{});
1345 m_strpool.push_back(c + propertyName);
1346 auto& col = m_cols.back();
1347 col.regName = m_strpool.back().c_str();
1348 col.opt = static_cast<int>(m_additionalPropertyNames.size());
1351 col.physicalIndex = fixedColPhysicalIndexMax + 1 + col.opt;
1353 col.getfnc = ColAllPropertyGet;
1354 col.sortfnc = ColAllPropertySort;
1358 col.physicalIndex = -1;
1360 col.getfnc = ColPropertyDiffGet;
1361 col.sortfnc = ColPropertyDiffSort;
1365 col.physicalIndex = -1;
1367 col.getfnc = ColPropertyMoveGet;
1368 col.sortfnc = ColPropertyMoveSort;
1372 col.physicalIndex = -1;
1373 col.offset = FIELD_OFFSET(DIFFITEM, diffFileInfo[pane]);
1374 if (c == 'L' || c == 'M' || c == 'R')
1376 col.getfnc = ColPropertyGet;
1377 col.sortfnc = ColPropertySort;
1381 col.opt |= pane << 16;
1382 col.getfnc = ColPropertyDuplicateCountGet;
1383 col.sortfnc = ColPropertyDuplicateCountSort;
1385 pane = (pane + 1) % m_nDirs;
1387 m_invcolorder.push_back(-1);
1389 m_invcolorder[m_dispcols] = m_numcols;
1390 m_colorder.push_back(c == 'A' ? m_dispcols++ : -1);
1393 m_additionalPropertyNames.push_back(propertyName);
1397 DirViewColItems::RemoveAdditionalPropertyName(const String& propertyName)
1399 const int nfixedcols = static_cast<int>(m_nDirs < 3 ? std::size(f_cols) : std::size(f_cols3));
1400 std::vector<int> deletedPhysicalIndexes;
1401 std::vector<int> deletedLogicalIndexes;
1402 int deletedOpt = -1;
1403 for (int i = static_cast<int>(m_cols.size()) - 1; i >= nfixedcols; --i)
1405 if (m_cols[i].regName + 1 == propertyName)
1407 deletedLogicalIndexes.push_back(static_cast<int>(i));
1408 if (m_colorder[i] >= 0)
1410 deletedPhysicalIndexes.push_back(m_colorder[i]);
1413 deletedOpt = m_cols[i].opt;
1414 m_cols.erase(m_cols.begin() + i);
1418 for (int i = static_cast<int>(m_invcolorder.size()) - 1; i >= 0; --i)
1420 auto it = std::find(deletedLogicalIndexes.begin(), deletedLogicalIndexes.end(), m_invcolorder[i]);
1421 if (it != deletedLogicalIndexes.end())
1422 m_invcolorder.erase(m_invcolorder.begin() + i);
1425 int logicalIndex = m_invcolorder[i];
1426 for (auto deletedLogicalIndex : deletedLogicalIndexes)
1428 if (deletedLogicalIndex < logicalIndex)
1433 for (int i = static_cast<int>(m_colorder.size()) - 1; i >= 0; --i)
1435 auto it = std::find(deletedLogicalIndexes.begin(), deletedLogicalIndexes.end(), i);
1436 if (it != deletedLogicalIndexes.end())
1437 m_colorder.erase(m_colorder.begin() + i);
1440 int physicalIndex = m_colorder[i];
1441 for (auto deletedPhysicalIndex : deletedPhysicalIndexes)
1443 if (deletedPhysicalIndex < physicalIndex)
1448 for (int i = static_cast<int>(m_cols.size()) - 1; i >= nfixedcols; --i)
1450 if (deletedOpt < m_cols[i].opt)
1453 for (size_t i = 0; i < m_additionalPropertyNames.size(); ++i)
1455 if (m_additionalPropertyNames[i] == propertyName)
1456 m_additionalPropertyNames.erase(m_additionalPropertyNames.begin() + i);
1458 m_colorder.resize(m_numcols);
1459 m_invcolorder.resize(m_numcols);
1460 if (m_dispcols <= 0)
1461 ResetColumnOrdering();
1465 DirViewColItems::SetAdditionalPropertyNames(const std::vector<String>& additionalPropertyNames)
1467 for (int i = static_cast<int>(m_additionalPropertyNames.size()) - 1; i >= 0; i--)
1469 auto it = std::find(additionalPropertyNames.begin(), additionalPropertyNames.end(), m_additionalPropertyNames[i]);
1470 if (it == additionalPropertyNames.end())
1471 RemoveAdditionalPropertyName(m_additionalPropertyNames[i]);
1473 for (const auto& propertyName : additionalPropertyNames)
1475 auto it = std::find(m_additionalPropertyNames.begin(), m_additionalPropertyNames.end(), propertyName);
1476 if (it == m_additionalPropertyNames.end())
1477 AddAdditionalPropertyName(propertyName);
1482 * @brief Registry base value name for saving/loading info for this column
1485 DirViewColItems::GetColRegValueNameBase(int col) const
1487 assert(col>=0 && col<m_numcols);
1488 return strutils::format(_T("WDirHdr_%s"), m_cols[col].regName);
1492 * @brief Get default physical order for specified logical column
1495 DirViewColItems::GetColDefaultOrder(int col) const
1497 assert(col>=0 && col<m_numcols);
1498 return m_cols[col].physicalIndex;
1502 * @brief Return the info about the specified physical column
1505 DirViewColItems::GetDirColInfo(int col) const
1507 if (col < 0 || col >= m_numcols)
1509 assert(false); // fix caller, should not ask for nonexistent columns
1512 return &m_cols[col];
1516 * @brief Check if specified physical column has specified resource id name
1519 DirViewColItems::IsColById(int col, const char *idname) const
1521 if (col < 0 || col >= m_numcols)
1523 assert(false); // fix caller, should not ask for nonexistent columns
1526 return m_cols[col].idName == idname;
1530 * @brief Is specified physical column the name column?
1533 DirViewColItems::IsColName(int col) const
1535 return IsColById(col, COLHDR_FILENAME);
1538 * @brief Is specified physical column the left modification time column?
1541 DirViewColItems::IsColLmTime(int col) const
1543 return IsColById(col, COLHDR_LTIMEM);
1546 * @brief Is specified physical column the middle modification time column?
1549 DirViewColItems::IsColMmTime(int col) const
1551 return IsColById(col, COLHDR_MTIMEM);
1554 * @brief Is specified physical column the right modification time column?
1557 DirViewColItems::IsColRmTime(int col) const
1559 return IsColById(col, COLHDR_RTIMEM);
1562 * @brief Is specified physical column the full status (result) column?
1565 DirViewColItems::IsColStatus(int col) const
1567 return IsColById(col, COLHDR_RESULT);
1570 * @brief Is specified physical column the full status (result) column?
1573 DirViewColItems::IsColStatusAbbr(int col) const
1575 return IsColById(col, COLHDR_RESULT_ABBR);
1579 * @brief return whether column normally sorts ascending (dates do not)
1582 DirViewColItems::IsDefaultSortAscending(int col) const
1584 const DirColInfo * pColInfo = GetDirColInfo(col);
1585 if (pColInfo == nullptr)
1587 assert(false); // fix caller, should not ask for nonexistent columns
1590 return pColInfo->defSortUp;
1594 * @brief Return display name of column
1597 DirViewColItems::GetColDisplayName(int col) const
1599 const DirColInfo * colinfo = GetDirColInfo(col);
1600 return colinfo ? colinfo->GetDisplayName() : _T("");
1604 * @brief Return description of column
1607 DirViewColItems::GetColDescription(int col) const
1609 const DirColInfo * colinfo = GetDirColInfo(col);
1610 return colinfo ? colinfo->GetDescription() : _T("");
1614 * @brief Get text for specified column.
1615 * This function retrieves the text for the specified colum. Text is
1616 * retrieved by using column-specific handler functions.
1617 * @param [in] pCtxt Compare context.
1618 * @param [in] col Column number.
1619 * @param [in] di Difference data.
1620 * @return Text for the specified column.
1623 DirViewColItems::ColGetTextToDisplay(const CDiffContext *pCtxt, int col,
1624 const DIFFITEM &di) const
1626 // Custom properties have custom get functions
1627 const DirColInfo * pColInfo = GetDirColInfo(col);
1628 if (pColInfo == nullptr)
1630 assert(false); // fix caller, should not ask for nonexistent columns
1633 ColGetFncPtrType fnc = pColInfo->getfnc;
1634 size_t offset = pColInfo->offset;
1635 String s = (*fnc)(pCtxt, reinterpret_cast<const char *>(&di) + offset, pColInfo->opt);
1637 // Add '*' to newer time field
1638 if (IsColLmTime(col) || IsColMmTime(col) || IsColRmTime(col))
1642 if (di.diffFileInfo[0].mtime != 0 || di.diffFileInfo[1].mtime != 0)
1646 IsColLmTime(col) && di.diffFileInfo[0].mtime > di.diffFileInfo[1].mtime // Left modification time
1647 || IsColRmTime(col) && di.diffFileInfo[0].mtime < di.diffFileInfo[1].mtime // Right modification time
1650 s.insert(0, _T("* "));
1654 s.insert(0, _T(" ")); // Looks best with a fixed-font, but not too bad otherwise
1656 // GreyMerlin (14 Nov 2009) - the flagging of Date needs to be done with
1657 // something not involving extra characters. Perhaps <red> for oldest,
1658 // <green> for newest. Note (20 March 2017): the introduction of 3-Way
1659 // Merge and the yellow difference highlighting adds to the design
1660 // difficulty of any changes. So maybe this "* "/" " scheme is good enough.
1666 if (di.diffFileInfo[0].mtime != 0 || di.diffFileInfo[1].mtime != 0 || di.diffFileInfo[2].mtime != 0)
1670 IsColLmTime(col) && di.diffFileInfo[0].mtime > di.diffFileInfo[1].mtime && di.diffFileInfo[0].mtime > di.diffFileInfo[2].mtime // Left modification time
1671 || IsColMmTime(col) && di.diffFileInfo[1].mtime > di.diffFileInfo[0].mtime && di.diffFileInfo[1].mtime > di.diffFileInfo[2].mtime // Middle modification time
1672 || IsColRmTime(col) && di.diffFileInfo[2].mtime > di.diffFileInfo[0].mtime && di.diffFileInfo[2].mtime > di.diffFileInfo[1].mtime // Right modification time
1675 s.insert(0, _T("* "));
1679 s.insert(0, _T(" ")); // Looks best with a fixed-font, but not too bad otherwise
1681 // GreyMerlin (14 Nov 2009) - See note above.
1692 * @brief Sort two items on specified column.
1693 * This function determines order of two items in specified column. Order
1694 * is determined by column-specific functions.
1695 * @param [in] pCtxt Compare context.
1696 * @param [in] col Column number to sort.
1697 * @param [in] ldi Left difference item data.
1698 * @param [in] rdi Right difference item data.
1699 * @return Order of items.
1702 DirViewColItems::ColSort(const CDiffContext *pCtxt, int col, const DIFFITEM &ldi,
1703 const DIFFITEM &rdi, bool bTreeMode) const
1705 // Custom properties have custom sort functions
1706 const DirColInfo * pColInfo = GetDirColInfo(col);
1707 if (pColInfo == nullptr)
1709 assert(false); // fix caller, should not ask for nonexistent columns
1712 size_t offset = pColInfo->offset;
1713 int opt = pColInfo->opt;
1718 int lLevel = ldi.GetDepth();
1719 int rLevel = rdi.GetDepth();
1720 const DIFFITEM *lcur = &ldi, *rcur = &rdi;
1721 if (lLevel < rLevel)
1723 for (; lLevel != rLevel; rLevel--)
1724 rcur = rcur->GetParentLink();
1726 else if (rLevel < lLevel)
1728 for (; lLevel != rLevel; lLevel--)
1729 lcur = lcur->GetParentLink();
1731 while (lcur->GetParentLink() != rcur->GetParentLink())
1733 lcur = lcur->GetParentLink();
1734 rcur = rcur->GetParentLink();
1736 arg1 = reinterpret_cast<const char *>(lcur) + offset;
1737 arg2 = reinterpret_cast<const char *>(rcur) + offset;
1741 arg1 = reinterpret_cast<const char *>(&ldi) + offset;
1742 arg2 = reinterpret_cast<const char *>(&rdi) + offset;
1744 if (ColSortFncPtrType fnc = pColInfo->sortfnc)
1746 return (*fnc)(pCtxt, arg1, arg2, opt);
1748 if (ColGetFncPtrType fnc = pColInfo->getfnc)
1750 String p = (*fnc)(pCtxt, arg1, opt);
1751 String q = (*fnc)(pCtxt, arg2, opt);
1752 return strutils::compare_nocase(p, q);
1757 void DirViewColItems::SetColumnOrdering(const int colorder[])
1760 for (int i = 0; i < m_numcols; ++i)
1762 m_colorder[i] = colorder[i];
1763 int phy = m_colorder[i];
1767 m_invcolorder[phy] = i;
1773 * @brief Set column ordering to default initial order
1775 void DirViewColItems::ResetColumnOrdering()
1777 ClearColumnOrders();
1779 for (int i=0; i<m_numcols; ++i)
1781 int phy = GetColDefaultOrder(i);
1782 m_colorder[i] = phy;
1785 m_invcolorder[phy] = i;
1792 * @brief Reset all current column ordering information
1794 void DirViewColItems::ClearColumnOrders()
1796 m_colorder.resize(m_numcols);
1797 m_invcolorder.resize(m_numcols);
1798 for (int i=0; i<m_numcols; ++i)
1801 m_invcolorder[i] = -1;
1806 * @brief Remove any windows reordering of columns (params are physical columns)
1808 void DirViewColItems::MoveColumn(int psrc, int pdest)
1810 // actually moved column
1811 m_colorder[m_invcolorder[psrc]] = pdest;
1812 // shift all other affected columns
1813 int dir = psrc > pdest ? +1 : -1;
1815 for (i=pdest; i!=psrc; i += dir)
1817 m_colorder[m_invcolorder[i]] = i+dir;
1819 // fix inverse mapping
1820 for (i=0; i<m_numcols; ++i)
1822 if (m_colorder[i] >= 0)
1823 m_invcolorder[m_colorder[i]] = i;
1828 * @brief Resets column widths to defaults.
1830 String DirViewColItems::ResetColumnWidths(int defcolwidth)
1833 for (int i = 0; i < m_numcols; i++)
1835 if (!result.empty()) result += ' ';
1836 result += strutils::to_str(defcolwidth);
1842 * @brief Load column orders from registry
1844 void DirViewColItems::LoadColumnOrders(const String& colOrders)
1846 ClearColumnOrders();
1848 std::basic_istringstream<TCHAR> ss(colOrders);
1850 // Load column orders
1851 // Break out if one is missing
1852 // Break out & mark failure (m_dispcols == -1) if one is invalid
1854 for (i=0; i<m_numcols; ++i)
1858 if (ord<-1 || ord >= m_numcols)
1860 m_colorder[i] = ord;
1864 if (m_invcolorder[ord] != -1)
1869 m_invcolorder[ord] = i;
1872 // Check that a contiguous range was set
1873 for (i=0; i<m_dispcols; ++i)
1875 if (m_invcolorder[i] < 0)
1881 // Must have at least one column
1884 ResetColumnOrdering();
1888 /// store current column orders into registry
1889 String DirViewColItems::SaveColumnOrders()
1891 assert(static_cast<int>(m_colorder.size()) == m_numcols);
1892 assert(static_cast<int>(m_invcolorder.size()) == m_numcols);
1893 return strutils::join<String (*)(int)>(m_colorder.begin(), m_colorder.end(), _T(" "), strutils::to_str);