Fixed issue #4126: Capitalize the first letter in the Push dialog
[TortoiseGit.git] / src / TortoiseProc / GitLogListBase.h
blobe69159b5a6b4b9d3e6b739e698a2dd8736b0a0fe
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
21 #pragma once
23 #include "HintCtrl.h"
24 #include "ResizableColumnsListCtrl.h"
25 #include "Git.h"
26 #include "ProjectProperties.h"
27 #include "TGitPath.h"
28 #include "registry.h"
29 #include "Colors.h"
30 #include "LogDlgHelper.h"
31 #include "GitRevLoglist.h"
32 #include "lanes.h"
33 #include "GitLogCache.h"
34 #include <regex>
35 #include "GitStatusListCtrl.h"
36 #include "FindDlg.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);
54 // CGitLogList
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
95 public:
96 SelectionHistory()
98 lastselected.reserve(HISTORYLENGTH);
100 void Add(CGitHash &hash)
102 if (hash.IsEmpty())
103 return;
105 const size_t size = lastselected.size();
107 // re-select last selected commit
108 if (size > 0 && hash == lastselected[size - 1])
110 // reset location
111 if (location != size - 1)
112 location = size - 1;
113 return;
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])
121 // Discard it later.
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.
126 return;
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)
141 if (location < 1)
142 return FALSE;
144 historyEntry = lastselected[--location];
146 return TRUE;
148 BOOL GoForward(CGitHash& historyEntry)
150 if (location >= lastselected.size() - 1)
151 return FALSE;
153 historyEntry = lastselected[++location];
155 return TRUE;
157 private:
158 std::vector<CGitHash> lastselected;
159 size_t location = 0;
162 class CThreadSafePtrArray : private std::vector<GitRevLoglist*>
164 CComCriticalSection* m_critSec = nullptr;
165 public:
166 CThreadSafePtrArray(CComCriticalSection* section) : m_critSec(section)
168 ATLASSERT(m_critSec);
171 GitRevLoglist* SafeGetAt(size_t i)
173 Locker lock(*m_critSec);
174 if (i >= size())
175 return nullptr;
177 return (*this)[i];
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);
189 if (i >= size())
190 return;
192 erase(begin() + i);
195 void SafeAddFront(GitRevLoglist* newElement)
197 Locker lock(*m_critSec);
198 insert(cbegin(), newElement);
201 void SafeRemoveAll()
203 Locker lock(*m_critSec);
204 clear();
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[];
219 class IAsyncDiffCB
223 class CGitLogListBase : public CHintCtrl<CResizableColumnsListCtrl<CListCtrl>>, public IAsyncDiffCB
225 DECLARE_DYNAMIC(CGitLogListBase)
227 public:
228 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;
238 else
239 m_bShowBugtraqColumn = false;
242 void ResetWcRev(bool refresh = false)
244 if (GetSafeHwnd())
246 CWnd* pParent = GetParent();
247 if (pParent && pParent->GetSafeHwnd())
248 pParent->SendMessage(LOGLIST_RESET_WCREV);
250 m_wcRev.Clear();
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;
264 protected:
265 void hideFromContextMenu(unsigned __int64 hideMask, bool exclusivelyShow);
266 public:
267 BOOL m_IsRebaseReplaceGraph = FALSE;
268 BOOL m_bNoHightlightHead = FALSE;
270 protected:
271 void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
273 public:
274 BOOL m_bShowBugtraqColumn = FALSE;
275 public:
276 bool m_bIsCherryPick = false;
277 unsigned __int64 m_ContextMenuMask = UINT64_MAX;
279 bool m_hasWC = true;
280 bool m_bShowWC = false;
281 protected:
282 GitRevLoglist m_wcRev;
283 public:
284 static volatile LONG s_bThreadRunning;
285 protected:
286 CLogCache m_LogCache;
288 public:
289 CString m_sRange;
290 // don't forget to bump BLAME_COLUMN_VERSION in GitStatusListCtrlHelpers.cpp if you change columns
291 enum
293 LOGLIST_GRAPH,
294 LOGLIST_REBASE,
295 LOGLIST_ID,
296 LOGLIST_HASH,
297 LOGLIST_ACTION,
298 LOGLIST_MESSAGE,
299 LOGLIST_AUTHOR,
300 LOGLIST_DATE,
301 LOGLIST_EMAIL,
302 LOGLIST_COMMIT_NAME,
303 LOGLIST_COMMIT_EMAIL,
304 LOGLIST_COMMIT_DATE,
305 LOGLIST_BUG,
306 LOGLIST_SVNREV,
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,
326 enum
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
330 ID_SAVEAS,
331 ID_COMPARETWO, // compare two revisions
332 ID_REVERTREV,
333 ID_MERGEREV,
334 ID_GNUDIFF1, // compare with WC, unified
335 ID_GNUDIFF2, // compare two revisions, unified
336 ID_FINDENTRY,
337 ID_OPEN,
338 ID_BLAME,
339 ID_REPOBROWSE,
340 ID_LOG,
341 ID_EDITNOTE,
342 ID_DIFF,
343 ID_OPENWITH,
344 ID_COPYCLIPBOARD,
345 ID_REVERTTOREV,
346 ID_BLAMECOMPARE,
347 ID_BLAMEDIFF,
348 ID_VIEWREV,
349 ID_VIEWPATHREV,
350 ID_EXPORT,
351 ID_COMPAREWITHPREVIOUS,
352 ID_BLAMEPREVIOUS,
353 ID_CHERRY_PICK,
354 ID_CREATE_BRANCH,
355 ID_CREATE_TAG,
356 ID_SWITCHTOREV,
357 ID_SWITCHBRANCH,
358 ID_RESET,
359 ID_REBASE_PICK,
360 ID_REBASE_EDIT,
361 ID_REBASE_SQUASH,
362 ID_REBASE_SKIP,
363 ID_COMBINE_COMMIT,
364 ID_STASH_SAVE,
365 ID_STASH_LIST,
366 ID_STASH_POP,
367 ID_REFLOG_STASH_APPLY,
368 ID_REFLOG_DEL,
369 ID_REBASE_TO_VERSION,
370 ID_CREATE_PATCH,
371 ID_DELETE,
372 ID_COMMIT,
373 ID_PUSH,
374 ID_PULL,
375 ID_FETCH,
376 ID_SHOWBRANCHES,
377 ID_BISECTSTART,
378 ID_LOG_VIEWRANGE,
379 ID_LOG_VIEWRANGE_REVERSE,
380 ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE,
381 ID_MERGE_ABORT,
382 ID_CLEANUP,
383 ID_SUBMODULE_UPDATE,
384 ID_BISECTGOOD,
385 ID_BISECTBAD,
386 ID_BISECTRESET,
387 ID_BISECTSKIP,
388 ID_SVNDCOMMIT,
389 ID_COMPARETWOCOMMITCHANGES,
390 ID_TOGGLE_ROLLUP,
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,
402 enum FilterShow
404 FILTERSHOW_REFS = 1,
405 FILTERSHOW_MERGEPOINTS = 2,
406 FILTERSHOW_ANYCOMMIT = 4,
407 FILTERSHOW_ALL = FILTERSHOW_ANYCOMMIT | FILTERSHOW_REFS | FILTERSHOW_MERGEPOINTS
409 enum : unsigned int
411 // For Rebase only
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();
428 protected:
429 int BeginFetchLog();
430 HWND GetParentHWND();
431 public:
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);
434 protected:
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);
440 public:
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;
447 void OnFind();
449 protected:
450 static const UINT m_ScrollToMessage;
451 public:
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);
459 void Clear();
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>();
475 CTGitPath m_Path;
476 int m_ShowMask = 0;
477 CGitHash m_lastSelectedHash;
478 SelectionHistory m_selectionHistory;
479 CGitHash m_highlight;
480 int m_ShowRefMask = LOGLIST_SHOWALLREFS;
482 protected:
483 CGit::SubmoduleInfo m_submoduleInfo;
485 public:
486 void GetTimeRange(CTime &oldest,CTime &latest);
487 protected:
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;
490 void ReloadHashMap()
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);
503 ExitProcess(1);
506 m_wcRev.m_ParentHash.clear();
507 m_wcRev.m_ParentHash.push_back(m_HeadHash);
509 FetchRemoteList();
510 FetchTrackingBranchList();
512 g_Git.GetSubmodulePointer(m_submoduleInfo);
514 void StartAsyncDiffThread();
515 void StartLoadingThread();
516 public:
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;
530 protected:
531 bool IsInWorkingThread()
533 return (AfxGetThread() == m_LoadingThread);
535 public:
536 void SetRange(const CString& range)
538 Locker lock(m_critSec);
539 m_sRange = range;
542 CString GetRange() const { return m_sRange; }
544 int m_nSearchIndex = 0;
545 protected:
546 volatile LONG m_bExitThread = FALSE;
547 CWinThread* m_LoadingThread = nullptr;
548 public:
549 std::atomic<std::shared_ptr<MAP_HASH_NAME>> m_HashMap = std::make_shared<MAP_HASH_NAME>();
551 protected:
552 std::map<CString, std::pair<CString, CString>> m_TrackingMap;
554 public:
555 void SetStyle();
557 CString m_ColumnRegKey;
559 protected:
560 struct REFLABEL
562 CString name;
563 COLORREF color = 0;
564 CString simplifiedName;
565 CString fullName;
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);
588 UINT LogThread();
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);
617 public:
618 // needs to be called from LogDlg.cpp and FileDiffDlg.cpp
619 static LRESULT DrawListItemWithMatches(CFilterHelper* filter, CListCtrl& listCtrl, NMLVCUSTOMDRAW* pLVCD, CColors& colors);
621 protected:
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
630 int GetHeadIndex();
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;
639 public:
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);
646 return true;
649 return false;
652 protected:
653 static void DiffAsync(GitRevLoglist* rev, IAsyncDiffCB* pdata)
655 auto data = static_cast<CGitLogListBase*>(pdata);
656 if (data->IsCached(rev))
657 return;
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();
672 public:
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)
682 MSG msg;
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);
693 protected:
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;
704 CFont m_Font;
705 CFont m_boldFont;
706 CFont m_FontItalics;
707 CFont m_boldItalicsFont;
709 CRegDWORD m_regMaxBugIDColWidth;
711 CColors m_Colors;
713 CString m_CurrentBranch;
714 CGitHash m_HeadHash;
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];
729 char m_szTip[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);
744 public:
745 void EnableDragnDrop(bool enable) { m_bDragndropEnabled = enable; }