1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2008 - TortoiseSVN
\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
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
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
21 #include ".\resource.h"
\r
22 #include "GitStatusListCtrl.h"
\r
24 // assign property list
\r
26 CGitStatusListCtrl::PropertyList&
\r
27 CGitStatusListCtrl::PropertyList::operator= (const char* rhs)
\r
29 // do you really want to replace the property list?
\r
31 assert (properties.empty());
\r
34 // add all properties in the list
\r
36 while ((rhs != NULL) && (*rhs != 0))
\r
38 const char* next = strchr (rhs, ' ');
\r
40 CString name (rhs, static_cast<int>(next == NULL ? strlen (rhs) : next - rhs));
\r
41 properties.insert (std::make_pair (name, CString()));
\r
43 rhs = next == NULL ? NULL : next+1;
\r
51 // collect property names in a set
\r
53 void CGitStatusListCtrl::PropertyList::GetPropertyNames (std::set<CString>& names)
\r
55 for ( CIT iter = properties.begin(), end = properties.end()
\r
59 names.insert (iter->first);
\r
63 // get a property value.
\r
65 CString CGitStatusListCtrl::PropertyList::operator[](const CString& name) const
\r
67 CIT iter = properties.find (name);
\r
69 return iter == properties.end()
\r
74 // set a property value.
\r
76 CString& CGitStatusListCtrl::PropertyList::operator[](const CString& name)
\r
78 return properties[name];
\r
81 /// check whether that property has been set on this item.
\r
83 bool CGitStatusListCtrl::PropertyList::HasProperty (const CString& name) const
\r
85 return properties.find (name) != properties.end();
\r
88 // due to frequent use: special check for svn:needs-lock
\r
90 bool CGitStatusListCtrl::PropertyList::IsNeedsLockSet() const
\r
92 static const CString svnNeedsLock = _T("svn:needs-lock");
\r
93 return HasProperty (svnNeedsLock);
\r
99 void CGitStatusListCtrl::ColumnManager::ReadSettings
\r
100 ( DWORD defaultColumns
\r
101 , const CString& containerName)
\r
105 DWORD selectedStandardColumns = defaultColumns;
\r
107 columns.resize (SVNSLC_NUMCOLUMNS);
\r
108 for (size_t i = 0; i < SVNSLC_NUMCOLUMNS; ++i)
\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
116 // userProps.clear();
\r
118 // where the settings are stored within the registry
\r
121 = _T("Software\\TortoiseGit\\StatusColumns\\") + containerName;
\r
123 // we accept settings version 2 only
\r
124 // (version 1 used different placement of hidden columns)
\r
126 bool valid = (DWORD)CRegDWORD (registryPrefix + _T("Version"), 0xff) == 2;
\r
129 // read (possibly different) column selection
\r
131 selectedStandardColumns
\r
132 = CRegDWORD (registryPrefix, selectedStandardColumns);
\r
134 // read user-prop lists
\r
136 CString userPropList
\r
137 = CRegString (registryPrefix + _T("UserProps"));
\r
138 CString shownUserProps
\r
139 = CRegString (registryPrefix + _T("ShownUserProps"));
\r
141 ParseUserPropSettings (userPropList, shownUserProps);
\r
143 // read column widths
\r
146 = CRegString (registryPrefix + _T("_Width"));
\r
148 ParseWidths (colWidths);
\r
151 // process old-style visibility setting
\r
153 SetStandardColumnVisibility (selectedStandardColumns);
\r
155 // clear all previously set header columns
\r
157 int c = ((CHeaderCtrl*)(control->GetDlgItem(0)))->GetItemCount()-1;
\r
159 control->DeleteColumn(c--);
\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
166 // restore column ordering
\r
169 ParseColumnOrder (CRegString (registryPrefix + _T("_Order")));
\r
171 ParseColumnOrder (CString());
\r
173 ApplyColumnOrder();
\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
178 for (int i = 0, count = GetColumnCount(); i < count; ++i)
\r
180 control->SetColumnWidth (i, GetVisibleWidth (i, true));
\r
183 void CGitStatusListCtrl::ColumnManager::WriteSettings() const
\r
185 // we are version 2
\r
187 CRegDWORD regVersion (registryPrefix + _T("Version"), 0, TRUE);
\r
190 // write (possibly different) column selection
\r
192 CRegDWORD regStandardColumns (registryPrefix, 0, TRUE);
\r
193 regStandardColumns = GetSelectedStandardColumns();
\r
195 // write user-prop lists
\r
197 CRegString regUserProps (registryPrefix + _T("UserProps"), CString(), TRUE);
\r
198 regUserProps = GetUserPropList();
\r
200 CRegString regShownUserProps (registryPrefix + _T("ShownUserProps"), CString(), TRUE);
\r
201 regShownUserProps = GetShownUserProps();
\r
203 // write column widths
\r
205 CRegString regWidths (registryPrefix + _T("_Width"), CString(), TRUE);
\r
206 regWidths = GetWidthString();
\r
208 // write column ordering
\r
210 CRegString regColumnOrder (registryPrefix + _T("_Order"), CString(), TRUE);
\r
211 regColumnOrder = GetColumnOrderString();
\r
214 // read column definitions
\r
216 int CGitStatusListCtrl::ColumnManager::GetColumnCount() const
\r
218 return static_cast<int>(columns.size());
\r
221 bool CGitStatusListCtrl::ColumnManager::IsVisible (int column) const
\r
223 size_t index = static_cast<size_t>(column);
\r
224 assert (columns.size() > index);
\r
226 return columns[index].visible;
\r
229 int CGitStatusListCtrl::ColumnManager::GetInvisibleCount() const
\r
231 int invisibleCount = 0;
\r
232 for (std::vector<ColumnInfo>::const_iterator it = columns.begin(); it != columns.end(); ++it)
\r
237 return invisibleCount;
\r
240 bool CGitStatusListCtrl::ColumnManager::IsRelevant (int column) const
\r
242 size_t index = static_cast<size_t>(column);
\r
243 assert (columns.size() > index);
\r
245 return columns[index].relevant;
\r
248 bool CGitStatusListCtrl::ColumnManager::IsUserProp (int column) const
\r
250 size_t index = static_cast<size_t>(column);
\r
251 assert (columns.size() > index);
\r
253 return columns[index].index >= SVNSLC_USERPROPCOLOFFSET;
\r
256 CString CGitStatusListCtrl::ColumnManager::GetName (int column) const
\r
258 static const UINT standardColumnNames[SVNSLC_NUMCOLUMNS]
\r
259 = { IDS_STATUSLIST_COLFILE
\r
261 , IDS_STATUSLIST_COLFILENAME
\r
262 , IDS_STATUSLIST_COLEXT
\r
263 , IDS_STATUSLIST_COLSTATUS
\r
265 // , IDS_STATUSLIST_COLREMOTESTATUS
\r
266 , IDS_STATUSLIST_COLTEXTSTATUS
\r
267 , IDS_STATUSLIST_COLPROPSTATUS
\r
269 // , IDS_STATUSLIST_COLREMOTETEXTSTATUS
\r
270 // , IDS_STATUSLIST_COLREMOTEPROPSTATUS
\r
271 // , IDS_STATUSLIST_COLURL
\r
273 // , IDS_STATUSLIST_COLLOCK
\r
274 // , IDS_STATUSLIST_COLLOCKCOMMENT
\r
275 , IDS_STATUSLIST_COLAUTHOR
\r
277 , IDS_STATUSLIST_COLREVISION
\r
278 // , IDS_STATUSLIST_COLREMOTEREVISION
\r
279 , IDS_STATUSLIST_COLDATE
\r
280 // , IDS_STATUSLIST_COLSVNLOCK
\r
282 // , IDS_STATUSLIST_COLCOPYFROM
\r
283 , IDS_STATUSLIST_COLMODIFICATIONDATE
\r
284 , IDS_STATUSLIST_COLADD
\r
285 , IDS_STATUSLIST_COLDEL
\r
288 // standard columns
\r
290 size_t index = static_cast<size_t>(column);
\r
291 if (index < SVNSLC_NUMCOLUMNS)
\r
294 result.LoadString (standardColumnNames[index]);
\r
298 // user-prop columns
\r
300 // if (index < columns.size())
\r
301 // return userProps[columns[index].index - SVNSLC_USERPROPCOLOFFSET].name;
\r
308 int CGitStatusListCtrl::ColumnManager::GetWidth (int column, bool useDefaults) const
\r
310 size_t index = static_cast<size_t>(column);
\r
311 assert (columns.size() > index);
\r
313 int width = columns[index].width;
\r
314 if ((width == 0) && useDefaults)
\r
315 width = LVSCW_AUTOSIZE_USEHEADER;
\r
320 int CGitStatusListCtrl::ColumnManager::GetVisibleWidth (int column, bool useDefaults) const
\r
322 return IsVisible (column)
\r
323 ? GetWidth (column, useDefaults)
\r
327 // switch columns on and off
\r
329 void CGitStatusListCtrl::ColumnManager::SetVisible
\r
333 size_t index = static_cast<size_t>(column);
\r
334 assert (index < columns.size());
\r
336 if (columns[index].visible != visible)
\r
338 columns[index].visible = visible;
\r
339 columns[index].relevant |= visible;
\r
341 columns[index].width = 0;
\r
343 control->SetColumnWidth (column, GetVisibleWidth (column, true));
\r
344 ApplyColumnOrder();
\r
346 control->Invalidate (FALSE);
\r
350 // tracking column modifications
\r
352 void CGitStatusListCtrl::ColumnManager::ColumnMoved (int column, int position)
\r
354 // in front of what column has it been inserted?
\r
356 int index = columns[column].index;
\r
358 std::vector<int> gridColumnOrder = GetGridColumnOrder();
\r
360 size_t visiblePosition = static_cast<size_t>(position);
\r
361 size_t columnCount = gridColumnOrder.size();
\r
363 for (; (visiblePosition < columnCount)
\r
364 && !columns[gridColumnOrder[visiblePosition]].visible
\r
365 ; ++visiblePosition )
\r
369 int next = visiblePosition == columnCount
\r
371 : gridColumnOrder[visiblePosition];
\r
373 // move logical column index just in front of that "next" column
\r
375 columnOrder.erase (std::find ( columnOrder.begin()
\r
376 , columnOrder.end()
\r
378 columnOrder.insert ( std::find ( columnOrder.begin()
\r
379 , columnOrder.end()
\r
383 // make sure, invisible columns are still put in front of all others
\r
385 ApplyColumnOrder();
\r
388 void CGitStatusListCtrl::ColumnManager::ColumnResized (int column)
\r
390 size_t index = static_cast<size_t>(column);
\r
391 assert (index < columns.size());
\r
392 assert (columns[index].visible);
\r
394 int width = control->GetColumnWidth (column);
\r
395 columns[index].width = width;
\r
397 int propertyIndex = columns[index].index;
\r
398 if (propertyIndex >= SVNSLC_USERPROPCOLOFFSET)
\r
399 userProps[propertyIndex - SVNSLC_USERPROPCOLOFFSET].width = width;
\r
401 control->Invalidate (FALSE);
\r
404 // call these to update the user-prop list
\r
405 // (will also auto-insert /-remove new list columns)
\r
407 void CGitStatusListCtrl::ColumnManager::UpdateUserPropList
\r
408 (const std::vector<FileEntry*>& files)
\r
410 // collect all user-defined props
\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
416 aggregatedProps.erase (_T("svn:needs-lock"));
\r
417 itemProps = aggregatedProps;
\r
419 // add new ones to the internal list
\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
425 while ( newProps.size() + userProps.size()
\r
426 > SVNSLC_MAXCOLUMNCOUNT - SVNSLC_USERPROPCOLOFFSET)
\r
427 newProps.erase (--newProps.end());
\r
429 typedef std::set<CString>::const_iterator CIT;
\r
430 for ( CIT iter = newProps.begin(), end = newProps.end()
\r
434 int index = static_cast<int>(userProps.size())
\r
435 + SVNSLC_USERPROPCOLOFFSET;
\r
436 columnOrder.push_back (index);
\r
439 userProp.name = *iter;
\r
440 userProp.width = 0;
\r
442 userProps.push_back (userProp);
\r
445 // remove unused columns from control.
\r
446 // remove used ones from the set of aggregatedProps.
\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
452 // this user-prop has not been set on any item
\r
454 if (!columns[i-1].visible)
\r
456 control->DeleteColumn (static_cast<int>(i-1));
\r
457 columns.erase (columns.begin() + i-1);
\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
466 for ( CIT iter = aggregatedProps.begin(), end = aggregatedProps.end()
\r
470 // get the logical column index / ID
\r
474 for (size_t i = 0, count = userProps.size(); i < count; ++i)
\r
475 if (userProps[i].name == *iter)
\r
477 index = static_cast<int>(i) + SVNSLC_USERPROPCOLOFFSET;
\r
478 width = userProps[i].width;
\r
482 assert (index != -1);
\r
484 // find insertion position
\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
492 column.index = index;
\r
493 column.width = width;
\r
494 column.visible = false;
\r
496 columns.insert (columnIter, column);
\r
500 int result = control->InsertColumn (pos, *iter, LVCFMT_LEFT, GetVisibleWidth(pos, false));
\r
501 assert (result != -1);
\r
502 UNREFERENCED_PARAMETER(result);
\r
505 // update column order
\r
507 ApplyColumnOrder();
\r
513 void CGitStatusListCtrl::ColumnManager::UpdateRelevance
\r
514 ( const std::vector<FileEntry*>& files
\r
515 , const std::vector<size_t>& visibleFiles)
\r
517 // collect all user-defined props that belong to shown files
\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
523 aggregatedProps.erase (_T("svn:needs-lock"));
\r
524 itemProps = aggregatedProps;
\r
526 // invisible columns for unused props are not relevant
\r
528 for (int i = 0, count = GetColumnCount(); i < count; ++i)
\r
529 if (IsUserProp(i) && !IsVisible(i))
\r
531 columns[i].relevant
\r
532 = aggregatedProps.find (GetName(i)) != aggregatedProps.end();
\r
537 // don't clutter the context menu with irrelevant prop info
\r
539 bool CGitStatusListCtrl::ColumnManager::AnyUnusedProperties() const
\r
541 return columns.size() < userProps.size() + SVNSLC_NUMCOLUMNS;
\r
544 void CGitStatusListCtrl::ColumnManager::RemoveUnusedProps()
\r
546 // determine what column indexes / IDs to keep.
\r
547 // map them onto new IDs (we may delete some IDs in between)
\r
549 std::map<int, int> validIndices;
\r
550 int userPropID = SVNSLC_USERPROPCOLOFFSET;
\r
552 for (size_t i = 0, count = columns.size(); i < count; ++i)
\r
554 int index = columns[i].index;
\r
556 if ( itemProps.find (GetName((int)i)) != itemProps.end()
\r
557 || columns[i].visible
\r
558 || index < SVNSLC_USERPROPCOLOFFSET)
\r
560 validIndices[index] = index < SVNSLC_USERPROPCOLOFFSET
\r
566 // remove everything else:
\r
568 // remove from columns and control.
\r
569 // also update index values in columns
\r
571 for (size_t i = columns.size(); i > 0; --i)
\r
573 std::map<int, int>::const_iterator iter
\r
574 = validIndices.find (columns[i-1].index);
\r
576 if (iter == validIndices.end())
\r
578 control->DeleteColumn (static_cast<int>(i-1));
\r
579 columns.erase (columns.begin() + i-1);
\r
583 columns[i-1].index = iter->second;
\r
587 // remove from user props
\r
589 for (size_t i = userProps.size(); i > 0; --i)
\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
596 // remove from and update column order
\r
598 for (size_t i = columnOrder.size(); i > 0; --i)
\r
600 std::map<int, int>::const_iterator iter
\r
601 = validIndices.find (columnOrder[i-1]);
\r
603 if (iter == validIndices.end())
\r
604 columnOrder.erase (columnOrder.begin() + i-1);
\r
606 columnOrder[i-1] = iter->second;
\r
610 // bring everything back to its "natural" order
\r
612 void CGitStatusListCtrl::ColumnManager::ResetColumns (DWORD defaultColumns)
\r
614 // update internal data
\r
616 std::sort (columnOrder.begin(), columnOrder.end());
\r
618 for (size_t i = 0, count = columns.size(); i < count; ++i)
\r
620 columns[i].width = 0;
\r
621 columns[i].visible = (i < 32) && (((defaultColumns >> i) & 1) != 0);
\r
624 for (size_t i = 0, count = userProps.size(); i < count; ++i)
\r
625 userProps[i].width = 0;
\r
629 for (int i = 0, count = GetColumnCount(); i < count; ++i)
\r
630 control->SetColumnWidth (i, GetVisibleWidth (i, true));
\r
632 ApplyColumnOrder();
\r
634 control->Invalidate (FALSE);
\r
637 // initialization utilities
\r
639 void CGitStatusListCtrl::ColumnManager::ParseUserPropSettings
\r
640 ( const CString& userPropList
\r
641 , const CString& shownUserProps)
\r
643 assert (userProps.empty());
\r
645 static CString delimiters (_T(" "));
\r
647 // parse list of visible user-props
\r
649 std::set<CString> visibles;
\r
652 CString name = shownUserProps.Tokenize (delimiters, pos);
\r
653 while (!name.IsEmpty())
\r
655 visibles.insert (name);
\r
656 name = shownUserProps.Tokenize (delimiters, pos);
\r
659 // create list of all user-props
\r
662 name = userPropList.Tokenize (delimiters, pos);
\r
663 while (!name.IsEmpty())
\r
665 bool visible = visibles.find (name) != visibles.end();
\r
668 newEntry.name = name;
\r
669 newEntry.width = 0;
\r
671 userProps.push_back (newEntry);
\r
673 // auto-create columns for visible user-props
\r
674 // (others may be added later)
\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
685 columns.push_back (newColumn);
\r
688 name = userPropList.Tokenize (delimiters, pos);
\r
692 void CGitStatusListCtrl::ColumnManager::ParseWidths (const CString& widths)
\r
694 for (int i = 0, count = widths.GetLength() / 8; i < count; ++i)
\r
696 long width = _tcstol (widths.Mid (i*8, 8), NULL, 16);
\r
697 if (i < SVNSLC_NUMCOLUMNS)
\r
699 // a standard column
\r
701 columns[i].width = width;
\r
703 else if (i >= SVNSLC_USERPROPCOLOFFSET)
\r
705 // a user-prop column
\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
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
717 // there is no such column
\r
719 assert (width == 0);
\r
724 void CGitStatusListCtrl::ColumnManager::SetStandardColumnVisibility
\r
727 for (size_t i = 0; i < SVNSLC_NUMCOLUMNS; ++i)
\r
729 columns[i].visible = (visibility & 1) > 0;
\r
734 void CGitStatusListCtrl::ColumnManager::ParseColumnOrder
\r
735 (const CString& widths)
\r
737 std::set<int> alreadyPlaced;
\r
738 columnOrder.clear();
\r
740 // place columns according to valid entries in orderString
\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
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
749 alreadyPlaced.insert (index);
\r
750 columnOrder.push_back (index);
\r
754 // place the remaining colums behind it
\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
760 for (int i = SVNSLC_USERPROPCOLOFFSET; i < limit; ++i)
\r
761 if (alreadyPlaced.find (i) == alreadyPlaced.end())
\r
762 columnOrder.push_back (i);
\r
765 // map internal column order onto visible column order
\r
766 // (all invisibles in front)
\r
768 std::vector<int> CGitStatusListCtrl::ColumnManager::GetGridColumnOrder()
\r
770 // extract order of used columns from order of all columns
\r
772 std::vector<int> result;
\r
773 result.reserve (SVNSLC_MAXCOLUMNCOUNT+1);
\r
775 size_t colCount = columns.size();
\r
776 bool visible = false;
\r
780 // put invisible cols in front
\r
782 for (size_t i = 0, count = columnOrder.size(); i < count; ++i)
\r
784 int index = columnOrder[i];
\r
785 for (size_t k = 0; k < colCount; ++k)
\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
793 visible = !visible;
\r
800 void CGitStatusListCtrl::ColumnManager::ApplyColumnOrder()
\r
802 // extract order of used columns from order of all columns
\r
804 int order[SVNSLC_MAXCOLUMNCOUNT+1];
\r
805 SecureZeroMemory (order, sizeof (order));
\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
810 // we must have placed all columns or something is really fishy ..
\r
812 assert (gridColumnOrder.size() == columns.size());
\r
813 assert (GetColumnCount() == ((CHeaderCtrl*)(control->GetDlgItem(0)))->GetItemCount());
\r
815 // o.k., apply our column ordering
\r
817 control->SetColumnOrderArray (GetColumnCount(), order);
\r
820 // utilities used when writing data to the registry
\r
822 DWORD CGitStatusListCtrl::ColumnManager::GetSelectedStandardColumns() const
\r
825 for (size_t i = SVNSLC_NUMCOLUMNS; i > 0; --i)
\r
826 result = result * 2 + (columns[i-1].visible ? 1 : 0);
\r
831 CString CGitStatusListCtrl::ColumnManager::GetUserPropList() const
\r
835 for (size_t i = 0, count = userProps.size(); i < count; ++i)
\r
836 result += userProps[i].name + _T(' ');
\r
841 CString CGitStatusListCtrl::ColumnManager::GetShownUserProps() const
\r
845 for (size_t i = 0, count = columns.size(); i < count; ++i)
\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
856 CString CGitStatusListCtrl::ColumnManager::GetWidthString() const
\r
863 for (size_t i = 0; i < SVNSLC_NUMCOLUMNS; ++i)
\r
865 _stprintf_s (buf, 10, _T("%08X"), columns[i].width);
\r
869 // range with no column IDs
\r
871 result += CString ('0', 8 * (SVNSLC_USERPROPCOLOFFSET - SVNSLC_NUMCOLUMNS));
\r
873 // user-prop columns
\r
875 for (size_t i = 0, count = userProps.size(); i < count; ++i)
\r
877 _stprintf_s (buf, 10, _T("%08X"), userProps[i].width);
\r
884 CString CGitStatusListCtrl::ColumnManager::GetColumnOrderString() const
\r
889 for (size_t i = 0, count = columnOrder.size(); i < count; ++i)
\r
891 _stprintf_s (buf, 3, _T("%02X"), columnOrder[i]);
\r
898 // sorter utility class
\r
900 CGitStatusListCtrl::CSorter::CSorter ( ColumnManager* columnManager
\r
903 : columnManager (columnManager)
\r
904 , sortedColumn (sortedColumn)
\r
905 , ascending (ascending)
\r
909 bool CGitStatusListCtrl::CSorter::operator()
\r
910 ( const CTGitPath* entry1
\r
911 , const CTGitPath* entry2) const
\r
913 #define SGN(x) ((x)==0?0:((x)>0?1:-1))
\r
916 switch (sortedColumn)
\r
922 __int64 writetime1 = entry1->GetLastWriteTime();
\r
923 __int64 writetime2 = entry2->GetLastWriteTime();
\r
925 FILETIME* filetime1 = (FILETIME*)(__int64*)&writetime1;
\r
926 FILETIME* filetime2 = (FILETIME*)(__int64*)&writetime2;
\r
928 result = CompareFileTime(filetime1,filetime2);
\r
935 // result = entry1->copyfrom_url.CompareNoCase(entry2->copyfrom_url);
\r
942 // result = SGN(entry1->needslock - entry2->needslock);
\r
949 // result = SGN(entry1->last_commit_date - entry2->last_commit_date);
\r
956 // result = entry1->remoterev - entry2->remoterev;
\r
963 // result = entry1->last_commit_rev - entry2->last_commit_rev;
\r
970 // result = entry1->last_commit_author.CompareNoCase(entry2->last_commit_author);
\r
977 // result = entry1->lock_comment.CompareNoCase(entry2->lock_comment);
\r
984 // result = entry1->lock_owner.CompareNoCase(entry2->lock_owner);
\r
991 // result = entry1->url.CompareNoCase(entry2->url);
\r
998 // result = entry1->remotepropstatus - entry2->remotepropstatus;
\r
1005 // result = entry1->remotetextstatus - entry2->remotetextstatus;
\r
1012 // result = entry1->propstatus - entry2->propstatus;
\r
1019 // result = entry1->textstatus - entry2->textstatus;
\r
1026 // result = entry1->remotestatus - entry2->remotestatus;
\r
1033 // result = entry1->status - entry2->status;
\r
1040 result = entry1->GetFileExtension().CompareNoCase(entry2->GetFileExtension());
\r
1047 result = entry1->GetFileOrDirectoryName().CompareNoCase(entry2->GetFileOrDirectoryName());
\r
1050 case 0: // path column
\r
1054 result = CTGitPath::Compare(entry1->GetGitPathString(), entry2->GetGitPathString());
\r
1058 if ((result == 0) && (sortedColumn > 0))
\r
1060 // N/A props are "less than" empty props
\r
1062 const CString& propName = columnManager->GetName (sortedColumn);
\r
1064 // bool entry1HasProp = entry1->present_props.HasProperty (propName);
\r
1065 // bool entry2HasProp = entry2->present_props.HasProperty (propName);
\r
1067 // if (entry1HasProp)
\r
1069 // result = entry2HasProp
\r
1070 // ? entry1->present_props[propName].Compare
\r
1071 // (entry2->present_props[propName])
\r
1076 // result = entry2HasProp ? -1 : 0;
\r
1079 } // switch (m_nSortedColumn)
\r
1083 return result < 0;
\r