OSDN Git Service

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