LogDlg: Allow to draw tags/branches on right side
[TortoiseGit.git] / src / TortoiseProc / GitLogListBase.h
blobf92d646ef1adb48595cf5c6e516782217526c54f
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013 - 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 "SplitterControl.h"
29 #include "Colors.h"
30 #include "MenuButton.h"
31 #include "LogDlgHelper.h"
32 #include "FilterEdit.h"
33 #include "GitRev.h"
34 #include "Tooltip.h"
35 #include "lanes.h"
36 #include "GitLogCache.h"
37 #include <regex>
38 #include <GitStatusListCtrl.h>
39 #include "FindDlg.h"
41 // CGitLogList
42 #if (NTDDI_VERSION < NTDDI_LONGHORN)
44 enum LISTITEMSTATES_MINE {
45 LISS_NORMAL = 1,
46 LISS_HOT = 2,
47 LISS_SELECTED = 3,
48 LISS_DISABLED = 4,
49 LISS_SELECTEDNOTFOCUS = 5,
50 LISS_HOTSELECTED = 6,
53 // these defines must be in the order the columns are inserted!
56 #define MCS_NOTRAILINGDATES 0x0040
57 #define MCS_SHORTDAYSOFWEEK 0x0080
58 #define MCS_NOSELCHANGEONNAV 0x0100
60 #define DTM_SETMCSTYLE (DTM_FIRST + 11)
62 #endif
64 #define ICONITEMBORDER 5
66 #define GITLOG_START 0
67 #define GITLOG_START_ALL 1
68 #define GITLOG_END 100
70 #define LOGFILTER_ALL 0xFFFF
71 #define LOGFILTER_MESSAGES 0x0001
72 #define LOGFILTER_PATHS 0x0002
73 #define LOGFILTER_AUTHORS 0x0004
74 #define LOGFILTER_REVS 0x0008
75 #define LOGFILTER_REGEX 0x0010
76 #define LOGFILTER_BUGID 0x0020
77 #define LOGFILTER_SUBJECT 0x0040
78 #define LOGFILTER_REFNAME 0x0080
79 #define LOGFILTER_EMAILS 0x0100
81 #define LOGLIST_SHOWNOREFS 0x0000
82 #define LOGLIST_SHOWLOCALBRANCHES 0x0001
83 #define LOGLIST_SHOWREMOTEBRANCHES 0x0002
84 #define LOGLIST_SHOWTAGS 0x0004
85 #define LOGLIST_SHOWSTASH 0x0008
86 #define LOGLIST_SHOWBISECT 0x0010
87 #define LOGLIST_SHOWALLREFS 0xFFFF
89 //typedef void CALLBACK_PROCESS(void * data, int progress);
90 #define MSG_LOADED (WM_USER+110)
91 #define MSG_LOAD_PERCENTAGE (WM_USER+111)
92 #define MSG_REFLOG_CHANGED (WM_USER+112)
93 #define MSG_FETCHED_DIFF (WM_USER+113)
95 class CThreadSafePtrArray: public CPtrArray
97 CComCriticalSection *m_critSec;
98 public:
99 CThreadSafePtrArray(CComCriticalSection *section){ m_critSec = section ;}
100 void * SafeGetAt(INT_PTR i)
102 if(m_critSec)
103 m_critSec->Lock();
105 if( i<0 || i>=GetCount())
107 if(m_critSec)
108 m_critSec->Unlock();
110 return NULL;
113 if(m_critSec)
114 m_critSec->Unlock();
116 return GetAt(i);
118 INT_PTR SafeAdd(void *newElement)
120 INT_PTR ret;
121 if(m_critSec)
122 m_critSec->Lock();
123 ret = Add(newElement);
124 if(m_critSec)
125 m_critSec->Unlock();
126 return ret;
129 void SafeRemoveAll()
131 if(m_critSec)
132 m_critSec->Lock();
133 RemoveAll();
134 if(m_critSec)
135 m_critSec->Unlock();
140 class CGitLogListBase : public CHintListCtrl
142 DECLARE_DYNAMIC(CGitLogListBase)
144 public:
145 CGitLogListBase();
146 virtual ~CGitLogListBase();
147 ProjectProperties m_ProjectProperties;
149 CFilterData m_Filter;
151 void UpdateProjectProperties()
153 // do not crash if config file is broken; needed for TortoiseGitBlame
156 m_ProjectProperties.ReadProps(this->m_Path);
158 catch (char *)
162 if ((!m_ProjectProperties.sUrl.IsEmpty())||(!m_ProjectProperties.sCheckRe.IsEmpty()))
163 m_bShowBugtraqColumn = true;
164 else
165 m_bShowBugtraqColumn = false;
168 void ResetWcRev(bool refresh = false)
170 m_wcRev.Clear();
171 m_wcRev.GetSubject() = CString(MAKEINTRESOURCE(IDS_LOG_WORKINGDIRCHANGES));
172 m_wcRev.m_Mark = _T('-');
173 m_wcRev.GetBody() = CString(MAKEINTRESOURCE(IDS_LOG_FETCHINGSTATUS));
174 m_wcRev.m_CallDiffAsync = DiffAsync;
175 InterlockedExchange(&m_wcRev.m_IsDiffFiles, FALSE);
176 if (refresh && m_bShowWC)
177 m_arShownList[0] = &m_wcRev;
179 void SetProjectPropertiesPath(const CTGitPath& path) {m_ProjectProperties.ReadProps(path);}
181 volatile LONG m_bNoDispUpdates;
182 BOOL m_IsIDReplaceAction;
183 BOOL m_IsOldFirst;
184 void hideFromContextMenu(unsigned __int64 hideMask, bool exclusivelyShow);
185 BOOL m_IsRebaseReplaceGraph;
186 BOOL m_bNoHightlightHead;
188 void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
190 BOOL m_bStrictStopped;
191 BOOL m_bShowBugtraqColumn;
192 BOOL m_bSearchIndex;
193 BOOL m_bCancelled;
194 unsigned __int64 m_ContextMenuMask;
196 bool m_hasWC;
197 bool m_bShowWC;
198 GitRev m_wcRev;
199 volatile LONG m_bThreadRunning;
200 CLogCache m_LogCache;
202 CString m_sRange;
203 // don't forget to bump BLAME_COLUMN_VERSION in GitStatusListCtrlHelpers.cpp if you change columns
204 enum
206 LOGLIST_GRAPH,
207 LOGLIST_REBASE,
208 LOGLIST_ID,
209 LOGLIST_HASH,
210 LOGLIST_ACTION,
211 LOGLIST_MESSAGE,
212 LOGLIST_AUTHOR,
213 LOGLIST_DATE,
214 LOGLIST_EMAIL,
215 LOGLIST_COMMIT_NAME,
216 LOGLIST_COMMIT_EMAIL,
217 LOGLIST_COMMIT_DATE,
218 LOGLIST_BUG,
219 LOGLIST_MESSAGE_MAX=300,
220 LOGLIST_MESSAGE_MIN=200,
222 GIT_LOG_GRAPH = 1<< LOGLIST_GRAPH,
223 GIT_LOG_REBASE = 1<< LOGLIST_REBASE,
224 GIT_LOG_ID = 1<< LOGLIST_ID,
225 GIT_LOG_HASH = 1<< LOGLIST_HASH,
226 GIT_LOG_ACTIONS = 1<< LOGLIST_ACTION,
227 GIT_LOG_MESSAGE = 1<< LOGLIST_MESSAGE,
228 GIT_LOG_AUTHOR = 1<< LOGLIST_AUTHOR,
229 GIT_LOG_DATE = 1<< LOGLIST_DATE,
230 GIT_LOG_EMAIL = 1<< LOGLIST_EMAIL,
231 GIT_LOG_COMMIT_NAME = 1<< LOGLIST_COMMIT_NAME,
232 GIT_LOG_COMMIT_EMAIL= 1<< LOGLIST_COMMIT_EMAIL,
233 GIT_LOG_COMMIT_DATE = 1<< LOGLIST_COMMIT_DATE,
234 GIT_LOGLIST_BUG = 1<< LOGLIST_BUG,
237 enum
239 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
240 ID_COMPARE = 1, // compare revision with WC
241 ID_SAVEAS,
242 ID_COMPARETWO, // compare two revisions
243 ID_COPY,
244 ID_REVERTREV,
245 ID_MERGEREV,
246 ID_GNUDIFF1, // compare with WC, unified
247 ID_GNUDIFF2, // compare two revisions, unified
248 ID_FINDENTRY,
249 ID_OPEN,
250 ID_BLAME,
251 ID_REPOBROWSE,
252 ID_LOG,
253 ID_EDITNOTE,
254 ID_DIFF,
255 ID_OPENWITH,
256 ID_COPYCLIPBOARD,
257 ID_COPYHASH,
258 ID_REVERTTOREV,
259 ID_BLAMECOMPARE,
260 ID_BLAMEDIFF,
261 ID_VIEWREV,
262 ID_VIEWPATHREV,
263 ID_EXPORT,
264 ID_COMPAREWITHPREVIOUS,
265 ID_BLAMEWITHPREVIOUS,
266 ID_CHERRY_PICK,
267 ID_CREATE_BRANCH,
268 ID_CREATE_TAG,
269 ID_SWITCHTOREV,
270 ID_SWITCHBRANCH,
271 ID_RESET,
272 ID_REBASE_PICK,
273 ID_REBASE_EDIT,
274 ID_REBASE_SQUASH,
275 ID_REBASE_SKIP,
276 ID_COMBINE_COMMIT,
277 ID_STASH_SAVE,
278 ID_STASH_LIST,
279 ID_STASH_POP,
280 ID_REFLOG_STASH_APPLY,
281 ID_REFLOG_DEL,
282 ID_REBASE_TO_VERSION,
283 ID_CREATE_PATCH,
284 ID_DELETE,
285 ID_COMMIT,
286 ID_PUSH,
287 ID_FETCH,
288 ID_SHOWBRANCHES,
289 ID_COPYCLIPBOARDMESSAGES,
290 ID_BISECTSTART,
291 ID_LOG_VIEWRANGE,
292 ID_LOG_VIEWRANGE_REACHABLEFROMONLYONE,
293 ID_MERGE_ABORT,
295 enum
297 ID_COPY_ALL,
298 ID_COPY_MESSAGE,
299 ID_COPY_SUBJECT,
300 ID_COPY_HASH,
302 inline unsigned __int64 GetContextMenuBit(int i){ return ((unsigned __int64 )0x1)<<i ;}
303 void InsertGitColumn();
304 void ResizeAllListCtrlCols();
305 void CopySelectionToClipBoard(int toCopy = ID_COPY_ALL);
306 void DiffSelectedRevWithPrevious();
307 bool IsSelectionContinuous();
308 int BeginFetchLog();
309 int FillGitLog(CTGitPath *path, CString *range = NULL, int infomask = CGit::LOG_INFO_STAT| CGit::LOG_INFO_FILESTATE | CGit::LOG_INFO_SHOW_MERGEDFILE);
310 BOOL IsMatchFilter(bool bRegex, GitRev *pRev, std::tr1::wregex &pat);
312 CFindDlg *m_pFindDialog;
313 static const UINT m_FindDialogMessage;
314 void OnFind();
316 static const UINT m_ScrollToMessage;
317 static const UINT m_RebaseActionMessage;
319 inline int ShownCountWithStopped() const { return (int)m_arShownList.GetCount() + (m_bStrictStopped ? 1 : 0); }
320 int FetchLogAsync(void * data=NULL);
321 CThreadSafePtrArray m_arShownList;
322 void Refresh(BOOL IsCleanFilter=TRUE);
323 void RecalculateShownList(CThreadSafePtrArray * pShownlist);
324 void Clear();
326 DWORD m_SelectedFilters;
327 bool m_bFilterWithRegex;
328 CLogDataVector m_logEntries;
329 void RemoveFilter();
330 void StartFilter();
331 bool ValidateRegexp(LPCTSTR regexp_str, std::tr1::wregex& pat, bool bMatchCase = false );
332 CString m_sFilterText;
334 __time64_t m_From;
335 __time64_t m_To;
337 CTGitPath m_Path;
338 int m_ShowMask;
339 CGitHash m_lastSelectedHash;
340 int m_ShowRefMask;
342 void GetTimeRange(CTime &oldest,CTime &latest);
343 virtual void ContextMenuAction(int cmd,int FirstSelect, int LastSelect, CMenu * menu)=0;
344 void ReloadHashMap()
346 m_HashMap.clear();
348 if (g_Git.GetMapHashToFriendName(m_HashMap))
349 MessageBox(g_Git.GetGitLastErr(_T("Could not get all refs.")), _T("TortoiseGit"), MB_ICONERROR);
351 m_CurrentBranch=g_Git.GetCurrentBranch();
353 if (g_Git.GetHash(m_HeadHash, _T("HEAD")))
355 MessageBox(g_Git.GetGitLastErr(_T("Could not get HEAD hash. Quitting...")), _T("TortoiseGit"), MB_ICONERROR);
356 ExitProcess(1);
359 m_wcRev.m_ParentHash.clear();
360 m_wcRev.m_ParentHash.push_back(m_HeadHash);
362 FetchRemoteList();
363 FetchTrackingBranchList();
365 void SafeTerminateThread()
367 if (m_LoadingThread!=NULL && InterlockedExchange(&m_bExitThread, TRUE) == FALSE)
369 DWORD ret = WAIT_TIMEOUT;
370 for (int i = 0; i < 200 && m_bThreadRunning; ++i)
371 ret =::WaitForSingleObject(m_LoadingThread->m_hThread, 100);
372 if (ret == WAIT_TIMEOUT && m_bThreadRunning)
373 ::TerminateThread(m_LoadingThread, 0);
374 m_LoadingThread = NULL;
378 bool IsInWorkingThread()
380 return (AfxGetThread() == m_LoadingThread);
383 void SetRange(const CString& range)
385 m_sRange = range;
388 CString GetRange() const { return m_sRange; }
390 int m_nSearchIndex;
392 volatile LONG m_bExitThread;
393 CWinThread* m_LoadingThread;
394 MAP_HASH_NAME m_HashMap;
395 std::map<CString, std::pair<CString, CString>> m_TrackingMap;
397 public:
398 CString m_ColumnRegKey;
399 CComCriticalSection m_critSec_AsyncDiff;
401 protected:
402 typedef struct {
403 CString name;
404 COLORREF color;
405 CString simplifiedName;
406 bool singleRemote;
407 bool hasTracking;
408 bool sameName;
409 bool annotatedTag;
410 } REFLABEL;
412 DECLARE_MESSAGE_MAP()
413 afx_msg void OnDestroy();
414 virtual afx_msg void OnNMCustomdrawLoglist(NMHDR *pNMHDR, LRESULT *pResult);
415 virtual afx_msg void OnLvnGetdispinfoLoglist(NMHDR *pNMHDR, LRESULT *pResult);
416 afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam);
417 afx_msg LRESULT OnScrollToMessage(WPARAM wParam, LPARAM lParam);
418 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
419 afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
420 afx_msg LRESULT OnLoad(WPARAM wParam, LPARAM lParam);
421 afx_msg void OnHdnBegintrack(NMHDR *pNMHDR, LRESULT *pResult);
422 afx_msg void OnHdnItemchanging(NMHDR *pNMHDR, LRESULT *pResult);
423 afx_msg void OnColumnResized(NMHDR *pNMHDR, LRESULT *pResult);
424 afx_msg void OnColumnMoved(NMHDR *pNMHDR, LRESULT *pResult);
425 void OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult);
426 afx_msg void OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult);
427 void PreSubclassWindow();
428 virtual BOOL PreTranslateMessage(MSG* pMsg);
429 static UINT LogThreadEntry(LPVOID pVoid);
430 UINT LogThread();
431 void FetchRemoteList();
432 void FetchTrackingBranchList();
433 void FetchLastLogInfo();
434 void FetchFullLogInfo(CString &from, CString &to);
435 void FillBackGround(HDC hdc, DWORD_PTR Index, CRect &rect);
436 void DrawTagBranchMessage(HDC hdc, CRect &rect, INT_PTR index, std::vector<REFLABEL> &refList);
437 void CGitLogListBase::DrawTagBranch(HDC hdc, CDC &W_Dc, HTHEME hTheme, CRect &rect, CRect &rt, LVITEM &rItem, GitRev* data, std::vector<REFLABEL> &refList);
438 void DrawGraph(HDC,CRect &rect,INT_PTR index);
440 void paintGraphLane(HDC hdc,int laneHeight, int type, int x1, int x2,
441 const COLORREF& col,const COLORREF& activeColor, int top) ;
442 void DrawLine(HDC hdc, int x1, int y1, int x2, int y2){::MoveToEx(hdc,x1,y1,NULL);::LineTo(hdc,x2,y2);}
444 * Save column widths to the registry
446 void SaveColumnWidths(); // save col widths to the registry
448 BOOL IsEntryInDateRange(int i);
450 int GetHeadIndex();
452 std::vector<GitRev*> m_AsynDiffList;
453 CComCriticalSection m_AsynDiffListLock;
454 HANDLE m_AsyncDiffEvent;
455 volatile LONG m_AsyncThreadExit;
456 CWinThread* m_DiffingThread;
458 static int DiffAsync(GitRev *rev, void *data)
460 ULONGLONG offset=((CGitLogListBase*)data)->m_LogCache.GetOffset(rev->m_CommitHash);
461 if(!offset)
463 ((CGitLogListBase*)data)->m_AsynDiffListLock.Lock();
464 ((CGitLogListBase*)data)->m_AsynDiffList.push_back(rev);
465 ((CGitLogListBase*)data)->m_AsynDiffListLock.Unlock();
466 ::SetEvent(((CGitLogListBase*)data)->m_AsyncDiffEvent);
468 else
470 if(((CGitLogListBase*)data)->m_LogCache.LoadOneItem(*rev,offset))
472 ((CGitLogListBase*)data)->m_AsynDiffListLock.Lock();
473 ((CGitLogListBase*)data)->m_AsynDiffList.push_back(rev);
474 ((CGitLogListBase*)data)->m_AsynDiffListLock.Unlock();
475 ::SetEvent(((CGitLogListBase*)data)->m_AsyncDiffEvent);
477 InterlockedExchange(&rev->m_IsDiffFiles, TRUE);
478 if(rev->m_IsDiffFiles && rev->m_IsCommitParsed)
479 InterlockedExchange(&rev->m_IsFull, TRUE);
481 return 0;
484 static UINT AsyncThread(LPVOID data)
486 return ((CGitLogListBase*)data)->AsyncDiffThread();
489 int AsyncDiffThread();
490 bool m_AsyncThreadExited;
492 int SafeGetAction(GitRev *rev, int **ptr = nullptr)
496 int *p = &rev->GetAction(this);
497 if (ptr)
498 *ptr = p;
499 return *p;
501 catch (const char* msg)
503 MessageBox(_T("Could not get action of commit ") + rev->m_CommitHash.ToString() + _T(".\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
504 return 0;
508 public:
509 void SafeTerminateAsyncDiffThread()
511 if(m_DiffingThread!=NULL && m_AsyncThreadExit != TRUE)
513 m_AsyncThreadExit = TRUE;
514 ::SetEvent(m_AsyncDiffEvent);
515 DWORD ret = WAIT_TIMEOUT;
516 // do not block here, but process messages and ask until the thread ends
517 while (ret == WAIT_TIMEOUT && !m_AsyncThreadExited)
519 MSG msg;
520 if (::PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE))
521 AfxGetThread()->PumpMessage(); // process messages, so that GetTopIndex and so on in the thread work
522 ret = ::WaitForSingleObject(m_DiffingThread->m_hThread, 100);
524 m_DiffingThread = NULL;
528 protected:
529 CComCriticalSection m_critSec;
531 HICON m_hModifiedIcon;
532 HICON m_hReplacedIcon;
533 HICON m_hAddedIcon;
534 HICON m_hDeletedIcon;
535 HICON m_hFetchIcon;
537 HFONT m_boldFont;
539 CRegDWORD m_regMaxBugIDColWidth;
541 void *m_ProcData;
542 CStoreSelection* m_pStoreSelection;
544 CColors m_Colors;
546 CString m_CurrentBranch;
547 CGitHash m_HeadHash;
549 COLORREF m_LineColors[Lanes::COLORS_NUM];
550 DWORD m_LineWidth;
551 DWORD m_NodeSize;
552 DWORD m_DateFormat; // DATE_SHORTDATE or DATE_LONGDATE
553 bool m_bRelativeTimes; // Show relative times
554 GIT_LOG m_DllGitLog;
555 CString m_SingleRemote;
556 bool m_bTagsBranchesOnRightSide;
557 bool m_bSymbolizeRefNames;
558 bool m_bIncludeBoundaryCommits;
560 ColumnManager m_ColumnManager;
561 DWORD m_dwDefaultColumns;