OSDN Git Service

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