OSDN Git Service

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