Use correct length for buffer
[TortoiseGit.git] / src / TortoiseProc / GitLogListBase.h
blob148bd6bcbdcfa69cb325abe43b8b21c784cb3c60
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - 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 "HintListCtrl.h"
24 #include "Git.h"
25 #include "ProjectProperties.h"
26 #include "TGitPath.h"
27 #include "registry.h"
28 #include "Colors.h"
29 #include "LogDlgHelper.h"
30 #include "GitRevLoglist.h"
31 #include "lanes.h"
32 #include "GitLogCache.h"
33 #include <regex>
34 #include "GitStatusListCtrl.h"
35 #include "FindDlg.h"
37 // CGitLogList
38 #define ICONITEMBORDER 5
40 #define GITLOG_START 0
41 #define GITLOG_START_ALL 1
42 #define GITLOG_END 100
44 #define LOGFILTER_ALL 0xFFFF
45 #define LOGFILTER_TOGGLE 0x8000
46 #define LOGFILTER_MESSAGES 0x0001
47 #define LOGFILTER_PATHS 0x0002
48 #define LOGFILTER_AUTHORS 0x0004
49 #define LOGFILTER_REVS 0x0008
50 #define LOGFILTER_REGEX 0x0010
51 #define LOGFILTER_BUGID 0x0020
52 #define LOGFILTER_SUBJECT 0x0040
53 #define LOGFILTER_REFNAME 0x0080
54 #define LOGFILTER_EMAILS 0x0100
55 #define LOGFILTER_NOTES 0x0200
56 #define LOGFILTER_ANNOTATEDTAG 0x0400
57 #define LOGFILTER_CASE 0x0800
59 #define LOGLIST_SHOWNOTHING 0x0000
60 #define LOGLIST_SHOWLOCALBRANCHES 0x0001
61 #define LOGLIST_SHOWREMOTEBRANCHES 0x0002
62 #define LOGLIST_SHOWTAGS 0x0004
63 #define LOGLIST_SHOWSTASH 0x0008
64 #define LOGLIST_SHOWBISECT 0x0010
65 #define LOGLIST_SHOWALLREFS 0xFFFF
67 //typedef void CALLBACK_PROCESS(void * data, int progress);
68 #define MSG_LOADED (WM_USER+110)
69 #define MSG_LOAD_PERCENTAGE (WM_USER+111)
70 #define MSG_REFLOG_CHANGED (WM_USER+112)
71 #define MSG_FETCHED_DIFF (WM_USER+113)
73 class SelectionHistory
75 #define HISTORYLENGTH 50
76 public:
77 SelectionHistory(void)
78 : location(0)
80 lastselected.reserve(HISTORYLENGTH);
82 void Add(CGitHash &hash)
84 if (hash.IsEmpty())
85 return;
87 size_t size = lastselected.size();
89 // re-select last selected commit
90 if (size > 0 && hash == lastselected[size - 1])
92 // reset location
93 if (location != size - 1)
94 location = size - 1;
95 return;
98 // go back and some commit was highlight
99 if (size > 0 && location != size - 1)
101 // Re-select current one, it may be a forked point.
102 if (hash == lastselected[location])
103 // Discard it later.
104 // That is that discarding forward history when a forked entry is really coming.
105 // And user has the chance to Go Forward again in this situation.
106 // IOW, (hash != lastselected[location]) means user wants a forked history,
107 // and this change saves one step from old behavior.
108 return;
110 // Discard forward history if any
111 while (lastselected.size() - 1 > location)
112 lastselected.pop_back();
115 if (lastselected.size() >= HISTORYLENGTH)
116 lastselected.erase(lastselected.cbegin());
118 lastselected.push_back(hash);
119 location = lastselected.size() - 1;
121 BOOL GoBack(CGitHash& historyEntry)
123 if (location < 1)
124 return FALSE;
126 historyEntry = lastselected[--location];
128 return TRUE;
130 BOOL GoForward(CGitHash& historyEntry)
132 if (location >= lastselected.size() - 1)
133 return FALSE;
135 historyEntry = lastselected[++location];
137 return TRUE;
139 private:
140 std::vector<CGitHash> lastselected;
141 size_t location;
144 class CThreadSafePtrArray : public std::vector<GitRevLoglist*>
146 CComCriticalSection *m_critSec;
147 public:
148 CThreadSafePtrArray(CComCriticalSection *section){ m_critSec = section ;}
149 GitRevLoglist* SafeGetAt(size_t i)
151 if(m_critSec)
152 m_critSec->Lock();
154 SCOPE_EXIT
156 if (m_critSec)
157 m_critSec->Unlock();
160 if (i >= size())
161 return nullptr;
163 return (*this)[i];
166 void SafeAdd(GitRevLoglist* newElement)
168 if(m_critSec)
169 m_critSec->Lock();
170 push_back(newElement);
171 if(m_critSec)
172 m_critSec->Unlock();
175 void SafeRemoveAt(size_t i)
177 if (m_critSec)
178 m_critSec->Lock();
180 SCOPE_EXIT
182 if (m_critSec)
183 m_critSec->Unlock();
186 if (i >= size())
187 return;
189 erase(begin() + i);
192 void SafeRemoveAll()
194 if(m_critSec)
195 m_critSec->Lock();
196 clear();
197 if(m_critSec)
198 m_critSec->Unlock();
202 class CGitLogListBase : public CHintListCtrl
204 DECLARE_DYNAMIC(CGitLogListBase)
206 public:
207 CGitLogListBase();
208 virtual ~CGitLogListBase();
209 ProjectProperties m_ProjectProperties;
211 void UpdateProjectProperties()
213 m_ProjectProperties.ReadProps();
215 if ((!m_ProjectProperties.sUrl.IsEmpty())||(!m_ProjectProperties.sCheckRe.IsEmpty()))
216 m_bShowBugtraqColumn = true;
217 else
218 m_bShowBugtraqColumn = false;
221 void ResetWcRev(bool refresh = false)
223 m_wcRev.Clear();
224 m_wcRev.GetSubject().LoadString(IDS_LOG_WORKINGDIRCHANGES);
225 m_wcRev.m_Mark = _T('-');
226 m_wcRev.GetBody().LoadString(IDS_LOG_FETCHINGSTATUS);
227 m_wcRev.m_CallDiffAsync = DiffAsync;
228 InterlockedExchange(&m_wcRev.m_IsDiffFiles, FALSE);
229 if (refresh && m_bShowWC)
230 m_arShownList[0] = &m_wcRev;
233 volatile LONG m_bNoDispUpdates;
234 BOOL m_IsIDReplaceAction;
235 BOOL m_IsOldFirst;
236 void hideFromContextMenu(unsigned __int64 hideMask, bool exclusivelyShow);
237 BOOL m_IsRebaseReplaceGraph;
238 BOOL m_bNoHightlightHead;
240 void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
242 BOOL m_bStrictStopped;
243 BOOL m_bShowBugtraqColumn;
244 BOOL m_bSearchIndex;
245 BOOL m_bCancelled;
246 bool m_bIsCherryPick;
247 unsigned __int64 m_ContextMenuMask;
249 bool m_hasWC;
250 bool m_bShowWC;
251 GitRevLoglist m_wcRev;
252 volatile LONG m_bThreadRunning;
253 CLogCache m_LogCache;
255 CString m_sRange;
256 // don't forget to bump BLAME_COLUMN_VERSION in GitStatusListCtrlHelpers.cpp if you change columns
257 enum
259 LOGLIST_GRAPH,
260 LOGLIST_REBASE,
261 LOGLIST_ID,
262 LOGLIST_HASH,
263 LOGLIST_ACTION,
264 LOGLIST_MESSAGE,
265 LOGLIST_AUTHOR,
266 LOGLIST_DATE,
267 LOGLIST_EMAIL,
268 LOGLIST_COMMIT_NAME,
269 LOGLIST_COMMIT_EMAIL,
270 LOGLIST_COMMIT_DATE,
271 LOGLIST_BUG,
272 LOGLIST_SVNREV,
273 LOGLIST_MESSAGE_MAX=300,
274 LOGLIST_MESSAGE_MIN=200,
276 GIT_LOG_GRAPH = 1<< LOGLIST_GRAPH,
277 GIT_LOG_REBASE = 1<< LOGLIST_REBASE,
278 GIT_LOG_ID = 1<< LOGLIST_ID,
279 GIT_LOG_HASH = 1<< LOGLIST_HASH,
280 GIT_LOG_ACTIONS = 1<< LOGLIST_ACTION,
281 GIT_LOG_MESSAGE = 1<< LOGLIST_MESSAGE,
282 GIT_LOG_AUTHOR = 1<< LOGLIST_AUTHOR,
283 GIT_LOG_DATE = 1<< LOGLIST_DATE,
284 GIT_LOG_EMAIL = 1<< LOGLIST_EMAIL,
285 GIT_LOG_COMMIT_NAME = 1<< LOGLIST_COMMIT_NAME,
286 GIT_LOG_COMMIT_EMAIL= 1<< LOGLIST_COMMIT_EMAIL,
287 GIT_LOG_COMMIT_DATE = 1<< LOGLIST_COMMIT_DATE,
288 GIT_LOGLIST_BUG = 1<< LOGLIST_BUG,
289 GIT_LOGLIST_SVNREV = 1<< LOGLIST_SVNREV,
292 enum
294 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
295 ID_COMPARE = 1, // compare revision with WC
296 ID_SAVEAS,
297 ID_COMPARETWO, // compare two revisions
298 ID_COPY,
299 ID_REVERTREV,
300 ID_MERGEREV,
301 ID_GNUDIFF1, // compare with WC, unified
302 ID_GNUDIFF2, // compare two revisions, unified
303 ID_FINDENTRY,
304 ID_OPEN,
305 ID_BLAME,
306 ID_REPOBROWSE,
307 ID_LOG,
308 ID_EDITNOTE,
309 ID_DIFF,
310 ID_OPENWITH,
311 ID_COPYCLIPBOARD,
312 ID_COPYHASH,
313 ID_REVERTTOREV,
314 ID_BLAMECOMPARE,
315 ID_BLAMEDIFF,
316 ID_VIEWREV,
317 ID_VIEWPATHREV,
318 ID_EXPORT,
319 ID_COMPAREWITHPREVIOUS,
320 ID_BLAMEPREVIOUS,
321 ID_CHERRY_PICK,
322 ID_CREATE_BRANCH,
323 ID_CREATE_TAG,
324 ID_SWITCHTOREV,
325 ID_SWITCHBRANCH,
326 ID_RESET,
327 ID_REBASE_PICK,
328 ID_REBASE_EDIT,
329 ID_REBASE_SQUASH,
330 ID_REBASE_SKIP,
331 ID_COMBINE_COMMIT,
332 ID_STASH_SAVE,
333 ID_STASH_LIST,
334 ID_STASH_POP,
335 ID_REFLOG_STASH_APPLY,
336 ID_REFLOG_DEL,
337 ID_REBASE_TO_VERSION,
338 ID_CREATE_PATCH,
339 ID_DELETE,
340 ID_COMMIT,
341 ID_PUSH,
342 ID_PULL,
343 ID_FETCH,
344 ID_SHOWBRANCHES,
345 ID_COPYCLIPBOARDMESSAGES,
346 ID_BISECTSTART,
347 ID_LOG_VIEWRANGE,
348 ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE,
349 ID_MERGE_ABORT,
350 ID_CLEANUP,
351 ID_SUBMODULE_UPDATE,
352 ID_BISECTGOOD,
353 ID_BISECTBAD,
354 ID_BISECTRESET,
356 enum
358 ID_COPY_ALL,
359 ID_COPY_MESSAGE,
360 ID_COPY_SUBJECT,
361 ID_COPY_HASH,
363 enum FilterShow
365 FILTERSHOW_REFS = 1,
366 FILTERSHOW_MERGEPOINTS = 2,
367 FILTERSHOW_ANYCOMMIT = 4,
368 FILTERSHOW_ALL = FILTERSHOW_ANYCOMMIT | FILTERSHOW_REFS | FILTERSHOW_MERGEPOINTS
370 enum : unsigned int
372 // For Rebase only
373 LOGACTIONS_REBASE_CURRENT = 0x08000000,
374 LOGACTIONS_REBASE_PICK = 0x04000000,
375 LOGACTIONS_REBASE_SQUASH = 0x02000000,
376 LOGACTIONS_REBASE_EDIT = 0x01000000,
377 LOGACTIONS_REBASE_DONE = 0x00800000,
378 LOGACTIONS_REBASE_SKIP = 0x00400000,
379 LOGACTIONS_REBASE_MASK = 0x0FC00000,
380 LOGACTIONS_REBASE_MODE_MASK = 0x07C00000,
382 inline unsigned __int64 GetContextMenuBit(int i){ return ((unsigned __int64 )0x1)<<i ;}
383 static CString GetRebaseActionName(int action);
384 void InsertGitColumn();
385 void CopySelectionToClipBoard(int toCopy = ID_COPY_ALL);
386 void DiffSelectedRevWithPrevious();
387 bool IsSelectionContinuous();
388 int BeginFetchLog();
389 int FillGitLog(CTGitPath* path, CString* range = nullptr, int infomask = CGit::LOG_INFO_STAT | CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
390 int FillGitLog(std::set<CGitHash>& hashes);
391 CString MessageDisplayStr(GitRev* pLogEntry);
392 BOOL IsMatchFilter(bool bRegex, GitRevLoglist* pRev, std::tr1::wregex& pat);
393 bool ShouldShowFilter(GitRevLoglist* pRev, const std::map<CGitHash, std::set<CGitHash>>& commitChildren);
394 void ShowGraphColumn(bool bShow);
395 CString GetTagInfo(GitRev* pLogEntry);
397 CFindDlg *m_pFindDialog;
398 static const UINT m_FindDialogMessage;
399 void OnFind();
401 static const UINT m_ScrollToMessage;
402 static const UINT m_RebaseActionMessage;
404 inline int ShownCountWithStopped() const { return (int)m_arShownList.size() + (m_bStrictStopped ? 1 : 0); }
405 void FetchLogAsync(void* data = nullptr);
406 CThreadSafePtrArray m_arShownList;
407 void Refresh(BOOL IsCleanFilter=TRUE);
408 void RecalculateShownList(CThreadSafePtrArray * pShownlist);
409 void Clear();
411 DWORD m_SelectedFilters;
412 FilterShow m_ShowFilter;
413 bool m_bFilterWithRegex;
414 bool m_bFilterCaseSensitively;
415 CLogDataVector m_logEntries;
416 void RemoveFilter();
417 void StartFilter();
418 bool ValidateRegexp(LPCTSTR regexp_str, std::tr1::wregex& pat, bool bMatchCase = false );
419 CString m_sFilterText;
421 CFilterData m_Filter;
423 CTGitPath m_Path;
424 int m_ShowMask;
425 CGitHash m_lastSelectedHash;
426 SelectionHistory m_selectionHistory;
427 CGitHash m_highlight;
428 int m_ShowRefMask;
430 void GetTimeRange(CTime &oldest,CTime &latest);
431 virtual void GetParentHashes(GitRev* pRev, GIT_REV_LIST& parentHash);
432 virtual void ContextMenuAction(int cmd,int FirstSelect, int LastSelect, CMenu * menu)=0;
433 void ReloadHashMap()
435 m_RefLabelPosMap.clear();
436 m_HashMap.clear();
438 if (g_Git.GetMapHashToFriendName(m_HashMap))
439 MessageBox(g_Git.GetGitLastErr(_T("Could not get all refs.")), _T("TortoiseGit"), MB_ICONERROR);
441 m_CurrentBranch=g_Git.GetCurrentBranch();
443 if (g_Git.GetHash(m_HeadHash, _T("HEAD")))
445 MessageBox(g_Git.GetGitLastErr(_T("Could not get HEAD hash. Quitting...")), _T("TortoiseGit"), MB_ICONERROR);
446 ExitProcess(1);
449 m_wcRev.m_ParentHash.clear();
450 m_wcRev.m_ParentHash.push_back(m_HeadHash);
452 FetchRemoteList();
453 FetchTrackingBranchList();
455 void StartAsyncDiffThread();
456 void StartLoadingThread();
457 void SafeTerminateThread()
459 if (m_LoadingThread && InterlockedExchange(&m_bExitThread, TRUE) == FALSE)
461 DWORD ret = WAIT_TIMEOUT;
462 for (int i = 0; i < 200 && m_bThreadRunning; ++i)
463 ret =::WaitForSingleObject(m_LoadingThread->m_hThread, 100);
464 if (ret == WAIT_TIMEOUT && m_bThreadRunning)
465 ::TerminateThread(m_LoadingThread, 0);
466 m_LoadingThread = nullptr;
470 bool IsInWorkingThread()
472 return (AfxGetThread() == m_LoadingThread);
475 void SetRange(const CString& range)
477 m_sRange = range;
480 CString GetRange() const { return m_sRange; }
482 bool HasFilterText() const { return !m_sFilterText.IsEmpty() && m_sFilterText != _T("!"); }
484 int m_nSearchIndex;
486 volatile LONG m_bExitThread;
487 CWinThread* m_LoadingThread;
488 MAP_HASH_NAME m_HashMap;
489 std::map<CString, std::pair<CString, CString>> m_TrackingMap;
491 public:
492 CString m_ColumnRegKey;
494 protected:
495 typedef struct {
496 CString name;
497 COLORREF color;
498 CString simplifiedName;
499 CString fullName;
500 bool singleRemote;
501 bool hasTracking;
502 bool sameName;
503 CGit::REF_TYPE refType;
504 } REFLABEL;
506 DECLARE_MESSAGE_MAP()
507 afx_msg void OnDestroy();
508 virtual afx_msg void OnNMCustomdrawLoglist(NMHDR *pNMHDR, LRESULT *pResult);
509 virtual afx_msg void OnLvnGetdispinfoLoglist(NMHDR *pNMHDR, LRESULT *pResult);
510 afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam);
511 afx_msg LRESULT OnScrollToMessage(WPARAM wParam, LPARAM lParam);
512 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
513 afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
514 afx_msg LRESULT OnLoad(WPARAM wParam, LPARAM lParam);
515 afx_msg void OnHdnBegintrack(NMHDR *pNMHDR, LRESULT *pResult);
516 afx_msg void OnHdnItemchanging(NMHDR *pNMHDR, LRESULT *pResult);
517 afx_msg void OnColumnResized(NMHDR *pNMHDR, LRESULT *pResult);
518 afx_msg void OnColumnMoved(NMHDR *pNMHDR, LRESULT *pResult);
519 void OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult);
520 afx_msg void OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult);
521 void PreSubclassWindow();
522 virtual BOOL PreTranslateMessage(MSG* pMsg);
523 static UINT LogThreadEntry(LPVOID pVoid);
524 UINT LogThread();
525 bool IsOnStash(int index);
526 bool IsStash(const GitRev * pSelLogEntry);
527 bool IsBisect(const GitRev * pSelLogEntry);
528 void FetchRemoteList();
529 void FetchTrackingBranchList();
530 void FetchLastLogInfo();
531 void FetchFullLogInfo(CString &from, CString &to);
533 virtual afx_msg BOOL OnToolTipText(UINT id, NMHDR * pNMHDR, LRESULT * pResult);
534 virtual INT_PTR OnToolHitTest(CPoint point, TOOLINFO * pTI) const;
535 CString GetToolTipText(int nItem, int nSubItem);
537 /** Checks whether a referenfe label is under pt and returns the index/type
538 * pLogEntry IN: the entry of commit
539 * pt IN: the mouse position in client coordinate
540 * type IN: give the specific reference type, then check if it is the same reference type.
541 * OUT: give CGit::REF_TYPE::UNKNOWN for getting the real type it is.
542 * pShortname OUT: the short name of that reference label
543 * pIndex OUT: the index value of label of that entry
545 bool IsMouseOnRefLabel(const GitRevLoglist* pLogEntry, const POINT& pt, CGit::REF_TYPE& type, CString* pShortname = nullptr, size_t* pIndex = nullptr);
546 bool IsMouseOnRefLabelFromPopupMenu(const GitRevLoglist* pLogEntry, const CPoint& pt, CGit::REF_TYPE& type, CString* pShortname = nullptr, size_t* pIndex = nullptr);
548 void FillBackGround(HDC hdc, DWORD_PTR Index, CRect &rect);
549 void DrawTagBranchMessage(HDC hdc, CRect &rect, INT_PTR index, std::vector<REFLABEL> &refList);
550 void DrawTagBranch(HDC hdc, CDC& W_Dc, HTHEME hTheme, CRect& rect, CRect& rt, LVITEM& rItem, GitRevLoglist* data, std::vector<REFLABEL>& refList);
551 void DrawGraph(HDC,CRect &rect,INT_PTR index);
553 void paintGraphLane(HDC hdc,int laneHeight, int type, int x1, int x2,
554 const COLORREF& col,const COLORREF& activeColor, int top) ;
555 void DrawLine(HDC hdc, int x1, int y1, int x2, int y2){ ::MoveToEx(hdc, x1, y1, nullptr); ::LineTo(hdc, x2, y2); }
557 * Save column widths to the registry
559 void SaveColumnWidths(); // save col widths to the registry
561 BOOL IsEntryInDateRange(int i);
563 int GetHeadIndex();
565 std::vector<GitRevLoglist*> m_AsynDiffList;
566 CComCriticalSection m_AsynDiffListLock;
567 HANDLE m_AsyncDiffEvent;
568 volatile LONG m_AsyncThreadExit;
569 CWinThread* m_DiffingThread;
570 volatile LONG m_AsyncThreadRunning;
572 static int DiffAsync(GitRevLoglist* rev, void* data)
574 ULONGLONG offset=((CGitLogListBase*)data)->m_LogCache.GetOffset(rev->m_CommitHash);
575 if (!offset || ((CGitLogListBase*)data)->m_LogCache.LoadOneItem(*rev, offset))
577 ((CGitLogListBase*)data)->m_AsynDiffListLock.Lock();
578 ((CGitLogListBase*)data)->m_AsynDiffList.push_back(rev);
579 ((CGitLogListBase*)data)->m_AsynDiffListLock.Unlock();
580 ::SetEvent(((CGitLogListBase*)data)->m_AsyncDiffEvent);
581 return 0;
584 InterlockedExchange(&rev->m_IsDiffFiles, TRUE);
585 if (!rev->m_IsCommitParsed)
586 return 0;
587 InterlockedExchange(&rev->m_IsFull, TRUE);
588 // we might need to signal that the changed files are now available
589 if (((CGitLogListBase*)data)->GetSelectedCount() == 1)
591 POSITION pos = ((CGitLogListBase*)data)->GetFirstSelectedItemPosition();
592 int nItem = ((CGitLogListBase*)data)->GetNextSelectedItem(pos);
593 if (nItem >= 0)
595 GitRevLoglist* data2 = (GitRevLoglist*)((CGitLogListBase*)data)->m_arShownList.SafeGetAt(nItem);
596 if (data2 && data2->m_CommitHash == rev->m_CommitHash)
597 ((CGitLogListBase*)data)->GetParent()->PostMessage(WM_COMMAND, MSG_FETCHED_DIFF, 0);
600 return 0;
603 static UINT AsyncThread(LPVOID data)
605 return ((CGitLogListBase*)data)->AsyncDiffThread();
608 int AsyncDiffThread();
610 public:
611 void SafeTerminateAsyncDiffThread()
613 if (m_DiffingThread && InterlockedExchange(&m_AsyncThreadExit, TRUE) == FALSE)
615 ::SetEvent(m_AsyncDiffEvent);
616 DWORD ret = WAIT_TIMEOUT;
617 // do not block here, but process messages and ask until the thread ends
618 while (ret == WAIT_TIMEOUT && m_AsyncThreadRunning)
620 MSG msg;
621 if (::PeekMessage(&msg, nullptr, 0,0, PM_NOREMOVE))
622 AfxGetThread()->PumpMessage(); // process messages, so that GetTopIndex and so on in the thread work
623 ret = ::WaitForSingleObject(m_DiffingThread->m_hThread, 100);
625 m_DiffingThread = nullptr;
626 InterlockedExchange(&m_AsyncThreadExit, FALSE);
630 protected:
631 CComCriticalSection m_critSec;
633 HICON m_hModifiedIcon;
634 HICON m_hReplacedIcon;
635 HICON m_hConflictedIcon;
636 HICON m_hAddedIcon;
637 HICON m_hDeletedIcon;
638 HICON m_hFetchIcon;
640 CFont m_boldFont;
641 CFont m_FontItalics;
642 CFont m_boldItalicsFont;
644 CRegDWORD m_regMaxBugIDColWidth;
646 void *m_ProcData;
648 CColors m_Colors;
650 CString m_CurrentBranch;
651 CGitHash m_HeadHash;
653 COLORREF m_LineColors[Lanes::COLORS_NUM];
654 DWORD m_LineWidth;
655 DWORD m_NodeSize;
656 DWORD m_DateFormat; // DATE_SHORTDATE or DATE_LONGDATE
657 bool m_bRelativeTimes; // Show relative times
658 GIT_LOG m_DllGitLog;
659 CString m_SingleRemote;
660 bool m_bTagsBranchesOnRightSide;
661 bool m_bFullCommitMessageOnLogLine;
662 bool m_bSymbolizeRefNames;
663 bool m_bIncludeBoundaryCommits;
665 ColumnManager m_ColumnManager;
666 DWORD m_dwDefaultColumns;
667 TCHAR m_wszTip[8192];
668 char m_szTip[8192];
669 std::map<CString, CRect> m_RefLabelPosMap; // ref name vs. label position
670 int m_OldTopIndex;