OSDN Git Service

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