OSDN Git Service

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