Save HEADER position and width at log dialog
[TortoiseGit.git] / src / TortoiseProc / GitLogListBase.cpp
blob8a536d8a8533ece5d2459006b54b47111d18de53
1 // GitLogList.cpp : implementation file
2 //
3 /*
4 Description: qgit revision list view
6 Author: Marco Costalba (C) 2005-2007
8 Copyright: See COPYING file that comes with this distribution
11 #include "stdafx.h"
12 #include "resource.h"
13 #include "GitLogListBase.h"
14 #include "GitRev.h"
15 //#include "VssStyle.h"
16 #include "IconMenu.h"
17 // CGitLogListBase
18 #include "cursor.h"
19 #include "InputDlg.h"
20 #include "PropDlg.h"
21 #include "SVNProgressDlg.h"
22 #include "ProgressDlg.h"
23 //#include "RepositoryBrowser.h"
24 //#include "CopyDlg.h"
25 //#include "StatGraphDlg.h"
26 #include "Logdlg.h"
27 #include "MessageBox.h"
28 #include "Registry.h"
29 #include "AppUtils.h"
30 #include "PathUtils.h"
31 #include "StringUtils.h"
32 #include "UnicodeUtils.h"
33 #include "TempFile.h"
34 //#include "GitInfo.h"
35 //#include "GitDiff.h"
36 #include "IconMenu.h"
37 //#include "RevisionRangeDlg.h"
38 //#include "BrowseFolder.h"
39 //#include "BlameDlg.h"
40 //#include "Blame.h"
41 //#include "GitHelpers.h"
42 #include "GitStatus.h"
43 //#include "LogDlgHelper.h"
44 //#include "CachedLogInfo.h"
45 //#include "RepositoryInfo.h"
46 //#include "EditPropertiesDlg.h"
47 #include "FileDiffDlg.h"
48 #include "..\\TortoiseShell\\Resource.h"
50 const UINT CGitLogListBase::m_FindDialogMessage = RegisterWindowMessage(FINDMSGSTRING);
52 IMPLEMENT_DYNAMIC(CGitLogListBase, CHintListCtrl)
54 CGitLogListBase::CGitLogListBase():CHintListCtrl()
55 ,m_regMaxBugIDColWidth(_T("Software\\TortoiseGit\\MaxBugIDColWidth"), 200)
56 ,m_nSearchIndex(0)
57 ,m_bNoDispUpdates(FALSE)
58 , m_bThreadRunning(FALSE)
59 , m_bStrictStopped(false)
60 , m_pStoreSelection(NULL)
61 , m_nSelectedFilter(LOGFILTER_ALL)
62 , m_bVista(false)
63 , m_bShowWC(false)
64 , m_logEntries(&m_LogCache)
65 , m_pFindDialog(NULL)
66 , m_ColumnManager(this)
67 , m_dwDefaultColumns(0)
69 // use the default GUI font, create a copy of it and
70 // change the copy to BOLD (leave the rest of the font
71 // the same)
72 HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
73 LOGFONT lf = {0};
74 GetObject(hFont, sizeof(LOGFONT), &lf);
75 lf.lfWeight = FW_BOLD;
76 m_boldFont = CreateFontIndirect(&lf);
78 m_bShowBugtraqColumn=0;
80 m_IsIDReplaceAction=FALSE;
82 this->m_critSec.Init();
83 m_wcRev.m_CommitHash.Empty();
84 m_wcRev.m_Subject=_T("Working dir changes");
85 m_wcRev.m_ParentHash.clear();
86 m_wcRev.m_Mark=_T('-');
87 m_wcRev.m_IsUpdateing=FALSE;
88 m_wcRev.m_IsFull = TRUE;
90 m_hModifiedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONMODIFIED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
91 m_hReplacedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONREPLACED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
92 m_hAddedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONADDED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
93 m_hDeletedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONDELETED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
95 m_bFilterWithRegex = !!CRegDWORD(_T("Software\\TortoiseGit\\UseRegexFilter"), TRUE);
97 g_Git.GetMapHashToFriendName(m_HashMap);
98 m_CurrentBranch=g_Git.GetCurrentBranch();
99 this->m_HeadHash=g_Git.GetHash(CString(_T("HEAD"))).Left(40);
101 m_From=CTime(1970,1,2,0,0,0);
102 m_To=CTime::GetCurrentTime();
103 m_ShowMask = 0;
104 m_LoadingThread = NULL;
106 InterlockedExchange(&m_bExitThread,FALSE);
107 m_IsOldFirst = FALSE;
108 m_IsRebaseReplaceGraph = FALSE;
111 for(int i=0;i<Lanes::COLORS_NUM;i++)
113 m_LineColors[i] = m_Colors.GetColor((CColors::Colors)(CColors::BranchLine1+i));
115 // get short/long datetime setting from registry
116 DWORD RegUseShortDateFormat = CRegDWORD(_T("Software\\TortoiseGit\\LogDateFormat"), TRUE);
117 if ( RegUseShortDateFormat )
119 m_DateFormat = DATE_SHORTDATE;
121 else
123 m_DateFormat = DATE_LONGDATE;
125 // get relative time display setting from registry
126 DWORD regRelativeTimes = CRegDWORD(_T("Software\\TortoiseGit\\RelativeTimes"), FALSE);
127 m_bRelativeTimes = (regRelativeTimes != 0);
128 m_ContextMenuMask = 0xFFFFFFFFFFFFFFFF;
130 m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_PICK);
131 m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_SQUASH);
132 m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_EDIT);
133 m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_SKIP);
135 OSVERSIONINFOEX inf;
136 SecureZeroMemory(&inf, sizeof(OSVERSIONINFOEX));
137 inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
138 GetVersionEx((OSVERSIONINFO *)&inf);
139 WORD fullver = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);
140 m_bVista = (fullver >= 0x0600);
142 m_ColumnRegKey=_T("log");
145 CGitLogListBase::~CGitLogListBase()
147 InterlockedExchange(&m_bNoDispUpdates, TRUE);
149 DestroyIcon(m_hModifiedIcon);
150 DestroyIcon(m_hReplacedIcon);
151 DestroyIcon(m_hAddedIcon);
152 DestroyIcon(m_hDeletedIcon);
153 m_logEntries.ClearAll();
155 if (m_boldFont)
156 DeleteObject(m_boldFont);
158 if ( m_pStoreSelection )
160 delete m_pStoreSelection;
161 m_pStoreSelection = NULL;
164 SafeTerminateThread();
168 BEGIN_MESSAGE_MAP(CGitLogListBase, CHintListCtrl)
169 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
170 ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdrawLoglist)
171 ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetdispinfoLoglist)
172 ON_WM_CONTEXTMENU()
173 ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclkLoglist)
174 ON_NOTIFY_REFLECT(LVN_ODFINDITEM,OnLvnOdfinditemLoglist)
175 ON_WM_CREATE()
176 ON_WM_DESTROY()
177 ON_MESSAGE(MSG_LOADED,OnLoad)
178 ON_WM_MEASUREITEM()
179 ON_WM_MEASUREITEM_REFLECT()
180 ON_NOTIFY(HDN_BEGINTRACKA, 0, OnHdnBegintrack)
181 ON_NOTIFY(HDN_BEGINTRACKW, 0, OnHdnBegintrack)
182 ON_NOTIFY(HDN_ITEMCHANGINGA, 0, OnHdnItemchanging)
183 ON_NOTIFY(HDN_ITEMCHANGINGW, 0, OnHdnItemchanging)
184 ON_NOTIFY(HDN_ENDTRACK, 0, OnColumnResized)
185 ON_NOTIFY(HDN_ENDDRAG, 0, OnColumnMoved)
186 END_MESSAGE_MAP()
188 void CGitLogListBase::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
190 // TODO: ÔÚ´ËÌí¼ÓÏûÏ¢´¦Àí³ÌÐò´úÂëºÍ/»òµ÷ÓÃĬÈÏÖµ
192 CListCtrl::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
195 void CGitLogListBase::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
197 //if (m_nRowHeight>0)
199 lpMeasureItemStruct->itemHeight = 50;
203 int CGitLogListBase:: OnCreate(LPCREATESTRUCT lpCreateStruct)
205 PreSubclassWindow();
206 return CHintListCtrl::OnCreate(lpCreateStruct);
209 void CGitLogListBase::PreSubclassWindow()
211 SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES);
212 // load the icons for the action columns
213 // m_Theme.Open(m_hWnd, L"ListView");
214 m_Theme.Open(m_hWnd, L"Explorer::ListView;ListView");
215 m_Theme.SetWindowTheme(m_hWnd, L"Explorer", NULL);
216 CHintListCtrl::PreSubclassWindow();
219 void CGitLogListBase::InsertGitColumn()
221 CString temp;
223 CRegDWORD regFullRowSelect(_T("Software\\TortoiseGit\\FullRowSelect"), TRUE);
224 DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_SUBITEMIMAGES;
225 if (DWORD(regFullRowSelect))
226 exStyle |= LVS_EX_FULLROWSELECT;
227 SetExtendedStyle(exStyle);
229 static UINT normal[] =
231 IDS_LOG_GRAPH,
232 IDS_LOG_REBASE,
233 IDS_LOG_ID,
234 IDS_LOG_ACTIONS,
235 IDS_LOG_MESSAGE,
236 IDS_LOG_AUTHOR,
237 IDS_LOG_DATE,
238 IDS_LOG_EMAIL,
239 IDS_LOG_COMMIT_NAME,
240 IDS_LOG_COMMIT_EMAIL,
241 IDS_LOG_COMMIT_DATE,
242 IDS_LOG_BUGIDS,
246 m_dwDefaultColumns = GIT_LOG_GRAPH|GIT_LOG_ACTIONS|GIT_LOG_MESSAGE|GIT_LOG_AUTHOR|GIT_LOG_DATE;
248 if(this->m_IsRebaseReplaceGraph)
250 m_dwDefaultColumns &= ~GIT_LOG_GRAPH;
251 m_dwDefaultColumns |= IDS_LOG_REBASE;
254 if(this->m_IsIDReplaceAction)
256 m_dwDefaultColumns &= ~GIT_LOG_ACTIONS;
257 m_dwDefaultColumns |= IDS_LOG_ID;
259 SetRedraw(false);
261 m_ColumnManager.SetNames(normal, sizeof(normal)/sizeof(UINT));
262 m_ColumnManager.ReadSettings(m_dwDefaultColumns, m_ColumnRegKey+_T("loglist"), sizeof(normal)/sizeof(UINT));
264 SetRedraw(true);
269 * Resizes all columns in a list control to values in registry.
271 void CGitLogListBase::ResizeAllListCtrlCols()
273 // column max and min widths to allow
274 static const int nMinimumWidth = 10;
275 static const int nMaximumWidth = 1000;
276 CHeaderCtrl* pHdrCtrl = (CHeaderCtrl*)(GetDlgItem(0));
277 if (pHdrCtrl)
279 int numcols = pHdrCtrl->GetItemCount();
280 for (int col = 0; col < numcols; col++)
282 // get width for this col last time from registry
283 CString regentry;
284 regentry.Format( _T("Software\\TortoiseGit\\%s\\ColWidth%d"),m_ColumnRegKey, col);
285 CRegDWORD regwidth(regentry, 0);
286 int cx = regwidth;
287 if ( cx == 0 )
289 // no saved value, setup sensible defaults
290 if (col == this->LOGLIST_MESSAGE)
292 cx = LOGLIST_MESSAGE_MIN;
294 else
296 cx = ICONITEMBORDER+16*4;
299 if (cx < nMinimumWidth)
301 cx = nMinimumWidth;
302 } else if (cx > nMaximumWidth)
304 cx = nMaximumWidth;
307 SetColumnWidth(col, cx);
314 BOOL CGitLogListBase::GetShortName(CString ref, CString &shortname,CString prefix)
316 //TRACE(_T("%s %s\r\n"),ref,prefix);
317 if(ref.Left(prefix.GetLength()) == prefix)
319 shortname = ref.Right(ref.GetLength()-prefix.GetLength());
320 if(shortname.Right(3)==_T("^{}"))
321 shortname=shortname.Left(shortname.GetLength()-3);
322 return TRUE;
324 return FALSE;
327 void CGitLogListBase::FillBackGround(HDC hdc, int Index,CRect &rect)
329 // HBRUSH brush;
330 LVITEM rItem;
331 SecureZeroMemory(&rItem, sizeof(LVITEM));
332 rItem.mask = LVIF_STATE;
333 rItem.iItem = Index;
334 rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
335 GetItem(&rItem);
337 GitRev* pLogEntry = (GitRev*)m_arShownList.GetAt(Index);
338 HBRUSH brush = NULL;
341 if (m_Theme.IsAppThemed() && m_bVista)
343 int state = LISS_NORMAL;
344 if (rItem.state & LVIS_SELECTED)
346 if (::GetFocus() == m_hWnd)
347 state |= LISS_SELECTED;
348 else
349 state |= LISS_SELECTEDNOTFOCUS;
351 else
353 if(pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_SQUASH)
354 brush = ::CreateSolidBrush(RGB(156,156,156));
355 else if(pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_EDIT)
356 brush = ::CreateSolidBrush(RGB(200,200,128));
359 if (brush != NULL)
361 ::FillRect(hdc, &rect, brush);
362 ::DeleteObject(brush);
364 else
366 if (m_Theme.IsBackgroundPartiallyTransparent(LVP_LISTITEM, state))
367 m_Theme.DrawParentBackground(m_hWnd, hdc, &rect);
369 CRect rectDraw = rect;
370 if(rItem.state & LVIS_SELECTED)
371 rectDraw.InflateRect(1,0);
372 else
373 rectDraw.InflateRect(1,1);
375 m_Theme.DrawBackground(hdc, LVP_LISTITEM, state, rectDraw, &rect);
378 else
381 if (rItem.state & LVIS_SELECTED)
383 if (::GetFocus() == m_hWnd)
384 brush = ::CreateSolidBrush(::GetSysColor(COLOR_HIGHLIGHT));
385 else
386 brush = ::CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));
388 else
390 //if (pLogEntry->bCopiedSelf)
391 // brush = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));
392 //else
393 if(pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_SQUASH)
394 brush = ::CreateSolidBrush(RGB(156,156,156));
395 else if(pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_EDIT)
396 brush = ::CreateSolidBrush(RGB(200,200,128));
397 else
398 brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
400 if (brush == NULL)
401 return;
403 ::FillRect(hdc, &rect, brush);
404 ::DeleteObject(brush);
409 void CGitLogListBase::DrawTagBranch(HDC hdc,CRect &rect,INT_PTR index)
411 GitRev* data = (GitRev*)m_arShownList.GetAt(index);
412 CRect rt=rect;
413 LVITEM rItem;
414 SecureZeroMemory(&rItem, sizeof(LVITEM));
415 rItem.mask = LVIF_STATE;
416 rItem.iItem = index;
417 rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
418 GetItem(&rItem);
420 for(unsigned int i=0;i<m_HashMap[data->m_CommitHash].size();i++)
422 CString str;
423 str=m_HashMap[data->m_CommitHash][i];
425 CString shortname;
426 HBRUSH brush = 0;
427 shortname = _T("");
428 COLORREF colRef = 0;
430 //Determine label color
431 if(GetShortName(str,shortname,_T("refs/heads/")))
433 if( shortname == m_CurrentBranch )
434 colRef = m_Colors.GetColor(CColors::CurrentBranch);
435 else
436 colRef = m_Colors.GetColor(CColors::LocalBranch);
438 }else if(GetShortName(str,shortname,_T("refs/remotes/")))
440 colRef = m_Colors.GetColor(CColors::RemoteBranch);
442 else if(GetShortName(str,shortname,_T("refs/tags/")))
444 colRef = m_Colors.GetColor(CColors::Tag);
446 else if(GetShortName(str,shortname,_T("refs/stash")))
448 colRef = m_Colors.GetColor(CColors::Stash);
449 shortname=_T("stash");
452 //When row selected, ajust label color
453 if (!(m_Theme.IsAppThemed() && m_bVista))
454 if (rItem.state & LVIS_SELECTED)
455 colRef = CColors::MixColors(colRef, ::GetSysColor(COLOR_HIGHLIGHT), 150);
457 brush = ::CreateSolidBrush(colRef);
460 if(!shortname.IsEmpty())
462 SIZE size;
463 memset(&size,0,sizeof(SIZE));
464 GetTextExtentPoint32(hdc, shortname,shortname.GetLength(),&size);
466 rt.SetRect(rt.left,rt.top,rt.left+size.cx,rt.bottom);
467 rt.right+=8;
469 //Fill interior of ref label
470 ::FillRect(hdc, &rt, brush);
472 //Draw edge of label
473 CDC W_Dc;
474 W_Dc.Attach(hdc);
476 CRect rectEdge = rt;
478 W_Dc.Draw3dRect(rectEdge, m_Colors.Lighten(colRef,100), m_Colors.Darken(colRef,100));
479 rectEdge.DeflateRect(1,1);
480 W_Dc.Draw3dRect(rectEdge, m_Colors.Lighten(colRef,50), m_Colors.Darken(colRef,50));
482 W_Dc.Detach();
484 //Draw text inside label
485 if (m_Theme.IsAppThemed() && m_bVista)
487 int txtState = LISS_NORMAL;
488 if (rItem.state & LVIS_SELECTED)
489 txtState = LISS_SELECTED;
491 m_Theme.DrawText(hdc, LVP_LISTITEM, txtState, shortname, -1, DT_CENTER | DT_SINGLELINE | DT_VCENTER, 0, &rt);
493 else
495 if (rItem.state & LVIS_SELECTED)
497 COLORREF clrNew = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
498 COLORREF clrOld = ::SetTextColor(hdc,clrNew);
499 ::DrawText(hdc,shortname,shortname.GetLength(),&rt,DT_CENTER | DT_SINGLELINE | DT_VCENTER);
500 ::SetTextColor(hdc,clrOld);
501 }else
503 ::DrawText(hdc,shortname,shortname.GetLength(),&rt,DT_CENTER | DT_SINGLELINE | DT_VCENTER);
508 //::MoveToEx(hdc,rt.left,rt.top,NULL);
509 //::LineTo(hdc,rt.right,rt.top);
510 //::LineTo(hdc,rt.right,rt.bottom);
511 //::LineTo(hdc,rt.left,rt.bottom);
512 //::LineTo(hdc,rt.left,rt.top);
515 rt.left=rt.right+1;
517 if(brush)
518 ::DeleteObject(brush);
520 rt.right=rect.right;
522 if (m_Theme.IsAppThemed() && m_bVista)
524 int txtState = LISS_NORMAL;
525 if (rItem.state & LVIS_SELECTED)
526 txtState = LISS_SELECTED;
528 m_Theme.DrawText(hdc, LVP_LISTITEM, txtState, data->m_Subject, -1, DT_LEFT | DT_SINGLELINE | DT_VCENTER, 0, &rt);
530 else
532 if (rItem.state & LVIS_SELECTED)
534 COLORREF clrOld = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT));
535 ::DrawText(hdc,data->m_Subject,data->m_Subject.GetLength(),&rt,DT_LEFT | DT_SINGLELINE | DT_VCENTER);
536 ::SetTextColor(hdc,clrOld);
537 }else
539 ::DrawText(hdc,data->m_Subject,data->m_Subject.GetLength(),&rt,DT_LEFT | DT_SINGLELINE | DT_VCENTER);
544 static COLORREF blend(const COLORREF& col1, const COLORREF& col2, int amount = 128) {
546 // Returns ((256 - amount)*col1 + amount*col2) / 256;
547 return RGB(((256 - amount)*GetRValue(col1) + amount*GetRValue(col2) ) / 256,
548 ((256 - amount)*GetGValue(col1) + amount*GetGValue(col2) ) / 256,
549 ((256 - amount)*GetBValue(col1) + amount*GetBValue(col2) ) / 256);
552 Gdiplus::Color GetGdiColor(COLORREF col)
554 return Gdiplus::Color(GetRValue(col),GetGValue(col),GetBValue(col));
556 void CGitLogListBase::paintGraphLane(HDC hdc, int laneHeight,int type, int x1, int x2,
557 const COLORREF& col,const COLORREF& activeColor, int top
560 int h = laneHeight / 2;
561 int m = (x1 + x2) / 2;
562 int r = (x2 - x1) / 3;
563 int d = 2 * r;
565 #define P_CENTER m , h+top
566 #define P_0 x2, h+top
567 #define P_90 m , 0+top-1
568 #define P_180 x1, h+top
569 #define P_270 m , 2 * h+top +1
570 #define R_CENTER m - r, h - r+top, d, d
573 #define DELTA_UR_B 2*(x1 - m), 2*h +top
574 #define DELTA_UR_E 0*16, 90*16 +top // -,
576 #define DELTA_DR_B 2*(x1 - m), 2*-h +top
577 #define DELTA_DR_E 270*16, 90*16 +top // -'
579 #define DELTA_UL_B 2*(x2 - m), 2*h +top
580 #define DELTA_UL_E 90*16, 90*16 +top // ,-
582 #define DELTA_DL_B 2*(x2 - m),2*-h +top
583 #define DELTA_DL_E 180*16, 90*16 // '-
585 #define CENTER_UR x1, 2*h, 225
586 #define CENTER_DR x1, 0 , 135
587 #define CENTER_UL x2, 2*h, 315
588 #define CENTER_DL x2, 0 , 45
591 Gdiplus::Graphics graphics( hdc );
593 graphics.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeAntiAlias);
595 // arc
596 switch (type) {
597 case Lanes::JOIN:
598 case Lanes::JOIN_R:
599 case Lanes::HEAD:
600 case Lanes::HEAD_R:
602 Gdiplus::LinearGradientBrush gradient(
603 Gdiplus::Point(x1-2, h+top-2),
604 Gdiplus::Point(P_270),
605 GetGdiColor(activeColor),GetGdiColor(col));
608 Gdiplus::Pen mypen(&gradient,2);
609 //Gdiplus::Pen mypen(Gdiplus::Color(0,0,0),2);
611 //graphics.DrawRectangle(&mypen,x1-(x2-x1)/2,top+h, x2-x1,laneHeight);
612 graphics.DrawArc(&mypen,x1-(x2-x1)/2-1,top+h-1, x2-x1,laneHeight,270,90);
613 //graphics.DrawLine(&mypen,x1-1,h+top,P_270);
615 break;
617 case Lanes::JOIN_L:
620 Gdiplus::Pen mypen(Gdiplus::Color(0,0,0),2);
623 graphics.DrawArc(&mypen,x1,top+h, x2-x1,laneHeight,270,90);
625 break;
627 case Lanes::TAIL:
628 case Lanes::TAIL_R:
631 Gdiplus::LinearGradientBrush gradient(
632 Gdiplus::Point(x1-2, h+top-2),
633 Gdiplus::Point(P_90),
634 GetGdiColor(activeColor),GetGdiColor(col));
637 Gdiplus::Pen mypen(&gradient,2);
639 graphics.DrawArc(&mypen,x1-(x2-x1)/2-1,top-h-1, x2-x1,laneHeight,0,90);
642 #if 0
643 QConicalGradient gradient(CENTER_DR);
644 gradient.setColorAt(0.375, activeCol);
645 gradient.setColorAt(0.625, col);
646 myPen.setBrush(gradient);
647 p->setPen(myPen);
648 p->drawArc(P_CENTER, DELTA_DR);
649 #endif
650 break;
652 default:
653 break;
657 //static QPen myPen(Qt::black, 2); // fast path here
658 CPen pen;
659 pen.CreatePen(PS_SOLID,2,col);
660 //myPen.setColor(col);
661 HPEN oldpen=(HPEN)::SelectObject(hdc,(HPEN)pen);
663 Gdiplus::Pen myPen(GetGdiColor(col),2);
665 graphics.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeNone);
667 //p->setPen(myPen);
669 // vertical line
670 switch (type) {
671 case Lanes::ACTIVE:
672 case Lanes::NOT_ACTIVE:
673 case Lanes::MERGE_FORK:
674 case Lanes::MERGE_FORK_R:
675 case Lanes::MERGE_FORK_L:
676 case Lanes::JOIN:
677 case Lanes::JOIN_R:
678 case Lanes::JOIN_L:
679 case Lanes::CROSS:
680 //DrawLine(hdc,P_90,P_270);
681 graphics.DrawLine(&myPen,P_90,P_270);
682 //p->drawLine(P_90, P_270);
683 break;
684 case Lanes::HEAD_L:
685 case Lanes::BRANCH:
686 //DrawLine(hdc,P_CENTER,P_270);
687 graphics.DrawLine(&myPen,P_CENTER,P_270);
688 //p->drawLine(P_CENTER, P_270);
689 break;
690 case Lanes::TAIL_L:
691 case Lanes::INITIAL:
692 case Lanes::BOUNDARY:
693 case Lanes::BOUNDARY_C:
694 case Lanes::BOUNDARY_R:
695 case Lanes::BOUNDARY_L:
696 //DrawLine(hdc,P_90, P_CENTER);
697 graphics.DrawLine(&myPen,P_90,P_CENTER);
698 //p->drawLine(P_90, P_CENTER);
699 break;
700 default:
701 break;
704 myPen.SetColor(GetGdiColor(activeColor));
706 // horizontal line
707 switch (type) {
708 case Lanes::MERGE_FORK:
709 case Lanes::JOIN:
710 case Lanes::HEAD:
711 case Lanes::TAIL:
712 case Lanes::CROSS:
713 case Lanes::CROSS_EMPTY:
714 case Lanes::BOUNDARY_C:
715 //DrawLine(hdc,P_180,P_0);
716 graphics.DrawLine(&myPen,P_180,P_0);
717 //p->drawLine(P_180, P_0);
718 break;
719 case Lanes::MERGE_FORK_R:
720 case Lanes::BOUNDARY_R:
721 //DrawLine(hdc,P_180,P_CENTER);
722 graphics.DrawLine(&myPen,P_180,P_CENTER);
723 //p->drawLine(P_180, P_CENTER);
724 break;
725 case Lanes::MERGE_FORK_L:
726 case Lanes::HEAD_L:
727 case Lanes::TAIL_L:
728 case Lanes::BOUNDARY_L:
729 //DrawLine(hdc,P_CENTER,P_0);
730 graphics.DrawLine(&myPen,P_CENTER,P_0);
731 //p->drawLine(P_CENTER, P_0);
732 break;
733 default:
734 break;
737 graphics.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeAntiAlias);
739 CBrush brush;
740 brush.CreateSolidBrush(col);
741 HBRUSH oldbrush=(HBRUSH)::SelectObject(hdc,(HBRUSH)brush);
743 Gdiplus::SolidBrush myBrush(GetGdiColor(col));
744 // center symbol, e.g. rect or ellipse
745 switch (type) {
746 case Lanes::ACTIVE:
747 case Lanes::INITIAL:
748 case Lanes::BRANCH:
750 //p->setPen(Qt::NoPen);
751 //p->setBrush(col);
752 graphics.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeAntiAlias);
753 graphics.FillEllipse(&myBrush, R_CENTER);
754 //p->drawEllipse(R_CENTER);
755 break;
756 case Lanes::MERGE_FORK:
757 case Lanes::MERGE_FORK_R:
758 case Lanes::MERGE_FORK_L:
759 //p->setPen(Qt::NoPen);
760 //p->setBrush(col);
761 //p->drawRect(R_CENTER);
762 graphics.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeNone);
763 graphics.FillRectangle(&myBrush, R_CENTER);
764 break;
765 case Lanes::UNAPPLIED:
766 // Red minus sign
767 //p->setPen(Qt::NoPen);
768 //p->setBrush(Qt::red);
769 //p->drawRect(m - r, h - 1, d, 2);
770 graphics.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeNone);
771 graphics.FillRectangle(&myBrush,m-r,h-1,d,2);
772 break;
773 case Lanes::APPLIED:
774 // Green plus sign
775 //p->setPen(Qt::NoPen);
776 //p->setBrush(DARK_GREEN);
777 //p->drawRect(m - r, h - 1, d, 2);
778 //p->drawRect(m - 1, h - r, 2, d);
779 graphics.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeNone);
780 graphics.FillRectangle(&myBrush,m-r,h-1,d,2);
781 graphics.FillRectangle(&myBrush,m-1,h-r,2,d);
782 break;
783 case Lanes::BOUNDARY:
784 //p->setBrush(back);
785 //p->drawEllipse(R_CENTER);
786 graphics.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeAntiAlias);
787 graphics.DrawEllipse(&myPen, R_CENTER);
788 break;
789 case Lanes::BOUNDARY_C:
790 case Lanes::BOUNDARY_R:
791 case Lanes::BOUNDARY_L:
792 //p->setBrush(back);
793 //p->drawRect(R_CENTER);
794 graphics.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeNone);
795 graphics.FillRectangle(&myBrush,R_CENTER);
796 break;
797 default:
798 break;
801 ::SelectObject(hdc,oldpen);
802 ::SelectObject(hdc,oldbrush);
803 #undef P_CENTER
804 #undef P_0
805 #undef P_90
806 #undef P_180
807 #undef P_270
808 #undef R_CENTER
811 void CGitLogListBase::DrawGraph(HDC hdc,CRect &rect,INT_PTR index)
813 //todo unfinished
814 // return;
815 GitRev* data = (GitRev*)m_arShownList.GetAt(index);
816 if(data->m_CommitHash.IsEmpty())
817 return;
819 CRect rt=rect;
820 LVITEM rItem;
821 SecureZeroMemory(&rItem, sizeof(LVITEM));
822 rItem.mask = LVIF_STATE;
823 rItem.iItem = index;
824 rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
825 GetItem(&rItem);
828 // p->translate(QPoint(opt.rect.left(), opt.rect.top()));
832 if (data->m_Lanes.size() == 0)
833 m_logEntries.setLane(data->m_CommitHash);
835 std::vector<int>& lanes=data->m_Lanes;
836 UINT laneNum = lanes.size();
837 UINT activeLane = 0;
838 for (UINT i = 0; i < laneNum; i++)
839 if (Lanes::isMerge(lanes[i])) {
840 activeLane = i;
841 break;
844 int x1 = 0, x2 = 0;
845 int maxWidth = rect.Width();
846 int lw = 3 * rect.Height() / 4; //laneWidth()
848 COLORREF activeColor = m_LineColors[activeLane % Lanes::COLORS_NUM];
849 //if (opt.state & QStyle::State_Selected)
850 // activeColor = blend(activeColor, opt.palette.highlightedText().color(), 208);
853 for (unsigned int i = 0; i < laneNum && x2 < maxWidth; i++)
856 x1 = x2;
857 x2 += lw;
859 int ln = lanes[i];
860 if (ln == Lanes::EMPTY)
861 continue;
863 COLORREF color = i == activeLane ? activeColor : m_LineColors[i % Lanes::COLORS_NUM];
864 paintGraphLane(hdc, rect.Height(),ln, x1+rect.left, x2+rect.left, color,activeColor, rect.top);
867 #if 0
868 for (UINT i = 0; i < laneNum && x2 < maxWidth; i++) {
870 x1 = x2;
871 x2 += lw;
873 int ln = lanes[i];
874 if (ln == Lanes::EMPTY)
875 continue;
877 UINT col = ( Lanes:: isHead(ln) ||Lanes:: isTail(ln) || Lanes::isJoin(ln)
878 || ln ==Lanes:: CROSS_EMPTY) ? activeLane : i;
880 if (ln == Lanes::CROSS) {
881 paintGraphLane(hdc, rect.Height(),Lanes::NOT_ACTIVE, x1, x2, m_LineColors[col % Lanes::COLORS_NUM],rect.top);
882 paintGraphLane(hdc, rect.Height(),Lanes::CROSS, x1, x2, m_LineColors[activeLane % Lanes::COLORS_NUM],rect.top);
883 } else
884 paintGraphLane(hdc, rect.Height(),ln, x1, x2, m_LineColors[col % Lanes::COLORS_NUM],rect.top);
886 #endif
890 void CGitLogListBase::OnNMCustomdrawLoglist(NMHDR *pNMHDR, LRESULT *pResult)
893 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
894 // Take the default processing unless we set this to something else below.
895 *pResult = CDRF_DODEFAULT;
897 if (m_bNoDispUpdates)
898 return;
902 switch (pLVCD->nmcd.dwDrawStage)
904 case CDDS_PREPAINT:
906 *pResult = CDRF_NOTIFYITEMDRAW;
907 return;
909 break;
910 case CDDS_ITEMPREPAINT:
912 // This is the prepaint stage for an item. Here's where we set the
913 // item's text color.
915 // Tell Windows to send draw notifications for each subitem.
916 *pResult = CDRF_NOTIFYSUBITEMDRAW;
918 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
920 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
922 GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);
923 if (data)
925 #if 0
926 if (data->bCopiedSelf)
928 // only change the background color if the item is not 'hot' (on vista with m_Themes enabled)
929 if (!m_Theme.IsAppm_Themed() || !m_bVista || ((pLVCD->nmcd.uItemState & CDIS_HOT)==0))
930 pLVCD->clrTextBk = GetSysColor(COLOR_MENU);
933 if (data->bCopies)
934 crText = m_Colors.GetColor(CColors::Modified);
935 #endif
936 if (data->m_Action& (CTGitPath::LOGACTIONS_REBASE_DONE| CTGitPath::LOGACTIONS_REBASE_SKIP) )
937 crText = RGB(128,128,128);
939 if(data->m_Action&CTGitPath::LOGACTIONS_REBASE_SQUASH)
940 pLVCD->clrTextBk = RGB(156,156,156);
941 else if(data->m_Action&CTGitPath::LOGACTIONS_REBASE_EDIT)
942 pLVCD->clrTextBk = RGB(200,200,128);
943 else
944 pLVCD->clrTextBk = ::GetSysColor(COLOR_WINDOW);
946 if(data->m_Action&CTGitPath::LOGACTIONS_REBASE_CURRENT)
948 SelectObject(pLVCD->nmcd.hdc, m_boldFont);
949 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
952 if(data->m_CommitHash.ToString() == m_HeadHash)
954 SelectObject(pLVCD->nmcd.hdc, m_boldFont);
955 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
958 // if ((data->childStackDepth)||(m_mergedRevs.find(data->Rev) != m_mergedRevs.end()))
959 // crText = GetSysColor(COLOR_GRAYTEXT);
961 if (data->m_CommitHash.IsEmpty())
963 //crText = GetSysColor(RGB(200,200,0));
964 //SelectObject(pLVCD->nmcd.hdc, m_boldFont);
965 // We changed the font, so we're returning CDRF_NEWFONT. This
966 // tells the control to recalculate the extent of the text.
967 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
971 if (m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec)
973 if (m_bStrictStopped)
974 crText = GetSysColor(COLOR_GRAYTEXT);
976 // Store the color back in the NMLVCUSTOMDRAW struct.
977 pLVCD->clrText = crText;
978 return;
980 break;
981 case CDDS_ITEMPREPAINT|CDDS_ITEM|CDDS_SUBITEM:
983 if ((m_bStrictStopped)&&(m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec))
985 pLVCD->nmcd.uItemState &= ~(CDIS_SELECTED|CDIS_FOCUS);
988 if (pLVCD->iSubItem == LOGLIST_GRAPH)
990 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec && (!this->m_IsRebaseReplaceGraph) )
992 CRect rect;
993 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
994 if(pLVCD->iSubItem == 0)
996 CRect second;
997 for(int i=1;i< m_ColumnManager.GetColumnCount();i++)
999 if( m_ColumnManager.IsVisible(i))
1001 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem+i, LVIR_BOUNDS, second);
1002 rect.right=second.left;
1007 //TRACE(_T("A Graphic left %d right %d\r\n"),rect.left,rect.right);
1008 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
1010 GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);
1011 if( !data ->m_CommitHash.IsEmpty())
1012 DrawGraph(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);
1014 *pResult = CDRF_SKIPDEFAULT;
1015 return;
1020 if (pLVCD->iSubItem == LOGLIST_MESSAGE)
1022 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
1024 GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);
1025 //if(!data->m_IsFull)
1027 //if(data->SafeFetchFullInfo(&g_Git))
1028 // this->Invalidate();
1029 //TRACE(_T("Update ... %d\r\n"),pLVCD->nmcd.dwItemSpec);
1032 if(m_HashMap[data->m_CommitHash].size()!=0)
1034 CRect rect;
1036 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
1038 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
1039 DrawTagBranch(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);
1041 *pResult = CDRF_SKIPDEFAULT;
1042 return;
1049 if (pLVCD->iSubItem == LOGLIST_ACTION)
1051 if(this->m_IsIDReplaceAction)
1053 *pResult = CDRF_DODEFAULT;
1054 return;
1056 *pResult = CDRF_DODEFAULT;
1058 if (m_arShownList.GetCount() <= (INT_PTR)pLVCD->nmcd.dwItemSpec)
1059 return;
1061 int nIcons = 0;
1062 int iconwidth = ::GetSystemMetrics(SM_CXSMICON);
1063 int iconheight = ::GetSystemMetrics(SM_CYSMICON);
1065 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec));
1066 CRect rect;
1067 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
1068 //TRACE(_T("Action left %d right %d\r\n"),rect.left,rect.right);
1069 // Get the selected state of the
1070 // item being drawn.
1072 // Fill the background
1073 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
1075 // Draw the icon(s) into the compatible DC
1076 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_MODIFIED)
1077 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left + ICONITEMBORDER, rect.top, m_hModifiedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1078 nIcons++;
1080 if (pLogEntry->m_Action & (CTGitPath::LOGACTIONS_ADDED|CTGitPath::LOGACTIONS_COPY) )
1081 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hAddedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1082 nIcons++;
1084 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_DELETED)
1085 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hDeletedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1086 nIcons++;
1088 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_REPLACED)
1089 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hReplacedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1090 nIcons++;
1091 *pResult = CDRF_SKIPDEFAULT;
1092 return;
1095 break;
1097 *pResult = CDRF_DODEFAULT;
1101 // CGitLogListBase message handlers
1103 void CGitLogListBase::OnLvnGetdispinfoLoglist(NMHDR *pNMHDR, LRESULT *pResult)
1105 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1107 // Create a pointer to the item
1108 LV_ITEM* pItem = &(pDispInfo)->item;
1110 // Do the list need text information?
1111 if (!(pItem->mask & LVIF_TEXT))
1112 return;
1114 // By default, clear text buffer.
1115 lstrcpyn(pItem->pszText, _T(""), pItem->cchTextMax);
1117 bool bOutOfRange = pItem->iItem >= ShownCountWithStopped();
1119 *pResult = 0;
1120 if (m_bNoDispUpdates || bOutOfRange)
1121 return;
1123 // Which item number?
1124 int itemid = pItem->iItem;
1125 GitRev * pLogEntry = NULL;
1126 if (itemid < m_arShownList.GetCount())
1127 pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(pItem->iItem));
1129 CString temp;
1130 if(m_IsOldFirst)
1132 temp.Format(_T("%d"),pItem->iItem+1);
1134 }else
1136 temp.Format(_T("%d"),m_arShownList.GetCount()-pItem->iItem);
1139 // Which column?
1140 switch (pItem->iSubItem)
1142 case this->LOGLIST_GRAPH: //Graphic
1143 break;
1144 case this->LOGLIST_REBASE:
1146 if(this->m_IsRebaseReplaceGraph)
1148 CTGitPath path;
1149 path.m_Action=pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_MODE_MASK;
1150 lstrcpyn(pItem->pszText,path.GetActionName(), pItem->cchTextMax);
1153 break;
1154 case this->LOGLIST_ACTION:
1155 break;
1156 case this->LOGLIST_ID: //action -- no text in the column
1157 if(this->m_IsIDReplaceAction)
1158 lstrcpyn(pItem->pszText, temp, pItem->cchTextMax);
1159 break;
1160 case this->LOGLIST_MESSAGE: //Message
1161 if (pLogEntry)
1162 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_Subject, pItem->cchTextMax);
1163 break;
1164 case this->LOGLIST_AUTHOR: //Author
1165 if (pLogEntry)
1166 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_AuthorName, pItem->cchTextMax);
1167 break;
1168 case this->LOGLIST_DATE: //Date
1169 if ( pLogEntry && (!pLogEntry->m_CommitHash.IsEmpty()) )
1170 lstrcpyn(pItem->pszText,
1171 CAppUtils::FormatDateAndTime( pLogEntry->m_AuthorDate, m_DateFormat, true, m_bRelativeTimes ),
1172 pItem->cchTextMax);
1173 break;
1175 case this->LOGLIST_EMAIL:
1176 if (pLogEntry)
1177 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_AuthorEmail, pItem->cchTextMax);
1178 break;
1180 case this->LOGLIST_COMMIT_NAME: //Commit
1181 if (pLogEntry)
1182 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_CommitterName, pItem->cchTextMax);
1183 break;
1185 case this->LOGLIST_COMMIT_EMAIL: //Commit Email
1186 if (pLogEntry)
1187 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_CommitterEmail, pItem->cchTextMax);
1188 break;
1190 case this->LOGLIST_COMMIT_DATE: //Commit Date
1191 if (pLogEntry)
1192 lstrcpyn(pItem->pszText,
1193 CAppUtils::FormatDateAndTime( pLogEntry->m_CommitterDate, m_DateFormat, true, m_bRelativeTimes ),
1194 pItem->cchTextMax);
1195 break;
1196 case this->LOGLIST_BUG: //Bug
1197 break;
1199 default:
1200 ASSERT(false);
1204 void CGitLogListBase::OnContextMenu(CWnd* pWnd, CPoint point)
1207 if (pWnd == GetHeaderCtrl())
1209 return m_ColumnManager.OnContextMenuHeader(pWnd,point,IsGroupViewEnabled());
1212 int selIndex = GetSelectionMark();
1213 if (selIndex < 0)
1214 return; // nothing selected, nothing to do with a context menu
1216 // if the user selected the info text telling about not all revisions shown due to
1217 // the "stop on copy/rename" option, we also don't show the context menu
1218 if ((m_bStrictStopped)&&(selIndex == m_arShownList.GetCount()))
1219 return;
1221 // if the context menu is invoked through the keyboard, we have to use
1222 // a calculated position on where to anchor the menu on
1223 if ((point.x == -1) && (point.y == -1))
1225 CRect rect;
1226 GetItemRect(selIndex, &rect, LVIR_LABEL);
1227 ClientToScreen(&rect);
1228 point = rect.CenterPoint();
1230 m_nSearchIndex = selIndex;
1231 m_bCancelled = FALSE;
1233 // calculate some information the context menu commands can use
1234 // CString pathURL = GetURLFromPath(m_path);
1236 POSITION pos = GetFirstSelectedItemPosition();
1237 int indexNext = GetNextSelectedItem(pos);
1238 if (indexNext < 0)
1239 return;
1241 GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));
1242 #if 0
1243 GitRev revSelected = pSelLogEntry->Rev;
1244 GitRev revPrevious = git_revnum_t(revSelected)-1;
1245 if ((pSelLogEntry->pArChangedPaths)&&(pSelLogEntry->pArChangedPaths->GetCount() <= 2))
1247 for (int i=0; i<pSelLogEntry->pArChangedPaths->GetCount(); ++i)
1249 LogChangedPath * changedpath = (LogChangedPath *)pSelLogEntry->pArChangedPaths->GetAt(i);
1250 if (changedpath->lCopyFromRev)
1251 revPrevious = changedpath->lCopyFromRev;
1254 GitRev revSelected2;
1255 if (pos)
1257 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));
1258 revSelected2 = pLogEntry->Rev;
1260 bool bAllFromTheSameAuthor = true;
1261 CString firstAuthor;
1262 CLogDataVector selEntries;
1263 GitRev revLowest, revHighest;
1264 GitRevRangeArray revisionRanges;
1266 POSITION pos = GetFirstSelectedItemPosition();
1267 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));
1268 revisionRanges.AddRevision(pLogEntry->Rev);
1269 selEntries.push_back(pLogEntry);
1270 firstAuthor = pLogEntry->sAuthor;
1271 revLowest = pLogEntry->Rev;
1272 revHighest = pLogEntry->Rev;
1273 while (pos)
1275 pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));
1276 revisionRanges.AddRevision(pLogEntry->Rev);
1277 selEntries.push_back(pLogEntry);
1278 if (firstAuthor.Compare(pLogEntry->sAuthor))
1279 bAllFromTheSameAuthor = false;
1280 revLowest = (git_revnum_t(pLogEntry->Rev) > git_revnum_t(revLowest) ? revLowest : pLogEntry->Rev);
1281 revHighest = (git_revnum_t(pLogEntry->Rev) < git_revnum_t(revHighest) ? revHighest : pLogEntry->Rev);
1285 #endif
1287 int FirstSelect=-1, LastSelect=-1;
1288 pos = GetFirstSelectedItemPosition();
1289 FirstSelect = GetNextSelectedItem(pos);
1290 while(pos)
1292 LastSelect = GetNextSelectedItem(pos);
1294 //entry is selected, now show the popup menu
1295 CIconMenu popup;
1296 CIconMenu submenu;
1297 if (popup.CreatePopupMenu())
1300 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_PICK))
1301 popup.AppendMenuIcon(ID_REBASE_PICK, IDS_REBASE_PICK, IDI_PICK);
1303 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_SQUASH))
1304 popup.AppendMenuIcon(ID_REBASE_SQUASH,IDS_REBASE_SQUASH, IDI_SQUASH);
1306 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_EDIT))
1307 popup.AppendMenuIcon(ID_REBASE_EDIT, IDS_REBASE_EDIT, IDI_EDIT);
1309 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_SKIP))
1310 popup.AppendMenuIcon(ID_REBASE_SKIP, IDS_REBASE_SKIP, IDI_SKIP);
1312 if(m_ContextMenuMask&(GetContextMenuBit(ID_REBASE_SKIP)|GetContextMenuBit(ID_REBASE_EDIT)|
1313 GetContextMenuBit(ID_REBASE_SQUASH)|GetContextMenuBit(ID_REBASE_PICK)))
1314 popup.AppendMenu(MF_SEPARATOR, NULL);
1316 if (GetSelectedCount() == 1)
1320 if( !pSelLogEntry->m_CommitHash.IsEmpty())
1322 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPARE))
1323 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
1324 // TODO:
1325 // TortoiseMerge could be improved to take a /blame switch
1326 // and then not 'cat' the files from a unified diff but
1327 // blame then.
1328 // But until that's implemented, the context menu entry for
1329 // this feature is commented out.
1330 //popup.AppendMenu(ID_BLAMECOMPARE, IDS_LOG_POPUP_BLAMECOMPARE, IDI_BLAME);
1331 }else
1333 if(m_ContextMenuMask&GetContextMenuBit(ID_COMMIT))
1334 popup.AppendMenuIcon(ID_COMMIT, IDS_LOG_POPUP_COMMIT, IDI_COMMIT);
1336 if(m_ContextMenuMask&GetContextMenuBit(ID_GNUDIFF1))
1337 popup.AppendMenuIcon(ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);
1339 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPAREWITHPREVIOUS))
1340 popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF);
1341 //popup.AppendMenuIcon(ID_BLAMEWITHPREVIOUS, IDS_LOG_POPUP_BLAMEWITHPREVIOUS, IDI_BLAME);
1342 popup.AppendMenu(MF_SEPARATOR, NULL);
1345 // if (!m_ProjectProperties.sWebViewerRev.IsEmpty())
1346 // {
1347 // popup.AppendMenuIcon(ID_VIEWREV, IDS_LOG_POPUP_VIEWREV);
1348 // }
1349 // if (!m_ProjectProperties.sWebViewerPathRev.IsEmpty())
1350 // {
1351 // popup.AppendMenuIcon(ID_VIEWPATHREV, IDS_LOG_POPUP_VIEWPATHREV);
1352 // }
1353 // if ((!m_ProjectProperties.sWebViewerPathRev.IsEmpty())||
1354 // (!m_ProjectProperties.sWebViewerRev.IsEmpty()))
1355 // {
1356 // popup.AppendMenu(MF_SEPARATOR, NULL);
1357 // }
1359 CString str,format;
1360 //if (m_hasWC)
1361 // popup.AppendMenuIcon(ID_REVERTTOREV, IDS_LOG_POPUP_REVERTTOREV, IDI_REVERT);
1363 if(!pSelLogEntry->m_CommitHash.IsEmpty())
1365 format.LoadString(IDS_LOG_POPUP_MERGEREV);
1366 str.Format(format,g_Git.GetCurrentBranch());
1368 if (m_ContextMenuMask&GetContextMenuBit(ID_MERGEREV))
1369 popup.AppendMenuIcon(ID_MERGEREV, str, IDI_MERGE);
1371 format.LoadString(IDS_RESET_TO_THIS_FORMAT);
1372 str.Format(format,g_Git.GetCurrentBranch());
1374 if(m_ContextMenuMask&GetContextMenuBit(ID_RESET))
1375 popup.AppendMenuIcon(ID_RESET,str,IDI_REVERT);
1377 if(m_ContextMenuMask&GetContextMenuBit(ID_SWITCHTOREV))
1378 popup.AppendMenuIcon(ID_SWITCHTOREV, IDS_SWITCH_TO_THIS , IDI_SWITCH);
1380 if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_BRANCH))
1381 popup.AppendMenuIcon(ID_CREATE_BRANCH, IDS_CREATE_BRANCH_AT_THIS , IDI_COPY);
1383 if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_TAG))
1384 popup.AppendMenuIcon(ID_CREATE_TAG,IDS_CREATE_TAG_AT_THIS , IDI_COPY);
1386 format.LoadString(IDS_REBASE_THIS_FORMAT);
1387 str.Format(format,g_Git.GetCurrentBranch());
1389 if(pSelLogEntry->m_CommitHash != m_HeadHash)
1390 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_TO_VERSION))
1391 popup.AppendMenuIcon(ID_REBASE_TO_VERSION, str , IDI_REBASE);
1393 if(m_ContextMenuMask&GetContextMenuBit(ID_EXPORT))
1394 popup.AppendMenuIcon(ID_EXPORT,IDS_EXPORT_TO_THIS, IDI_EXPORT);
1396 if (m_ContextMenuMask&GetContextMenuBit(ID_REVERTREV))
1397 popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREV, IDI_REVERT);
1399 popup.AppendMenu(MF_SEPARATOR, NULL);
1404 if(!pSelLogEntry->m_Ref.IsEmpty() && GetSelectedCount() == 1)
1406 popup.AppendMenuIcon(ID_REFLOG_DEL, IDS_REFLOG_DEL, IDI_DELETE);
1407 popup.AppendMenuIcon(ID_STASH_APPLY,IDS_MENUSTASHAPPLY, IDI_RELOCATE);
1408 popup.AppendMenu(MF_SEPARATOR, NULL);
1411 if (GetSelectedCount() >= 2)
1413 bool bAddSeparator = false;
1414 if (IsSelectionContinuous() || (GetSelectedCount() == 2))
1416 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPARETWO))
1417 popup.AppendMenuIcon(ID_COMPARETWO, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
1420 if (GetSelectedCount() == 2)
1422 //popup.AppendMenuIcon(ID_BLAMETWO, IDS_LOG_POPUP_BLAMEREVS, IDI_BLAME);
1423 if(m_ContextMenuMask&GetContextMenuBit(ID_GNUDIFF2))
1424 popup.AppendMenuIcon(ID_GNUDIFF2, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
1425 bAddSeparator = true;
1428 if (m_hasWC)
1430 //popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREVS, IDI_REVERT);
1431 // if (m_hasWC)
1432 // popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREVS, IDI_MERGE);
1433 bAddSeparator = true;
1436 if (m_ContextMenuMask&GetContextMenuBit(ID_REVERTREV))
1437 popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREVS, IDI_REVERT);
1440 if (bAddSeparator)
1441 popup.AppendMenu(MF_SEPARATOR, NULL);
1444 if ( GetSelectedCount() >0 && (!pSelLogEntry->m_CommitHash.IsEmpty()))
1446 if ( IsSelectionContinuous() && GetSelectedCount() >= 2 )
1448 if(m_ContextMenuMask&GetContextMenuBit(ID_COMBINE_COMMIT))
1450 CString head;
1451 int headindex;
1452 headindex = this->GetHeadIndex();
1453 if(headindex>=0)
1455 head.Format(_T("HEAD~%d"),LastSelect-headindex);
1456 CString hash=g_Git.GetHash(head);
1457 hash=hash.Left(40);
1458 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
1459 if(pLastEntry->m_CommitHash.ToString() == hash)
1460 popup.AppendMenuIcon(ID_COMBINE_COMMIT,IDS_COMBINE_TO_ONE,IDI_COMBINE);
1464 if(m_ContextMenuMask&GetContextMenuBit(ID_CHERRY_PICK))
1465 popup.AppendMenuIcon(ID_CHERRY_PICK, IDS_CHERRY_PICK_VERSION, IDI_EXPORT);
1467 if(GetSelectedCount()<=2 || (IsSelectionContinuous() && GetSelectedCount() > 0))
1468 if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_PATCH))
1469 popup.AppendMenuIcon(ID_CREATE_PATCH, IDS_CREATE_PATCH, IDI_PATCH);
1471 popup.AppendMenu(MF_SEPARATOR, NULL);
1476 #if 0
1477 // if ((selEntries.size() > 0)&&(bAllFromTheSameAuthor))
1478 // {
1479 // popup.AppendMenuIcon(ID_EDITAUTHOR, IDS_LOG_POPUP_EDITAUTHOR);
1480 // }
1481 // if (GetSelectedCount() == 1)
1482 // {
1483 // popup.AppendMenuIcon(ID_EDITLOG, IDS_LOG_POPUP_EDITLOG);
1484 // popup.AppendMenuIcon(ID_REVPROPS, IDS_REPOBROWSE_SHOWREVPROP, IDI_PROPERTIES); // "Show Revision Properties"
1485 // popup.AppendMenu(MF_SEPARATOR, NULL);
1486 // }
1487 #endif
1490 if (GetSelectedCount() == 1)
1492 if(m_ContextMenuMask&GetContextMenuBit(ID_COPYHASH))
1493 popup.AppendMenuIcon(ID_COPYHASH, IDS_COPY_COMMIT_HASH);
1495 if (GetSelectedCount() != 0)
1497 if(m_ContextMenuMask&GetContextMenuBit(ID_COPYCLIPBOARD))
1498 popup.AppendMenuIcon(ID_COPYCLIPBOARD, IDS_LOG_POPUP_COPYTOCLIPBOARD);
1501 if(m_ContextMenuMask&GetContextMenuBit(ID_FINDENTRY))
1502 popup.AppendMenuIcon(ID_FINDENTRY, IDS_LOG_POPUP_FIND);
1505 if (GetSelectedCount() == 1)
1507 if(m_ContextMenuMask &GetContextMenuBit(ID_DELETE))
1509 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) != m_HashMap.end() )
1511 CString str;
1512 str.LoadString(IDS_DELETE_BRANCHTAG);
1513 if( m_HashMap[pSelLogEntry->m_CommitHash].size() == 1 )
1515 str+=_T(" ");
1516 str+=m_HashMap[pSelLogEntry->m_CommitHash].at(0);
1517 popup.AppendMenuIcon(ID_DELETE,str+_T("..."),IDI_DELETE);
1519 else if( m_HashMap[pSelLogEntry->m_CommitHash].size() > 1 )
1522 submenu.CreatePopupMenu();
1523 for(int i=0;i<m_HashMap[pSelLogEntry->m_CommitHash].size();i++)
1525 submenu.AppendMenuIcon(ID_DELETE+(i<<16),m_HashMap[pSelLogEntry->m_CommitHash][i]+_T("..."));
1528 popup.AppendMenu(MF_BYPOSITION|MF_POPUP|MF_STRING,(UINT) submenu.m_hMenu,str);
1536 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1537 // DialogEnableWindow(IDOK, FALSE);
1538 // SetPromptApp(&theApp);
1540 this->ContextMenuAction(cmd, FirstSelect, LastSelect);
1542 // EnableOKButton();
1543 } // if (popup.CreatePopupMenu())
1547 bool CGitLogListBase::IsSelectionContinuous()
1549 if ( GetSelectedCount()==1 )
1551 // if only one revision is selected, the selection is of course
1552 // continuous
1553 return true;
1556 POSITION pos = GetFirstSelectedItemPosition();
1557 bool bContinuous = (m_arShownList.GetCount() == (INT_PTR)m_logEntries.size());
1558 if (bContinuous)
1560 int itemindex = GetNextSelectedItem(pos);
1561 while (pos)
1563 int nextindex = GetNextSelectedItem(pos);
1564 if (nextindex - itemindex > 1)
1566 bContinuous = false;
1567 break;
1569 itemindex = nextindex;
1572 return bContinuous;
1575 void CGitLogListBase::CopySelectionToClipBoard(bool HashOnly)
1578 CString sClipdata;
1579 POSITION pos = GetFirstSelectedItemPosition();
1580 if (pos != NULL)
1582 CString sRev;
1583 sRev.LoadString(IDS_LOG_REVISION);
1584 CString sAuthor;
1585 sAuthor.LoadString(IDS_LOG_AUTHOR);
1586 CString sDate;
1587 sDate.LoadString(IDS_LOG_DATE);
1588 CString sMessage;
1589 sMessage.LoadString(IDS_LOG_MESSAGE);
1590 while (pos)
1592 CString sLogCopyText;
1593 CString sPaths;
1594 GitRev * pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.GetAt(GetNextSelectedItem(pos)));
1596 if(!HashOnly)
1598 //pLogEntry->m_Files
1599 //LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;
1601 for (int cpPathIndex = 0; cpPathIndex<pLogEntry->m_Files.GetCount(); ++cpPathIndex)
1603 sPaths += ((CTGitPath&)pLogEntry->m_Files[cpPathIndex]).GetActionName() + _T(" : ") + pLogEntry->m_Files[cpPathIndex].GetGitPathString();
1604 sPaths += _T("\r\n");
1606 sPaths.Trim();
1607 sLogCopyText.Format(_T("%s: %s\r\n%s: %s\r\n%s: %s\r\n%s:\r\n%s\r\n----\r\n%s\r\n\r\n"),
1608 (LPCTSTR)sRev, pLogEntry->m_CommitHash.ToString(),
1609 (LPCTSTR)sAuthor, (LPCTSTR)pLogEntry->m_AuthorName,
1610 (LPCTSTR)sDate,
1611 (LPCTSTR)CAppUtils::FormatDateAndTime( pLogEntry->m_AuthorDate, m_DateFormat, true, m_bRelativeTimes ),
1612 (LPCTSTR)sMessage, pLogEntry->m_Subject+_T("\r\n")+pLogEntry->m_Body,
1613 (LPCTSTR)sPaths);
1614 sClipdata += sLogCopyText;
1615 }else
1617 sClipdata += pLogEntry->m_CommitHash;
1618 break;
1622 CStringUtils::WriteAsciiStringToClipboard(sClipdata, GetSafeHwnd());
1627 void CGitLogListBase::DiffSelectedRevWithPrevious()
1629 if (m_bThreadRunning)
1630 return;
1632 int FirstSelect=-1, LastSelect=-1;
1633 POSITION pos = GetFirstSelectedItemPosition();
1634 FirstSelect = GetNextSelectedItem(pos);
1635 while(pos)
1637 LastSelect = GetNextSelectedItem(pos);
1640 ContextMenuAction(ID_COMPAREWITHPREVIOUS,FirstSelect,LastSelect);
1642 #if 0
1643 UpdateLogInfoLabel();
1644 int selIndex = m_LogList.GetSelectionMark();
1645 if (selIndex < 0)
1646 return;
1647 int selCount = m_LogList.GetSelectedCount();
1648 if (selCount != 1)
1649 return;
1651 // Find selected entry in the log list
1652 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1653 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));
1654 long rev1 = pLogEntry->Rev;
1655 long rev2 = rev1-1;
1656 CTGitPath path = m_path;
1658 // See how many files under the relative root were changed in selected revision
1659 int nChanged = 0;
1660 LogChangedPath * changed = NULL;
1661 for (INT_PTR c = 0; c < pLogEntry->pArChangedPaths->GetCount(); ++c)
1663 LogChangedPath * cpath = pLogEntry->pArChangedPaths->GetAt(c);
1664 if (cpath && cpath -> sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)
1666 ++nChanged;
1667 changed = cpath;
1671 if (m_path.IsDirectory() && nChanged == 1)
1673 // We're looking at the log for a directory and only one file under dir was changed in the revision
1674 // Do diff on that file instead of whole directory
1675 path.AppendPathString(changed->sPath.Mid(m_sRelativeRoot.GetLength()));
1678 m_bCancelled = FALSE;
1679 DialogEnableWindow(IDOK, FALSE);
1680 SetPromptApp(&theApp);
1681 theApp.DoWaitCursor(1);
1683 if (PromptShown())
1685 GitDiff diff(this, m_hWnd, true);
1686 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1687 diff.SetHEADPeg(m_LogRevision);
1688 diff.ShowCompare(path, rev2, path, rev1);
1690 else
1692 CAppUtils::StartShowCompare(m_hWnd, path, rev2, path, rev1, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1695 theApp.DoWaitCursor(-1);
1696 EnableOKButton();
1697 #endif
1700 void CGitLogListBase::OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult)
1702 LPNMLVFINDITEM pFindInfo = reinterpret_cast<LPNMLVFINDITEM>(pNMHDR);
1703 *pResult = -1;
1705 if (pFindInfo->lvfi.flags & LVFI_PARAM)
1706 return;
1707 if ((pFindInfo->iStart < 0)||(pFindInfo->iStart >= m_arShownList.GetCount()))
1708 return;
1709 if (pFindInfo->lvfi.psz == 0)
1710 return;
1711 #if 0
1712 CString sCmp = pFindInfo->lvfi.psz;
1713 CString sRev;
1714 for (int i=pFindInfo->iStart; i<m_arShownList.GetCount(); ++i)
1716 GitRev * pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
1717 sRev.Format(_T("%ld"), pLogEntry->Rev);
1718 if (pFindInfo->lvfi.flags & LVFI_PARTIAL)
1720 if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)
1722 *pResult = i;
1723 return;
1726 else
1728 if (sCmp.Compare(sRev)==0)
1730 *pResult = i;
1731 return;
1735 if (pFindInfo->lvfi.flags & LVFI_WRAP)
1737 for (int i=0; i<pFindInfo->iStart; ++i)
1739 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(i));
1740 sRev.Format(_T("%ld"), pLogEntry->Rev);
1741 if (pFindInfo->lvfi.flags & LVFI_PARTIAL)
1743 if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)
1745 *pResult = i;
1746 return;
1749 else
1751 if (sCmp.Compare(sRev)==0)
1753 *pResult = i;
1754 return;
1759 #endif
1760 *pResult = -1;
1763 int CGitLogListBase::FillGitLog(CTGitPath *path,int info,CString *from,CString *to)
1765 ClearText();
1767 this->m_logEntries.ClearAll();
1768 this->m_logEntries.ParserFromLog(path,-1,info,from,to);
1770 //this->m_logEntries.ParserFromLog();
1771 SetItemCountEx(this->m_logEntries.size());
1773 this->m_arShownList.RemoveAll();
1775 for(unsigned int i=0;i<m_logEntries.size();i++)
1777 if(m_IsOldFirst)
1779 m_logEntries.GetGitRevAt(m_logEntries.size()-i-1).m_IsFull=TRUE;
1780 this->m_arShownList.Add(&m_logEntries.GetGitRevAt(m_logEntries.size()-i-1));
1782 }else
1784 m_logEntries.GetGitRevAt(i).m_IsFull=TRUE;
1785 this->m_arShownList.Add(&m_logEntries.GetGitRevAt(i));
1789 if(path)
1790 m_Path=*path;
1791 return 0;
1795 int CGitLogListBase::BeginFetchLog()
1797 ClearText();
1799 this->m_logEntries.ClearAll();
1800 git_init();
1802 this->m_LogCache.ClearAllParent();
1804 m_LogCache.FetchCacheIndex(g_Git.m_CurrentDir);
1806 CTGitPath *path;
1807 if(this->m_Path.IsEmpty())
1808 path=NULL;
1809 else
1810 path=&this->m_Path;
1812 CString hash;
1813 int mask;
1814 mask = CGit::LOG_INFO_ONLY_HASH | CGit::LOG_INFO_BOUNDARY;
1815 // if(this->m_bAllBranch)
1816 mask |= m_ShowMask ;
1818 this->m_arShownList.RemoveAll();
1820 if(m_bShowWC)
1822 this->m_logEntries.insert(m_logEntries.begin(),this->m_wcRev.m_CommitHash);
1823 this->m_LogCache.m_HashMap[m_wcRev.m_CommitHash]=m_wcRev;
1826 CString *pFrom, *pTo;
1827 pFrom = pTo = NULL;
1828 if(!this->m_startrev.IsEmpty())
1830 pFrom = &this->m_startrev;
1831 if(!this->m_endrev.IsEmpty())
1832 pTo = &this->m_endrev;
1833 else
1834 pTo = &CString(_T("HEAD"));
1836 CString cmd=g_Git.GetLogCmd(m_StartRef,path,-1,mask,pFrom,pTo,true);
1838 //this->m_logEntries.ParserFromLog();
1839 if(IsInWorkingThread())
1841 PostMessage(LVM_SETITEMCOUNT, (WPARAM) this->m_logEntries.size(),(LPARAM) LVSICF_NOINVALIDATEALL);
1843 else
1845 SetItemCountEx(this->m_logEntries.size());
1848 git_init();
1850 if(g_Git.IsInitRepos())
1851 return 0;
1853 if(git_open_log(&m_DllGitLog,CUnicodeUtils::GetMulti(cmd,CP_ACP).GetBuffer()))
1855 return -1;
1858 return 0;
1861 BOOL CGitLogListBase::PreTranslateMessage(MSG* pMsg)
1863 // Skip Ctrl-C when copying text out of the log message or search filter
1864 BOOL bSkipAccelerator = ( pMsg->message == WM_KEYDOWN && pMsg->wParam=='C' && (GetFocus()==GetDlgItem(IDC_MSGVIEW) || GetFocus()==GetDlgItem(IDC_SEARCHEDIT) ) && GetKeyState(VK_CONTROL)&0x8000 );
1865 if (pMsg->message == WM_KEYDOWN && pMsg->wParam=='\r')
1867 //if (GetFocus()==GetDlgItem(IDC_LOGLIST))
1869 if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))
1871 DiffSelectedRevWithPrevious();
1872 return TRUE;
1875 #if 0
1876 if (GetFocus()==GetDlgItem(IDC_LOGMSG))
1878 DiffSelectedFile();
1879 return TRUE;
1881 #endif
1884 #if 0
1885 if (m_hAccel && !bSkipAccelerator)
1887 int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);
1888 if (ret)
1889 return TRUE;
1892 #endif
1893 //m_tooltips.RelayEvent(pMsg);
1894 return __super::PreTranslateMessage(pMsg);
1897 void CGitLogListBase::OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1899 // a double click on an entry in the revision list has happened
1900 *pResult = 0;
1902 if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))
1903 DiffSelectedRevWithPrevious();
1906 int CGitLogListBase::FetchLogAsync(void * data)
1908 m_ProcData=data;
1909 m_bExitThread=FALSE;
1910 InterlockedExchange(&m_bThreadRunning, TRUE);
1911 InterlockedExchange(&m_bNoDispUpdates, TRUE);
1912 m_LoadingThread = AfxBeginThread(LogThreadEntry, this);
1913 if (m_LoadingThread ==NULL)
1915 InterlockedExchange(&m_bThreadRunning, FALSE);
1916 InterlockedExchange(&m_bNoDispUpdates, FALSE);
1917 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1918 return -1;
1920 return 0;
1923 //this is the thread function which calls the subversion function
1924 UINT CGitLogListBase::LogThreadEntry(LPVOID pVoid)
1926 return ((CGitLogListBase*)pVoid)->LogThread();
1929 void CGitLogListBase::GetTimeRange(CTime &oldest, CTime &latest)
1931 //CTime time;
1932 oldest=CTime::GetCurrentTime();
1933 latest=CTime(1971,1,2,0,0,0);
1934 for(unsigned int i=0;i<m_logEntries.size();i++)
1936 if(m_logEntries[i].IsEmpty())
1937 continue;
1939 if(m_logEntries.GetGitRevAt(i).m_AuthorDate.GetTime() < oldest.GetTime())
1940 oldest = m_logEntries.GetGitRevAt(i).m_AuthorDate.GetTime();
1942 if(m_logEntries.GetGitRevAt(i).m_AuthorDate.GetTime() > latest.GetTime())
1943 latest = m_logEntries.GetGitRevAt(i).m_AuthorDate.GetTime();
1947 if(latest<oldest)
1948 latest=oldest;
1951 //Helper class for FetchFullLogInfo()
1952 class CGitCall_FetchFullLogInfo : public CGitCall
1954 public:
1955 CGitCall_FetchFullLogInfo(CGitLogListBase* ploglist):m_ploglist(ploglist),m_CollectedCount(0){}
1956 virtual bool OnOutputData(const BYTE* data, size_t size)
1958 if(size==0)
1959 return m_ploglist->m_bExitThread;
1960 //Add received data to byte collector
1961 m_ByteCollector.append(data,size);
1963 //Find loginfo endmarker
1964 static const BYTE dataToFind[]={0,0,'#','<'};
1965 int found=m_ByteCollector.findData(dataToFind,4);
1966 if(found<0)
1967 return m_ploglist->m_bExitThread;//Not found
1968 found+=2;//Position after loginfo end-marker
1970 //Prepare data for OnLogInfo and call it
1971 BYTE_VECTOR logInfo;
1972 logInfo.append(&*m_ByteCollector.begin(),found);
1973 OnLogInfo(logInfo);
1975 //Remove loginfo from bytecollector
1976 m_ByteCollector.erase(m_ByteCollector.begin(),m_ByteCollector.begin()+found);
1978 return m_ploglist->m_bExitThread;
1980 virtual void OnEnd()
1982 //Rest should be a complete log line.
1983 if(!m_ByteCollector.empty())
1984 OnLogInfo(m_ByteCollector);
1988 void OnLogInfo(BYTE_VECTOR& logInfo)
1990 GitRev fullRev;
1991 fullRev.ParserFromLog(logInfo);
1992 MAP_HASH_REV::iterator itRev=m_ploglist->m_logEntries.m_HashMap.find(fullRev.m_CommitHash);
1993 if(itRev==m_ploglist->m_logEntries.m_HashMap.end())
1995 //Should not occur, only when Git-tree updated in the mean time. (Race condition)
1996 return;//Ignore
1998 //Set updating
1999 int rev=itRev->second;
2000 GitRev* revInVector=&m_ploglist->m_logEntries.GetGitRevAt(rev);
2003 if(revInVector->m_IsFull)
2004 return;
2006 GitRev *pRev= m_ploglist->m_LogCache.GetCacheData(m_ploglist->m_logEntries[rev]);
2007 if(pRev)
2009 ++m_CollectedCount;
2010 InterlockedExchange(&pRev->m_IsUpdateing,FALSE);
2011 InterlockedExchange(&pRev->m_IsFull,TRUE);
2012 ::PostMessage(m_ploglist->m_hWnd,MSG_LOADED,(WPARAM)rev,0);
2013 return;
2016 // fullRev.m_IsUpdateing=TRUE;
2017 // fullRev.m_IsFull=TRUE;
2020 if(InterlockedExchange(&revInVector->m_IsUpdateing,TRUE))
2021 return;//Cannot update this row now. Ignore.
2022 TCHAR oldmark=revInVector->m_Mark;
2023 GIT_REV_LIST oldlist=revInVector->m_ParentHash;
2024 // CString oldhash=m_CommitHash;
2026 //Parse new rev info
2027 revInVector->ParserFromLog(logInfo);
2029 if(oldmark!=0)
2030 revInVector->m_Mark=oldmark; //parser full log will cause old mark overwrited.
2031 //So we need keep old bound mark.
2032 revInVector->m_ParentHash=oldlist;
2034 //update cache
2035 m_ploglist->m_LogCache.AddCacheEntry(*revInVector);
2037 //Reset updating
2038 InterlockedExchange(&revInVector->m_IsFull,TRUE);
2039 InterlockedExchange(&revInVector->m_IsUpdateing,FALSE);
2041 //Notify listcontrol and update progress bar
2042 ++m_CollectedCount;
2044 ::PostMessage(m_ploglist->m_hWnd,MSG_LOADED,(WPARAM)rev,0);
2046 DWORD percent=m_CollectedCount*68/m_ploglist->m_logEntries.size() + GITLOG_START+1+30;
2047 if(percent == GITLOG_END)
2048 percent = GITLOG_END -1;
2050 ::PostMessage(m_ploglist->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) percent,0);
2053 CGitLogListBase* m_ploglist;
2054 BYTE_VECTOR m_ByteCollector;
2055 int m_CollectedCount;
2059 void CGitLogListBase::FetchFullLogInfo(CString &from, CString &to)
2061 CGitCall_FetchFullLogInfo fetcher(this);
2062 int mask=
2063 CGit::LOG_INFO_FULL_DIFF|
2064 CGit::LOG_INFO_STAT|
2065 CGit::LOG_INFO_FILESTATE|
2066 CGit::LOG_INFO_DETECT_COPYRENAME|
2067 CGit::LOG_INFO_SHOW_MERGEDFILE |
2068 m_ShowMask;
2070 CTGitPath *path;
2071 if(this->m_Path.IsEmpty())
2072 path=NULL;
2073 else
2074 path=&this->m_Path;
2076 g_Git.GetLog(&fetcher,CString(),path,-1,mask,&from,&to);
2079 void CGitLogListBase::FetchLastLogInfo()
2081 unsigned int updated=0;
2082 int percent=0;
2083 CRect rect;
2085 for(unsigned int i=0;i<m_logEntries.size();i++)
2087 if(m_logEntries.GetGitRevAt(i).m_IsFull)
2088 continue;
2090 GitRev *pRev = m_LogCache.GetCacheData(m_logEntries[i]);
2091 if(pRev == NULL)
2093 if(!m_logEntries.FetchFullInfo(i))
2095 updated++;
2097 m_LogCache.AddCacheEntry(m_logEntries.GetGitRevAt(i));
2099 }else
2101 updated++;
2102 InterlockedExchange(&pRev->m_IsUpdateing,FALSE);
2103 InterlockedExchange(&pRev->m_IsFull,TRUE);
2106 ::PostMessage(m_hWnd,MSG_LOADED,(WPARAM)i,0);
2108 if(m_bExitThread)
2110 InterlockedExchange(&m_bThreadRunning, FALSE);
2111 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2112 return;
2118 UINT CGitLogListBase::LogThread()
2120 ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_START,0);
2122 InterlockedExchange(&m_bThreadRunning, TRUE);
2123 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2125 ULONGLONG t1,t2;
2127 if(BeginFetchLog())
2128 return -1;
2130 TRACE(_T("\n===Begin===\n"));
2131 //Update work copy item;
2132 if( m_logEntries.size() > 0)
2134 GitRev *pRev = &m_logEntries.GetGitRevAt(0);
2136 m_arShownList.Add(pRev);
2138 if( pRev->m_CommitHash.IsEmpty() )
2140 pRev->m_Files.Clear();
2141 pRev->m_ParentHash.clear();
2142 pRev->m_ParentHash.push_back(m_HeadHash);
2143 if(g_Git.IsInitRepos())
2145 g_Git.GetInitAddList(pRev->m_Files);
2147 }else
2149 g_Git.GetCommitDiffList(pRev->m_CommitHash.ToString(),this->m_HeadHash.ToString(), pRev->m_Files);
2151 pRev->m_Action =0;
2153 for(int j=0;j< pRev->m_Files.GetCount();j++)
2154 pRev->m_Action |= pRev->m_Files[j].m_Action;
2156 pRev->m_Body.Format(_T("%d files changed"),m_logEntries.GetGitRevAt(0).m_Files.GetCount());
2157 ::PostMessage(m_hWnd,MSG_LOADED,(WPARAM)0,0);
2161 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2163 if(!g_Git.IsInitRepos())
2165 git_get_log_firstcommit(m_DllGitLog);
2166 int total = git_get_log_estimate_commit_count(m_DllGitLog);
2167 GIT_COMMIT commit;
2168 t2=t1=GetTickCount();
2169 int oldprecentage = 0;
2170 int oldsize=m_logEntries.size();
2171 while( git_get_log_nextcommit(this->m_DllGitLog,&commit) == 0)
2173 //printf("%s\r\n",commit.m_Subject);
2174 if(m_bExitThread)
2175 break;
2177 CGitHash hash = (char*)commit.m_hash ;
2179 GitRev *pRev = m_LogCache.GetCacheData(hash);
2181 if(pRev == NULL || !pRev->m_IsFull)
2183 pRev->ParserFromCommit(&commit);
2184 pRev->ParserParentFromCommit(&commit);
2185 git_free_commit(&commit);
2186 //Must call free commit before SafeFetchFullInfo, commit parent is rewrite by log.
2187 //file list will wrong if parent rewrite.
2188 pRev->SafeFetchFullInfo(&g_Git);
2190 }else
2192 ASSERT(pRev->m_CommitHash == hash);
2193 pRev->ParserParentFromCommit(&commit);
2194 git_free_commit(&commit);
2196 #ifdef DEBUG
2197 pRev->DbgPrint();
2198 TRACE(_T("\n"));
2199 #endif
2202 this->m_critSec.Lock();
2203 m_logEntries.push_back(hash);
2204 m_arShownList.Add(pRev);
2205 this->m_critSec.Unlock();
2207 t2=GetTickCount();
2209 if(t2-t1>500 || (m_logEntries.size()-oldsize >100))
2211 //update UI
2212 int percent=m_logEntries.size()*100/total + GITLOG_START+1;
2213 if(percent > 99)
2214 percent =99;
2215 if(percent < GITLOG_START)
2216 percent = GITLOG_START +1;
2218 oldsize = m_logEntries.size();
2219 PostMessage(LVM_SETITEMCOUNT, (WPARAM) this->m_logEntries.size(),(LPARAM) LVSICF_NOINVALIDATEALL|LVSICF_NOSCROLL);
2221 //if( percent > oldprecentage )
2223 ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) percent,0);
2224 oldprecentage = percent;
2226 t1 = t2;
2231 //Update UI;
2232 PostMessage(LVM_SETITEMCOUNT, (WPARAM) this->m_logEntries.size(),(LPARAM) LVSICF_NOINVALIDATEALL|LVSICF_NOSCROLL);
2233 ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_END,0);
2235 InterlockedExchange(&m_bThreadRunning, FALSE);
2237 #if 0
2238 // if(m_ProcCallBack)
2239 // m_ProcCallBack(m_ProcData,GITLOG_START);
2240 ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_START,0);
2242 InterlockedExchange(&m_bThreadRunning, TRUE);
2243 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2245 //does the user force the cache to refresh (shift or control key down)?
2246 bool refresh = (GetKeyState (VK_CONTROL) < 0)
2247 || (GetKeyState (VK_SHIFT) < 0);
2249 //disable the "Get All" button while we're receiving
2250 //log messages.
2252 FillGitShortLog();
2254 if(this->m_bExitThread)
2256 InterlockedExchange(&m_bThreadRunning, FALSE);
2257 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2258 return 0;
2260 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2261 ::PostMessage(GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_START_ALL, 0);
2263 int start=0; CString firstcommit,lastcommit;
2264 int update=0;
2265 for(int i=0;i<m_logEntries.size();i++)
2267 if( i==0 && m_logEntries.GetGitRevAt(i).m_CommitHash.IsEmpty() )
2269 m_logEntries.GetGitRevAt(i).m_Files.Clear();
2270 m_logEntries.GetGitRevAt(i).m_ParentHash.clear();
2271 m_logEntries.GetGitRevAt(i).m_ParentHash.push_back(m_HeadHash);
2272 g_Git.GetCommitDiffList(m_logEntries.GetGitRevAt(i).m_CommitHash.ToString(),this->m_HeadHash,m_logEntries.GetGitRevAt(i).m_Files);
2273 m_logEntries.GetGitRevAt(i).m_Action =0;
2274 for(int j=0;j< m_logEntries.GetGitRevAt(i).m_Files.GetCount();j++)
2275 m_logEntries.GetGitRevAt(i).m_Action |= m_logEntries.GetGitRevAt(i).m_Files[j].m_Action;
2277 m_logEntries.GetGitRevAt(i).m_Body.Format(_T("%d files changed"),m_logEntries.GetGitRevAt(i).m_Files.GetCount());
2278 ::PostMessage(m_hWnd,MSG_LOADED,(WPARAM)0,0);
2279 continue;
2282 start=this->m_logEntries.GetGitRevAt(i).ParserFromLog(m_logEntries.m_RawlogData,start);
2283 m_logEntries.m_HashMap[m_logEntries.GetGitRevAt(i).m_CommitHash.ToString()]=i;
2285 if(m_LogCache.GetCacheData(m_logEntries.GetGitRevAt(i)))
2287 if(firstcommit.IsEmpty())
2288 firstcommit=m_logEntries.GetGitRevAt(i).m_CommitHash.ToString();
2289 lastcommit=m_logEntries.GetGitRevAt(i).m_CommitHash.ToString();
2291 }else
2293 InterlockedExchange(&m_logEntries.GetGitRevAt(i).m_IsUpdateing,FALSE);
2294 InterlockedExchange(&m_logEntries.GetGitRevAt(i).m_IsFull,TRUE);
2295 update++;
2297 ::PostMessage(m_hWnd,MSG_LOADED,(WPARAM) i, 0);
2299 if(start<0)
2300 break;
2301 if(start>=m_logEntries.m_RawlogData.size())
2302 break;
2304 int percent=i*30/m_logEntries.size() + GITLOG_START+1;
2306 ::PostMessage(GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) percent, 0);
2308 if(this->m_bExitThread)
2310 InterlockedExchange(&m_bThreadRunning, FALSE);
2311 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2312 return 0;
2315 if(!lastcommit.IsEmpty())
2316 FetchFullLogInfo(lastcommit,firstcommit);
2318 this->FetchLastLogInfo();
2320 #if 0
2321 RedrawItems(0, m_arShownList.GetCount());
2322 // SetRedraw(false);
2323 // ResizeAllListCtrlCols();
2324 // SetRedraw(true);
2326 if ( m_pStoreSelection )
2328 // Deleting the instance will restore the
2329 // selection of the CLogDlg.
2330 delete m_pStoreSelection;
2331 m_pStoreSelection = NULL;
2333 else
2335 // If no selection has been set then this must be the first time
2336 // the revisions are shown. Let's preselect the topmost revision.
2337 if ( GetItemCount()>0 )
2339 SetSelectionMark(0);
2340 SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
2343 #endif
2347 //FetchFullLogInfo();
2348 //FetchFullLogInfoOrig();
2349 //RefreshCursor();
2350 // make sure the filter is applied (if any) now, after we refreshed/fetched
2351 // the log messages
2353 ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_END,0);
2355 InterlockedExchange(&m_bThreadRunning, FALSE);
2356 #endif
2357 return 0;
2360 void CGitLogListBase::Refresh()
2362 SafeTerminateThread();
2364 this->SetItemCountEx(0);
2365 this->Clear();
2367 //Update branch and Tag info
2368 ReloadHashMap();
2369 //Assume Thread have exited
2370 //if(!m_bThreadRunning)
2373 m_logEntries.clear();
2374 InterlockedExchange(&m_bExitThread,FALSE);
2376 InterlockedExchange(&m_bThreadRunning, TRUE);
2377 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2378 if ( (m_LoadingThread=AfxBeginThread(LogThreadEntry, this)) ==NULL)
2380 InterlockedExchange(&m_bThreadRunning, FALSE);
2381 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2382 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
2384 m_sFilterText.Empty();
2385 m_From=CTime(1970,1,2,0,0,0);
2386 m_To=CTime::GetCurrentTime();
2389 bool CGitLogListBase::ValidateRegexp(LPCTSTR regexp_str, tr1::wregex& pat, bool bMatchCase /* = false */)
2393 tr1::regex_constants::syntax_option_type type = tr1::regex_constants::ECMAScript;
2394 if (!bMatchCase)
2395 type |= tr1::regex_constants::icase;
2396 pat = tr1::wregex(regexp_str, type);
2397 return true;
2399 catch (exception) {}
2400 return false;
2403 void CGitLogListBase::RecalculateShownList(CPtrArray * pShownlist)
2406 pShownlist->RemoveAll();
2407 tr1::wregex pat;//(_T("Remove"), tr1::regex_constants::icase);
2408 bool bRegex = false;
2409 if (m_bFilterWithRegex)
2410 bRegex = ValidateRegexp(m_sFilterText, pat, false);
2412 tr1::regex_constants::match_flag_type flags = tr1::regex_constants::match_any;
2413 CString sRev;
2414 for (DWORD i=0; i<m_logEntries.size(); ++i)
2416 if ((bRegex)&&(m_bFilterWithRegex))
2418 #if 0
2419 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))
2421 ATLTRACE(_T("bugID = \"%s\"\n"), (LPCTSTR)m_logEntries[i]->sBugIDs);
2422 if (regex_search(wstring((LPCTSTR)m_logEntries[i]->sBugIDs), pat, flags)&&IsEntryInDateRange(i))
2424 pShownlist->Add(m_logEntries[i]);
2425 continue;
2428 #endif
2429 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))
2431 ATLTRACE(_T("messge = \"%s\"\n"),m_logEntries.GetGitRevAt(i).m_Subject);
2432 if (regex_search(wstring((LPCTSTR)m_logEntries.GetGitRevAt(i).m_Subject), pat, flags)&&IsEntryInDateRange(i))
2434 pShownlist->Add(&m_logEntries.GetGitRevAt(i));
2435 continue;
2438 ATLTRACE(_T("messge = \"%s\"\n"),m_logEntries.GetGitRevAt(i).m_Body);
2439 if (regex_search(wstring((LPCTSTR)m_logEntries.GetGitRevAt(i).m_Body), pat, flags)&&IsEntryInDateRange(i))
2441 pShownlist->Add(&m_logEntries.GetGitRevAt(i));
2442 continue;
2445 #if 0
2446 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))
2448 LogChangedPathArray * cpatharray = m_logEntries[i]->pArChangedPaths;
2450 bool bGoing = true;
2451 for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount() && bGoing; ++cpPathIndex)
2453 LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);
2454 if (regex_search(wstring((LPCTSTR)cpath->sCopyFromPath), pat, flags)&&IsEntryInDateRange(i))
2456 pShownlist->Add(m_logEntries[i]);
2457 bGoing = false;
2458 continue;
2460 if (regex_search(wstring((LPCTSTR)cpath->sPath), pat, flags)&&IsEntryInDateRange(i))
2462 pShownlist->Add(m_logEntries[i]);
2463 bGoing = false;
2464 continue;
2466 if (regex_search(wstring((LPCTSTR)cpath->GetAction()), pat, flags)&&IsEntryInDateRange(i))
2468 pShownlist->Add(m_logEntries[i]);
2469 bGoing = false;
2470 continue;
2473 if (!bGoing)
2474 continue;
2476 #endif
2477 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))
2479 if (regex_search(wstring((LPCTSTR)m_logEntries.GetGitRevAt(i).m_AuthorName), pat, flags)&&IsEntryInDateRange(i))
2481 pShownlist->Add(&m_logEntries.GetGitRevAt(i));
2482 continue;
2485 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))
2487 sRev.Format(_T("%s"), m_logEntries.GetGitRevAt(i).m_CommitHash.ToString());
2488 if (regex_search(wstring((LPCTSTR)sRev), pat, flags)&&IsEntryInDateRange(i))
2490 pShownlist->Add(&m_logEntries.GetGitRevAt(i));
2491 continue;
2494 } // if (bRegex)
2495 else
2497 CString find = m_sFilterText;
2498 find.MakeLower();
2499 #if 0
2500 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))
2502 CString sBugIDs = m_logEntries[i]->sBugIDs;
2504 sBugIDs = sBugIDs.MakeLower();
2505 if ((sBugIDs.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2507 pShownlist->Add(m_logEntries[i]);
2508 continue;
2511 #endif
2512 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))
2514 CString msg = m_logEntries.GetGitRevAt(i).m_Subject;
2516 msg = msg.MakeLower();
2517 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2519 pShownlist->Add(&m_logEntries.GetGitRevAt(i));
2520 continue;
2522 msg = m_logEntries.GetGitRevAt(i).m_Body;
2524 msg = msg.MakeLower();
2525 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2527 pShownlist->Add(&m_logEntries.GetGitRevAt(i));
2528 continue;
2531 #if 0
2532 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))
2534 LogChangedPathArray * cpatharray = m_logEntries[i]->pArChangedPaths;
2536 bool bGoing = true;
2537 for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount() && bGoing; ++cpPathIndex)
2539 LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);
2540 CString path = cpath->sCopyFromPath;
2541 path.MakeLower();
2542 if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))
2544 pShownlist->Add(m_logEntries[i]);
2545 bGoing = false;
2546 continue;
2548 path = cpath->sPath;
2549 path.MakeLower();
2550 if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))
2552 pShownlist->Add(m_logEntries[i]);
2553 bGoing = false;
2554 continue;
2556 path = cpath->GetAction();
2557 path.MakeLower();
2558 if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))
2560 pShownlist->Add(m_logEntries[i]);
2561 bGoing = false;
2562 continue;
2566 #endif
2567 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))
2569 CString msg = m_logEntries.GetGitRevAt(i).m_AuthorName;
2570 msg = msg.MakeLower();
2571 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2573 pShownlist->Add(&m_logEntries.GetGitRevAt(i));
2574 continue;
2577 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))
2579 sRev.Format(_T("%s"), m_logEntries.GetGitRevAt(i).m_CommitHash.ToString());
2580 if ((sRev.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2582 pShownlist->Add(&m_logEntries.GetGitRevAt(i));
2583 continue;
2586 } // else (from if (bRegex))
2587 } // for (DWORD i=0; i<m_logEntries.size(); ++i)
2591 BOOL CGitLogListBase::IsEntryInDateRange(int i)
2593 __time64_t time = m_logEntries.GetGitRevAt(i).m_AuthorDate.GetTime();
2594 if ((time >= m_From.GetTime())&&(time <= m_To.GetTime()))
2595 return TRUE;
2597 return FALSE;
2599 // return TRUE;
2601 void CGitLogListBase::StartFilter()
2603 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2604 RecalculateShownList(&m_arShownList);
2605 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2608 DeleteAllItems();
2609 SetItemCountEx(ShownCountWithStopped());
2610 RedrawItems(0, ShownCountWithStopped());
2611 SetRedraw(false);
2612 //ResizeAllListCtrlCols();
2613 SetRedraw(true);
2614 Invalidate();
2617 void CGitLogListBase::RemoveFilter()
2620 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2622 m_arShownList.RemoveAll();
2624 // reset the time filter too
2625 #if 0
2626 m_timFrom = (__time64_t(m_tFrom));
2627 m_timTo = (__time64_t(m_tTo));
2628 m_DateFrom.SetTime(&m_timFrom);
2629 m_DateTo.SetTime(&m_timTo);
2630 m_DateFrom.SetRange(&m_timFrom, &m_timTo);
2631 m_DateTo.SetRange(&m_timFrom, &m_timTo);
2632 #endif
2634 for (DWORD i=0; i<m_logEntries.size(); ++i)
2636 if(this->m_IsOldFirst)
2638 m_arShownList.Add(&m_logEntries.GetGitRevAt(m_logEntries.size()-i-1));
2639 }else
2641 m_arShownList.Add(&m_logEntries.GetGitRevAt(i));
2644 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
2645 DeleteAllItems();
2646 SetItemCountEx(ShownCountWithStopped());
2647 RedrawItems(0, ShownCountWithStopped());
2648 // SetRedraw(false);
2649 // ResizeAllListCtrlCols();
2650 // SetRedraw(true);
2652 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2655 void CGitLogListBase::Clear()
2657 m_arShownList.RemoveAll();
2658 DeleteAllItems();
2660 m_logEntries.ClearAll();
2664 void CGitLogListBase::OnDestroy()
2666 // save the column widths to the registry
2667 SaveColumnWidths();
2669 SafeTerminateThread();
2671 while(m_LogCache.SaveCache())
2673 if(CMessageBox::Show(NULL,_T("Cannot Save Log Cache to Disk. To retry click yes. To give up click no."),_T("TortoiseGit"),
2674 MB_YESNO) == IDNO)
2675 break;
2677 CHintListCtrl::OnDestroy();
2680 LRESULT CGitLogListBase::OnLoad(WPARAM wParam,LPARAM lParam)
2682 CRect rect;
2683 int i=(int)wParam;
2684 this->GetItemRect(i,&rect,LVIR_BOUNDS);
2685 this->InvalidateRect(rect);
2687 if(this->GetItemState(i,LVIF_STATE) & LVIS_SELECTED)
2689 int i=0;
2691 return 0;
2695 * Save column widths to the registry
2697 void CGitLogListBase::SaveColumnWidths()
2699 CHeaderCtrl* pHdrCtrl = (CHeaderCtrl*)(GetDlgItem(0));
2700 int maxcol;
2702 for (int col = 0; col <= maxcol; col++)
2703 if (m_ColumnManager.IsVisible (col))
2704 m_ColumnManager.ColumnResized (col);
2706 m_ColumnManager.WriteSettings();
2709 int CGitLogListBase::GetHeadIndex()
2711 if(m_HeadHash.IsEmpty())
2712 return -1;
2714 for(int i=0;i<m_arShownList.GetCount();i++)
2716 GitRev *pRev = (GitRev*)m_arShownList[i];
2717 if(pRev)
2719 if(pRev->m_CommitHash.ToString() == m_HeadHash )
2720 return i;
2723 return -1;
2725 void CGitLogListBase::OnFind()
2727 if (!m_pFindDialog)
2729 m_pFindDialog = new CFindReplaceDialog();
2730 m_pFindDialog->Create(TRUE, NULL, NULL, FR_HIDEUPDOWN | FR_HIDEWHOLEWORD, this);
2733 void CGitLogListBase::OnHdnBegintrack(NMHDR *pNMHDR, LRESULT *pResult)
2735 m_ColumnManager.OnHdnBegintrack(pNMHDR, pResult);
2737 void CGitLogListBase::OnHdnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)
2739 if(!m_ColumnManager.OnHdnItemchanging(pNMHDR, pResult))
2740 Default();
2742 LRESULT CGitLogListBase::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/)
2745 ASSERT(m_pFindDialog != NULL);
2747 if (m_pFindDialog->IsTerminating())
2749 // invalidate the handle identifying the dialog box.
2750 m_pFindDialog = NULL;
2751 return 0;
2754 if(m_pFindDialog->FindNext())
2756 //read data from dialog
2757 CString FindText = m_pFindDialog->GetFindString();
2758 bool bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
2759 bool bFound = false;
2760 tr1::wregex pat;
2761 bool bRegex = ValidateRegexp(FindText, pat, bMatchCase);
2763 tr1::regex_constants::match_flag_type flags = tr1::regex_constants::match_not_null;
2765 int i;
2766 for (i = this->m_nSearchIndex; i<m_arShownList.GetCount()&&!bFound; i++)
2768 GitRev* pLogEntry = (GitRev*)m_arShownList.GetAt(i);
2770 CString str;
2771 str+=pLogEntry->m_CommitHash.ToString();
2772 str+=_T("\n");
2774 for(int j=0;j<this->m_HashMap[pLogEntry->m_CommitHash].size();j++)
2776 str+=m_HashMap[pLogEntry->m_CommitHash][j];
2777 str+=_T("\n");
2780 str+=pLogEntry->m_AuthorEmail;
2781 str+=_T("\n");
2782 str+=pLogEntry->m_AuthorName;
2783 str+=_T("\n");
2784 str+=pLogEntry->m_Body;
2785 str+=_T("\n");
2786 str+=pLogEntry->m_CommitterEmail;
2787 str+=_T("\n");
2788 str+=pLogEntry->m_CommitterName;
2789 str+=_T("\n");
2790 str+=pLogEntry->m_Subject;
2791 str+=_T("\n");
2793 for(int i=0;i<pLogEntry->m_Files.GetCount();i++)
2795 str+=pLogEntry->m_Files[i].GetWinPath();
2796 str+=_T("\n");
2797 str+=pLogEntry->m_Files[i].GetGitOldPathString();
2798 str+=_T("\n");
2801 if (bRegex)
2804 if (regex_search(wstring(str), pat, flags))
2806 bFound = true;
2807 break;
2810 else
2812 if (bMatchCase)
2814 if (str.Find(FindText) >= 0)
2816 bFound = true;
2817 break;
2821 else
2823 CString msg = str;
2824 msg = msg.MakeLower();
2825 CString find = FindText.MakeLower();
2826 if (msg.Find(find) >= 0)
2828 bFound = TRUE;
2829 break;
2833 } // for (i = this->m_nSearchIndex; i<m_arShownList.GetItemCount()&&!bFound; i++)
2834 if (bFound)
2836 this->m_nSearchIndex = i;
2837 EnsureVisible(i, FALSE);
2838 SetItemState(GetSelectionMark(), 0, LVIS_SELECTED);
2839 SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
2840 SetSelectionMark(i);
2841 //FillLogMessageCtrl();
2842 UpdateData(FALSE);
2843 m_nSearchIndex++;
2844 if (m_nSearchIndex >= m_arShownList.GetCount())
2845 m_nSearchIndex = (int)m_arShownList.GetCount()-1;
2847 } // if(m_pFindDialog->FindNext())
2848 //UpdateLogInfoLabel();
2850 return 0;
2853 void CGitLogListBase::OnColumnResized(NMHDR *pNMHDR, LRESULT *pResult)
2855 m_ColumnManager.OnColumnResized(pNMHDR,pResult);
2857 *pResult = FALSE;
2860 void CGitLogListBase::OnColumnMoved(NMHDR *pNMHDR, LRESULT *pResult)
2862 m_ColumnManager.OnColumnMoved(pNMHDR, pResult);
2864 Invalidate(FALSE);