Make sounds for indicating a warning or error work
[TortoiseGit.git] / src / TortoiseProc / GitStatusListCtrlHelpers.cpp
blob1e44d7cbd3a748414cd4836d1ad4cb2f4d71f7a0
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008 - TortoiseSVN
4 // Copyright (C) 2008-2013 - 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.
21 #include "stdafx.h"
22 #include ".\resource.h"
23 #include "GitStatusListCtrl.h"
24 #include <iterator>
26 // registry version number of column-settings of GitLogListBase
27 #define GITSLC_COL_VERSION 5
29 #ifndef assert
30 #define assert(x) ATLASSERT(x)
31 #endif
32 // assign property list
33 #if 0
34 PropertyList&
35 PropertyList::operator= (const char* rhs)
37 // do you really want to replace the property list?
39 assert (properties.empty());
40 properties.clear();
42 // add all properties in the list
44 while ((rhs != NULL) && (*rhs != 0))
46 const char* next = strchr (rhs, ' ');
48 CString name (rhs, static_cast<int>(next == NULL ? strlen (rhs) : next - rhs));
49 properties.insert (std::make_pair (name, CString()));
51 rhs = next == NULL ? NULL : next+1;
54 // done
56 return *this;
59 // collect property names in a set
61 void PropertyList::GetPropertyNames (std::set<CString>& names)
63 for ( CIT iter = properties.begin(), end = properties.end()
64 ; iter != end
65 ; ++iter)
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()
78 ? CString()
79 : iter->second;
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);
104 #endif
105 // registry access
107 void ColumnManager::ReadSettings
108 ( DWORD defaultColumns
109 , DWORD hideColumns
110 , const CString& containerName
111 , int maxsize
112 , int * widthlist)
114 // defaults
115 DWORD selectedStandardColumns = defaultColumns & ~hideColumns;
116 m_dwDefaultColumns = defaultColumns & ~hideColumns;
118 columns.resize (maxsize);
119 int power = 1;
120 for (size_t i = 0; i < maxsize; ++i)
122 columns[i].index = static_cast<int>(i);
123 if(widthlist==NULL)
124 columns[i].width = 0;
125 else
126 columns[i].width = widthlist[i];
127 columns[i].visible = true;
128 columns[i].relevant = !(hideColumns & power);
129 power *= 2;
132 // userProps.clear();
134 // where the settings are stored within the registry
136 registryPrefix = _T("Software\\TortoiseGit\\StatusColumns\\") + containerName;
138 // we accept settings of current version only
139 bool valid = (DWORD)CRegDWORD (registryPrefix + _T("Version"), 0xff) == GITSLC_COL_VERSION;
140 if (valid)
142 // read (possibly different) column selection
144 selectedStandardColumns = CRegDWORD (registryPrefix, selectedStandardColumns) & ~hideColumns;
146 // read column widths
148 CString colWidths = CRegString (registryPrefix + _T("_Width"));
150 ParseWidths (colWidths);
153 // process old-style visibility setting
155 SetStandardColumnVisibility (selectedStandardColumns);
157 // clear all previously set header columns
159 int c = ((CHeaderCtrl*)(control->GetDlgItem(0)))->GetItemCount()-1;
160 while (c>=0)
161 control->DeleteColumn(c--);
163 // create columns
165 for (int i = 0, count = GetColumnCount(); i < count; ++i)
166 control->InsertColumn (i, GetName(i), LVCFMT_LEFT, IsVisible(i)&&IsRelevant(i) ? -1 : GetVisibleWidth(i, false));
168 // restore column ordering
170 if (valid)
171 ParseColumnOrder (CRegString (registryPrefix + _T("_Order")));
172 else
173 ParseColumnOrder (CString());
175 ApplyColumnOrder();
177 // auto-size the columns so we can see them while fetching status
178 // (seems the same values will not take affect in InsertColumn)
180 for (int i = 0, count = GetColumnCount(); i < count; ++i)
181 if (IsVisible(i))
182 control->SetColumnWidth (i, GetVisibleWidth (i, true));
185 void ColumnManager::WriteSettings() const
187 CRegDWORD regVersion (registryPrefix + _T("Version"), 0, TRUE);
188 regVersion = GITSLC_COL_VERSION;
190 // write (possibly different) column selection
192 CRegDWORD regStandardColumns (registryPrefix, 0, TRUE);
193 regStandardColumns = GetSelectedStandardColumns();
195 // write column widths
197 CRegString regWidths (registryPrefix + _T("_Width"), CString(), TRUE);
198 regWidths = GetWidthString();
200 // write column ordering
202 CRegString regColumnOrder (registryPrefix + _T("_Order"), CString(), TRUE);
203 regColumnOrder = GetColumnOrderString();
206 // read column definitions
208 int ColumnManager::GetColumnCount() const
210 return static_cast<int>(columns.size());
213 bool ColumnManager::IsVisible (int column) const
215 size_t index = static_cast<size_t>(column);
216 assert (columns.size() > index);
218 return columns[index].visible;
221 int ColumnManager::GetInvisibleCount() const
223 int invisibleCount = 0;
224 for (std::vector<ColumnInfo>::const_iterator it = columns.begin(); it != columns.end(); ++it)
226 if (!it->visible)
227 invisibleCount++;
229 return invisibleCount;
232 bool ColumnManager::IsRelevant (int column) const
234 size_t index = static_cast<size_t>(column);
235 assert (columns.size() > index);
237 return columns[index].relevant;
240 int ColumnManager::SetNames(UINT* buffer, int size)
242 itemName.clear();
243 for (int i = 0; i < size; ++i)
244 itemName.push_back(*buffer++);
245 return 0;
248 CString ColumnManager::GetName (int column) const
250 // standard columns
251 size_t index = static_cast<size_t>(column);
252 if (index < itemName.size())
254 CString result;
255 result.LoadString (itemName[index]);
256 return result;
259 // user-prop columns
261 // if (index < columns.size())
262 // return userProps[columns[index].index - SVNSLC_USERPROPCOLOFFSET].name;
264 // default: empty
266 return CString();
269 int ColumnManager::GetWidth (int column, bool useDefaults) const
271 size_t index = static_cast<size_t>(column);
272 assert (columns.size() > index);
274 int width = columns[index].width;
275 if ((width == 0) && useDefaults)
276 width = LVSCW_AUTOSIZE;
278 return width;
281 int ColumnManager::GetVisibleWidth (int column, bool useDefaults) const
283 return IsVisible (column)
284 ? GetWidth (column, useDefaults)
285 : 0;
288 // switch columns on and off
290 void ColumnManager::SetVisible
291 ( int column
292 , bool visible)
294 size_t index = static_cast<size_t>(column);
295 assert (index < columns.size());
297 if (columns[index].visible != visible)
299 columns[index].visible = visible;
300 columns[index].relevant |= visible;
301 if (!visible)
302 columns[index].width = 0;
304 control->SetColumnWidth (column, GetVisibleWidth (column, true));
305 ApplyColumnOrder();
307 control->Invalidate (FALSE);
311 // tracking column modifications
313 void ColumnManager::ColumnMoved (int column, int position)
315 // in front of what column has it been inserted?
317 int index = columns[column].index;
319 std::vector<int> gridColumnOrder = GetGridColumnOrder();
321 size_t visiblePosition = static_cast<size_t>(position);
322 size_t columnCount = gridColumnOrder.size();
324 int next = -1;
325 if (visiblePosition < columnCount - 1)
327 // the new position (visiblePosition) is the column id w/o the moved column
328 gridColumnOrder.erase(std::find(gridColumnOrder.begin(), gridColumnOrder.end(), index));
329 next = gridColumnOrder[visiblePosition];
332 // move logical column index just in front of that "next" column
334 columnOrder.erase (std::find ( columnOrder.begin(), columnOrder.end(), index));
335 columnOrder.insert ( std::find ( columnOrder.begin(), columnOrder.end(), next), index);
337 // make sure, invisible columns are still put in front of all others
339 ApplyColumnOrder();
342 void ColumnManager::ColumnResized (int column)
344 size_t index = static_cast<size_t>(column);
345 assert (index < columns.size());
346 assert (columns[index].visible);
348 int width = control->GetColumnWidth (column);
349 columns[index].width = width;
351 control->Invalidate (FALSE);
354 void ColumnManager::RemoveUnusedProps()
356 // determine what column indexes / IDs to keep.
357 // map them onto new IDs (we may delete some IDs in between)
359 std::map<int, int> validIndices;
361 for (size_t i = 0, count = columns.size(); i < count; ++i)
363 int index = columns[i].index;
365 if (itemProps.find (GetName((int)i)) != itemProps.end()
366 || columns[i].visible)
368 validIndices[index] = index;
372 // remove everything else:
374 // remove from columns and control.
375 // also update index values in columns
377 for (size_t i = columns.size(); i > 0; --i)
379 std::map<int, int>::const_iterator iter
380 = validIndices.find (columns[i-1].index);
382 if (iter == validIndices.end())
384 control->DeleteColumn (static_cast<int>(i-1));
385 columns.erase (columns.begin() + i-1);
387 else
389 columns[i-1].index = iter->second;
393 // remove from and update column order
395 for (size_t i = columnOrder.size(); i > 0; --i)
397 std::map<int, int>::const_iterator iter
398 = validIndices.find (columnOrder[i-1]);
400 if (iter == validIndices.end())
401 columnOrder.erase (columnOrder.begin() + i-1);
402 else
403 columnOrder[i-1] = iter->second;
407 // bring everything back to its "natural" order
409 void ColumnManager::ResetColumns (DWORD defaultColumns)
411 // update internal data
413 std::sort (columnOrder.begin(), columnOrder.end());
415 for (size_t i = 0, count = columns.size(); i < count; ++i)
417 columns[i].width = 0;
418 columns[i].visible = (i < 32) && (((defaultColumns >> i) & 1) != 0);
421 // update UI
423 for (int i = 0, count = GetColumnCount(); i < count; ++i)
424 control->SetColumnWidth (i, GetVisibleWidth (i, true));
426 ApplyColumnOrder();
428 control->Invalidate (FALSE);
431 // initialization utilities
433 void ColumnManager::ParseWidths (const CString& widths)
435 for (int i = 0, count = widths.GetLength() / 8; i < count; ++i)
437 long width = _tcstol (widths.Mid (i*8, 8), NULL, 16);
438 if (i < itemName.size())
440 // a standard column
442 columns[i].width = width;
444 else
446 // there is no such column
448 assert (width == 0);
453 void ColumnManager::SetStandardColumnVisibility
454 (DWORD visibility)
456 for (size_t i = 0; i < itemName.size(); ++i)
458 columns[i].visible = (visibility & 1) > 0;
459 visibility /= 2;
463 void ColumnManager::ParseColumnOrder
464 (const CString& widths)
466 std::set<int> alreadyPlaced;
467 columnOrder.clear();
469 // place columns according to valid entries in orderString
471 for (int i = 0, count = widths.GetLength() / 2; i < count; ++i)
473 int index = _tcstol (widths.Mid (i*2, 2), NULL, 16);
474 if ((index < itemName.size()))
476 alreadyPlaced.insert (index);
477 columnOrder.push_back (index);
481 // place the remaining colums behind it
483 for (int i = 0; i < itemName.size(); ++i)
484 if (alreadyPlaced.find (i) == alreadyPlaced.end())
485 columnOrder.push_back (i);
488 // map internal column order onto visible column order
489 // (all invisibles in front)
491 std::vector<int> ColumnManager::GetGridColumnOrder()
493 // extract order of used columns from order of all columns
495 std::vector<int> result;
496 result.reserve (GITSLC_MAXCOLUMNCOUNT+1);
498 size_t colCount = columns.size();
499 bool visible = false;
503 // put invisible cols in front
505 for (size_t i = 0, count = columnOrder.size(); i < count; ++i)
507 int index = columnOrder[i];
508 for (size_t k = 0; k < colCount; ++k)
510 const ColumnInfo& column = columns[k];
511 if ((column.index == index) && (column.visible == visible))
512 result.push_back (static_cast<int>(k));
516 visible = !visible;
518 while (visible);
520 return result;
523 void ColumnManager::ApplyColumnOrder()
525 // extract order of used columns from order of all columns
527 int order[GITSLC_MAXCOLUMNCOUNT+1];
528 SecureZeroMemory (order, sizeof (order));
530 std::vector<int> gridColumnOrder = GetGridColumnOrder();
531 std::copy (gridColumnOrder.begin(), gridColumnOrder.end(), stdext::checked_array_iterator<int*>(&order[0], sizeof(order)));
533 // we must have placed all columns or something is really fishy ..
535 assert (gridColumnOrder.size() == columns.size());
536 assert (GetColumnCount() == ((CHeaderCtrl*)(control->GetDlgItem(0)))->GetItemCount());
538 // o.k., apply our column ordering
540 control->SetColumnOrderArray (GetColumnCount(), order);
543 // utilities used when writing data to the registry
545 DWORD ColumnManager::GetSelectedStandardColumns() const
547 DWORD result = 0;
548 for (size_t i = itemName.size(); i > 0; --i)
549 result = result * 2 + (columns[i-1].visible ? 1 : 0);
551 return result;
554 CString ColumnManager::GetWidthString() const
556 CString result;
558 // regular columns
560 TCHAR buf[10];
561 for (size_t i = 0; i < itemName.size(); ++i)
563 _stprintf_s (buf, 10, _T("%08X"), columns[i].width);
564 result += buf;
567 return result;
570 CString ColumnManager::GetColumnOrderString() const
572 CString result;
574 TCHAR buf[3];
575 for (size_t i = 0, count = columnOrder.size(); i < count; ++i)
577 _stprintf_s (buf, 3, _T("%02X"), columnOrder[i]);
578 result += buf;
581 return result;
584 // sorter utility class, only used by GitStatusList!
586 CSorter::CSorter ( ColumnManager* columnManager
587 , int sortedColumn
588 , bool ascending)
589 : columnManager (columnManager)
590 , sortedColumn (sortedColumn)
591 , ascending (ascending)
595 bool CSorter::operator() (const CTGitPath* entry1 , const CTGitPath* entry2) const
597 #define SGN(x) ((x)==0?0:((x)>0?1:-1))
599 int result = 0;
600 switch (sortedColumn)
602 case 7: // File size
604 if (result == 0)
606 __int64 fileSize1 = entry1->IsDirectory() ? 0 : entry1->GetFileSize();
607 __int64 fileSize2 = entry2->IsDirectory() ? 0 : entry2->GetFileSize();
609 result = int(fileSize1 - fileSize2);
611 break;
613 case 6: //Last Modification Date
615 if (result == 0)
617 __int64 writetime1 = entry1->GetLastWriteTime();
618 __int64 writetime2 = entry2->GetLastWriteTime();
620 FILETIME* filetime1 = (FILETIME*)(__int64*)&writetime1;
621 FILETIME* filetime2 = (FILETIME*)(__int64*)&writetime2;
623 result = CompareFileTime(filetime1, filetime2);
625 break;
627 case 5: //Del Number
629 if (result == 0)
631 // result = entry1->lock_comment.CompareNoCase(entry2->lock_comment);
632 result = A2L(entry1->m_StatDel)-A2L(entry2->m_StatDel);
634 break;
636 case 4: //Add Number
638 if (result == 0)
640 //result = entry1->lock_owner.CompareNoCase(entry2->lock_owner);
641 result = A2L(entry1->m_StatAdd)-A2L(entry2->m_StatAdd);
643 break;
646 case 3: // Status
648 if (result == 0)
650 result = entry1->GetActionName(entry1->m_Action).CompareNoCase(entry2->GetActionName(entry2->m_Action));
652 break;
654 case 2: //Ext file
656 if (result == 0)
658 result = entry1->GetFileExtension().CompareNoCase(entry2->GetFileExtension());
660 break;
662 case 1: // File name
664 if (result == 0)
666 result = entry1->GetFileOrDirectoryName().CompareNoCase(entry2->GetFileOrDirectoryName());
668 break;
670 case 0: // Full path column
672 if (result == 0)
674 result = CTGitPath::Compare(entry1->GetGitPathString(), entry2->GetGitPathString());
676 break;
678 } // switch (m_nSortedColumn)
679 // sort by path name as second priority
680 if (sortedColumn > 0 && result == 0)
681 result = CTGitPath::Compare(entry1->GetGitPathString(), entry2->GetGitPathString());
682 if (!ascending)
683 result = -result;
685 return result < 0;