OSDN Git Service

Add work dir copy at log dialog
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / GitLogListBase.cpp
1 // GitLogList.cpp : implementation file\r
2 //\r
3 /*\r
4         Description: qgit revision list view\r
5 \r
6         Author: Marco Costalba (C) 2005-2007\r
7 \r
8         Copyright: See COPYING file that comes with this distribution\r
9 \r
10 */\r
11 #include "stdafx.h"\r
12 #include "GitLogListBase.h"\r
13 #include "GitRev.h"\r
14 //#include "VssStyle.h"\r
15 #include "IconMenu.h"\r
16 // CGitLogListBase\r
17 #include "cursor.h"\r
18 #include "InputDlg.h"\r
19 #include "PropDlg.h"\r
20 #include "SVNProgressDlg.h"\r
21 #include "ProgressDlg.h"\r
22 //#include "RepositoryBrowser.h"\r
23 //#include "CopyDlg.h"\r
24 //#include "StatGraphDlg.h"\r
25 #include "Logdlg.h"\r
26 #include "MessageBox.h"\r
27 #include "Registry.h"\r
28 #include "AppUtils.h"\r
29 #include "PathUtils.h"\r
30 #include "StringUtils.h"\r
31 #include "UnicodeUtils.h"\r
32 #include "TempFile.h"\r
33 //#include "GitInfo.h"\r
34 //#include "GitDiff.h"\r
35 #include "IconMenu.h"\r
36 //#include "RevisionRangeDlg.h"\r
37 //#include "BrowseFolder.h"\r
38 //#include "BlameDlg.h"\r
39 //#include "Blame.h"\r
40 //#include "GitHelpers.h"\r
41 #include "GitStatus.h"\r
42 //#include "LogDlgHelper.h"\r
43 //#include "CachedLogInfo.h"\r
44 //#include "RepositoryInfo.h"\r
45 //#include "EditPropertiesDlg.h"\r
46 #include "FileDiffDlg.h"\r
47 #include "..\\TortoiseShell\\Resource.h"\r
48 \r
49 \r
50 IMPLEMENT_DYNAMIC(CGitLogListBase, CHintListCtrl)\r
51 \r
52 CGitLogListBase::CGitLogListBase():CHintListCtrl()\r
53         ,m_regMaxBugIDColWidth(_T("Software\\TortoiseGit\\MaxBugIDColWidth"), 200)\r
54         ,m_nSearchIndex(0)\r
55         ,m_bNoDispUpdates(FALSE)\r
56         , m_bThreadRunning(FALSE)\r
57         , m_bStrictStopped(false)\r
58         , m_pStoreSelection(NULL)\r
59         , m_nSelectedFilter(LOGFILTER_ALL)\r
60         , m_bVista(false)\r
61         , m_bShowWC(false)\r
62 {\r
63         // use the default GUI font, create a copy of it and\r
64         // change the copy to BOLD (leave the rest of the font\r
65         // the same)\r
66         HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);\r
67         LOGFONT lf = {0};\r
68         GetObject(hFont, sizeof(LOGFONT), &lf);\r
69         lf.lfWeight = FW_BOLD;\r
70         m_boldFont = CreateFontIndirect(&lf);\r
71         \r
72         m_bShowBugtraqColumn=0;\r
73 \r
74         m_IsIDReplaceAction=FALSE;\r
75 \r
76         m_wcRev.m_CommitHash=GIT_REV_ZERO;\r
77         m_wcRev.m_Subject=_T("Working dir changes");\r
78         m_wcRev.m_ParentHash.clear();\r
79         m_wcRev.m_Mark=_T('-');\r
80         m_wcRev.m_IsUpdateing=FALSE;\r
81         m_wcRev.m_IsFull = TRUE;\r
82 \r
83         m_hModifiedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONMODIFIED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);\r
84         m_hReplacedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONREPLACED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);\r
85         m_hAddedIcon    =  (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONADDED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);\r
86         m_hDeletedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONDELETED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);\r
87 \r
88         m_bFilterWithRegex = !!CRegDWORD(_T("Software\\TortoiseGit\\UseRegexFilter"), TRUE);\r
89 \r
90         g_Git.GetMapHashToFriendName(m_HashMap);\r
91         m_CurrentBranch=g_Git.GetCurrentBranch();\r
92         this->m_HeadHash=g_Git.GetHash(CString(_T("HEAD"))).Left(40);\r
93 \r
94         m_From=CTime(1970,1,2,0,0,0);\r
95         m_To=CTime::GetCurrentTime();\r
96     m_ShowMask = 0;\r
97         m_LoadingThread = NULL;\r
98 \r
99         m_bExitThread=FALSE;\r
100         m_IsOldFirst = FALSE;\r
101         m_IsRebaseReplaceGraph = FALSE;\r
102 \r
103 \r
104         for(int i=0;i<Lanes::COLORS_NUM;i++)\r
105         {\r
106                 m_LineColors[i] = m_Colors.GetColor((CColors::Colors)(CColors::BranchLine1+i));\r
107         }\r
108         // get short/long datetime setting from registry\r
109         DWORD RegUseShortDateFormat = CRegDWORD(_T("Software\\TortoiseGit\\LogDateFormat"), TRUE);\r
110         if ( RegUseShortDateFormat )\r
111         {\r
112                 m_DateFormat = DATE_SHORTDATE;\r
113         }\r
114         else\r
115         {\r
116                 m_DateFormat = DATE_LONGDATE;\r
117         }\r
118         // get relative time display setting from registry\r
119         DWORD regRelativeTimes = CRegDWORD(_T("Software\\TortoiseGit\\RelativeTimes"), FALSE);\r
120         m_bRelativeTimes = (regRelativeTimes != 0);\r
121         m_ContextMenuMask = 0xFFFFFFFFFFFFFFFF;\r
122 \r
123         m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_PICK);\r
124         m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_SQUASH);\r
125         m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_EDIT);\r
126         m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_SKIP);\r
127 \r
128         OSVERSIONINFOEX inf;\r
129         SecureZeroMemory(&inf, sizeof(OSVERSIONINFOEX));\r
130         inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\r
131         GetVersionEx((OSVERSIONINFO *)&inf);\r
132         WORD fullver = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);\r
133         m_bVista = (fullver >= 0x0600);\r
134 \r
135         m_ColumnRegKey=_T("log");\r
136 }\r
137 \r
138 CGitLogListBase::~CGitLogListBase()\r
139 {\r
140         InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
141 \r
142         DestroyIcon(m_hModifiedIcon);\r
143         DestroyIcon(m_hReplacedIcon);\r
144         DestroyIcon(m_hAddedIcon);\r
145         DestroyIcon(m_hDeletedIcon);\r
146         m_logEntries.ClearAll();\r
147 \r
148         if (m_boldFont)\r
149                 DeleteObject(m_boldFont);\r
150 \r
151         if ( m_pStoreSelection )\r
152         {\r
153                 delete m_pStoreSelection;\r
154                 m_pStoreSelection = NULL;\r
155         }\r
156 \r
157         if(this->m_bThreadRunning)\r
158         {\r
159                 m_bExitThread=true;\r
160                 WaitForSingleObject(m_LoadingThread->m_hThread,1000);\r
161                 TerminateThread();\r
162         }\r
163 }\r
164 \r
165 \r
166 BEGIN_MESSAGE_MAP(CGitLogListBase, CHintListCtrl)\r
167         ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdrawLoglist)\r
168         ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetdispinfoLoglist)\r
169         ON_WM_CONTEXTMENU()\r
170         ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclkLoglist)\r
171         ON_NOTIFY_REFLECT(LVN_ODFINDITEM,OnLvnOdfinditemLoglist)\r
172         ON_WM_CREATE()\r
173         ON_WM_DESTROY()\r
174         ON_MESSAGE(MSG_LOADED,OnLoad)\r
175         ON_WM_MEASUREITEM()\r
176         ON_WM_MEASUREITEM_REFLECT()\r
177 END_MESSAGE_MAP()\r
178 \r
179 void CGitLogListBase::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)\r
180 {\r
181         // TODO: ÔÚ´ËÌí¼ÓÏûÏ¢´¦Àí³ÌÐò´úÂëºÍ/»òµ÷ÓÃĬÈÏÖµ\r
182 \r
183         CListCtrl::OnMeasureItem(nIDCtl, lpMeasureItemStruct);\r
184 }\r
185 \r
186 void CGitLogListBase::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)\r
187 {\r
188         //if (m_nRowHeight>0)\r
189         {\r
190                 lpMeasureItemStruct->itemHeight = 50;\r
191         }\r
192 }\r
193 \r
194 int CGitLogListBase:: OnCreate(LPCREATESTRUCT lpCreateStruct)\r
195 {\r
196         PreSubclassWindow();\r
197         return CHintListCtrl::OnCreate(lpCreateStruct);\r
198 }\r
199 \r
200 void CGitLogListBase::PreSubclassWindow()\r
201 {\r
202         SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES);\r
203         // load the icons for the action columns\r
204 //      m_Theme.Open(m_hWnd, L"ListView");\r
205         m_Theme.Open(m_hWnd, L"Explorer::ListView;ListView");\r
206         m_Theme.SetWindowTheme(m_hWnd, L"Explorer", NULL);\r
207         CHintListCtrl::PreSubclassWindow();\r
208 }\r
209 \r
210 void CGitLogListBase::InsertGitColumn()\r
211 {\r
212         CString temp;\r
213 \r
214         int c = GetHeaderCtrl()->GetItemCount()-1;\r
215         \r
216         while (c>=0)\r
217                 DeleteColumn(c--);\r
218         temp.LoadString(IDS_LOG_GRAPH);\r
219 \r
220         if(m_IsRebaseReplaceGraph)\r
221         {\r
222                 temp=_T("Rebase");\r
223         }\r
224         else\r
225         {\r
226                 temp.LoadString(IDS_LOG_GRAPH);\r
227         }\r
228         InsertColumn(this->LOGLIST_GRAPH, temp);\r
229         \r
230 #if 0   \r
231         // make the revision column right aligned\r
232         LVCOLUMN Column;\r
233         Column.mask = LVCF_FMT;\r
234         Column.fmt = LVCFMT_RIGHT;\r
235         SetColumn(0, &Column); \r
236 #endif  \r
237 //      CString log;\r
238 //      g_Git.GetLog(log);\r
239 \r
240         if(m_IsIDReplaceAction)\r
241         {\r
242                 temp.LoadString(IDS_LOG_ID);\r
243                 InsertColumn(this->LOGLIST_ACTION, temp);\r
244         }\r
245         else\r
246         {\r
247                 temp.LoadString(IDS_LOG_ACTIONS);\r
248                 InsertColumn(this->LOGLIST_ACTION, temp);\r
249         }\r
250         temp.LoadString(IDS_LOG_MESSAGE);\r
251         InsertColumn(this->LOGLIST_MESSAGE, temp);\r
252         \r
253         temp.LoadString(IDS_LOG_AUTHOR);\r
254         InsertColumn(this->LOGLIST_AUTHOR, temp);\r
255         \r
256         temp.LoadString(IDS_LOG_DATE);\r
257         InsertColumn(this->LOGLIST_DATE, temp);\r
258         \r
259 \r
260         if (m_bShowBugtraqColumn)\r
261         {\r
262 //              temp = m_ProjectProperties.sLabel;\r
263                 if (temp.IsEmpty())\r
264                         temp.LoadString(IDS_LOG_BUGIDS);\r
265                 InsertColumn(this->LOGLIST_BUG, temp);\r
266 \r
267         }\r
268         \r
269         SetRedraw(false);\r
270         ResizeAllListCtrlCols();\r
271         SetRedraw(true);\r
272 \r
273 }\r
274 \r
275 /**\r
276  * Resizes all columns in a list control to values in registry.\r
277  */\r
278 void CGitLogListBase::ResizeAllListCtrlCols()\r
279 {\r
280         // column max and min widths to allow\r
281         static const int nMinimumWidth = 10;\r
282         static const int nMaximumWidth = 1000;\r
283         CHeaderCtrl* pHdrCtrl = (CHeaderCtrl*)(GetDlgItem(0));\r
284         if (pHdrCtrl)\r
285         {\r
286                 int numcols = pHdrCtrl->GetItemCount();\r
287                 for (int col = 0; col < numcols; col++)\r
288                 {\r
289                         // get width for this col last time from registry\r
290                         CString regentry;\r
291                         regentry.Format( _T("Software\\TortoiseGit\\%s\\ColWidth%d"),m_ColumnRegKey, col);\r
292                         CRegDWORD regwidth(regentry, 0);\r
293                         int cx = regwidth;\r
294                         if ( cx == 0 )\r
295                         {\r
296                                 // no saved value, setup sensible defaults\r
297                                 if (col == this->LOGLIST_MESSAGE)\r
298                                 {\r
299                                         cx = LOGLIST_MESSAGE_MIN;\r
300                                 }\r
301                                 else\r
302                                 {\r
303                                         cx = ICONITEMBORDER+16*4;\r
304                                 }\r
305                         }\r
306                         if (cx < nMinimumWidth)\r
307                         {\r
308                                 cx = nMinimumWidth;\r
309                         } else if (cx > nMaximumWidth)\r
310                         {\r
311                                 cx = nMaximumWidth;\r
312                         }\r
313 \r
314                         SetColumnWidth(col, cx);\r
315                 }\r
316         }\r
317 \r
318 }\r
319 \r
320 \r
321 BOOL CGitLogListBase::GetShortName(CString ref, CString &shortname,CString prefix)\r
322 {\r
323         TRACE(_T("%s %s\r\n"),ref,prefix);\r
324         if(ref.Left(prefix.GetLength()) ==  prefix)\r
325         {\r
326                 shortname = ref.Right(ref.GetLength()-prefix.GetLength());\r
327                 if(shortname.Right(3)==_T("^{}"))\r
328                         shortname=shortname.Left(shortname.GetLength()-3);\r
329                 return TRUE;\r
330         }\r
331         return FALSE;\r
332 }\r
333 \r
334 void CGitLogListBase::FillBackGround(HDC hdc, int Index,CRect &rect)\r
335 {       \r
336 //      HBRUSH brush;\r
337         LVITEM   rItem;\r
338         SecureZeroMemory(&rItem, sizeof(LVITEM));\r
339         rItem.mask  = LVIF_STATE;\r
340         rItem.iItem = Index;\r
341         rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;\r
342         GetItem(&rItem);\r
343 \r
344         GitRev* pLogEntry = (GitRev*)m_arShownList.GetAt(Index);\r
345         HBRUSH brush = NULL;\r
346 \r
347         \r
348         if (m_Theme.IsAppThemed() && m_bVista)\r
349         {\r
350                 int state = LISS_NORMAL;\r
351                 if (rItem.state & LVIS_SELECTED)\r
352                 {\r
353                         if (::GetFocus() == m_hWnd)\r
354                                 state |= LISS_SELECTED;\r
355                         else\r
356                                 state |= LISS_SELECTEDNOTFOCUS;\r
357                 }\r
358                 else\r
359                 {\r
360                         if(pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_SQUASH)\r
361                                 brush = ::CreateSolidBrush(RGB(156,156,156));\r
362                         else if(pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_EDIT)\r
363                                 brush = ::CreateSolidBrush(RGB(200,200,128));\r
364 \r
365                         if(pLogEntry->m_CommitHash == GIT_REV_ZERO)\r
366                                 brush = ::CreateSolidBrush(RGB(200,200,128));\r
367                 }\r
368 \r
369                 if (brush != NULL)\r
370                 {\r
371                         ::FillRect(hdc, &rect, brush);\r
372                         ::DeleteObject(brush);\r
373                 }\r
374                 else\r
375                 {\r
376                         if (m_Theme.IsBackgroundPartiallyTransparent(LVP_LISTITEM, state))\r
377                                 m_Theme.DrawParentBackground(m_hWnd, hdc, &rect);\r
378 \r
379                         CRect rectDraw = rect;\r
380                         if(rItem.state & LVIS_SELECTED)\r
381                                 rectDraw.InflateRect(1,0);\r
382                         else\r
383                                 rectDraw.InflateRect(1,1);\r
384 \r
385                         m_Theme.DrawBackground(hdc, LVP_LISTITEM, state, rectDraw, &rect);\r
386                 }\r
387         }\r
388         else\r
389         {\r
390                 \r
391                 if (rItem.state & LVIS_SELECTED)\r
392                 {\r
393                         if (::GetFocus() == m_hWnd)\r
394                                 brush = ::CreateSolidBrush(::GetSysColor(COLOR_HIGHLIGHT));\r
395                         else\r
396                                 brush = ::CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));\r
397                 }\r
398                 else\r
399                 {\r
400                         //if (pLogEntry->bCopiedSelf)\r
401                         //      brush = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));\r
402                         //else\r
403                         if(pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_SQUASH)\r
404                                 brush = ::CreateSolidBrush(RGB(156,156,156));\r
405                         else if(pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_EDIT)\r
406                                 brush = ::CreateSolidBrush(RGB(200,200,128));\r
407                         else if(pLogEntry->m_CommitHash == GIT_REV_ZERO)\r
408                                 brush = ::CreateSolidBrush(RGB(200,200,128));\r
409                         else\r
410                                 brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));\r
411                 }\r
412                 if (brush == NULL)\r
413                         return;\r
414 \r
415                 ::FillRect(hdc, &rect, brush);\r
416                 ::DeleteObject(brush);\r
417                 \r
418         }\r
419 }\r
420 \r
421 void CGitLogListBase::DrawTagBranch(HDC hdc,CRect &rect,INT_PTR index)\r
422 {\r
423         GitRev* data = (GitRev*)m_arShownList.GetAt(index);\r
424         CRect rt=rect;\r
425         LVITEM   rItem;\r
426         SecureZeroMemory(&rItem, sizeof(LVITEM));\r
427         rItem.mask  = LVIF_STATE;\r
428         rItem.iItem = index;\r
429         rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;\r
430         GetItem(&rItem);\r
431 \r
432         for(unsigned int i=0;i<m_HashMap[data->m_CommitHash].size();i++)\r
433         {\r
434                 CString str;\r
435                 str=m_HashMap[data->m_CommitHash][i];\r
436                 \r
437                 CString shortname;\r
438                 HBRUSH brush = 0;\r
439                 shortname = _T("");\r
440                 COLORREF colRef = 0;\r
441 \r
442                 //Determine label color\r
443                 if(GetShortName(str,shortname,_T("refs/heads/")))\r
444                 {\r
445                         if( shortname == m_CurrentBranch )\r
446                                 colRef = m_Colors.GetColor(CColors::CurrentBranch);\r
447                         else\r
448                                 colRef = m_Colors.GetColor(CColors::LocalBranch);\r
449 \r
450                 }else if(GetShortName(str,shortname,_T("refs/remotes/")))\r
451                 {\r
452                         colRef = m_Colors.GetColor(CColors::RemoteBranch);\r
453                 }\r
454                 else if(GetShortName(str,shortname,_T("refs/tags/")))\r
455                 {\r
456                         colRef = m_Colors.GetColor(CColors::Tag);\r
457                 }\r
458                 else if(GetShortName(str,shortname,_T("refs/stash")))\r
459                 {\r
460                         colRef = m_Colors.GetColor(CColors::Stash);\r
461                         shortname=_T("stash");\r
462                 }\r
463 \r
464                 //When row selected, ajust label color\r
465                 if (!(m_Theme.IsAppThemed() && m_bVista))\r
466                         if (rItem.state & LVIS_SELECTED)\r
467                                 colRef = CColors::MixColors(colRef, ::GetSysColor(COLOR_HIGHLIGHT), 150);\r
468 \r
469                 brush = ::CreateSolidBrush(colRef);\r
470                 \r
471 \r
472                 if(!shortname.IsEmpty())\r
473                 {\r
474                         SIZE size;\r
475                         memset(&size,0,sizeof(SIZE));\r
476                         GetTextExtentPoint32(hdc, shortname,shortname.GetLength(),&size);\r
477                 \r
478                         rt.SetRect(rt.left,rt.top,rt.left+size.cx,rt.bottom);\r
479                         rt.right+=8;\r
480 \r
481                         //Fill interior of ref label\r
482                         ::FillRect(hdc, &rt, brush);\r
483 \r
484                         //Draw edge of label\r
485                         CDC W_Dc;\r
486                         W_Dc.Attach(hdc);\r
487 \r
488                         CRect rectEdge = rt;\r
489 \r
490                         W_Dc.Draw3dRect(rectEdge, m_Colors.Lighten(colRef,100), m_Colors.Darken(colRef,100));\r
491                         rectEdge.DeflateRect(1,1);\r
492                         W_Dc.Draw3dRect(rectEdge, m_Colors.Lighten(colRef,50), m_Colors.Darken(colRef,50));\r
493 \r
494                         W_Dc.Detach();\r
495 \r
496                         //Draw text inside label\r
497                         if (m_Theme.IsAppThemed() && m_bVista)\r
498                         {\r
499                                 int txtState = LISS_NORMAL;\r
500                                 if (rItem.state & LVIS_SELECTED)\r
501                                         txtState = LISS_SELECTED;\r
502 \r
503                                 m_Theme.DrawText(hdc, LVP_LISTITEM, txtState, shortname, -1, DT_CENTER | DT_SINGLELINE | DT_VCENTER, 0, &rt);\r
504                         }\r
505                         else\r
506                         {\r
507                                 if (rItem.state & LVIS_SELECTED)\r
508                                 {\r
509                                         COLORREF clrNew = ::GetSysColor(COLOR_HIGHLIGHTTEXT);\r
510                                         COLORREF   clrOld   = ::SetTextColor(hdc,clrNew);   \r
511                                         ::DrawText(hdc,shortname,shortname.GetLength(),&rt,DT_CENTER | DT_SINGLELINE | DT_VCENTER);\r
512                                         ::SetTextColor(hdc,clrOld);\r
513                                 }else\r
514                                 {\r
515                                         ::DrawText(hdc,shortname,shortname.GetLength(),&rt,DT_CENTER | DT_SINGLELINE | DT_VCENTER);\r
516                                 }\r
517                         }\r
518 \r
519                         \r
520                         //::MoveToEx(hdc,rt.left,rt.top,NULL);\r
521                         //::LineTo(hdc,rt.right,rt.top);\r
522                         //::LineTo(hdc,rt.right,rt.bottom);\r
523                         //::LineTo(hdc,rt.left,rt.bottom);\r
524                         //::LineTo(hdc,rt.left,rt.top);\r
525 \r
526                                 \r
527                         rt.left=rt.right+1;\r
528                 }\r
529                 if(brush)\r
530                         ::DeleteObject(brush);\r
531         }               \r
532         rt.right=rect.right;\r
533 \r
534         if (m_Theme.IsAppThemed() && m_bVista)\r
535         {\r
536                 int txtState = LISS_NORMAL;\r
537                 if (rItem.state & LVIS_SELECTED)\r
538                         txtState = LISS_SELECTED;\r
539 \r
540                 m_Theme.DrawText(hdc, LVP_LISTITEM, txtState, data->m_Subject, -1, DT_LEFT | DT_SINGLELINE | DT_VCENTER, 0, &rt);\r
541         }\r
542         else\r
543         {\r
544                 if (rItem.state & LVIS_SELECTED)\r
545                 {\r
546                         COLORREF   clrOld   = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT));   \r
547                         ::DrawText(hdc,data->m_Subject,data->m_Subject.GetLength(),&rt,DT_LEFT | DT_SINGLELINE | DT_VCENTER);\r
548                         ::SetTextColor(hdc,clrOld);   \r
549                 }else\r
550                 {\r
551                         ::DrawText(hdc,data->m_Subject,data->m_Subject.GetLength(),&rt,DT_LEFT | DT_SINGLELINE | DT_VCENTER);\r
552                 }\r
553         }\r
554 }\r
555 \r
556 static COLORREF blend(const COLORREF& col1, const COLORREF& col2, int amount = 128) {\r
557 \r
558         // Returns ((256 - amount)*col1 + amount*col2) / 256;\r
559         return RGB(((256 - amount)*GetRValue(col1)   + amount*GetRValue(col2)  ) / 256,\r
560                       ((256 - amount)*GetGValue(col1) + amount*GetGValue(col2) ) / 256,\r
561                       ((256 - amount)*GetBValue(col1)  + amount*GetBValue(col2) ) / 256);\r
562 }\r
563 \r
564 Gdiplus::Color GetGdiColor(COLORREF col)\r
565 {\r
566         return Gdiplus::Color(GetRValue(col),GetGValue(col),GetBValue(col));\r
567 }\r
568 void CGitLogListBase::paintGraphLane(HDC hdc, int laneHeight,int type, int x1, int x2,\r
569                                       const COLORREF& col,const COLORREF& activeColor, int top\r
570                                                                           )  \r
571 {\r
572         int h = laneHeight / 2;\r
573         int m = (x1 + x2) / 2;\r
574         int r = (x2 - x1) / 3;\r
575         int d =  2 * r;\r
576 \r
577         #define P_CENTER m , h+top\r
578         #define P_0      x2, h+top\r
579         #define P_90     m , 0+top\r
580         #define P_180    x1, h+top\r
581         #define P_270    m , 2 * h+top\r
582         #define R_CENTER m - r, h - r+top, m - r+d, h - r+top+d\r
583 \r
584 \r
585         #define DELTA_UR_B 2*(x1 - m), 2*h +top \r
586         #define DELTA_UR_E 0*16, 90*16 +top  // -,\r
587 \r
588         #define DELTA_DR_B 2*(x1 - m), 2*-h +top\r
589         #define DELTA_DR_E 270*16, 90*16 +top  // -'\r
590 \r
591         #define DELTA_UL_B 2*(x2 - m), 2*h +top\r
592         #define DELTA_UL_E 90*16, 90*16 +top //  ,-\r
593 \r
594         #define DELTA_DL_B 2*(x2 - m),2*-h +top \r
595         #define DELTA_DL_E 180*16, 90*16  //  '-\r
596 \r
597         #define CENTER_UR x1, 2*h, 225\r
598         #define CENTER_DR x1, 0  , 135\r
599         #define CENTER_UL x2, 2*h, 315\r
600         #define CENTER_DL x2, 0  ,  45\r
601 \r
602 \r
603         Gdiplus::Graphics graphics( hdc );\r
604 \r
605         // arc\r
606         switch (type) {\r
607         case Lanes::JOIN:\r
608         case Lanes::JOIN_R:\r
609         case Lanes::HEAD:\r
610         case Lanes::HEAD_R: \r
611         {\r
612                 Gdiplus::LinearGradientBrush gradient(\r
613                                                                 Gdiplus::Point(x1-2, h+top-2),\r
614                                                                 Gdiplus::Point(P_270),\r
615                                                                 GetGdiColor(activeColor),GetGdiColor(col));\r
616 \r
617                 \r
618                 Gdiplus::Pen mypen(&gradient,2);\r
619                 //Gdiplus::Pen mypen(Gdiplus::Color(0,0,0),2);\r
620                 \r
621                 //graphics.DrawRectangle(&mypen,x1-(x2-x1)/2,top+h, x2-x1,laneHeight);\r
622                 graphics.DrawArc(&mypen,x1-(x2-x1)/2-1,top+h-1, x2-x1+1,laneHeight+1,270,90);\r
623                 //graphics.DrawLine(&mypen,x1-1,h+top,P_270);\r
624 \r
625                 break;\r
626         }\r
627         case Lanes::JOIN_L: \r
628         {\r
629         \r
630                 Gdiplus::Pen mypen(Gdiplus::Color(0,0,0),2);\r
631 \r
632                 \r
633                 graphics.DrawArc(&mypen,x1,top+h, x2-x1,laneHeight,270,90);\r
634 \r
635                 break;\r
636         }\r
637         case Lanes::TAIL:\r
638         case Lanes::TAIL_R: \r
639         {\r
640                 \r
641                 Gdiplus::LinearGradientBrush gradient(\r
642                                                                 Gdiplus::Point(x1-2, h+top-2),\r
643                                                                 Gdiplus::Point(P_90),\r
644                                                                 GetGdiColor(activeColor),GetGdiColor(col));\r
645 \r
646                 \r
647                 Gdiplus::Pen mypen(&gradient,2);\r
648 \r
649                 graphics.DrawArc(&mypen,x1-(x2-x1)/2-1,top-h-1, x2-x1+1,laneHeight+1,0,90);\r
650 \r
651 \r
652 #if 0\r
653                 QConicalGradient gradient(CENTER_DR);\r
654                 gradient.setColorAt(0.375, activeCol);\r
655                 gradient.setColorAt(0.625, col);\r
656                 myPen.setBrush(gradient);\r
657                 p->setPen(myPen);\r
658                 p->drawArc(P_CENTER, DELTA_DR);\r
659 #endif\r
660                 break;\r
661         }\r
662         default:\r
663                 break;\r
664         }\r
665 \r
666 \r
667         //static QPen myPen(Qt::black, 2); // fast path here\r
668         CPen pen;\r
669         pen.CreatePen(PS_SOLID,2,col);\r
670         //myPen.setColor(col);\r
671         HPEN oldpen=(HPEN)::SelectObject(hdc,(HPEN)pen);\r
672 \r
673         Gdiplus::Pen myPen(GetGdiColor(col),2);\r
674 \r
675         //p->setPen(myPen);\r
676 \r
677         // vertical line\r
678         switch (type) {\r
679         case Lanes::ACTIVE:\r
680         case Lanes::NOT_ACTIVE:\r
681         case Lanes::MERGE_FORK:\r
682         case Lanes::MERGE_FORK_R:\r
683         case Lanes::MERGE_FORK_L:\r
684         case Lanes::JOIN:\r
685         case Lanes::JOIN_R:\r
686         case Lanes::JOIN_L:\r
687         case Lanes::CROSS:\r
688                 //DrawLine(hdc,P_90,P_270);\r
689                 graphics.DrawLine(&myPen,P_90,P_270);\r
690                 //p->drawLine(P_90, P_270);\r
691                 break;\r
692         case Lanes::HEAD_L:\r
693         case Lanes::BRANCH:\r
694                 //DrawLine(hdc,P_CENTER,P_270);\r
695                 graphics.DrawLine(&myPen,P_CENTER,P_270);\r
696                 //p->drawLine(P_CENTER, P_270);\r
697                 break;\r
698         case Lanes::TAIL_L:\r
699         case Lanes::INITIAL:\r
700         case Lanes::BOUNDARY:\r
701         case Lanes::BOUNDARY_C:\r
702         case Lanes::BOUNDARY_R:\r
703         case Lanes::BOUNDARY_L:\r
704                 //DrawLine(hdc,P_90, P_CENTER);\r
705                 graphics.DrawLine(&myPen,P_90,P_CENTER);\r
706                 //p->drawLine(P_90, P_CENTER);\r
707                 break;\r
708         default:\r
709                 break;\r
710         }\r
711 \r
712         myPen.SetColor(GetGdiColor(activeColor));\r
713 \r
714         // horizontal line\r
715         switch (type) {\r
716         case Lanes::MERGE_FORK:\r
717         case Lanes::JOIN:\r
718         case Lanes::HEAD:\r
719         case Lanes::TAIL:\r
720         case Lanes::CROSS:\r
721         case Lanes::CROSS_EMPTY:\r
722         case Lanes::BOUNDARY_C:\r
723                 //DrawLine(hdc,P_180,P_0);\r
724                 graphics.DrawLine(&myPen,P_180,P_0);\r
725                 //p->drawLine(P_180, P_0);\r
726                 break;\r
727         case Lanes::MERGE_FORK_R:\r
728         case Lanes::BOUNDARY_R:\r
729                 //DrawLine(hdc,P_180,P_CENTER);\r
730                 graphics.DrawLine(&myPen,P_180,P_CENTER);\r
731                 //p->drawLine(P_180, P_CENTER);\r
732                 break;\r
733         case Lanes::MERGE_FORK_L:\r
734         case Lanes::HEAD_L:\r
735         case Lanes::TAIL_L:\r
736         case Lanes::BOUNDARY_L:\r
737                 //DrawLine(hdc,P_CENTER,P_0);\r
738                 graphics.DrawLine(&myPen,P_CENTER,P_0);\r
739                 //p->drawLine(P_CENTER, P_0);\r
740                 break;\r
741         default:\r
742                 break;\r
743         }\r
744 \r
745         CBrush brush;\r
746         brush.CreateSolidBrush(col);\r
747         HBRUSH oldbrush=(HBRUSH)::SelectObject(hdc,(HBRUSH)brush);\r
748         // center symbol, e.g. rect or ellipse\r
749         switch (type) {\r
750         case Lanes::ACTIVE:\r
751         case Lanes::INITIAL:\r
752         case Lanes::BRANCH:\r
753 \r
754                 //p->setPen(Qt::NoPen);\r
755                 //p->setBrush(col);\r
756                 ::Ellipse(hdc, R_CENTER);\r
757                 //p->drawEllipse(R_CENTER);\r
758                 break;\r
759         case Lanes::MERGE_FORK:\r
760         case Lanes::MERGE_FORK_R:\r
761         case Lanes::MERGE_FORK_L:\r
762                 //p->setPen(Qt::NoPen);\r
763                 //p->setBrush(col);\r
764                 //p->drawRect(R_CENTER);\r
765                 Rectangle(hdc,R_CENTER);\r
766                 break;\r
767         case Lanes::UNAPPLIED:\r
768                 // Red minus sign\r
769                 //p->setPen(Qt::NoPen);\r
770                 //p->setBrush(Qt::red);\r
771                 //p->drawRect(m - r, h - 1, d, 2);\r
772                 ::Rectangle(hdc,m-r,h-1,d,2);\r
773                 break;\r
774         case Lanes::APPLIED:\r
775                 // Green plus sign\r
776                 //p->setPen(Qt::NoPen);\r
777                 //p->setBrush(DARK_GREEN);\r
778                 //p->drawRect(m - r, h - 1, d, 2);\r
779                 //p->drawRect(m - 1, h - r, 2, d);\r
780                 ::Rectangle(hdc,m-r,h-1,d,2);\r
781                 ::Rectangle(hdc,m-1,h-r,2,d);\r
782                 break;\r
783         case Lanes::BOUNDARY:\r
784                 //p->setBrush(back);\r
785                 //p->drawEllipse(R_CENTER);\r
786                 ::Ellipse(hdc, R_CENTER);\r
787                 break;\r
788         case Lanes::BOUNDARY_C:\r
789         case Lanes::BOUNDARY_R:\r
790         case Lanes::BOUNDARY_L:\r
791                 //p->setBrush(back);\r
792                 //p->drawRect(R_CENTER);\r
793                 ::Rectangle(hdc,R_CENTER);\r
794                 break;\r
795         default:\r
796                 break;\r
797         }\r
798 \r
799         ::SelectObject(hdc,oldpen);\r
800         ::SelectObject(hdc,oldbrush);\r
801         #undef P_CENTER\r
802         #undef P_0\r
803         #undef P_90\r
804         #undef P_180\r
805         #undef P_270\r
806         #undef R_CENTER\r
807 }\r
808 \r
809 void CGitLogListBase::DrawGraph(HDC hdc,CRect &rect,INT_PTR index)\r
810 {\r
811         //todo unfinished\r
812 //      return;\r
813         GitRev* data = (GitRev*)m_arShownList.GetAt(index);\r
814         if(data->m_CommitHash.IsEmpty())\r
815                 return;\r
816 \r
817         CRect rt=rect;\r
818         LVITEM   rItem;\r
819         SecureZeroMemory(&rItem, sizeof(LVITEM));\r
820         rItem.mask  = LVIF_STATE;\r
821         rItem.iItem = index;\r
822         rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;\r
823         GetItem(&rItem);\r
824 \r
825         \r
826 //      p->translate(QPoint(opt.rect.left(), opt.rect.top()));\r
827 \r
828 \r
829 \r
830         if (data->m_Lanes.size() == 0)\r
831                 m_logEntries.setLane(data->m_CommitHash);\r
832 \r
833         std::vector<int>& lanes=data->m_Lanes;\r
834         UINT laneNum = lanes.size();\r
835         UINT activeLane = 0;\r
836         for (UINT i = 0; i < laneNum; i++)\r
837                 if (Lanes::isMerge(lanes[i])) {\r
838                         activeLane = i;\r
839                         break;\r
840                 }\r
841 \r
842         int x1 = 0, x2 = 0;\r
843         int maxWidth = rect.Width();\r
844         int lw = 3 * rect.Height() / 4; //laneWidth() \r
845 \r
846         COLORREF activeColor = m_LineColors[activeLane % Lanes::COLORS_NUM];\r
847         //if (opt.state & QStyle::State_Selected)\r
848         //      activeColor = blend(activeColor, opt.palette.highlightedText().color(), 208);\r
849         \r
850 \r
851         for (unsigned int i = 0; i < laneNum && x2 < maxWidth; i++) \r
852         {\r
853 \r
854                 x1 = x2;\r
855                 x2 += lw;\r
856 \r
857                 int ln = lanes[i];\r
858                 if (ln == Lanes::EMPTY)\r
859                         continue;\r
860 \r
861                 COLORREF color = i == activeLane ? activeColor : m_LineColors[i % Lanes::COLORS_NUM];\r
862                 paintGraphLane(hdc, rect.Height(),ln, x1+rect.left, x2+rect.left, color,activeColor, rect.top);\r
863         }\r
864 \r
865 #if 0\r
866         for (UINT i = 0; i < laneNum && x2 < maxWidth; i++) {\r
867 \r
868                 x1 = x2;\r
869                 x2 += lw;\r
870 \r
871                 int ln = lanes[i];\r
872                 if (ln == Lanes::EMPTY)\r
873                         continue;\r
874 \r
875                 UINT col = (  Lanes:: isHead(ln) ||Lanes:: isTail(ln) || Lanes::isJoin(ln)\r
876                             || ln ==Lanes:: CROSS_EMPTY) ? activeLane : i;\r
877 \r
878                 if (ln == Lanes::CROSS) {\r
879                         paintGraphLane(hdc, rect.Height(),Lanes::NOT_ACTIVE, x1, x2, m_LineColors[col % Lanes::COLORS_NUM],rect.top);\r
880                         paintGraphLane(hdc, rect.Height(),Lanes::CROSS, x1, x2, m_LineColors[activeLane % Lanes::COLORS_NUM],rect.top);\r
881                 } else\r
882                         paintGraphLane(hdc, rect.Height(),ln, x1, x2, m_LineColors[col % Lanes::COLORS_NUM],rect.top);\r
883         }\r
884 #endif\r
885 \r
886 }\r
887 \r
888 void CGitLogListBase::OnNMCustomdrawLoglist(NMHDR *pNMHDR, LRESULT *pResult)\r
889 {\r
890 \r
891         NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );\r
892         // Take the default processing unless we set this to something else below.\r
893         *pResult = CDRF_DODEFAULT;\r
894 \r
895         if (m_bNoDispUpdates)\r
896                 return;\r
897 \r
898 \r
899 \r
900         switch (pLVCD->nmcd.dwDrawStage)\r
901         {\r
902         case CDDS_PREPAINT:\r
903                 {\r
904                         *pResult = CDRF_NOTIFYITEMDRAW;\r
905                         return;\r
906                 }\r
907                 break;\r
908         case CDDS_ITEMPREPAINT:\r
909                 {\r
910                         // This is the prepaint stage for an item. Here's where we set the\r
911                         // item's text color. \r
912                         \r
913                         // Tell Windows to send draw notifications for each subitem.\r
914                         *pResult = CDRF_NOTIFYSUBITEMDRAW;\r
915 \r
916                         COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);\r
917 \r
918                         if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)\r
919                         {\r
920                                 GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);\r
921                                 if (data)\r
922                                 {\r
923 #if 0\r
924                                         if (data->bCopiedSelf)\r
925                                         {\r
926                                                 // only change the background color if the item is not 'hot' (on vista with m_Themes enabled)\r
927                                                 if (!m_Theme.IsAppm_Themed() || !m_bVista || ((pLVCD->nmcd.uItemState & CDIS_HOT)==0))\r
928                                                         pLVCD->clrTextBk = GetSysColor(COLOR_MENU);\r
929                                         }\r
930 \r
931                                         if (data->bCopies)\r
932                                                 crText = m_Colors.GetColor(CColors::Modified);\r
933 #endif\r
934                                         if (data->m_Action& (CTGitPath::LOGACTIONS_REBASE_DONE| CTGitPath::LOGACTIONS_REBASE_SKIP) ) \r
935                                                 crText = RGB(128,128,128);\r
936 \r
937                                         if(data->m_Action&CTGitPath::LOGACTIONS_REBASE_SQUASH)\r
938                                                 pLVCD->clrTextBk = RGB(156,156,156);\r
939                                         else if(data->m_Action&CTGitPath::LOGACTIONS_REBASE_EDIT)\r
940                                                 pLVCD->clrTextBk  = RGB(200,200,128);\r
941                                         else if(data->m_CommitHash == GIT_REV_ZERO)\r
942                                                 pLVCD->clrTextBk  = RGB(200,200,128);\r
943                                         else\r
944                                                 pLVCD->clrTextBk  = ::GetSysColor(COLOR_WINDOW);\r
945 \r
946                                         if(data->m_Action&CTGitPath::LOGACTIONS_REBASE_CURRENT)\r
947                                         {\r
948                                                 SelectObject(pLVCD->nmcd.hdc, m_boldFont);\r
949                                                 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;\r
950                                         }\r
951 \r
952                                         if(data->m_CommitHash == m_HeadHash)\r
953                                         {\r
954                                                 SelectObject(pLVCD->nmcd.hdc, m_boldFont);\r
955                                                 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;\r
956                                         }\r
957 \r
958 //                                      if ((data->childStackDepth)||(m_mergedRevs.find(data->Rev) != m_mergedRevs.end()))\r
959 //                                              crText = GetSysColor(COLOR_GRAYTEXT);\r
960 //                                      \r
961                                         if (data->m_CommitHash == GIT_REV_ZERO)\r
962                                         {\r
963                                                 //crText = GetSysColor(COLOR_GRAYTEXT);\r
964                                                 SelectObject(pLVCD->nmcd.hdc, m_boldFont);\r
965                                                 // We changed the font, so we're returning CDRF_NEWFONT. This\r
966                                                 // tells the control to recalculate the extent of the text.\r
967                                                 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;\r
968                                         }\r
969                                 }\r
970                         }\r
971                         if (m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec)\r
972                         {\r
973                                 if (m_bStrictStopped)\r
974                                         crText = GetSysColor(COLOR_GRAYTEXT);\r
975                         }\r
976                         // Store the color back in the NMLVCUSTOMDRAW struct.\r
977                         pLVCD->clrText = crText;\r
978                         return;\r
979                 }\r
980                 break;\r
981         case CDDS_ITEMPREPAINT|CDDS_ITEM|CDDS_SUBITEM:\r
982                 {\r
983                         if ((m_bStrictStopped)&&(m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec))\r
984                         {\r
985                                 pLVCD->nmcd.uItemState &= ~(CDIS_SELECTED|CDIS_FOCUS);\r
986                         }\r
987 \r
988                         if (pLVCD->iSubItem == LOGLIST_GRAPH)\r
989                         {\r
990                                 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec && (!this->m_IsRebaseReplaceGraph) )\r
991                                 {\r
992                                         CRect rect;\r
993                                         GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);\r
994                                         if(pLVCD->iSubItem == 0)\r
995                                         {\r
996                                                 CRect second;\r
997                                                 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem+1, LVIR_BOUNDS, second);\r
998                                                 rect.right=second.left;\r
999                                         }\r
1000                                         \r
1001                                         TRACE(_T("A Graphic left %d right %d\r\n"),rect.left,rect.right);\r
1002                                         FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);\r
1003                                         DrawGraph(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);\r
1004 \r
1005                                         *pResult = CDRF_SKIPDEFAULT;\r
1006                                         return;\r
1007                                 \r
1008                                 }\r
1009                         }\r
1010 \r
1011                         if (pLVCD->iSubItem == LOGLIST_MESSAGE)\r
1012                         {\r
1013                                 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)\r
1014                                 {\r
1015                                         GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);\r
1016                                         //if(!data->m_IsFull)\r
1017                                         //{\r
1018                                                 //if(data->SafeFetchFullInfo(&g_Git))\r
1019                                                 //      this->Invalidate();\r
1020                                                 //TRACE(_T("Update ... %d\r\n"),pLVCD->nmcd.dwItemSpec);\r
1021                                         //}\r
1022 \r
1023                                         if(m_HashMap[data->m_CommitHash].size()!=0)\r
1024                                         {\r
1025                                                 CRect rect;\r
1026 \r
1027                                                 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);\r
1028                                         \r
1029                                                 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);\r
1030                                                 DrawTagBranch(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);\r
1031 \r
1032                                                 *pResult = CDRF_SKIPDEFAULT;\r
1033                                                 return;\r
1034 \r
1035                                         }\r
1036 \r
1037                                 }       \r
1038                         }\r
1039                         \r
1040                         if (pLVCD->iSubItem == 1)\r
1041                         {\r
1042                                 if(this->m_IsIDReplaceAction)\r
1043                                 {\r
1044                                         *pResult = CDRF_DODEFAULT;\r
1045                                         return;\r
1046                                 }\r
1047                                 *pResult = CDRF_DODEFAULT;\r
1048 \r
1049                                 if (m_arShownList.GetCount() <= (INT_PTR)pLVCD->nmcd.dwItemSpec)\r
1050                                         return;\r
1051 \r
1052                                 int             nIcons = 0;\r
1053                                 int             iconwidth = ::GetSystemMetrics(SM_CXSMICON);\r
1054                                 int             iconheight = ::GetSystemMetrics(SM_CYSMICON);\r
1055 \r
1056                                 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec));\r
1057                                 CRect rect;\r
1058                                 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);\r
1059                                 TRACE(_T("Action left %d right %d\r\n"),rect.left,rect.right);\r
1060                                 // Get the selected state of the\r
1061                                 // item being drawn.                                                    \r
1062 \r
1063                                 // Fill the background\r
1064                                 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);\r
1065                                 \r
1066                                 // Draw the icon(s) into the compatible DC\r
1067                                 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_MODIFIED)\r
1068                                         ::DrawIconEx(pLVCD->nmcd.hdc, rect.left + ICONITEMBORDER, rect.top, m_hModifiedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);\r
1069                                 nIcons++;\r
1070 \r
1071                                 if (pLogEntry->m_Action & (CTGitPath::LOGACTIONS_ADDED|CTGitPath::LOGACTIONS_COPY) )\r
1072                                         ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hAddedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);\r
1073                                 nIcons++;\r
1074 \r
1075                                 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_DELETED)\r
1076                                         ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hDeletedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);\r
1077                                 nIcons++;\r
1078 \r
1079                                 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_REPLACED)\r
1080                                         ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hReplacedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);\r
1081                                 nIcons++;\r
1082                                 *pResult = CDRF_SKIPDEFAULT;\r
1083                                 return;\r
1084                         }\r
1085                 }\r
1086                 break;\r
1087         }\r
1088         *pResult = CDRF_DODEFAULT;\r
1089 \r
1090 }\r
1091 \r
1092 // CGitLogListBase message handlers\r
1093 \r
1094 void CGitLogListBase::OnLvnGetdispinfoLoglist(NMHDR *pNMHDR, LRESULT *pResult)\r
1095 {\r
1096         NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);\r
1097 \r
1098         // Create a pointer to the item\r
1099         LV_ITEM* pItem = &(pDispInfo)->item;\r
1100 \r
1101         // Do the list need text information?\r
1102         if (!(pItem->mask & LVIF_TEXT))\r
1103                 return;\r
1104 \r
1105         // By default, clear text buffer.\r
1106         lstrcpyn(pItem->pszText, _T(""), pItem->cchTextMax);\r
1107 \r
1108         bool bOutOfRange = pItem->iItem >= ShownCountWithStopped();\r
1109         \r
1110         *pResult = 0;\r
1111         if (m_bNoDispUpdates || bOutOfRange)\r
1112                 return;\r
1113 \r
1114         // Which item number?\r
1115         int itemid = pItem->iItem;\r
1116         GitRev * pLogEntry = NULL;\r
1117         if (itemid < m_arShownList.GetCount())\r
1118                 pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(pItem->iItem));\r
1119 \r
1120         CString temp;\r
1121         if(m_IsOldFirst)\r
1122         {\r
1123                 temp.Format(_T("%d"),pItem->iItem+1);\r
1124 \r
1125         }else\r
1126         {\r
1127                 temp.Format(_T("%d"),m_arShownList.GetCount()-pItem->iItem);\r
1128         }\r
1129             \r
1130         // Which column?\r
1131         switch (pItem->iSubItem)\r
1132         {\r
1133         case this->LOGLIST_GRAPH:       //Graphic\r
1134                 if (pLogEntry)\r
1135                 {\r
1136                         if(this->m_IsRebaseReplaceGraph)\r
1137                         {\r
1138                                 CTGitPath path;\r
1139                                 path.m_Action=pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_MODE_MASK;\r
1140 \r
1141                                 lstrcpyn(pItem->pszText,path.GetActionName(), pItem->cchTextMax);\r
1142                         }\r
1143                 }\r
1144                 break;\r
1145         case this->LOGLIST_ACTION: //action -- no text in the column\r
1146                 if(this->m_IsIDReplaceAction)\r
1147                         lstrcpyn(pItem->pszText, temp, pItem->cchTextMax);\r
1148                 break;\r
1149         case this->LOGLIST_MESSAGE: //Message\r
1150                 if (pLogEntry)\r
1151                         lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_Subject, pItem->cchTextMax);\r
1152                 break;\r
1153         case this->LOGLIST_AUTHOR: //Author\r
1154                 if (pLogEntry)\r
1155                         lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_AuthorName, pItem->cchTextMax);\r
1156                 break;\r
1157         case this->LOGLIST_DATE: //Date\r
1158                 if (pLogEntry)\r
1159                         lstrcpyn(pItem->pszText,\r
1160                                 CAppUtils::FormatDateAndTime( pLogEntry->m_AuthorDate, m_DateFormat, true, m_bRelativeTimes ), \r
1161                                 pItem->cchTextMax);\r
1162                 break;\r
1163                 \r
1164         case 5:\r
1165 \r
1166                 break;\r
1167         default:\r
1168                 ASSERT(false);\r
1169         }\r
1170 }\r
1171 \r
1172 void CGitLogListBase::OnContextMenu(CWnd* pWnd, CPoint point)\r
1173 {\r
1174 \r
1175         int selIndex = GetSelectionMark();\r
1176         if (selIndex < 0)\r
1177                 return; // nothing selected, nothing to do with a context menu\r
1178 \r
1179         // if the user selected the info text telling about not all revisions shown due to\r
1180         // the "stop on copy/rename" option, we also don't show the context menu\r
1181         if ((m_bStrictStopped)&&(selIndex == m_arShownList.GetCount()))\r
1182                 return;\r
1183 \r
1184         // if the context menu is invoked through the keyboard, we have to use\r
1185         // a calculated position on where to anchor the menu on\r
1186         if ((point.x == -1) && (point.y == -1))\r
1187         {\r
1188                 CRect rect;\r
1189                 GetItemRect(selIndex, &rect, LVIR_LABEL);\r
1190                 ClientToScreen(&rect);\r
1191                 point = rect.CenterPoint();\r
1192         }\r
1193         m_nSearchIndex = selIndex;\r
1194         m_bCancelled = FALSE;\r
1195 \r
1196         // calculate some information the context menu commands can use\r
1197 //      CString pathURL = GetURLFromPath(m_path);\r
1198 \r
1199         POSITION pos = GetFirstSelectedItemPosition();\r
1200         int indexNext = GetNextSelectedItem(pos);\r
1201         if (indexNext < 0)\r
1202                 return;\r
1203 \r
1204         GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));\r
1205 #if 0\r
1206         GitRev revSelected = pSelLogEntry->Rev;\r
1207         GitRev revPrevious = git_revnum_t(revSelected)-1;\r
1208         if ((pSelLogEntry->pArChangedPaths)&&(pSelLogEntry->pArChangedPaths->GetCount() <= 2))\r
1209         {\r
1210                 for (int i=0; i<pSelLogEntry->pArChangedPaths->GetCount(); ++i)\r
1211                 {\r
1212                         LogChangedPath * changedpath = (LogChangedPath *)pSelLogEntry->pArChangedPaths->GetAt(i);\r
1213                         if (changedpath->lCopyFromRev)\r
1214                                 revPrevious = changedpath->lCopyFromRev;\r
1215                 }\r
1216         }\r
1217         GitRev revSelected2;\r
1218         if (pos)\r
1219         {\r
1220                 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));\r
1221                 revSelected2 = pLogEntry->Rev;\r
1222         }\r
1223         bool bAllFromTheSameAuthor = true;\r
1224         CString firstAuthor;\r
1225         CLogDataVector selEntries;\r
1226         GitRev revLowest, revHighest;\r
1227         GitRevRangeArray revisionRanges;\r
1228         {\r
1229                 POSITION pos = GetFirstSelectedItemPosition();\r
1230                 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));\r
1231                 revisionRanges.AddRevision(pLogEntry->Rev);\r
1232                 selEntries.push_back(pLogEntry);\r
1233                 firstAuthor = pLogEntry->sAuthor;\r
1234                 revLowest = pLogEntry->Rev;\r
1235                 revHighest = pLogEntry->Rev;\r
1236                 while (pos)\r
1237                 {\r
1238                         pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));\r
1239                         revisionRanges.AddRevision(pLogEntry->Rev);\r
1240                         selEntries.push_back(pLogEntry);\r
1241                         if (firstAuthor.Compare(pLogEntry->sAuthor))\r
1242                                 bAllFromTheSameAuthor = false;\r
1243                         revLowest = (git_revnum_t(pLogEntry->Rev) > git_revnum_t(revLowest) ? revLowest : pLogEntry->Rev);\r
1244                         revHighest = (git_revnum_t(pLogEntry->Rev) < git_revnum_t(revHighest) ? revHighest : pLogEntry->Rev);\r
1245                 }\r
1246         }\r
1247 \r
1248 #endif\r
1249 \r
1250         int FirstSelect=-1, LastSelect=-1;\r
1251         pos = GetFirstSelectedItemPosition();\r
1252         FirstSelect = GetNextSelectedItem(pos);\r
1253         while(pos)\r
1254         {\r
1255                 LastSelect = GetNextSelectedItem(pos);\r
1256         }\r
1257         //entry is selected, now show the popup menu\r
1258         CIconMenu popup;\r
1259         CIconMenu submenu;\r
1260         if (popup.CreatePopupMenu())\r
1261         {\r
1262 \r
1263                 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_PICK))\r
1264                         popup.AppendMenuIcon(ID_REBASE_PICK,  IDS_REBASE_PICK,   IDI_PICK);\r
1265 \r
1266                 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_SQUASH))\r
1267                         popup.AppendMenuIcon(ID_REBASE_SQUASH,IDS_REBASE_SQUASH, IDI_SQUASH);\r
1268 \r
1269                 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_EDIT))\r
1270                         popup.AppendMenuIcon(ID_REBASE_EDIT,  IDS_REBASE_EDIT,   IDI_EDIT);\r
1271 \r
1272                 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_SKIP))\r
1273                         popup.AppendMenuIcon(ID_REBASE_SKIP,  IDS_REBASE_SKIP,   IDI_SKIP);\r
1274                 \r
1275                 if(m_ContextMenuMask&(GetContextMenuBit(ID_REBASE_SKIP)|GetContextMenuBit(ID_REBASE_EDIT)|\r
1276                               GetContextMenuBit(ID_REBASE_SQUASH)|GetContextMenuBit(ID_REBASE_PICK)))\r
1277                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
1278 \r
1279                 if (GetSelectedCount() == 1)\r
1280                 {\r
1281                         {\r
1282                                 //if (m_hasWC)\r
1283                                 {\r
1284                                         if(m_ContextMenuMask&GetContextMenuBit(ID_COMPARE))\r
1285                                                 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);\r
1286                                         // TODO:\r
1287                                         // TortoiseMerge could be improved to take a /blame switch\r
1288                                         // and then not 'cat' the files from a unified diff but\r
1289                                         // blame then.\r
1290                                         // But until that's implemented, the context menu entry for\r
1291                                         // this feature is commented out.\r
1292                                         //popup.AppendMenu(ID_BLAMECOMPARE, IDS_LOG_POPUP_BLAMECOMPARE, IDI_BLAME);\r
1293                                 }\r
1294                                 if(m_ContextMenuMask&GetContextMenuBit(ID_GNUDIFF1))\r
1295                                         popup.AppendMenuIcon(ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);\r
1296 \r
1297                                 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPAREWITHPREVIOUS))\r
1298                                         popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF);\r
1299                                 //popup.AppendMenuIcon(ID_BLAMEWITHPREVIOUS, IDS_LOG_POPUP_BLAMEWITHPREVIOUS, IDI_BLAME);\r
1300                                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
1301                         }\r
1302 \r
1303 //                      if (!m_ProjectProperties.sWebViewerRev.IsEmpty())\r
1304 //                      {\r
1305 //                              popup.AppendMenuIcon(ID_VIEWREV, IDS_LOG_POPUP_VIEWREV);\r
1306 //                      }\r
1307 //                      if (!m_ProjectProperties.sWebViewerPathRev.IsEmpty())\r
1308 //                      {\r
1309 //                              popup.AppendMenuIcon(ID_VIEWPATHREV, IDS_LOG_POPUP_VIEWPATHREV);\r
1310 //                      }\r
1311 //                      if ((!m_ProjectProperties.sWebViewerPathRev.IsEmpty())||\r
1312 //                              (!m_ProjectProperties.sWebViewerRev.IsEmpty()))\r
1313 //                      {\r
1314 //                              popup.AppendMenu(MF_SEPARATOR, NULL);\r
1315 //                      }\r
1316 \r
1317                         //if (m_hasWC)\r
1318                         //      popup.AppendMenuIcon(ID_REVERTTOREV, IDS_LOG_POPUP_REVERTTOREV, IDI_REVERT);\r
1319                         //if (m_hasWC)\r
1320                         //      popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREV, IDI_REVERT);\r
1321                         //if (m_hasWC)\r
1322                         //      popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREV, IDI_MERGE);\r
1323                         \r
1324                         CString str,format;\r
1325                         format.LoadString(IDS_RESET_TO_THIS_FORMAT);\r
1326                         str.Format(format,g_Git.GetCurrentBranch());\r
1327 \r
1328                         if(m_ContextMenuMask&GetContextMenuBit(ID_RESET))\r
1329                                 popup.AppendMenuIcon(ID_RESET,str,IDI_REVERT);\r
1330 \r
1331                         if(m_ContextMenuMask&GetContextMenuBit(ID_SWITCHTOREV))\r
1332                                 popup.AppendMenuIcon(ID_SWITCHTOREV, IDS_SWITCH_TO_THIS , IDI_SWITCH);\r
1333 \r
1334                         if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_BRANCH))\r
1335                                 popup.AppendMenuIcon(ID_CREATE_BRANCH, IDS_CREATE_BRANCH_AT_THIS , IDI_COPY);\r
1336 \r
1337                         if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_TAG))\r
1338                                 popup.AppendMenuIcon(ID_CREATE_TAG,IDS_CREATE_TAG_AT_THIS , IDI_COPY);\r
1339                         \r
1340                         format.LoadString(IDS_REBASE_THIS_FORMAT);\r
1341                         str.Format(format,g_Git.GetCurrentBranch());\r
1342 \r
1343                         if(pSelLogEntry->m_CommitHash != m_HeadHash)\r
1344                                 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_TO_VERSION))\r
1345                                         popup.AppendMenuIcon(ID_REBASE_TO_VERSION, str , IDI_REBASE);                   \r
1346 \r
1347                         if(m_ContextMenuMask&GetContextMenuBit(ID_EXPORT))\r
1348                                 popup.AppendMenuIcon(ID_EXPORT,IDS_EXPORT_TO_THIS, IDI_EXPORT); \r
1349                         \r
1350 \r
1351                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
1352 \r
1353                 }\r
1354 \r
1355                 if(!pSelLogEntry->m_Ref.IsEmpty() && GetSelectedCount() == 1)\r
1356                 {\r
1357                         popup.AppendMenuIcon(ID_REFLOG_DEL, IDS_REFLOG_DEL,     IDI_DELETE);    \r
1358                         popup.AppendMenuIcon(ID_STASH_APPLY,IDS_MENUSTASHAPPLY, IDI_RELOCATE);  \r
1359                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
1360                 }\r
1361         \r
1362                 if (GetSelectedCount() >= 2)\r
1363                 {\r
1364                         bool bAddSeparator = false;\r
1365                         if (IsSelectionContinuous() || (GetSelectedCount() == 2))\r
1366                         {\r
1367                                 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPARETWO))\r
1368                                         popup.AppendMenuIcon(ID_COMPARETWO, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);\r
1369                         }\r
1370 \r
1371                         if (GetSelectedCount() == 2)\r
1372                         {\r
1373                                 //popup.AppendMenuIcon(ID_BLAMETWO, IDS_LOG_POPUP_BLAMEREVS, IDI_BLAME);\r
1374                                 if(m_ContextMenuMask&GetContextMenuBit(ID_GNUDIFF2))\r
1375                                         popup.AppendMenuIcon(ID_GNUDIFF2, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);\r
1376                                 bAddSeparator = true;\r
1377                         }\r
1378 \r
1379                         if (m_hasWC)\r
1380                         {\r
1381                                 //popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREVS, IDI_REVERT);\r
1382 //                              if (m_hasWC)\r
1383 //                                      popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREVS, IDI_MERGE);\r
1384                                 bAddSeparator = true;\r
1385                         }\r
1386                         if (bAddSeparator)\r
1387                                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
1388                 }\r
1389 \r
1390                 if ( GetSelectedCount() >0 )\r
1391                 {\r
1392                         if ( IsSelectionContinuous() && GetSelectedCount() >= 2 )\r
1393                         {\r
1394                                 if(m_ContextMenuMask&GetContextMenuBit(ID_COMBINE_COMMIT))\r
1395                                 {\r
1396                                         CString head;\r
1397                                         int headindex;\r
1398                                         headindex = this->GetHeadIndex();\r
1399                                         if(headindex>=0)\r
1400                                         {\r
1401                                                 head.Format(_T("HEAD~%d"),LastSelect-headindex);\r
1402                                                 CString hash=g_Git.GetHash(head);\r
1403                                                 hash=hash.Left(40);\r
1404                                                 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));\r
1405                                                 if(pLastEntry->m_CommitHash == hash)\r
1406                                                         popup.AppendMenuIcon(ID_COMBINE_COMMIT,IDS_COMBINE_TO_ONE,IDI_COMBINE);\r
1407                                         }\r
1408                                 }\r
1409                         }\r
1410                         if(m_ContextMenuMask&GetContextMenuBit(ID_CHERRY_PICK))\r
1411                                 popup.AppendMenuIcon(ID_CHERRY_PICK, IDS_CHERRY_PICK_VERSION, IDI_EXPORT);\r
1412 \r
1413                         if(GetSelectedCount()<=2 || (IsSelectionContinuous() && GetSelectedCount() > 0))\r
1414                                 if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_PATCH))\r
1415                                         popup.AppendMenuIcon(ID_CREATE_PATCH, IDS_CREATE_PATCH, IDI_PATCH);\r
1416                         \r
1417                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
1418         \r
1419                 }\r
1420 \r
1421                 \r
1422 #if 0\r
1423 //              if ((selEntries.size() > 0)&&(bAllFromTheSameAuthor))\r
1424 //              {\r
1425 //                      popup.AppendMenuIcon(ID_EDITAUTHOR, IDS_LOG_POPUP_EDITAUTHOR);\r
1426 //              }\r
1427 //              if (GetSelectedCount() == 1)\r
1428 //              {\r
1429 //                      popup.AppendMenuIcon(ID_EDITLOG, IDS_LOG_POPUP_EDITLOG);\r
1430 //                      popup.AppendMenuIcon(ID_REVPROPS, IDS_REPOBROWSE_SHOWREVPROP, IDI_PROPERTIES); // "Show Revision Properties"\r
1431 //                      popup.AppendMenu(MF_SEPARATOR, NULL);\r
1432 //              }\r
1433 #endif\r
1434 \r
1435                 \r
1436                 if (GetSelectedCount() == 1)\r
1437                 {\r
1438                         if(m_ContextMenuMask&GetContextMenuBit(ID_COPYHASH))\r
1439                                 popup.AppendMenuIcon(ID_COPYHASH, IDS_COPY_COMMIT_HASH);\r
1440                 }\r
1441                 if (GetSelectedCount() != 0)\r
1442                 {\r
1443                         if(m_ContextMenuMask&GetContextMenuBit(ID_COPYCLIPBOARD))\r
1444                                 popup.AppendMenuIcon(ID_COPYCLIPBOARD, IDS_LOG_POPUP_COPYTOCLIPBOARD);\r
1445                 }\r
1446 \r
1447                 if(m_ContextMenuMask&GetContextMenuBit(ID_FINDENTRY))\r
1448                         popup.AppendMenuIcon(ID_FINDENTRY, IDS_LOG_POPUP_FIND);\r
1449 \r
1450 \r
1451                 if (GetSelectedCount() == 1)\r
1452                 {\r
1453                         if(m_ContextMenuMask &GetContextMenuBit(ID_DELETE))\r
1454                         {\r
1455                                 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) != m_HashMap.end() )\r
1456                                 {\r
1457                                         CString str;\r
1458                                         str.LoadString(IDS_DELETE_BRANCHTAG);\r
1459                                         if( m_HashMap[pSelLogEntry->m_CommitHash].size() == 1 )\r
1460                                         {\r
1461                                                 str+=_T(" ");\r
1462                                                 str+=m_HashMap[pSelLogEntry->m_CommitHash].at(0);\r
1463                                                 popup.AppendMenuIcon(ID_DELETE,str+_T("..."),IDI_DELETE);\r
1464                                         }\r
1465                                         else if( m_HashMap[pSelLogEntry->m_CommitHash].size() > 1 )\r
1466                                         {\r
1467                                                 \r
1468                                                 submenu.CreatePopupMenu();\r
1469                                                 for(int i=0;i<m_HashMap[pSelLogEntry->m_CommitHash].size();i++)\r
1470                                                 {\r
1471                                                         submenu.AppendMenuIcon(ID_DELETE+(i<<16),m_HashMap[pSelLogEntry->m_CommitHash][i]+_T("..."));\r
1472                                                 }\r
1473 \r
1474                                                 popup.AppendMenu(MF_BYPOSITION|MF_POPUP|MF_STRING,(UINT) submenu.m_hMenu,str); \r
1475 \r
1476                                         }\r
1477                                         \r
1478                                 }\r
1479                         }\r
1480                 }\r
1481 \r
1482                 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
1483 //              DialogEnableWindow(IDOK, FALSE);\r
1484 //              SetPromptApp(&theApp);\r
1485         \r
1486                 this->ContextMenuAction(cmd, FirstSelect, LastSelect);\r
1487                 \r
1488 //              EnableOKButton();\r
1489         } // if (popup.CreatePopupMenu())\r
1490 \r
1491 }\r
1492 \r
1493 bool CGitLogListBase::IsSelectionContinuous()\r
1494 {\r
1495         if ( GetSelectedCount()==1 )\r
1496         {\r
1497                 // if only one revision is selected, the selection is of course\r
1498                 // continuous\r
1499                 return true;\r
1500         }\r
1501 \r
1502         POSITION pos = GetFirstSelectedItemPosition();\r
1503         bool bContinuous = (m_arShownList.GetCount() == (INT_PTR)m_logEntries.size());\r
1504         if (bContinuous)\r
1505         {\r
1506                 int itemindex = GetNextSelectedItem(pos);\r
1507                 while (pos)\r
1508                 {\r
1509                         int nextindex = GetNextSelectedItem(pos);\r
1510                         if (nextindex - itemindex > 1)\r
1511                         {\r
1512                                 bContinuous = false;\r
1513                                 break;\r
1514                         }\r
1515                         itemindex = nextindex;\r
1516                 }\r
1517         }\r
1518         return bContinuous;\r
1519 }\r
1520 \r
1521 void CGitLogListBase::CopySelectionToClipBoard(bool HashOnly)\r
1522 {\r
1523 \r
1524         CString sClipdata;\r
1525         POSITION pos = GetFirstSelectedItemPosition();\r
1526         if (pos != NULL)\r
1527         {\r
1528                 CString sRev;\r
1529                 sRev.LoadString(IDS_LOG_REVISION);\r
1530                 CString sAuthor;\r
1531                 sAuthor.LoadString(IDS_LOG_AUTHOR);\r
1532                 CString sDate;\r
1533                 sDate.LoadString(IDS_LOG_DATE);\r
1534                 CString sMessage;\r
1535                 sMessage.LoadString(IDS_LOG_MESSAGE);\r
1536                 while (pos)\r
1537                 {\r
1538                         CString sLogCopyText;\r
1539                         CString sPaths;\r
1540                         GitRev * pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.GetAt(GetNextSelectedItem(pos)));\r
1541 \r
1542                         if(!HashOnly)\r
1543                         {\r
1544                                 //pLogEntry->m_Files\r
1545                                 //LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;\r
1546                         \r
1547                                 for (int cpPathIndex = 0; cpPathIndex<pLogEntry->m_Files.GetCount(); ++cpPathIndex)\r
1548                                 {\r
1549                                         sPaths += ((CTGitPath&)pLogEntry->m_Files[cpPathIndex]).GetActionName() + _T(" : ") + pLogEntry->m_Files[cpPathIndex].GetGitPathString();\r
1550                                         sPaths += _T("\r\n");\r
1551                                 }\r
1552                                 sPaths.Trim();\r
1553                                 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"),\r
1554                                         (LPCTSTR)sRev, pLogEntry->m_CommitHash,\r
1555                                         (LPCTSTR)sAuthor, (LPCTSTR)pLogEntry->m_AuthorName,\r
1556                                         (LPCTSTR)sDate, \r
1557                                         (LPCTSTR)CAppUtils::FormatDateAndTime( pLogEntry->m_AuthorDate, m_DateFormat, true, m_bRelativeTimes ),\r
1558                                         (LPCTSTR)sMessage, pLogEntry->m_Subject+_T("\r\n")+pLogEntry->m_Body,\r
1559                                         (LPCTSTR)sPaths);\r
1560                                 sClipdata +=  sLogCopyText;\r
1561                         }else\r
1562                         {\r
1563                                 sClipdata += pLogEntry->m_CommitHash;\r
1564                                 break;\r
1565                         }\r
1566 \r
1567                 }\r
1568                 CStringUtils::WriteAsciiStringToClipboard(sClipdata, GetSafeHwnd());\r
1569         }\r
1570 \r
1571 }\r
1572 \r
1573 void CGitLogListBase::DiffSelectedRevWithPrevious()\r
1574 {\r
1575         if (m_bThreadRunning)\r
1576                 return;\r
1577 \r
1578         int FirstSelect=-1, LastSelect=-1;\r
1579         POSITION pos = GetFirstSelectedItemPosition();\r
1580         FirstSelect = GetNextSelectedItem(pos);\r
1581         while(pos)\r
1582         {\r
1583                 LastSelect = GetNextSelectedItem(pos);\r
1584         }\r
1585 \r
1586         ContextMenuAction(ID_COMPAREWITHPREVIOUS,FirstSelect,LastSelect);\r
1587 \r
1588 #if 0\r
1589         UpdateLogInfoLabel();\r
1590         int selIndex = m_LogList.GetSelectionMark();\r
1591         if (selIndex < 0)\r
1592                 return;\r
1593         int selCount = m_LogList.GetSelectedCount();\r
1594         if (selCount != 1)\r
1595                 return;\r
1596 \r
1597         // Find selected entry in the log list\r
1598         POSITION pos = m_LogList.GetFirstSelectedItemPosition();\r
1599         PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));\r
1600         long rev1 = pLogEntry->Rev;\r
1601         long rev2 = rev1-1;\r
1602         CTGitPath path = m_path;\r
1603 \r
1604         // See how many files under the relative root were changed in selected revision\r
1605         int nChanged = 0;\r
1606         LogChangedPath * changed = NULL;\r
1607         for (INT_PTR c = 0; c < pLogEntry->pArChangedPaths->GetCount(); ++c)\r
1608         {\r
1609                 LogChangedPath * cpath = pLogEntry->pArChangedPaths->GetAt(c);\r
1610                 if (cpath  &&  cpath -> sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)\r
1611                 {\r
1612                         ++nChanged;\r
1613                         changed = cpath;\r
1614                 }\r
1615         }\r
1616 \r
1617         if (m_path.IsDirectory() && nChanged == 1) \r
1618         {\r
1619                 // We're looking at the log for a directory and only one file under dir was changed in the revision\r
1620                 // Do diff on that file instead of whole directory\r
1621                 path.AppendPathString(changed->sPath.Mid(m_sRelativeRoot.GetLength()));\r
1622         } \r
1623 \r
1624         m_bCancelled = FALSE;\r
1625         DialogEnableWindow(IDOK, FALSE);\r
1626         SetPromptApp(&theApp);\r
1627         theApp.DoWaitCursor(1);\r
1628 \r
1629         if (PromptShown())\r
1630         {\r
1631                 GitDiff diff(this, m_hWnd, true);\r
1632                 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
1633                 diff.SetHEADPeg(m_LogRevision);\r
1634                 diff.ShowCompare(path, rev2, path, rev1);\r
1635         }\r
1636         else\r
1637         {\r
1638                 CAppUtils::StartShowCompare(m_hWnd, path, rev2, path, rev1, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
1639         }\r
1640 \r
1641         theApp.DoWaitCursor(-1);\r
1642         EnableOKButton();\r
1643 #endif\r
1644 }\r
1645 \r
1646 void CGitLogListBase::OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult)\r
1647 {\r
1648         LPNMLVFINDITEM pFindInfo = reinterpret_cast<LPNMLVFINDITEM>(pNMHDR);\r
1649         *pResult = -1;\r
1650         \r
1651         if (pFindInfo->lvfi.flags & LVFI_PARAM)\r
1652                 return; \r
1653         if ((pFindInfo->iStart < 0)||(pFindInfo->iStart >= m_arShownList.GetCount()))\r
1654                 return;\r
1655         if (pFindInfo->lvfi.psz == 0)\r
1656                 return;\r
1657 #if 0\r
1658         CString sCmp = pFindInfo->lvfi.psz;\r
1659         CString sRev;   \r
1660         for (int i=pFindInfo->iStart; i<m_arShownList.GetCount(); ++i)\r
1661         {\r
1662                 GitRev * pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));\r
1663                 sRev.Format(_T("%ld"), pLogEntry->Rev);\r
1664                 if (pFindInfo->lvfi.flags & LVFI_PARTIAL)\r
1665                 {\r
1666                         if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)\r
1667                         {\r
1668                                 *pResult = i;\r
1669                                 return;\r
1670                         }\r
1671                 }\r
1672                 else\r
1673                 {\r
1674                         if (sCmp.Compare(sRev)==0)\r
1675                         {\r
1676                                 *pResult = i;\r
1677                                 return;\r
1678                         }\r
1679                 }\r
1680         }\r
1681         if (pFindInfo->lvfi.flags & LVFI_WRAP)\r
1682         {\r
1683                 for (int i=0; i<pFindInfo->iStart; ++i)\r
1684                 {\r
1685                         PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(i));\r
1686                         sRev.Format(_T("%ld"), pLogEntry->Rev);\r
1687                         if (pFindInfo->lvfi.flags & LVFI_PARTIAL)\r
1688                         {\r
1689                                 if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)\r
1690                                 {\r
1691                                         *pResult = i;\r
1692                                         return;\r
1693                                 }\r
1694                         }\r
1695                         else\r
1696                         {\r
1697                                 if (sCmp.Compare(sRev)==0)\r
1698                                 {\r
1699                                         *pResult = i;\r
1700                                         return;\r
1701                                 }\r
1702                         }\r
1703                 }\r
1704         }\r
1705 #endif\r
1706         *pResult = -1;\r
1707 }\r
1708 \r
1709 int CGitLogListBase::FillGitLog(CTGitPath *path,int info,CString *from,CString *to)\r
1710 {\r
1711         ClearText();\r
1712 \r
1713         this->m_logEntries.ClearAll();\r
1714         this->m_logEntries.ParserFromLog(path,-1,info,from,to);\r
1715 \r
1716         //this->m_logEntries.ParserFromLog();\r
1717         SetItemCountEx(this->m_logEntries.size());\r
1718 \r
1719         this->m_arShownList.RemoveAll();\r
1720 \r
1721         for(unsigned int i=0;i<m_logEntries.size();i++)\r
1722         {\r
1723                 if(m_IsOldFirst)\r
1724                 {\r
1725                         m_logEntries[m_logEntries.size()-i-1].m_IsFull=TRUE;\r
1726                         this->m_arShownList.Add(&m_logEntries[m_logEntries.size()-i-1]);\r
1727                 \r
1728                 }else\r
1729                 {\r
1730                         m_logEntries[i].m_IsFull=TRUE;\r
1731                         this->m_arShownList.Add(&m_logEntries[i]);\r
1732                 }\r
1733         }\r
1734 \r
1735     if(path)\r
1736         m_Path=*path;\r
1737         return 0;\r
1738 \r
1739 }\r
1740 \r
1741 int CGitLogListBase::FillGitShortLog()\r
1742 {\r
1743         ClearText();\r
1744 \r
1745         this->m_logEntries.ClearAll();\r
1746 \r
1747         m_LogCache.FetchCacheIndex(g_Git.m_CurrentDir);\r
1748 \r
1749     CTGitPath *path;\r
1750     if(this->m_Path.IsEmpty())\r
1751         path=NULL;\r
1752     else\r
1753         path=&this->m_Path;\r
1754 \r
1755         CString hash;\r
1756         int mask;\r
1757         mask = CGit::LOG_INFO_ONLY_HASH | CGit::LOG_INFO_BOUNDARY;\r
1758 //      if(this->m_bAllBranch)\r
1759         mask |= m_ShowMask;\r
1760         \r
1761         if(m_bShowWC)\r
1762                 this->m_logEntries.insert(m_logEntries.begin(),this->m_wcRev);\r
1763 \r
1764         this->m_logEntries.FetchShortLog(path,m_StartRef,-1,mask);\r
1765 \r
1766         //this->m_logEntries.ParserFromLog();\r
1767         if(IsInWorkingThread())\r
1768                 PostMessage(LVM_SETITEMCOUNT, (WPARAM) this->m_logEntries.size(),(LPARAM) LVSICF_NOINVALIDATEALL);\r
1769         else\r
1770                 SetItemCountEx(this->m_logEntries.size());\r
1771 \r
1772         this->m_arShownList.RemoveAll();\r
1773 \r
1774         for(unsigned int i=0;i<m_logEntries.size();i++)\r
1775         {\r
1776                 if(i>0 || m_logEntries[i].m_CommitHash != GIT_REV_ZERO)\r
1777                         m_logEntries[i].m_Subject=_T("parser...");\r
1778 \r
1779                 if(this->m_IsOldFirst)\r
1780                 {\r
1781                         this->m_arShownList.Add(&m_logEntries[m_logEntries.size()-1-i]);\r
1782 \r
1783                 }else\r
1784                 {\r
1785                         this->m_arShownList.Add(&m_logEntries[i]);\r
1786                 }\r
1787         }\r
1788         return 0;\r
1789 }\r
1790 \r
1791 BOOL CGitLogListBase::PreTranslateMessage(MSG* pMsg)\r
1792 {\r
1793         // Skip Ctrl-C when copying text out of the log message or search filter\r
1794         BOOL bSkipAccelerator = ( pMsg->message == WM_KEYDOWN && pMsg->wParam=='C' && (GetFocus()==GetDlgItem(IDC_MSGVIEW) || GetFocus()==GetDlgItem(IDC_SEARCHEDIT) ) && GetKeyState(VK_CONTROL)&0x8000 );\r
1795         if (pMsg->message == WM_KEYDOWN && pMsg->wParam=='\r')\r
1796         {\r
1797                 //if (GetFocus()==GetDlgItem(IDC_LOGLIST))\r
1798                 {\r
1799                         if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))\r
1800                         {\r
1801                                 DiffSelectedRevWithPrevious();\r
1802                                 return TRUE;\r
1803                         }\r
1804                 }\r
1805 #if 0\r
1806                 if (GetFocus()==GetDlgItem(IDC_LOGMSG))\r
1807                 {\r
1808                         DiffSelectedFile();\r
1809                         return TRUE;\r
1810                 }\r
1811 #endif\r
1812         }\r
1813 \r
1814 #if 0\r
1815         if (m_hAccel && !bSkipAccelerator)\r
1816         {\r
1817                 int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);\r
1818                 if (ret)\r
1819                         return TRUE;\r
1820         }\r
1821         \r
1822 #endif\r
1823         //m_tooltips.RelayEvent(pMsg);\r
1824         return __super::PreTranslateMessage(pMsg);\r
1825 }\r
1826 \r
1827 void CGitLogListBase::OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult)\r
1828 {\r
1829         // a double click on an entry in the revision list has happened\r
1830         *pResult = 0;\r
1831 \r
1832         if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))\r
1833                 DiffSelectedRevWithPrevious();\r
1834 }\r
1835 \r
1836 int CGitLogListBase::FetchLogAsync(void * data)\r
1837 {\r
1838         m_ProcData=data;\r
1839         m_bExitThread=FALSE;\r
1840         InterlockedExchange(&m_bThreadRunning, TRUE);\r
1841         InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
1842         m_LoadingThread = AfxBeginThread(LogThreadEntry, this);\r
1843         if (m_LoadingThread ==NULL)\r
1844         {\r
1845                 InterlockedExchange(&m_bThreadRunning, FALSE);\r
1846                 InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
1847                 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
1848                 return -1;\r
1849         }\r
1850         return 0;\r
1851 }\r
1852 \r
1853 //this is the thread function which calls the subversion function\r
1854 UINT CGitLogListBase::LogThreadEntry(LPVOID pVoid)\r
1855 {\r
1856         return ((CGitLogListBase*)pVoid)->LogThread();\r
1857 }\r
1858 \r
1859 void CGitLogListBase::GetTimeRange(CTime &oldest, CTime &latest)\r
1860 {\r
1861         //CTime time;\r
1862         oldest=CTime::GetCurrentTime();\r
1863         latest=CTime(1971,1,2,0,0,0);\r
1864         for(unsigned int i=0;i<m_logEntries.size();i++)\r
1865         {\r
1866                 if(m_logEntries[i].m_AuthorDate.GetTime() < oldest.GetTime())\r
1867                         oldest = m_logEntries[i].m_AuthorDate.GetTime();\r
1868 \r
1869                 if(m_logEntries[i].m_AuthorDate.GetTime() > latest.GetTime())\r
1870                         latest = m_logEntries[i].m_AuthorDate.GetTime();\r
1871 \r
1872         }\r
1873 }\r
1874 \r
1875 //Helper class for FetchFullLogInfo()\r
1876 class CGitCall_FetchFullLogInfo : public CGitCall\r
1877 {\r
1878 public:\r
1879         CGitCall_FetchFullLogInfo(CGitLogListBase* ploglist):m_ploglist(ploglist),m_CollectedCount(0){}\r
1880         virtual bool OnOutputData(const BYTE* data, size_t size)\r
1881         {\r
1882                 if(size==0)\r
1883                         return m_ploglist->m_bExitThread;\r
1884                 //Add received data to byte collector\r
1885                 m_ByteCollector.append(data,size);\r
1886 \r
1887                 //Find loginfo endmarker\r
1888                 static const BYTE dataToFind[]={0,0,'#','<'};\r
1889                 int found=m_ByteCollector.findData(dataToFind,4);\r
1890                 if(found<0)\r
1891                         return m_ploglist->m_bExitThread;//Not found\r
1892                 found+=2;//Position after loginfo end-marker\r
1893 \r
1894                 //Prepare data for OnLogInfo and call it\r
1895                 BYTE_VECTOR logInfo;\r
1896                 logInfo.append(&*m_ByteCollector.begin(),found);\r
1897                 OnLogInfo(logInfo);\r
1898 \r
1899                 //Remove loginfo from bytecollector\r
1900                 m_ByteCollector.erase(m_ByteCollector.begin(),m_ByteCollector.begin()+found);\r
1901 \r
1902                 return m_ploglist->m_bExitThread;\r
1903         }\r
1904         virtual void OnEnd()\r
1905         {\r
1906                 //Rest should be a complete log line.\r
1907                 if(!m_ByteCollector.empty())\r
1908                         OnLogInfo(m_ByteCollector);\r
1909         }\r
1910 \r
1911 \r
1912         void OnLogInfo(BYTE_VECTOR& logInfo)\r
1913         {\r
1914                 GitRev fullRev;\r
1915                 fullRev.ParserFromLog(logInfo);\r
1916                 MAP_HASH_REV::iterator itRev=m_ploglist->m_logEntries.m_HashMap.find(fullRev.m_CommitHash);\r
1917                 if(itRev==m_ploglist->m_logEntries.m_HashMap.end())\r
1918                 {\r
1919                         //Should not occur, only when Git-tree updated in the mean time. (Race condition)\r
1920                         return;//Ignore\r
1921                 }\r
1922                 //Set updating\r
1923                 int rev=itRev->second;\r
1924                 GitRev* revInVector=&m_ploglist->m_logEntries[rev];\r
1925 \r
1926 \r
1927                 if(revInVector->m_IsFull)\r
1928                         return;\r
1929 \r
1930                 if(!m_ploglist->m_LogCache.GetCacheData(m_ploglist->m_logEntries[rev]))\r
1931                 {\r
1932                         ++m_CollectedCount;\r
1933                         InterlockedExchange(&m_ploglist->m_logEntries[rev].m_IsUpdateing,FALSE);\r
1934                         InterlockedExchange(&m_ploglist->m_logEntries[rev].m_IsFull,TRUE);\r
1935                         ::PostMessage(m_ploglist->m_hWnd,MSG_LOADED,(WPARAM)rev,0);\r
1936                         return;\r
1937                 }\r
1938 \r
1939 //              fullRev.m_IsUpdateing=TRUE;\r
1940 //              fullRev.m_IsFull=TRUE;\r
1941         \r
1942 \r
1943                 if(InterlockedExchange(&revInVector->m_IsUpdateing,TRUE))\r
1944                         return;//Cannot update this row now. Ignore.\r
1945                 TCHAR oldmark=revInVector->m_Mark;\r
1946                 GIT_REV_LIST oldlist=revInVector->m_ParentHash;\r
1947 //              CString oldhash=m_CommitHash;\r
1948 \r
1949                 //Parse new rev info\r
1950                 revInVector->ParserFromLog(logInfo);\r
1951 \r
1952                 if(oldmark!=0)\r
1953                         revInVector->m_Mark=oldmark;  //parser full log will cause old mark overwrited. \r
1954                                                                //So we need keep old bound mark.\r
1955                 revInVector->m_ParentHash=oldlist;\r
1956 \r
1957                 //update cache\r
1958                 m_ploglist->m_LogCache.AddCacheEntry(*revInVector);\r
1959 \r
1960                 //Reset updating\r
1961                 InterlockedExchange(&revInVector->m_IsFull,TRUE);\r
1962                 InterlockedExchange(&revInVector->m_IsUpdateing,FALSE);\r
1963 \r
1964                 //Notify listcontrol and update progress bar\r
1965                 ++m_CollectedCount;\r
1966 \r
1967                 ::PostMessage(m_ploglist->m_hWnd,MSG_LOADED,(WPARAM)rev,0);\r
1968 \r
1969                 DWORD percent=m_CollectedCount*68/m_ploglist->m_logEntries.size() + GITLOG_START+1+30;\r
1970                 if(percent == GITLOG_END)\r
1971                         percent = GITLOG_END -1;\r
1972                 \r
1973                 ::PostMessage(m_ploglist->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) percent,0);\r
1974         }\r
1975 \r
1976         CGitLogListBase*        m_ploglist;\r
1977         BYTE_VECTOR                     m_ByteCollector;\r
1978         int                                     m_CollectedCount;\r
1979 \r
1980 };\r
1981 \r
1982 void CGitLogListBase::FetchFullLogInfo(CString &from, CString &to)\r
1983 {\r
1984         CGitCall_FetchFullLogInfo fetcher(this);\r
1985         int mask=\r
1986                 CGit::LOG_INFO_FULL_DIFF|\r
1987                 CGit::LOG_INFO_STAT|\r
1988                 CGit::LOG_INFO_FILESTATE|\r
1989                 CGit::LOG_INFO_DETECT_COPYRENAME|\r
1990                 CGit::LOG_INFO_SHOW_MERGEDFILE |\r
1991                 m_ShowMask;\r
1992 \r
1993         CTGitPath *path;\r
1994     if(this->m_Path.IsEmpty())\r
1995         path=NULL;\r
1996     else\r
1997         path=&this->m_Path;\r
1998 \r
1999         g_Git.GetLog(&fetcher,CString(),path,-1,mask,&from,&to);\r
2000 }\r
2001 \r
2002 void CGitLogListBase::FetchLastLogInfo()\r
2003 {\r
2004         unsigned int updated=0;\r
2005         int percent=0;\r
2006         CRect rect;\r
2007         {\r
2008                 for(unsigned int i=0;i<m_logEntries.size();i++)\r
2009                 {\r
2010                         if(m_logEntries[i].m_IsFull)\r
2011                                 continue;\r
2012 \r
2013                         if(m_LogCache.GetCacheData(m_logEntries[i]))\r
2014                         {\r
2015                                 if(!m_logEntries.FetchFullInfo(i))\r
2016                                 {\r
2017                                         updated++;\r
2018                                 }\r
2019                                 m_LogCache.AddCacheEntry(m_logEntries[i]);\r
2020 \r
2021                         }else\r
2022                         {\r
2023                                 updated++;\r
2024                                 InterlockedExchange(&m_logEntries[i].m_IsUpdateing,FALSE);\r
2025                                 InterlockedExchange(&m_logEntries[i].m_IsFull,TRUE);\r
2026                         }\r
2027                         \r
2028                         ::PostMessage(m_hWnd,MSG_LOADED,(WPARAM)i,0);\r
2029 \r
2030                         if(m_bExitThread)\r
2031                         {\r
2032                                 InterlockedExchange(&m_bThreadRunning, FALSE);\r
2033                                 InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
2034                                 return;\r
2035                         }                       \r
2036                 }\r
2037         }\r
2038 }\r
2039 \r
2040 UINT CGitLogListBase::LogThread()\r
2041 {\r
2042 \r
2043 //      if(m_ProcCallBack)\r
2044 //              m_ProcCallBack(m_ProcData,GITLOG_START);\r
2045         ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_START,0);\r
2046 \r
2047         InterlockedExchange(&m_bThreadRunning, TRUE);\r
2048         InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
2049 \r
2050     //does the user force the cache to refresh (shift or control key down)?\r
2051     bool refresh =    (GetKeyState (VK_CONTROL) < 0) \r
2052                    || (GetKeyState (VK_SHIFT) < 0);\r
2053 \r
2054         //disable the "Get All" button while we're receiving\r
2055         //log messages.\r
2056 \r
2057         FillGitShortLog();\r
2058         \r
2059         if(this->m_bExitThread)\r
2060         {\r
2061                 InterlockedExchange(&m_bThreadRunning, FALSE);\r
2062                 InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
2063                 return 0;\r
2064         }\r
2065         InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
2066         ::PostMessage(GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_START_ALL, 0);\r
2067 \r
2068         int start=0; CString firstcommit,lastcommit;\r
2069         int update=0;\r
2070         for(int i=0;i<m_logEntries.size();i++)\r
2071         {\r
2072                 if( i==0 && m_logEntries[i].m_CommitHash == GIT_REV_ZERO)\r
2073                         continue;\r
2074 \r
2075                 start=this->m_logEntries[i].ParserFromLog(m_logEntries.m_RawlogData,start);\r
2076                 m_logEntries.m_HashMap[m_logEntries[i].m_CommitHash]=i;\r
2077 \r
2078                 if(m_LogCache.GetCacheData(m_logEntries[i]))\r
2079                 {\r
2080                         if(firstcommit.IsEmpty())\r
2081                                 firstcommit=m_logEntries[i].m_CommitHash;\r
2082                         lastcommit=m_logEntries[i].m_CommitHash;\r
2083 \r
2084                 }else\r
2085                 {\r
2086                         InterlockedExchange(&m_logEntries[i].m_IsUpdateing,FALSE);\r
2087                         InterlockedExchange(&m_logEntries[i].m_IsFull,TRUE);\r
2088                         update++;\r
2089                 }\r
2090                 if(start<0)\r
2091                         break;\r
2092                 if(start>=m_logEntries.m_RawlogData.size())\r
2093                         break;\r
2094 \r
2095                 int percent=i*30/m_logEntries.size() + GITLOG_START+1;\r
2096 \r
2097                 ::PostMessage(GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) percent, 0);\r
2098                 ::PostMessage(m_hWnd,MSG_LOADED,(WPARAM) i, 0);\r
2099 \r
2100                 if(this->m_bExitThread)\r
2101                 {       \r
2102                         InterlockedExchange(&m_bThreadRunning, FALSE);\r
2103                         InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
2104                         return 0;\r
2105                 }\r
2106         }\r
2107         if(!lastcommit.IsEmpty())\r
2108                 FetchFullLogInfo(lastcommit,firstcommit);\r
2109         \r
2110         this->FetchLastLogInfo();\r
2111         \r
2112 #if 0\r
2113         RedrawItems(0, m_arShownList.GetCount());\r
2114 //      SetRedraw(false);\r
2115 //      ResizeAllListCtrlCols();\r
2116 //      SetRedraw(true);\r
2117 \r
2118         if ( m_pStoreSelection )\r
2119         {\r
2120                 // Deleting the instance will restore the\r
2121                 // selection of the CLogDlg.\r
2122                 delete m_pStoreSelection;\r
2123                 m_pStoreSelection = NULL;\r
2124         }\r
2125         else\r
2126         {\r
2127                 // If no selection has been set then this must be the first time\r
2128                 // the revisions are shown. Let's preselect the topmost revision.\r
2129                 if ( GetItemCount()>0 )\r
2130                 {\r
2131                         SetSelectionMark(0);\r
2132                         SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);\r
2133                 }\r
2134         }\r
2135 #endif\r
2136 \r
2137 \r
2138 \r
2139         //FetchFullLogInfo();\r
2140         //FetchFullLogInfoOrig();\r
2141         //RefreshCursor();\r
2142         // make sure the filter is applied (if any) now, after we refreshed/fetched\r
2143         // the log messages\r
2144 \r
2145         ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_END,0);\r
2146 \r
2147         InterlockedExchange(&m_bThreadRunning, FALSE);\r
2148 \r
2149         return 0;\r
2150 }\r
2151 \r
2152 void CGitLogListBase::Refresh()\r
2153 {       \r
2154         m_bExitThread=TRUE;\r
2155         if(m_LoadingThread!=NULL)\r
2156         {\r
2157                 DWORD ret =::WaitForSingleObject(m_LoadingThread->m_hThread,20000);\r
2158                 if(ret == WAIT_TIMEOUT)\r
2159                         TerminateThread();\r
2160         }\r
2161 \r
2162         this->Clear();\r
2163 \r
2164         //Update branch and Tag info\r
2165         ReloadHashMap();\r
2166         //Assume Thread have exited\r
2167         //if(!m_bThreadRunning)\r
2168         {\r
2169                 this->SetItemCountEx(0);\r
2170                 m_logEntries.clear();\r
2171                 m_bExitThread=FALSE;\r
2172                 InterlockedExchange(&m_bThreadRunning, TRUE);\r
2173                 InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
2174                 if (AfxBeginThread(LogThreadEntry, this)==NULL)\r
2175                 {\r
2176                         InterlockedExchange(&m_bThreadRunning, FALSE);\r
2177                         InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
2178                         CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
2179                 }\r
2180                 m_sFilterText.Empty();\r
2181                 m_From=CTime(1970,1,2,0,0,0);\r
2182                 m_To=CTime::GetCurrentTime();\r
2183         }\r
2184 }\r
2185 bool CGitLogListBase::ValidateRegexp(LPCTSTR regexp_str, tr1::wregex& pat, bool bMatchCase /* = false */)\r
2186 {\r
2187         try\r
2188         {\r
2189                 tr1::regex_constants::syntax_option_type type = tr1::regex_constants::ECMAScript;\r
2190                 if (!bMatchCase)\r
2191                         type |= tr1::regex_constants::icase;\r
2192                 pat = tr1::wregex(regexp_str, type);\r
2193                 return true;\r
2194         }\r
2195         catch (exception) {}\r
2196         return false;\r
2197 }\r
2198 \r
2199 void CGitLogListBase::RecalculateShownList(CPtrArray * pShownlist)\r
2200 {\r
2201 \r
2202         pShownlist->RemoveAll();\r
2203         tr1::wregex pat;//(_T("Remove"), tr1::regex_constants::icase);\r
2204         bool bRegex = false;\r
2205         if (m_bFilterWithRegex)\r
2206                 bRegex = ValidateRegexp(m_sFilterText, pat, false);\r
2207 \r
2208         tr1::regex_constants::match_flag_type flags = tr1::regex_constants::match_any;\r
2209         CString sRev;\r
2210         for (DWORD i=0; i<m_logEntries.size(); ++i)\r
2211         {\r
2212                 if ((bRegex)&&(m_bFilterWithRegex))\r
2213                 {\r
2214 #if 0\r
2215                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))\r
2216                         {\r
2217                                 ATLTRACE(_T("bugID = \"%s\"\n"), (LPCTSTR)m_logEntries[i]->sBugIDs);\r
2218                                 if (regex_search(wstring((LPCTSTR)m_logEntries[i]->sBugIDs), pat, flags)&&IsEntryInDateRange(i))\r
2219                                 {\r
2220                                         pShownlist->Add(m_logEntries[i]);\r
2221                                         continue;\r
2222                                 }\r
2223                         }\r
2224 #endif\r
2225                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))\r
2226                         {\r
2227                                 ATLTRACE(_T("messge = \"%s\"\n"),m_logEntries[i].m_Subject);\r
2228                                 if (regex_search(wstring((LPCTSTR)m_logEntries[i].m_Subject), pat, flags)&&IsEntryInDateRange(i))\r
2229                                 {\r
2230                                         pShownlist->Add(&m_logEntries[i]);\r
2231                                         continue;\r
2232                                 }\r
2233 \r
2234                                 ATLTRACE(_T("messge = \"%s\"\n"),m_logEntries[i].m_Body);\r
2235                                 if (regex_search(wstring((LPCTSTR)m_logEntries[i].m_Body), pat, flags)&&IsEntryInDateRange(i))\r
2236                                 {\r
2237                                         pShownlist->Add(&m_logEntries[i]);\r
2238                                         continue;\r
2239                                 }\r
2240                         }\r
2241 #if 0\r
2242                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))\r
2243                         {\r
2244                                 LogChangedPathArray * cpatharray = m_logEntries[i]->pArChangedPaths;\r
2245 \r
2246                                 bool bGoing = true;\r
2247                                 for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount() && bGoing; ++cpPathIndex)\r
2248                                 {\r
2249                                         LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);\r
2250                                         if (regex_search(wstring((LPCTSTR)cpath->sCopyFromPath), pat, flags)&&IsEntryInDateRange(i))\r
2251                                         {\r
2252                                                 pShownlist->Add(m_logEntries[i]);\r
2253                                                 bGoing = false;\r
2254                                                 continue;\r
2255                                         }\r
2256                                         if (regex_search(wstring((LPCTSTR)cpath->sPath), pat, flags)&&IsEntryInDateRange(i))\r
2257                                         {\r
2258                                                 pShownlist->Add(m_logEntries[i]);\r
2259                                                 bGoing = false;\r
2260                                                 continue;\r
2261                                         }\r
2262                                         if (regex_search(wstring((LPCTSTR)cpath->GetAction()), pat, flags)&&IsEntryInDateRange(i))\r
2263                                         {\r
2264                                                 pShownlist->Add(m_logEntries[i]);\r
2265                                                 bGoing = false;\r
2266                                                 continue;\r
2267                                         }\r
2268                                 }\r
2269                                 if (!bGoing)\r
2270                                         continue;\r
2271                         }\r
2272 #endif\r
2273                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))\r
2274                         {\r
2275                                 if (regex_search(wstring((LPCTSTR)m_logEntries[i].m_AuthorName), pat, flags)&&IsEntryInDateRange(i))\r
2276                                 {\r
2277                                         pShownlist->Add(&m_logEntries[i]);\r
2278                                         continue;\r
2279                                 }\r
2280                         }\r
2281                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))\r
2282                         {\r
2283                                 sRev.Format(_T("%s"), m_logEntries[i].m_CommitHash);\r
2284                                 if (regex_search(wstring((LPCTSTR)sRev), pat, flags)&&IsEntryInDateRange(i))\r
2285                                 {\r
2286                                         pShownlist->Add(&m_logEntries[i]);\r
2287                                         continue;\r
2288                                 }\r
2289                         }\r
2290                 } // if (bRegex)\r
2291                 else\r
2292                 {\r
2293                         CString find = m_sFilterText;\r
2294                         find.MakeLower();\r
2295 #if 0\r
2296                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))\r
2297                         {\r
2298                                 CString sBugIDs = m_logEntries[i]->sBugIDs;\r
2299 \r
2300                                 sBugIDs = sBugIDs.MakeLower();\r
2301                                 if ((sBugIDs.Find(find) >= 0)&&(IsEntryInDateRange(i)))\r
2302                                 {\r
2303                                         pShownlist->Add(m_logEntries[i]);\r
2304                                         continue;\r
2305                                 }\r
2306                         }\r
2307 #endif\r
2308                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))\r
2309                         {\r
2310                                 CString msg = m_logEntries[i].m_Subject;\r
2311 \r
2312                                 msg = msg.MakeLower();\r
2313                                 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))\r
2314                                 {\r
2315                                         pShownlist->Add(&m_logEntries[i]);\r
2316                                         continue;\r
2317                                 }\r
2318                                 msg = m_logEntries[i].m_Body;\r
2319 \r
2320                                 msg = msg.MakeLower();\r
2321                                 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))\r
2322                                 {\r
2323                                         pShownlist->Add(&m_logEntries[i]);\r
2324                                         continue;\r
2325                                 }\r
2326                         }\r
2327 #if 0\r
2328                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))\r
2329                         {\r
2330                                 LogChangedPathArray * cpatharray = m_logEntries[i]->pArChangedPaths;\r
2331 \r
2332                                 bool bGoing = true;\r
2333                                 for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount() && bGoing; ++cpPathIndex)\r
2334                                 {\r
2335                                         LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);\r
2336                                         CString path = cpath->sCopyFromPath;\r
2337                                         path.MakeLower();\r
2338                                         if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))\r
2339                                         {\r
2340                                                 pShownlist->Add(m_logEntries[i]);\r
2341                                                 bGoing = false;\r
2342                                                 continue;\r
2343                                         }\r
2344                                         path = cpath->sPath;\r
2345                                         path.MakeLower();\r
2346                                         if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))\r
2347                                         {\r
2348                                                 pShownlist->Add(m_logEntries[i]);\r
2349                                                 bGoing = false;\r
2350                                                 continue;\r
2351                                         }\r
2352                                         path = cpath->GetAction();\r
2353                                         path.MakeLower();\r
2354                                         if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))\r
2355                                         {\r
2356                                                 pShownlist->Add(m_logEntries[i]);\r
2357                                                 bGoing = false;\r
2358                                                 continue;\r
2359                                         }\r
2360                                 }\r
2361                         }\r
2362 #endif\r
2363                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))\r
2364                         {\r
2365                                 CString msg = m_logEntries[i].m_AuthorName;\r
2366                                 msg = msg.MakeLower();\r
2367                                 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))\r
2368                                 {\r
2369                                         pShownlist->Add(&m_logEntries[i]);\r
2370                                         continue;\r
2371                                 }\r
2372                         }\r
2373                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))\r
2374                         {\r
2375                                 sRev.Format(_T("%s"), m_logEntries[i].m_CommitHash);\r
2376                                 if ((sRev.Find(find) >= 0)&&(IsEntryInDateRange(i)))\r
2377                                 {\r
2378                                         pShownlist->Add(&m_logEntries[i]);\r
2379                                         continue;\r
2380                                 }\r
2381                         }\r
2382                 } // else (from if (bRegex))    \r
2383         } // for (DWORD i=0; i<m_logEntries.size(); ++i) \r
2384 \r
2385 }\r
2386 \r
2387 BOOL CGitLogListBase::IsEntryInDateRange(int i)\r
2388 {\r
2389         __time64_t time = m_logEntries[i].m_AuthorDate.GetTime();\r
2390         if ((time >= m_From.GetTime())&&(time <= m_To.GetTime()))\r
2391                 return TRUE;\r
2392 \r
2393         return FALSE;\r
2394 \r
2395 //      return TRUE;\r
2396 }\r
2397 void CGitLogListBase::StartFilter()\r
2398 {\r
2399         InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
2400         RecalculateShownList(&m_arShownList);\r
2401         InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
2402 \r
2403 \r
2404         DeleteAllItems();\r
2405         SetItemCountEx(ShownCountWithStopped());\r
2406         RedrawItems(0, ShownCountWithStopped());\r
2407         SetRedraw(false);\r
2408         //ResizeAllListCtrlCols();\r
2409         SetRedraw(true);\r
2410         Invalidate();\r
2411 \r
2412 }\r
2413 void CGitLogListBase::RemoveFilter()\r
2414 {\r
2415 \r
2416         InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
2417 \r
2418         m_arShownList.RemoveAll();\r
2419 \r
2420         // reset the time filter too\r
2421 #if 0\r
2422         m_timFrom = (__time64_t(m_tFrom));\r
2423         m_timTo = (__time64_t(m_tTo));\r
2424         m_DateFrom.SetTime(&m_timFrom);\r
2425         m_DateTo.SetTime(&m_timTo);\r
2426         m_DateFrom.SetRange(&m_timFrom, &m_timTo);\r
2427         m_DateTo.SetRange(&m_timFrom, &m_timTo);\r
2428 #endif\r
2429 \r
2430         for (DWORD i=0; i<m_logEntries.size(); ++i)\r
2431         {\r
2432                 if(this->m_IsOldFirst)\r
2433                 {\r
2434                         m_arShownList.Add(&m_logEntries[m_logEntries.size()-i-1]);\r
2435                 }else\r
2436                 {\r
2437                         m_arShownList.Add(&m_logEntries[i]);\r
2438                 }\r
2439         }\r
2440 //      InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
2441         DeleteAllItems();\r
2442         SetItemCountEx(ShownCountWithStopped());\r
2443         RedrawItems(0, ShownCountWithStopped());\r
2444 //      SetRedraw(false);\r
2445 //      ResizeAllListCtrlCols();\r
2446 //      SetRedraw(true);\r
2447 \r
2448         InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
2449 }\r
2450 \r
2451 void CGitLogListBase::Clear()\r
2452 {\r
2453         m_arShownList.RemoveAll();\r
2454         DeleteAllItems();\r
2455 \r
2456         m_logEntries.ClearAll();\r
2457 \r
2458 }\r
2459 \r
2460 void CGitLogListBase::OnDestroy()\r
2461 {\r
2462         // save the column widths to the registry\r
2463         SaveColumnWidths();\r
2464 \r
2465         if(this->m_bThreadRunning)\r
2466         {\r
2467                 this->m_bExitThread=true;\r
2468                 DWORD ret =::WaitForSingleObject(m_LoadingThread->m_hThread,20000);\r
2469                 if(ret == WAIT_TIMEOUT)\r
2470                         TerminateThread();\r
2471         }\r
2472         while(m_LogCache.SaveCache())\r
2473         {\r
2474                 if(CMessageBox::Show(NULL,_T("Cannot Save Log Cache to Disk. To retry click yes. To give up click no."),_T("TortoiseGit"),\r
2475                                                         MB_YESNO) == IDNO)\r
2476                                                         break;\r
2477         }\r
2478         CHintListCtrl::OnDestroy();\r
2479 }\r
2480 \r
2481 LRESULT CGitLogListBase::OnLoad(WPARAM wParam,LPARAM lParam)\r
2482 {\r
2483         CRect rect;\r
2484         int i=(int)wParam;\r
2485         this->GetItemRect(i,&rect,LVIR_BOUNDS);\r
2486         this->InvalidateRect(rect);\r
2487         return 0;\r
2488 }\r
2489 \r
2490 /**\r
2491  * Save column widths to the registry\r
2492  */\r
2493 void CGitLogListBase::SaveColumnWidths()\r
2494 {\r
2495         CHeaderCtrl* pHdrCtrl = (CHeaderCtrl*)(GetDlgItem(0));\r
2496         if (pHdrCtrl)\r
2497         {\r
2498                 int numcols = pHdrCtrl->GetItemCount();\r
2499                 for (int col = 0; col < numcols; col++)\r
2500                 {\r
2501                         int width = GetColumnWidth( col );\r
2502                         CString regentry;\r
2503                         regentry.Format( _T("Software\\TortoiseGit\\%s\\ColWidth%d"),m_ColumnRegKey, col);\r
2504                         CRegDWORD regwidth(regentry, 0);\r
2505                         regwidth = width;       // this writes it to reg\r
2506                 }\r
2507         }\r
2508 }\r
2509 \r
2510 int CGitLogListBase::GetHeadIndex()\r
2511 {\r
2512         if(m_HeadHash.IsEmpty())\r
2513                 return -1;\r
2514 \r
2515         for(int i=0;i<m_arShownList.GetCount();i++)\r
2516         {\r
2517                 GitRev *pRev = (GitRev*)m_arShownList[i];\r
2518                 if(pRev)\r
2519                 {\r
2520                         if(pRev->m_CommitHash == m_HeadHash )\r
2521                                 return i;\r
2522                 }\r
2523         }\r
2524         return -1;\r
2525 }