SyncDlg: Disallow in/out changes to include local context menu
[TortoiseGit.git] / src / TortoiseProc / GitLogListBase.h
blob1ca5d2258dbcd68b028d38c2c26f9fed6e264c68
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 // CGitLogList
40 #define ICONITEMBORDER 5
42 #define GITLOG_START 0
43 #define GITLOG_START_ALL 1
44 #define GITLOG_END 100
46 #define LOGFILTER_ALL 0xFFFF
47 #define LOGFILTER_TOGGLE 0x8000
48 #define LOGFILTER_MESSAGES 0x0001
49 #define LOGFILTER_PATHS 0x0002
50 #define LOGFILTER_AUTHORS 0x0004
51 #define LOGFILTER_REVS 0x0008
52 #define LOGFILTER_REGEX 0x0010
53 #define LOGFILTER_BUGID 0x0020
54 #define LOGFILTER_SUBJECT 0x0040
55 #define LOGFILTER_REFNAME 0x0080
56 #define LOGFILTER_EMAILS 0x0100
57 #define LOGFILTER_NOTES 0x0200
58 #define LOGFILTER_ANNOTATEDTAG 0x0400
59 #define LOGFILTER_CASE 0x0800
61 #define LOGLIST_SHOWNOTHING 0x0000
62 #define LOGLIST_SHOWLOCALBRANCHES 0x0001
63 #define LOGLIST_SHOWREMOTEBRANCHES 0x0002
64 #define LOGLIST_SHOWTAGS 0x0004
65 #define LOGLIST_SHOWSTASH 0x0008
66 #define LOGLIST_SHOWBISECT 0x0010
67 #define LOGLIST_SHOWALLREFS 0xFFFF
69 //typedef void CALLBACK_PROCESS(void * data, int progress);
70 #define MSG_LOADED (WM_USER+110)
71 #define MSG_LOAD_PERCENTAGE (WM_USER+111)
72 #define MSG_REFLOG_CHANGED (WM_USER+112)
73 #define MSG_FETCHED_DIFF (WM_USER+113)
74 #define MSG_COMMITS_REORDERED (WM_USER+114)
76 class SelectionHistory
78 #define HISTORYLENGTH 50
79 public:
80 SelectionHistory(void)
81 : location(0)
83 lastselected.reserve(HISTORYLENGTH);
85 void Add(CGitHash &hash)
87 if (hash.IsEmpty())
88 return;
90 size_t size = lastselected.size();
92 // re-select last selected commit
93 if (size > 0 && hash == lastselected[size - 1])
95 // reset location
96 if (location != size - 1)
97 location = size - 1;
98 return;
101 // go back and some commit was highlight
102 if (size > 0 && location != size - 1)
104 // Re-select current one, it may be a forked point.
105 if (hash == lastselected[location])
106 // Discard it later.
107 // That is that discarding forward history when a forked entry is really coming.
108 // And user has the chance to Go Forward again in this situation.
109 // IOW, (hash != lastselected[location]) means user wants a forked history,
110 // and this change saves one step from old behavior.
111 return;
113 // Discard forward history if any
114 while (lastselected.size() - 1 > location)
115 lastselected.pop_back();
118 if (lastselected.size() >= HISTORYLENGTH)
119 lastselected.erase(lastselected.cbegin());
121 lastselected.push_back(hash);
122 location = lastselected.size() - 1;
124 BOOL GoBack(CGitHash& historyEntry)
126 if (location < 1)
127 return FALSE;
129 historyEntry = lastselected[--location];
131 return TRUE;
133 BOOL GoForward(CGitHash& historyEntry)
135 if (location >= lastselected.size() - 1)
136 return FALSE;
138 historyEntry = lastselected[++location];
140 return TRUE;
142 private:
143 std::vector<CGitHash> lastselected;
144 size_t location;
147 class CThreadSafePtrArray : public std::vector<GitRevLoglist*>
149 CComCriticalSection *m_critSec;
150 public:
151 CThreadSafePtrArray(CComCriticalSection *section){ m_critSec = section ;}
152 GitRevLoglist* SafeGetAt(size_t i)
154 if(m_critSec)
155 m_critSec->Lock();
157 SCOPE_EXIT
159 if (m_critSec)
160 m_critSec->Unlock();
163 if (i >= size())
164 return nullptr;
166 return (*this)[i];
169 void SafeAdd(GitRevLoglist* newElement)
171 if(m_critSec)
172 m_critSec->Lock();
173 push_back(newElement);
174 if(m_critSec)
175 m_critSec->Unlock();
178 void SafeRemoveAt(size_t i)
180 if (m_critSec)
181 m_critSec->Lock();
183 SCOPE_EXIT
185 if (m_critSec)
186 m_critSec->Unlock();
189 if (i >= size())
190 return;
192 erase(begin() + i);
195 void SafeAddFront(GitRevLoglist* newElement)
197 if (m_critSec)
198 m_critSec->Lock();
199 insert(cbegin(), newElement);
200 if (m_critSec)
201 m_critSec->Unlock();
204 void SafeRemoveAll()
206 if(m_critSec)
207 m_critSec->Lock();
208 clear();
209 if(m_critSec)
210 m_critSec->Unlock();
214 class CGitLogListBase : public CHintCtrl<CResizableColumnsListCtrl<CListCtrl>>
216 DECLARE_DYNAMIC(CGitLogListBase)
218 public:
219 CGitLogListBase();
220 virtual ~CGitLogListBase();
221 ProjectProperties m_ProjectProperties;
223 void UpdateProjectProperties()
225 m_ProjectProperties.ReadProps();
227 if ((!m_ProjectProperties.sUrl.IsEmpty())||(!m_ProjectProperties.sCheckRe.IsEmpty()))
228 m_bShowBugtraqColumn = true;
229 else
230 m_bShowBugtraqColumn = false;
233 void ResetWcRev(bool refresh = false)
235 m_wcRev.Clear();
236 m_wcRev.GetSubject().LoadString(IDS_LOG_WORKINGDIRCHANGES);
237 m_wcRev.m_Mark = L'-';
238 m_wcRev.GetBody().LoadString(IDS_LOG_FETCHINGSTATUS);
239 m_wcRev.GetBody() = L'\n' + m_wcRev.GetBody();
240 m_wcRev.m_CallDiffAsync = DiffAsync;
241 InterlockedExchange(&m_wcRev.m_IsDiffFiles, FALSE);
242 if (refresh && m_bShowWC)
243 m_arShownList[0] = &m_wcRev;
246 volatile LONG m_bNoDispUpdates;
247 BOOL m_IsIDReplaceAction;
248 BOOL m_IsOldFirst;
249 protected:
250 void hideFromContextMenu(unsigned __int64 hideMask, bool exclusivelyShow);
251 public:
252 BOOL m_IsRebaseReplaceGraph;
253 BOOL m_bNoHightlightHead;
255 protected:
256 void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
258 BOOL m_bStrictStopped;
259 public:
260 BOOL m_bShowBugtraqColumn;
261 protected:
262 BOOL m_bSearchIndex;
263 BOOL m_bCancelled;
264 public:
265 bool m_bIsCherryPick;
266 unsigned __int64 m_ContextMenuMask;
268 bool m_hasWC;
269 bool m_bShowWC;
270 protected:
271 GitRevLoglist m_wcRev;
272 public:
273 volatile LONG m_bThreadRunning;
274 protected:
275 CLogCache m_LogCache;
277 public:
278 CString m_sRange;
279 // don't forget to bump BLAME_COLUMN_VERSION in GitStatusListCtrlHelpers.cpp if you change columns
280 enum
282 LOGLIST_GRAPH,
283 LOGLIST_REBASE,
284 LOGLIST_ID,
285 LOGLIST_HASH,
286 LOGLIST_ACTION,
287 LOGLIST_MESSAGE,
288 LOGLIST_AUTHOR,
289 LOGLIST_DATE,
290 LOGLIST_EMAIL,
291 LOGLIST_COMMIT_NAME,
292 LOGLIST_COMMIT_EMAIL,
293 LOGLIST_COMMIT_DATE,
294 LOGLIST_BUG,
295 LOGLIST_SVNREV,
296 LOGLIST_MESSAGE_MAX=300,
297 LOGLIST_MESSAGE_MIN=200,
299 GIT_LOG_GRAPH = 1<< LOGLIST_GRAPH,
300 GIT_LOG_REBASE = 1<< LOGLIST_REBASE,
301 GIT_LOG_ID = 1<< LOGLIST_ID,
302 GIT_LOG_HASH = 1<< LOGLIST_HASH,
303 GIT_LOG_ACTIONS = 1<< LOGLIST_ACTION,
304 GIT_LOG_MESSAGE = 1<< LOGLIST_MESSAGE,
305 GIT_LOG_AUTHOR = 1<< LOGLIST_AUTHOR,
306 GIT_LOG_DATE = 1<< LOGLIST_DATE,
307 GIT_LOG_EMAIL = 1<< LOGLIST_EMAIL,
308 GIT_LOG_COMMIT_NAME = 1<< LOGLIST_COMMIT_NAME,
309 GIT_LOG_COMMIT_EMAIL= 1<< LOGLIST_COMMIT_EMAIL,
310 GIT_LOG_COMMIT_DATE = 1<< LOGLIST_COMMIT_DATE,
311 GIT_LOGLIST_BUG = 1<< LOGLIST_BUG,
312 GIT_LOGLIST_SVNREV = 1<< LOGLIST_SVNREV,
315 enum
317 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
318 ID_COMPARE = 1, // compare revision with WC
319 ID_SAVEAS,
320 ID_COMPARETWO, // compare two revisions
321 ID_COPY,
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_COPYHASH,
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_COPYCLIPBOARDMESSAGES,
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,
382 enum
384 ID_COPY_ALL,
385 ID_COPY_MESSAGE,
386 ID_COPY_SUBJECT,
387 ID_COPY_HASH,
389 enum FilterShow
391 FILTERSHOW_REFS = 1,
392 FILTERSHOW_MERGEPOINTS = 2,
393 FILTERSHOW_ANYCOMMIT = 4,
394 FILTERSHOW_ALL = FILTERSHOW_ANYCOMMIT | FILTERSHOW_REFS | FILTERSHOW_MERGEPOINTS
396 enum : unsigned int
398 // For Rebase only
399 LOGACTIONS_REBASE_CURRENT = 0x08000000,
400 LOGACTIONS_REBASE_PICK = 0x04000000,
401 LOGACTIONS_REBASE_SQUASH = 0x02000000,
402 LOGACTIONS_REBASE_EDIT = 0x01000000,
403 LOGACTIONS_REBASE_DONE = 0x00800000,
404 LOGACTIONS_REBASE_SKIP = 0x00400000,
405 LOGACTIONS_REBASE_MASK = 0x0FC00000,
406 LOGACTIONS_REBASE_MODE_MASK = 0x07C00000,
408 inline unsigned __int64 GetContextMenuBit(int i){ return ((unsigned __int64 )0x1)<<i ;}
409 static CString GetRebaseActionName(int action);
410 void InsertGitColumn();
411 void CopySelectionToClipBoard(int toCopy = ID_COPY_ALL);
412 void DiffSelectedRevWithPrevious();
413 bool IsSelectionContinuous();
414 protected:
415 int BeginFetchLog();
416 public:
417 int FillGitLog(CTGitPath* path, CString* range = nullptr, int infomask = CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
418 int FillGitLog(std::unordered_set<CGitHash>& hashes);
419 protected:
420 CString MessageDisplayStr(GitRev* pLogEntry);
421 BOOL IsMatchFilter(bool bRegex, GitRevLoglist* pRev, std::tr1::wregex& pat);
422 bool ShouldShowFilter(GitRevLoglist* pRev, const std::unordered_map<CGitHash, std::unordered_set<CGitHash>>& commitChildren);
423 public:
424 void ShowGraphColumn(bool bShow);
425 CString GetTagInfo(GitRev* pLogEntry);
427 CFindDlg *m_pFindDialog;
428 static const UINT m_FindDialogMessage;
429 void OnFind();
431 protected:
432 static const UINT m_ScrollToMessage;
433 public:
434 static const UINT m_ScrollToRef;
435 static const UINT m_RebaseActionMessage;
437 inline int ShownCountWithStopped() const { return (int)m_arShownList.size() + (m_bStrictStopped ? 1 : 0); }
438 void FetchLogAsync(void* data = nullptr);
439 CThreadSafePtrArray m_arShownList;
440 void Refresh(BOOL IsCleanFilter=TRUE);
441 void RecalculateShownList(CThreadSafePtrArray * pShownlist);
442 void Clear();
444 DWORD m_SelectedFilters;
445 FilterShow m_ShowFilter;
446 bool m_bFilterWithRegex;
447 bool m_bFilterCaseSensitively;
448 CLogDataVector m_logEntries;
449 void RemoveFilter();
450 void StartFilter();
451 bool ValidateRegexp(LPCTSTR regexp_str, std::tr1::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.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();
595 virtual BOOL PreTranslateMessage(MSG* pMsg);
596 static UINT LogThreadEntry(LPVOID pVoid);
597 UINT LogThread();
598 bool IsOnStash(int index);
599 bool IsStash(const GitRev * pSelLogEntry);
600 bool IsBisect(const GitRev * pSelLogEntry);
601 void FetchRemoteList();
602 void FetchTrackingBranchList();
603 void FetchLastLogInfo();
604 void FetchFullLogInfo(CString &from, CString &to);
606 virtual afx_msg BOOL OnToolTipText(UINT id, NMHDR * pNMHDR, LRESULT * pResult);
607 virtual INT_PTR OnToolHitTest(CPoint point, TOOLINFO * pTI) const;
608 CString GetToolTipText(int nItem, int nSubItem);
610 /** Checks whether a referenfe label is under pt and returns the index/type
611 * pLogEntry IN: the entry of commit
612 * pt IN: the mouse position in client coordinate
613 * type IN: give the specific reference type, then check if it is the same reference type.
614 * OUT: give CGit::REF_TYPE::UNKNOWN for getting the real type it is.
615 * pShortname OUT: the short name of that reference label
616 * pIndex OUT: the index value of label of that entry
618 bool IsMouseOnRefLabel(const GitRevLoglist* pLogEntry, const POINT& pt, CGit::REF_TYPE& type, CString* pShortname = nullptr, size_t* pIndex = nullptr);
619 bool IsMouseOnRefLabelFromPopupMenu(const GitRevLoglist* pLogEntry, const CPoint& pt, CGit::REF_TYPE& type, CString* pShortname = nullptr, size_t* pIndex = nullptr);
621 void FillBackGround(HDC hdc, DWORD_PTR Index, CRect &rect);
622 void DrawTagBranchMessage(HDC hdc, CRect &rect, INT_PTR index, std::vector<REFLABEL> &refList);
623 void DrawTagBranch(HDC hdc, CDC& W_Dc, HTHEME hTheme, CRect& rect, CRect& rt, LVITEM& rItem, GitRevLoglist* data, std::vector<REFLABEL>& refList);
624 void DrawGraph(HDC,CRect &rect,INT_PTR index);
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 BOOL IsEntryInDateRange(int i);
636 int GetHeadIndex();
638 std::vector<GitRevLoglist*> m_AsynDiffList;
639 CComCriticalSection m_AsynDiffListLock;
640 HANDLE m_AsyncDiffEvent;
641 volatile LONG m_AsyncThreadExit;
642 CWinThread* m_DiffingThread;
643 volatile LONG m_AsyncThreadRunning;
645 static int DiffAsync(GitRevLoglist* rev, void* pdata)
647 auto data = reinterpret_cast<CGitLogListBase*>(pdata);
648 ULONGLONG offset = data->m_LogCache.GetOffset(rev->m_CommitHash);
649 if (!offset || data->m_LogCache.LoadOneItem(*rev, offset))
651 data->m_AsynDiffListLock.Lock();
652 data->m_AsynDiffList.push_back(rev);
653 data->m_AsynDiffListLock.Unlock();
654 ::SetEvent(data->m_AsyncDiffEvent);
655 return 0;
658 InterlockedExchange(&rev->m_IsDiffFiles, TRUE);
659 if (!rev->m_IsCommitParsed)
660 return 0;
661 InterlockedExchange(&rev->m_IsFull, TRUE);
662 // we might need to signal that the changed files are now available
663 if (data->GetSelectedCount() == 1)
665 POSITION pos = data->GetFirstSelectedItemPosition();
666 int nItem = data->GetNextSelectedItem(pos);
667 if (nItem >= 0)
669 GitRevLoglist* data2 = data->m_arShownList.SafeGetAt(nItem);
670 if (data2 && data2->m_CommitHash == rev->m_CommitHash)
671 data->GetParent()->PostMessage(WM_COMMAND, MSG_FETCHED_DIFF, 0);
674 return 0;
677 static UINT AsyncThread(LPVOID data)
679 return reinterpret_cast<CGitLogListBase*>(data)->AsyncDiffThread();
682 int AsyncDiffThread();
684 public:
685 void SafeTerminateAsyncDiffThread()
687 if (m_DiffingThread && InterlockedExchange(&m_AsyncThreadExit, TRUE) == FALSE)
689 ::SetEvent(m_AsyncDiffEvent);
690 DWORD ret = WAIT_TIMEOUT;
691 // do not block here, but process messages and ask until the thread ends
692 while (ret == WAIT_TIMEOUT && m_AsyncThreadRunning)
694 MSG msg;
695 if (::PeekMessage(&msg, nullptr, 0,0, PM_NOREMOVE))
696 AfxGetThread()->PumpMessage(); // process messages, so that GetTopIndex and so on in the thread work
697 ret = ::WaitForSingleObject(m_DiffingThread->m_hThread, 100);
699 m_DiffingThread = nullptr;
700 InterlockedExchange(&m_AsyncThreadExit, FALSE);
704 protected:
705 CComCriticalSection m_critSec;
707 HICON m_hModifiedIcon;
708 HICON m_hReplacedIcon;
709 HICON m_hConflictedIcon;
710 HICON m_hAddedIcon;
711 HICON m_hDeletedIcon;
712 HICON m_hFetchIcon;
714 CFont m_boldFont;
715 CFont m_FontItalics;
716 CFont m_boldItalicsFont;
718 CRegDWORD m_regMaxBugIDColWidth;
720 void *m_ProcData;
722 CColors m_Colors;
724 CString m_CurrentBranch;
725 CGitHash m_HeadHash;
727 COLORREF m_LineColors[Lanes::COLORS_NUM];
728 DWORD m_LineWidth;
729 DWORD m_NodeSize;
730 DWORD m_DateFormat; // DATE_SHORTDATE or DATE_LONGDATE
731 bool m_bRelativeTimes; // Show relative times
732 GIT_LOG m_DllGitLog;
733 CString m_SingleRemote;
734 bool m_bTagsBranchesOnRightSide;
735 bool m_bFullCommitMessageOnLogLine;
736 bool m_bSymbolizeRefNames;
737 bool m_bIncludeBoundaryCommits;
739 DWORD m_dwDefaultColumns;
740 TCHAR m_wszTip[8192];
741 char m_szTip[8192];
742 std::map<CString, CRect> m_RefLabelPosMap; // ref name vs. label position
743 int m_OldTopIndex;
745 GIT_MAILMAP m_pMailmap;
747 bool m_bDragndropEnabled;
748 BOOL m_bDragging;
749 int m_nDropIndex;
750 int m_nDropMarkerLast;
751 int m_nDropMarkerLastHot;
752 afx_msg void OnMouseMove(UINT nFlags, CPoint point);
753 afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
754 afx_msg void OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult);
755 void DrawDropInsertMarker(int nIndex);
756 void DrawDropInsertMarkerLine(int nIndex);
758 public:
759 void EnableDragnDrop(bool enable) { m_bDragndropEnabled = enable; }