1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008, 2014 - TortoiseSVN
4 // Copyright (C) 2008-2016 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "GitStatusListCtrl.h"
26 // registry version number of column-settings of GitLogListBase
27 #define GITSLC_COL_VERSION 5
30 #define assert(x) ATLASSERT(x)
32 // assign property list
35 PropertyList::operator= (const char* rhs
)
37 // do you really want to replace the property list?
39 assert (properties
.empty());
42 // add all properties in the list
46 const char* next
= strchr (rhs
, ' ');
48 CString
name (rhs
, static_cast<int>(!next
? strlen (rhs
) : next
- rhs
));
49 properties
.insert (std::make_pair (name
, CString()));
51 rhs
= !next
? nullptr : next
+ 1;
59 // collect property names in a set
61 void PropertyList::GetPropertyNames (std::set
<CString
>& names
)
63 for (CIT iter
= properties
.cbegin(), end
= properties
.cend()
67 names
.insert (iter
->first
);
71 // get a property value.
73 CString
PropertyList::operator[](const CString
& name
) const
75 CIT iter
= properties
.find (name
);
77 return iter
== properties
.end()
82 // set a property value.
84 CString
& PropertyList::operator[](const CString
& name
)
86 return properties
[name
];
89 /// check whether that property has been set on this item.
91 bool PropertyList::HasProperty (const CString
& name
) const
93 return properties
.find (name
) != properties
.end();
96 // due to frequent use: special check for svn:needs-lock
98 bool PropertyList::IsNeedsLockSet() const
100 static const CString svnNeedsLock
= _T("svn:needs-lock");
101 return HasProperty (svnNeedsLock
);
107 void ColumnManager::ReadSettings
108 ( DWORD defaultColumns
110 , const CString
& containerName
115 DWORD selectedStandardColumns
= defaultColumns
& ~hideColumns
;
116 m_dwDefaultColumns
= defaultColumns
& ~hideColumns
;
118 columns
.resize (maxsize
);
120 for (int i
= 0; i
< maxsize
; ++i
)
122 columns
[i
].index
= static_cast<int>(i
);
124 columns
[i
].width
= 0;
126 columns
[i
].width
= widthlist
[i
];
127 columns
[i
].visible
= true;
128 columns
[i
].relevant
= !(hideColumns
& power
);
129 columns
[i
].adjusted
= false;
133 // userProps.clear();
135 // where the settings are stored within the registry
137 registryPrefix
= _T("Software\\TortoiseGit\\StatusColumns\\") + containerName
;
139 // we accept settings of current version only
140 bool valid
= (DWORD
)CRegDWORD (registryPrefix
+ _T("Version"), 0xff) == GITSLC_COL_VERSION
;
143 // read (possibly different) column selection
145 selectedStandardColumns
= CRegDWORD (registryPrefix
, selectedStandardColumns
) & ~hideColumns
;
147 // read column widths
149 CString colWidths
= CRegString (registryPrefix
+ _T("_Width"));
151 ParseWidths (colWidths
);
154 // process old-style visibility setting
156 SetStandardColumnVisibility (selectedStandardColumns
);
158 // clear all previously set header columns
160 int c
= control
->GetHeaderCtrl()->GetItemCount() - 1;
162 control
->DeleteColumn(c
--);
166 for (int i
= 0, count
= GetColumnCount(); i
< count
; ++i
)
167 control
->InsertColumn (i
, GetName(i
), LVCFMT_LEFT
, IsVisible(i
)&&IsRelevant(i
) ? -1 : GetVisibleWidth(i
, false));
169 // restore column ordering
172 ParseColumnOrder (CRegString (registryPrefix
+ _T("_Order")));
174 ParseColumnOrder (CString());
178 // auto-size the columns so we can see them while fetching status
179 // (seems the same values will not take affect in InsertColumn)
181 for (int i
= 0, count
= GetColumnCount(); i
< count
; ++i
)
183 control
->SetColumnWidth (i
, GetVisibleWidth (i
, true));
186 void ColumnManager::WriteSettings() const
188 CRegDWORD
regVersion (registryPrefix
+ _T("Version"), 0, TRUE
);
189 regVersion
= GITSLC_COL_VERSION
;
191 // write (possibly different) column selection
193 CRegDWORD
regStandardColumns (registryPrefix
, 0, TRUE
);
194 regStandardColumns
= GetSelectedStandardColumns();
196 // write column widths
198 CRegString
regWidths (registryPrefix
+ _T("_Width"), CString(), TRUE
);
199 regWidths
= GetWidthString();
201 // write column ordering
203 CRegString
regColumnOrder (registryPrefix
+ _T("_Order"), CString(), TRUE
);
204 regColumnOrder
= GetColumnOrderString();
207 // read column definitions
209 int ColumnManager::GetColumnCount() const
211 return static_cast<int>(columns
.size());
214 bool ColumnManager::IsVisible (int column
) const
216 size_t index
= static_cast<size_t>(column
);
217 assert (columns
.size() > index
);
219 return columns
[index
].visible
;
222 int ColumnManager::GetInvisibleCount() const
224 int invisibleCount
= 0;
225 for (const auto& column
: columns
)
230 return invisibleCount
;
233 bool ColumnManager::IsRelevant (int column
) const
235 size_t index
= static_cast<size_t>(column
);
236 assert (columns
.size() > index
);
238 return columns
[index
].relevant
;
241 int ColumnManager::SetNames(UINT
* buffer
, int size
)
244 for (int i
= 0; i
< size
; ++i
)
245 itemName
.push_back(*buffer
++);
249 void ColumnManager::SetRightAlign(int column
) const
251 assert(column
<= columns
.size());
253 if (!IsVisible(column
))
256 LVCOLUMN col
= { 0 };
258 col
.fmt
= LVCFMT_RIGHT
;
259 control
->SetColumn(column
, &col
);
261 control
->Invalidate(FALSE
);
264 CString
ColumnManager::GetName (int column
) const
267 size_t index
= static_cast<size_t>(column
);
268 if (index
< itemName
.size())
271 result
.LoadString (itemName
[index
]);
277 // if (index < columns.size())
278 // return userProps[columns[index].index - SVNSLC_USERPROPCOLOFFSET].name;
285 int ColumnManager::GetWidth (int column
, bool useDefaults
) const
287 size_t index
= static_cast<size_t>(column
);
288 assert (columns
.size() > index
);
290 int width
= columns
[index
].width
;
291 if ((width
== 0) && useDefaults
)
295 int cx
= control
->GetStringWidth(GetName(column
)) + 20; // 20 pixels for col separator and margin
297 for (int i
= 0, itemCnt
= control
->GetItemCount(); i
< itemCnt
; ++i
)
299 // get the width of the string and add 14 pixels for the column separator and margins
300 int linewidth
= control
->GetStringWidth(control
->GetItemText(i
, column
)) + 14;
306 return LVSCW_AUTOSIZE_USEHEADER
;
311 int ColumnManager::GetVisibleWidth (int column
, bool useDefaults
) const
313 return IsVisible (column
)
314 ? GetWidth (column
, useDefaults
)
318 // switch columns on and off
320 void ColumnManager::SetVisible
324 size_t index
= static_cast<size_t>(column
);
325 assert (index
< columns
.size());
327 if (columns
[index
].visible
!= visible
)
329 columns
[index
].visible
= visible
;
330 columns
[index
].relevant
|= visible
;
332 columns
[index
].width
= 0;
334 control
->SetColumnWidth (column
, GetVisibleWidth (column
, true));
337 control
->Invalidate (FALSE
);
341 // tracking column modifications
343 void ColumnManager::ColumnMoved (int column
, int position
)
345 // in front of what column has it been inserted?
347 int index
= columns
[column
].index
;
349 std::vector
<int> gridColumnOrder
= GetGridColumnOrder();
351 size_t visiblePosition
= static_cast<size_t>(position
);
352 size_t columnCount
= gridColumnOrder
.size();
355 if (visiblePosition
< columnCount
- 1)
357 // the new position (visiblePosition) is the column id w/o the moved column
358 gridColumnOrder
.erase(std::find(gridColumnOrder
.cbegin(), gridColumnOrder
.cend(), index
));
359 next
= gridColumnOrder
[visiblePosition
];
362 // move logical column index just in front of that "next" column
364 columnOrder
.erase(std::find(columnOrder
.cbegin(), columnOrder
.cend(), index
));
365 columnOrder
.insert(std::find(columnOrder
.cbegin(), columnOrder
.cend(), next
), index
);
367 // make sure, invisible columns are still put in front of all others
372 void ColumnManager::ColumnResized(int column
, int manual
)
374 size_t index
= static_cast<size_t>(column
);
375 assert (index
< columns
.size());
376 assert (columns
[index
].visible
);
378 int width
= control
->GetColumnWidth (column
);
380 columns
[index
].adjusted
= (manual
< 3);
383 control
->SetColumnWidth(column
, LVSCW_AUTOSIZE
);
384 columns
[index
].width
= control
->GetColumnWidth(column
);
386 else if (manual
== 3)
388 columns
[index
].width
= 0;
389 control
->SetColumnWidth(column
, GetWidth(column
, true));
392 columns
[index
].width
= width
;
394 control
->Invalidate (FALSE
);
397 void ColumnManager::RemoveUnusedProps()
399 // determine what column indexes / IDs to keep.
400 // map them onto new IDs (we may delete some IDs in between)
402 std::map
<int, int> validIndices
;
404 for (size_t i
= 0, count
= columns
.size(); i
< count
; ++i
)
406 int index
= columns
[i
].index
;
408 if (itemProps
.find (GetName((int)i
)) != itemProps
.end()
409 || columns
[i
].visible
)
411 validIndices
[index
] = index
;
415 // remove everything else:
417 // remove from columns and control.
418 // also update index values in columns
420 for (size_t i
= columns
.size(); i
> 0; --i
)
422 std::map
<int, int>::const_iterator iter
423 = validIndices
.find (columns
[i
-1].index
);
425 if (iter
== validIndices
.end())
427 control
->DeleteColumn (static_cast<int>(i
-1));
428 columns
.erase(columns
.cbegin() + i
- 1);
432 columns
[i
-1].index
= iter
->second
;
436 // remove from and update column order
438 for (size_t i
= columnOrder
.size(); i
> 0; --i
)
440 std::map
<int, int>::const_iterator iter
441 = validIndices
.find (columnOrder
[i
-1]);
443 if (iter
== validIndices
.end())
444 columnOrder
.erase(columnOrder
.cbegin() + i
- 1);
446 columnOrder
[i
-1] = iter
->second
;
450 // bring everything back to its "natural" order
452 void ColumnManager::ResetColumns (DWORD defaultColumns
)
454 // update internal data
456 std::sort(columnOrder
.begin(), columnOrder
.end());
458 for (size_t i
= 0, count
= columns
.size(); i
< count
; ++i
)
460 columns
[i
].width
= 0;
461 columns
[i
].visible
= (i
< 32) && (((defaultColumns
>> i
) & 1) != 0);
462 columns
[i
].adjusted
= false;
467 for (int i
= 0, count
= GetColumnCount(); i
< count
; ++i
)
468 control
->SetColumnWidth (i
, GetVisibleWidth (i
, true));
472 control
->Invalidate (FALSE
);
475 // initialization utilities
477 void ColumnManager::ParseWidths (const CString
& widths
)
479 for (int i
= 0, count
= widths
.GetLength() / 8; i
< count
; ++i
)
481 long width
= _tcstol (widths
.Mid (i
* 8, 8), nullptr, 16);
482 if (i
< (int)itemName
.size())
485 if (width
!= MAXLONG
)
487 columns
[i
].width
= width
;
488 columns
[i
].adjusted
= true;
493 // there is no such column
500 void ColumnManager::SetStandardColumnVisibility
503 for (size_t i
= 0; i
< itemName
.size(); ++i
)
505 columns
[i
].visible
= (visibility
& 1) > 0;
510 void ColumnManager::ParseColumnOrder
511 (const CString
& widths
)
513 std::set
<int> alreadyPlaced
;
516 // place columns according to valid entries in orderString
518 for (int i
= 0, count
= widths
.GetLength() / 2; i
< count
; ++i
)
520 int index
= _tcstol (widths
.Mid (i
* 2, 2), nullptr, 16);
521 if ((index
< (int)itemName
.size()))
523 alreadyPlaced
.insert (index
);
524 columnOrder
.push_back (index
);
528 // place the remaining colums behind it
530 for (int i
= 0; i
< (int)itemName
.size(); ++i
)
531 if (alreadyPlaced
.find (i
) == alreadyPlaced
.end())
532 columnOrder
.push_back (i
);
535 // map internal column order onto visible column order
536 // (all invisibles in front)
538 std::vector
<int> ColumnManager::GetGridColumnOrder() const
540 // extract order of used columns from order of all columns
542 std::vector
<int> result
;
543 result
.reserve (GITSLC_MAXCOLUMNCOUNT
+1);
545 size_t colCount
= columns
.size();
546 bool visible
= false;
550 // put invisible cols in front
552 for (size_t i
= 0, count
= columnOrder
.size(); i
< count
; ++i
)
554 int index
= columnOrder
[i
];
555 for (size_t k
= 0; k
< colCount
; ++k
)
557 const ColumnInfo
& column
= columns
[k
];
558 if ((column
.index
== index
) && (column
.visible
== visible
))
559 result
.push_back (static_cast<int>(k
));
570 void ColumnManager::ApplyColumnOrder()
572 // extract order of used columns from order of all columns
574 int order
[GITSLC_MAXCOLUMNCOUNT
+ 1] = { 0 };
576 std::vector
<int> gridColumnOrder
= GetGridColumnOrder();
577 std::copy(gridColumnOrder
.cbegin(), gridColumnOrder
.cend(), stdext::checked_array_iterator
<int*>(&order
[0], sizeof(order
)));
579 // we must have placed all columns or something is really fishy ..
581 assert (gridColumnOrder
.size() == columns
.size());
582 assert(GetColumnCount() == control
->GetHeaderCtrl()->GetItemCount());
584 // o.k., apply our column ordering
586 control
->SetColumnOrderArray (GetColumnCount(), order
);
589 // utilities used when writing data to the registry
591 DWORD
ColumnManager::GetSelectedStandardColumns() const
594 for (size_t i
= itemName
.size(); i
> 0; --i
)
595 result
= result
* 2 + (columns
[i
-1].visible
? 1 : 0);
600 CString
ColumnManager::GetWidthString() const
606 TCHAR buf
[10] = { 0 };
607 for (size_t i
= 0; i
< itemName
.size(); ++i
)
609 _stprintf_s (buf
, 10, L
"%08X", columns
[i
].adjusted
? columns
[i
].width
: MAXLONG
);
616 CString
ColumnManager::GetColumnOrderString() const
620 TCHAR buf
[3] = { 0 };
621 for (size_t i
= 0, count
= columnOrder
.size(); i
< count
; ++i
)
623 _stprintf_s (buf
, 3, _T("%02X"), columnOrder
[i
]);
630 // sorter utility class, only used by GitStatusList!
632 CSorter::CSorter ( ColumnManager
* columnManager
635 : columnManager (columnManager
)
636 , sortedColumn (sortedColumn
)
637 , ascending (ascending
)
641 bool CSorter::operator() (const CTGitPath
* entry1
, const CTGitPath
* entry2
) const
643 #define SGN(x) ((x)==0?0:((x)>0?1:-1))
646 switch (sortedColumn
)
652 __int64 fileSize1
= entry1
->IsDirectory() ? 0 : entry1
->GetFileSize();
653 __int64 fileSize2
= entry2
->IsDirectory() ? 0 : entry2
->GetFileSize();
655 result
= int(fileSize1
- fileSize2
);
659 case 6: //Last Modification Date
663 __int64 writetime1
= entry1
->GetLastWriteTime();
664 __int64 writetime2
= entry2
->GetLastWriteTime();
666 FILETIME
* filetime1
= (FILETIME
*)(__int64
*)&writetime1
;
667 FILETIME
* filetime2
= (FILETIME
*)(__int64
*)&writetime2
;
669 result
= CompareFileTime(filetime1
, filetime2
);
677 // result = entry1->lock_comment.CompareNoCase(entry2->lock_comment);
678 result
= A2L(entry1
->m_StatDel
)-A2L(entry2
->m_StatDel
);
686 //result = entry1->lock_owner.CompareNoCase(entry2->lock_owner);
687 result
= A2L(entry1
->m_StatAdd
)-A2L(entry2
->m_StatAdd
);
696 result
= entry1
->GetActionName(entry1
->m_Action
).CompareNoCase(entry2
->GetActionName(entry2
->m_Action
));
704 result
= entry1
->GetFileExtension().CompareNoCase(entry2
->GetFileExtension());
712 result
= entry1
->GetFileOrDirectoryName().CompareNoCase(entry2
->GetFileOrDirectoryName());
716 case 0: // Full path column
720 result
= CTGitPath::Compare(entry1
->GetGitPathString(), entry2
->GetGitPathString());
724 } // switch (m_nSortedColumn)
725 // sort by path name as second priority
726 if (sortedColumn
> 0 && result
== 0)
727 result
= CTGitPath::Compare(entry1
->GetGitPathString(), entry2
->GetGitPathString());