OSDN Git Service

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