OSDN Git Service

c8e3e153bda8f4785c1ccef8144129cec6c495a3
[tortoisegit/TortoiseGitJp.git] / TortoiseProc / GitStatusListCtrlHelpers.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2008 - TortoiseSVN\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 \r
20 #include "stdafx.h"\r
21 #include ".\resource.h"\r
22 #include "GitStatusListCtrl.h"\r
23 #include <iterator>\r
24 // assign property list\r
25 #if 0\r
26 CGitStatusListCtrl::PropertyList& \r
27 CGitStatusListCtrl::PropertyList::operator= (const char* rhs)\r
28 {\r
29         // do you really want to replace the property list?\r
30 \r
31         assert (properties.empty());\r
32         properties.clear();\r
33 \r
34         // add all properties in the list\r
35 \r
36         while ((rhs != NULL) && (*rhs != 0))\r
37         {\r
38                 const char* next = strchr (rhs, ' ');\r
39 \r
40                 CString name (rhs, static_cast<int>(next == NULL ? strlen (rhs) : next - rhs));\r
41                 properties.insert (std::make_pair (name, CString()));\r
42 \r
43                 rhs = next == NULL ? NULL : next+1;\r
44         }\r
45 \r
46         // done\r
47 \r
48         return *this;\r
49 }\r
50 \r
51 // collect property names in a set\r
52 \r
53 void CGitStatusListCtrl::PropertyList::GetPropertyNames (std::set<CString>& names)\r
54 {\r
55         for ( CIT iter = properties.begin(), end = properties.end()\r
56                 ; iter != end\r
57                 ; ++iter)\r
58         {\r
59                 names.insert (iter->first);\r
60         }\r
61 }\r
62 \r
63 // get a property value. \r
64 \r
65 CString CGitStatusListCtrl::PropertyList::operator[](const CString& name) const\r
66 {\r
67         CIT iter = properties.find (name);\r
68 \r
69         return iter == properties.end()\r
70                 ? CString()\r
71                 : iter->second;\r
72 }\r
73 \r
74 // set a property value.\r
75 \r
76 CString& CGitStatusListCtrl::PropertyList::operator[](const CString& name)\r
77 {\r
78         return properties[name];\r
79 }\r
80 \r
81 /// check whether that property has been set on this item.\r
82 \r
83 bool CGitStatusListCtrl::PropertyList::HasProperty (const CString& name) const\r
84 {\r
85         return properties.find (name) != properties.end();\r
86 }\r
87 \r
88 // due to frequent use: special check for svn:needs-lock\r
89 \r
90 bool CGitStatusListCtrl::PropertyList::IsNeedsLockSet() const\r
91 {\r
92         static const CString svnNeedsLock = _T("svn:needs-lock");\r
93         return HasProperty (svnNeedsLock);\r
94 }\r
95 \r
96 #endif\r
97 // registry access\r
98 \r
99 void CGitStatusListCtrl::ColumnManager::ReadSettings \r
100     ( DWORD defaultColumns\r
101     , const CString& containerName)\r
102 {\r
103     // defaults\r
104 \r
105     DWORD selectedStandardColumns = defaultColumns;\r
106 \r
107     columns.resize (SVNSLC_NUMCOLUMNS);\r
108     for (size_t i = 0; i < SVNSLC_NUMCOLUMNS; ++i)\r
109     {\r
110         columns[i].index = static_cast<int>(i);\r
111         columns[i].width = 0;\r
112         columns[i].visible = true;\r
113         columns[i].relevant = true;\r
114     }\r
115 \r
116     userProps.clear();\r
117 \r
118     // where the settings are stored within the registry\r
119 \r
120     registryPrefix \r
121         = _T("Software\\TortoiseGit\\StatusColumns\\") + containerName;\r
122 \r
123     // we accept settings version 2 only\r
124     // (version 1 used different placement of hidden columns)\r
125 \r
126         bool valid = (DWORD)CRegDWORD (registryPrefix + _T("Version"), 0xff) == 2;\r
127     if (valid)\r
128     {\r
129         // read (possibly different) column selection\r
130 \r
131                 selectedStandardColumns \r
132             = CRegDWORD (registryPrefix, selectedStandardColumns);\r
133 \r
134         // read user-prop lists\r
135 \r
136         CString userPropList \r
137             = CRegString (registryPrefix + _T("UserProps"));\r
138         CString shownUserProps \r
139             = CRegString (registryPrefix + _T("ShownUserProps"));\r
140 \r
141         ParseUserPropSettings (userPropList, shownUserProps);\r
142 \r
143         // read column widths\r
144 \r
145         CString colWidths\r
146             = CRegString (registryPrefix + _T("_Width"));\r
147 \r
148         ParseWidths (colWidths);\r
149     }\r
150 \r
151     // process old-style visibility setting\r
152 \r
153     SetStandardColumnVisibility (selectedStandardColumns);\r
154 \r
155         // clear all previously set header columns\r
156 \r
157         int c = ((CHeaderCtrl*)(control->GetDlgItem(0)))->GetItemCount()-1;\r
158         while (c>=0)\r
159                 control->DeleteColumn(c--);\r
160 \r
161     // create columns\r
162 \r
163     for (int i = 0, count = GetColumnCount(); i < count; ++i)\r
164                 control->InsertColumn (i, GetName(i), LVCFMT_LEFT, IsVisible(i) ? -1 : GetVisibleWidth(i, false));\r
165 \r
166     // restore column ordering\r
167 \r
168     if (valid)\r
169         ParseColumnOrder (CRegString (registryPrefix + _T("_Order")));\r
170     else\r
171         ParseColumnOrder (CString());\r
172 \r
173     ApplyColumnOrder();\r
174 \r
175     // auto-size the columns so we can see them while fetching status\r
176     // (seems the same values will not take affect in InsertColumn)\r
177 \r
178     for (int i = 0, count = GetColumnCount(); i < count; ++i)\r
179         if (IsVisible(i))\r
180                     control->SetColumnWidth (i, GetVisibleWidth (i, true));\r
181 }\r
182 \r
183 void CGitStatusListCtrl::ColumnManager::WriteSettings() const\r
184 {\r
185     // we are version 2\r
186 \r
187         CRegDWORD regVersion (registryPrefix + _T("Version"), 0, TRUE);\r
188     regVersion = 2;\r
189 \r
190     // write (possibly different) column selection\r
191 \r
192     CRegDWORD regStandardColumns (registryPrefix, 0, TRUE);\r
193     regStandardColumns = GetSelectedStandardColumns();\r
194 \r
195     // write user-prop lists\r
196 \r
197     CRegString regUserProps (registryPrefix + _T("UserProps"), CString(), TRUE);\r
198     regUserProps = GetUserPropList();\r
199 \r
200     CRegString regShownUserProps (registryPrefix + _T("ShownUserProps"), CString(), TRUE);\r
201     regShownUserProps = GetShownUserProps();\r
202 \r
203     // write column widths\r
204 \r
205     CRegString regWidths (registryPrefix + _T("_Width"), CString(), TRUE);\r
206     regWidths = GetWidthString();\r
207 \r
208     // write column ordering\r
209 \r
210     CRegString regColumnOrder (registryPrefix + _T("_Order"), CString(), TRUE);\r
211     regColumnOrder = GetColumnOrderString();\r
212 }\r
213 \r
214 // read column definitions\r
215 \r
216 int CGitStatusListCtrl::ColumnManager::GetColumnCount() const\r
217 {\r
218     return static_cast<int>(columns.size());\r
219 }\r
220 \r
221 bool CGitStatusListCtrl::ColumnManager::IsVisible (int column) const\r
222 {\r
223     size_t index = static_cast<size_t>(column);\r
224     assert (columns.size() > index);\r
225 \r
226     return columns[index].visible;\r
227 }\r
228 \r
229 int CGitStatusListCtrl::ColumnManager::GetInvisibleCount() const\r
230 {\r
231         int invisibleCount = 0;\r
232         for (std::vector<ColumnInfo>::const_iterator it = columns.begin(); it != columns.end(); ++it)\r
233         {\r
234                 if (!it->visible)\r
235                         invisibleCount++;\r
236         }\r
237         return invisibleCount;\r
238 }\r
239 \r
240 bool CGitStatusListCtrl::ColumnManager::IsRelevant (int column) const\r
241 {\r
242     size_t index = static_cast<size_t>(column);\r
243     assert (columns.size() > index);\r
244 \r
245     return columns[index].relevant;\r
246 }\r
247 \r
248 bool CGitStatusListCtrl::ColumnManager::IsUserProp (int column) const\r
249 {\r
250     size_t index = static_cast<size_t>(column);\r
251     assert (columns.size() > index);\r
252 \r
253     return columns[index].index >= SVNSLC_USERPROPCOLOFFSET;\r
254 }\r
255 \r
256 CString CGitStatusListCtrl::ColumnManager::GetName (int column) const\r
257 {\r
258     static const UINT standardColumnNames[SVNSLC_NUMCOLUMNS] \r
259         = { IDS_STATUSLIST_COLFILE\r
260 \r
261           , IDS_STATUSLIST_COLFILENAME\r
262           , IDS_STATUSLIST_COLEXT\r
263                   , IDS_STATUSLIST_COLSTATUS\r
264 \r
265                   , IDS_STATUSLIST_COLREMOTESTATUS\r
266                   , IDS_STATUSLIST_COLTEXTSTATUS\r
267                   , IDS_STATUSLIST_COLPROPSTATUS\r
268 \r
269                   , IDS_STATUSLIST_COLREMOTETEXTSTATUS\r
270                   , IDS_STATUSLIST_COLREMOTEPROPSTATUS\r
271                   , IDS_STATUSLIST_COLURL\r
272 \r
273                   , IDS_STATUSLIST_COLLOCK\r
274                   , IDS_STATUSLIST_COLLOCKCOMMENT\r
275                   , IDS_STATUSLIST_COLAUTHOR\r
276 \r
277                   , IDS_STATUSLIST_COLREVISION\r
278                   , IDS_STATUSLIST_COLREMOTEREVISION\r
279                   , IDS_STATUSLIST_COLDATE\r
280                   , IDS_STATUSLIST_COLSVNLOCK\r
281 \r
282                   , IDS_STATUSLIST_COLCOPYFROM\r
283           , IDS_STATUSLIST_COLMODIFICATIONDATE};\r
284 \r
285     // standard columns\r
286 \r
287     size_t index = static_cast<size_t>(column);\r
288     if (index < SVNSLC_NUMCOLUMNS)\r
289     {\r
290         CString result;\r
291         result.LoadString (standardColumnNames[index]);\r
292         return result;\r
293     }\r
294 \r
295     // user-prop columns\r
296 \r
297     if (index < columns.size())\r
298         return userProps[columns[index].index - SVNSLC_USERPROPCOLOFFSET].name;\r
299 \r
300     // default: empty\r
301 \r
302     return CString();\r
303 }\r
304 \r
305 int CGitStatusListCtrl::ColumnManager::GetWidth (int column, bool useDefaults) const\r
306 {\r
307     size_t index = static_cast<size_t>(column);\r
308     assert (columns.size() > index);\r
309 \r
310     int width = columns[index].width;\r
311     if ((width == 0) && useDefaults)\r
312         width = LVSCW_AUTOSIZE_USEHEADER;\r
313 \r
314     return width;\r
315 }\r
316 \r
317 int CGitStatusListCtrl::ColumnManager::GetVisibleWidth (int column, bool useDefaults) const\r
318 {\r
319     return IsVisible (column)\r
320         ? GetWidth (column, useDefaults)\r
321         : 0;\r
322 }\r
323 \r
324 // switch columns on and off\r
325 \r
326 void CGitStatusListCtrl::ColumnManager::SetVisible \r
327     ( int column\r
328     , bool visible)\r
329 {\r
330     size_t index = static_cast<size_t>(column);\r
331     assert (index < columns.size());\r
332        \r
333     if (columns[index].visible != visible)\r
334     {\r
335         columns[index].visible = visible;\r
336         columns[index].relevant |= visible;\r
337         if (!visible)\r
338             columns[index].width = 0; \r
339 \r
340         control->SetColumnWidth (column, GetVisibleWidth (column, true));\r
341         ApplyColumnOrder();\r
342 \r
343         control->Invalidate (FALSE);\r
344     }\r
345 }\r
346 \r
347 // tracking column modifications\r
348 \r
349 void CGitStatusListCtrl::ColumnManager::ColumnMoved (int column, int position)\r
350 {\r
351     // in front of what column has it been inserted?\r
352 \r
353     int index = columns[column].index;\r
354 \r
355     std::vector<int> gridColumnOrder = GetGridColumnOrder();\r
356 \r
357     size_t visiblePosition = static_cast<size_t>(position);\r
358     size_t columnCount = gridColumnOrder.size();\r
359 \r
360     for (;    (visiblePosition < columnCount) \r
361            && !columns[gridColumnOrder[visiblePosition]].visible\r
362          ; ++visiblePosition )\r
363     {\r
364     }\r
365 \r
366     int next = visiblePosition == columnCount\r
367              ? -1 \r
368              : gridColumnOrder[visiblePosition];\r
369 \r
370     // move logical column index just in front of that "next" column\r
371 \r
372     columnOrder.erase (std::find ( columnOrder.begin()\r
373                                  , columnOrder.end()\r
374                                  , index));\r
375     columnOrder.insert ( std::find ( columnOrder.begin()\r
376                                    , columnOrder.end()\r
377                                    , next)\r
378                        , index);\r
379 \r
380     // make sure, invisible columns are still put in front of all others\r
381 \r
382     ApplyColumnOrder();\r
383 }\r
384 \r
385 void CGitStatusListCtrl::ColumnManager::ColumnResized (int column)\r
386 {\r
387     size_t index = static_cast<size_t>(column);\r
388     assert (index < columns.size());\r
389     assert (columns[index].visible);\r
390        \r
391     int width = control->GetColumnWidth (column);\r
392     columns[index].width = width;\r
393 \r
394     int propertyIndex = columns[index].index;\r
395     if (propertyIndex >= SVNSLC_USERPROPCOLOFFSET)\r
396         userProps[propertyIndex - SVNSLC_USERPROPCOLOFFSET].width = width;\r
397 \r
398     control->Invalidate  (FALSE);\r
399 }\r
400 \r
401 // call these to update the user-prop list\r
402 // (will also auto-insert /-remove new list columns)\r
403 \r
404 void CGitStatusListCtrl::ColumnManager::UpdateUserPropList \r
405     (const std::vector<FileEntry*>& files)\r
406 {\r
407     // collect all user-defined props\r
408 #if 0\r
409     std::set<CString> aggregatedProps;\r
410     for (size_t i = 0, count = files.size(); i < count; ++i)\r
411         files[i]->present_props.GetPropertyNames (aggregatedProps);\r
412 \r
413     aggregatedProps.erase (_T("svn:needs-lock"));\r
414     itemProps = aggregatedProps;\r
415 \r
416     // add new ones to the internal list\r
417 \r
418     std::set<CString> newProps = aggregatedProps;\r
419     for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
420         newProps.erase (userProps[i].name);\r
421 \r
422     while (   newProps.size() + userProps.size()\r
423             > SVNSLC_MAXCOLUMNCOUNT - SVNSLC_USERPROPCOLOFFSET)\r
424         newProps.erase (--newProps.end());\r
425 \r
426     typedef std::set<CString>::const_iterator CIT;\r
427     for ( CIT iter = newProps.begin(), end = newProps.end()\r
428         ; iter != end\r
429         ; ++iter)\r
430     {\r
431         int index = static_cast<int>(userProps.size()) \r
432                   + SVNSLC_USERPROPCOLOFFSET;\r
433         columnOrder.push_back (index);\r
434 \r
435         UserProp userProp;\r
436         userProp.name = *iter;\r
437         userProp.width = 0;\r
438 \r
439         userProps.push_back (userProp);\r
440     }\r
441 \r
442     // remove unused columns from control.\r
443     // remove used ones from the set of aggregatedProps.\r
444 \r
445     for (size_t i = columns.size(); i > 0; --i)\r
446         if (   (columns[i-1].index >= SVNSLC_USERPROPCOLOFFSET)\r
447             && (aggregatedProps.erase (GetName ((int)i-1)) == 0))\r
448         {\r
449             // this user-prop has not been set on any item\r
450 \r
451             if (!columns[i-1].visible)\r
452             {\r
453                 control->DeleteColumn (static_cast<int>(i-1));\r
454                 columns.erase (columns.begin() + i-1);\r
455             }\r
456         }\r
457 \r
458     // aggregatedProps now contains new columns only.\r
459     // we can't use newProps here because some props may have been used\r
460     // earlier but were not in the recent list of used props.\r
461     // -> they are neither in columns[] nor in newProps.\r
462 \r
463     for ( CIT iter = aggregatedProps.begin(), end = aggregatedProps.end()\r
464         ; iter != end\r
465         ; ++iter)\r
466     {\r
467         // get the logical column index / ID\r
468 \r
469         int index = -1;\r
470         int width = 0;\r
471         for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
472             if (userProps[i].name == *iter)\r
473             {\r
474                 index = static_cast<int>(i) + SVNSLC_USERPROPCOLOFFSET;\r
475                 width = userProps[i].width;\r
476                 break;\r
477             }\r
478 \r
479         assert (index != -1);\r
480 \r
481         // find insertion position\r
482 \r
483         std::vector<ColumnInfo>::iterator columnIter = columns.begin();\r
484         std::vector<ColumnInfo>::iterator end = columns.end();\r
485         for (; (columnIter != end) && columnIter->index < index; ++columnIter);\r
486         int pos = static_cast<int>(columnIter - columns.begin());\r
487 \r
488         ColumnInfo column;\r
489         column.index = index;\r
490         column.width = width;\r
491         column.visible = false;\r
492 \r
493         columns.insert (columnIter, column);\r
494 \r
495         // update control\r
496 \r
497         int result = control->InsertColumn (pos, *iter, LVCFMT_LEFT, GetVisibleWidth(pos, false));\r
498         assert (result != -1);\r
499                 UNREFERENCED_PARAMETER(result);\r
500     }\r
501 \r
502     // update column order\r
503 \r
504     ApplyColumnOrder();\r
505 \r
506 #endif\r
507 }\r
508 \r
509 void CGitStatusListCtrl::ColumnManager::UpdateRelevance \r
510     ( const std::vector<FileEntry*>& files\r
511     , const std::vector<size_t>& visibleFiles)\r
512 {\r
513     // collect all user-defined props that belong to shown files\r
514 #if 0\r
515     std::set<CString> aggregatedProps;\r
516     for (size_t i = 0, count = visibleFiles.size(); i < count; ++i)\r
517         files[visibleFiles[i]]->present_props.GetPropertyNames (aggregatedProps);\r
518 \r
519     aggregatedProps.erase (_T("svn:needs-lock"));\r
520     itemProps = aggregatedProps;\r
521 \r
522     // invisible columns for unused props are not relevant\r
523 \r
524     for (int i = 0, count = GetColumnCount(); i < count; ++i)\r
525         if (IsUserProp(i) && !IsVisible(i))\r
526         {\r
527             columns[i].relevant \r
528                 = aggregatedProps.find (GetName(i)) != aggregatedProps.end();\r
529         }\r
530 #endif\r
531 }\r
532 \r
533 // don't clutter the context menu with irrelevant prop info\r
534 \r
535 bool CGitStatusListCtrl::ColumnManager::AnyUnusedProperties() const\r
536 {\r
537     return columns.size() < userProps.size() + SVNSLC_NUMCOLUMNS;\r
538 }\r
539 \r
540 void CGitStatusListCtrl::ColumnManager::RemoveUnusedProps()\r
541 {\r
542     // determine what column indexes / IDs to keep.\r
543     // map them onto new IDs (we may delete some IDs in between)\r
544 \r
545     std::map<int, int> validIndices;\r
546     int userPropID = SVNSLC_USERPROPCOLOFFSET;\r
547 \r
548     for (size_t i = 0, count = columns.size(); i < count; ++i)\r
549     {\r
550         int index = columns[i].index;\r
551 \r
552         if (   itemProps.find (GetName((int)i)) != itemProps.end()\r
553             || columns[i].visible\r
554             || index < SVNSLC_USERPROPCOLOFFSET)\r
555         {\r
556             validIndices[index] = index < SVNSLC_USERPROPCOLOFFSET\r
557                                 ? index\r
558                                 : userPropID++;\r
559         }\r
560     }\r
561 \r
562     // remove everything else:\r
563 \r
564     // remove from columns and control.\r
565     // also update index values in columns\r
566 \r
567     for (size_t i = columns.size(); i > 0; --i)\r
568     {\r
569         std::map<int, int>::const_iterator iter \r
570             = validIndices.find (columns[i-1].index);\r
571 \r
572         if (iter == validIndices.end())\r
573         {\r
574             control->DeleteColumn (static_cast<int>(i-1));\r
575             columns.erase (columns.begin() + i-1);\r
576         }\r
577         else\r
578         {\r
579             columns[i-1].index = iter->second;\r
580         }\r
581     }\r
582 \r
583     // remove from user props\r
584 \r
585     for (size_t i = userProps.size(); i > 0; --i)\r
586     {\r
587         int index = static_cast<int>(i)-1 + SVNSLC_USERPROPCOLOFFSET;\r
588         if (validIndices.find (index) == validIndices.end())\r
589             userProps.erase (userProps.begin() + i-1);\r
590     }\r
591 \r
592     // remove from and update column order\r
593 \r
594     for (size_t i = columnOrder.size(); i > 0; --i)\r
595     {\r
596         std::map<int, int>::const_iterator iter \r
597             = validIndices.find (columnOrder[i-1]);\r
598 \r
599         if (iter == validIndices.end())\r
600             columnOrder.erase (columnOrder.begin() + i-1);\r
601         else\r
602             columnOrder[i-1] = iter->second;\r
603     }\r
604 }\r
605 \r
606 // bring everything back to its "natural" order\r
607 \r
608 void CGitStatusListCtrl::ColumnManager::ResetColumns (DWORD defaultColumns)\r
609 {\r
610     // update internal data\r
611 \r
612     std::sort (columnOrder.begin(), columnOrder.end());\r
613 \r
614     for (size_t i = 0, count = columns.size(); i < count; ++i)\r
615     {\r
616         columns[i].width = 0;\r
617         columns[i].visible = (i < 32) && (((defaultColumns >> i) & 1) != 0);\r
618     }\r
619 \r
620     for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
621         userProps[i].width = 0;\r
622 \r
623     // update UI\r
624 \r
625     for (int i = 0, count = GetColumnCount(); i < count; ++i)\r
626         control->SetColumnWidth (i, GetVisibleWidth (i, true));\r
627 \r
628     ApplyColumnOrder();\r
629 \r
630     control->Invalidate (FALSE);\r
631 }\r
632 \r
633 // initialization utilities\r
634 \r
635 void CGitStatusListCtrl::ColumnManager::ParseUserPropSettings \r
636     ( const CString& userPropList\r
637     , const CString& shownUserProps)\r
638 {\r
639     assert (userProps.empty());\r
640 \r
641     static CString delimiters (_T(" "));\r
642 \r
643     // parse list of visible user-props\r
644 \r
645     std::set<CString> visibles;\r
646 \r
647     int pos = 0;\r
648     CString name = shownUserProps.Tokenize (delimiters, pos);\r
649     while (!name.IsEmpty())\r
650     {\r
651         visibles.insert (name);\r
652         name = shownUserProps.Tokenize (delimiters, pos);\r
653     }\r
654 \r
655     // create list of all user-props\r
656 \r
657     pos = 0;\r
658     name = userPropList.Tokenize (delimiters, pos);\r
659     while (!name.IsEmpty())\r
660     {\r
661         bool visible = visibles.find (name) != visibles.end();\r
662 \r
663         UserProp newEntry;\r
664         newEntry.name = name;\r
665         newEntry.width = 0;\r
666 \r
667         userProps.push_back (newEntry);\r
668 \r
669         // auto-create columns for visible user-props\r
670         // (others may be added later)\r
671 \r
672         if (visible)\r
673         {\r
674             ColumnInfo newColumn;\r
675             newColumn.width = 0;\r
676             newColumn.visible = true;\r
677             newColumn.relevant = true;\r
678             newColumn.index = static_cast<int>(userProps.size()) \r
679                             + SVNSLC_USERPROPCOLOFFSET - 1;\r
680 \r
681             columns.push_back (newColumn);\r
682         }\r
683 \r
684         name = userPropList.Tokenize (delimiters, pos);\r
685     }\r
686 }\r
687 \r
688 void CGitStatusListCtrl::ColumnManager::ParseWidths (const CString& widths)\r
689 {\r
690     for (int i = 0, count = widths.GetLength() / 8; i < count; ++i)\r
691     {\r
692                 long width = _tcstol (widths.Mid (i*8, 8), NULL, 16);\r
693         if (i < SVNSLC_NUMCOLUMNS)\r
694         {\r
695             // a standard column\r
696 \r
697             columns[i].width = width;\r
698         }\r
699         else if (i >= SVNSLC_USERPROPCOLOFFSET)\r
700         {\r
701             // a user-prop column\r
702 \r
703             size_t index = static_cast<size_t>(i - SVNSLC_USERPROPCOLOFFSET);\r
704             assert (index < userProps.size());\r
705             userProps[index].width = width;\r
706 \r
707             for (size_t k = 0, count = columns.size(); k < count; ++k)\r
708                 if (columns[k].index == i)\r
709                     columns[k].width = width;\r
710         }\r
711         else\r
712         {\r
713             // there is no such column \r
714 \r
715             assert (width == 0);\r
716         }\r
717     }\r
718 }\r
719 \r
720 void CGitStatusListCtrl::ColumnManager::SetStandardColumnVisibility \r
721     (DWORD visibility)\r
722 {\r
723     for (size_t i = 0; i < SVNSLC_NUMCOLUMNS; ++i)\r
724     {\r
725         columns[i].visible = (visibility & 1) > 0;\r
726         visibility /= 2;\r
727     }\r
728 }\r
729 \r
730 void CGitStatusListCtrl::ColumnManager::ParseColumnOrder \r
731     (const CString& widths)\r
732 {\r
733     std::set<int> alreadyPlaced;\r
734     columnOrder.clear();\r
735 \r
736     // place columns according to valid entries in orderString\r
737 \r
738     int limit = static_cast<int>(SVNSLC_USERPROPCOLOFFSET + userProps.size());\r
739     for (int i = 0, count = widths.GetLength() / 2; i < count; ++i)\r
740     {\r
741                 int index = _tcstol (widths.Mid (i*2, 2), NULL, 16);\r
742         if (   (index < SVNSLC_NUMCOLUMNS)\r
743             || ((index >= SVNSLC_USERPROPCOLOFFSET) && (index < limit)))\r
744         {\r
745             alreadyPlaced.insert (index);\r
746             columnOrder.push_back (index);\r
747         }\r
748     }\r
749 \r
750     // place the remaining colums behind it\r
751 \r
752     for (int i = 0; i < SVNSLC_NUMCOLUMNS; ++i)\r
753         if (alreadyPlaced.find (i) == alreadyPlaced.end())\r
754             columnOrder.push_back (i);\r
755 \r
756     for (int i = SVNSLC_USERPROPCOLOFFSET; i < limit; ++i)\r
757         if (alreadyPlaced.find (i) == alreadyPlaced.end())\r
758             columnOrder.push_back (i);\r
759 }\r
760 \r
761 // map internal column order onto visible column order\r
762 // (all invisibles in front)\r
763 \r
764 std::vector<int> CGitStatusListCtrl::ColumnManager::GetGridColumnOrder()\r
765 {\r
766     // extract order of used columns from order of all columns\r
767 \r
768     std::vector<int> result;\r
769     result.reserve (SVNSLC_MAXCOLUMNCOUNT+1);\r
770 \r
771     size_t colCount = columns.size();\r
772     bool visible = false;\r
773 \r
774     do\r
775     {\r
776         // put invisible cols in front\r
777 \r
778         for (size_t i = 0, count = columnOrder.size(); i < count; ++i)\r
779         {\r
780             int index = columnOrder[i];\r
781             for (size_t k = 0; k < colCount; ++k)\r
782             {\r
783                 const ColumnInfo& column = columns[k];\r
784                 if ((column.index == index) && (column.visible == visible))\r
785                     result.push_back (static_cast<int>(k));\r
786             }\r
787         }\r
788 \r
789         visible = !visible;\r
790     }\r
791     while (visible);\r
792 \r
793     return result;\r
794 }\r
795 \r
796 void CGitStatusListCtrl::ColumnManager::ApplyColumnOrder()\r
797 {\r
798     // extract order of used columns from order of all columns\r
799 \r
800     int order[SVNSLC_MAXCOLUMNCOUNT+1];\r
801     SecureZeroMemory (order, sizeof (order));\r
802 \r
803     std::vector<int> gridColumnOrder = GetGridColumnOrder();\r
804         std::copy (gridColumnOrder.begin(), gridColumnOrder.end(), stdext::checked_array_iterator<int*>(&order[0], sizeof(order)));\r
805 \r
806     // we must have placed all columns or something is really fishy ..\r
807 \r
808     assert (gridColumnOrder.size() == columns.size());\r
809         assert (GetColumnCount() == ((CHeaderCtrl*)(control->GetDlgItem(0)))->GetItemCount());\r
810 \r
811     // o.k., apply our column ordering\r
812 \r
813     control->SetColumnOrderArray (GetColumnCount(), order);\r
814 }\r
815 \r
816 // utilities used when writing data to the registry\r
817 \r
818 DWORD CGitStatusListCtrl::ColumnManager::GetSelectedStandardColumns() const\r
819 {\r
820     DWORD result = 0;\r
821     for (size_t i = SVNSLC_NUMCOLUMNS; i > 0; --i)\r
822         result = result * 2 + (columns[i-1].visible ? 1 : 0);\r
823 \r
824     return result;\r
825 }\r
826 \r
827 CString CGitStatusListCtrl::ColumnManager::GetUserPropList() const\r
828 {\r
829     CString result;\r
830 \r
831     for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
832         result += userProps[i].name + _T(' ');\r
833 \r
834     return result;\r
835 }\r
836 \r
837 CString CGitStatusListCtrl::ColumnManager::GetShownUserProps() const\r
838 {\r
839     CString result;\r
840 \r
841     for (size_t i = 0, count = columns.size(); i < count; ++i)\r
842     {\r
843         size_t index = static_cast<size_t>(columns[i].index);\r
844         if (columns[i].visible && (index >= SVNSLC_USERPROPCOLOFFSET))\r
845             result += userProps[index - SVNSLC_USERPROPCOLOFFSET].name \r
846                     + _T(' ');\r
847     }\r
848 \r
849     return result;\r
850 }\r
851 \r
852 CString CGitStatusListCtrl::ColumnManager::GetWidthString() const\r
853 {\r
854     CString result;\r
855 \r
856     // regular columns\r
857 \r
858         TCHAR buf[10];\r
859     for (size_t i = 0; i < SVNSLC_NUMCOLUMNS; ++i)\r
860         {\r
861                 _stprintf_s (buf, 10, _T("%08X"), columns[i].width);\r
862                 result += buf;\r
863         }\r
864 \r
865     // range with no column IDs\r
866 \r
867     result += CString ('0', 8 * (SVNSLC_USERPROPCOLOFFSET - SVNSLC_NUMCOLUMNS));\r
868 \r
869     // user-prop columns\r
870 \r
871     for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
872         {\r
873                 _stprintf_s (buf, 10, _T("%08X"), userProps[i].width);\r
874                 result += buf;\r
875         }\r
876 \r
877     return result;\r
878 }\r
879 \r
880 CString CGitStatusListCtrl::ColumnManager::GetColumnOrderString() const\r
881 {\r
882     CString result;\r
883 \r
884         TCHAR buf[3];\r
885     for (size_t i = 0, count = columnOrder.size(); i < count; ++i)\r
886         {\r
887                 _stprintf_s (buf, 3, _T("%02X"), columnOrder[i]);\r
888                 result += buf;\r
889         }\r
890 \r
891     return result;\r
892 }\r
893 \r
894 // sorter utility class\r
895 \r
896 CGitStatusListCtrl::CSorter::CSorter ( ColumnManager* columnManager\r
897                                                                           , int sortedColumn\r
898                                                                           , bool ascending)\r
899                                                                           : columnManager (columnManager)\r
900                                                                           , sortedColumn (sortedColumn)\r
901                                                                           , ascending (ascending)\r
902 {\r
903 }\r
904 \r
905 bool CGitStatusListCtrl::CSorter::operator()\r
906 ( const FileEntry* entry1\r
907  , const FileEntry* entry2) const\r
908 {\r
909 #define SGN(x) ((x)==0?0:((x)>0?1:-1))\r
910 \r
911         int result = 0;\r
912         switch (sortedColumn)\r
913         {\r
914         case 18:\r
915                 {\r
916                         if (result == 0)\r
917                         {\r
918                                 __int64 writetime1 = entry1->GetPath().GetLastWriteTime();\r
919                                 __int64 writetime2 = entry2->GetPath().GetLastWriteTime();\r
920 \r
921                                 FILETIME* filetime1 = (FILETIME*)(__int64*)&writetime1;\r
922                                 FILETIME* filetime2 = (FILETIME*)(__int64*)&writetime2;\r
923 \r
924                                 result = CompareFileTime(filetime1,filetime2);\r
925                         }\r
926                 }\r
927         case 17:\r
928                 {\r
929                         if (result == 0)\r
930                         {\r
931 //                              result = entry1->copyfrom_url.CompareNoCase(entry2->copyfrom_url);\r
932                         }\r
933                 }\r
934         case 16:\r
935                 {\r
936                         if (result == 0)\r
937                         {\r
938                                 result = SGN(entry1->needslock - entry2->needslock);\r
939                         }\r
940                 }\r
941         case 15:\r
942                 {\r
943                         if (result == 0)\r
944                         {\r
945                                 result = SGN(entry1->last_commit_date - entry2->last_commit_date);\r
946                         }\r
947                 }\r
948         case 14:\r
949                 {\r
950                         if (result == 0)\r
951                         {\r
952                                 result = entry1->remoterev - entry2->remoterev;\r
953                         }\r
954                 }\r
955         case 13:\r
956                 {\r
957                         if (result == 0)\r
958                         {\r
959                                 result = entry1->last_commit_rev - entry2->last_commit_rev;\r
960                         }\r
961                 }\r
962         case 12:\r
963                 {\r
964                         if (result == 0)\r
965                         {\r
966                                 result = entry1->last_commit_author.CompareNoCase(entry2->last_commit_author);\r
967                         }\r
968                 }\r
969         case 11:\r
970                 {\r
971                         if (result == 0)\r
972                         {\r
973 //                              result = entry1->lock_comment.CompareNoCase(entry2->lock_comment);\r
974                         }\r
975                 }\r
976         case 10:\r
977                 {\r
978                         if (result == 0)\r
979                         {\r
980 //                              result = entry1->lock_owner.CompareNoCase(entry2->lock_owner);\r
981                         }\r
982                 }\r
983         case 9:\r
984                 {\r
985                         if (result == 0)\r
986                         {\r
987 //                              result = entry1->url.CompareNoCase(entry2->url);\r
988                         }\r
989                 }\r
990         case 8:\r
991                 {\r
992                         if (result == 0)\r
993                         {\r
994 //                              result = entry1->remotepropstatus - entry2->remotepropstatus;\r
995                         }\r
996                 }\r
997         case 7:\r
998                 {\r
999                         if (result == 0)\r
1000                         {\r
1001 //                              result = entry1->remotetextstatus - entry2->remotetextstatus;\r
1002                         }\r
1003                 }\r
1004         case 6:\r
1005                 {\r
1006                         if (result == 0)\r
1007                         {\r
1008 //                              result = entry1->propstatus - entry2->propstatus;\r
1009                         }\r
1010                 }\r
1011         case 5:\r
1012                 {\r
1013                         if (result == 0)\r
1014                         {\r
1015 //                              result = entry1->textstatus - entry2->textstatus;\r
1016                         }\r
1017                 }\r
1018         case 4:\r
1019                 {\r
1020                         if (result == 0)\r
1021                         {\r
1022         //                      result = entry1->remotestatus - entry2->remotestatus;\r
1023                         }\r
1024                 }\r
1025         case 3:\r
1026                 {\r
1027                         if (result == 0)\r
1028                         {\r
1029                                 result = entry1->status - entry2->status;\r
1030                         }\r
1031                 }\r
1032         case 2:\r
1033                 {\r
1034                         if (result == 0)\r
1035                         {\r
1036                                 result = entry1->path.GetFileExtension().CompareNoCase(entry2->path.GetFileExtension());\r
1037                         }\r
1038                 }\r
1039         case 1:\r
1040                 {\r
1041                         if (result == 0)\r
1042                         {\r
1043                                 result = entry1->path.GetFileOrDirectoryName().CompareNoCase(entry2->path.GetFileOrDirectoryName());\r
1044                         }\r
1045                 }\r
1046         case 0:         // path column\r
1047                 {\r
1048                         if (result == 0)\r
1049                         {\r
1050                                 result = CTGitPath::Compare(entry1->path, entry2->path);\r
1051                         }\r
1052                 }\r
1053         default:\r
1054                 if ((result == 0) && (sortedColumn > 0))\r
1055                 {\r
1056                         // N/A props are "less than" empty props\r
1057 \r
1058                         const CString& propName = columnManager->GetName (sortedColumn);\r
1059 \r
1060 //                      bool entry1HasProp = entry1->present_props.HasProperty (propName);\r
1061 //                      bool entry2HasProp = entry2->present_props.HasProperty (propName);\r
1062 \r
1063 //                      if (entry1HasProp)\r
1064 //                      {\r
1065 //                              result = entry2HasProp\r
1066 //                                      ? entry1->present_props[propName].Compare \r
1067 //                                      (entry2->present_props[propName])\r
1068 //                                      : 1;\r
1069 //                      }\r
1070 //                      else\r
1071 //                      {\r
1072 //                              result = entry2HasProp ? -1 : 0;\r
1073 //                      }\r
1074                 }\r
1075         } // switch (m_nSortedColumn)\r
1076         if (!ascending)\r
1077                 result = -result;\r
1078 \r
1079         return result < 0;\r
1080 }\r