OSDN Git Service

16c6a5eff5e6a334d555605709605ea500acaf9e
[tortoisegit/TortoiseGitJp.git] / src / 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                   , IDS_STATUSLIST_COLADD\r
285                   , IDS_STATUSLIST_COLDEL\r
286                 };\r
287 \r
288     // standard columns\r
289 \r
290     size_t index = static_cast<size_t>(column);\r
291     if (index < SVNSLC_NUMCOLUMNS)\r
292     {\r
293         CString result;\r
294         result.LoadString (standardColumnNames[index]);\r
295         return result;\r
296     }\r
297 \r
298     // user-prop columns\r
299 \r
300         //    if (index < columns.size())\r
301         //        return userProps[columns[index].index - SVNSLC_USERPROPCOLOFFSET].name;\r
302 \r
303     // default: empty\r
304 \r
305     return CString();\r
306 }\r
307 \r
308 int CGitStatusListCtrl::ColumnManager::GetWidth (int column, bool useDefaults) const\r
309 {\r
310     size_t index = static_cast<size_t>(column);\r
311     assert (columns.size() > index);\r
312 \r
313     int width = columns[index].width;\r
314     if ((width == 0) && useDefaults)\r
315         width = LVSCW_AUTOSIZE_USEHEADER;\r
316 \r
317     return width;\r
318 }\r
319 \r
320 int CGitStatusListCtrl::ColumnManager::GetVisibleWidth (int column, bool useDefaults) const\r
321 {\r
322     return IsVisible (column)\r
323         ? GetWidth (column, useDefaults)\r
324         : 0;\r
325 }\r
326 \r
327 // switch columns on and off\r
328 \r
329 void CGitStatusListCtrl::ColumnManager::SetVisible \r
330     ( int column\r
331     , bool visible)\r
332 {\r
333     size_t index = static_cast<size_t>(column);\r
334     assert (index < columns.size());\r
335        \r
336     if (columns[index].visible != visible)\r
337     {\r
338         columns[index].visible = visible;\r
339         columns[index].relevant |= visible;\r
340         if (!visible)\r
341             columns[index].width = 0; \r
342 \r
343         control->SetColumnWidth (column, GetVisibleWidth (column, true));\r
344         ApplyColumnOrder();\r
345 \r
346         control->Invalidate (FALSE);\r
347     }\r
348 }\r
349 \r
350 // tracking column modifications\r
351 \r
352 void CGitStatusListCtrl::ColumnManager::ColumnMoved (int column, int position)\r
353 {\r
354     // in front of what column has it been inserted?\r
355 \r
356     int index = columns[column].index;\r
357 \r
358     std::vector<int> gridColumnOrder = GetGridColumnOrder();\r
359 \r
360     size_t visiblePosition = static_cast<size_t>(position);\r
361     size_t columnCount = gridColumnOrder.size();\r
362 \r
363     for (;    (visiblePosition < columnCount) \r
364            && !columns[gridColumnOrder[visiblePosition]].visible\r
365          ; ++visiblePosition )\r
366     {\r
367     }\r
368 \r
369     int next = visiblePosition == columnCount\r
370              ? -1 \r
371              : gridColumnOrder[visiblePosition];\r
372 \r
373     // move logical column index just in front of that "next" column\r
374 \r
375     columnOrder.erase (std::find ( columnOrder.begin()\r
376                                  , columnOrder.end()\r
377                                  , index));\r
378     columnOrder.insert ( std::find ( columnOrder.begin()\r
379                                    , columnOrder.end()\r
380                                    , next)\r
381                        , index);\r
382 \r
383     // make sure, invisible columns are still put in front of all others\r
384 \r
385     ApplyColumnOrder();\r
386 }\r
387 \r
388 void CGitStatusListCtrl::ColumnManager::ColumnResized (int column)\r
389 {\r
390     size_t index = static_cast<size_t>(column);\r
391     assert (index < columns.size());\r
392     assert (columns[index].visible);\r
393        \r
394     int width = control->GetColumnWidth (column);\r
395     columns[index].width = width;\r
396 \r
397     int propertyIndex = columns[index].index;\r
398     if (propertyIndex >= SVNSLC_USERPROPCOLOFFSET)\r
399         userProps[propertyIndex - SVNSLC_USERPROPCOLOFFSET].width = width;\r
400 \r
401     control->Invalidate  (FALSE);\r
402 }\r
403 \r
404 // call these to update the user-prop list\r
405 // (will also auto-insert /-remove new list columns)\r
406 \r
407 void CGitStatusListCtrl::ColumnManager::UpdateUserPropList \r
408     (const std::vector<FileEntry*>& files)\r
409 {\r
410     // collect all user-defined props\r
411 #if 0\r
412     std::set<CString> aggregatedProps;\r
413     for (size_t i = 0, count = files.size(); i < count; ++i)\r
414         files[i]->present_props.GetPropertyNames (aggregatedProps);\r
415 \r
416     aggregatedProps.erase (_T("svn:needs-lock"));\r
417     itemProps = aggregatedProps;\r
418 \r
419     // add new ones to the internal list\r
420 \r
421     std::set<CString> newProps = aggregatedProps;\r
422     for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
423         newProps.erase (userProps[i].name);\r
424 \r
425     while (   newProps.size() + userProps.size()\r
426             > SVNSLC_MAXCOLUMNCOUNT - SVNSLC_USERPROPCOLOFFSET)\r
427         newProps.erase (--newProps.end());\r
428 \r
429     typedef std::set<CString>::const_iterator CIT;\r
430     for ( CIT iter = newProps.begin(), end = newProps.end()\r
431         ; iter != end\r
432         ; ++iter)\r
433     {\r
434         int index = static_cast<int>(userProps.size()) \r
435                   + SVNSLC_USERPROPCOLOFFSET;\r
436         columnOrder.push_back (index);\r
437 \r
438         UserProp userProp;\r
439         userProp.name = *iter;\r
440         userProp.width = 0;\r
441 \r
442         userProps.push_back (userProp);\r
443     }\r
444 \r
445     // remove unused columns from control.\r
446     // remove used ones from the set of aggregatedProps.\r
447 \r
448     for (size_t i = columns.size(); i > 0; --i)\r
449         if (   (columns[i-1].index >= SVNSLC_USERPROPCOLOFFSET)\r
450             && (aggregatedProps.erase (GetName ((int)i-1)) == 0))\r
451         {\r
452             // this user-prop has not been set on any item\r
453 \r
454             if (!columns[i-1].visible)\r
455             {\r
456                 control->DeleteColumn (static_cast<int>(i-1));\r
457                 columns.erase (columns.begin() + i-1);\r
458             }\r
459         }\r
460 \r
461     // aggregatedProps now contains new columns only.\r
462     // we can't use newProps here because some props may have been used\r
463     // earlier but were not in the recent list of used props.\r
464     // -> they are neither in columns[] nor in newProps.\r
465 \r
466     for ( CIT iter = aggregatedProps.begin(), end = aggregatedProps.end()\r
467         ; iter != end\r
468         ; ++iter)\r
469     {\r
470         // get the logical column index / ID\r
471 \r
472         int index = -1;\r
473         int width = 0;\r
474         for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
475             if (userProps[i].name == *iter)\r
476             {\r
477                 index = static_cast<int>(i) + SVNSLC_USERPROPCOLOFFSET;\r
478                 width = userProps[i].width;\r
479                 break;\r
480             }\r
481 \r
482         assert (index != -1);\r
483 \r
484         // find insertion position\r
485 \r
486         std::vector<ColumnInfo>::iterator columnIter = columns.begin();\r
487         std::vector<ColumnInfo>::iterator end = columns.end();\r
488         for (; (columnIter != end) && columnIter->index < index; ++columnIter);\r
489         int pos = static_cast<int>(columnIter - columns.begin());\r
490 \r
491         ColumnInfo column;\r
492         column.index = index;\r
493         column.width = width;\r
494         column.visible = false;\r
495 \r
496         columns.insert (columnIter, column);\r
497 \r
498         // update control\r
499 \r
500         int result = control->InsertColumn (pos, *iter, LVCFMT_LEFT, GetVisibleWidth(pos, false));\r
501         assert (result != -1);\r
502                 UNREFERENCED_PARAMETER(result);\r
503     }\r
504 \r
505     // update column order\r
506 \r
507     ApplyColumnOrder();\r
508 \r
509 #endif\r
510 }\r
511 \r
512 void CGitStatusListCtrl::ColumnManager::UpdateRelevance \r
513     ( const std::vector<FileEntry*>& files\r
514     , const std::vector<size_t>& visibleFiles)\r
515 {\r
516     // collect all user-defined props that belong to shown files\r
517 #if 0\r
518     std::set<CString> aggregatedProps;\r
519     for (size_t i = 0, count = visibleFiles.size(); i < count; ++i)\r
520         files[visibleFiles[i]]->present_props.GetPropertyNames (aggregatedProps);\r
521 \r
522     aggregatedProps.erase (_T("svn:needs-lock"));\r
523     itemProps = aggregatedProps;\r
524 \r
525     // invisible columns for unused props are not relevant\r
526 \r
527     for (int i = 0, count = GetColumnCount(); i < count; ++i)\r
528         if (IsUserProp(i) && !IsVisible(i))\r
529         {\r
530             columns[i].relevant \r
531                 = aggregatedProps.find (GetName(i)) != aggregatedProps.end();\r
532         }\r
533 #endif\r
534 }\r
535 \r
536 // don't clutter the context menu with irrelevant prop info\r
537 \r
538 bool CGitStatusListCtrl::ColumnManager::AnyUnusedProperties() const\r
539 {\r
540     return columns.size() < userProps.size() + SVNSLC_NUMCOLUMNS;\r
541 }\r
542 \r
543 void CGitStatusListCtrl::ColumnManager::RemoveUnusedProps()\r
544 {\r
545     // determine what column indexes / IDs to keep.\r
546     // map them onto new IDs (we may delete some IDs in between)\r
547 \r
548     std::map<int, int> validIndices;\r
549     int userPropID = SVNSLC_USERPROPCOLOFFSET;\r
550 \r
551     for (size_t i = 0, count = columns.size(); i < count; ++i)\r
552     {\r
553         int index = columns[i].index;\r
554 \r
555         if (   itemProps.find (GetName((int)i)) != itemProps.end()\r
556             || columns[i].visible\r
557             || index < SVNSLC_USERPROPCOLOFFSET)\r
558         {\r
559             validIndices[index] = index < SVNSLC_USERPROPCOLOFFSET\r
560                                 ? index\r
561                                 : userPropID++;\r
562         }\r
563     }\r
564 \r
565     // remove everything else:\r
566 \r
567     // remove from columns and control.\r
568     // also update index values in columns\r
569 \r
570     for (size_t i = columns.size(); i > 0; --i)\r
571     {\r
572         std::map<int, int>::const_iterator iter \r
573             = validIndices.find (columns[i-1].index);\r
574 \r
575         if (iter == validIndices.end())\r
576         {\r
577             control->DeleteColumn (static_cast<int>(i-1));\r
578             columns.erase (columns.begin() + i-1);\r
579         }\r
580         else\r
581         {\r
582             columns[i-1].index = iter->second;\r
583         }\r
584     }\r
585 \r
586     // remove from user props\r
587 \r
588     for (size_t i = userProps.size(); i > 0; --i)\r
589     {\r
590         int index = static_cast<int>(i)-1 + SVNSLC_USERPROPCOLOFFSET;\r
591         if (validIndices.find (index) == validIndices.end())\r
592             userProps.erase (userProps.begin() + i-1);\r
593     }\r
594 \r
595     // remove from and update column order\r
596 \r
597     for (size_t i = columnOrder.size(); i > 0; --i)\r
598     {\r
599         std::map<int, int>::const_iterator iter \r
600             = validIndices.find (columnOrder[i-1]);\r
601 \r
602         if (iter == validIndices.end())\r
603             columnOrder.erase (columnOrder.begin() + i-1);\r
604         else\r
605             columnOrder[i-1] = iter->second;\r
606     }\r
607 }\r
608 \r
609 // bring everything back to its "natural" order\r
610 \r
611 void CGitStatusListCtrl::ColumnManager::ResetColumns (DWORD defaultColumns)\r
612 {\r
613     // update internal data\r
614 \r
615     std::sort (columnOrder.begin(), columnOrder.end());\r
616 \r
617     for (size_t i = 0, count = columns.size(); i < count; ++i)\r
618     {\r
619         columns[i].width = 0;\r
620         columns[i].visible = (i < 32) && (((defaultColumns >> i) & 1) != 0);\r
621     }\r
622 \r
623     for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
624         userProps[i].width = 0;\r
625 \r
626     // update UI\r
627 \r
628     for (int i = 0, count = GetColumnCount(); i < count; ++i)\r
629         control->SetColumnWidth (i, GetVisibleWidth (i, true));\r
630 \r
631     ApplyColumnOrder();\r
632 \r
633     control->Invalidate (FALSE);\r
634 }\r
635 \r
636 // initialization utilities\r
637 \r
638 void CGitStatusListCtrl::ColumnManager::ParseUserPropSettings \r
639     ( const CString& userPropList\r
640     , const CString& shownUserProps)\r
641 {\r
642     assert (userProps.empty());\r
643 \r
644     static CString delimiters (_T(" "));\r
645 \r
646     // parse list of visible user-props\r
647 \r
648     std::set<CString> visibles;\r
649 \r
650     int pos = 0;\r
651     CString name = shownUserProps.Tokenize (delimiters, pos);\r
652     while (!name.IsEmpty())\r
653     {\r
654         visibles.insert (name);\r
655         name = shownUserProps.Tokenize (delimiters, pos);\r
656     }\r
657 \r
658     // create list of all user-props\r
659 \r
660     pos = 0;\r
661     name = userPropList.Tokenize (delimiters, pos);\r
662     while (!name.IsEmpty())\r
663     {\r
664         bool visible = visibles.find (name) != visibles.end();\r
665 \r
666         UserProp newEntry;\r
667         newEntry.name = name;\r
668         newEntry.width = 0;\r
669 \r
670         userProps.push_back (newEntry);\r
671 \r
672         // auto-create columns for visible user-props\r
673         // (others may be added later)\r
674 \r
675         if (visible)\r
676         {\r
677             ColumnInfo newColumn;\r
678             newColumn.width = 0;\r
679             newColumn.visible = true;\r
680             newColumn.relevant = true;\r
681             newColumn.index = static_cast<int>(userProps.size()) \r
682                             + SVNSLC_USERPROPCOLOFFSET - 1;\r
683 \r
684             columns.push_back (newColumn);\r
685         }\r
686 \r
687         name = userPropList.Tokenize (delimiters, pos);\r
688     }\r
689 }\r
690 \r
691 void CGitStatusListCtrl::ColumnManager::ParseWidths (const CString& widths)\r
692 {\r
693     for (int i = 0, count = widths.GetLength() / 8; i < count; ++i)\r
694     {\r
695                 long width = _tcstol (widths.Mid (i*8, 8), NULL, 16);\r
696         if (i < SVNSLC_NUMCOLUMNS)\r
697         {\r
698             // a standard column\r
699 \r
700             columns[i].width = width;\r
701         }\r
702         else if (i >= SVNSLC_USERPROPCOLOFFSET)\r
703         {\r
704             // a user-prop column\r
705 \r
706             size_t index = static_cast<size_t>(i - SVNSLC_USERPROPCOLOFFSET);\r
707             assert (index < userProps.size());\r
708             userProps[index].width = width;\r
709 \r
710             for (size_t k = 0, count = columns.size(); k < count; ++k)\r
711                 if (columns[k].index == i)\r
712                     columns[k].width = width;\r
713         }\r
714         else\r
715         {\r
716             // there is no such column \r
717 \r
718             assert (width == 0);\r
719         }\r
720     }\r
721 }\r
722 \r
723 void CGitStatusListCtrl::ColumnManager::SetStandardColumnVisibility \r
724     (DWORD visibility)\r
725 {\r
726     for (size_t i = 0; i < SVNSLC_NUMCOLUMNS; ++i)\r
727     {\r
728         columns[i].visible = (visibility & 1) > 0;\r
729         visibility /= 2;\r
730     }\r
731 }\r
732 \r
733 void CGitStatusListCtrl::ColumnManager::ParseColumnOrder \r
734     (const CString& widths)\r
735 {\r
736     std::set<int> alreadyPlaced;\r
737     columnOrder.clear();\r
738 \r
739     // place columns according to valid entries in orderString\r
740 \r
741     int limit = static_cast<int>(SVNSLC_USERPROPCOLOFFSET + userProps.size());\r
742     for (int i = 0, count = widths.GetLength() / 2; i < count; ++i)\r
743     {\r
744                 int index = _tcstol (widths.Mid (i*2, 2), NULL, 16);\r
745         if (   (index < SVNSLC_NUMCOLUMNS)\r
746             || ((index >= SVNSLC_USERPROPCOLOFFSET) && (index < limit)))\r
747         {\r
748             alreadyPlaced.insert (index);\r
749             columnOrder.push_back (index);\r
750         }\r
751     }\r
752 \r
753     // place the remaining colums behind it\r
754 \r
755     for (int i = 0; i < SVNSLC_NUMCOLUMNS; ++i)\r
756         if (alreadyPlaced.find (i) == alreadyPlaced.end())\r
757             columnOrder.push_back (i);\r
758 \r
759     for (int i = SVNSLC_USERPROPCOLOFFSET; i < limit; ++i)\r
760         if (alreadyPlaced.find (i) == alreadyPlaced.end())\r
761             columnOrder.push_back (i);\r
762 }\r
763 \r
764 // map internal column order onto visible column order\r
765 // (all invisibles in front)\r
766 \r
767 std::vector<int> CGitStatusListCtrl::ColumnManager::GetGridColumnOrder()\r
768 {\r
769     // extract order of used columns from order of all columns\r
770 \r
771     std::vector<int> result;\r
772     result.reserve (SVNSLC_MAXCOLUMNCOUNT+1);\r
773 \r
774     size_t colCount = columns.size();\r
775     bool visible = false;\r
776 \r
777     do\r
778     {\r
779         // put invisible cols in front\r
780 \r
781         for (size_t i = 0, count = columnOrder.size(); i < count; ++i)\r
782         {\r
783             int index = columnOrder[i];\r
784             for (size_t k = 0; k < colCount; ++k)\r
785             {\r
786                 const ColumnInfo& column = columns[k];\r
787                 if ((column.index == index) && (column.visible == visible))\r
788                     result.push_back (static_cast<int>(k));\r
789             }\r
790         }\r
791 \r
792         visible = !visible;\r
793     }\r
794     while (visible);\r
795 \r
796     return result;\r
797 }\r
798 \r
799 void CGitStatusListCtrl::ColumnManager::ApplyColumnOrder()\r
800 {\r
801     // extract order of used columns from order of all columns\r
802 \r
803     int order[SVNSLC_MAXCOLUMNCOUNT+1];\r
804     SecureZeroMemory (order, sizeof (order));\r
805 \r
806     std::vector<int> gridColumnOrder = GetGridColumnOrder();\r
807         std::copy (gridColumnOrder.begin(), gridColumnOrder.end(), stdext::checked_array_iterator<int*>(&order[0], sizeof(order)));\r
808 \r
809     // we must have placed all columns or something is really fishy ..\r
810 \r
811     assert (gridColumnOrder.size() == columns.size());\r
812         assert (GetColumnCount() == ((CHeaderCtrl*)(control->GetDlgItem(0)))->GetItemCount());\r
813 \r
814     // o.k., apply our column ordering\r
815 \r
816     control->SetColumnOrderArray (GetColumnCount(), order);\r
817 }\r
818 \r
819 // utilities used when writing data to the registry\r
820 \r
821 DWORD CGitStatusListCtrl::ColumnManager::GetSelectedStandardColumns() const\r
822 {\r
823     DWORD result = 0;\r
824     for (size_t i = SVNSLC_NUMCOLUMNS; i > 0; --i)\r
825         result = result * 2 + (columns[i-1].visible ? 1 : 0);\r
826 \r
827     return result;\r
828 }\r
829 \r
830 CString CGitStatusListCtrl::ColumnManager::GetUserPropList() const\r
831 {\r
832     CString result;\r
833 \r
834     for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
835         result += userProps[i].name + _T(' ');\r
836 \r
837     return result;\r
838 }\r
839 \r
840 CString CGitStatusListCtrl::ColumnManager::GetShownUserProps() const\r
841 {\r
842     CString result;\r
843 \r
844     for (size_t i = 0, count = columns.size(); i < count; ++i)\r
845     {\r
846         size_t index = static_cast<size_t>(columns[i].index);\r
847         if (columns[i].visible && (index >= SVNSLC_USERPROPCOLOFFSET))\r
848             result += userProps[index - SVNSLC_USERPROPCOLOFFSET].name \r
849                     + _T(' ');\r
850     }\r
851 \r
852     return result;\r
853 }\r
854 \r
855 CString CGitStatusListCtrl::ColumnManager::GetWidthString() const\r
856 {\r
857     CString result;\r
858 \r
859     // regular columns\r
860 \r
861         TCHAR buf[10];\r
862     for (size_t i = 0; i < SVNSLC_NUMCOLUMNS; ++i)\r
863         {\r
864                 _stprintf_s (buf, 10, _T("%08X"), columns[i].width);\r
865                 result += buf;\r
866         }\r
867 \r
868     // range with no column IDs\r
869 \r
870     result += CString ('0', 8 * (SVNSLC_USERPROPCOLOFFSET - SVNSLC_NUMCOLUMNS));\r
871 \r
872     // user-prop columns\r
873 \r
874     for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
875         {\r
876                 _stprintf_s (buf, 10, _T("%08X"), userProps[i].width);\r
877                 result += buf;\r
878         }\r
879 \r
880     return result;\r
881 }\r
882 \r
883 CString CGitStatusListCtrl::ColumnManager::GetColumnOrderString() const\r
884 {\r
885     CString result;\r
886 \r
887         TCHAR buf[3];\r
888     for (size_t i = 0, count = columnOrder.size(); i < count; ++i)\r
889         {\r
890                 _stprintf_s (buf, 3, _T("%02X"), columnOrder[i]);\r
891                 result += buf;\r
892         }\r
893 \r
894     return result;\r
895 }\r
896 \r
897 // sorter utility class\r
898 \r
899 CGitStatusListCtrl::CSorter::CSorter ( ColumnManager* columnManager\r
900                                                                           , int sortedColumn\r
901                                                                           , bool ascending)\r
902                                                                           : columnManager (columnManager)\r
903                                                                           , sortedColumn (sortedColumn)\r
904                                                                           , ascending (ascending)\r
905 {\r
906 }\r
907 \r
908 bool CGitStatusListCtrl::CSorter::operator()\r
909 ( const CTGitPath* entry1\r
910  , const CTGitPath* entry2) const\r
911 {\r
912 #define SGN(x) ((x)==0?0:((x)>0?1:-1))\r
913 \r
914         int result = 0;\r
915         switch (sortedColumn)\r
916         {\r
917         case 18:\r
918                 {\r
919                         if (result == 0)\r
920                         {\r
921                                 __int64 writetime1 = entry1->GetLastWriteTime();\r
922                                 __int64 writetime2 = entry2->GetLastWriteTime();\r
923 \r
924                                 FILETIME* filetime1 = (FILETIME*)(__int64*)&writetime1;\r
925                                 FILETIME* filetime2 = (FILETIME*)(__int64*)&writetime2;\r
926 \r
927                                 result = CompareFileTime(filetime1,filetime2);\r
928                         }\r
929                 }\r
930         case 17:\r
931                 {\r
932                         if (result == 0)\r
933                         {\r
934 //                              result = entry1->copyfrom_url.CompareNoCase(entry2->copyfrom_url);\r
935                         }\r
936                 }\r
937         case 16:\r
938                 {\r
939                         if (result == 0)\r
940                         {\r
941 //                              result = SGN(entry1->needslock - entry2->needslock);\r
942                         }\r
943                 }\r
944         case 15:\r
945                 {\r
946                         if (result == 0)\r
947                         {\r
948 //                              result = SGN(entry1->last_commit_date - entry2->last_commit_date);\r
949                         }\r
950                 }\r
951         case 14:\r
952                 {\r
953                         if (result == 0)\r
954                         {\r
955 //                              result = entry1->remoterev - entry2->remoterev;\r
956                         }\r
957                 }\r
958         case 13:\r
959                 {\r
960                         if (result == 0)\r
961                         {\r
962 //                              result = entry1->last_commit_rev - entry2->last_commit_rev;\r
963                         }\r
964                 }\r
965         case 12:\r
966                 {\r
967                         if (result == 0)\r
968                         {\r
969 //                              result = entry1->last_commit_author.CompareNoCase(entry2->last_commit_author);\r
970                         }\r
971                 }\r
972         case 11:\r
973                 {\r
974                         if (result == 0)\r
975                         {\r
976 //                              result = entry1->lock_comment.CompareNoCase(entry2->lock_comment);\r
977                         }\r
978                 }\r
979         case 10:\r
980                 {\r
981                         if (result == 0)\r
982                         {\r
983 //                              result = entry1->lock_owner.CompareNoCase(entry2->lock_owner);\r
984                         }\r
985                 }\r
986         case 9:\r
987                 {\r
988                         if (result == 0)\r
989                         {\r
990 //                              result = entry1->url.CompareNoCase(entry2->url);\r
991                         }\r
992                 }\r
993         case 8:\r
994                 {\r
995                         if (result == 0)\r
996                         {\r
997 //                              result = entry1->remotepropstatus - entry2->remotepropstatus;\r
998                         }\r
999                 }\r
1000         case 7:\r
1001                 {\r
1002                         if (result == 0)\r
1003                         {\r
1004 //                              result = entry1->remotetextstatus - entry2->remotetextstatus;\r
1005                         }\r
1006                 }\r
1007         case 6:\r
1008                 {\r
1009                         if (result == 0)\r
1010                         {\r
1011 //                              result = entry1->propstatus - entry2->propstatus;\r
1012                         }\r
1013                 }\r
1014         case 5:\r
1015                 {\r
1016                         if (result == 0)\r
1017                         {\r
1018 //                              result = entry1->textstatus - entry2->textstatus;\r
1019                         }\r
1020                 }\r
1021         case 4:\r
1022                 {\r
1023                         if (result == 0)\r
1024                         {\r
1025         //                      result = entry1->remotestatus - entry2->remotestatus;\r
1026                         }\r
1027                 }\r
1028         case 3:\r
1029                 {\r
1030                         if (result == 0)\r
1031                         {\r
1032         //                      result = entry1->status - entry2->status;\r
1033                         }\r
1034                 }\r
1035         case 2:\r
1036                 {\r
1037                         if (result == 0)\r
1038                         {\r
1039                                 result = entry1->GetFileExtension().CompareNoCase(entry2->GetFileExtension());\r
1040                         }\r
1041                 }\r
1042         case 1:\r
1043                 {\r
1044                         if (result == 0)\r
1045                         {\r
1046                                 result = entry1->GetFileOrDirectoryName().CompareNoCase(entry2->GetFileOrDirectoryName());\r
1047                         }\r
1048                 }\r
1049         case 0:         // path column\r
1050                 {\r
1051                         if (result == 0)\r
1052                         {\r
1053                                 result = CTGitPath::Compare(entry1->GetGitPathString(), entry2->GetGitPathString());\r
1054                         }\r
1055                 }\r
1056         default:\r
1057                 if ((result == 0) && (sortedColumn > 0))\r
1058                 {\r
1059                         // N/A props are "less than" empty props\r
1060 \r
1061                         const CString& propName = columnManager->GetName (sortedColumn);\r
1062 \r
1063 //                      bool entry1HasProp = entry1->present_props.HasProperty (propName);\r
1064 //                      bool entry2HasProp = entry2->present_props.HasProperty (propName);\r
1065 \r
1066 //                      if (entry1HasProp)\r
1067 //                      {\r
1068 //                              result = entry2HasProp\r
1069 //                                      ? entry1->present_props[propName].Compare \r
1070 //                                      (entry2->present_props[propName])\r
1071 //                                      : 1;\r
1072 //                      }\r
1073 //                      else\r
1074 //                      {\r
1075 //                              result = entry2HasProp ? -1 : 0;\r
1076 //                      }\r
1077                 }\r
1078         } // switch (m_nSortedColumn)\r
1079         if (!ascending)\r
1080                 result = -result;\r
1081 \r
1082         return result < 0;\r
1083 }\r