Refactor rebase commit list: Don't fill list again and again when reordering commits
[TortoiseGit.git] / src / TortoiseProc / GitLogListBase.h
blobe2d8ba6febef7a946a1fdaa3ce51655e9614a9cb
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>
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 public:
262 BOOL m_bShowBugtraqColumn;
263 protected:
264 BOOL m_bSearchIndex;
265 public:
266 bool m_bIsCherryPick;
267 unsigned __int64 m_ContextMenuMask;
269 bool m_hasWC;
270 bool m_bShowWC;
271 protected:
272 GitRevLoglist m_wcRev;
273 public:
274 volatile LONG m_bThreadRunning;
275 protected:
276 CLogCache m_LogCache;
278 public:
279 CString m_sRange;
280 // don't forget to bump BLAME_COLUMN_VERSION in GitStatusListCtrlHelpers.cpp if you change columns
281 enum
283 LOGLIST_GRAPH,
284 LOGLIST_REBASE,
285 LOGLIST_ID,
286 LOGLIST_HASH,
287 LOGLIST_ACTION,
288 LOGLIST_MESSAGE,
289 LOGLIST_AUTHOR,
290 LOGLIST_DATE,
291 LOGLIST_EMAIL,
292 LOGLIST_COMMIT_NAME,
293 LOGLIST_COMMIT_EMAIL,
294 LOGLIST_COMMIT_DATE,
295 LOGLIST_BUG,
296 LOGLIST_SVNREV,
297 LOGLIST_MESSAGE_MAX=300,
298 LOGLIST_MESSAGE_MIN=200,
300 GIT_LOG_GRAPH = 1<< LOGLIST_GRAPH,
301 GIT_LOG_REBASE = 1<< LOGLIST_REBASE,
302 GIT_LOG_ID = 1<< LOGLIST_ID,
303 GIT_LOG_HASH = 1<< LOGLIST_HASH,
304 GIT_LOG_ACTIONS = 1<< LOGLIST_ACTION,
305 GIT_LOG_MESSAGE = 1<< LOGLIST_MESSAGE,
306 GIT_LOG_AUTHOR = 1<< LOGLIST_AUTHOR,
307 GIT_LOG_DATE = 1<< LOGLIST_DATE,
308 GIT_LOG_EMAIL = 1<< LOGLIST_EMAIL,
309 GIT_LOG_COMMIT_NAME = 1<< LOGLIST_COMMIT_NAME,
310 GIT_LOG_COMMIT_EMAIL= 1<< LOGLIST_COMMIT_EMAIL,
311 GIT_LOG_COMMIT_DATE = 1<< LOGLIST_COMMIT_DATE,
312 GIT_LOGLIST_BUG = 1<< LOGLIST_BUG,
313 GIT_LOGLIST_SVNREV = 1<< LOGLIST_SVNREV,
316 enum
318 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
319 ID_COMPARE = 1, // compare revision with WC
320 ID_SAVEAS,
321 ID_COMPARETWO, // compare two revisions
322 ID_REVERTREV,
323 ID_MERGEREV,
324 ID_GNUDIFF1, // compare with WC, unified
325 ID_GNUDIFF2, // compare two revisions, unified
326 ID_FINDENTRY,
327 ID_OPEN,
328 ID_BLAME,
329 ID_REPOBROWSE,
330 ID_LOG,
331 ID_EDITNOTE,
332 ID_DIFF,
333 ID_OPENWITH,
334 ID_COPYCLIPBOARD,
335 ID_REVERTTOREV,
336 ID_BLAMECOMPARE,
337 ID_BLAMEDIFF,
338 ID_VIEWREV,
339 ID_VIEWPATHREV,
340 ID_EXPORT,
341 ID_COMPAREWITHPREVIOUS,
342 ID_BLAMEPREVIOUS,
343 ID_CHERRY_PICK,
344 ID_CREATE_BRANCH,
345 ID_CREATE_TAG,
346 ID_SWITCHTOREV,
347 ID_SWITCHBRANCH,
348 ID_RESET,
349 ID_REBASE_PICK,
350 ID_REBASE_EDIT,
351 ID_REBASE_SQUASH,
352 ID_REBASE_SKIP,
353 ID_COMBINE_COMMIT,
354 ID_STASH_SAVE,
355 ID_STASH_LIST,
356 ID_STASH_POP,
357 ID_REFLOG_STASH_APPLY,
358 ID_REFLOG_DEL,
359 ID_REBASE_TO_VERSION,
360 ID_CREATE_PATCH,
361 ID_DELETE,
362 ID_COMMIT,
363 ID_PUSH,
364 ID_PULL,
365 ID_FETCH,
366 ID_SHOWBRANCHES,
367 ID_BISECTSTART,
368 ID_LOG_VIEWRANGE,
369 ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE,
370 ID_MERGE_ABORT,
371 ID_CLEANUP,
372 ID_SUBMODULE_UPDATE,
373 ID_BISECTGOOD,
374 ID_BISECTBAD,
375 ID_BISECTRESET,
376 ID_BISECTSKIP,
377 ID_SVNDCOMMIT,
378 ID_COMPARETWOCOMMITCHANGES,
379 // the following can be >= 64 as those are not used for GetContextMenuBit(), all others must be < 64 in order to fit into __int64
380 ID_COPYCLIPBOARDFULL,
381 ID_COPYCLIPBOARDFULLNOPATHS,
382 ID_COPYCLIPBOARDHASH,
383 ID_COPYCLIPBOARDAUTHORSFULL,
384 ID_COPYCLIPBOARDAUTHORSNAME,
385 ID_COPYCLIPBOARDAUTHORSEMAIL,
386 ID_COPYCLIPBOARDSUBJECTS,
387 ID_COPYCLIPBOARDMESSAGES,
388 ID_COPYCLIPBOARDBRANCHTAG,
390 enum FilterShow
392 FILTERSHOW_REFS = 1,
393 FILTERSHOW_MERGEPOINTS = 2,
394 FILTERSHOW_ANYCOMMIT = 4,
395 FILTERSHOW_ALL = FILTERSHOW_ANYCOMMIT | FILTERSHOW_REFS | FILTERSHOW_MERGEPOINTS
397 enum : unsigned int
399 // For Rebase only
400 LOGACTIONS_REBASE_CURRENT = 0x08000000,
401 LOGACTIONS_REBASE_PICK = 0x04000000,
402 LOGACTIONS_REBASE_SQUASH = 0x02000000,
403 LOGACTIONS_REBASE_EDIT = 0x01000000,
404 LOGACTIONS_REBASE_DONE = 0x00800000,
405 LOGACTIONS_REBASE_SKIP = 0x00400000,
406 LOGACTIONS_REBASE_MASK = 0x0FC00000,
407 LOGACTIONS_REBASE_MODE_MASK = 0x07C00000,
409 static_assert(ID_COMPARETWOCOMMITCHANGES < 64 && ID_COPYCLIPBOARDFULL <= 64, "IDs must be <64 in order to be usable in a bitfield");
410 static inline unsigned __int64 GetContextMenuBit(int i){ return ((unsigned __int64 )0x1)<<i ;}
411 static CString GetRebaseActionName(int action);
412 void InsertGitColumn();
413 void CopySelectionToClipBoard(int toCopy = ID_COPYCLIPBOARDFULL);
414 void DiffSelectedRevWithPrevious();
415 bool IsSelectionContinuous();
416 protected:
417 int BeginFetchLog();
418 HWND GetParentHWND();
419 public:
420 int FillGitLog(CTGitPath* path, CString* range = nullptr, int infomask = CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
421 int FillGitLog(std::unordered_set<CGitHash>& hashes);
422 protected:
423 CString MessageDisplayStr(GitRev* pLogEntry);
424 BOOL IsMatchFilter(bool bRegex, GitRevLoglist* pRev, std::wregex& pat);
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 DWORD m_SelectedFilters;
447 FilterShow m_ShowFilter;
448 bool m_bFilterWithRegex;
449 bool m_bFilterCaseSensitively;
450 CLogDataVector m_logEntries;
451 bool ValidateRegexp(LPCTSTR regexp_str, std::wregex& pat, bool bMatchCase = false );
452 CString m_sFilterText;
454 CFilterData m_Filter;
456 CTGitPath m_Path;
457 int m_ShowMask;
458 CGitHash m_lastSelectedHash;
459 SelectionHistory m_selectionHistory;
460 CGitHash m_highlight;
461 int m_ShowRefMask;
463 protected:
464 CGitHash m_superProjectHash;
466 public:
467 void GetTimeRange(CTime &oldest,CTime &latest);
468 protected:
469 virtual void GetParentHashes(GitRev* pRev, GIT_REV_LIST& parentHash);
470 virtual void ContextMenuAction(int cmd,int FirstSelect, int LastSelect, CMenu * menu)=0;
471 void UpdateSubmodulePointer()
473 m_superProjectHash.Empty();
474 if (CRegDWORD(L"Software\\TortoiseGit\\LogShowSuperProjectSubmodulePointer", TRUE) != TRUE)
475 return;
476 if (GitAdminDir::IsBareRepo(g_Git.m_CurrentDir))
477 return;
478 CString superprojectRoot;
479 GitAdminDir::HasAdminDir(g_Git.m_CurrentDir, false, &superprojectRoot);
480 if (superprojectRoot.IsEmpty())
481 return;
483 CAutoRepository repo(superprojectRoot);
484 if (!repo)
485 return;
486 CAutoIndex index;
487 if (git_repository_index(index.GetPointer(), repo))
488 return;
490 CString submodulePath;
491 if (superprojectRoot[superprojectRoot.GetLength() - 1] == L'\\')
492 submodulePath = g_Git.m_CurrentDir.Right(g_Git.m_CurrentDir.GetLength() - superprojectRoot.GetLength());
493 else
494 submodulePath = g_Git.m_CurrentDir.Right(g_Git.m_CurrentDir.GetLength() - superprojectRoot.GetLength() - 1);
495 submodulePath.Replace(L'\\', L'/');
496 const git_index_entry* entry = git_index_get_bypath(index, CUnicodeUtils::GetUTF8(submodulePath), 0);
497 if (!entry)
498 return;
500 m_superProjectHash = entry->id;
502 void ReloadHashMap()
504 m_RefLabelPosMap.clear();
505 m_HashMap.clear();
507 if (g_Git.GetMapHashToFriendName(m_HashMap))
508 MessageBox(g_Git.GetGitLastErr(L"Could not get all refs."), L"TortoiseGit", MB_ICONERROR);
510 m_CurrentBranch=g_Git.GetCurrentBranch();
512 if (g_Git.GetHash(m_HeadHash, L"HEAD"))
514 MessageBox(g_Git.GetGitLastErr(L"Could not get HEAD hash. Quitting..."), L"TortoiseGit", MB_ICONERROR);
515 ExitProcess(1);
518 m_wcRev.m_ParentHash.clear();
519 m_wcRev.m_ParentHash.push_back(m_HeadHash);
521 FetchRemoteList();
522 FetchTrackingBranchList();
524 UpdateSubmodulePointer();
526 void StartAsyncDiffThread();
527 void StartLoadingThread();
528 public:
529 void SafeTerminateThread()
531 if (m_LoadingThread && InterlockedExchange(&m_bExitThread, TRUE) == FALSE)
533 DWORD ret = WAIT_TIMEOUT;
534 for (int i = 0; i < 200 && m_bThreadRunning; ++i)
535 ret =::WaitForSingleObject(m_LoadingThread->m_hThread, 100);
536 if (ret == WAIT_TIMEOUT && m_bThreadRunning)
537 ::TerminateThread(m_LoadingThread, 0);
538 m_LoadingThread = nullptr;
541 protected:
542 bool IsInWorkingThread()
544 return (AfxGetThread() == m_LoadingThread);
546 public:
547 void SetRange(const CString& range)
549 m_sRange = range;
552 CString GetRange() const { return m_sRange; }
554 bool HasFilterText() const { return !m_sFilterText.IsEmpty() && m_sFilterText != L"!"; }
556 int m_nSearchIndex;
557 protected:
558 volatile LONG m_bExitThread;
559 CWinThread* m_LoadingThread;
560 public:
561 MAP_HASH_NAME m_HashMap;
562 protected:
563 std::map<CString, std::pair<CString, CString>> m_TrackingMap;
565 public:
566 void SetStyle();
568 CString m_ColumnRegKey;
570 protected:
571 typedef struct {
572 CString name;
573 COLORREF color;
574 CString simplifiedName;
575 CString fullName;
576 bool singleRemote;
577 bool hasTracking;
578 bool sameName;
579 CGit::REF_TYPE refType;
580 } REFLABEL;
582 DECLARE_MESSAGE_MAP()
583 afx_msg void OnDestroy();
584 virtual afx_msg void OnNMCustomdrawLoglist(NMHDR* pNMHDR, LRESULT* pResult);
585 virtual afx_msg void OnLvnGetdispinfoLoglist(NMHDR* pNMHDR, LRESULT* pResult);
586 afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam);
587 afx_msg LRESULT OnScrollToMessage(WPARAM wParam, LPARAM lParam);
588 afx_msg LRESULT OnScrollToRef(WPARAM wParam, LPARAM lParam);
589 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
590 afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
591 afx_msg LRESULT OnLoad(WPARAM wParam, LPARAM lParam);
592 void OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult);
593 afx_msg void OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult);
594 void PreSubclassWindow() override;
595 virtual BOOL PreTranslateMessage(MSG* pMsg) override;
596 virtual ULONG GetGestureStatus(CPoint ptTouch) override;
597 static UINT LogThreadEntry(LPVOID pVoid);
598 UINT LogThread();
599 bool IsOnStash(int index);
600 bool IsStash(const GitRev * pSelLogEntry);
601 bool IsBisect(const GitRev * pSelLogEntry);
602 void FetchRemoteList();
603 void FetchTrackingBranchList();
605 virtual afx_msg BOOL OnToolTipText(UINT id, NMHDR* pNMHDR, LRESULT* pResult);
606 virtual INT_PTR OnToolHitTest(CPoint point, TOOLINFO* pTI) const override;
607 CString GetToolTipText(int nItem, int nSubItem);
609 /** Checks whether a referenfe label is under pt and returns the index/type
610 * pLogEntry IN: the entry of commit
611 * pt IN: the mouse position in client coordinate
612 * type IN: give the specific reference type, then check if it is the same reference type.
613 * OUT: give CGit::REF_TYPE::UNKNOWN for getting the real type it is.
614 * pShortname OUT: the short name of that reference label
615 * pIndex OUT: the index value of label of that entry
617 bool IsMouseOnRefLabel(const GitRevLoglist* pLogEntry, const POINT& pt, CGit::REF_TYPE& type, CString* pShortname = nullptr, size_t* pIndex = nullptr);
618 bool IsMouseOnRefLabelFromPopupMenu(const GitRevLoglist* pLogEntry, const CPoint& pt, CGit::REF_TYPE& type, CString* pShortname = nullptr, size_t* pIndex = nullptr);
620 void FillBackGround(HDC hdc, DWORD_PTR Index, CRect &rect);
621 void DrawTagBranchMessage(HDC hdc, CRect &rect, INT_PTR index, std::vector<REFLABEL> &refList);
622 void DrawTagBranch(HDC hdc, CDC& W_Dc, HTHEME hTheme, CRect& rect, CRect& rt, LVITEM& rItem, GitRevLoglist* data, std::vector<REFLABEL>& refList);
623 void DrawGraph(HDC,CRect &rect,INT_PTR index);
625 void paintGraphLane(HDC hdc,int laneHeight, int type, int x1, int x2,
626 const COLORREF& col,const COLORREF& activeColor, int top) ;
627 void DrawLine(HDC hdc, int x1, int y1, int x2, int y2){ ::MoveToEx(hdc, x1, y1, nullptr); ::LineTo(hdc, x2, y2); }
629 * Save column widths to the registry
631 void SaveColumnWidths(); // save col widths to the registry
633 int GetHeadIndex();
635 std::vector<GitRevLoglist*> m_AsynDiffList;
636 CComCriticalSection m_AsynDiffListLock;
637 HANDLE m_AsyncDiffEvent;
638 volatile LONG m_AsyncThreadExit;
639 CWinThread* m_DiffingThread;
640 volatile LONG m_AsyncThreadRunning;
642 static int DiffAsync(GitRevLoglist* rev, IAsyncDiffCB* pdata)
644 auto data = static_cast<CGitLogListBase*>(pdata);
645 ULONGLONG offset = data->m_LogCache.GetOffset(rev->m_CommitHash);
646 if (!offset || data->m_LogCache.LoadOneItem(*rev, offset))
648 data->m_AsynDiffListLock.Lock();
649 data->m_AsynDiffList.push_back(rev);
650 data->m_AsynDiffListLock.Unlock();
651 ::SetEvent(data->m_AsyncDiffEvent);
652 return 0;
655 InterlockedExchange(&rev->m_IsDiffFiles, TRUE);
656 if (!rev->m_IsCommitParsed)
657 return 0;
658 InterlockedExchange(&rev->m_IsFull, TRUE);
659 // we might need to signal that the changed files are now available
660 if (data->GetSelectedCount() == 1)
662 POSITION pos = data->GetFirstSelectedItemPosition();
663 int nItem = data->GetNextSelectedItem(pos);
664 if (nItem >= 0)
666 GitRevLoglist* data2 = data->m_arShownList.SafeGetAt(nItem);
667 if (data2 && data2->m_CommitHash == rev->m_CommitHash)
668 data->GetParent()->PostMessage(WM_COMMAND, MSG_FETCHED_DIFF, 0);
671 return 0;
674 static UINT AsyncThread(LPVOID data)
676 return reinterpret_cast<CGitLogListBase*>(data)->AsyncDiffThread();
679 int AsyncDiffThread();
681 public:
682 void SafeTerminateAsyncDiffThread()
684 if (m_DiffingThread && InterlockedExchange(&m_AsyncThreadExit, TRUE) == FALSE)
686 ::SetEvent(m_AsyncDiffEvent);
687 DWORD ret = WAIT_TIMEOUT;
688 // do not block here, but process messages and ask until the thread ends
689 while (ret == WAIT_TIMEOUT && m_AsyncThreadRunning)
691 MSG msg;
692 if (::PeekMessage(&msg, nullptr, 0,0, PM_NOREMOVE))
693 AfxGetThread()->PumpMessage(); // process messages, so that GetTopIndex and so on in the thread work
694 ret = ::WaitForSingleObject(m_DiffingThread->m_hThread, 100);
696 m_DiffingThread = nullptr;
697 InterlockedExchange(&m_AsyncThreadExit, FALSE);
701 protected:
702 CComCriticalSection m_critSec;
704 HICON m_hModifiedIcon;
705 HICON m_hReplacedIcon;
706 HICON m_hConflictedIcon;
707 HICON m_hAddedIcon;
708 HICON m_hDeletedIcon;
709 HICON m_hFetchIcon;
711 CFont m_boldFont;
712 CFont m_FontItalics;
713 CFont m_boldItalicsFont;
715 CRegDWORD m_regMaxBugIDColWidth;
717 void *m_ProcData;
719 CColors m_Colors;
721 CString m_CurrentBranch;
722 CGitHash m_HeadHash;
724 COLORREF m_LineColors[Lanes::COLORS_NUM];
725 DWORD m_LineWidth;
726 DWORD m_NodeSize;
727 DWORD m_DateFormat; // DATE_SHORTDATE or DATE_LONGDATE
728 bool m_bRelativeTimes; // Show relative times
729 GIT_LOG m_DllGitLog;
730 CString m_SingleRemote;
731 bool m_bTagsBranchesOnRightSide;
732 bool m_bFullCommitMessageOnLogLine;
733 bool m_bSymbolizeRefNames;
734 bool m_bIncludeBoundaryCommits;
736 DWORD m_dwDefaultColumns;
737 TCHAR m_wszTip[8192];
738 char m_szTip[8192];
739 std::map<CString, CRect> m_RefLabelPosMap; // ref name vs. label position
740 int m_OldTopIndex;
742 GIT_MAILMAP m_pMailmap;
744 bool m_bDragndropEnabled;
745 BOOL m_bDragging;
746 int m_nDropIndex;
747 int m_nDropMarkerLast;
748 int m_nDropMarkerLastHot;
749 afx_msg void OnMouseMove(UINT nFlags, CPoint point);
750 afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
751 afx_msg void OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult);
752 void DrawDropInsertMarker(int nIndex);
753 void DrawDropInsertMarkerLine(int nIndex);
755 public:
756 void EnableDragnDrop(bool enable) { m_bDragndropEnabled = enable; }