1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2024 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // GitLogList.cpp : implementation file
24 #include "ResizableColumnsListCtrl.h"
26 #include "ProjectProperties.h"
30 #include "LogDlgHelper.h"
31 #include "GitRevLoglist.h"
33 #include "GitLogCache.h"
35 #include "GitStatusListCtrl.h"
37 #include <unordered_set>
38 #include "LogDlgFilter.h"
40 using Locker
= CComCritSecLock
<CComCriticalSection
>;
42 template < typename Cont
, typename Pred
>
43 void for_each(Cont
& c
, Pred
&& p
)
45 std::for_each(cbegin(c
), cend(c
), p
);
48 template <typename Cont
, typename Pred
>
49 auto any_of(Cont
& c
, Pred
&& p
)
51 return std::any_of(cbegin(c
), cend(c
), p
);
55 #define ICONITEMBORDER 5
57 #define GITLOG_START 0
58 #define GITLOG_START_ALL 1
59 #define GITLOG_END 100
61 #define LOGFILTER_ALL 0xFFFF
62 #define LOGFILTER_TOGGLE 0x8000
63 #define LOGFILTER_MESSAGES 0x0001
64 #define LOGFILTER_PATHS 0x0002
65 #define LOGFILTER_AUTHORS 0x0004
66 #define LOGFILTER_REVS 0x0008
67 #define LOGFILTER_REGEX 0x0010
68 #define LOGFILTER_BUGID 0x0020
69 #define LOGFILTER_SUBJECT 0x0040
70 #define LOGFILTER_REFNAME 0x0080
71 #define LOGFILTER_EMAILS 0x0100
72 #define LOGFILTER_NOTES 0x0200
73 #define LOGFILTER_ANNOTATEDTAG 0x0400
74 #define LOGFILTER_CASE 0x0800
76 #define LOGLIST_SHOWNOTHING 0x0000
77 #define LOGLIST_SHOWLOCALBRANCHES 0x0001
78 #define LOGLIST_SHOWREMOTEBRANCHES 0x0002
79 #define LOGLIST_SHOWTAGS 0x0004
80 #define LOGLIST_SHOWSTASH 0x0008
81 #define LOGLIST_SHOWBISECT 0x0010
82 #define LOGLIST_SHOWOTHERREFS 0x0020
83 #define LOGLIST_SHOWALLREFS 0xFFFF
85 //typedef void CALLBACK_PROCESS(void * data, int progress);
86 #define MSG_LOADED (WM_USER+110)
87 #define MSG_LOAD_PERCENTAGE (WM_USER+111)
88 #define MSG_REFLOG_CHANGED (WM_USER+112)
89 #define MSG_FETCHED_DIFF (WM_USER+113)
90 #define MSG_COMMITS_REORDERED (WM_USER+114)
92 class SelectionHistory
94 #define HISTORYLENGTH 50
98 lastselected
.reserve(HISTORYLENGTH
);
100 void Add(CGitHash
&hash
)
105 const size_t size
= lastselected
.size();
107 // re-select last selected commit
108 if (size
> 0 && hash
== lastselected
[size
- 1])
111 if (location
!= size
- 1)
116 // go back and some commit was highlight
117 if (size
> 0 && location
!= size
- 1)
119 // Re-select current one, it may be a forked point.
120 if (hash
== lastselected
[location
])
122 // That is that discarding forward history when a forked entry is really coming.
123 // And user has the chance to Go Forward again in this situation.
124 // IOW, (hash != lastselected[location]) means user wants a forked history,
125 // and this change saves one step from old behavior.
128 // Discard forward history if any
129 while (lastselected
.size() - 1 > location
)
130 lastselected
.pop_back();
133 if (lastselected
.size() >= HISTORYLENGTH
)
134 lastselected
.erase(lastselected
.cbegin());
136 lastselected
.push_back(hash
);
137 location
= lastselected
.size() - 1;
139 BOOL
GoBack(CGitHash
& historyEntry
)
144 historyEntry
= lastselected
[--location
];
148 BOOL
GoForward(CGitHash
& historyEntry
)
150 if (location
>= lastselected
.size() - 1)
153 historyEntry
= lastselected
[++location
];
158 std::vector
<CGitHash
> lastselected
;
162 class CThreadSafePtrArray
: private std::vector
<GitRevLoglist
*>
164 CComCriticalSection
* m_critSec
= nullptr;
166 CThreadSafePtrArray(CComCriticalSection
* section
) : m_critSec(section
)
168 ATLASSERT(m_critSec
);
171 GitRevLoglist
* SafeGetAt(size_t i
)
173 Locker
lock(*m_critSec
);
180 void SafeAdd(GitRevLoglist
* newElement
)
182 Locker
lock(*m_critSec
);
183 push_back(newElement
);
186 void SafeRemoveAt(size_t i
)
188 Locker
lock(*m_critSec
);
195 void SafeAddFront(GitRevLoglist
* newElement
)
197 Locker
lock(*m_critSec
);
198 insert(cbegin(), newElement
);
203 Locker
lock(*m_critSec
);
207 using std::vector
<GitRevLoglist
*>::begin
;
208 using std::vector
<GitRevLoglist
*>::end
;
209 using std::vector
<GitRevLoglist
*>::cbegin
;
210 using std::vector
<GitRevLoglist
*>::cend
;
211 using std::vector
<GitRevLoglist
*>::clear
;
212 using std::vector
<GitRevLoglist
*>::empty
;
213 using std::vector
<GitRevLoglist
*>::erase
;
214 using std::vector
<GitRevLoglist
*>::push_back
;
215 using std::vector
<GitRevLoglist
*>::size
;
216 using std::vector
<GitRevLoglist
*>::operator[];
223 class CGitLogListBase
: public CHintCtrl
<CResizableColumnsListCtrl
<CListCtrl
>>, public IAsyncDiffCB
225 DECLARE_DYNAMIC(CGitLogListBase
)
229 virtual ~CGitLogListBase();
230 ProjectProperties m_ProjectProperties
;
232 void UpdateProjectProperties()
234 m_ProjectProperties
.ReadProps();
236 if ((!m_ProjectProperties
.sUrl
.IsEmpty())||(!m_ProjectProperties
.sCheckRe
.IsEmpty()))
237 m_bShowBugtraqColumn
= true;
239 m_bShowBugtraqColumn
= false;
242 void ResetWcRev(bool refresh
= false)
246 CWnd
* pParent
= GetParent();
247 if (pParent
&& pParent
->GetSafeHwnd())
248 pParent
->SendMessage(LOGLIST_RESET_WCREV
);
251 m_wcRev
.GetSubject().LoadString(IDS_LOG_WORKINGDIRCHANGES
);
252 m_wcRev
.m_Mark
= L
'-';
253 m_wcRev
.GetBody().LoadString(IDS_LOG_FETCHINGSTATUS
);
254 m_wcRev
.GetBody() = L
'\n' + m_wcRev
.GetBody();
255 m_wcRev
.m_CallDiffAsync
= DiffAsync
;
256 InterlockedExchange(&m_wcRev
.m_IsDiffFiles
, FALSE
);
257 if (refresh
&& m_bShowWC
)
258 m_arShownList
[0] = &m_wcRev
;
261 volatile LONG m_bNoDispUpdates
= FALSE
;
262 BOOL m_IsIDReplaceAction
= FALSE
;
263 BOOL m_IsOldFirst
= FALSE
;
265 void hideFromContextMenu(unsigned __int64 hideMask
, bool exclusivelyShow
);
267 BOOL m_IsRebaseReplaceGraph
= FALSE
;
268 BOOL m_bNoHightlightHead
= FALSE
;
271 void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct
);
274 BOOL m_bShowBugtraqColumn
= FALSE
;
276 bool m_bIsCherryPick
= false;
277 unsigned __int64 m_ContextMenuMask
= UINT64_MAX
;
280 bool m_bShowWC
= false;
282 GitRevLoglist m_wcRev
;
284 static volatile LONG s_bThreadRunning
;
286 CLogCache m_LogCache
;
290 // don't forget to bump BLAME_COLUMN_VERSION in GitStatusListCtrlHelpers.cpp if you change columns
303 LOGLIST_COMMIT_EMAIL
,
307 LOGLIST_MESSAGE_MAX
=300,
308 LOGLIST_MESSAGE_MIN
=200,
310 GIT_LOG_GRAPH
= 1<< LOGLIST_GRAPH
,
311 GIT_LOG_REBASE
= 1<< LOGLIST_REBASE
,
312 GIT_LOG_ID
= 1<< LOGLIST_ID
,
313 GIT_LOG_HASH
= 1<< LOGLIST_HASH
,
314 GIT_LOG_ACTIONS
= 1<< LOGLIST_ACTION
,
315 GIT_LOG_MESSAGE
= 1<< LOGLIST_MESSAGE
,
316 GIT_LOG_AUTHOR
= 1<< LOGLIST_AUTHOR
,
317 GIT_LOG_DATE
= 1<< LOGLIST_DATE
,
318 GIT_LOG_EMAIL
= 1<< LOGLIST_EMAIL
,
319 GIT_LOG_COMMIT_NAME
= 1<< LOGLIST_COMMIT_NAME
,
320 GIT_LOG_COMMIT_EMAIL
= 1<< LOGLIST_COMMIT_EMAIL
,
321 GIT_LOG_COMMIT_DATE
= 1<< LOGLIST_COMMIT_DATE
,
322 GIT_LOGLIST_BUG
= 1<< LOGLIST_BUG
,
323 GIT_LOGLIST_SVNREV
= 1<< LOGLIST_SVNREV
,
328 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
329 ID_COMPARE
= 1, // compare revision with WC
331 ID_COMPARETWO
, // compare two revisions
334 ID_GNUDIFF1
, // compare with WC, unified
335 ID_GNUDIFF2
, // compare two revisions, unified
351 ID_COMPAREWITHPREVIOUS
,
367 ID_REFLOG_STASH_APPLY
,
369 ID_REBASE_TO_VERSION
,
379 ID_LOG_VIEWRANGE_REVERSE
,
380 ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE
,
389 ID_COMPARETWOCOMMITCHANGES
,
391 // the following can be >= 64 as those are not used for GetContextMenuBit(), all others must be < 64 in order to fit into __int64
392 ID_COPYCLIPBOARDFULL
,
393 ID_COPYCLIPBOARDFULLNOPATHS
,
394 ID_COPYCLIPBOARDHASH
,
395 ID_COPYCLIPBOARDAUTHORSFULL
,
396 ID_COPYCLIPBOARDAUTHORSNAME
,
397 ID_COPYCLIPBOARDAUTHORSEMAIL
,
398 ID_COPYCLIPBOARDSUBJECTS
,
399 ID_COPYCLIPBOARDMESSAGES
,
400 ID_COPYCLIPBOARDBRANCHTAG
,
405 FILTERSHOW_MERGEPOINTS
= 2,
406 FILTERSHOW_ANYCOMMIT
= 4,
407 FILTERSHOW_ALL
= FILTERSHOW_ANYCOMMIT
| FILTERSHOW_REFS
| FILTERSHOW_MERGEPOINTS
412 LOGACTIONS_REBASE_CURRENT
= 0x08000000,
413 LOGACTIONS_REBASE_PICK
= 0x04000000,
414 LOGACTIONS_REBASE_SQUASH
= 0x02000000,
415 LOGACTIONS_REBASE_EDIT
= 0x01000000,
416 LOGACTIONS_REBASE_DONE
= 0x00800000,
417 LOGACTIONS_REBASE_SKIP
= 0x00400000,
418 LOGACTIONS_REBASE_MASK
= 0x0FC00000,
419 LOGACTIONS_REBASE_MODE_MASK
= 0x07C00000,
421 static_assert(ID_COMPARETWOCOMMITCHANGES
< 64 && ID_COPYCLIPBOARDFULL
<= 64, "IDs must be <64 in order to be usable in a bitfield");
422 static constexpr unsigned __int64
GetContextMenuBit(int i
) noexcept
{ return unsigned __int64(0x1) << i
; }
423 static CString
GetRebaseActionName(int action
);
424 void InsertGitColumn();
425 virtual void CopySelectionToClipBoard(int toCopy
= ID_COPYCLIPBOARDFULL
);
426 void DiffSelectedRevWithPrevious();
427 bool IsSelectionContinuous();
430 HWND
GetParentHWND();
432 int FillGitLog(CTGitPath
* path
, CString
* range
= nullptr, int infomask
= CGit::LOG_INFO_STAT
| CGit::LOG_INFO_FILESTATE
| CGit::LOG_INFO_SHOW_MERGEDFILE
);
433 int FillGitLog(std::unordered_set
<CGitHash
>& hashes
);
435 CString
MessageDisplayStr(GitRev
* pLogEntry
);
436 bool ShouldShowFilter(GitRevLoglist
* pRev
, const std::unordered_map
<CGitHash
, std::unordered_set
<CGitHash
>>& commitChildren
, const MAP_HASH_NAME
& hashMap
);
437 bool ShouldShowAnyFilter();
438 bool ShouldShowRefsFilter(GitRevLoglist
* pRev
, const MAP_HASH_NAME
& hashMap
);
439 bool ShouldShowMergePointsFilter(GitRevLoglist
* pRev
, const std::unordered_map
<CGitHash
, std::unordered_set
<CGitHash
>>& commitChildren
);
441 void ShowGraphColumn(bool bShow
);
442 CString
GetTagInfo(GitRev
* pLogEntry
) const;
443 CString
GetTagInfo(const STRING_VECTOR
& refs
) const;
445 CFindDlg
* m_pFindDialog
= nullptr;
446 static const UINT m_FindDialogMessage
;
450 static const UINT m_ScrollToMessage
;
452 static const UINT m_ScrollToRef
;
453 static const UINT m_RebaseActionMessage
;
454 static const UINT LOGLIST_RESET_WCREV
;
456 void FetchLogAsync(void* data
= nullptr);
457 CThreadSafePtrArray m_arShownList
;
458 void Refresh(BOOL IsCleanFilter
=TRUE
);
461 FilterShow m_ShowFilter
= FILTERSHOW_ALL
;
462 CLogDataVector m_logEntries
;
464 std::atomic
<std::shared_ptr
<CLogDlgFilter
>> m_LogFilter
= std::make_shared
<CLogDlgFilter
>();
465 CFilterData m_Filter
;
467 enum class RollUpState
469 Expand
, // Parents of the node should be shown even in a compressed graph
470 Collapse
, // Parents of the node should be hidden
472 using RollUpStateMap
= std::unordered_map
<CGitHash
, RollUpState
>;
473 std::atomic
<std::shared_ptr
<RollUpStateMap
>> m_RollUpStates
= std::make_shared
<RollUpStateMap
>();
477 CGitHash m_lastSelectedHash
;
478 SelectionHistory m_selectionHistory
;
479 CGitHash m_highlight
;
480 int m_ShowRefMask
= LOGLIST_SHOWALLREFS
;
483 CGit::SubmoduleInfo m_submoduleInfo
;
486 void GetTimeRange(CTime
&oldest
,CTime
&latest
);
488 virtual void GetParentHashes(GitRev
* pRev
, GIT_REV_LIST
& parentHash
);
489 virtual void ContextMenuAction(int cmd
, int FirstSelect
, int LastSelect
, CMenu
* menu
, const MAP_HASH_NAME
& hashMap
) = 0;
492 m_RefLabelPosMap
.clear();
493 auto newHashMap
= std::make_shared
<MAP_HASH_NAME
>();
494 if (g_Git
.GetMapHashToFriendName(*newHashMap
.get()))
495 MessageBox(g_Git
.GetGitLastErr(L
"Could not get all refs."), L
"TortoiseGit", MB_ICONERROR
);
496 m_HashMap
= newHashMap
;
498 m_CurrentBranch
=g_Git
.GetCurrentBranch();
500 if (g_Git
.GetHash(m_HeadHash
, L
"HEAD"))
502 MessageBox(g_Git
.GetGitLastErr(L
"Could not get HEAD hash. Quitting..."), L
"TortoiseGit", MB_ICONERROR
);
506 m_wcRev
.m_ParentHash
.clear();
507 m_wcRev
.m_ParentHash
.push_back(m_HeadHash
);
510 FetchTrackingBranchList();
512 g_Git
.GetSubmodulePointer(m_submoduleInfo
);
514 void StartAsyncDiffThread();
515 void StartLoadingThread();
517 void SafeTerminateThread()
519 if (m_LoadingThread
&& InterlockedExchange(&m_bExitThread
, TRUE
) == FALSE
)
521 DWORD ret
= WAIT_TIMEOUT
;
522 for (int i
= 0; i
< 200 && s_bThreadRunning
; ++i
)
523 ret
=::WaitForSingleObject(m_LoadingThread
->m_hThread
, 100);
524 if (ret
== WAIT_TIMEOUT
&& s_bThreadRunning
)
525 ::TerminateThread(m_LoadingThread
, 0);
526 delete m_LoadingThread
;
527 m_LoadingThread
= nullptr;
531 bool IsInWorkingThread()
533 return (AfxGetThread() == m_LoadingThread
);
536 void SetRange(const CString
& range
)
538 Locker
lock(m_critSec
);
542 CString
GetRange() const { return m_sRange
; }
544 int m_nSearchIndex
= 0;
546 volatile LONG m_bExitThread
= FALSE
;
547 CWinThread
* m_LoadingThread
= nullptr;
549 std::atomic
<std::shared_ptr
<MAP_HASH_NAME
>> m_HashMap
= std::make_shared
<MAP_HASH_NAME
>();
552 std::map
<CString
, std::pair
<CString
, CString
>> m_TrackingMap
;
557 CString m_ColumnRegKey
;
564 CString simplifiedName
;
566 bool singleRemote
= false;
567 bool hasTracking
= false;
568 bool sameName
= false;
569 CGit::REF_TYPE refType
= CGit::REF_TYPE::UNKNOWN
;
572 DECLARE_MESSAGE_MAP()
573 afx_msg
void OnDestroy();
574 virtual afx_msg
void OnNMCustomdrawLoglist(NMHDR
* pNMHDR
, LRESULT
* pResult
);
575 virtual afx_msg
void OnLvnGetdispinfoLoglist(NMHDR
* pNMHDR
, LRESULT
* pResult
);
576 afx_msg LRESULT
OnFindDialogMessage(WPARAM wParam
, LPARAM lParam
);
577 afx_msg LRESULT
OnScrollToMessage(WPARAM wParam
, LPARAM lParam
);
578 afx_msg LRESULT
OnScrollToRef(WPARAM wParam
, LPARAM lParam
);
579 afx_msg
int OnCreate(LPCREATESTRUCT lpCreateStruct
);
580 afx_msg
void OnContextMenu(CWnd
* pWnd
, CPoint point
);
581 afx_msg LRESULT
OnLoad(WPARAM wParam
, LPARAM lParam
);
582 virtual void OnNMDblclkLoglist(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
);
583 afx_msg
void OnLvnOdfinditemLoglist(NMHDR
*pNMHDR
, LRESULT
*pResult
);
584 void PreSubclassWindow() override
;
585 BOOL
PreTranslateMessage(MSG
* pMsg
) override
;
586 ULONG
GetGestureStatus(CPoint ptTouch
) override
;
587 static UINT
LogThreadEntry(LPVOID pVoid
);
589 bool IsOnStash(int index
);
590 bool IsStash(const GitRev
* pSelLogEntry
);
591 bool IsBisect(const GitRev
* pSelLogEntry
);
592 void FetchRemoteList();
593 void FetchTrackingBranchList();
595 virtual afx_msg BOOL
OnToolTipText(UINT id
, NMHDR
* pNMHDR
, LRESULT
* pResult
);
596 INT_PTR
OnToolHitTest(CPoint point
, TOOLINFO
* pTI
) const override
;
597 CString
GetToolTipText(int nItem
, int nSubItem
);
599 /** Checks whether a referenfe label is under pt and returns the index/type
600 * pLogEntry IN: the entry of commit
601 * pt IN: the mouse position in client coordinate
602 * type IN: give the specific reference type, then check if it is the same reference type.
603 * OUT: give CGit::REF_TYPE::UNKNOWN for getting the real type it is.
604 * pShortname OUT: the short name of that reference label
605 * pIndex OUT: the index value of label of that entry
607 bool IsMouseOnRefLabel(const GitRevLoglist
* pLogEntry
, const POINT
& pt
, CGit::REF_TYPE
& type
, const MAP_HASH_NAME
& hashMap
, CString
* pShortname
= nullptr, size_t* pIndex
= nullptr);
608 bool IsMouseOnRefLabelFromPopupMenu(const GitRevLoglist
* pLogEntry
, const CPoint
& pt
, CGit::REF_TYPE
& type
, const MAP_HASH_NAME
& hashMap
, CString
* pShortname
= nullptr, size_t* pIndex
= nullptr);
610 void FillBackGround(HDC hdc
, DWORD_PTR Index
, CRect
&rect
);
611 void DrawTagBranchMessage(NMLVCUSTOMDRAW
* pLVCD
, const CRect
& rect
, INT_PTR index
, const std::vector
<REFLABEL
>& refList
);
612 void DrawTagBranch(HDC hdc
, CDC
& W_Dc
, HTHEME hTheme
, const CRect
& rect
, CRect
& rt
, LVITEM
& rItem
, GitRevLoglist
* data
, const std::vector
<REFLABEL
>& refList
);
613 void DrawGraph(HDC
,CRect
&rect
,INT_PTR index
);
614 bool DrawListItemWithMatchesIfEnabled(std::shared_ptr
<CLogDlgFilter
> filter
, DWORD selectedFilter
, NMLVCUSTOMDRAW
* pLVCD
, LRESULT
* pResult
);
615 static void DrawListItemWithMatchesRect(NMLVCUSTOMDRAW
* pLVCD
, const std::vector
<CHARRANGE
>& ranges
, CRect rect
, const CString
& text
, CColors
& colors
, HTHEME hTheme
= nullptr, int txtState
= 0);
618 // needs to be called from LogDlg.cpp and FileDiffDlg.cpp
619 static LRESULT
DrawListItemWithMatches(CFilterHelper
* filter
, CListCtrl
& listCtrl
, NMLVCUSTOMDRAW
* pLVCD
, CColors
& colors
);
622 void paintGraphLane(HDC hdc
, int laneHeight
, Lanes::LaneType type
, bool rolledUp
, int x1
, int x2
,
623 const COLORREF
& col
,const COLORREF
& activeColor
, int top
) ;
624 void DrawLine(HDC hdc
, int x1
, int y1
, int x2
, int y2
){ ::MoveToEx(hdc
, x1
, y1
, nullptr); ::LineTo(hdc
, x2
, y2
); }
626 * Save column widths to the registry
628 void SaveColumnWidths() override
; // save col widths to the registry
632 std::vector
<GitRevLoglist
*> m_AsynDiffList
;
633 CComAutoCriticalSection m_AsynDiffListLock
;
634 HANDLE m_AsyncDiffEvent
= nullptr;
635 volatile LONG m_AsyncThreadExit
= FALSE
;
636 CWinThread
* m_DiffingThread
= nullptr;
637 volatile LONG m_AsyncThreadRunning
= FALSE
;
640 bool IsCached(GitRevLoglist
* rev
)
642 ULONGLONG offset
= m_LogCache
.GetOffset(rev
->m_CommitHash
);
643 if (offset
&& !m_LogCache
.LoadOneItem(*rev
, offset
))
645 InterlockedExchange(&rev
->m_IsDiffFiles
, TRUE
);
653 static void DiffAsync(GitRevLoglist
* rev
, IAsyncDiffCB
* pdata
)
655 auto data
= static_cast<CGitLogListBase
*>(pdata
);
656 if (data
->IsCached(rev
))
659 data
->m_AsynDiffListLock
.Lock();
660 data
->m_AsynDiffList
.push_back(rev
);
661 data
->m_AsynDiffListLock
.Unlock();
662 ::SetEvent(data
->m_AsyncDiffEvent
);
665 static UINT
AsyncThread(LPVOID data
)
667 return reinterpret_cast<CGitLogListBase
*>(data
)->AsyncDiffThread();
670 int AsyncDiffThread();
673 void SafeTerminateAsyncDiffThread()
675 if (m_DiffingThread
&& InterlockedExchange(&m_AsyncThreadExit
, TRUE
) == FALSE
)
677 ::SetEvent(m_AsyncDiffEvent
);
678 DWORD ret
= WAIT_TIMEOUT
;
679 // do not block here, but process messages and ask until the thread ends
680 while (ret
== WAIT_TIMEOUT
&& m_AsyncThreadRunning
)
683 if (::PeekMessage(&msg
, nullptr, 0,0, PM_NOREMOVE
))
684 AfxGetThread()->PumpMessage(); // process messages, so that GetTopIndex and so on in the thread work
685 ret
= ::WaitForSingleObject(m_DiffingThread
->m_hThread
, 100);
687 delete m_DiffingThread
;
688 m_DiffingThread
= nullptr;
689 InterlockedExchange(&m_AsyncThreadExit
, FALSE
);
694 CComAutoCriticalSection m_critSec
;
696 CAutoIcon m_hModifiedIcon
;
697 CAutoIcon m_hReplacedIcon
;
698 CAutoIcon m_hConflictedIcon
;
699 CAutoIcon m_hAddedIcon
;
700 CAutoIcon m_hDeletedIcon
;
701 CAutoIcon m_hFetchIcon
;
702 CAutoIcon m_hErrorIcon
;
707 CFont m_boldItalicsFont
;
709 CRegDWORD m_regMaxBugIDColWidth
;
713 CString m_CurrentBranch
;
716 DWORD m_LineWidth
= 0;
717 DWORD m_NodeSize
= 0;
718 DWORD m_DateFormat
= DATE_SHORTDATE
; // DATE_SHORTDATE or DATE_LONGDATE
719 bool m_bRelativeTimes
= false; // Show relative times
720 GIT_LOG m_DllGitLog
= nullptr;
721 CString m_SingleRemote
;
722 bool m_bTagsBranchesOnRightSide
= false;
723 bool m_bFullCommitMessageOnLogLine
= false;
724 bool m_bSymbolizeRefNames
= false;
725 bool m_bIncludeBoundaryCommits
= false;
727 DWORD m_dwDefaultColumns
= 0;
728 wchar_t m_wszTip
[8192];
730 std::map
<CString
, CRect
> m_RefLabelPosMap
; // ref name vs. label position
731 int m_OldTopIndex
= -1;
733 bool m_bDragndropEnabled
= false;
734 bool m_bDragging
= false;
735 int m_nDropIndex
= -1;
736 int m_nDropMarkerLast
= -1;
737 int m_nDropMarkerLastHot
= -1;
738 afx_msg
void OnMouseMove(UINT nFlags
, CPoint point
);
739 afx_msg
void OnLButtonUp(UINT nFlags
, CPoint point
);
740 afx_msg
void OnBeginDrag(NMHDR
* pNMHDR
, LRESULT
* pResult
);
741 void DrawDropInsertMarker(int nIndex
);
742 void DrawDropInsertMarkerLine(int nIndex
);
745 void EnableDragnDrop(bool enable
) { m_bDragndropEnabled
= enable
; }