Fixed issue #3304: Double click on stash list item does nothing, show log instead
[TortoiseGit.git] / src / TortoiseProc / GitLogListBase.h
blob75497c999a0c5edb748c9493bbf5f84bec3ef1b4
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2018 - 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 template < typename Cont, typename Pred>
41 void for_each(Cont& c, Pred&& p)
43 std::for_each(cbegin(c), cend(c), p);
46 template <typename Cont, typename Pred>
47 auto find_if(Cont& c, Pred&& p)
49 return std::find_if(cbegin(c), cend(c), p);
52 // CGitLogList
53 #define ICONITEMBORDER 5
55 #define GITLOG_START 0
56 #define GITLOG_START_ALL 1
57 #define GITLOG_END 100
59 #define LOGFILTER_ALL 0xFFFF
60 #define LOGFILTER_TOGGLE 0x8000
61 #define LOGFILTER_MESSAGES 0x0001
62 #define LOGFILTER_PATHS 0x0002
63 #define LOGFILTER_AUTHORS 0x0004
64 #define LOGFILTER_REVS 0x0008
65 #define LOGFILTER_REGEX 0x0010
66 #define LOGFILTER_BUGID 0x0020
67 #define LOGFILTER_SUBJECT 0x0040
68 #define LOGFILTER_REFNAME 0x0080
69 #define LOGFILTER_EMAILS 0x0100
70 #define LOGFILTER_NOTES 0x0200
71 #define LOGFILTER_ANNOTATEDTAG 0x0400
72 #define LOGFILTER_CASE 0x0800
74 #define LOGLIST_SHOWNOTHING 0x0000
75 #define LOGLIST_SHOWLOCALBRANCHES 0x0001
76 #define LOGLIST_SHOWREMOTEBRANCHES 0x0002
77 #define LOGLIST_SHOWTAGS 0x0004
78 #define LOGLIST_SHOWSTASH 0x0008
79 #define LOGLIST_SHOWBISECT 0x0010
80 #define LOGLIST_SHOWOTHERREFS 0x0020
81 #define LOGLIST_SHOWALLREFS 0xFFFF
83 //typedef void CALLBACK_PROCESS(void * data, int progress);
84 #define MSG_LOADED (WM_USER+110)
85 #define MSG_LOAD_PERCENTAGE (WM_USER+111)
86 #define MSG_REFLOG_CHANGED (WM_USER+112)
87 #define MSG_FETCHED_DIFF (WM_USER+113)
88 #define MSG_COMMITS_REORDERED (WM_USER+114)
90 class SelectionHistory
92 #define HISTORYLENGTH 50
93 public:
94 SelectionHistory(void)
95 : location(0)
97 lastselected.reserve(HISTORYLENGTH);
99 void Add(CGitHash &hash)
101 if (hash.IsEmpty())
102 return;
104 size_t size = lastselected.size();
106 // re-select last selected commit
107 if (size > 0 && hash == lastselected[size - 1])
109 // reset location
110 if (location != size - 1)
111 location = size - 1;
112 return;
115 // go back and some commit was highlight
116 if (size > 0 && location != size - 1)
118 // Re-select current one, it may be a forked point.
119 if (hash == lastselected[location])
120 // Discard it later.
121 // That is that discarding forward history when a forked entry is really coming.
122 // And user has the chance to Go Forward again in this situation.
123 // IOW, (hash != lastselected[location]) means user wants a forked history,
124 // and this change saves one step from old behavior.
125 return;
127 // Discard forward history if any
128 while (lastselected.size() - 1 > location)
129 lastselected.pop_back();
132 if (lastselected.size() >= HISTORYLENGTH)
133 lastselected.erase(lastselected.cbegin());
135 lastselected.push_back(hash);
136 location = lastselected.size() - 1;
138 BOOL GoBack(CGitHash& historyEntry)
140 if (location < 1)
141 return FALSE;
143 historyEntry = lastselected[--location];
145 return TRUE;
147 BOOL GoForward(CGitHash& historyEntry)
149 if (location >= lastselected.size() - 1)
150 return FALSE;
152 historyEntry = lastselected[++location];
154 return TRUE;
156 private:
157 std::vector<CGitHash> lastselected;
158 size_t location;
161 class CThreadSafePtrArray : public std::vector<GitRevLoglist*>
163 typedef CComCritSecLock<CComCriticalSection> Locker;
164 CComCriticalSection *m_critSec;
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();
208 class IAsyncDiffCB
212 class CGitLogListBase : public CHintCtrl<CResizableColumnsListCtrl<CListCtrl>>, public IAsyncDiffCB
214 DECLARE_DYNAMIC(CGitLogListBase)
216 public:
217 CGitLogListBase();
218 virtual ~CGitLogListBase();
219 ProjectProperties m_ProjectProperties;
221 void UpdateProjectProperties()
223 m_ProjectProperties.ReadProps();
225 if ((!m_ProjectProperties.sUrl.IsEmpty())||(!m_ProjectProperties.sCheckRe.IsEmpty()))
226 m_bShowBugtraqColumn = true;
227 else
228 m_bShowBugtraqColumn = false;
231 void ResetWcRev(bool refresh = false)
233 if (GetSafeHwnd())
235 CWnd* pParent = GetParent();
236 if (pParent && pParent->GetSafeHwnd())
237 pParent->SendMessage(LOGLIST_RESET_WCREV);
239 m_wcRev.Clear();
240 m_wcRev.GetSubject().LoadString(IDS_LOG_WORKINGDIRCHANGES);
241 m_wcRev.m_Mark = L'-';
242 m_wcRev.GetBody().LoadString(IDS_LOG_FETCHINGSTATUS);
243 m_wcRev.GetBody() = L'\n' + m_wcRev.GetBody();
244 m_wcRev.m_CallDiffAsync = DiffAsync;
245 InterlockedExchange(&m_wcRev.m_IsDiffFiles, FALSE);
246 if (refresh && m_bShowWC)
247 m_arShownList[0] = &m_wcRev;
250 volatile LONG m_bNoDispUpdates;
251 BOOL m_IsIDReplaceAction;
252 BOOL m_IsOldFirst;
253 protected:
254 void hideFromContextMenu(unsigned __int64 hideMask, bool exclusivelyShow);
255 public:
256 BOOL m_IsRebaseReplaceGraph;
257 BOOL m_bNoHightlightHead;
259 protected:
260 void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
262 public:
263 BOOL m_bShowBugtraqColumn;
264 protected:
265 BOOL m_bSearchIndex;
266 public:
267 bool m_bIsCherryPick;
268 unsigned __int64 m_ContextMenuMask;
270 bool m_hasWC;
271 bool m_bShowWC;
272 protected:
273 GitRevLoglist m_wcRev;
274 public:
275 volatile LONG m_bThreadRunning;
276 protected:
277 CLogCache m_LogCache;
279 public:
280 CString m_sRange;
281 // don't forget to bump BLAME_COLUMN_VERSION in GitStatusListCtrlHelpers.cpp if you change columns
282 enum
284 LOGLIST_GRAPH,
285 LOGLIST_REBASE,
286 LOGLIST_ID,
287 LOGLIST_HASH,
288 LOGLIST_ACTION,
289 LOGLIST_MESSAGE,
290 LOGLIST_AUTHOR,
291 LOGLIST_DATE,
292 LOGLIST_EMAIL,
293 LOGLIST_COMMIT_NAME,
294 LOGLIST_COMMIT_EMAIL,
295 LOGLIST_COMMIT_DATE,
296 LOGLIST_BUG,
297 LOGLIST_SVNREV,
298 LOGLIST_MESSAGE_MAX=300,
299 LOGLIST_MESSAGE_MIN=200,
301 GIT_LOG_GRAPH = 1<< LOGLIST_GRAPH,
302 GIT_LOG_REBASE = 1<< LOGLIST_REBASE,
303 GIT_LOG_ID = 1<< LOGLIST_ID,
304 GIT_LOG_HASH = 1<< LOGLIST_HASH,
305 GIT_LOG_ACTIONS = 1<< LOGLIST_ACTION,
306 GIT_LOG_MESSAGE = 1<< LOGLIST_MESSAGE,
307 GIT_LOG_AUTHOR = 1<< LOGLIST_AUTHOR,
308 GIT_LOG_DATE = 1<< LOGLIST_DATE,
309 GIT_LOG_EMAIL = 1<< LOGLIST_EMAIL,
310 GIT_LOG_COMMIT_NAME = 1<< LOGLIST_COMMIT_NAME,
311 GIT_LOG_COMMIT_EMAIL= 1<< LOGLIST_COMMIT_EMAIL,
312 GIT_LOG_COMMIT_DATE = 1<< LOGLIST_COMMIT_DATE,
313 GIT_LOGLIST_BUG = 1<< LOGLIST_BUG,
314 GIT_LOGLIST_SVNREV = 1<< LOGLIST_SVNREV,
317 enum
319 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
320 ID_COMPARE = 1, // compare revision with WC
321 ID_SAVEAS,
322 ID_COMPARETWO, // compare two revisions
323 ID_REVERTREV,
324 ID_MERGEREV,
325 ID_GNUDIFF1, // compare with WC, unified
326 ID_GNUDIFF2, // compare two revisions, unified
327 ID_FINDENTRY,
328 ID_OPEN,
329 ID_BLAME,
330 ID_REPOBROWSE,
331 ID_LOG,
332 ID_EDITNOTE,
333 ID_DIFF,
334 ID_OPENWITH,
335 ID_COPYCLIPBOARD,
336 ID_REVERTTOREV,
337 ID_BLAMECOMPARE,
338 ID_BLAMEDIFF,
339 ID_VIEWREV,
340 ID_VIEWPATHREV,
341 ID_EXPORT,
342 ID_COMPAREWITHPREVIOUS,
343 ID_BLAMEPREVIOUS,
344 ID_CHERRY_PICK,
345 ID_CREATE_BRANCH,
346 ID_CREATE_TAG,
347 ID_SWITCHTOREV,
348 ID_SWITCHBRANCH,
349 ID_RESET,
350 ID_REBASE_PICK,
351 ID_REBASE_EDIT,
352 ID_REBASE_SQUASH,
353 ID_REBASE_SKIP,
354 ID_COMBINE_COMMIT,
355 ID_STASH_SAVE,
356 ID_STASH_LIST,
357 ID_STASH_POP,
358 ID_REFLOG_STASH_APPLY,
359 ID_REFLOG_DEL,
360 ID_REBASE_TO_VERSION,
361 ID_CREATE_PATCH,
362 ID_DELETE,
363 ID_COMMIT,
364 ID_PUSH,
365 ID_PULL,
366 ID_FETCH,
367 ID_SHOWBRANCHES,
368 ID_BISECTSTART,
369 ID_LOG_VIEWRANGE,
370 ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE,
371 ID_MERGE_ABORT,
372 ID_CLEANUP,
373 ID_SUBMODULE_UPDATE,
374 ID_BISECTGOOD,
375 ID_BISECTBAD,
376 ID_BISECTRESET,
377 ID_BISECTSKIP,
378 ID_SVNDCOMMIT,
379 ID_COMPARETWOCOMMITCHANGES,
380 // the following can be >= 64 as those are not used for GetContextMenuBit(), all others must be < 64 in order to fit into __int64
381 ID_COPYCLIPBOARDFULL,
382 ID_COPYCLIPBOARDFULLNOPATHS,
383 ID_COPYCLIPBOARDHASH,
384 ID_COPYCLIPBOARDAUTHORSFULL,
385 ID_COPYCLIPBOARDAUTHORSNAME,
386 ID_COPYCLIPBOARDAUTHORSEMAIL,
387 ID_COPYCLIPBOARDSUBJECTS,
388 ID_COPYCLIPBOARDMESSAGES,
389 ID_COPYCLIPBOARDBRANCHTAG,
391 enum FilterShow
393 FILTERSHOW_REFS = 1,
394 FILTERSHOW_MERGEPOINTS = 2,
395 FILTERSHOW_ANYCOMMIT = 4,
396 FILTERSHOW_ALL = FILTERSHOW_ANYCOMMIT | FILTERSHOW_REFS | FILTERSHOW_MERGEPOINTS
398 enum : unsigned int
400 // For Rebase only
401 LOGACTIONS_REBASE_CURRENT = 0x08000000,
402 LOGACTIONS_REBASE_PICK = 0x04000000,
403 LOGACTIONS_REBASE_SQUASH = 0x02000000,
404 LOGACTIONS_REBASE_EDIT = 0x01000000,
405 LOGACTIONS_REBASE_DONE = 0x00800000,
406 LOGACTIONS_REBASE_SKIP = 0x00400000,
407 LOGACTIONS_REBASE_MASK = 0x0FC00000,
408 LOGACTIONS_REBASE_MODE_MASK = 0x07C00000,
410 static_assert(ID_COMPARETWOCOMMITCHANGES < 64 && ID_COPYCLIPBOARDFULL <= 64, "IDs must be <64 in order to be usable in a bitfield");
411 static inline unsigned __int64 GetContextMenuBit(int i){ return ((unsigned __int64 )0x1)<<i ;}
412 static CString GetRebaseActionName(int action);
413 void InsertGitColumn();
414 void CopySelectionToClipBoard(int toCopy = ID_COPYCLIPBOARDFULL);
415 void DiffSelectedRevWithPrevious();
416 bool IsSelectionContinuous();
417 protected:
418 int BeginFetchLog();
419 HWND GetParentHWND();
420 public:
421 int FillGitLog(CTGitPath* path, CString* range = nullptr, int infomask = CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
422 int FillGitLog(std::unordered_set<CGitHash>& hashes);
423 protected:
424 CString MessageDisplayStr(GitRev* pLogEntry);
425 bool ShouldShowFilter(GitRevLoglist* pRev, const std::unordered_map<CGitHash, std::unordered_set<CGitHash>>& commitChildren);
426 public:
427 void ShowGraphColumn(bool bShow);
428 CString GetTagInfo(GitRev* pLogEntry);
430 CFindDlg *m_pFindDialog;
431 static const UINT m_FindDialogMessage;
432 void OnFind();
434 protected:
435 static const UINT m_ScrollToMessage;
436 public:
437 static const UINT m_ScrollToRef;
438 static const UINT m_RebaseActionMessage;
439 static const UINT LOGLIST_RESET_WCREV;
441 void FetchLogAsync(void* data = nullptr);
442 CThreadSafePtrArray m_arShownList;
443 void Refresh(BOOL IsCleanFilter=TRUE);
444 void Clear();
446 FilterShow m_ShowFilter;
447 CLogDataVector m_logEntries;
449 std::shared_ptr<CLogDlgFilter> m_LogFilter;
450 CFilterData m_Filter;
452 CTGitPath m_Path;
453 int m_ShowMask;
454 CGitHash m_lastSelectedHash;
455 SelectionHistory m_selectionHistory;
456 CGitHash m_highlight;
457 int m_ShowRefMask;
459 protected:
460 CGitHash m_superProjectHash;
462 public:
463 void GetTimeRange(CTime &oldest,CTime &latest);
464 protected:
465 virtual void GetParentHashes(GitRev* pRev, GIT_REV_LIST& parentHash);
466 virtual void ContextMenuAction(int cmd,int FirstSelect, int LastSelect, CMenu * menu)=0;
467 void UpdateSubmodulePointer()
469 m_superProjectHash.Empty();
470 if (CRegDWORD(L"Software\\TortoiseGit\\LogShowSuperProjectSubmodulePointer", TRUE) != TRUE)
471 return;
472 if (GitAdminDir::IsBareRepo(g_Git.m_CurrentDir))
473 return;
474 CString superprojectRoot;
475 GitAdminDir::HasAdminDir(g_Git.m_CurrentDir, false, &superprojectRoot);
476 if (superprojectRoot.IsEmpty())
477 return;
479 CAutoRepository repo(superprojectRoot);
480 if (!repo)
481 return;
482 CAutoIndex index;
483 if (git_repository_index(index.GetPointer(), repo))
484 return;
486 CString submodulePath;
487 if (superprojectRoot[superprojectRoot.GetLength() - 1] == L'\\')
488 submodulePath = g_Git.m_CurrentDir.Right(g_Git.m_CurrentDir.GetLength() - superprojectRoot.GetLength());
489 else
490 submodulePath = g_Git.m_CurrentDir.Right(g_Git.m_CurrentDir.GetLength() - superprojectRoot.GetLength() - 1);
491 submodulePath.Replace(L'\\', L'/');
492 const git_index_entry* entry = git_index_get_bypath(index, CUnicodeUtils::GetUTF8(submodulePath), 0);
493 if (!entry)
494 return;
496 m_superProjectHash = entry->id;
498 void ReloadHashMap()
500 m_RefLabelPosMap.clear();
501 m_HashMap.clear();
503 if (g_Git.GetMapHashToFriendName(m_HashMap))
504 MessageBox(g_Git.GetGitLastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
506 m_CurrentBranch=g_Git.GetCurrentBranch();
508 if (g_Git.GetHash(m_HeadHash, L"HEAD"))
510 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash. Quitting..."), L"TortoiseGit", MB_ICONERROR);
511 ExitProcess(1);
514 m_wcRev.m_ParentHash.clear();
515 m_wcRev.m_ParentHash.push_back(m_HeadHash);
517 FetchRemoteList();
518 FetchTrackingBranchList();
520 UpdateSubmodulePointer();
522 void StartAsyncDiffThread();
523 void StartLoadingThread();
524 public:
525 void SafeTerminateThread()
527 if (m_LoadingThread && InterlockedExchange(&m_bExitThread, TRUE) == FALSE)
529 DWORD ret = WAIT_TIMEOUT;
530 for (int i = 0; i < 200 && m_bThreadRunning; ++i)
531 ret =::WaitForSingleObject(m_LoadingThread->m_hThread, 100);
532 if (ret == WAIT_TIMEOUT && m_bThreadRunning)
533 ::TerminateThread(m_LoadingThread, 0);
534 m_LoadingThread = nullptr;
537 protected:
538 bool IsInWorkingThread()
540 return (AfxGetThread() == m_LoadingThread);
542 public:
543 void SetRange(const CString& range)
545 m_sRange = range;
548 CString GetRange() const { return m_sRange; }
550 int m_nSearchIndex;
551 protected:
552 volatile LONG m_bExitThread;
553 CWinThread* m_LoadingThread;
554 public:
555 MAP_HASH_NAME m_HashMap;
556 protected:
557 std::map<CString, std::pair<CString, CString>> m_TrackingMap;
559 public:
560 void SetStyle();
562 CString m_ColumnRegKey;
564 protected:
565 typedef struct {
566 CString name;
567 COLORREF color;
568 CString simplifiedName;
569 CString fullName;
570 bool singleRemote;
571 bool hasTracking;
572 bool sameName;
573 CGit::REF_TYPE refType;
574 } REFLABEL;
576 DECLARE_MESSAGE_MAP()
577 afx_msg void OnDestroy();
578 virtual afx_msg void OnNMCustomdrawLoglist(NMHDR* pNMHDR, LRESULT* pResult);
579 virtual afx_msg void OnLvnGetdispinfoLoglist(NMHDR* pNMHDR, LRESULT* pResult);
580 afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam);
581 afx_msg LRESULT OnScrollToMessage(WPARAM wParam, LPARAM lParam);
582 afx_msg LRESULT OnScrollToRef(WPARAM wParam, LPARAM lParam);
583 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
584 afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
585 afx_msg LRESULT OnLoad(WPARAM wParam, LPARAM lParam);
586 virtual void OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult);
587 afx_msg void OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult);
588 void PreSubclassWindow() override;
589 virtual BOOL PreTranslateMessage(MSG* pMsg) override;
590 virtual ULONG GetGestureStatus(CPoint ptTouch) override;
591 static UINT LogThreadEntry(LPVOID pVoid);
592 UINT LogThread();
593 bool IsOnStash(int index);
594 bool IsStash(const GitRev * pSelLogEntry);
595 bool IsBisect(const GitRev * pSelLogEntry);
596 void FetchRemoteList();
597 void FetchTrackingBranchList();
599 virtual afx_msg BOOL OnToolTipText(UINT id, NMHDR* pNMHDR, LRESULT* pResult);
600 virtual INT_PTR OnToolHitTest(CPoint point, TOOLINFO* pTI) const override;
601 CString GetToolTipText(int nItem, int nSubItem);
603 /** Checks whether a referenfe label is under pt and returns the index/type
604 * pLogEntry IN: the entry of commit
605 * pt IN: the mouse position in client coordinate
606 * type IN: give the specific reference type, then check if it is the same reference type.
607 * OUT: give CGit::REF_TYPE::UNKNOWN for getting the real type it is.
608 * pShortname OUT: the short name of that reference label
609 * pIndex OUT: the index value of label of that entry
611 bool IsMouseOnRefLabel(const GitRevLoglist* pLogEntry, const POINT& pt, CGit::REF_TYPE& type, CString* pShortname = nullptr, size_t* pIndex = nullptr);
612 bool IsMouseOnRefLabelFromPopupMenu(const GitRevLoglist* pLogEntry, const CPoint& pt, CGit::REF_TYPE& type, CString* pShortname = nullptr, size_t* pIndex = nullptr);
614 void FillBackGround(HDC hdc, DWORD_PTR Index, CRect &rect);
615 void DrawTagBranchMessage(NMLVCUSTOMDRAW* pLVCD, CRect& rect, INT_PTR index, std::vector<REFLABEL>& refList);
616 void DrawTagBranch(HDC hdc, CDC& W_Dc, HTHEME hTheme, CRect& rect, CRect& rt, LVITEM& rItem, GitRevLoglist* data, std::vector<REFLABEL>& refList);
617 void DrawGraph(HDC,CRect &rect,INT_PTR index);
618 bool CGitLogListBase::DrawListItemWithMatchesIfEnabled(std::shared_ptr<CLogDlgFilter> filter, DWORD selectedFilter, NMLVCUSTOMDRAW* pLVCD, LRESULT* pResult);
619 void DrawListItemWithMatchesRect(NMLVCUSTOMDRAW* pLVCD, const std::vector<CHARRANGE>& ranges, CRect rect, const CString& text, HTHEME hTheme = nullptr, int txtState = 0);
621 public:
622 // needs to be called from LogDlg.cpp
623 LRESULT DrawListItemWithMatches(CLogDlgFilter* filter, CListCtrl& listCtrl, NMLVCUSTOMDRAW* pLVCD);
625 protected:
626 void paintGraphLane(HDC hdc,int laneHeight, int type, int x1, int x2,
627 const COLORREF& col,const COLORREF& activeColor, int top) ;
628 void DrawLine(HDC hdc, int x1, int y1, int x2, int y2){ ::MoveToEx(hdc, x1, y1, nullptr); ::LineTo(hdc, x2, y2); }
630 * Save column widths to the registry
632 void SaveColumnWidths(); // save col widths to the registry
634 int GetHeadIndex();
636 std::vector<GitRevLoglist*> m_AsynDiffList;
637 CComCriticalSection m_AsynDiffListLock;
638 HANDLE m_AsyncDiffEvent;
639 volatile LONG m_AsyncThreadExit;
640 CWinThread* m_DiffingThread;
641 volatile LONG m_AsyncThreadRunning;
643 static int DiffAsync(GitRevLoglist* rev, IAsyncDiffCB* pdata)
645 auto data = static_cast<CGitLogListBase*>(pdata);
646 ULONGLONG offset = data->m_LogCache.GetOffset(rev->m_CommitHash);
647 if (!offset || data->m_LogCache.LoadOneItem(*rev, offset))
649 data->m_AsynDiffListLock.Lock();
650 data->m_AsynDiffList.push_back(rev);
651 data->m_AsynDiffListLock.Unlock();
652 ::SetEvent(data->m_AsyncDiffEvent);
653 return 0;
656 InterlockedExchange(&rev->m_IsDiffFiles, TRUE);
657 if (!rev->m_IsCommitParsed)
658 return 0;
659 InterlockedExchange(&rev->m_IsFull, TRUE);
660 // we might need to signal that the changed files are now available
661 if (data->GetSelectedCount() == 1)
663 POSITION pos = data->GetFirstSelectedItemPosition();
664 int nItem = data->GetNextSelectedItem(pos);
665 if (nItem >= 0)
667 GitRevLoglist* data2 = data->m_arShownList.SafeGetAt(nItem);
668 if (data2 && data2->m_CommitHash == rev->m_CommitHash)
669 data->GetParent()->PostMessage(WM_COMMAND, MSG_FETCHED_DIFF, 0);
672 return 0;
675 static UINT AsyncThread(LPVOID data)
677 return reinterpret_cast<CGitLogListBase*>(data)->AsyncDiffThread();
680 int AsyncDiffThread();
682 public:
683 void SafeTerminateAsyncDiffThread()
685 if (m_DiffingThread && InterlockedExchange(&m_AsyncThreadExit, TRUE) == FALSE)
687 ::SetEvent(m_AsyncDiffEvent);
688 DWORD ret = WAIT_TIMEOUT;
689 // do not block here, but process messages and ask until the thread ends
690 while (ret == WAIT_TIMEOUT && m_AsyncThreadRunning)
692 MSG msg;
693 if (::PeekMessage(&msg, nullptr, 0,0, PM_NOREMOVE))
694 AfxGetThread()->PumpMessage(); // process messages, so that GetTopIndex and so on in the thread work
695 ret = ::WaitForSingleObject(m_DiffingThread->m_hThread, 100);
697 m_DiffingThread = nullptr;
698 InterlockedExchange(&m_AsyncThreadExit, FALSE);
702 protected:
703 CComCriticalSection m_critSec;
705 HICON m_hModifiedIcon;
706 HICON m_hReplacedIcon;
707 HICON m_hConflictedIcon;
708 HICON m_hAddedIcon;
709 HICON m_hDeletedIcon;
710 HICON m_hFetchIcon;
712 CFont m_boldFont;
713 CFont m_FontItalics;
714 CFont m_boldItalicsFont;
716 CRegDWORD m_regMaxBugIDColWidth;
718 void *m_ProcData;
720 CColors m_Colors;
722 CString m_CurrentBranch;
723 CGitHash m_HeadHash;
725 COLORREF m_LineColors[Lanes::COLORS_NUM];
726 DWORD m_LineWidth;
727 DWORD m_NodeSize;
728 DWORD m_DateFormat; // DATE_SHORTDATE or DATE_LONGDATE
729 bool m_bRelativeTimes; // Show relative times
730 GIT_LOG m_DllGitLog;
731 CString m_SingleRemote;
732 bool m_bTagsBranchesOnRightSide;
733 bool m_bFullCommitMessageOnLogLine;
734 bool m_bSymbolizeRefNames;
735 bool m_bIncludeBoundaryCommits;
737 DWORD m_dwDefaultColumns;
738 TCHAR m_wszTip[8192];
739 char m_szTip[8192];
740 std::map<CString, CRect> m_RefLabelPosMap; // ref name vs. label position
741 int m_OldTopIndex;
743 GIT_MAILMAP m_pMailmap;
745 bool m_bDragndropEnabled;
746 BOOL m_bDragging;
747 int m_nDropIndex;
748 int m_nDropMarkerLast;
749 int m_nDropMarkerLastHot;
750 afx_msg void OnMouseMove(UINT nFlags, CPoint point);
751 afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
752 afx_msg void OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult);
753 void DrawDropInsertMarker(int nIndex);
754 void DrawDropInsertMarkerLine(int nIndex);
756 public:
757 void EnableDragnDrop(bool enable) { m_bDragndropEnabled = enable; }