1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2012 - 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
23 #include "HintListCtrl.h"
25 #include "ProjectProperties.h"
28 #include "SplitterControl.h"
30 #include "MenuButton.h"
31 #include "LogDlgHelper.h"
32 #include "FilterEdit.h"
35 //#include "GitLogList.h"
37 #include "GitLogCache.h"
39 #include <GitStatusListCtrl.h>
43 #if (NTDDI_VERSION < NTDDI_LONGHORN)
45 enum LISTITEMSTATES_MINE
{
50 LISS_SELECTEDNOTFOCUS
= 5,
54 // these defines must be in the order the columns are inserted!
57 #define MCS_NOTRAILINGDATES 0x0040
58 #define MCS_SHORTDAYSOFWEEK 0x0080
59 #define MCS_NOSELCHANGEONNAV 0x0100
61 #define DTM_SETMCSTYLE (DTM_FIRST + 11)
65 #define ICONITEMBORDER 5
67 #define GITLOG_START 0
68 #define GITLOG_START_ALL 1
69 #define GITLOG_END 100
71 #define LOGFILTER_ALL 0xFFFF
72 #define LOGFILTER_MESSAGES 0x0001
73 #define LOGFILTER_PATHS 0x0002
74 #define LOGFILTER_AUTHORS 0x0004
75 #define LOGFILTER_REVS 0x0008
76 #define LOGFILTER_REGEX 0x0010
77 #define LOGFILTER_BUGID 0x0020
78 #define LOGFILTER_SUBJECT 0x0040
80 //typedef void CALLBACK_PROCESS(void * data, int progress);
81 #define MSG_LOADED (WM_USER+110)
82 #define MSG_LOAD_PERCENTAGE (WM_USER+111)
83 #define MSG_REFLOG_CHANGED (WM_USER+112)
84 #define MSG_FETCHED_DIFF (WM_USER+113)
86 class CThreadSafePtrArray
: public CPtrArray
88 CComCriticalSection
*m_critSec
;
90 CThreadSafePtrArray(CComCriticalSection
*section
){ m_critSec
= section
;}
91 void * SafeGetAt(INT_PTR i
)
96 if( i
<0 || i
>=GetCount())
109 INT_PTR
SafeAdd(void *newElement
)
114 ret
= Add(newElement
);
131 class CGitLogListBase
: public CHintListCtrl
133 DECLARE_DYNAMIC(CGitLogListBase
)
137 virtual ~CGitLogListBase();
138 ProjectProperties m_ProjectProperties
;
140 CFilterData m_Filter
;
142 void UpdateProjectProperties()
144 m_ProjectProperties
.ReadProps(this->m_Path
);
146 if ((!m_ProjectProperties
.sUrl
.IsEmpty())||(!m_ProjectProperties
.sCheckRe
.IsEmpty()))
147 m_bShowBugtraqColumn
= true;
149 m_bShowBugtraqColumn
= false;
154 m_wcRev
.GetBody()=_T("Fetching Status...");
155 m_wcRev
.m_CallDiffAsync
= DiffAsync
;
156 InterlockedExchange(&m_wcRev
.m_IsDiffFiles
, FALSE
);
158 void SetProjectPropertiesPath(const CTGitPath
& path
) {m_ProjectProperties
.ReadProps(path
);}
160 volatile LONG m_bNoDispUpdates
;
161 BOOL m_IsIDReplaceAction
;
163 void hideFromContextMenu(unsigned __int64 hideMask
, bool exclusivelyShow
);
164 BOOL m_IsRebaseReplaceGraph
;
166 void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct
);
168 BOOL m_bStrictStopped
;
169 BOOL m_bShowBugtraqColumn
;
172 unsigned __int64 m_ContextMenuMask
;
177 volatile LONG m_bThreadRunning
;
178 CLogCache m_LogCache
;
183 // don't forget to bump BLAME_COLUMN_VERSION in GitStatusListCtrlHelpers.cpp if you change columns
196 LOGLIST_COMMIT_EMAIL
,
199 LOGLIST_MESSAGE_MAX
=300,
200 LOGLIST_MESSAGE_MIN
=200,
202 GIT_LOG_GRAPH
= 1<< LOGLIST_GRAPH
,
203 GIT_LOG_REBASE
= 1<< LOGLIST_REBASE
,
204 GIT_LOG_ID
= 1<< LOGLIST_ID
,
205 GIT_LOG_HASH
= 1<< LOGLIST_HASH
,
206 GIT_LOG_ACTIONS
= 1<< LOGLIST_ACTION
,
207 GIT_LOG_MESSAGE
= 1<< LOGLIST_MESSAGE
,
208 GIT_LOG_AUTHOR
= 1<< LOGLIST_AUTHOR
,
209 GIT_LOG_DATE
= 1<< LOGLIST_DATE
,
210 GIT_LOG_EMAIL
= 1<< LOGLIST_EMAIL
,
211 GIT_LOG_COMMIT_NAME
= 1<< LOGLIST_COMMIT_NAME
,
212 GIT_LOG_COMMIT_EMAIL
= 1<< LOGLIST_COMMIT_EMAIL
,
213 GIT_LOG_COMMIT_DATE
= 1<< LOGLIST_COMMIT_DATE
,
214 GIT_LOGLIST_BUG
= 1<< LOGLIST_BUG
,
219 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
220 ID_COMPARE
= 1, // compare revision with WC
222 ID_COMPARETWO
, // compare two revisions
227 ID_GNUDIFF1
, // compare with WC, unified
228 ID_GNUDIFF2
, // compare two revisions, unified
248 ID_COMPAREWITHPREVIOUS
,
249 ID_BLAMEWITHPREVIOUS
,
266 ID_REFLOG_STASH_APPLY
,
268 ID_REBASE_TO_VERSION
,
275 inline unsigned __int64
GetContextMenuBit(int i
){ return ((unsigned __int64
)0x1)<<i
;}
276 void InsertGitColumn();
277 void ResizeAllListCtrlCols();
278 void CopySelectionToClipBoard(bool hashonly
=FALSE
);
279 void DiffSelectedRevWithPrevious();
280 bool IsSelectionContinuous();
282 int FillGitLog(CTGitPath
*path
,int infomask
=CGit:: LOG_INFO_STAT
| CGit::LOG_INFO_FILESTATE
| CGit::LOG_INFO_SHOW_MERGEDFILE
,CString
*from
=NULL
,CString
*to
=NULL
);
283 BOOL
IsMatchFilter(bool bRegex
, GitRev
*pRev
, tr1::wregex
&pat
);
285 CFindDlg
*m_pFindDialog
;
286 static const UINT m_FindDialogMessage
;
289 inline int ShownCountWithStopped() const { return (int)m_arShownList
.GetCount() + (m_bStrictStopped
? 1 : 0); }
290 int FetchLogAsync(void * data
=NULL
);
291 CThreadSafePtrArray m_arShownList
;
292 void Refresh(BOOL IsCleanFilter
=TRUE
);
293 void RecalculateShownList(CThreadSafePtrArray
* pShownlist
);
296 DWORD m_SelectedFilters
;
297 bool m_bFilterWithRegex
;
298 CLogDataVector m_logEntries
;
301 bool ValidateRegexp(LPCTSTR regexp_str
, tr1::wregex
& pat
, bool bMatchCase
= false );
302 CString m_sFilterText
;
309 CGitHash m_lastSelectedHash
;
311 void GetTimeRange(CTime
&oldest
,CTime
&latest
);
312 virtual void ContextMenuAction(int cmd
,int FirstSelect
, int LastSelect
, CMenu
* menu
)=0;
316 g_Git
.GetMapHashToFriendName(m_HashMap
);
317 m_CurrentBranch
=g_Git
.GetCurrentBranch();
318 this->m_HeadHash
=g_Git
.GetHash(_T("HEAD"));
319 m_wcRev
.m_ParentHash
.clear();
320 m_wcRev
.m_ParentHash
.push_back(m_HeadHash
);
322 void SafeTerminateThread()
324 if(m_LoadingThread
!=NULL
)
326 InterlockedExchange(&m_bExitThread
,TRUE
);
327 DWORD ret
=::WaitForSingleObject(m_LoadingThread
->m_hThread
,20000);
328 if(ret
== WAIT_TIMEOUT
)
329 ::TerminateThread(m_LoadingThread
,0);
330 m_LoadingThread
= NULL
;
334 bool IsInWorkingThread()
336 return (AfxGetThread() == m_LoadingThread
);
339 void SetStartRef(const CString
& StartRef
)
344 CString
GetStartRef() const {return m_StartRef
;}
348 volatile LONG m_bExitThread
;
349 CWinThread
* m_LoadingThread
;
350 MAP_HASH_NAME m_HashMap
;
353 CString m_ColumnRegKey
;
356 DECLARE_MESSAGE_MAP()
357 afx_msg
void OnDestroy();
358 virtual afx_msg
void OnNMCustomdrawLoglist(NMHDR
*pNMHDR
, LRESULT
*pResult
);
359 virtual afx_msg
void OnLvnGetdispinfoLoglist(NMHDR
*pNMHDR
, LRESULT
*pResult
);
360 afx_msg LRESULT
OnFindDialogMessage(WPARAM wParam
, LPARAM lParam
);
361 afx_msg
int OnCreate(LPCREATESTRUCT lpCreateStruct
);
362 afx_msg
void OnContextMenu(CWnd
* pWnd
, CPoint point
);
363 afx_msg LRESULT
OnLoad(WPARAM wParam
, LPARAM lParam
);
364 afx_msg
void OnHdnBegintrack(NMHDR
*pNMHDR
, LRESULT
*pResult
);
365 afx_msg
void OnHdnItemchanging(NMHDR
*pNMHDR
, LRESULT
*pResult
);
366 afx_msg
void OnColumnResized(NMHDR
*pNMHDR
, LRESULT
*pResult
);
367 afx_msg
void OnColumnMoved(NMHDR
*pNMHDR
, LRESULT
*pResult
);
368 void OnNMDblclkLoglist(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
);
369 afx_msg
void OnLvnOdfinditemLoglist(NMHDR
*pNMHDR
, LRESULT
*pResult
);
370 void PreSubclassWindow();
371 virtual BOOL
PreTranslateMessage(MSG
* pMsg
);
372 static UINT
LogThreadEntry(LPVOID pVoid
);
374 void FetchLastLogInfo();
375 void FetchFullLogInfo(CString
&from
, CString
&to
);
376 void FillBackGround(HDC hdc
, int Index
,CRect
&rect
);
377 void DrawTagBranch(HDC
,CRect
&rect
,INT_PTR index
);
378 void DrawGraph(HDC
,CRect
&rect
,INT_PTR index
);
380 BOOL
GetShortName(CString ref
, CString
&shortname
,CString prefix
);
381 void paintGraphLane(HDC hdc
,int laneHeight
, int type
, int x1
, int x2
,
382 const COLORREF
& col
,const COLORREF
& activeColor
, int top
) ;
383 void DrawLine(HDC hdc
, int x1
, int y1
, int x2
, int y2
){::MoveToEx(hdc
,x1
,y1
,NULL
);::LineTo(hdc
,x2
,y2
);}
385 * Save column widths to the registry
387 void SaveColumnWidths(); // save col widths to the registry
389 BOOL
IsEntryInDateRange(int i
);
393 std::vector
<GitRev
*> m_AsynDiffList
;
394 CComCriticalSection m_AsynDiffListLock
;
395 HANDLE m_AsyncDiffEvent
;
396 volatile LONG m_AsyncThreadExit
;
397 CWinThread
* m_DiffingThread
;
399 static int DiffAsync(GitRev
*rev
, void *data
)
401 ULONGLONG offset
=((CGitLogListBase
*)data
)->m_LogCache
.GetOffset(rev
->m_CommitHash
);
404 ((CGitLogListBase
*)data
)->m_AsynDiffListLock
.Lock();
405 ((CGitLogListBase
*)data
)->m_AsynDiffList
.push_back(rev
);
406 ((CGitLogListBase
*)data
)->m_AsynDiffListLock
.Unlock();
407 ::SetEvent(((CGitLogListBase
*)data
)->m_AsyncDiffEvent
);
411 if(((CGitLogListBase
*)data
)->m_LogCache
.LoadOneItem(*rev
,offset
))
413 ((CGitLogListBase
*)data
)->m_AsynDiffListLock
.Lock();
414 ((CGitLogListBase
*)data
)->m_AsynDiffList
.push_back(rev
);
415 ((CGitLogListBase
*)data
)->m_AsynDiffListLock
.Unlock();
416 ::SetEvent(((CGitLogListBase
*)data
)->m_AsyncDiffEvent
);
418 InterlockedExchange(&rev
->m_IsDiffFiles
, TRUE
);
419 if(rev
->m_IsDiffFiles
&& rev
->m_IsCommitParsed
)
420 InterlockedExchange(&rev
->m_IsFull
, TRUE
);
425 static UINT
AsyncThread(LPVOID data
)
427 return ((CGitLogListBase
*)data
)->AsyncDiffThread();
430 int AsyncDiffThread();
431 bool m_AsyncThreadExited
;
434 void SafeTerminateAsyncDiffThread()
436 if(m_DiffingThread
!=NULL
&& m_AsyncThreadExit
!= TRUE
)
438 m_AsyncThreadExit
= TRUE
;
439 ::SetEvent(m_AsyncDiffEvent
);
440 DWORD ret
= WAIT_TIMEOUT
;
441 // do not block here, but process messages and ask until the thread ends
442 while (ret
== WAIT_TIMEOUT
&& !m_AsyncThreadExited
)
444 AfxGetThread()->PumpMessage(); // process messages, so that GetTopIndex and so on in the thread work
445 ret
= ::WaitForSingleObject(m_DiffingThread
->m_hThread
, 100);
447 m_DiffingThread
= NULL
;
452 CComCriticalSection m_critSec
;
454 HICON m_hModifiedIcon
;
455 HICON m_hReplacedIcon
;
457 HICON m_hDeletedIcon
;
462 CRegDWORD m_regMaxBugIDColWidth
;
465 CStoreSelection
* m_pStoreSelection
;
469 CString m_CurrentBranch
;
472 CString m_StartRef
; //Ref of the top-commit
474 COLORREF m_LineColors
[Lanes::COLORS_NUM
];
475 DWORD m_DateFormat
; // DATE_SHORTDATE or DATE_LONGDATE
476 bool m_bRelativeTimes
; // Show relative times
479 ColumnManager m_ColumnManager
;
480 DWORD m_dwDefaultColumns
;