Extend static functions in CAppUtils with a window handle parameter
[TortoiseGit.git] / src / TortoiseProc / GitLogListBase.h
blob794e3deceae5c9b01417b6352e22bb9bf87cb4d3
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2017 - 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>
39 template < typename Cont, typename Pred>
40 void for_each(Cont& c, Pred&& p)
42 std::for_each(cbegin(c), cend(c), p);
45 template <typename Cont, typename Pred>
46 auto find_if(Cont& c, Pred&& p)
48 return std::find_if(cbegin(c), cend(c), p);
51 // CGitLogList
52 #define ICONITEMBORDER 5
54 #define GITLOG_START 0
55 #define GITLOG_START_ALL 1
56 #define GITLOG_END 100
58 #define LOGFILTER_ALL 0xFFFF
59 #define LOGFILTER_TOGGLE 0x8000
60 #define LOGFILTER_MESSAGES 0x0001
61 #define LOGFILTER_PATHS 0x0002
62 #define LOGFILTER_AUTHORS 0x0004
63 #define LOGFILTER_REVS 0x0008
64 #define LOGFILTER_REGEX 0x0010
65 #define LOGFILTER_BUGID 0x0020
66 #define LOGFILTER_SUBJECT 0x0040
67 #define LOGFILTER_REFNAME 0x0080
68 #define LOGFILTER_EMAILS 0x0100
69 #define LOGFILTER_NOTES 0x0200
70 #define LOGFILTER_ANNOTATEDTAG 0x0400
71 #define LOGFILTER_CASE 0x0800
73 #define LOGLIST_SHOWNOTHING 0x0000
74 #define LOGLIST_SHOWLOCALBRANCHES 0x0001
75 #define LOGLIST_SHOWREMOTEBRANCHES 0x0002
76 #define LOGLIST_SHOWTAGS 0x0004
77 #define LOGLIST_SHOWSTASH 0x0008
78 #define LOGLIST_SHOWBISECT 0x0010
79 #define LOGLIST_SHOWOTHERREFS 0x0020
80 #define LOGLIST_SHOWALLREFS 0xFFFF
82 //typedef void CALLBACK_PROCESS(void * data, int progress);
83 #define MSG_LOADED (WM_USER+110)
84 #define MSG_LOAD_PERCENTAGE (WM_USER+111)
85 #define MSG_REFLOG_CHANGED (WM_USER+112)
86 #define MSG_FETCHED_DIFF (WM_USER+113)
87 #define MSG_COMMITS_REORDERED (WM_USER+114)
89 class SelectionHistory
91 #define HISTORYLENGTH 50
92 public:
93 SelectionHistory(void)
94 : location(0)
96 lastselected.reserve(HISTORYLENGTH);
98 void Add(CGitHash &hash)
100 if (hash.IsEmpty())
101 return;
103 size_t size = lastselected.size();
105 // re-select last selected commit
106 if (size > 0 && hash == lastselected[size - 1])
108 // reset location
109 if (location != size - 1)
110 location = size - 1;
111 return;
114 // go back and some commit was highlight
115 if (size > 0 && location != size - 1)
117 // Re-select current one, it may be a forked point.
118 if (hash == lastselected[location])
119 // Discard it later.
120 // That is that discarding forward history when a forked entry is really coming.
121 // And user has the chance to Go Forward again in this situation.
122 // IOW, (hash != lastselected[location]) means user wants a forked history,
123 // and this change saves one step from old behavior.
124 return;
126 // Discard forward history if any
127 while (lastselected.size() - 1 > location)
128 lastselected.pop_back();
131 if (lastselected.size() >= HISTORYLENGTH)
132 lastselected.erase(lastselected.cbegin());
134 lastselected.push_back(hash);
135 location = lastselected.size() - 1;
137 BOOL GoBack(CGitHash& historyEntry)
139 if (location < 1)
140 return FALSE;
142 historyEntry = lastselected[--location];
144 return TRUE;
146 BOOL GoForward(CGitHash& historyEntry)
148 if (location >= lastselected.size() - 1)
149 return FALSE;
151 historyEntry = lastselected[++location];
153 return TRUE;
155 private:
156 std::vector<CGitHash> lastselected;
157 size_t location;
160 class CThreadSafePtrArray : public std::vector<GitRevLoglist*>
162 typedef CComCritSecLock<CComCriticalSection> Locker;
163 CComCriticalSection *m_critSec;
164 public:
165 CThreadSafePtrArray(CComCriticalSection* section) : m_critSec(section)
167 ATLASSERT(m_critSec);
170 GitRevLoglist* SafeGetAt(size_t i)
172 Locker lock(*m_critSec);
173 if (i >= size())
174 return nullptr;
176 return (*this)[i];
179 void SafeAdd(GitRevLoglist* newElement)
181 Locker lock(*m_critSec);
182 push_back(newElement);
185 void SafeRemoveAt(size_t i)
187 Locker lock(*m_critSec);
188 if (i >= size())
189 return;
191 erase(begin() + i);
194 void SafeAddFront(GitRevLoglist* newElement)
196 Locker lock(*m_critSec);
197 insert(cbegin(), newElement);
200 void SafeRemoveAll()
202 Locker lock(*m_critSec);
203 clear();
207 class IAsyncDiffCB
211 class CGitLogListBase : public CHintCtrl<CResizableColumnsListCtrl<CListCtrl>>, public IAsyncDiffCB
213 DECLARE_DYNAMIC(CGitLogListBase)
215 public:
216 CGitLogListBase();
217 virtual ~CGitLogListBase();
218 ProjectProperties m_ProjectProperties;
220 void UpdateProjectProperties()
222 m_ProjectProperties.ReadProps();
224 if ((!m_ProjectProperties.sUrl.IsEmpty())||(!m_ProjectProperties.sCheckRe.IsEmpty()))
225 m_bShowBugtraqColumn = true;
226 else
227 m_bShowBugtraqColumn = false;
230 void ResetWcRev(bool refresh = false)
232 if (GetSafeHwnd())
234 CWnd* pParent = GetParent();
235 if (pParent && pParent->GetSafeHwnd())
236 pParent->SendMessage(LOGLIST_RESET_WCREV);
238 m_wcRev.Clear();
239 m_wcRev.GetSubject().LoadString(IDS_LOG_WORKINGDIRCHANGES);
240 m_wcRev.m_Mark = L'-';
241 m_wcRev.GetBody().LoadString(IDS_LOG_FETCHINGSTATUS);
242 m_wcRev.GetBody() = L'\n' + m_wcRev.GetBody();
243 m_wcRev.m_CallDiffAsync = DiffAsync;
244 InterlockedExchange(&m_wcRev.m_IsDiffFiles, FALSE);
245 if (refresh && m_bShowWC)
246 m_arShownList[0] = &m_wcRev;
249 volatile LONG m_bNoDispUpdates;
250 BOOL m_IsIDReplaceAction;
251 BOOL m_IsOldFirst;
252 protected:
253 void hideFromContextMenu(unsigned __int64 hideMask, bool exclusivelyShow);
254 public:
255 BOOL m_IsRebaseReplaceGraph;
256 BOOL m_bNoHightlightHead;
258 protected:
259 void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
261 BOOL m_bStrictStopped;
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 IsMatchFilter(bool bRegex, GitRevLoglist* pRev, std::wregex& pat);
426 bool ShouldShowFilter(GitRevLoglist* pRev, const std::unordered_map<CGitHash, std::unordered_set<CGitHash>>& commitChildren);
427 public:
428 void ShowGraphColumn(bool bShow);
429 CString GetTagInfo(GitRev* pLogEntry);
431 CFindDlg *m_pFindDialog;
432 static const UINT m_FindDialogMessage;
433 void OnFind();
435 protected:
436 static const UINT m_ScrollToMessage;
437 public:
438 static const UINT m_ScrollToRef;
439 static const UINT m_RebaseActionMessage;
440 static const UINT LOGLIST_RESET_WCREV;
442 inline int ShownCountWithStopped() const { return (int)m_arShownList.size() + (m_bStrictStopped ? 1 : 0); }
443 void FetchLogAsync(void* data = nullptr);
444 CThreadSafePtrArray m_arShownList;
445 void Refresh(BOOL IsCleanFilter=TRUE);
446 void RecalculateShownList(CThreadSafePtrArray * pShownlist);
447 void Clear();
449 DWORD m_SelectedFilters;
450 FilterShow m_ShowFilter;
451 bool m_bFilterWithRegex;
452 bool m_bFilterCaseSensitively;
453 CLogDataVector m_logEntries;
454 void RemoveFilter();
455 void StartFilter();
456 bool ValidateRegexp(LPCTSTR regexp_str, std::wregex& pat, bool bMatchCase = false );
457 CString m_sFilterText;
459 CFilterData m_Filter;
461 CTGitPath m_Path;
462 int m_ShowMask;
463 CGitHash m_lastSelectedHash;
464 SelectionHistory m_selectionHistory;
465 CGitHash m_highlight;
466 int m_ShowRefMask;
468 protected:
469 CGitHash m_superProjectHash;
471 public:
472 void GetTimeRange(CTime &oldest,CTime &latest);
473 protected:
474 virtual void GetParentHashes(GitRev* pRev, GIT_REV_LIST& parentHash);
475 virtual void ContextMenuAction(int cmd,int FirstSelect, int LastSelect, CMenu * menu)=0;
476 void UpdateSubmodulePointer()
478 m_superProjectHash.Empty();
479 if (CRegDWORD(L"Software\\TortoiseGit\\LogShowSuperProjectSubmodulePointer", TRUE) != TRUE)
480 return;
481 if (GitAdminDir::IsBareRepo(g_Git.m_CurrentDir))
482 return;
483 CString superprojectRoot;
484 GitAdminDir::HasAdminDir(g_Git.m_CurrentDir, false, &superprojectRoot);
485 if (superprojectRoot.IsEmpty())
486 return;
488 CAutoRepository repo(superprojectRoot);
489 if (!repo)
490 return;
491 CAutoIndex index;
492 if (git_repository_index(index.GetPointer(), repo))
493 return;
495 CString submodulePath;
496 if (superprojectRoot[superprojectRoot.GetLength() - 1] == L'\\')
497 submodulePath = g_Git.m_CurrentDir.Right(g_Git.m_CurrentDir.GetLength() - superprojectRoot.GetLength());
498 else
499 submodulePath = g_Git.m_CurrentDir.Right(g_Git.m_CurrentDir.GetLength() - superprojectRoot.GetLength() - 1);
500 submodulePath.Replace(L'\\', L'/');
501 const git_index_entry* entry = git_index_get_bypath(index, CUnicodeUtils::GetUTF8(submodulePath), 0);
502 if (!entry)
503 return;
505 m_superProjectHash = entry->id.id;
507 void ReloadHashMap()
509 m_RefLabelPosMap.clear();
510 m_HashMap.clear();
512 if (g_Git.GetMapHashToFriendName(m_HashMap))
513 MessageBox(g_Git.GetGitLastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
515 m_CurrentBranch=g_Git.GetCurrentBranch();
517 if (g_Git.GetHash(m_HeadHash, L"HEAD"))
519 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash. Quitting..."), L"TortoiseGit", MB_ICONERROR);
520 ExitProcess(1);
523 m_wcRev.m_ParentHash.clear();
524 m_wcRev.m_ParentHash.push_back(m_HeadHash);
526 FetchRemoteList();
527 FetchTrackingBranchList();
529 UpdateSubmodulePointer();
531 void StartAsyncDiffThread();
532 void StartLoadingThread();
533 public:
534 void SafeTerminateThread()
536 if (m_LoadingThread && InterlockedExchange(&m_bExitThread, TRUE) == FALSE)
538 DWORD ret = WAIT_TIMEOUT;
539 for (int i = 0; i < 200 && m_bThreadRunning; ++i)
540 ret =::WaitForSingleObject(m_LoadingThread->m_hThread, 100);
541 if (ret == WAIT_TIMEOUT && m_bThreadRunning)
542 ::TerminateThread(m_LoadingThread, 0);
543 m_LoadingThread = nullptr;
546 protected:
547 bool IsInWorkingThread()
549 return (AfxGetThread() == m_LoadingThread);
551 public:
552 void SetRange(const CString& range)
554 m_sRange = range;
557 CString GetRange() const { return m_sRange; }
559 bool HasFilterText() const { return !m_sFilterText.IsEmpty() && m_sFilterText != L"!"; }
561 int m_nSearchIndex;
562 protected:
563 volatile LONG m_bExitThread;
564 CWinThread* m_LoadingThread;
565 public:
566 MAP_HASH_NAME m_HashMap;
567 protected:
568 std::map<CString, std::pair<CString, CString>> m_TrackingMap;
570 public:
571 void SetStyle();
573 CString m_ColumnRegKey;
575 protected:
576 typedef struct {
577 CString name;
578 COLORREF color;
579 CString simplifiedName;
580 CString fullName;
581 bool singleRemote;
582 bool hasTracking;
583 bool sameName;
584 CGit::REF_TYPE refType;
585 } REFLABEL;
587 DECLARE_MESSAGE_MAP()
588 afx_msg void OnDestroy();
589 virtual afx_msg void OnNMCustomdrawLoglist(NMHDR* pNMHDR, LRESULT* pResult);
590 virtual afx_msg void OnLvnGetdispinfoLoglist(NMHDR* pNMHDR, LRESULT* pResult);
591 afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam);
592 afx_msg LRESULT OnScrollToMessage(WPARAM wParam, LPARAM lParam);
593 afx_msg LRESULT OnScrollToRef(WPARAM wParam, LPARAM lParam);
594 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
595 afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
596 afx_msg LRESULT OnLoad(WPARAM wParam, LPARAM lParam);
597 void OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult);
598 afx_msg void OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult);
599 void PreSubclassWindow();
600 virtual BOOL PreTranslateMessage(MSG* pMsg) override;
601 virtual ULONG GetGestureStatus(CPoint ptTouch) override;
602 static UINT LogThreadEntry(LPVOID pVoid);
603 UINT LogThread();
604 bool IsOnStash(int index);
605 bool IsStash(const GitRev * pSelLogEntry);
606 bool IsBisect(const GitRev * pSelLogEntry);
607 void FetchRemoteList();
608 void FetchTrackingBranchList();
610 virtual afx_msg BOOL OnToolTipText(UINT id, NMHDR* pNMHDR, LRESULT* pResult);
611 virtual INT_PTR OnToolHitTest(CPoint point, TOOLINFO* pTI) const override;
612 CString GetToolTipText(int nItem, int nSubItem);
614 /** Checks whether a referenfe label is under pt and returns the index/type
615 * pLogEntry IN: the entry of commit
616 * pt IN: the mouse position in client coordinate
617 * type IN: give the specific reference type, then check if it is the same reference type.
618 * OUT: give CGit::REF_TYPE::UNKNOWN for getting the real type it is.
619 * pShortname OUT: the short name of that reference label
620 * pIndex OUT: the index value of label of that entry
622 bool IsMouseOnRefLabel(const GitRevLoglist* pLogEntry, const POINT& pt, CGit::REF_TYPE& type, CString* pShortname = nullptr, size_t* pIndex = nullptr);
623 bool IsMouseOnRefLabelFromPopupMenu(const GitRevLoglist* pLogEntry, const CPoint& pt, CGit::REF_TYPE& type, CString* pShortname = nullptr, size_t* pIndex = nullptr);
625 void FillBackGround(HDC hdc, DWORD_PTR Index, CRect &rect);
626 void DrawTagBranchMessage(HDC hdc, CRect &rect, INT_PTR index, std::vector<REFLABEL> &refList);
627 void DrawTagBranch(HDC hdc, CDC& W_Dc, HTHEME hTheme, CRect& rect, CRect& rt, LVITEM& rItem, GitRevLoglist* data, std::vector<REFLABEL>& refList);
628 void DrawGraph(HDC,CRect &rect,INT_PTR index);
630 void paintGraphLane(HDC hdc,int laneHeight, int type, int x1, int x2,
631 const COLORREF& col,const COLORREF& activeColor, int top) ;
632 void DrawLine(HDC hdc, int x1, int y1, int x2, int y2){ ::MoveToEx(hdc, x1, y1, nullptr); ::LineTo(hdc, x2, y2); }
634 * Save column widths to the registry
636 void SaveColumnWidths(); // save col widths to the registry
638 BOOL IsEntryInDateRange(int i);
640 int GetHeadIndex();
642 std::vector<GitRevLoglist*> m_AsynDiffList;
643 CComCriticalSection m_AsynDiffListLock;
644 HANDLE m_AsyncDiffEvent;
645 volatile LONG m_AsyncThreadExit;
646 CWinThread* m_DiffingThread;
647 volatile LONG m_AsyncThreadRunning;
649 static int DiffAsync(GitRevLoglist* rev, IAsyncDiffCB* pdata)
651 auto data = static_cast<CGitLogListBase*>(pdata);
652 ULONGLONG offset = data->m_LogCache.GetOffset(rev->m_CommitHash);
653 if (!offset || data->m_LogCache.LoadOneItem(*rev, offset))
655 data->m_AsynDiffListLock.Lock();
656 data->m_AsynDiffList.push_back(rev);
657 data->m_AsynDiffListLock.Unlock();
658 ::SetEvent(data->m_AsyncDiffEvent);
659 return 0;
662 InterlockedExchange(&rev->m_IsDiffFiles, TRUE);
663 if (!rev->m_IsCommitParsed)
664 return 0;
665 InterlockedExchange(&rev->m_IsFull, TRUE);
666 // we might need to signal that the changed files are now available
667 if (data->GetSelectedCount() == 1)
669 POSITION pos = data->GetFirstSelectedItemPosition();
670 int nItem = data->GetNextSelectedItem(pos);
671 if (nItem >= 0)
673 GitRevLoglist* data2 = data->m_arShownList.SafeGetAt(nItem);
674 if (data2 && data2->m_CommitHash == rev->m_CommitHash)
675 data->GetParent()->PostMessage(WM_COMMAND, MSG_FETCHED_DIFF, 0);
678 return 0;
681 static UINT AsyncThread(LPVOID data)
683 return reinterpret_cast<CGitLogListBase*>(data)->AsyncDiffThread();
686 int AsyncDiffThread();
688 public:
689 void SafeTerminateAsyncDiffThread()
691 if (m_DiffingThread && InterlockedExchange(&m_AsyncThreadExit, TRUE) == FALSE)
693 ::SetEvent(m_AsyncDiffEvent);
694 DWORD ret = WAIT_TIMEOUT;
695 // do not block here, but process messages and ask until the thread ends
696 while (ret == WAIT_TIMEOUT && m_AsyncThreadRunning)
698 MSG msg;
699 if (::PeekMessage(&msg, nullptr, 0,0, PM_NOREMOVE))
700 AfxGetThread()->PumpMessage(); // process messages, so that GetTopIndex and so on in the thread work
701 ret = ::WaitForSingleObject(m_DiffingThread->m_hThread, 100);
703 m_DiffingThread = nullptr;
704 InterlockedExchange(&m_AsyncThreadExit, FALSE);
708 protected:
709 CComCriticalSection m_critSec;
711 HICON m_hModifiedIcon;
712 HICON m_hReplacedIcon;
713 HICON m_hConflictedIcon;
714 HICON m_hAddedIcon;
715 HICON m_hDeletedIcon;
716 HICON m_hFetchIcon;
718 CFont m_boldFont;
719 CFont m_FontItalics;
720 CFont m_boldItalicsFont;
722 CRegDWORD m_regMaxBugIDColWidth;
724 void *m_ProcData;
726 CColors m_Colors;
728 CString m_CurrentBranch;
729 CGitHash m_HeadHash;
731 COLORREF m_LineColors[Lanes::COLORS_NUM];
732 DWORD m_LineWidth;
733 DWORD m_NodeSize;
734 DWORD m_DateFormat; // DATE_SHORTDATE or DATE_LONGDATE
735 bool m_bRelativeTimes; // Show relative times
736 GIT_LOG m_DllGitLog;
737 CString m_SingleRemote;
738 bool m_bTagsBranchesOnRightSide;
739 bool m_bFullCommitMessageOnLogLine;
740 bool m_bSymbolizeRefNames;
741 bool m_bIncludeBoundaryCommits;
743 DWORD m_dwDefaultColumns;
744 TCHAR m_wszTip[8192];
745 char m_szTip[8192];
746 std::map<CString, CRect> m_RefLabelPosMap; // ref name vs. label position
747 int m_OldTopIndex;
749 GIT_MAILMAP m_pMailmap;
751 bool m_bDragndropEnabled;
752 BOOL m_bDragging;
753 int m_nDropIndex;
754 int m_nDropMarkerLast;
755 int m_nDropMarkerLastHot;
756 afx_msg void OnMouseMove(UINT nFlags, CPoint point);
757 afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
758 afx_msg void OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult);
759 void DrawDropInsertMarker(int nIndex);
760 void DrawDropInsertMarkerLine(int nIndex);
762 public:
763 void EnableDragnDrop(bool enable) { m_bDragndropEnabled = enable; }