OSDN Git Service

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