OSDN Git Service

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