OSDN Git Service

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