OSDN Git Service

remove FileEntry Structure
[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 #if 0\r
407 void CGitStatusListCtrl::ColumnManager::UpdateUserPropList \r
408     (const std::vector<FileEntry*>& files)\r
409 {\r
410     // collect all user-defined props\r
411 \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 \r
510 }\r
511 #endif\r
512 #if 0\r
513 void CGitStatusListCtrl::ColumnManager::UpdateRelevance \r
514     ( const std::vector<FileEntry*>& files\r
515     , const std::vector<size_t>& visibleFiles)\r
516 {\r
517     // collect all user-defined props that belong to shown files\r
518 \r
519     std::set<CString> aggregatedProps;\r
520     for (size_t i = 0, count = visibleFiles.size(); i < count; ++i)\r
521         files[visibleFiles[i]]->present_props.GetPropertyNames (aggregatedProps);\r
522 \r
523     aggregatedProps.erase (_T("svn:needs-lock"));\r
524     itemProps = aggregatedProps;\r
525 \r
526     // invisible columns for unused props are not relevant\r
527 \r
528     for (int i = 0, count = GetColumnCount(); i < count; ++i)\r
529         if (IsUserProp(i) && !IsVisible(i))\r
530         {\r
531             columns[i].relevant \r
532                 = aggregatedProps.find (GetName(i)) != aggregatedProps.end();\r
533         }\r
534 \r
535 }\r
536 #endif\r
537 // don't clutter the context menu with irrelevant prop info\r
538 \r
539 bool CGitStatusListCtrl::ColumnManager::AnyUnusedProperties() const\r
540 {\r
541     return columns.size() < userProps.size() + SVNSLC_NUMCOLUMNS;\r
542 }\r
543 \r
544 void CGitStatusListCtrl::ColumnManager::RemoveUnusedProps()\r
545 {\r
546     // determine what column indexes / IDs to keep.\r
547     // map them onto new IDs (we may delete some IDs in between)\r
548 \r
549     std::map<int, int> validIndices;\r
550     int userPropID = SVNSLC_USERPROPCOLOFFSET;\r
551 \r
552     for (size_t i = 0, count = columns.size(); i < count; ++i)\r
553     {\r
554         int index = columns[i].index;\r
555 \r
556         if (   itemProps.find (GetName((int)i)) != itemProps.end()\r
557             || columns[i].visible\r
558             || index < SVNSLC_USERPROPCOLOFFSET)\r
559         {\r
560             validIndices[index] = index < SVNSLC_USERPROPCOLOFFSET\r
561                                 ? index\r
562                                 : userPropID++;\r
563         }\r
564     }\r
565 \r
566     // remove everything else:\r
567 \r
568     // remove from columns and control.\r
569     // also update index values in columns\r
570 \r
571     for (size_t i = columns.size(); i > 0; --i)\r
572     {\r
573         std::map<int, int>::const_iterator iter \r
574             = validIndices.find (columns[i-1].index);\r
575 \r
576         if (iter == validIndices.end())\r
577         {\r
578             control->DeleteColumn (static_cast<int>(i-1));\r
579             columns.erase (columns.begin() + i-1);\r
580         }\r
581         else\r
582         {\r
583             columns[i-1].index = iter->second;\r
584         }\r
585     }\r
586 \r
587     // remove from user props\r
588 \r
589     for (size_t i = userProps.size(); i > 0; --i)\r
590     {\r
591         int index = static_cast<int>(i)-1 + SVNSLC_USERPROPCOLOFFSET;\r
592         if (validIndices.find (index) == validIndices.end())\r
593             userProps.erase (userProps.begin() + i-1);\r
594     }\r
595 \r
596     // remove from and update column order\r
597 \r
598     for (size_t i = columnOrder.size(); i > 0; --i)\r
599     {\r
600         std::map<int, int>::const_iterator iter \r
601             = validIndices.find (columnOrder[i-1]);\r
602 \r
603         if (iter == validIndices.end())\r
604             columnOrder.erase (columnOrder.begin() + i-1);\r
605         else\r
606             columnOrder[i-1] = iter->second;\r
607     }\r
608 }\r
609 \r
610 // bring everything back to its "natural" order\r
611 \r
612 void CGitStatusListCtrl::ColumnManager::ResetColumns (DWORD defaultColumns)\r
613 {\r
614     // update internal data\r
615 \r
616     std::sort (columnOrder.begin(), columnOrder.end());\r
617 \r
618     for (size_t i = 0, count = columns.size(); i < count; ++i)\r
619     {\r
620         columns[i].width = 0;\r
621         columns[i].visible = (i < 32) && (((defaultColumns >> i) & 1) != 0);\r
622     }\r
623 \r
624     for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
625         userProps[i].width = 0;\r
626 \r
627     // update UI\r
628 \r
629     for (int i = 0, count = GetColumnCount(); i < count; ++i)\r
630         control->SetColumnWidth (i, GetVisibleWidth (i, true));\r
631 \r
632     ApplyColumnOrder();\r
633 \r
634     control->Invalidate (FALSE);\r
635 }\r
636 \r
637 // initialization utilities\r
638 \r
639 void CGitStatusListCtrl::ColumnManager::ParseUserPropSettings \r
640     ( const CString& userPropList\r
641     , const CString& shownUserProps)\r
642 {\r
643     assert (userProps.empty());\r
644 \r
645     static CString delimiters (_T(" "));\r
646 \r
647     // parse list of visible user-props\r
648 \r
649     std::set<CString> visibles;\r
650 \r
651     int pos = 0;\r
652     CString name = shownUserProps.Tokenize (delimiters, pos);\r
653     while (!name.IsEmpty())\r
654     {\r
655         visibles.insert (name);\r
656         name = shownUserProps.Tokenize (delimiters, pos);\r
657     }\r
658 \r
659     // create list of all user-props\r
660 \r
661     pos = 0;\r
662     name = userPropList.Tokenize (delimiters, pos);\r
663     while (!name.IsEmpty())\r
664     {\r
665         bool visible = visibles.find (name) != visibles.end();\r
666 \r
667         UserProp newEntry;\r
668         newEntry.name = name;\r
669         newEntry.width = 0;\r
670 \r
671         userProps.push_back (newEntry);\r
672 \r
673         // auto-create columns for visible user-props\r
674         // (others may be added later)\r
675 \r
676         if (visible)\r
677         {\r
678             ColumnInfo newColumn;\r
679             newColumn.width = 0;\r
680             newColumn.visible = true;\r
681             newColumn.relevant = true;\r
682             newColumn.index = static_cast<int>(userProps.size()) \r
683                             + SVNSLC_USERPROPCOLOFFSET - 1;\r
684 \r
685             columns.push_back (newColumn);\r
686         }\r
687 \r
688         name = userPropList.Tokenize (delimiters, pos);\r
689     }\r
690 }\r
691 \r
692 void CGitStatusListCtrl::ColumnManager::ParseWidths (const CString& widths)\r
693 {\r
694     for (int i = 0, count = widths.GetLength() / 8; i < count; ++i)\r
695     {\r
696                 long width = _tcstol (widths.Mid (i*8, 8), NULL, 16);\r
697         if (i < SVNSLC_NUMCOLUMNS)\r
698         {\r
699             // a standard column\r
700 \r
701             columns[i].width = width;\r
702         }\r
703         else if (i >= SVNSLC_USERPROPCOLOFFSET)\r
704         {\r
705             // a user-prop column\r
706 \r
707             size_t index = static_cast<size_t>(i - SVNSLC_USERPROPCOLOFFSET);\r
708             assert (index < userProps.size());\r
709             userProps[index].width = width;\r
710 \r
711             for (size_t k = 0, count = columns.size(); k < count; ++k)\r
712                 if (columns[k].index == i)\r
713                     columns[k].width = width;\r
714         }\r
715         else\r
716         {\r
717             // there is no such column \r
718 \r
719             assert (width == 0);\r
720         }\r
721     }\r
722 }\r
723 \r
724 void CGitStatusListCtrl::ColumnManager::SetStandardColumnVisibility \r
725     (DWORD visibility)\r
726 {\r
727     for (size_t i = 0; i < SVNSLC_NUMCOLUMNS; ++i)\r
728     {\r
729         columns[i].visible = (visibility & 1) > 0;\r
730         visibility /= 2;\r
731     }\r
732 }\r
733 \r
734 void CGitStatusListCtrl::ColumnManager::ParseColumnOrder \r
735     (const CString& widths)\r
736 {\r
737     std::set<int> alreadyPlaced;\r
738     columnOrder.clear();\r
739 \r
740     // place columns according to valid entries in orderString\r
741 \r
742     int limit = static_cast<int>(SVNSLC_USERPROPCOLOFFSET + userProps.size());\r
743     for (int i = 0, count = widths.GetLength() / 2; i < count; ++i)\r
744     {\r
745                 int index = _tcstol (widths.Mid (i*2, 2), NULL, 16);\r
746         if (   (index < SVNSLC_NUMCOLUMNS)\r
747             || ((index >= SVNSLC_USERPROPCOLOFFSET) && (index < limit)))\r
748         {\r
749             alreadyPlaced.insert (index);\r
750             columnOrder.push_back (index);\r
751         }\r
752     }\r
753 \r
754     // place the remaining colums behind it\r
755 \r
756     for (int i = 0; i < SVNSLC_NUMCOLUMNS; ++i)\r
757         if (alreadyPlaced.find (i) == alreadyPlaced.end())\r
758             columnOrder.push_back (i);\r
759 \r
760     for (int i = SVNSLC_USERPROPCOLOFFSET; i < limit; ++i)\r
761         if (alreadyPlaced.find (i) == alreadyPlaced.end())\r
762             columnOrder.push_back (i);\r
763 }\r
764 \r
765 // map internal column order onto visible column order\r
766 // (all invisibles in front)\r
767 \r
768 std::vector<int> CGitStatusListCtrl::ColumnManager::GetGridColumnOrder()\r
769 {\r
770     // extract order of used columns from order of all columns\r
771 \r
772     std::vector<int> result;\r
773     result.reserve (SVNSLC_MAXCOLUMNCOUNT+1);\r
774 \r
775     size_t colCount = columns.size();\r
776     bool visible = false;\r
777 \r
778     do\r
779     {\r
780         // put invisible cols in front\r
781 \r
782         for (size_t i = 0, count = columnOrder.size(); i < count; ++i)\r
783         {\r
784             int index = columnOrder[i];\r
785             for (size_t k = 0; k < colCount; ++k)\r
786             {\r
787                 const ColumnInfo& column = columns[k];\r
788                 if ((column.index == index) && (column.visible == visible))\r
789                     result.push_back (static_cast<int>(k));\r
790             }\r
791         }\r
792 \r
793         visible = !visible;\r
794     }\r
795     while (visible);\r
796 \r
797     return result;\r
798 }\r
799 \r
800 void CGitStatusListCtrl::ColumnManager::ApplyColumnOrder()\r
801 {\r
802     // extract order of used columns from order of all columns\r
803 \r
804     int order[SVNSLC_MAXCOLUMNCOUNT+1];\r
805     SecureZeroMemory (order, sizeof (order));\r
806 \r
807     std::vector<int> gridColumnOrder = GetGridColumnOrder();\r
808         std::copy (gridColumnOrder.begin(), gridColumnOrder.end(), stdext::checked_array_iterator<int*>(&order[0], sizeof(order)));\r
809 \r
810     // we must have placed all columns or something is really fishy ..\r
811 \r
812     assert (gridColumnOrder.size() == columns.size());\r
813         assert (GetColumnCount() == ((CHeaderCtrl*)(control->GetDlgItem(0)))->GetItemCount());\r
814 \r
815     // o.k., apply our column ordering\r
816 \r
817     control->SetColumnOrderArray (GetColumnCount(), order);\r
818 }\r
819 \r
820 // utilities used when writing data to the registry\r
821 \r
822 DWORD CGitStatusListCtrl::ColumnManager::GetSelectedStandardColumns() const\r
823 {\r
824     DWORD result = 0;\r
825     for (size_t i = SVNSLC_NUMCOLUMNS; i > 0; --i)\r
826         result = result * 2 + (columns[i-1].visible ? 1 : 0);\r
827 \r
828     return result;\r
829 }\r
830 \r
831 CString CGitStatusListCtrl::ColumnManager::GetUserPropList() const\r
832 {\r
833     CString result;\r
834 \r
835     for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
836         result += userProps[i].name + _T(' ');\r
837 \r
838     return result;\r
839 }\r
840 \r
841 CString CGitStatusListCtrl::ColumnManager::GetShownUserProps() const\r
842 {\r
843     CString result;\r
844 \r
845     for (size_t i = 0, count = columns.size(); i < count; ++i)\r
846     {\r
847         size_t index = static_cast<size_t>(columns[i].index);\r
848         if (columns[i].visible && (index >= SVNSLC_USERPROPCOLOFFSET))\r
849             result += userProps[index - SVNSLC_USERPROPCOLOFFSET].name \r
850                     + _T(' ');\r
851     }\r
852 \r
853     return result;\r
854 }\r
855 \r
856 CString CGitStatusListCtrl::ColumnManager::GetWidthString() const\r
857 {\r
858     CString result;\r
859 \r
860     // regular columns\r
861 \r
862         TCHAR buf[10];\r
863     for (size_t i = 0; i < SVNSLC_NUMCOLUMNS; ++i)\r
864         {\r
865                 _stprintf_s (buf, 10, _T("%08X"), columns[i].width);\r
866                 result += buf;\r
867         }\r
868 \r
869     // range with no column IDs\r
870 \r
871     result += CString ('0', 8 * (SVNSLC_USERPROPCOLOFFSET - SVNSLC_NUMCOLUMNS));\r
872 \r
873     // user-prop columns\r
874 \r
875     for (size_t i = 0, count = userProps.size(); i < count; ++i)\r
876         {\r
877                 _stprintf_s (buf, 10, _T("%08X"), userProps[i].width);\r
878                 result += buf;\r
879         }\r
880 \r
881     return result;\r
882 }\r
883 \r
884 CString CGitStatusListCtrl::ColumnManager::GetColumnOrderString() const\r
885 {\r
886     CString result;\r
887 \r
888         TCHAR buf[3];\r
889     for (size_t i = 0, count = columnOrder.size(); i < count; ++i)\r
890         {\r
891                 _stprintf_s (buf, 3, _T("%02X"), columnOrder[i]);\r
892                 result += buf;\r
893         }\r
894 \r
895     return result;\r
896 }\r
897 \r
898 // sorter utility class\r
899 \r
900 CGitStatusListCtrl::CSorter::CSorter ( ColumnManager* columnManager\r
901                                                                           , int sortedColumn\r
902                                                                           , bool ascending)\r
903                                                                           : columnManager (columnManager)\r
904                                                                           , sortedColumn (sortedColumn)\r
905                                                                           , ascending (ascending)\r
906 {\r
907 }\r
908 \r
909 bool CGitStatusListCtrl::CSorter::operator()\r
910 ( const CTGitPath* entry1\r
911  , const CTGitPath* entry2) const\r
912 {\r
913 #define SGN(x) ((x)==0?0:((x)>0?1:-1))\r
914 \r
915         int result = 0;\r
916         switch (sortedColumn)\r
917         {\r
918         case 18:\r
919                 {\r
920                         if (result == 0)\r
921                         {\r
922                                 __int64 writetime1 = entry1->GetLastWriteTime();\r
923                                 __int64 writetime2 = entry2->GetLastWriteTime();\r
924 \r
925                                 FILETIME* filetime1 = (FILETIME*)(__int64*)&writetime1;\r
926                                 FILETIME* filetime2 = (FILETIME*)(__int64*)&writetime2;\r
927 \r
928                                 result = CompareFileTime(filetime1,filetime2);\r
929                         }\r
930                 }\r
931         case 17:\r
932                 {\r
933                         if (result == 0)\r
934                         {\r
935 //                              result = entry1->copyfrom_url.CompareNoCase(entry2->copyfrom_url);\r
936                         }\r
937                 }\r
938         case 16:\r
939                 {\r
940                         if (result == 0)\r
941                         {\r
942 //                              result = SGN(entry1->needslock - entry2->needslock);\r
943                         }\r
944                 }\r
945         case 15:\r
946                 {\r
947                         if (result == 0)\r
948                         {\r
949 //                              result = SGN(entry1->last_commit_date - entry2->last_commit_date);\r
950                         }\r
951                 }\r
952         case 14:\r
953                 {\r
954                         if (result == 0)\r
955                         {\r
956 //                              result = entry1->remoterev - entry2->remoterev;\r
957                         }\r
958                 }\r
959         case 13:\r
960                 {\r
961                         if (result == 0)\r
962                         {\r
963 //                              result = entry1->last_commit_rev - entry2->last_commit_rev;\r
964                         }\r
965                 }\r
966         case 12:\r
967                 {\r
968                         if (result == 0)\r
969                         {\r
970 //                              result = entry1->last_commit_author.CompareNoCase(entry2->last_commit_author);\r
971                         }\r
972                 }\r
973         case 11:\r
974                 {\r
975                         if (result == 0)\r
976                         {\r
977 //                              result = entry1->lock_comment.CompareNoCase(entry2->lock_comment);\r
978                         }\r
979                 }\r
980         case 10:\r
981                 {\r
982                         if (result == 0)\r
983                         {\r
984 //                              result = entry1->lock_owner.CompareNoCase(entry2->lock_owner);\r
985                         }\r
986                 }\r
987         case 9:\r
988                 {\r
989                         if (result == 0)\r
990                         {\r
991 //                              result = entry1->url.CompareNoCase(entry2->url);\r
992                         }\r
993                 }\r
994         case 8:\r
995                 {\r
996                         if (result == 0)\r
997                         {\r
998 //                              result = entry1->remotepropstatus - entry2->remotepropstatus;\r
999                         }\r
1000                 }\r
1001         case 7:\r
1002                 {\r
1003                         if (result == 0)\r
1004                         {\r
1005 //                              result = entry1->remotetextstatus - entry2->remotetextstatus;\r
1006                         }\r
1007                 }\r
1008         case 6:\r
1009                 {\r
1010                         if (result == 0)\r
1011                         {\r
1012 //                              result = entry1->propstatus - entry2->propstatus;\r
1013                         }\r
1014                 }\r
1015         case 5:\r
1016                 {\r
1017                         if (result == 0)\r
1018                         {\r
1019 //                              result = entry1->textstatus - entry2->textstatus;\r
1020                         }\r
1021                 }\r
1022         case 4:\r
1023                 {\r
1024                         if (result == 0)\r
1025                         {\r
1026         //                      result = entry1->remotestatus - entry2->remotestatus;\r
1027                         }\r
1028                 }\r
1029         case 3:\r
1030                 {\r
1031                         if (result == 0)\r
1032                         {\r
1033         //                      result = entry1->status - entry2->status;\r
1034                         }\r
1035                 }\r
1036         case 2:\r
1037                 {\r
1038                         if (result == 0)\r
1039                         {\r
1040                                 result = entry1->GetFileExtension().CompareNoCase(entry2->GetFileExtension());\r
1041                         }\r
1042                 }\r
1043         case 1:\r
1044                 {\r
1045                         if (result == 0)\r
1046                         {\r
1047                                 result = entry1->GetFileOrDirectoryName().CompareNoCase(entry2->GetFileOrDirectoryName());\r
1048                         }\r
1049                 }\r
1050         case 0:         // path column\r
1051                 {\r
1052                         if (result == 0)\r
1053                         {\r
1054                                 result = CTGitPath::Compare(entry1->GetGitPathString(), entry2->GetGitPathString());\r
1055                         }\r
1056                 }\r
1057         default:\r
1058                 if ((result == 0) && (sortedColumn > 0))\r
1059                 {\r
1060                         // N/A props are "less than" empty props\r
1061 \r
1062                         const CString& propName = columnManager->GetName (sortedColumn);\r
1063 \r
1064 //                      bool entry1HasProp = entry1->present_props.HasProperty (propName);\r
1065 //                      bool entry2HasProp = entry2->present_props.HasProperty (propName);\r
1066 \r
1067 //                      if (entry1HasProp)\r
1068 //                      {\r
1069 //                              result = entry2HasProp\r
1070 //                                      ? entry1->present_props[propName].Compare \r
1071 //                                      (entry2->present_props[propName])\r
1072 //                                      : 1;\r
1073 //                      }\r
1074 //                      else\r
1075 //                      {\r
1076 //                              result = entry2HasProp ? -1 : 0;\r
1077 //                      }\r
1078                 }\r
1079         } // switch (m_nSortedColumn)\r
1080         if (!ascending)\r
1081                 result = -result;\r
1082 \r
1083         return result < 0;\r
1084 }\r