1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - TortoiseGit
4 // Copyright (C) 2003-2008, 2014 - TortoiseSVN
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.
22 #include "GitStatus.h"
25 #include "LoglistCommonResource.h"
26 #include "HintListCtrl.h"
28 #define GIT_WC_ENTRY_WORKING_SIZE_UNKNOWN (-1)
30 // these defines must be in the order the columns are inserted!
31 #define GITSLC_COLFILENAME 0x000000002
32 #define GITSLC_COLEXT 0x000000004
33 #define GITSLC_COLSTATUS 0x000000008
34 //#define SVNSLC_COLAUTHOR 0x000000040
35 //#define SVNSLC_COLREVISION 0x000000080
36 //#define SVNSLC_COLDATE 0x000000100
37 #define GITSLC_COLADD 0x000000010
38 #define GITSLC_COLDEL 0x000000020
39 #define GITSLC_COLMODIFICATIONDATE 0x000000040
40 #define GITSLC_COLSIZE 0x000000080
41 #define GITSLC_NUMCOLUMNS 8
43 //#define SVNSLC_COLURL 0x000000200
44 //#define SVNSLC_COLCOPYFROM 0x000020000
46 #define GITSLC_SHOWUNVERSIONED CTGitPath::LOGACTIONS_UNVER
47 #define GITSLC_SHOWNORMAL 0x00000000
48 #define GITSLC_SHOWMODIFIED (CTGitPath::LOGACTIONS_MODIFIED)
49 #define GITSLC_SHOWADDED (CTGitPath::LOGACTIONS_ADDED|CTGitPath::LOGACTIONS_COPY)
50 #define GITSLC_SHOWREMOVED CTGitPath::LOGACTIONS_DELETED
51 #define GITSLC_SHOWCONFLICTED CTGitPath::LOGACTIONS_UNMERGED
52 #define GITSLC_SHOWMISSING 0x00000000
53 #define GITSLC_SHOWREPLACED CTGitPath::LOGACTIONS_REPLACED
54 #define GITSLC_SHOWMERGED CTGitPath::LOGACTIONS_MERGED
55 #define GITSLC_SHOWIGNORED CTGitPath::LOGACTIONS_IGNORE
56 #define GITSLC_SHOWOBSTRUCTED 0x00000000
57 #define GITSLC_SHOWEXTERNAL 0x00000000
58 #define GITSLC_SHOWINCOMPLETE 0x00000000
59 #define GITSLC_SHOWINEXTERNALS 0x00000000
60 #define GITSLC_SHOWREMOVEDANDPRESENT 0x00000000
61 #define GITSLC_SHOWLOCKS 0x00000000
62 #define GITSLC_SHOWDIRECTFILES 0x04000000
63 #define GITSLC_SHOWDIRECTFOLDER 0x00000000
64 #define GITSLC_SHOWEXTERNALFROMDIFFERENTREPO 0x00000000
65 #define GITSLC_SHOWSWITCHED 0x00000000
66 #define GITSLC_SHOWINCHANGELIST 0x00000000
67 #define GITSLC_SHOWASSUMEVALID CTGitPath::LOGACTIONS_ASSUMEVALID
68 #define GITSLC_SHOWSKIPWORKTREE CTGitPath::LOGACTIONS_SKIPWORKTREE
70 #define GITSLC_SHOWDIRECTS (GITSLC_SHOWDIRECTFILES | GITSLC_SHOWDIRECTFOLDER)
72 #define GITSLC_SHOWFILES 0x01000000
73 #define GITSLC_SHOWSUBMODULES 0x02000000
74 #define GITSLC_SHOWEVERYTHING 0xffffffff
76 #define GITSLC_SHOWVERSIONED (GITSLC_SHOWNORMAL|GITSLC_SHOWMODIFIED|\
77 GITSLC_SHOWADDED|GITSLC_SHOWREMOVED|GITSLC_SHOWCONFLICTED|GITSLC_SHOWMISSING|\
78 GITSLC_SHOWREPLACED|GITSLC_SHOWMERGED|GITSLC_SHOWIGNORED|GITSLC_SHOWOBSTRUCTED|\
79 GITSLC_SHOWEXTERNAL|GITSLC_SHOWINCOMPLETE|GITSLC_SHOWINEXTERNALS|\
80 GITSLC_SHOWEXTERNALFROMDIFFERENTREPO)
82 #define GITSLC_SHOWVERSIONEDBUTNORMAL (GITSLC_SHOWMODIFIED|GITSLC_SHOWADDED|\
83 GITSLC_SHOWREMOVED|GITSLC_SHOWCONFLICTED|GITSLC_SHOWMISSING|\
84 GITSLC_SHOWREPLACED|GITSLC_SHOWMERGED|GITSLC_SHOWIGNORED|GITSLC_SHOWOBSTRUCTED|\
85 GITSLC_SHOWEXTERNAL|GITSLC_SHOWINCOMPLETE|GITSLC_SHOWINEXTERNALS|\
86 GITSLC_SHOWEXTERNALFROMDIFFERENTREPO)
88 #define GITSLC_SHOWVERSIONEDBUTNORMALANDEXTERNALSFROMDIFFERENTREPOS (GITSLC_SHOWMODIFIED|\
89 GITSLC_SHOWADDED|GITSLC_SHOWREMOVED|GITSLC_SHOWCONFLICTED|GITSLC_SHOWMISSING|\
90 GITSLC_SHOWREPLACED|GITSLC_SHOWMERGED|GITSLC_SHOWIGNORED|GITSLC_SHOWOBSTRUCTED|\
91 GITSLC_SHOWINCOMPLETE|GITSLC_SHOWEXTERNAL|GITSLC_SHOWINEXTERNALS)
93 #define GITSLC_SHOWVERSIONEDBUTNORMALANDEXTERNALS (GITSLC_SHOWMODIFIED|\
94 GITSLC_SHOWADDED|GITSLC_SHOWREMOVED|GITSLC_SHOWCONFLICTED|GITSLC_SHOWMISSING|\
95 GITSLC_SHOWREPLACED|GITSLC_SHOWMERGED|GITSLC_SHOWIGNORED|GITSLC_SHOWOBSTRUCTED|\
96 GITSLC_SHOWINCOMPLETE)
98 #define GITSLC_SHOWALL (GITSLC_SHOWVERSIONED|GITSLC_SHOWUNVERSIONED)
100 #define GITSLC_POPALL 0xFFFFFFFFFFFFFFFF
101 #define GITSLC_POPCOMPAREWITHBASE CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPARE)
102 #define GITSLC_POPCOMPARE CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPAREWC)
103 #define GITSLC_POPGNUDIFF CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_GNUDIFF1)
104 #define GITSLC_POPREVERT CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_REVERT)
105 #define GITSLC_POPSHOWLOG CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_LOG)
106 #define GITSLC_POPSHOWLOGSUBMODULE CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_LOGSUBMODULE)
107 #define GITSLC_POPSHOWLOGOLDNAME CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_LOGOLDNAME)
108 #define GITSLC_POPOPEN CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_OPEN)
109 #define GITSLC_POPDELETE CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_DELETE)
110 #define GITSLC_POPADD CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_ADD)
111 #define GITSLC_POPIGNORE CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_IGNORE)
112 #define GITSLC_POPCONFLICT CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_EDITCONFLICT)
113 #define GITSLC_POPRESOLVE CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_RESOLVECONFLICT)
114 #define GITSLC_POPEXPLORE CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_EXPLORE)
115 #define GITSLC_POPCOMMIT CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMMIT)
116 #define GITSLC_POPCHANGELISTS CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_CHECKGROUP)
117 #define GITSLC_POPBLAME CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_BLAME)
118 #define GITSLC_POPSAVEAS CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_SAVEAS)
119 #define GITSLC_POPCOMPARETWOFILES CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_COMPARETWOFILES)
120 #define GITSLC_POPRESTORE CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_POPRESTORE)
121 #define GITSLC_POPASSUMEVALID CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_ASSUMEVALID)
122 #define GITSLC_POPSKIPWORKTREE CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_SKIPWORKTREE)
123 #define GITSLC_POPEXPORT CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_EXPORT)
124 #define GITLC_POPUNSETIGNORELOCALCHANGES CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_UNSETIGNORELOCALCHANGES)
125 #define GITSLC_PREPAREDIFF CGitStatusListCtrl::GetContextMenuBit(CGitStatusListCtrl::IDGITLC_PREPAREDIFF)
127 #define GITSLC_IGNORECHANGELIST _T("ignore-on-commit")
129 // This gives up to 64 standard properties and menu entries
130 #define GITSLC_MAXCOLUMNCOUNT 0xff
132 #define OVL_RESTORE 1
134 typedef int (__cdecl
*GENERICCOMPAREFN
)(const void * elem1
, const void * elem2
);
135 typedef CComCritSecLock
<CComCriticalSection
> Locker
;
137 class CGitStatusListCtrlDropTarget
;
140 * \ingroup TortoiseProc
141 * Helper class for CGitStatusListCtrl that represents
142 * the columns visible and their order as well as
143 * persisting that data in the registry.
145 * It assigns logical index values to the (potential) columns:
146 * 0 .. GitSLC_NUMCOLUMNS-1 contain the standard attributes
148 * The column vector contains the columns that are actually
149 * available in the control.
156 /// construction / destruction
158 ColumnManager (CListCtrl
* control
) : control (control
), m_dwDefaultColumns(0) {};
163 void ReadSettings (DWORD defaultColumns
, DWORD hideColumns
, const CString
& containerName
, int ReadSettings
, int* withlist
= nullptr);
164 void WriteSettings() const;
166 /// read column definitions
168 int GetColumnCount() const; ///< total number of columns
169 bool IsVisible (int column
) const;
170 int GetInvisibleCount() const;
171 bool IsRelevant (int column
) const;
172 CString
GetName (int column
) const;
173 int SetNames(UINT
* buff
, int size
);
174 int GetWidth (int column
, bool useDefaults
= false) const;
175 int GetVisibleWidth (int column
, bool useDefaults
) const;
176 void SetRightAlign(int column
) const;
178 /// switch columns on and off
180 void SetVisible (int column
, bool visible
);
182 /// tracking column modifications
184 void ColumnMoved (int column
, int position
);
186 manual: 0: automatic updates, 1: manual updates, 2: manual updates and set to optimal width, 3: reset manual adjusted state
188 void ColumnResized(int column
, int manual
= 0);
190 /// call these to update the user-prop list
191 /// (will also auto-insert /-remove new list columns)
193 /// don't clutter the context menu with irrelevant prop info
195 void RemoveUnusedProps();
197 /// bring everything back to its "natural" order
199 void ResetColumns (DWORD defaultColumns
);
201 void OnHeaderDblClick(NMHDR
* pNMHDR
, LRESULT
* pResult
)
203 LPNMHEADER header
= reinterpret_cast<LPNMHEADER
>(pNMHDR
);
205 && (header
->iItem
>= 0)
206 && (header
->iItem
< GetColumnCount()))
208 bool bShift
= !!(GetAsyncKeyState(VK_SHIFT
) & 0x8000);
209 ColumnResized(header
->iItem
, bShift
? 3: 2);
214 void OnColumnResized(NMHDR
*pNMHDR
, LRESULT
*pResult
)
216 LPNMHEADER header
= reinterpret_cast<LPNMHEADER
>(pNMHDR
);
218 && (header
->iItem
>= 0)
219 && (header
->iItem
< GetColumnCount()))
221 ColumnResized (header
->iItem
, 1);
226 void OnColumnMoved(NMHDR
*pNMHDR
, LRESULT
*pResult
)
228 LPNMHEADER header
= reinterpret_cast<LPNMHEADER
>(pNMHDR
);
231 && (header
->iItem
>= 0)
232 && (header
->iItem
< GetColumnCount())
233 // only allow the reordering if the column was not moved left of the first
234 // visible item - otherwise the 'invisible' columns are not at the far left
235 // anymore and we get all kinds of redrawing problems.
237 && (header
->pitem
->iOrder
>= GetInvisibleCount()))
239 ColumnMoved (header
->iItem
, header
->pitem
->iOrder
);
243 void OnHdnBegintrack(NMHDR
*pNMHDR
, LRESULT
*pResult
)
245 LPNMHEADER phdr
= reinterpret_cast<LPNMHEADER
>(pNMHDR
);
247 if ((phdr
->iItem
< 0)||(phdr
->iItem
>= (int)itemName
.size()))
250 if (IsVisible (phdr
->iItem
))
255 int OnHdnItemchanging(NMHDR
*pNMHDR
, LRESULT
*pResult
)
257 LPNMHEADER phdr
= reinterpret_cast<LPNMHEADER
>(pNMHDR
);
259 if ((phdr
->iItem
< 0)||(phdr
->iItem
>= (int)itemName
.size()))
262 // visible columns may be modified
264 if (IsVisible (phdr
->iItem
))
267 // columns already marked as "invisible" internally may be (re-)sized to 0
270 && (phdr
->pitem
->mask
== HDI_WIDTH
)
271 && (phdr
->pitem
->cxy
== 0))
277 && (phdr
->pitem
->mask
!= HDI_WIDTH
))
285 void OnContextMenuHeader(CWnd
* pWnd
, CPoint point
, bool isGroundEnable
=false)
287 CHeaderCtrl
* pHeaderCtrl
= (CHeaderCtrl
*)pWnd
;
288 if ((point
.x
== -1) && (point
.y
== -1))
291 pHeaderCtrl
->GetItemRect(0, &rect
);
292 pHeaderCtrl
->ClientToScreen(&rect
);
293 point
= rect
.CenterPoint();
297 if (popup
.CreatePopupMenu())
299 int columnCount
= GetColumnCount();
302 UINT uCheckedFlags
= MF_STRING
| MF_ENABLED
| MF_CHECKED
;
303 UINT uUnCheckedFlags
= MF_STRING
| MF_ENABLED
;
305 // build control menu
307 //temp.LoadString(IDS_STATUSLIST_SHOWGROUPS);
308 //popup.AppendMenu(isGroundEnable? uCheckedFlags : uUnCheckedFlags, columnCount, temp);
310 temp
.LoadString(IDS_STATUSLIST_RESETCOLUMNORDER
);
311 popup
.AppendMenu(uUnCheckedFlags
, columnCount
+2, temp
);
312 popup
.AppendMenu(MF_SEPARATOR
);
317 // user-prop columns:
318 // find relevant ones and sort 'em
320 std::map
<CString
, int> sortedProps
;
321 for (int i
= (int)itemName
.size(); i
< columnCount
; ++i
)
323 sortedProps
[GetName(i
)] = i
;
325 if (!sortedProps
.empty())
327 // add 'em to the menu
329 popup
.AppendMenu(MF_SEPARATOR
);
331 for (auto iter
= sortedProps
.cbegin(), end
= sortedProps
.cend()
335 popup
.AppendMenu ( IsVisible(iter
->second
)
343 // show menu & let user pick an entry
345 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, pWnd
, 0);
346 if ((cmd
>= 1)&&(cmd
< columnCount
))
347 SetVisible (cmd
, !IsVisible(cmd
));
348 else if (cmd
== columnCount
)
350 pWnd
->GetParent()->SendMessage(LVM_ENABLEGROUPVIEW
, !isGroundEnable
, NULL
);
351 //EnableGroupView(!isGroundEnable);
353 else if (cmd
== columnCount
+1)
355 else if (cmd
== columnCount
+2)
357 temp
.LoadString(IDS_CONFIRMRESETCOLUMNORDER
);
358 if (MessageBox(pWnd
->m_hWnd
, temp
, _T("TortoiseGit"), MB_YESNO
| MB_ICONQUESTION
) == IDYES
)
359 ResetColumns (m_dwDefaultColumns
);
365 void AddMenuItem(CMenu
*pop
)
367 UINT uCheckedFlags
= MF_STRING
| MF_ENABLED
| MF_CHECKED
;
368 UINT uUnCheckedFlags
= MF_STRING
| MF_ENABLED
;
370 for (int i
= 1; i
< (int)itemName
.size(); ++i
)
373 pop
->AppendMenu ( IsVisible(i
)
381 DWORD m_dwDefaultColumns
;
383 /// initialization utilities
385 void ParseWidths (const CString
& widths
);
386 void SetStandardColumnVisibility (DWORD visibility
);
387 void ParseColumnOrder (const CString
& widths
);
389 /// map internal column order onto visible column order
390 /// (all invisibles in front)
392 std::vector
<int> GetGridColumnOrder() const;
393 void ApplyColumnOrder();
395 /// utilities used when writing data to the registry
397 DWORD
GetSelectedStandardColumns() const;
398 CString
GetWidthString() const;
399 CString
GetColumnOrderString() const;
401 /// our parent control and its data
405 /// where to store in the registry
407 CString registryPrefix
;
409 /// all columns in their "natural" order
413 int index
; ///< is a user prop when < GitSLC_USERPROPCOLOFFSET
416 bool relevant
; ///< set to @a visible, if no *shown* item has that property
420 std::vector
<ColumnInfo
> columns
;
422 /// user-defined properties
424 std::set
<CString
> itemProps
;
426 /// global column ordering including unused user props
428 std::vector
<int> columnOrder
;
430 std::vector
<int> itemName
;
434 * \ingroup TortoiseProc
435 * Simple utility class that defines the sort column order.
441 CSorter ( ColumnManager
* columnManager
445 bool operator() ( const CTGitPath
* entry1
446 , const CTGitPath
* entry2
) const;
448 static int A2L(const CString
&str
)
458 ColumnManager
* columnManager
;
465 * A List control, based on the MFC CListCtrl which shows a list of
466 * files with their git status. The control also provides a context
467 * menu to do some git tasks on the selected files.
469 * This is the main control used in many dialogs to show a list of files to
472 class CGitStatusListCtrl
:
479 /** Compare with base version. when current version is zero (i.e. working tree changes), compare working tree and HEAD */
484 /** Compare with base version and generate unified diff. when current version is zero (i.e. working tree changes), compare working tree and HEAD */
488 IDGITLC_LOGSUBMODULE
,
489 IDGITLC_EDITCONFLICT
,
491 IDGITLC_IGNOREFOLDER
,
493 IDGITLC_RESOLVECONFLICT
,
496 IDGITLC_RESOLVETHEIRS
,
497 IDGITLC_RESOLVEMINE
,
502 IDGITLC_REMOVEFROMCS
,
504 IDGITLC_CREATEIGNORECS
,
506 IDGITLC_UNCHECKGROUP
,
507 /** Compare current version and working tree */
511 IDGITLC_REVERTTOREV
,
512 IDGITLC_REVERTTOPARENT
,
515 /** used in sync dlg, compare in/out file changes; in combination with m_Rev1 and m_Rev2 */
516 IDGITLC_COMPARETWOREVISIONS
,
517 /** used in sync dlg, compare in/out file changes; in combination with m_Rev1 and m_Rev2 */
518 IDGITLC_GNUDIFF2REVISIONS
,
519 /** Compare two selected files */
520 IDGITLC_COMPARETWOFILES
,
522 IDGITLC_CREATERESTORE
,
523 IDGITLC_RESTOREPATH
,
524 IDGITLC_ASSUMEVALID
,
525 IDGITLC_SKIPWORKTREE
,
527 IDGITLC_UNSETIGNORELOCALCHANGES
,
528 IDGITLC_PREPAREDIFF
,
529 IDGITLC_PREPAREDIFF_COMPARE
,
530 // the IDSVNLC_MOVETOCS *must* be the last index, because it contains a dynamic submenu where
531 // the submenu items get command ID's sequent to this number
534 int GetColumnIndex(int colmask
);
535 static inline unsigned __int64
GetContextMenuBit(int i
){ return ((unsigned __int64
)0x1)<<i
;}
537 * Sent to the parent window (using ::SendMessage) after a context menu
538 * command has finished if the item count has changed.
540 static const UINT GITSLNM_ITEMCOUNTCHANGED
;
542 * Sent to the parent window (using ::SendMessage) when the control needs
543 * to be refreshed. Since this is done usually in the parent window using
544 * a thread, this message is used to tell the parent to do exactly that.
546 static const UINT GITSLNM_NEEDSREFRESH
;
549 * Sent to the parent window (using ::SendMessage) when the user drops
550 * files on the control. The LPARAM is a pointer to a TCHAR string
551 * containing the dropped path.
553 static const UINT GITSLNM_ADDFILE
;
556 * Sent to the parent window (using ::SendMessage) when the user checks/unchecks
557 * one or more items in the control. The WPARAM contains the number of
558 * checked items in the control.
560 static const UINT GITSLNM_CHECKCHANGED
;
562 static const UINT GITSLNM_ITEMCHANGED
;
564 CGitStatusListCtrl(void);
565 ~CGitStatusListCtrl(void);
571 * \ingroup TortoiseProc
572 * Helper class for CGitStatusListCtrl which represents
573 * the data for each file shown.
579 FileEntry() : status(git_wc_status_unversioned
)
580 // , copyfrom_rev(GIT_REV_ZERO)
581 , last_commit_date(0)
582 , last_commit_rev(GIT_REV_ZERO
)
583 // , remoterev(GIT_REV_ZERO)
584 , textstatus(git_wc_status_unversioned
)
585 , propstatus(git_wc_status_unversioned
)
586 // , remotestatus(git_wc_status_unversioned)
587 // , remotetextstatus(git_wc_status_unversioned)
588 // , remotepropstatus(git_wc_status_unversioned)
592 , inunversionedfolder(false)
594 , differentrepo(false)
598 , Revision(GIT_REV_ZERO
)
599 , isConflicted(false)
602 /// , working_size(SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN)
604 // , depth(git_depth_unknown)
607 const CTGitPath
& GetPath() const
611 const bool IsChecked() const
615 CString
GetRelativeGitPath() const
617 if (path
.IsEquivalentTo(basepath
))
618 return path
.GetGitPathString();
619 return path
.GetGitPathString().Mid(basepath
.GetGitPathString().GetLength()+1);
621 // const bool IsLocked() const
623 // return !(lock_token.IsEmpty() && lock_remotetoken.IsEmpty());
625 // const bool HasNeedsLock() const
629 const bool IsFolder() const
633 const bool IsInExternal() const
637 const bool IsNested() const
641 const bool IsFromDifferentRepository() const
643 return differentrepo
;
645 CString
GetDisplayName() const
647 CString
const& chopped
= path
.GetDisplayString(&basepath
);
648 if (!chopped
.IsEmpty())
654 // "Display name" must not be empty.
655 return path
.GetFileOrDirectoryName();
658 CString
GetChangeList() const
662 // CString GetURL() const
667 git_wc_status_kind status
; ///< local status
668 git_wc_status_kind textstatus
; ///< local text status
669 git_wc_status_kind propstatus
; ///< local property status
672 CTGitPath path
; ///< full path of the file
673 CTGitPath basepath
; ///< common ancestor path of all files
675 CString changelist
; ///< the name of the changelist the item belongs to
677 CString last_commit_author
; ///< the author which last committed this item
678 CTime last_commit_date
; ///< the date when this item was last committed
679 git_revnum_t last_commit_rev
; ///< the revision where this item was last committed
681 git_revnum_t remoterev
; ///< the revision in HEAD of the repository
682 bool copied
; ///< if the file/folder is added-with-history
683 bool switched
; ///< if the file/folder is switched to another url
684 bool checked
; ///< if the file is checked in the list control
685 bool inunversionedfolder
; ///< if the file is inside an unversioned folder
686 bool inexternal
; ///< if the item is in an external folder
687 bool differentrepo
; ///< if the item is from a different repository than the rest
688 bool direct
; ///< directly included (TRUE) or just a child of a folder
689 bool isfolder
; ///< TRUE if entry refers to a folder
690 bool isNested
; ///< TRUE if the folder from a different repository and/or path
691 bool isConflicted
; ///< TRUE if a file entry is conflicted, i.e. if it has the conflicted paths set
692 bool needslock
; ///< TRUE if the Git:needs-lock property is set
693 git_revnum_t Revision
; ///< the base revision
694 // PropertyList present_props; ///< cacheable properties present in BASE
695 bool keeplocal
; ///< Whether a local copy of this entry should be kept in the working copy after a deletion has been committed
696 git_depth_t depth
; ///< the depth of this entry
697 friend class CGitStatusListCtrl
;
698 friend class CGitStatusListCtrlDropTarget
;
699 friend class CSorter
;
704 * Initializes the control, sets up the columns.
705 * \param dwColumns mask of columns to show. Use the GitSLC_COLxxx defines.
706 * \param sColumnInfoContainer Name of a registry key
707 * where the position and visibility of each column
708 * is saved and used from. If the registry key
709 * doesn't exist, the default order is used
710 * and dwColumns tells which columns are visible.
711 * \param dwContextMenus mask of context menus to be active, not all make sense for every use of this control.
712 * Use the GitSLC_POPxxx defines.
713 * \param bHasCheckboxes TRUE if the control should show check boxes on the left of each file entry.
714 * \param bHasWC TRUE if the reporisty is not a bare repository (hides wc related items on the contextmenu)
716 void Init(DWORD dwColumns
, const CString
& sColumnInfoContainer
, unsigned __int64 dwContextMenus
= ((GITSLC_POPALL
^ GITSLC_POPCOMMIT
) ^ GITSLC_POPRESTORE
), bool bHasCheckboxes
= true, bool bHasWC
= true, DWORD allowedColumns
= 0xffffffff);
718 * Sets a background image for the list control.
719 * The image is shown in the right bottom corner.
720 * \param nID the resource ID of the bitmap to use as the background
722 bool SetBackgroundImage(UINT nID
);
724 * Makes the 'ignore' context menu only ignore the files and not add the
725 * folder which gets the Git:ignore property changed to the list.
726 * This is needed e.g. for the Add-dialog, where the modified folder
727 * showing up would break the resulting "add" command.
729 void SetIgnoreRemoveOnly(bool bRemoveOnly
= true) {m_bIgnoreRemoveOnly
= bRemoveOnly
;}
731 * The unversioned items are by default shown after all other files in the list.
732 * If that behavior should be changed, set this value to false.
734 void PutUnversionedLast(bool bLast
) {m_bUnversionedLast
= bLast
;}
736 * Fetches the git status of all files and stores the information
737 * about them in an internal array.
738 * \param sFilePath path to a file which contains a list of files and/or folders for which to
739 * fetch the status, separated by newlines.
740 * \param bUpdate TRUE if the remote status is requested too.
741 * \return TRUE on success.
743 BOOL
GetStatus (const CTGitPathList
* pathList
= nullptr
744 , bool bUpdate
= false
745 , bool bShowIgnores
= false
746 , bool bShowUnRev
= false
747 , bool bShowLocalChangesIgnored
= false);
750 * Populates the list control with the previously (with GetStatus) gathered status information.
751 * \param dwShow mask of file types to show. Use the GitSLC_SHOWxxx defines.
752 * \param dwCheck mask of file types to check. Use GitLC_SHOWxxx defines. Default (0) means 'use the entry's stored check status'
754 void Show(unsigned int dwShow
, unsigned int dwCheck
= 0, bool bShowFolders
= true,BOOL updateStatusList
=FALSE
, bool UseStoredCheckStatus
=false);
755 void Show(unsigned int dwShow
, const CTGitPathList
& checkedList
, bool bShowFolders
= true);
758 * Copies the selected entries in the control to the clipboard. The entries
759 * are separated by newlines.
760 * \param dwCols the columns to copy. Each column is separated by a tab.
762 bool CopySelectedEntriesToClipboard(DWORD dwCols
);
765 * If during the call to GetStatus() some Git:externals are found from different
766 * repositories than the first one checked, then this method returns TRUE.
768 BOOL
HasExternalsFromDifferentRepos() const {return m_bHasExternalsFromDifferentRepos
;}
771 * If during the call to GetStatus() some Git:externals are found then this method returns TRUE.
773 BOOL
HasExternals() const {return m_bHasExternals
;}
776 * If unversioned files are found (but not necessarily shown) TRUE is returned.
778 BOOL
HasUnversionedItems() {return m_bHasUnversionedItems
;}
781 * If there are any locks in the working copy, TRUE is returned
783 BOOL
HasLocks() const {return m_bHasLocks
;}
786 * If there are any change lists defined in the working copy, TRUE is returned
788 BOOL
HasChangeLists() const {return m_bHasChangeLists
;}
791 * Returns the file entry data for the list control index.
793 //CGitStatusListCtrl::FileEntry * GetListEntry(UINT_PTR index);
796 * Returns the file entry data for the specified path.
797 * \note The entry might not be shown in the list control.
799 //CGitStatusListCtrl::FileEntry * GetListEntry(const CTGitPath& path);
802 * Returns the index of the list control entry with the specified path,
803 * or -1 if the path is not in the list control.
805 int GetIndex(const CTGitPath
& path
);
808 * Returns the file entry data for the specified path in the list control.
810 //CGitStatusListCtrl::FileEntry * GetVisibleListEntry(const CTGitPath& path);
813 * Returns a String containing some statistics like number of modified, normal, deleted,...
816 CString
GetStatisticsString(bool simple
=false);
819 * Set a static control which will be updated automatically with
820 * the number of selected and total files shown in the list control.
822 void SetStatLabel(CWnd
* pStatLabel
){m_pStatLabel
= pStatLabel
;};
825 * Set a tri-state checkbox which is updated automatically if the
826 * user checks/unchecks file entries in the list control to indicate
827 * if all files are checked, none are checked or some are checked.
829 void SetSelectButton(CButton
* pButton
) {m_pSelectButton
= pButton
;}
832 * Set a button which is de-/activated automatically. The button is
833 * only set active if at least one item is selected.
835 void SetConfirmButton(CButton
* pButton
) {m_pConfirmButton
= pButton
;}
838 * Select/unselect all entries in the list control.
839 * \param bSelect TRUE to check, FALSE to uncheck.
841 void SelectAll(bool bSelect
, bool bIncludeNoCommits
= false);
844 * Checks or unchecks all specified items
845 * \param dwCheck GITLC_SHOWxxx defines
846 * \param check if true matching items will be selected, false unchecks matching items
848 void Check(DWORD dwCheck
, bool check
= true);
850 /** Set a checkbox on an entry in the listbox
851 * Keeps the listctrl checked state and the FileEntry's checked flag in sync
853 void SetEntryCheck(CTGitPath
* pEntry
, int listboxIndex
, bool bCheck
);
855 /** Write a list of the checked items' paths into a path list
857 void WriteCheckedNamesToPathList(CTGitPathList
& pathList
);
859 /** fills in \a lMin and \a lMax with the lowest/highest revision of all
860 * files/folders in the working copy.
861 * \param bShownOnly if true, the min/max revisions are calculated only for shown items
862 * \param bCheckedOnly if true, the min/max revisions are calculated only for items
864 * \remark Since an item can only be checked if it is visible/shown in the list control
865 * bShownOnly is automatically set to true if bCheckedOnly is true
867 void GetMinMaxRevisions(git_revnum_t
& rMin
, git_revnum_t
& rMax
, bool bShownOnly
, bool bCheckedOnly
);
870 * Returns the parent directory of all entries in the control.
871 * if \a bStrict is set to false, then the paths passed to the control
872 * to fetch the status (in GetStatus()) are used if possible.
874 CString
GetCommonDirectory(bool bStrict
);
877 * Returns the parent url of all entries in the control.
878 * if \a bStrict is set to false, then the paths passed to the control
879 * to fetch the status (in GetStatus()) are used if possible.
881 CTGitPath
GetCommonURL(bool bStrict
);
884 * Sets a pointer to a boolean variable which is checked periodically
885 * during the status fetching. As soon as the variable changes to true,
886 * the operations stops.
888 void SetCancelBool(bool * pbCanceled
) {m_pbCanceled
= pbCanceled
;}
891 * Sets the string shown in the control while the status is fetched.
892 * If not set, it defaults to "please wait..."
894 void SetBusyString(const CString
& str
) {m_sBusy
= str
;}
895 void SetBusyString(UINT id
) {m_sBusy
.LoadString(id
);}
898 * Sets the string shown in the control if no items are shown. This
899 * can happen for example if there's nothing modified and the unversioned
900 * files aren't shown either, so there's nothing to commit.
901 * If not set, it defaults to "file list is empty".
903 void SetEmptyString(const CString
& str
) {m_sEmpty
= str
;}
904 void SetEmptyString(UINT id
) {m_sEmpty
.LoadString(id
);}
907 * Returns the number of selected items
909 LONG
GetSelected(){return m_nSelected
;};
912 * Enables dropping of files on the control.
914 bool EnableFileDrop();
917 * Checks if the path already exists in the list.
919 bool HasPath(const CTGitPath
& path
);
921 * Checks if the path is shown/visible in the list control.
923 bool IsPathShown(const CTGitPath
& path
);
925 * Forces the children to be checked when the parent folder is checked,
926 * and the parent folder to be unchecked if one of its children is unchecked.
928 void CheckChildrenWithParent(bool bCheck
) {m_bCheckChildrenWithParent
= bCheck
;}
931 * Allows checking the items if change lists are present. If set to false,
932 * items are not checked if at least one changelist is available.
934 void CheckIfChangelistsArePresent(bool bCheck
) {m_bCheckIfGroupsExist
= bCheck
;}
936 * Returns the currently used show flags passed to the Show() method.
938 DWORD
GetShowFlags() {return m_dwShow
;}
941 CString
GetLastErrorMessage() {return m_sLastError
;}
943 void Block(BOOL block
, BOOL blockUI
) {m_bBlock
= block
; m_bBlockUI
= blockUI
;}
945 LONG
GetUnversionedCount() { return m_nShownUnversioned
; }
946 LONG
GetModifiedCount() { return m_nShownModified
; }
947 LONG
GetAddedCount() { return m_nShownAdded
; }
948 LONG
GetDeletedCount() { return m_nShownDeleted
; }
949 LONG
GetConflictedCount() { return m_nShownConflicted
; }
950 LONG
GetFileCount() { return m_nShownFiles
; }
951 LONG
GetSubmoduleCount() { return m_nShownSubmodules
; }
953 LONG m_nTargetCount
; ///< number of targets in the file passed to GetStatus()
955 CString m_sURL
; ///< the URL of the target or "(multiple targets)"
957 GitRev m_HeadRev
; ///< the HEAD revision of the repository if bUpdate was TRUE
959 bool m_amend
; ///< if true show the changes to the revision before the last commit
961 CString m_sUUID
; ///< the UUID of the associated repository
963 CString m_sDisplayedBranch
; ///< When on LogDialog, what is the current displayed branch
965 CWnd
*m_hwndLogicalParent
;
967 DECLARE_MESSAGE_MAP()
970 void SetBusy(bool b
) {m_bBusy
= b
; Invalidate();}
971 bool IsBusy() const { return m_bBusy
; }
972 void SetHasCheckboxes(bool bHasCheckboxes
)
974 m_bHasCheckboxes
= bHasCheckboxes
;
975 DWORD exStyle
= GetExtendedStyle();
977 exStyle
|= LVS_EX_CHECKBOXES
;
979 exStyle
&= ~LVS_EX_CHECKBOXES
;
980 SetExtendedStyle(exStyle
);
984 void SaveColumnWidths(bool bSaveToRegistry
= false);
985 //void AddEntry(FileEntry * entry, WORD langID, int listIndex); ///< add an entry to the control
986 void RemoveListEntry(int index
); ///< removes an entry from the listcontrol and both arrays
987 bool BuildStatistics(); ///< build the statistics and correct the case of files/folders
988 void StartDiff(int fileindex
); ///< start the external diff program
989 void StartDiffWC(int fileindex
); ///< start the external diff program
990 void StartDiffTwo(int fileindex
);
992 void SetGitIndexFlagsForSelectedFiles(UINT message
, BOOL assumevalid
, BOOL skipworktree
);
994 CString m_sMarkForDiffFilename
;
995 CString m_sMarkForDiffVersion
;
997 /* while rebasing, Their and My versios are swapped. */
998 bool m_bIsRevertTheirMy
;
1006 void OpenFile(CTGitPath
*path
,int mode
);
1008 /// Clear the status vector (contains custodial pointers)
1009 void ClearStatusArray();
1011 /// Sort predicate function - Compare the paths of two entries without regard to case
1012 //static bool EntryPathCompareNoCase(const FileEntry* pEntry1, const FileEntry* pEntry2);
1014 /// Predicate used to build a list of only the versioned entries of the FileEntry array
1015 //static bool IsEntryVersioned(const FileEntry* pEntry1);
1017 /// Look up the relevant show flags for a particular Git status value
1018 DWORD
GetShowFlagsFromGitStatus(git_wc_status_kind status
);
1020 /// Adjust the checkbox-state on all descendants of a specific item
1021 //void SetCheckOnAllDescendentsOf(const FileEntry* parentEntry, bool bCheck);
1023 /// Build a path list of all the selected items in the list (NOTE - SELECTED, not CHECKED)
1024 void FillListOfSelectedItemPaths(CTGitPathList
& pathList
, bool bNoIgnored
= false);
1026 /// Enables/Disables group view and adds all groups to the list control.
1027 /// If bForce is true, then group view is enabled and the 'null' group is added.
1028 bool PrepareGroups(bool bForce
= false);
1029 /// Returns the group number to which the group header belongs
1030 /// If the point is not over a group header, -1 is returned
1031 int GetGroupFromPoint(POINT
* ppt
);
1032 /// Returns the number of change lists the selection has
1033 size_t GetNumberOfChangelistsInSelection();
1035 /// Puts the item to the corresponding group
1036 bool SetItemGroup(int item
, int groupindex
);
1038 void CheckEntry(int index
, int nListItems
);
1039 void UncheckEntry(int index
, int nListItems
);
1041 /// sends an GitSLNM_CHECKCHANGED notification to the parent
1043 CWnd
* GetLogicalParent() { return m_hwndLogicalParent
? m_hwndLogicalParent
: this->GetParent(); }
1045 void OnContextMenuList(CWnd
* pWnd
, CPoint point
);
1046 void OnContextMenuGroup(CWnd
* pWnd
, CPoint point
);
1047 void OnContextMenuHeader(CWnd
* pWnd
, CPoint point
);
1048 bool CheckMultipleDiffs();
1050 void DeleteSelectedFiles();
1052 virtual void PreSubclassWindow();
1053 virtual BOOL
PreTranslateMessage(MSG
* pMsg
);
1054 virtual BOOL
OnWndMsg(UINT message
, WPARAM wParam
, LPARAM lParam
, LRESULT
* pResult
);
1055 afx_msg
void OnBeginDrag(NMHDR
* pNMHDR
, LRESULT
* pResult
);
1056 afx_msg BOOL
OnToolTipText(UINT id
, NMHDR
*pNMHDR
, LRESULT
*pResult
);
1057 afx_msg
void OnHdnItemclick(NMHDR
*pNMHDR
, LRESULT
*pResult
);
1058 afx_msg
void OnLvnItemchanging(NMHDR
*pNMHDR
, LRESULT
*pResult
);
1059 afx_msg BOOL
OnLvnItemchanged(NMHDR
*pNMHDR
, LRESULT
*pResult
);
1060 afx_msg
void OnColumnResized(NMHDR
*pNMHDR
, LRESULT
*pResult
);
1061 afx_msg
void OnHeaderDblClick(NMHDR
* pNMHDR
, LRESULT
* pResult
);
1062 afx_msg
void OnColumnMoved(NMHDR
*pNMHDR
, LRESULT
*pResult
);
1063 afx_msg
void OnContextMenu(CWnd
* pWnd
, CPoint point
);
1065 void CreateChangeList(const CString
& name
);
1067 afx_msg
void OnNMDblclk(NMHDR
*pNMHDR
, LRESULT
*pResult
);
1068 afx_msg
void OnLvnGetInfoTip(NMHDR
*pNMHDR
, LRESULT
*pResult
);
1069 afx_msg
void OnNMCustomdraw(NMHDR
*pNMHDR
, LRESULT
*pResult
);
1070 afx_msg BOOL
OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
);
1071 afx_msg UINT
OnGetDlgCode();
1072 afx_msg
void OnNMReturn(NMHDR
*pNMHDR
, LRESULT
*pResult
);
1073 afx_msg
void OnKeyDown(UINT nChar
, UINT nRepCnt
, UINT nFlags
);
1074 afx_msg
void OnPaint();
1075 afx_msg
void OnHdnBegintrack(NMHDR
*pNMHDR
, LRESULT
*pResult
);
1076 afx_msg
void OnHdnItemchanging(NMHDR
*pNMHDR
, LRESULT
*pResult
);
1077 afx_msg
void OnDestroy();
1081 void FileSaveAs(CTGitPath
*path
);
1082 int RevertSelectedItemToVersion(bool parent
= false);
1085 bool * m_pbCanceled
;
1086 bool m_bAscending
; ///< sort direction
1087 int m_nSortedColumn
; ///< which column to sort
1088 bool m_bHasCheckboxes
;
1090 bool m_bUnversionedLast
;
1091 bool m_bHasExternalsFromDifferentRepos
;
1092 bool m_bHasExternals
;
1093 BOOL m_bHasUnversionedItems
;
1095 bool m_bHasChangeLists
;
1096 //typedef std::vector<FileEntry*> FileEntryVector;
1097 //FileEntryVector m_arStatusArray;
1098 std::vector
<CTGitPath
*> m_arStatusArray
;
1099 std::vector
<size_t> m_arListArray
;
1100 std::map
<CString
, int> m_changelists
;
1101 bool m_bHasIgnoreGroup
;
1102 //CTGitPathList m_ConflictFileList;
1103 CTGitPathList m_StatusFileList
;
1104 CTGitPathList m_UnRevFileList
;
1105 CTGitPathList m_IgnoreFileList
;
1106 CTGitPathList m_LocalChangesIgnoredFileList
; // assume valid & skip worktree
1107 //CTGitPathList m_StatusUrlList;
1108 CString m_sLastError
;
1110 LONG m_nUnversioned
;
1119 LONG m_nLineDeleted
;
1122 LONG m_nShownUnversioned
;
1123 LONG m_nShownModified
;
1125 LONG m_nShownDeleted
;
1126 LONG m_nShownConflicted
;
1128 LONG m_nShownSubmodules
;
1130 DWORD m_dwDefaultColumns
;
1132 bool m_bShowFolders
;
1133 bool m_bShowIgnores
;
1135 unsigned __int64 m_dwContextMenus
;
1140 bool m_bIgnoreRemoveOnly
;
1141 bool m_bCheckIfGroupsExist
;
1142 bool m_bFileDropsEnabled
;
1148 CWnd
* m_pStatLabel
;
1149 CButton
* m_pSelectButton
;
1150 CButton
* m_pConfirmButton
;
1155 CString m_sNoPropValueText
;
1157 bool m_bCheckChildrenWithParent
;
1158 CGitStatusListCtrlDropTarget
* m_pDropTarget
;
1160 ColumnManager m_ColumnManager
;
1162 std::map
<CString
,bool> m_mapFilenameToChecked
; ///< Remember de-/selected items
1163 std::map
<CString
,bool> m_mapDirectFiles
;
1164 CComCriticalSection m_critSec
;
1166 friend class CGitStatusListCtrlDropTarget
;
1170 FILELIST_MODIFY
= 0x1,
1171 FILELIST_UNVER
= 0x2,
1172 FILELIST_IGNORE
=0x4,
1173 FILELIST_LOCALCHANGESIGNORED
= 0x8, // assume valid & skip worktree files
1176 int UpdateFileList(CTGitPathList
* List
= nullptr);
1178 int UpdateFileList(int mask
, bool once
= true, CTGitPathList
* List
= nullptr);
1179 int UpdateUnRevFileList(CTGitPathList
&list
);
1180 int UpdateUnRevFileList(CTGitPathList
* List
= nullptr);
1181 int UpdateIgnoreFileList(CTGitPathList
* List
= nullptr);
1182 int UpdateLocalChangesIgnoredFileList(CTGitPathList
* list
= nullptr);
1184 int UpdateWithGitPathList(CTGitPathList
&list
);
1186 void AddEntry(CTGitPath
* path
, WORD langID
, int ListIndex
);
1189 git_revnum_t m_CurrentVersion
;
1190 bool m_bDoNotAutoselectSubmodules
;
1191 bool m_bNoAutoselectMissing
;
1192 std::map
<CString
, CString
> m_restorepaths
;
1195 LPCONTEXTMENU m_pContextMenu
;
1199 class CGitStatusListCtrlDropTarget
: public CIDropTarget
1202 CGitStatusListCtrlDropTarget(CGitStatusListCtrl
* pGitStatusListCtrl
):CIDropTarget(pGitStatusListCtrl
->m_hWnd
){m_pGitStatusListCtrl
= pGitStatusListCtrl
;}
1204 virtual bool OnDrop(FORMATETC
* pFmtEtc
, STGMEDIUM
& medium
, DWORD
* /*pdwEffect*/, POINTL pt
);
1205 virtual HRESULT STDMETHODCALLTYPE
DragOver(DWORD grfKeyState
, POINTL pt
, DWORD __RPC_FAR
*pdwEffect
);
1207 CGitStatusListCtrl
* m_pGitStatusListCtrl
;