OSDN Git Service

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