OSDN Git Service

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