OSDN Git Service

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