Fixed issue #3668: "Revert to revision" fails for added files
[TortoiseGit.git] / src / TortoiseProc / GitLogListBase.h
bloba8a199d1aac7df4fd1b3e0d5807240648711526e
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2020 - 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 typedef CComCritSecLock<CComCriticalSection> Locker;
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(void)
97 : location(0)
99 lastselected.reserve(HISTORYLENGTH);
101 void Add(CGitHash &hash)
103 if (hash.IsEmpty())
104 return;
106 size_t size = lastselected.size();
108 // re-select last selected commit
109 if (size > 0 && hash == lastselected[size - 1])
111 // reset location
112 if (location != size - 1)
113 location = size - 1;
114 return;
117 // go back and some commit was highlight
118 if (size > 0 && location != size - 1)
120 // Re-select current one, it may be a forked point.
121 if (hash == lastselected[location])
122 // Discard it later.
123 // That is that discarding forward history when a forked entry is really coming.
124 // And user has the chance to Go Forward again in this situation.
125 // IOW, (hash != lastselected[location]) means user wants a forked history,
126 // and this change saves one step from old behavior.
127 return;
129 // Discard forward history if any
130 while (lastselected.size() - 1 > location)
131 lastselected.pop_back();
134 if (lastselected.size() >= HISTORYLENGTH)
135 lastselected.erase(lastselected.cbegin());
137 lastselected.push_back(hash);
138 location = lastselected.size() - 1;
140 BOOL GoBack(CGitHash& historyEntry)
142 if (location < 1)
143 return FALSE;
145 historyEntry = lastselected[--location];
147 return TRUE;
149 BOOL GoForward(CGitHash& historyEntry)
151 if (location >= lastselected.size() - 1)
152 return FALSE;
154 historyEntry = lastselected[++location];
156 return TRUE;
158 private:
159 std::vector<CGitHash> lastselected;
160 size_t location;
163 class CThreadSafePtrArray : public std::vector<GitRevLoglist*>
165 CComCriticalSection *m_critSec;
166 public:
167 CThreadSafePtrArray(CComCriticalSection* section) : m_critSec(section)
169 ATLASSERT(m_critSec);
172 GitRevLoglist* SafeGetAt(size_t i)
174 Locker lock(*m_critSec);
175 if (i >= size())
176 return nullptr;
178 return (*this)[i];
181 void SafeAdd(GitRevLoglist* newElement)
183 Locker lock(*m_critSec);
184 push_back(newElement);
187 void SafeRemoveAt(size_t i)
189 Locker lock(*m_critSec);
190 if (i >= size())
191 return;
193 erase(begin() + i);
196 void SafeAddFront(GitRevLoglist* newElement)
198 Locker lock(*m_critSec);
199 insert(cbegin(), newElement);
202 void SafeRemoveAll()
204 Locker lock(*m_critSec);
205 clear();
209 class IAsyncDiffCB
213 class CGitLogListBase : public CHintCtrl<CResizableColumnsListCtrl<CListCtrl>>, public IAsyncDiffCB
215 DECLARE_DYNAMIC(CGitLogListBase)
217 public:
218 CGitLogListBase();
219 virtual ~CGitLogListBase();
220 ProjectProperties m_ProjectProperties;
222 void UpdateProjectProperties()
224 m_ProjectProperties.ReadProps();
226 if ((!m_ProjectProperties.sUrl.IsEmpty())||(!m_ProjectProperties.sCheckRe.IsEmpty()))
227 m_bShowBugtraqColumn = true;
228 else
229 m_bShowBugtraqColumn = false;
232 void ResetWcRev(bool refresh = false)
234 if (GetSafeHwnd())
236 CWnd* pParent = GetParent();
237 if (pParent && pParent->GetSafeHwnd())
238 pParent->SendMessage(LOGLIST_RESET_WCREV);
240 m_wcRev.Clear();
241 m_wcRev.GetSubject().LoadString(IDS_LOG_WORKINGDIRCHANGES);
242 m_wcRev.m_Mark = L'-';
243 m_wcRev.GetBody().LoadString(IDS_LOG_FETCHINGSTATUS);
244 m_wcRev.GetBody() = L'\n' + m_wcRev.GetBody();
245 m_wcRev.m_CallDiffAsync = DiffAsync;
246 InterlockedExchange(&m_wcRev.m_IsDiffFiles, FALSE);
247 if (refresh && m_bShowWC)
248 m_arShownList[0] = &m_wcRev;
251 volatile LONG m_bNoDispUpdates;
252 BOOL m_IsIDReplaceAction;
253 BOOL m_IsOldFirst;
254 protected:
255 void hideFromContextMenu(unsigned __int64 hideMask, bool exclusivelyShow);
256 public:
257 BOOL m_IsRebaseReplaceGraph;
258 BOOL m_bNoHightlightHead;
260 protected:
261 void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
263 public:
264 BOOL m_bShowBugtraqColumn;
265 protected:
266 BOOL m_bSearchIndex;
267 public:
268 bool m_bIsCherryPick;
269 unsigned __int64 m_ContextMenuMask;
271 bool m_hasWC;
272 bool m_bShowWC;
273 protected:
274 GitRevLoglist m_wcRev;
275 public:
276 static volatile LONG s_bThreadRunning;
277 protected:
278 CLogCache m_LogCache;
280 public:
281 CString m_sRange;
282 // don't forget to bump BLAME_COLUMN_VERSION in GitStatusListCtrlHelpers.cpp if you change columns
283 enum
285 LOGLIST_GRAPH,
286 LOGLIST_REBASE,
287 LOGLIST_ID,
288 LOGLIST_HASH,
289 LOGLIST_ACTION,
290 LOGLIST_MESSAGE,
291 LOGLIST_AUTHOR,
292 LOGLIST_DATE,
293 LOGLIST_EMAIL,
294 LOGLIST_COMMIT_NAME,
295 LOGLIST_COMMIT_EMAIL,
296 LOGLIST_COMMIT_DATE,
297 LOGLIST_BUG,
298 LOGLIST_SVNREV,
299 LOGLIST_MESSAGE_MAX=300,
300 LOGLIST_MESSAGE_MIN=200,
302 GIT_LOG_GRAPH = 1<< LOGLIST_GRAPH,
303 GIT_LOG_REBASE = 1<< LOGLIST_REBASE,
304 GIT_LOG_ID = 1<< LOGLIST_ID,
305 GIT_LOG_HASH = 1<< LOGLIST_HASH,
306 GIT_LOG_ACTIONS = 1<< LOGLIST_ACTION,
307 GIT_LOG_MESSAGE = 1<< LOGLIST_MESSAGE,
308 GIT_LOG_AUTHOR = 1<< LOGLIST_AUTHOR,
309 GIT_LOG_DATE = 1<< LOGLIST_DATE,
310 GIT_LOG_EMAIL = 1<< LOGLIST_EMAIL,
311 GIT_LOG_COMMIT_NAME = 1<< LOGLIST_COMMIT_NAME,
312 GIT_LOG_COMMIT_EMAIL= 1<< LOGLIST_COMMIT_EMAIL,
313 GIT_LOG_COMMIT_DATE = 1<< LOGLIST_COMMIT_DATE,
314 GIT_LOGLIST_BUG = 1<< LOGLIST_BUG,
315 GIT_LOGLIST_SVNREV = 1<< LOGLIST_SVNREV,
318 enum
320 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
321 ID_COMPARE = 1, // compare revision with WC
322 ID_SAVEAS,
323 ID_COMPARETWO, // compare two revisions
324 ID_REVERTREV,
325 ID_MERGEREV,
326 ID_GNUDIFF1, // compare with WC, unified
327 ID_GNUDIFF2, // compare two revisions, unified
328 ID_FINDENTRY,
329 ID_OPEN,
330 ID_BLAME,
331 ID_REPOBROWSE,
332 ID_LOG,
333 ID_EDITNOTE,
334 ID_DIFF,
335 ID_OPENWITH,
336 ID_COPYCLIPBOARD,
337 ID_REVERTTOREV,
338 ID_BLAMECOMPARE,
339 ID_BLAMEDIFF,
340 ID_VIEWREV,
341 ID_VIEWPATHREV,
342 ID_EXPORT,
343 ID_COMPAREWITHPREVIOUS,
344 ID_BLAMEPREVIOUS,
345 ID_CHERRY_PICK,
346 ID_CREATE_BRANCH,
347 ID_CREATE_TAG,
348 ID_SWITCHTOREV,
349 ID_SWITCHBRANCH,
350 ID_RESET,
351 ID_REBASE_PICK,
352 ID_REBASE_EDIT,
353 ID_REBASE_SQUASH,
354 ID_REBASE_SKIP,
355 ID_COMBINE_COMMIT,
356 ID_STASH_SAVE,
357 ID_STASH_LIST,
358 ID_STASH_POP,
359 ID_REFLOG_STASH_APPLY,
360 ID_REFLOG_DEL,
361 ID_REBASE_TO_VERSION,
362 ID_CREATE_PATCH,
363 ID_DELETE,
364 ID_COMMIT,
365 ID_PUSH,
366 ID_PULL,
367 ID_FETCH,
368 ID_SHOWBRANCHES,
369 ID_BISECTSTART,
370 ID_LOG_VIEWRANGE,
371 ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE,
372 ID_MERGE_ABORT,
373 ID_CLEANUP,
374 ID_SUBMODULE_UPDATE,
375 ID_BISECTGOOD,
376 ID_BISECTBAD,
377 ID_BISECTRESET,
378 ID_BISECTSKIP,
379 ID_SVNDCOMMIT,
380 ID_COMPARETWOCOMMITCHANGES,
381 // the following can be >= 64 as those are not used for GetContextMenuBit(), all others must be < 64 in order to fit into __int64
382 ID_COPYCLIPBOARDFULL,
383 ID_COPYCLIPBOARDFULLNOPATHS,
384 ID_COPYCLIPBOARDHASH,
385 ID_COPYCLIPBOARDAUTHORSFULL,
386 ID_COPYCLIPBOARDAUTHORSNAME,
387 ID_COPYCLIPBOARDAUTHORSEMAIL,
388 ID_COPYCLIPBOARDSUBJECTS,
389 ID_COPYCLIPBOARDMESSAGES,
390 ID_COPYCLIPBOARDBRANCHTAG,
392 enum FilterShow
394 FILTERSHOW_REFS = 1,
395 FILTERSHOW_MERGEPOINTS = 2,
396 FILTERSHOW_ANYCOMMIT = 4,
397 FILTERSHOW_ALL = FILTERSHOW_ANYCOMMIT | FILTERSHOW_REFS | FILTERSHOW_MERGEPOINTS
399 enum : unsigned int
401 // For Rebase only
402 LOGACTIONS_REBASE_CURRENT = 0x08000000,
403 LOGACTIONS_REBASE_PICK = 0x04000000,
404 LOGACTIONS_REBASE_SQUASH = 0x02000000,
405 LOGACTIONS_REBASE_EDIT = 0x01000000,
406 LOGACTIONS_REBASE_DONE = 0x00800000,
407 LOGACTIONS_REBASE_SKIP = 0x00400000,
408 LOGACTIONS_REBASE_MASK = 0x0FC00000,
409 LOGACTIONS_REBASE_MODE_MASK = 0x07C00000,
411 static_assert(ID_COMPARETWOCOMMITCHANGES < 64 && ID_COPYCLIPBOARDFULL <= 64, "IDs must be <64 in order to be usable in a bitfield");
412 static inline unsigned __int64 GetContextMenuBit(int i) { return unsigned __int64(0x1) << i; }
413 static CString GetRebaseActionName(int action);
414 void InsertGitColumn();
415 virtual void CopySelectionToClipBoard(int toCopy = ID_COPYCLIPBOARDFULL);
416 void DiffSelectedRevWithPrevious();
417 bool IsSelectionContinuous();
418 protected:
419 int BeginFetchLog();
420 HWND GetParentHWND();
421 public:
422 int FillGitLog(CTGitPath* path, CString* range = nullptr, int infomask = CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
423 int FillGitLog(std::unordered_set<CGitHash>& hashes);
424 protected:
425 CString MessageDisplayStr(GitRev* pLogEntry);
426 bool ShouldShowFilter(GitRevLoglist* pRev, const std::unordered_map<CGitHash, std::unordered_set<CGitHash>>& commitChildren, const MAP_HASH_NAME& hashMap);
427 public:
428 void ShowGraphColumn(bool bShow);
429 CString GetTagInfo(GitRev* pLogEntry) const;
430 CString GetTagInfo(const STRING_VECTOR& refs) const;
432 CFindDlg *m_pFindDialog;
433 static const UINT m_FindDialogMessage;
434 void OnFind();
436 protected:
437 static const UINT m_ScrollToMessage;
438 public:
439 static const UINT m_ScrollToRef;
440 static const UINT m_RebaseActionMessage;
441 static const UINT LOGLIST_RESET_WCREV;
443 void FetchLogAsync(void* data = nullptr);
444 CThreadSafePtrArray m_arShownList;
445 void Refresh(BOOL IsCleanFilter=TRUE);
446 void Clear();
448 FilterShow m_ShowFilter;
449 CLogDataVector m_logEntries;
451 std::shared_ptr<CLogDlgFilter> m_LogFilter;
452 CFilterData m_Filter;
454 CTGitPath m_Path;
455 int m_ShowMask;
456 CGitHash m_lastSelectedHash;
457 SelectionHistory m_selectionHistory;
458 CGitHash m_highlight;
459 int m_ShowRefMask;
461 protected:
462 CGitHash m_superProjectHash;
464 public:
465 void GetTimeRange(CTime &oldest,CTime &latest);
466 protected:
467 virtual void GetParentHashes(GitRev* pRev, GIT_REV_LIST& parentHash);
468 virtual void ContextMenuAction(int cmd, int FirstSelect, int LastSelect, CMenu* menu, const MAP_HASH_NAME& hashMap) = 0;
469 void UpdateSubmodulePointer()
471 m_superProjectHash.Empty();
472 if (CRegDWORD(L"Software\\TortoiseGit\\LogShowSuperProjectSubmodulePointer", TRUE) != TRUE)
473 return;
474 m_superProjectHash = g_Git.GetSubmodulePointer();
476 void ReloadHashMap()
478 m_RefLabelPosMap.clear();
479 auto newHashMap = std::make_shared<MAP_HASH_NAME>();
480 if (g_Git.GetMapHashToFriendName(*newHashMap.get()))
481 MessageBox(g_Git.GetGitLastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
482 m_HashMap = newHashMap;
484 m_CurrentBranch=g_Git.GetCurrentBranch();
486 if (g_Git.GetHash(m_HeadHash, L"HEAD"))
488 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash. Quitting..."), L"TortoiseGit", MB_ICONERROR);
489 ExitProcess(1);
492 m_wcRev.m_ParentHash.clear();
493 m_wcRev.m_ParentHash.push_back(m_HeadHash);
495 FetchRemoteList();
496 FetchTrackingBranchList();
498 UpdateSubmodulePointer();
500 void StartAsyncDiffThread();
501 void StartLoadingThread();
502 public:
503 void SafeTerminateThread()
505 if (m_LoadingThread && InterlockedExchange(&m_bExitThread, TRUE) == FALSE)
507 DWORD ret = WAIT_TIMEOUT;
508 for (int i = 0; i < 200 && s_bThreadRunning; ++i)
509 ret =::WaitForSingleObject(m_LoadingThread->m_hThread, 100);
510 if (ret == WAIT_TIMEOUT && s_bThreadRunning)
511 ::TerminateThread(m_LoadingThread, 0);
512 delete m_LoadingThread;
513 m_LoadingThread = nullptr;
516 protected:
517 bool IsInWorkingThread()
519 return (AfxGetThread() == m_LoadingThread);
521 public:
522 void SetRange(const CString& range)
524 Locker lock(m_critSec);
525 m_sRange = range;
528 CString GetRange() const { return m_sRange; }
530 int m_nSearchIndex;
531 protected:
532 volatile LONG m_bExitThread;
533 CWinThread* m_LoadingThread;
534 public:
535 std::shared_ptr<MAP_HASH_NAME> m_HashMap;
536 protected:
537 std::map<CString, std::pair<CString, CString>> m_TrackingMap;
539 public:
540 void SetStyle();
542 CString m_ColumnRegKey;
544 protected:
545 typedef struct {
546 CString name;
547 COLORREF color;
548 CString simplifiedName;
549 CString fullName;
550 bool singleRemote;
551 bool hasTracking;
552 bool sameName;
553 CGit::REF_TYPE refType;
554 } REFLABEL;
556 DECLARE_MESSAGE_MAP()
557 afx_msg void OnDestroy();
558 virtual afx_msg void OnNMCustomdrawLoglist(NMHDR* pNMHDR, LRESULT* pResult);
559 virtual afx_msg void OnLvnGetdispinfoLoglist(NMHDR* pNMHDR, LRESULT* pResult);
560 afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam);
561 afx_msg LRESULT OnScrollToMessage(WPARAM wParam, LPARAM lParam);
562 afx_msg LRESULT OnScrollToRef(WPARAM wParam, LPARAM lParam);
563 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
564 afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
565 afx_msg LRESULT OnLoad(WPARAM wParam, LPARAM lParam);
566 virtual void OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult);
567 afx_msg void OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult);
568 void PreSubclassWindow() override;
569 virtual BOOL PreTranslateMessage(MSG* pMsg) override;
570 virtual ULONG GetGestureStatus(CPoint ptTouch) override;
571 static UINT LogThreadEntry(LPVOID pVoid);
572 UINT LogThread();
573 bool IsOnStash(int index);
574 bool IsStash(const GitRev * pSelLogEntry);
575 bool IsBisect(const GitRev * pSelLogEntry);
576 void FetchRemoteList();
577 void FetchTrackingBranchList();
579 virtual afx_msg BOOL OnToolTipText(UINT id, NMHDR* pNMHDR, LRESULT* pResult);
580 virtual INT_PTR OnToolHitTest(CPoint point, TOOLINFO* pTI) const override;
581 CString GetToolTipText(int nItem, int nSubItem);
583 /** Checks whether a referenfe label is under pt and returns the index/type
584 * pLogEntry IN: the entry of commit
585 * pt IN: the mouse position in client coordinate
586 * type IN: give the specific reference type, then check if it is the same reference type.
587 * OUT: give CGit::REF_TYPE::UNKNOWN for getting the real type it is.
588 * pShortname OUT: the short name of that reference label
589 * pIndex OUT: the index value of label of that entry
591 bool IsMouseOnRefLabel(const GitRevLoglist* pLogEntry, const POINT& pt, CGit::REF_TYPE& type, const MAP_HASH_NAME& hashMap, CString* pShortname = nullptr, size_t* pIndex = nullptr);
592 bool IsMouseOnRefLabelFromPopupMenu(const GitRevLoglist* pLogEntry, const CPoint& pt, CGit::REF_TYPE& type, const MAP_HASH_NAME& hashMap, CString* pShortname = nullptr, size_t* pIndex = nullptr);
594 void FillBackGround(HDC hdc, DWORD_PTR Index, CRect &rect);
595 void DrawTagBranchMessage(NMLVCUSTOMDRAW* pLVCD, const CRect& rect, INT_PTR index, const std::vector<REFLABEL>& refList);
596 void DrawTagBranch(HDC hdc, CDC& W_Dc, HTHEME hTheme, const CRect& rect, CRect& rt, LVITEM& rItem, GitRevLoglist* data, const std::vector<REFLABEL>& refList);
597 void DrawGraph(HDC,CRect &rect,INT_PTR index);
598 bool DrawListItemWithMatchesIfEnabled(std::shared_ptr<CLogDlgFilter> filter, DWORD selectedFilter, NMLVCUSTOMDRAW* pLVCD, LRESULT* pResult);
599 static void DrawListItemWithMatchesRect(NMLVCUSTOMDRAW* pLVCD, const std::vector<CHARRANGE>& ranges, CRect rect, const CString& text, CColors& colors, HTHEME hTheme = nullptr, int txtState = 0);
601 public:
602 // needs to be called from LogDlg.cpp and FileDiffDlg.cpp
603 static LRESULT DrawListItemWithMatches(CFilterHelper* filter, CListCtrl& listCtrl, NMLVCUSTOMDRAW* pLVCD, CColors& colors);
605 protected:
606 void paintGraphLane(HDC hdc,int laneHeight, int type, int x1, int x2,
607 const COLORREF& col,const COLORREF& activeColor, int top) ;
608 void DrawLine(HDC hdc, int x1, int y1, int x2, int y2){ ::MoveToEx(hdc, x1, y1, nullptr); ::LineTo(hdc, x2, y2); }
610 * Save column widths to the registry
612 void SaveColumnWidths() override; // save col widths to the registry
614 int GetHeadIndex();
616 std::vector<GitRevLoglist*> m_AsynDiffList;
617 CComAutoCriticalSection m_AsynDiffListLock;
618 HANDLE m_AsyncDiffEvent;
619 volatile LONG m_AsyncThreadExit;
620 CWinThread* m_DiffingThread;
621 volatile LONG m_AsyncThreadRunning;
623 public:
624 bool IsCached(GitRevLoglist* rev)
626 ULONGLONG offset = m_LogCache.GetOffset(rev->m_CommitHash);
627 if (offset && !m_LogCache.LoadOneItem(*rev, offset))
629 InterlockedExchange(&rev->m_IsDiffFiles, TRUE);
630 return true;
633 return false;
636 protected:
637 static void DiffAsync(GitRevLoglist* rev, IAsyncDiffCB* pdata)
639 auto data = static_cast<CGitLogListBase*>(pdata);
640 if (data->IsCached(rev))
641 return;
643 data->m_AsynDiffListLock.Lock();
644 data->m_AsynDiffList.push_back(rev);
645 data->m_AsynDiffListLock.Unlock();
646 ::SetEvent(data->m_AsyncDiffEvent);
649 static UINT AsyncThread(LPVOID data)
651 return reinterpret_cast<CGitLogListBase*>(data)->AsyncDiffThread();
654 int AsyncDiffThread();
656 public:
657 void SafeTerminateAsyncDiffThread()
659 if (m_DiffingThread && InterlockedExchange(&m_AsyncThreadExit, TRUE) == FALSE)
661 ::SetEvent(m_AsyncDiffEvent);
662 DWORD ret = WAIT_TIMEOUT;
663 // do not block here, but process messages and ask until the thread ends
664 while (ret == WAIT_TIMEOUT && m_AsyncThreadRunning)
666 MSG msg;
667 if (::PeekMessage(&msg, nullptr, 0,0, PM_NOREMOVE))
668 AfxGetThread()->PumpMessage(); // process messages, so that GetTopIndex and so on in the thread work
669 ret = ::WaitForSingleObject(m_DiffingThread->m_hThread, 100);
671 delete m_DiffingThread;
672 m_DiffingThread = nullptr;
673 InterlockedExchange(&m_AsyncThreadExit, FALSE);
677 protected:
678 CComAutoCriticalSection m_critSec;
680 CAutoIcon m_hModifiedIcon;
681 CAutoIcon m_hReplacedIcon;
682 CAutoIcon m_hConflictedIcon;
683 CAutoIcon m_hAddedIcon;
684 CAutoIcon m_hDeletedIcon;
685 CAutoIcon m_hFetchIcon;
687 CFont m_Font;
688 CFont m_boldFont;
689 CFont m_FontItalics;
690 CFont m_boldItalicsFont;
692 CRegDWORD m_regMaxBugIDColWidth;
694 void *m_ProcData;
696 CColors m_Colors;
698 CString m_CurrentBranch;
699 CGitHash m_HeadHash;
701 DWORD m_LineWidth;
702 DWORD m_NodeSize;
703 DWORD m_DateFormat; // DATE_SHORTDATE or DATE_LONGDATE
704 bool m_bRelativeTimes; // Show relative times
705 GIT_LOG m_DllGitLog;
706 CString m_SingleRemote;
707 bool m_bTagsBranchesOnRightSide;
708 bool m_bFullCommitMessageOnLogLine;
709 bool m_bSymbolizeRefNames;
710 bool m_bIncludeBoundaryCommits;
712 DWORD m_dwDefaultColumns;
713 TCHAR m_wszTip[8192];
714 char m_szTip[8192];
715 std::map<CString, CRect> m_RefLabelPosMap; // ref name vs. label position
716 int m_OldTopIndex;
718 bool m_bDragndropEnabled;
719 BOOL m_bDragging;
720 int m_nDropIndex;
721 int m_nDropMarkerLast;
722 int m_nDropMarkerLastHot;
723 afx_msg void OnMouseMove(UINT nFlags, CPoint point);
724 afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
725 afx_msg void OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult);
726 void DrawDropInsertMarker(int nIndex);
727 void DrawDropInsertMarkerLine(int nIndex);
729 public:
730 void EnableDragnDrop(bool enable) { m_bDragndropEnabled = enable; }