OSDN Git Service

Upgrade graphic tree from qgit2.3
[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         , m_bVista(false)\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         this->m_HeadHash=g_Git.GetHash(CString(_T("HEAD"))).Left(40);\r
88 \r
89         m_From=CTime(1970,1,2,0,0,0);\r
90         m_To=CTime::GetCurrentTime();\r
91     m_ShowMask = 0;\r
92         m_LoadingThread = NULL;\r
93 \r
94         m_bExitThread=FALSE;\r
95         m_IsOldFirst = FALSE;\r
96         m_IsRebaseReplaceGraph = FALSE;\r
97 \r
98 \r
99         for(int i=0;i<Lanes::COLORS_NUM;i++)\r
100         {\r
101                 m_LineColors[i] = m_Colors.GetColor((CColors::Colors)(CColors::BranchLine1+i));\r
102         }\r
103         // get short/long datetime setting from registry\r
104         DWORD RegUseShortDateFormat = CRegDWORD(_T("Software\\TortoiseGit\\LogDateFormat"), TRUE);\r
105         if ( RegUseShortDateFormat )\r
106         {\r
107                 m_DateFormat = DATE_SHORTDATE;\r
108         }\r
109         else\r
110         {\r
111                 m_DateFormat = DATE_LONGDATE;\r
112         }\r
113         // get relative time display setting from registry\r
114         DWORD regRelativeTimes = CRegDWORD(_T("Software\\TortoiseGit\\RelativeTimes"), FALSE);\r
115         m_bRelativeTimes = (regRelativeTimes != 0);\r
116         m_ContextMenuMask = 0xFFFFFFFFFFFFFFFF;\r
117 \r
118         m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_PICK);\r
119         m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_SQUASH);\r
120         m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_EDIT);\r
121         m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_SKIP);\r
122 \r
123         OSVERSIONINFOEX inf;\r
124         SecureZeroMemory(&inf, sizeof(OSVERSIONINFOEX));\r
125         inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\r
126         GetVersionEx((OSVERSIONINFO *)&inf);\r
127         WORD fullver = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);\r
128         m_bVista = (fullver >= 0x0600);\r
129 \r
130         m_ColumnRegKey=_T("log");\r
131 }\r
132 \r
133 CGitLogListBase::~CGitLogListBase()\r
134 {\r
135         InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
136 \r
137         DestroyIcon(m_hModifiedIcon);\r
138         DestroyIcon(m_hReplacedIcon);\r
139         DestroyIcon(m_hAddedIcon);\r
140         DestroyIcon(m_hDeletedIcon);\r
141         m_logEntries.ClearAll();\r
142 \r
143         if (m_boldFont)\r
144                 DeleteObject(m_boldFont);\r
145 \r
146         if ( m_pStoreSelection )\r
147         {\r
148                 delete m_pStoreSelection;\r
149                 m_pStoreSelection = NULL;\r
150         }\r
151 \r
152         if(this->m_bThreadRunning)\r
153         {\r
154                 m_bExitThread=true;\r
155                 WaitForSingleObject(m_LoadingThread->m_hThread,1000);\r
156                 TerminateThread();\r
157         }\r
158 }\r
159 \r
160 \r
161 BEGIN_MESSAGE_MAP(CGitLogListBase, CHintListCtrl)\r
162         ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdrawLoglist)\r
163         ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetdispinfoLoglist)\r
164         ON_WM_CONTEXTMENU()\r
165         ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclkLoglist)\r
166         ON_NOTIFY_REFLECT(LVN_ODFINDITEM,OnLvnOdfinditemLoglist)\r
167         ON_WM_CREATE()\r
168         ON_WM_DESTROY()\r
169         ON_MESSAGE(MSG_LOADED,OnLoad)\r
170 END_MESSAGE_MAP()\r
171 \r
172 int CGitLogListBase:: OnCreate(LPCREATESTRUCT lpCreateStruct)\r
173 {\r
174         PreSubclassWindow();\r
175         return CHintListCtrl::OnCreate(lpCreateStruct);\r
176 }\r
177 \r
178 void CGitLogListBase::PreSubclassWindow()\r
179 {\r
180         SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES);\r
181         // load the icons for the action columns\r
182         m_Theme.SetWindowTheme(GetSafeHwnd(), L"Explorer", NULL);\r
183         CHintListCtrl::PreSubclassWindow();\r
184 }\r
185 \r
186 void CGitLogListBase::InsertGitColumn()\r
187 {\r
188         CString temp;\r
189 \r
190         int c = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;\r
191         \r
192         while (c>=0)\r
193                 DeleteColumn(c--);\r
194         temp.LoadString(IDS_LOG_GRAPH);\r
195 \r
196         if(m_IsRebaseReplaceGraph)\r
197         {\r
198                 temp=_T("Rebase");\r
199         }\r
200         else\r
201         {\r
202                 temp.LoadString(IDS_LOG_GRAPH);\r
203         }\r
204         InsertColumn(this->LOGLIST_GRAPH, temp);\r
205         \r
206 #if 0   \r
207         // make the revision column right aligned\r
208         LVCOLUMN Column;\r
209         Column.mask = LVCF_FMT;\r
210         Column.fmt = LVCFMT_RIGHT;\r
211         SetColumn(0, &Column); \r
212 #endif  \r
213 //      CString log;\r
214 //      g_Git.GetLog(log);\r
215 \r
216         if(m_IsIDReplaceAction)\r
217         {\r
218                 temp.LoadString(IDS_LOG_ID);\r
219                 InsertColumn(this->LOGLIST_ACTION, temp);\r
220         }\r
221         else\r
222         {\r
223                 temp.LoadString(IDS_LOG_ACTIONS);\r
224                 InsertColumn(this->LOGLIST_ACTION, temp);\r
225         }\r
226         temp.LoadString(IDS_LOG_MESSAGE);\r
227         InsertColumn(this->LOGLIST_MESSAGE, temp);\r
228         \r
229         temp.LoadString(IDS_LOG_AUTHOR);\r
230         InsertColumn(this->LOGLIST_AUTHOR, temp);\r
231         \r
232         temp.LoadString(IDS_LOG_DATE);\r
233         InsertColumn(this->LOGLIST_DATE, temp);\r
234         \r
235 \r
236         if (m_bShowBugtraqColumn)\r
237         {\r
238 //              temp = m_ProjectProperties.sLabel;\r
239                 if (temp.IsEmpty())\r
240                         temp.LoadString(IDS_LOG_BUGIDS);\r
241                 InsertColumn(this->LOGLIST_BUG, temp);\r
242 \r
243         }\r
244         \r
245         SetRedraw(false);\r
246         ResizeAllListCtrlCols();\r
247         SetRedraw(true);\r
248 \r
249 }\r
250 \r
251 /**\r
252  * Resizes all columns in a list control to values in registry.\r
253  */\r
254 void CGitLogListBase::ResizeAllListCtrlCols()\r
255 {\r
256         // column max and min widths to allow\r
257         static const int nMinimumWidth = 10;\r
258         static const int nMaximumWidth = 1000;\r
259         CHeaderCtrl* pHdrCtrl = (CHeaderCtrl*)(GetDlgItem(0));\r
260         if (pHdrCtrl)\r
261         {\r
262                 int numcols = pHdrCtrl->GetItemCount();\r
263                 for (int col = 0; col < numcols; col++)\r
264                 {\r
265                         // get width for this col last time from registry\r
266                         CString regentry;\r
267                         regentry.Format( _T("Software\\TortoiseGit\\%s\\ColWidth%d"),m_ColumnRegKey, col);\r
268                         CRegDWORD regwidth(regentry, 0);\r
269                         int cx = regwidth;\r
270                         if ( cx == 0 )\r
271                         {\r
272                                 // no saved value, setup sensible defaults\r
273                                 if (col == this->LOGLIST_MESSAGE)\r
274                                 {\r
275                                         cx = LOGLIST_MESSAGE_MIN;\r
276                                 }\r
277                                 else\r
278                                 {\r
279                                         cx = ICONITEMBORDER+16*4;\r
280                                 }\r
281                         }\r
282                         if (cx < nMinimumWidth)\r
283                         {\r
284                                 cx = nMinimumWidth;\r
285                         } else if (cx > nMaximumWidth)\r
286                         {\r
287                                 cx = nMaximumWidth;\r
288                         }\r
289 \r
290                         SetColumnWidth(col, cx);\r
291                 }\r
292         }\r
293 \r
294 }\r
295 \r
296 \r
297 BOOL CGitLogListBase::GetShortName(CString ref, CString &shortname,CString prefix)\r
298 {\r
299         TRACE(_T("%s %s\r\n"),ref,prefix);\r
300         if(ref.Left(prefix.GetLength()) ==  prefix)\r
301         {\r
302                 shortname = ref.Right(ref.GetLength()-prefix.GetLength());\r
303                 if(shortname.Right(3)==_T("^{}"))\r
304                         shortname=shortname.Left(shortname.GetLength()-3);\r
305                 return TRUE;\r
306         }\r
307         return FALSE;\r
308 }\r
309 void CGitLogListBase::FillBackGround(HDC hdc, int Index,CRect &rect)\r
310 {       \r
311 //      HBRUSH brush;\r
312         LVITEM   rItem;\r
313         SecureZeroMemory(&rItem, sizeof(LVITEM));\r
314         rItem.mask  = LVIF_STATE;\r
315         rItem.iItem = Index;\r
316         rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;\r
317         GetItem(&rItem);\r
318 \r
319         GitRev* pLogEntry = (GitRev*)m_arShownList.GetAt(Index);\r
320         HBRUSH brush;\r
321 \r
322         \r
323         if (m_Theme.IsAppThemed() && m_bVista)\r
324         {\r
325                 m_Theme.Open(m_hWnd, L"Explorer");\r
326                 int state = LISS_NORMAL;\r
327                 if (rItem.state & LVIS_SELECTED)\r
328                 {\r
329                         if (::GetFocus() == m_hWnd)\r
330                                 state |= LISS_SELECTED;\r
331                         else\r
332                                 state |= LISS_SELECTEDNOTFOCUS;\r
333                 }\r
334                 else\r
335                 {\r
336                         if(pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_SQUASH)\r
337                                 brush = ::CreateSolidBrush(RGB(156,156,156));\r
338                         else if(pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_EDIT)\r
339                                 brush = ::CreateSolidBrush(RGB(200,200,128));\r
340 \r
341                         if (brush == NULL)\r
342                                 return;\r
343 \r
344                         ::FillRect(hdc, &rect, brush);\r
345                         ::DeleteObject(brush);\r
346 \r
347                 }\r
348 \r
349                 if (m_Theme.IsBackgroundPartiallyTransparent(LVP_LISTDETAIL, state))\r
350                         m_Theme.DrawParentBackground(m_hWnd, hdc, &rect);\r
351                 else\r
352                         m_Theme.DrawBackground(hdc, LVP_LISTDETAIL, state, &rect, NULL);\r
353         }\r
354         else\r
355         {\r
356                 \r
357                 if (rItem.state & LVIS_SELECTED)\r
358                 {\r
359                         if (::GetFocus() == m_hWnd)\r
360                                 brush = ::CreateSolidBrush(::GetSysColor(COLOR_HIGHLIGHT));\r
361                         else\r
362                                 brush = ::CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));\r
363                 }\r
364                 else\r
365                 {\r
366                         //if (pLogEntry->bCopiedSelf)\r
367                         //      brush = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));\r
368                         //else\r
369                         if(pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_SQUASH)\r
370                                 brush = ::CreateSolidBrush(RGB(156,156,156));\r
371                         else if(pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_EDIT)\r
372                                 brush = ::CreateSolidBrush(RGB(200,200,128));\r
373                         else \r
374                                 brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));\r
375                 }\r
376                 if (brush == NULL)\r
377                         return;\r
378 \r
379                 ::FillRect(hdc, &rect, brush);\r
380                 ::DeleteObject(brush);\r
381                 \r
382         }\r
383 }\r
384 \r
385 void CGitLogListBase::DrawTagBranch(HDC hdc,CRect &rect,INT_PTR index)\r
386 {\r
387         GitRev* data = (GitRev*)m_arShownList.GetAt(index);\r
388         CRect rt=rect;\r
389         LVITEM   rItem;\r
390         SecureZeroMemory(&rItem, sizeof(LVITEM));\r
391         rItem.mask  = LVIF_STATE;\r
392         rItem.iItem = index;\r
393         rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;\r
394         GetItem(&rItem);\r
395 \r
396         for(unsigned int i=0;i<m_HashMap[data->m_CommitHash].size();i++)\r
397         {\r
398                 CString str;\r
399                 str=m_HashMap[data->m_CommitHash][i];\r
400                 \r
401                 CString shortname;\r
402                 HBRUSH brush=0;\r
403                 shortname=_T("");\r
404                 if(GetShortName(str,shortname,_T("refs/heads/")))\r
405                 {\r
406                         if( shortname == m_CurrentBranch )\r
407                                 brush = ::CreateSolidBrush(m_Colors.GetColor(CColors::CurrentBranch));\r
408                         else\r
409                                 brush = ::CreateSolidBrush(m_Colors.GetColor(CColors::LocalBranch));\r
410 \r
411                 }else if(GetShortName(str,shortname,_T("refs/remotes/")))\r
412                 {\r
413                         brush = ::CreateSolidBrush(m_Colors.GetColor(CColors::RemoteBranch));\r
414                 }\r
415                 else if(GetShortName(str,shortname,_T("refs/tags/")))\r
416                 {\r
417                         brush = ::CreateSolidBrush(m_Colors.GetColor(CColors::Tag));\r
418                 }\r
419                 else if(GetShortName(str,shortname,_T("refs/stash")))\r
420                 {\r
421                         brush = ::CreateSolidBrush(m_Colors.GetColor(CColors::Stash));\r
422                         shortname=_T("stash");\r
423                 }\r
424                 \r
425 \r
426                 if(!shortname.IsEmpty())\r
427                 {\r
428                         SIZE size;\r
429                         memset(&size,0,sizeof(SIZE));\r
430                         GetTextExtentPoint32(hdc, shortname,shortname.GetLength(),&size);\r
431                 \r
432                         rt.SetRect(rt.left,rt.top,rt.left+size.cx,rt.bottom);\r
433                         rt.right+=4;\r
434                         ::FillRect(hdc, &rt, brush);\r
435                         if (rItem.state & LVIS_SELECTED)\r
436                         {\r
437                                 COLORREF   clrOld   = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT));   \r
438                                 ::DrawText(hdc,shortname,shortname.GetLength(),&rt,DT_CENTER);\r
439                                 ::SetTextColor(hdc,clrOld);   \r
440                         }else\r
441                         {\r
442                                 ::DrawText(hdc,shortname,shortname.GetLength(),&rt,DT_CENTER);\r
443                         }\r
444 \r
445                         \r
446                         ::MoveToEx(hdc,rt.left,rt.top,NULL);\r
447                         ::LineTo(hdc,rt.right,rt.top);\r
448                         ::LineTo(hdc,rt.right,rt.bottom);\r
449                         ::LineTo(hdc,rt.left,rt.bottom);\r
450                         ::LineTo(hdc,rt.left,rt.top);\r
451                                 \r
452                         rt.left=rt.right+3;\r
453                 }\r
454                 if(brush)\r
455                         ::DeleteObject(brush);\r
456         }               \r
457         rt.right=rect.right;\r
458 \r
459         if (rItem.state & LVIS_SELECTED)\r
460         {\r
461                 COLORREF   clrOld   = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT));   \r
462                 ::DrawText(hdc,data->m_Subject,data->m_Subject.GetLength(),&rt,DT_LEFT);\r
463                 ::SetTextColor(hdc,clrOld);   \r
464         }else\r
465         {\r
466                 ::DrawText(hdc,data->m_Subject,data->m_Subject.GetLength(),&rt,DT_LEFT);\r
467         }\r
468         \r
469 }\r
470 \r
471 void CGitLogListBase::paintGraphLane(HDC hdc, int laneHeight,int type, int x1, int x2,\r
472                                       const COLORREF& col,const COLORREF& activeColor, int top\r
473                                                                           )  \r
474 {\r
475         int h = laneHeight / 2;\r
476         int m = (x1 + x2) / 2;\r
477         int r = (x2 - x1) / 3;\r
478         int d =  2 * r;\r
479 \r
480         #define P_CENTER m , h+top\r
481         #define P_0      x2, h+top\r
482         #define P_90     m , 0+top\r
483         #define P_180    x1, h+top\r
484         #define P_270    m , 2 * h+top\r
485         #define R_CENTER m - r, h - r+top, m - r+d, h - r+top+d\r
486 \r
487         //static QPen myPen(Qt::black, 2); // fast path here\r
488         CPen pen;\r
489         pen.CreatePen(PS_SOLID,2,col);\r
490         //myPen.setColor(col);\r
491         HPEN oldpen=(HPEN)::SelectObject(hdc,(HPEN)pen);\r
492 \r
493         //p->setPen(myPen);\r
494 \r
495         // vertical line\r
496         switch (type) {\r
497         case Lanes::ACTIVE:\r
498         case Lanes::NOT_ACTIVE:\r
499         case Lanes::MERGE_FORK:\r
500         case Lanes::MERGE_FORK_R:\r
501         case Lanes::MERGE_FORK_L:\r
502         case Lanes::JOIN:\r
503         case Lanes::JOIN_R:\r
504         case Lanes::JOIN_L:\r
505                 DrawLine(hdc,P_90,P_270);\r
506                 //p->drawLine(P_90, P_270);\r
507                 break;\r
508         case Lanes::HEAD:\r
509         case Lanes::HEAD_R:\r
510         case Lanes::HEAD_L:\r
511         case Lanes::BRANCH:\r
512                 DrawLine(hdc,P_CENTER,P_270);\r
513                 //p->drawLine(P_CENTER, P_270);\r
514                 break;\r
515         case Lanes::TAIL:\r
516         case Lanes::TAIL_R:\r
517         case Lanes::TAIL_L:\r
518         case Lanes::INITIAL:\r
519         case Lanes::BOUNDARY:\r
520         case Lanes::BOUNDARY_C:\r
521         case Lanes::BOUNDARY_R:\r
522         case Lanes::BOUNDARY_L:\r
523                 DrawLine(hdc,P_90, P_CENTER);\r
524                 //p->drawLine(P_90, P_CENTER);\r
525                 break;\r
526         default:\r
527                 break;\r
528         }\r
529 \r
530         // horizontal line\r
531         switch (type) {\r
532         case Lanes::MERGE_FORK:\r
533         case Lanes::JOIN:\r
534         case Lanes::HEAD:\r
535         case Lanes::TAIL:\r
536         case Lanes::CROSS:\r
537         case Lanes::CROSS_EMPTY:\r
538         case Lanes::BOUNDARY_C:\r
539                 DrawLine(hdc,P_180,P_0);\r
540                 //p->drawLine(P_180, P_0);\r
541                 break;\r
542         case Lanes::MERGE_FORK_R:\r
543         case Lanes::JOIN_R:\r
544         case Lanes::HEAD_R:\r
545         case Lanes::TAIL_R:\r
546         case Lanes::BOUNDARY_R:\r
547                 DrawLine(hdc,P_180,P_CENTER);\r
548                 //p->drawLine(P_180, P_CENTER);\r
549                 break;\r
550         case Lanes::MERGE_FORK_L:\r
551         case Lanes::JOIN_L:\r
552         case Lanes::HEAD_L:\r
553         case Lanes::TAIL_L:\r
554         case Lanes::BOUNDARY_L:\r
555                 DrawLine(hdc,P_CENTER,P_0);\r
556                 //p->drawLine(P_CENTER, P_0);\r
557                 break;\r
558         default:\r
559                 break;\r
560         }\r
561 \r
562         CBrush brush;\r
563         brush.CreateSolidBrush(col);\r
564         HBRUSH oldbrush=(HBRUSH)::SelectObject(hdc,(HBRUSH)brush);\r
565         // center symbol, e.g. rect or ellipse\r
566         switch (type) {\r
567         case Lanes::ACTIVE:\r
568         case Lanes::INITIAL:\r
569         case Lanes::BRANCH:\r
570 \r
571                 //p->setPen(Qt::NoPen);\r
572                 //p->setBrush(col);\r
573                 ::Ellipse(hdc, R_CENTER);\r
574                 //p->drawEllipse(R_CENTER);\r
575                 break;\r
576         case Lanes::MERGE_FORK:\r
577         case Lanes::MERGE_FORK_R:\r
578         case Lanes::MERGE_FORK_L:\r
579                 //p->setPen(Qt::NoPen);\r
580                 //p->setBrush(col);\r
581                 //p->drawRect(R_CENTER);\r
582                 Rectangle(hdc,R_CENTER);\r
583                 break;\r
584         case Lanes::UNAPPLIED:\r
585                 // Red minus sign\r
586                 //p->setPen(Qt::NoPen);\r
587                 //p->setBrush(Qt::red);\r
588                 //p->drawRect(m - r, h - 1, d, 2);\r
589                 ::Rectangle(hdc,m-r,h-1,d,2);\r
590                 break;\r
591         case Lanes::APPLIED:\r
592                 // Green plus sign\r
593                 //p->setPen(Qt::NoPen);\r
594                 //p->setBrush(DARK_GREEN);\r
595                 //p->drawRect(m - r, h - 1, d, 2);\r
596                 //p->drawRect(m - 1, h - r, 2, d);\r
597                 ::Rectangle(hdc,m-r,h-1,d,2);\r
598                 ::Rectangle(hdc,m-1,h-r,2,d);\r
599                 break;\r
600         case Lanes::BOUNDARY:\r
601                 //p->setBrush(back);\r
602                 //p->drawEllipse(R_CENTER);\r
603                 ::Ellipse(hdc, R_CENTER);\r
604                 break;\r
605         case Lanes::BOUNDARY_C:\r
606         case Lanes::BOUNDARY_R:\r
607         case Lanes::BOUNDARY_L:\r
608                 //p->setBrush(back);\r
609                 //p->drawRect(R_CENTER);\r
610                 ::Rectangle(hdc,R_CENTER);\r
611                 break;\r
612         default:\r
613                 break;\r
614         }\r
615 \r
616         ::SelectObject(hdc,oldpen);\r
617         ::SelectObject(hdc,oldbrush);\r
618         #undef P_CENTER\r
619         #undef P_0\r
620         #undef P_90\r
621         #undef P_180\r
622         #undef P_270\r
623         #undef R_CENTER\r
624 }\r
625 \r
626 void CGitLogListBase::DrawGraph(HDC hdc,CRect &rect,INT_PTR index)\r
627 {\r
628         //todo unfinished\r
629 //      return;\r
630         GitRev* data = (GitRev*)m_arShownList.GetAt(index);\r
631         if(data->m_CommitHash.IsEmpty())\r
632                 return;\r
633 \r
634         CRect rt=rect;\r
635         LVITEM   rItem;\r
636         SecureZeroMemory(&rItem, sizeof(LVITEM));\r
637         rItem.mask  = LVIF_STATE;\r
638         rItem.iItem = index;\r
639         rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;\r
640         GetItem(&rItem);\r
641 \r
642         \r
643 //      p->translate(QPoint(opt.rect.left(), opt.rect.top()));\r
644 \r
645 \r
646 \r
647         if (data->m_Lanes.size() == 0)\r
648                 m_logEntries.setLane(data->m_CommitHash);\r
649 \r
650         std::vector<int>& lanes=data->m_Lanes;\r
651         UINT laneNum = lanes.size();\r
652         UINT activeLane = 0;\r
653         for (UINT i = 0; i < laneNum; i++)\r
654                 if (Lanes::isMerge(lanes[i])) {\r
655                         activeLane = i;\r
656                         break;\r
657                 }\r
658 \r
659         int x1 = 0, x2 = 0;\r
660         int maxWidth = rect.Width();\r
661         int lw = 3 * rect.Height() / 4; //laneWidth() \r
662 \r
663         COLORREF activeColor = m_LineColors[activeLane % Lanes::COLORS_NUM];\r
664         //if (opt.state & QStyle::State_Selected)\r
665         //      activeColor = blend(activeColor, opt.palette.highlightedText().color(), 208);\r
666         \r
667 \r
668         for (unsigned int i = 0; i < laneNum && x2 < maxWidth; i++) \r
669         {\r
670 \r
671                 x1 = x2;\r
672                 x2 += lw;\r
673 \r
674                 int ln = lanes[i];\r
675                 if (ln == Lanes::EMPTY)\r
676                         continue;\r
677 \r
678                 COLORREF color = i == activeLane ? activeColor : m_LineColors[i % Lanes::COLORS_NUM];\r
679                 paintGraphLane(hdc, rect.Height(),ln, x1, x2, color,activeColor, rect.top);\r
680         }\r
681 \r
682 #if 0\r
683         for (UINT i = 0; i < laneNum && x2 < maxWidth; i++) {\r
684 \r
685                 x1 = x2;\r
686                 x2 += lw;\r
687 \r
688                 int ln = lanes[i];\r
689                 if (ln == Lanes::EMPTY)\r
690                         continue;\r
691 \r
692                 UINT col = (  Lanes:: isHead(ln) ||Lanes:: isTail(ln) || Lanes::isJoin(ln)\r
693                             || ln ==Lanes:: CROSS_EMPTY) ? activeLane : i;\r
694 \r
695                 if (ln == Lanes::CROSS) {\r
696                         paintGraphLane(hdc, rect.Height(),Lanes::NOT_ACTIVE, x1, x2, m_LineColors[col % Lanes::COLORS_NUM],rect.top);\r
697                         paintGraphLane(hdc, rect.Height(),Lanes::CROSS, x1, x2, m_LineColors[activeLane % Lanes::COLORS_NUM],rect.top);\r
698                 } else\r
699                         paintGraphLane(hdc, rect.Height(),ln, x1, x2, m_LineColors[col % Lanes::COLORS_NUM],rect.top);\r
700         }\r
701 #endif\r
702 \r
703         TRACE(_T("index %d %d\r\n"),index,data->m_Lanes.size());\r
704 }\r
705 \r
706 void CGitLogListBase::OnNMCustomdrawLoglist(NMHDR *pNMHDR, LRESULT *pResult)\r
707 {\r
708 \r
709         NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );\r
710         // Take the default processing unless we set this to something else below.\r
711         *pResult = CDRF_DODEFAULT;\r
712 \r
713         if (m_bNoDispUpdates)\r
714                 return;\r
715 \r
716 \r
717 \r
718         switch (pLVCD->nmcd.dwDrawStage)\r
719         {\r
720         case CDDS_PREPAINT:\r
721                 {\r
722                         *pResult = CDRF_NOTIFYITEMDRAW;\r
723                         return;\r
724                 }\r
725                 break;\r
726         case CDDS_ITEMPREPAINT:\r
727                 {\r
728                         // This is the prepaint stage for an item. Here's where we set the\r
729                         // item's text color. \r
730                         \r
731                         // Tell Windows to send draw notifications for each subitem.\r
732                         *pResult = CDRF_NOTIFYSUBITEMDRAW;\r
733 \r
734                         COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);\r
735 \r
736                         if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)\r
737                         {\r
738                                 GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);\r
739                                 if (data)\r
740                                 {\r
741 #if 0\r
742                                         if (data->bCopiedSelf)\r
743                                         {\r
744                                                 // only change the background color if the item is not 'hot' (on vista with m_Themes enabled)\r
745                                                 if (!m_Theme.IsAppm_Themed() || !m_bVista || ((pLVCD->nmcd.uItemState & CDIS_HOT)==0))\r
746                                                         pLVCD->clrTextBk = GetSysColor(COLOR_MENU);\r
747                                         }\r
748 \r
749                                         if (data->bCopies)\r
750                                                 crText = m_Colors.GetColor(CColors::Modified);\r
751 #endif\r
752                                         if (data->m_Action& (CTGitPath::LOGACTIONS_REBASE_DONE| CTGitPath::LOGACTIONS_REBASE_SKIP) ) \r
753                                                 crText = RGB(128,128,128);\r
754 \r
755                                         if(data->m_Action&CTGitPath::LOGACTIONS_REBASE_SQUASH)\r
756                                                 pLVCD->clrTextBk = RGB(156,156,156);\r
757                                         else if(data->m_Action&CTGitPath::LOGACTIONS_REBASE_EDIT)\r
758                                                 pLVCD->clrTextBk  = RGB(200,200,128);\r
759                                         else \r
760                                                 pLVCD->clrTextBk  = ::GetSysColor(COLOR_WINDOW);\r
761 \r
762                                         if(data->m_Action&CTGitPath::LOGACTIONS_REBASE_CURRENT)\r
763                                         {\r
764                                                 SelectObject(pLVCD->nmcd.hdc, m_boldFont);\r
765                                                 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;\r
766                                         }\r
767 \r
768                                         if(data->m_CommitHash == m_HeadHash)\r
769                                         {\r
770                                                 SelectObject(pLVCD->nmcd.hdc, m_boldFont);\r
771                                                 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;\r
772                                         }\r
773 \r
774 //                                      if ((data->childStackDepth)||(m_mergedRevs.find(data->Rev) != m_mergedRevs.end()))\r
775 //                                              crText = GetSysColor(COLOR_GRAYTEXT);\r
776 //                                      if (data->Rev == m_wcRev)\r
777 //                                      {\r
778 //                                              SelectObject(pLVCD->nmcd.hdc, m_boldFont);\r
779                                                 // We changed the font, so we're returning CDRF_NEWFONT. This\r
780                                                 // tells the control to recalculate the extent of the text.\r
781 //                                              *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;\r
782 //                                      }\r
783                                 }\r
784                         }\r
785                         if (m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec)\r
786                         {\r
787                                 if (m_bStrictStopped)\r
788                                         crText = GetSysColor(COLOR_GRAYTEXT);\r
789                         }\r
790                         // Store the color back in the NMLVCUSTOMDRAW struct.\r
791                         pLVCD->clrText = crText;\r
792                         return;\r
793                 }\r
794                 break;\r
795         case CDDS_ITEMPREPAINT|CDDS_ITEM|CDDS_SUBITEM:\r
796                 {\r
797                         if ((m_bStrictStopped)&&(m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec))\r
798                         {\r
799                                 pLVCD->nmcd.uItemState &= ~(CDIS_SELECTED|CDIS_FOCUS);\r
800                         }\r
801 \r
802                         if (pLVCD->iSubItem == LOGLIST_GRAPH)\r
803                         {\r
804                                 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec && (!this->m_IsRebaseReplaceGraph) )\r
805                                 {\r
806                                         CRect rect;\r
807                                         GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);\r
808                                         \r
809                                         FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);\r
810                                         DrawGraph(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);\r
811 \r
812                                         *pResult = CDRF_SKIPDEFAULT;\r
813                                         return;\r
814                                 \r
815                                 }\r
816                         }\r
817 \r
818                         if (pLVCD->iSubItem == LOGLIST_MESSAGE)\r
819                         {\r
820                                 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)\r
821                                 {\r
822                                         GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);\r
823                                         //if(!data->m_IsFull)\r
824                                         //{\r
825                                                 //if(data->SafeFetchFullInfo(&g_Git))\r
826                                                 //      this->Invalidate();\r
827                                                 //TRACE(_T("Update ... %d\r\n"),pLVCD->nmcd.dwItemSpec);\r
828                                         //}\r
829 \r
830                                         if(m_HashMap[data->m_CommitHash].size()!=0)\r
831                                         {\r
832                                                 CRect rect;\r
833 \r
834                                                 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);\r
835                                         \r
836                                                 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);\r
837                                                 DrawTagBranch(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);\r
838 \r
839                                                 *pResult = CDRF_SKIPDEFAULT;\r
840                                                 return;\r
841 \r
842                                         }\r
843                                 }\r
844                         }\r
845                         \r
846                         if (pLVCD->iSubItem == 1)\r
847                         {\r
848                                 if(this->m_IsIDReplaceAction)\r
849                                 {\r
850                                         *pResult = CDRF_DODEFAULT;\r
851                                         return;\r
852                                 }\r
853                                 *pResult = CDRF_DODEFAULT;\r
854 \r
855                                 if (m_arShownList.GetCount() <= (INT_PTR)pLVCD->nmcd.dwItemSpec)\r
856                                         return;\r
857 \r
858                                 int             nIcons = 0;\r
859                                 int             iconwidth = ::GetSystemMetrics(SM_CXSMICON);\r
860                                 int             iconheight = ::GetSystemMetrics(SM_CYSMICON);\r
861 \r
862                                 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec));\r
863                                 CRect rect;\r
864                                 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);\r
865                                 // Get the selected state of the\r
866                                 // item being drawn.                                                    \r
867 \r
868                                 // Fill the background\r
869                                 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);\r
870                                 \r
871                                 // Draw the icon(s) into the compatible DC\r
872                                 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_MODIFIED)\r
873                                         ::DrawIconEx(pLVCD->nmcd.hdc, rect.left + ICONITEMBORDER, rect.top, m_hModifiedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);\r
874                                 nIcons++;\r
875 \r
876                                 if (pLogEntry->m_Action & (CTGitPath::LOGACTIONS_ADDED|CTGitPath::LOGACTIONS_COPY) )\r
877                                         ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hAddedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);\r
878                                 nIcons++;\r
879 \r
880                                 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_DELETED)\r
881                                         ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hDeletedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);\r
882                                 nIcons++;\r
883 \r
884                                 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_REPLACED)\r
885                                         ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hReplacedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);\r
886                                 nIcons++;\r
887                                 *pResult = CDRF_SKIPDEFAULT;\r
888                                 return;\r
889                         }\r
890                 }\r
891                 break;\r
892         }\r
893         *pResult = CDRF_DODEFAULT;\r
894 \r
895 }\r
896 \r
897 // CGitLogListBase message handlers\r
898 \r
899 void CGitLogListBase::OnLvnGetdispinfoLoglist(NMHDR *pNMHDR, LRESULT *pResult)\r
900 {\r
901         NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);\r
902 \r
903         // Create a pointer to the item\r
904         LV_ITEM* pItem = &(pDispInfo)->item;\r
905 \r
906         // Do the list need text information?\r
907         if (!(pItem->mask & LVIF_TEXT))\r
908                 return;\r
909 \r
910         // By default, clear text buffer.\r
911         lstrcpyn(pItem->pszText, _T(""), pItem->cchTextMax);\r
912 \r
913         bool bOutOfRange = pItem->iItem >= ShownCountWithStopped();\r
914         \r
915         *pResult = 0;\r
916         if (m_bNoDispUpdates || bOutOfRange)\r
917                 return;\r
918 \r
919         // Which item number?\r
920         int itemid = pItem->iItem;\r
921         GitRev * pLogEntry = NULL;\r
922         if (itemid < m_arShownList.GetCount())\r
923                 pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(pItem->iItem));\r
924 \r
925         CString temp;\r
926         if(m_IsOldFirst)\r
927         {\r
928                 temp.Format(_T("%d"),pItem->iItem+1);\r
929 \r
930         }else\r
931         {\r
932                 temp.Format(_T("%d"),m_arShownList.GetCount()-pItem->iItem);\r
933         }\r
934             \r
935         // Which column?\r
936         switch (pItem->iSubItem)\r
937         {\r
938         case this->LOGLIST_GRAPH:       //Graphic\r
939                 if (pLogEntry)\r
940                 {\r
941                         if(this->m_IsRebaseReplaceGraph)\r
942                         {\r
943                                 CTGitPath path;\r
944                                 path.m_Action=pLogEntry->m_Action&CTGitPath::LOGACTIONS_REBASE_MODE_MASK;\r
945 \r
946                                 lstrcpyn(pItem->pszText,path.GetActionName(), pItem->cchTextMax);\r
947                         }\r
948                 }\r
949                 break;\r
950         case this->LOGLIST_ACTION: //action -- no text in the column\r
951                 if(this->m_IsIDReplaceAction)\r
952                         lstrcpyn(pItem->pszText, temp, pItem->cchTextMax);\r
953                 break;\r
954         case this->LOGLIST_MESSAGE: //Message\r
955                 if (pLogEntry)\r
956                         lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_Subject, pItem->cchTextMax);\r
957                 break;\r
958         case this->LOGLIST_AUTHOR: //Author\r
959                 if (pLogEntry)\r
960                         lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_AuthorName, pItem->cchTextMax);\r
961                 break;\r
962         case this->LOGLIST_DATE: //Date\r
963                 if (pLogEntry)\r
964                         lstrcpyn(pItem->pszText,\r
965                                 CAppUtils::FormatDateAndTime( pLogEntry->m_AuthorDate, m_DateFormat, true, m_bRelativeTimes ), \r
966                                 pItem->cchTextMax);\r
967                 break;\r
968                 \r
969         case 5:\r
970 \r
971                 break;\r
972         default:\r
973                 ASSERT(false);\r
974         }\r
975 }\r
976 \r
977 void CGitLogListBase::OnContextMenu(CWnd* pWnd, CPoint point)\r
978 {\r
979 \r
980         int selIndex = GetSelectionMark();\r
981         if (selIndex < 0)\r
982                 return; // nothing selected, nothing to do with a context menu\r
983 \r
984         // if the user selected the info text telling about not all revisions shown due to\r
985         // the "stop on copy/rename" option, we also don't show the context menu\r
986         if ((m_bStrictStopped)&&(selIndex == m_arShownList.GetCount()))\r
987                 return;\r
988 \r
989         // if the context menu is invoked through the keyboard, we have to use\r
990         // a calculated position on where to anchor the menu on\r
991         if ((point.x == -1) && (point.y == -1))\r
992         {\r
993                 CRect rect;\r
994                 GetItemRect(selIndex, &rect, LVIR_LABEL);\r
995                 ClientToScreen(&rect);\r
996                 point = rect.CenterPoint();\r
997         }\r
998         m_nSearchIndex = selIndex;\r
999         m_bCancelled = FALSE;\r
1000 \r
1001         // calculate some information the context menu commands can use\r
1002 //      CString pathURL = GetURLFromPath(m_path);\r
1003 \r
1004         POSITION pos = GetFirstSelectedItemPosition();\r
1005         int indexNext = GetNextSelectedItem(pos);\r
1006         if (indexNext < 0)\r
1007                 return;\r
1008 \r
1009         GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));\r
1010 #if 0\r
1011         GitRev revSelected = pSelLogEntry->Rev;\r
1012         GitRev revPrevious = git_revnum_t(revSelected)-1;\r
1013         if ((pSelLogEntry->pArChangedPaths)&&(pSelLogEntry->pArChangedPaths->GetCount() <= 2))\r
1014         {\r
1015                 for (int i=0; i<pSelLogEntry->pArChangedPaths->GetCount(); ++i)\r
1016                 {\r
1017                         LogChangedPath * changedpath = (LogChangedPath *)pSelLogEntry->pArChangedPaths->GetAt(i);\r
1018                         if (changedpath->lCopyFromRev)\r
1019                                 revPrevious = changedpath->lCopyFromRev;\r
1020                 }\r
1021         }\r
1022         GitRev revSelected2;\r
1023         if (pos)\r
1024         {\r
1025                 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));\r
1026                 revSelected2 = pLogEntry->Rev;\r
1027         }\r
1028         bool bAllFromTheSameAuthor = true;\r
1029         CString firstAuthor;\r
1030         CLogDataVector selEntries;\r
1031         GitRev revLowest, revHighest;\r
1032         GitRevRangeArray revisionRanges;\r
1033         {\r
1034                 POSITION pos = GetFirstSelectedItemPosition();\r
1035                 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));\r
1036                 revisionRanges.AddRevision(pLogEntry->Rev);\r
1037                 selEntries.push_back(pLogEntry);\r
1038                 firstAuthor = pLogEntry->sAuthor;\r
1039                 revLowest = pLogEntry->Rev;\r
1040                 revHighest = pLogEntry->Rev;\r
1041                 while (pos)\r
1042                 {\r
1043                         pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));\r
1044                         revisionRanges.AddRevision(pLogEntry->Rev);\r
1045                         selEntries.push_back(pLogEntry);\r
1046                         if (firstAuthor.Compare(pLogEntry->sAuthor))\r
1047                                 bAllFromTheSameAuthor = false;\r
1048                         revLowest = (git_revnum_t(pLogEntry->Rev) > git_revnum_t(revLowest) ? revLowest : pLogEntry->Rev);\r
1049                         revHighest = (git_revnum_t(pLogEntry->Rev) < git_revnum_t(revHighest) ? revHighest : pLogEntry->Rev);\r
1050                 }\r
1051         }\r
1052 \r
1053 #endif\r
1054 \r
1055         int FirstSelect=-1, LastSelect=-1;\r
1056         pos = GetFirstSelectedItemPosition();\r
1057         FirstSelect = GetNextSelectedItem(pos);\r
1058         while(pos)\r
1059         {\r
1060                 LastSelect = GetNextSelectedItem(pos);\r
1061         }\r
1062         //entry is selected, now show the popup menu\r
1063         CIconMenu popup;\r
1064         CIconMenu submenu;\r
1065         if (popup.CreatePopupMenu())\r
1066         {\r
1067 \r
1068                 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_PICK))\r
1069                         popup.AppendMenuIcon(ID_REBASE_PICK,  IDS_REBASE_SKIP,   IDI_PICK);\r
1070 \r
1071                 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_SQUASH))\r
1072                         popup.AppendMenuIcon(ID_REBASE_SQUASH,IDS_REBASE_SQUASH, IDI_SQUASH);\r
1073 \r
1074                 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_EDIT))\r
1075                         popup.AppendMenuIcon(ID_REBASE_EDIT,  IDS_REBASE_EDIT,   IDI_EDIT);\r
1076 \r
1077                 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_SKIP))\r
1078                         popup.AppendMenuIcon(ID_REBASE_SKIP,  IDS_REBASE_SKIP,   IDI_SKIP);\r
1079                 \r
1080                 if(m_ContextMenuMask&(GetContextMenuBit(ID_REBASE_SKIP)|GetContextMenuBit(ID_REBASE_EDIT)|\r
1081                               GetContextMenuBit(ID_REBASE_SQUASH)|GetContextMenuBit(ID_REBASE_PICK)))\r
1082                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
1083 \r
1084                 if (GetSelectedCount() == 1)\r
1085                 {\r
1086                         {\r
1087                                 if (m_hasWC)\r
1088                                 {\r
1089                                         if(m_ContextMenuMask&GetContextMenuBit(ID_COMPARE))\r
1090                                                 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);\r
1091                                         // TODO:\r
1092                                         // TortoiseMerge could be improved to take a /blame switch\r
1093                                         // and then not 'cat' the files from a unified diff but\r
1094                                         // blame then.\r
1095                                         // But until that's implemented, the context menu entry for\r
1096                                         // this feature is commented out.\r
1097                                         //popup.AppendMenu(ID_BLAMECOMPARE, IDS_LOG_POPUP_BLAMECOMPARE, IDI_BLAME);\r
1098                                 }\r
1099                                 if(m_ContextMenuMask&GetContextMenuBit(ID_GNUDIFF1))\r
1100                                         popup.AppendMenuIcon(ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);\r
1101 \r
1102                                 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPAREWITHPREVIOUS))\r
1103                                         popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF);\r
1104                                 //popup.AppendMenuIcon(ID_BLAMEWITHPREVIOUS, IDS_LOG_POPUP_BLAMEWITHPREVIOUS, IDI_BLAME);\r
1105                                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
1106                         }\r
1107 \r
1108 //                      if (!m_ProjectProperties.sWebViewerRev.IsEmpty())\r
1109 //                      {\r
1110 //                              popup.AppendMenuIcon(ID_VIEWREV, IDS_LOG_POPUP_VIEWREV);\r
1111 //                      }\r
1112 //                      if (!m_ProjectProperties.sWebViewerPathRev.IsEmpty())\r
1113 //                      {\r
1114 //                              popup.AppendMenuIcon(ID_VIEWPATHREV, IDS_LOG_POPUP_VIEWPATHREV);\r
1115 //                      }\r
1116 //                      if ((!m_ProjectProperties.sWebViewerPathRev.IsEmpty())||\r
1117 //                              (!m_ProjectProperties.sWebViewerRev.IsEmpty()))\r
1118 //                      {\r
1119 //                              popup.AppendMenu(MF_SEPARATOR, NULL);\r
1120 //                      }\r
1121 \r
1122                         //if (m_hasWC)\r
1123                         //      popup.AppendMenuIcon(ID_REVERTTOREV, IDS_LOG_POPUP_REVERTTOREV, IDI_REVERT);\r
1124                         //if (m_hasWC)\r
1125                         //      popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREV, IDI_REVERT);\r
1126                         //if (m_hasWC)\r
1127                         //      popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREV, IDI_MERGE);\r
1128                         \r
1129                         CString str,format;\r
1130                         format.LoadString(IDS_RESET_TO_THIS_FORMAT);\r
1131                         str.Format(format,g_Git.GetCurrentBranch());\r
1132 \r
1133                         if(m_ContextMenuMask&GetContextMenuBit(ID_RESET))\r
1134                                 popup.AppendMenuIcon(ID_RESET,str,IDI_REVERT);\r
1135 \r
1136                         if(m_ContextMenuMask&GetContextMenuBit(ID_SWITCHTOREV))\r
1137                                 popup.AppendMenuIcon(ID_SWITCHTOREV, IDS_SWITCH_TO_THIS , IDI_SWITCH);\r
1138 \r
1139                         if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_BRANCH))\r
1140                                 popup.AppendMenuIcon(ID_CREATE_BRANCH, IDS_CREATE_BRANCH_AT_THIS , IDI_COPY);\r
1141 \r
1142                         if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_TAG))\r
1143                                 popup.AppendMenuIcon(ID_CREATE_TAG,IDS_CREATE_TAG_AT_THIS , IDI_COPY);\r
1144                         \r
1145                         format.LoadString(IDS_REBASE_THIS_FORMAT);\r
1146                         str.Format(format,g_Git.GetCurrentBranch());\r
1147 \r
1148                         if(pSelLogEntry->m_CommitHash != m_HeadHash)\r
1149                                 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_TO_VERSION))\r
1150                                         popup.AppendMenuIcon(ID_REBASE_TO_VERSION, str , IDI_REBASE);                   \r
1151 \r
1152                         if(m_ContextMenuMask&GetContextMenuBit(ID_EXPORT))\r
1153                                 popup.AppendMenuIcon(ID_EXPORT,IDS_EXPORT_TO_THIS, IDI_EXPORT); \r
1154                         \r
1155 \r
1156                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
1157 \r
1158                 }\r
1159 \r
1160                 if(!pSelLogEntry->m_Ref.IsEmpty() && GetSelectedCount() == 1)\r
1161                 {\r
1162                         popup.AppendMenuIcon(ID_REFLOG_DEL, IDS_REFLOG_DEL,     IDI_DELETE);    \r
1163                         popup.AppendMenuIcon(ID_STASH_APPLY,IDS_MENUSTASHAPPLY, IDI_RELOCATE);  \r
1164                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
1165                 }\r
1166         \r
1167                 if (GetSelectedCount() >= 2)\r
1168                 {\r
1169                         bool bAddSeparator = false;\r
1170                         if (IsSelectionContinuous() || (GetSelectedCount() == 2))\r
1171                         {\r
1172                                 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPARETWO))\r
1173                                         popup.AppendMenuIcon(ID_COMPARETWO, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);\r
1174                         }\r
1175 \r
1176                         if (GetSelectedCount() == 2)\r
1177                         {\r
1178                                 //popup.AppendMenuIcon(ID_BLAMETWO, IDS_LOG_POPUP_BLAMEREVS, IDI_BLAME);\r
1179                                 if(m_ContextMenuMask&GetContextMenuBit(ID_GNUDIFF2))\r
1180                                         popup.AppendMenuIcon(ID_GNUDIFF2, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);\r
1181                                 bAddSeparator = true;\r
1182                         }\r
1183 \r
1184                         if (m_hasWC)\r
1185                         {\r
1186                                 //popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREVS, IDI_REVERT);\r
1187 //                              if (m_hasWC)\r
1188 //                                      popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREVS, IDI_MERGE);\r
1189                                 bAddSeparator = true;\r
1190                         }\r
1191                         if (bAddSeparator)\r
1192                                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
1193                 }\r
1194 \r
1195                 if ( GetSelectedCount() >0 )\r
1196                 {\r
1197                         if ( IsSelectionContinuous() && GetSelectedCount() >= 2 )\r
1198                         {\r
1199                                 if(m_ContextMenuMask&GetContextMenuBit(ID_COMBINE_COMMIT))\r
1200                                 {\r
1201                                         CString head;\r
1202                                         int headindex;\r
1203                                         headindex = this->GetHeadIndex();\r
1204                                         if(headindex>=0)\r
1205                                         {\r
1206                                                 head.Format(_T("HEAD~%d"),LastSelect-headindex);\r
1207                                                 CString hash=g_Git.GetHash(head);\r
1208                                                 hash=hash.Left(40);\r
1209                                                 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));\r
1210                                                 if(pLastEntry->m_CommitHash == hash)\r
1211                                                         popup.AppendMenuIcon(ID_COMBINE_COMMIT,IDS_COMBINE_TO_ONE,IDI_COMBINE);\r
1212                                         }\r
1213                                 }\r
1214                         }\r
1215                         if(m_ContextMenuMask&GetContextMenuBit(ID_CHERRY_PICK))\r
1216                                 popup.AppendMenuIcon(ID_CHERRY_PICK, IDS_CHERRY_PICK_VERSION, IDI_EXPORT);\r
1217 \r
1218                         if(GetSelectedCount()<=2)\r
1219                                 if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_PATCH))\r
1220                                         popup.AppendMenuIcon(ID_CREATE_PATCH, IDS_CREATE_PATCH, IDI_PATCH);\r
1221                         \r
1222                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
1223         \r
1224                 }\r
1225 \r
1226                 \r
1227 #if 0\r
1228 //              if ((selEntries.size() > 0)&&(bAllFromTheSameAuthor))\r
1229 //              {\r
1230 //                      popup.AppendMenuIcon(ID_EDITAUTHOR, IDS_LOG_POPUP_EDITAUTHOR);\r
1231 //              }\r
1232 //              if (GetSelectedCount() == 1)\r
1233 //              {\r
1234 //                      popup.AppendMenuIcon(ID_EDITLOG, IDS_LOG_POPUP_EDITLOG);\r
1235 //                      popup.AppendMenuIcon(ID_REVPROPS, IDS_REPOBROWSE_SHOWREVPROP, IDI_PROPERTIES); // "Show Revision Properties"\r
1236 //                      popup.AppendMenu(MF_SEPARATOR, NULL);\r
1237 //              }\r
1238 #endif\r
1239 \r
1240                 \r
1241                 if (GetSelectedCount() == 1)\r
1242                 {\r
1243                         if(m_ContextMenuMask&GetContextMenuBit(ID_COPYHASH))\r
1244                                 popup.AppendMenuIcon(ID_COPYHASH, IDS_COPY_COMMIT_HASH);\r
1245                 }\r
1246                 if (GetSelectedCount() != 0)\r
1247                 {\r
1248                         if(m_ContextMenuMask&GetContextMenuBit(ID_COPYCLIPBOARD))\r
1249                                 popup.AppendMenuIcon(ID_COPYCLIPBOARD, IDS_LOG_POPUP_COPYTOCLIPBOARD);\r
1250                 }\r
1251 \r
1252                 if(m_ContextMenuMask&GetContextMenuBit(ID_FINDENTRY))\r
1253                         popup.AppendMenuIcon(ID_FINDENTRY, IDS_LOG_POPUP_FIND);\r
1254 \r
1255 \r
1256                 if (GetSelectedCount() == 1)\r
1257                 {\r
1258                         if(m_ContextMenuMask &GetContextMenuBit(ID_DELETE))\r
1259                         {\r
1260                                 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) != m_HashMap.end() )\r
1261                                 {\r
1262                                         CString str;\r
1263                                         str.LoadString(IDS_DELETE_BRANCHTAG);\r
1264                                         if( m_HashMap[pSelLogEntry->m_CommitHash].size() == 1 )\r
1265                                         {\r
1266                                                 str+=_T(" ");\r
1267                                                 str+=m_HashMap[pSelLogEntry->m_CommitHash].at(0);\r
1268                                                 popup.AppendMenuIcon(ID_DELETE,str+_T("..."),IDI_DELETE);\r
1269                                         }\r
1270                                         else if( m_HashMap[pSelLogEntry->m_CommitHash].size() > 1 )\r
1271                                         {\r
1272                                                 \r
1273                                                 submenu.CreatePopupMenu();\r
1274                                                 for(int i=0;i<m_HashMap[pSelLogEntry->m_CommitHash].size();i++)\r
1275                                                 {\r
1276                                                         submenu.AppendMenuIcon(ID_DELETE+(i<<16),m_HashMap[pSelLogEntry->m_CommitHash][i]+_T("..."));\r
1277                                                 }\r
1278 \r
1279                                                 popup.AppendMenu(MF_BYPOSITION|MF_POPUP|MF_STRING,(UINT) submenu.m_hMenu,str); \r
1280 \r
1281                                         }\r
1282                                         \r
1283                                 }\r
1284                         }\r
1285                 }\r
1286 \r
1287                 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
1288 //              DialogEnableWindow(IDOK, FALSE);\r
1289 //              SetPromptApp(&theApp);\r
1290         \r
1291                 this->ContextMenuAction(cmd, FirstSelect, LastSelect);\r
1292                 \r
1293 //              EnableOKButton();\r
1294         } // if (popup.CreatePopupMenu())\r
1295 \r
1296 }\r
1297 \r
1298 bool CGitLogListBase::IsSelectionContinuous()\r
1299 {\r
1300         if ( GetSelectedCount()==1 )\r
1301         {\r
1302                 // if only one revision is selected, the selection is of course\r
1303                 // continuous\r
1304                 return true;\r
1305         }\r
1306 \r
1307         POSITION pos = GetFirstSelectedItemPosition();\r
1308         bool bContinuous = (m_arShownList.GetCount() == (INT_PTR)m_logEntries.size());\r
1309         if (bContinuous)\r
1310         {\r
1311                 int itemindex = GetNextSelectedItem(pos);\r
1312                 while (pos)\r
1313                 {\r
1314                         int nextindex = GetNextSelectedItem(pos);\r
1315                         if (nextindex - itemindex > 1)\r
1316                         {\r
1317                                 bContinuous = false;\r
1318                                 break;\r
1319                         }\r
1320                         itemindex = nextindex;\r
1321                 }\r
1322         }\r
1323         return bContinuous;\r
1324 }\r
1325 \r
1326 void CGitLogListBase::CopySelectionToClipBoard(bool HashOnly)\r
1327 {\r
1328 \r
1329         CString sClipdata;\r
1330         POSITION pos = GetFirstSelectedItemPosition();\r
1331         if (pos != NULL)\r
1332         {\r
1333                 CString sRev;\r
1334                 sRev.LoadString(IDS_LOG_REVISION);\r
1335                 CString sAuthor;\r
1336                 sAuthor.LoadString(IDS_LOG_AUTHOR);\r
1337                 CString sDate;\r
1338                 sDate.LoadString(IDS_LOG_DATE);\r
1339                 CString sMessage;\r
1340                 sMessage.LoadString(IDS_LOG_MESSAGE);\r
1341                 while (pos)\r
1342                 {\r
1343                         CString sLogCopyText;\r
1344                         CString sPaths;\r
1345                         GitRev * pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.GetAt(GetNextSelectedItem(pos)));\r
1346 \r
1347                         if(!HashOnly)\r
1348                         {\r
1349                                 //pLogEntry->m_Files\r
1350                                 //LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;\r
1351                         \r
1352                                 for (int cpPathIndex = 0; cpPathIndex<pLogEntry->m_Files.GetCount(); ++cpPathIndex)\r
1353                                 {\r
1354                                         sPaths += ((CTGitPath&)pLogEntry->m_Files[cpPathIndex]).GetActionName() + _T(" : ") + pLogEntry->m_Files[cpPathIndex].GetGitPathString();\r
1355                                         sPaths += _T("\r\n");\r
1356                                 }\r
1357                                 sPaths.Trim();\r
1358                                 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
1359                                         (LPCTSTR)sRev, pLogEntry->m_CommitHash,\r
1360                                         (LPCTSTR)sAuthor, (LPCTSTR)pLogEntry->m_AuthorName,\r
1361                                         (LPCTSTR)sDate, \r
1362                                         (LPCTSTR)CAppUtils::FormatDateAndTime( pLogEntry->m_AuthorDate, m_DateFormat, true, m_bRelativeTimes ),\r
1363                                         (LPCTSTR)sMessage, pLogEntry->m_Subject+_T("\r\n")+pLogEntry->m_Body,\r
1364                                         (LPCTSTR)sPaths);\r
1365                                 sClipdata +=  sLogCopyText;\r
1366                         }else\r
1367                         {\r
1368                                 sClipdata += pLogEntry->m_CommitHash;\r
1369                                 break;\r
1370                         }\r
1371 \r
1372                 }\r
1373                 CStringUtils::WriteAsciiStringToClipboard(sClipdata, GetSafeHwnd());\r
1374         }\r
1375 \r
1376 }\r
1377 \r
1378 void CGitLogListBase::DiffSelectedRevWithPrevious()\r
1379 {\r
1380         if (m_bThreadRunning)\r
1381                 return;\r
1382 \r
1383         int FirstSelect=-1, LastSelect=-1;\r
1384         POSITION pos = GetFirstSelectedItemPosition();\r
1385         FirstSelect = GetNextSelectedItem(pos);\r
1386         while(pos)\r
1387         {\r
1388                 LastSelect = GetNextSelectedItem(pos);\r
1389         }\r
1390 \r
1391         ContextMenuAction(ID_COMPAREWITHPREVIOUS,FirstSelect,LastSelect);\r
1392 \r
1393 #if 0\r
1394         UpdateLogInfoLabel();\r
1395         int selIndex = m_LogList.GetSelectionMark();\r
1396         if (selIndex < 0)\r
1397                 return;\r
1398         int selCount = m_LogList.GetSelectedCount();\r
1399         if (selCount != 1)\r
1400                 return;\r
1401 \r
1402         // Find selected entry in the log list\r
1403         POSITION pos = m_LogList.GetFirstSelectedItemPosition();\r
1404         PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));\r
1405         long rev1 = pLogEntry->Rev;\r
1406         long rev2 = rev1-1;\r
1407         CTGitPath path = m_path;\r
1408 \r
1409         // See how many files under the relative root were changed in selected revision\r
1410         int nChanged = 0;\r
1411         LogChangedPath * changed = NULL;\r
1412         for (INT_PTR c = 0; c < pLogEntry->pArChangedPaths->GetCount(); ++c)\r
1413         {\r
1414                 LogChangedPath * cpath = pLogEntry->pArChangedPaths->GetAt(c);\r
1415                 if (cpath  &&  cpath -> sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)\r
1416                 {\r
1417                         ++nChanged;\r
1418                         changed = cpath;\r
1419                 }\r
1420         }\r
1421 \r
1422         if (m_path.IsDirectory() && nChanged == 1) \r
1423         {\r
1424                 // We're looking at the log for a directory and only one file under dir was changed in the revision\r
1425                 // Do diff on that file instead of whole directory\r
1426                 path.AppendPathString(changed->sPath.Mid(m_sRelativeRoot.GetLength()));\r
1427         } \r
1428 \r
1429         m_bCancelled = FALSE;\r
1430         DialogEnableWindow(IDOK, FALSE);\r
1431         SetPromptApp(&theApp);\r
1432         theApp.DoWaitCursor(1);\r
1433 \r
1434         if (PromptShown())\r
1435         {\r
1436                 GitDiff diff(this, m_hWnd, true);\r
1437                 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
1438                 diff.SetHEADPeg(m_LogRevision);\r
1439                 diff.ShowCompare(path, rev2, path, rev1);\r
1440         }\r
1441         else\r
1442         {\r
1443                 CAppUtils::StartShowCompare(m_hWnd, path, rev2, path, rev1, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
1444         }\r
1445 \r
1446         theApp.DoWaitCursor(-1);\r
1447         EnableOKButton();\r
1448 #endif\r
1449 }\r
1450 \r
1451 void CGitLogListBase::OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult)\r
1452 {\r
1453         LPNMLVFINDITEM pFindInfo = reinterpret_cast<LPNMLVFINDITEM>(pNMHDR);\r
1454         *pResult = -1;\r
1455         \r
1456         if (pFindInfo->lvfi.flags & LVFI_PARAM)\r
1457                 return; \r
1458         if ((pFindInfo->iStart < 0)||(pFindInfo->iStart >= m_arShownList.GetCount()))\r
1459                 return;\r
1460         if (pFindInfo->lvfi.psz == 0)\r
1461                 return;\r
1462 #if 0\r
1463         CString sCmp = pFindInfo->lvfi.psz;\r
1464         CString sRev;   \r
1465         for (int i=pFindInfo->iStart; i<m_arShownList.GetCount(); ++i)\r
1466         {\r
1467                 GitRev * pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));\r
1468                 sRev.Format(_T("%ld"), pLogEntry->Rev);\r
1469                 if (pFindInfo->lvfi.flags & LVFI_PARTIAL)\r
1470                 {\r
1471                         if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)\r
1472                         {\r
1473                                 *pResult = i;\r
1474                                 return;\r
1475                         }\r
1476                 }\r
1477                 else\r
1478                 {\r
1479                         if (sCmp.Compare(sRev)==0)\r
1480                         {\r
1481                                 *pResult = i;\r
1482                                 return;\r
1483                         }\r
1484                 }\r
1485         }\r
1486         if (pFindInfo->lvfi.flags & LVFI_WRAP)\r
1487         {\r
1488                 for (int i=0; i<pFindInfo->iStart; ++i)\r
1489                 {\r
1490                         PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(i));\r
1491                         sRev.Format(_T("%ld"), pLogEntry->Rev);\r
1492                         if (pFindInfo->lvfi.flags & LVFI_PARTIAL)\r
1493                         {\r
1494                                 if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)\r
1495                                 {\r
1496                                         *pResult = i;\r
1497                                         return;\r
1498                                 }\r
1499                         }\r
1500                         else\r
1501                         {\r
1502                                 if (sCmp.Compare(sRev)==0)\r
1503                                 {\r
1504                                         *pResult = i;\r
1505                                         return;\r
1506                                 }\r
1507                         }\r
1508                 }\r
1509         }\r
1510 #endif\r
1511         *pResult = -1;\r
1512 }\r
1513 \r
1514 int CGitLogListBase::FillGitLog(CTGitPath *path,int info,CString *from,CString *to)\r
1515 {\r
1516         ClearText();\r
1517 \r
1518         this->m_logEntries.ClearAll();\r
1519         this->m_logEntries.ParserFromLog(path,-1,info,from,to);\r
1520 \r
1521         //this->m_logEntries.ParserFromLog();\r
1522         SetItemCountEx(this->m_logEntries.size());\r
1523 \r
1524         this->m_arShownList.RemoveAll();\r
1525 \r
1526         for(unsigned int i=0;i<m_logEntries.size();i++)\r
1527         {\r
1528                 if(m_IsOldFirst)\r
1529                 {\r
1530                         m_logEntries[m_logEntries.size()-i-1].m_IsFull=TRUE;\r
1531                         this->m_arShownList.Add(&m_logEntries[m_logEntries.size()-i-1]);\r
1532                 \r
1533                 }else\r
1534                 {\r
1535                         m_logEntries[i].m_IsFull=TRUE;\r
1536                         this->m_arShownList.Add(&m_logEntries[i]);\r
1537                 }\r
1538         }\r
1539 \r
1540     if(path)\r
1541         m_Path=*path;\r
1542         return 0;\r
1543 \r
1544 }\r
1545 \r
1546 int CGitLogListBase::FillGitShortLog()\r
1547 {\r
1548         ClearText();\r
1549 \r
1550         this->m_logEntries.ClearAll();\r
1551 \r
1552         m_LogCache.FetchCacheIndex(g_Git.m_CurrentDir);\r
1553 \r
1554     CTGitPath *path;\r
1555     if(this->m_Path.IsEmpty())\r
1556         path=NULL;\r
1557     else\r
1558         path=&this->m_Path;\r
1559 \r
1560         CString hash;\r
1561         int mask;\r
1562         mask = CGit::LOG_INFO_ONLY_HASH | CGit::LOG_INFO_BOUNDARY;\r
1563 //      if(this->m_bAllBranch)\r
1564         mask |= m_ShowMask;\r
1565 \r
1566         this->m_logEntries.FetchShortLog(path,m_StartRef,-1,mask);\r
1567         \r
1568         //this->m_logEntries.ParserFromLog();\r
1569         if(IsInWorkingThread())\r
1570                 PostMessage(LVM_SETITEMCOUNT, (WPARAM) this->m_logEntries.size(),(LPARAM) LVSICF_NOINVALIDATEALL);\r
1571         else\r
1572                 SetItemCountEx(this->m_logEntries.size());\r
1573 \r
1574         this->m_arShownList.RemoveAll();\r
1575 \r
1576         for(unsigned int i=0;i<m_logEntries.size();i++)\r
1577         {\r
1578                 m_logEntries[i].m_Subject=_T("parser...");\r
1579                 if(this->m_IsOldFirst)\r
1580                 {\r
1581                         this->m_arShownList.Add(&m_logEntries[m_logEntries.size()-1-i]);\r
1582 \r
1583                 }else\r
1584                 {\r
1585                         this->m_arShownList.Add(&m_logEntries[i]);\r
1586                 }\r
1587         }\r
1588         return 0;\r
1589 }\r
1590 \r
1591 BOOL CGitLogListBase::PreTranslateMessage(MSG* pMsg)\r
1592 {\r
1593         // Skip Ctrl-C when copying text out of the log message or search filter\r
1594         BOOL bSkipAccelerator = ( pMsg->message == WM_KEYDOWN && pMsg->wParam=='C' && (GetFocus()==GetDlgItem(IDC_MSGVIEW) || GetFocus()==GetDlgItem(IDC_SEARCHEDIT) ) && GetKeyState(VK_CONTROL)&0x8000 );\r
1595         if (pMsg->message == WM_KEYDOWN && pMsg->wParam=='\r')\r
1596         {\r
1597                 //if (GetFocus()==GetDlgItem(IDC_LOGLIST))\r
1598                 {\r
1599                         if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))\r
1600                         {\r
1601                                 DiffSelectedRevWithPrevious();\r
1602                                 return TRUE;\r
1603                         }\r
1604                 }\r
1605 #if 0\r
1606                 if (GetFocus()==GetDlgItem(IDC_LOGMSG))\r
1607                 {\r
1608                         DiffSelectedFile();\r
1609                         return TRUE;\r
1610                 }\r
1611 #endif\r
1612         }\r
1613 \r
1614 #if 0\r
1615         if (m_hAccel && !bSkipAccelerator)\r
1616         {\r
1617                 int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);\r
1618                 if (ret)\r
1619                         return TRUE;\r
1620         }\r
1621         \r
1622 #endif\r
1623         //m_tooltips.RelayEvent(pMsg);\r
1624         return __super::PreTranslateMessage(pMsg);\r
1625 }\r
1626 \r
1627 void CGitLogListBase::OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult)\r
1628 {\r
1629         // a double click on an entry in the revision list has happened\r
1630         *pResult = 0;\r
1631 \r
1632         if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))\r
1633                 DiffSelectedRevWithPrevious();\r
1634 }\r
1635 \r
1636 int CGitLogListBase::FetchLogAsync(void * data)\r
1637 {\r
1638         m_ProcData=data;\r
1639         m_bExitThread=FALSE;\r
1640         InterlockedExchange(&m_bThreadRunning, TRUE);\r
1641         InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
1642         m_LoadingThread = AfxBeginThread(LogThreadEntry, this);\r
1643         if (m_LoadingThread ==NULL)\r
1644         {\r
1645                 InterlockedExchange(&m_bThreadRunning, FALSE);\r
1646                 InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
1647                 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
1648                 return -1;\r
1649         }\r
1650         return 0;\r
1651 }\r
1652 \r
1653 //this is the thread function which calls the subversion function\r
1654 UINT CGitLogListBase::LogThreadEntry(LPVOID pVoid)\r
1655 {\r
1656         return ((CGitLogListBase*)pVoid)->LogThread();\r
1657 }\r
1658 \r
1659 void CGitLogListBase::GetTimeRange(CTime &oldest, CTime &latest)\r
1660 {\r
1661         //CTime time;\r
1662         oldest=CTime::GetCurrentTime();\r
1663         latest=CTime(1971,1,2,0,0,0);\r
1664         for(unsigned int i=0;i<m_logEntries.size();i++)\r
1665         {\r
1666                 if(m_logEntries[i].m_AuthorDate.GetTime() < oldest.GetTime())\r
1667                         oldest = m_logEntries[i].m_AuthorDate.GetTime();\r
1668 \r
1669                 if(m_logEntries[i].m_AuthorDate.GetTime() > latest.GetTime())\r
1670                         latest = m_logEntries[i].m_AuthorDate.GetTime();\r
1671 \r
1672         }\r
1673 }\r
1674 \r
1675 //Helper class for FetchFullLogInfo()\r
1676 class CGitCall_FetchFullLogInfo : public CGitCall\r
1677 {\r
1678 public:\r
1679         CGitCall_FetchFullLogInfo(CGitLogListBase* ploglist):m_ploglist(ploglist),m_CollectedCount(0){}\r
1680         virtual bool OnOutputData(const BYTE* data, size_t size)\r
1681         {\r
1682                 if(size==0)\r
1683                         return m_ploglist->m_bExitThread;\r
1684                 //Add received data to byte collector\r
1685                 m_ByteCollector.append(data,size);\r
1686 \r
1687                 //Find loginfo endmarker\r
1688                 static const BYTE dataToFind[]={0,0,'#','<'};\r
1689                 int found=m_ByteCollector.findData(dataToFind,4);\r
1690                 if(found<0)\r
1691                         return m_ploglist->m_bExitThread;//Not found\r
1692                 found+=2;//Position after loginfo end-marker\r
1693 \r
1694                 //Prepare data for OnLogInfo and call it\r
1695                 BYTE_VECTOR logInfo;\r
1696                 logInfo.append(&*m_ByteCollector.begin(),found);\r
1697                 OnLogInfo(logInfo);\r
1698 \r
1699                 //Remove loginfo from bytecollector\r
1700                 m_ByteCollector.erase(m_ByteCollector.begin(),m_ByteCollector.begin()+found);\r
1701 \r
1702                 return m_ploglist->m_bExitThread;\r
1703         }\r
1704         virtual void OnEnd()\r
1705         {\r
1706                 //Rest should be a complete log line.\r
1707                 if(!m_ByteCollector.empty())\r
1708                         OnLogInfo(m_ByteCollector);\r
1709         }\r
1710 \r
1711 \r
1712         void OnLogInfo(BYTE_VECTOR& logInfo)\r
1713         {\r
1714                 GitRev fullRev;\r
1715                 fullRev.ParserFromLog(logInfo);\r
1716                 MAP_HASH_REV::iterator itRev=m_ploglist->m_logEntries.m_HashMap.find(fullRev.m_CommitHash);\r
1717                 if(itRev==m_ploglist->m_logEntries.m_HashMap.end())\r
1718                 {\r
1719                         //Should not occur, only when Git-tree updated in the mean time. (Race condition)\r
1720                         return;//Ignore\r
1721                 }\r
1722                 //Set updating\r
1723                 int rev=itRev->second;\r
1724                 GitRev* revInVector=&m_ploglist->m_logEntries[rev];\r
1725 \r
1726 \r
1727                 if(revInVector->m_IsFull)\r
1728                         return;\r
1729 \r
1730                 if(!m_ploglist->m_LogCache.GetCacheData(m_ploglist->m_logEntries[rev]))\r
1731                 {\r
1732                         ++m_CollectedCount;\r
1733                         InterlockedExchange(&m_ploglist->m_logEntries[rev].m_IsUpdateing,FALSE);\r
1734                         InterlockedExchange(&m_ploglist->m_logEntries[rev].m_IsFull,TRUE);\r
1735                         ::PostMessage(m_ploglist->m_hWnd,MSG_LOADED,(WPARAM)rev,0);\r
1736                         return;\r
1737                 }\r
1738 \r
1739 //              fullRev.m_IsUpdateing=TRUE;\r
1740 //              fullRev.m_IsFull=TRUE;\r
1741         \r
1742 \r
1743                 if(InterlockedExchange(&revInVector->m_IsUpdateing,TRUE))\r
1744                         return;//Cannot update this row now. Ignore.\r
1745                 TCHAR oldmark=revInVector->m_Mark;\r
1746                 GIT_REV_LIST oldlist=revInVector->m_ParentHash;\r
1747 //              CString oldhash=m_CommitHash;\r
1748 \r
1749                 //Parse new rev info\r
1750                 revInVector->ParserFromLog(logInfo);\r
1751 \r
1752                 if(oldmark!=0)\r
1753                         revInVector->m_Mark=oldmark;  //parser full log will cause old mark overwrited. \r
1754                                                                //So we need keep old bound mark.\r
1755                 revInVector->m_ParentHash=oldlist;\r
1756 \r
1757                 //update cache\r
1758                 m_ploglist->m_LogCache.AddCacheEntry(*revInVector);\r
1759 \r
1760                 //Reset updating\r
1761                 InterlockedExchange(&revInVector->m_IsFull,TRUE);\r
1762                 InterlockedExchange(&revInVector->m_IsUpdateing,FALSE);\r
1763 \r
1764                 //Notify listcontrol and update progress bar\r
1765                 ++m_CollectedCount;\r
1766 \r
1767                 ::PostMessage(m_ploglist->m_hWnd,MSG_LOADED,(WPARAM)rev,0);\r
1768 \r
1769                 DWORD percent=m_CollectedCount*68/m_ploglist->m_logEntries.size() + GITLOG_START+1+30;\r
1770                 if(percent == GITLOG_END)\r
1771                         percent = GITLOG_END -1;\r
1772                 \r
1773                 ::PostMessage(m_ploglist->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) percent,0);\r
1774         }\r
1775 \r
1776         CGitLogListBase*        m_ploglist;\r
1777         BYTE_VECTOR                     m_ByteCollector;\r
1778         int                                     m_CollectedCount;\r
1779 \r
1780 };\r
1781 \r
1782 void CGitLogListBase::FetchFullLogInfo(CString &from, CString &to)\r
1783 {\r
1784         CGitCall_FetchFullLogInfo fetcher(this);\r
1785         int mask=\r
1786                 CGit::LOG_INFO_STAT|\r
1787                 CGit::LOG_INFO_FILESTATE|\r
1788                 CGit::LOG_INFO_DETECT_COPYRENAME|\r
1789                 CGit::LOG_INFO_SHOW_MERGEDFILE |\r
1790                 m_ShowMask;\r
1791 \r
1792         CTGitPath *path;\r
1793     if(this->m_Path.IsEmpty())\r
1794         path=NULL;\r
1795     else\r
1796         path=&this->m_Path;\r
1797 \r
1798         g_Git.GetLog(&fetcher,CString(),path,-1,mask,&from,&to);\r
1799 }\r
1800 \r
1801 void CGitLogListBase::FetchLastLogInfo()\r
1802 {\r
1803         unsigned int updated=0;\r
1804         int percent=0;\r
1805         CRect rect;\r
1806         {\r
1807                 for(unsigned int i=0;i<m_logEntries.size();i++)\r
1808                 {\r
1809                         if(m_logEntries[i].m_IsFull)\r
1810                                 continue;\r
1811 \r
1812                         if(m_LogCache.GetCacheData(m_logEntries[i]))\r
1813                         {\r
1814                                 if(!m_logEntries.FetchFullInfo(i))\r
1815                                 {\r
1816                                         updated++;\r
1817                                 }\r
1818                                 m_LogCache.AddCacheEntry(m_logEntries[i]);\r
1819 \r
1820                         }else\r
1821                         {\r
1822                                 updated++;\r
1823                                 InterlockedExchange(&m_logEntries[i].m_IsUpdateing,FALSE);\r
1824                                 InterlockedExchange(&m_logEntries[i].m_IsFull,TRUE);\r
1825                         }\r
1826                         \r
1827                         ::PostMessage(m_hWnd,MSG_LOADED,(WPARAM)i,0);\r
1828 \r
1829                         if(m_bExitThread)\r
1830                         {\r
1831                                 InterlockedExchange(&m_bThreadRunning, FALSE);\r
1832                                 InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
1833                                 return;\r
1834                         }                       \r
1835                 }\r
1836         }\r
1837 }\r
1838 \r
1839 UINT CGitLogListBase::LogThread()\r
1840 {\r
1841 \r
1842 //      if(m_ProcCallBack)\r
1843 //              m_ProcCallBack(m_ProcData,GITLOG_START);\r
1844         ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_START,0);\r
1845 \r
1846         InterlockedExchange(&m_bThreadRunning, TRUE);\r
1847         InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
1848 \r
1849     //does the user force the cache to refresh (shift or control key down)?\r
1850     bool refresh =    (GetKeyState (VK_CONTROL) < 0) \r
1851                    || (GetKeyState (VK_SHIFT) < 0);\r
1852 \r
1853         //disable the "Get All" button while we're receiving\r
1854         //log messages.\r
1855 \r
1856         FillGitShortLog();\r
1857         \r
1858         if(this->m_bExitThread)\r
1859         {\r
1860                 InterlockedExchange(&m_bThreadRunning, FALSE);\r
1861                 InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
1862                 return 0;\r
1863         }\r
1864         InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
1865         ::PostMessage(GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_START_ALL, 0);\r
1866 \r
1867         int start=0; CString firstcommit,lastcommit;\r
1868         int update=0;\r
1869         for(int i=0;i<m_logEntries.size();i++)\r
1870         {\r
1871                 start=this->m_logEntries[i].ParserFromLog(m_logEntries.m_RawlogData,start);\r
1872                 m_logEntries.m_HashMap[m_logEntries[i].m_CommitHash]=i;\r
1873 \r
1874                 if(m_LogCache.GetCacheData(m_logEntries[i]))\r
1875                 {\r
1876                         if(firstcommit.IsEmpty())\r
1877                                 firstcommit=m_logEntries[i].m_CommitHash;\r
1878                         lastcommit=m_logEntries[i].m_CommitHash;\r
1879 \r
1880                 }else\r
1881                 {\r
1882                         InterlockedExchange(&m_logEntries[i].m_IsUpdateing,FALSE);\r
1883                         InterlockedExchange(&m_logEntries[i].m_IsFull,TRUE);\r
1884                         update++;\r
1885                 }\r
1886                 if(start<0)\r
1887                         break;\r
1888                 if(start>=m_logEntries.m_RawlogData.size())\r
1889                         break;\r
1890 \r
1891                 int percent=i*30/m_logEntries.size() + GITLOG_START+1;\r
1892 \r
1893                 ::PostMessage(GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) percent, 0);\r
1894                 ::PostMessage(m_hWnd,MSG_LOADED,(WPARAM) i, 0);\r
1895 \r
1896                 if(this->m_bExitThread)\r
1897                 {       \r
1898                         InterlockedExchange(&m_bThreadRunning, FALSE);\r
1899                         InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
1900                         return 0;\r
1901                 }\r
1902         }\r
1903         if(!lastcommit.IsEmpty())\r
1904                 FetchFullLogInfo(lastcommit,firstcommit);\r
1905         \r
1906         this->FetchLastLogInfo();\r
1907         \r
1908 #if 0\r
1909         RedrawItems(0, m_arShownList.GetCount());\r
1910 //      SetRedraw(false);\r
1911 //      ResizeAllListCtrlCols();\r
1912 //      SetRedraw(true);\r
1913 \r
1914         if ( m_pStoreSelection )\r
1915         {\r
1916                 // Deleting the instance will restore the\r
1917                 // selection of the CLogDlg.\r
1918                 delete m_pStoreSelection;\r
1919                 m_pStoreSelection = NULL;\r
1920         }\r
1921         else\r
1922         {\r
1923                 // If no selection has been set then this must be the first time\r
1924                 // the revisions are shown. Let's preselect the topmost revision.\r
1925                 if ( GetItemCount()>0 )\r
1926                 {\r
1927                         SetSelectionMark(0);\r
1928                         SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);\r
1929                 }\r
1930         }\r
1931 #endif\r
1932 \r
1933 \r
1934 \r
1935         //FetchFullLogInfo();\r
1936         //FetchFullLogInfoOrig();\r
1937         //RefreshCursor();\r
1938         // make sure the filter is applied (if any) now, after we refreshed/fetched\r
1939         // the log messages\r
1940 \r
1941         ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_END,0);\r
1942 \r
1943         InterlockedExchange(&m_bThreadRunning, FALSE);\r
1944 \r
1945         return 0;\r
1946 }\r
1947 \r
1948 void CGitLogListBase::Refresh()\r
1949 {       \r
1950         m_bExitThread=TRUE;\r
1951         if(m_LoadingThread!=NULL)\r
1952         {\r
1953                 DWORD ret =::WaitForSingleObject(m_LoadingThread->m_hThread,20000);\r
1954                 if(ret == WAIT_TIMEOUT)\r
1955                         TerminateThread();\r
1956         }\r
1957 \r
1958         this->Clear();\r
1959 \r
1960         //Update branch and Tag info\r
1961         ReloadHashMap();\r
1962         //Assume Thread have exited\r
1963         //if(!m_bThreadRunning)\r
1964         {\r
1965                 this->SetItemCountEx(0);\r
1966                 m_logEntries.clear();\r
1967                 m_bExitThread=FALSE;\r
1968                 InterlockedExchange(&m_bThreadRunning, TRUE);\r
1969                 InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
1970                 if (AfxBeginThread(LogThreadEntry, this)==NULL)\r
1971                 {\r
1972                         InterlockedExchange(&m_bThreadRunning, FALSE);\r
1973                         InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
1974                         CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
1975                 }\r
1976                 m_sFilterText.Empty();\r
1977                 m_From=CTime(1970,1,2,0,0,0);\r
1978                 m_To=CTime::GetCurrentTime();\r
1979         }\r
1980 }\r
1981 bool CGitLogListBase::ValidateRegexp(LPCTSTR regexp_str, tr1::wregex& pat, bool bMatchCase /* = false */)\r
1982 {\r
1983         try\r
1984         {\r
1985                 tr1::regex_constants::syntax_option_type type = tr1::regex_constants::ECMAScript;\r
1986                 if (!bMatchCase)\r
1987                         type |= tr1::regex_constants::icase;\r
1988                 pat = tr1::wregex(regexp_str, type);\r
1989                 return true;\r
1990         }\r
1991         catch (exception) {}\r
1992         return false;\r
1993 }\r
1994 \r
1995 void CGitLogListBase::RecalculateShownList(CPtrArray * pShownlist)\r
1996 {\r
1997 \r
1998         pShownlist->RemoveAll();\r
1999         tr1::wregex pat;//(_T("Remove"), tr1::regex_constants::icase);\r
2000         bool bRegex = false;\r
2001         if (m_bFilterWithRegex)\r
2002                 bRegex = ValidateRegexp(m_sFilterText, pat, false);\r
2003 \r
2004         tr1::regex_constants::match_flag_type flags = tr1::regex_constants::match_any;\r
2005         CString sRev;\r
2006         for (DWORD i=0; i<m_logEntries.size(); ++i)\r
2007         {\r
2008                 if ((bRegex)&&(m_bFilterWithRegex))\r
2009                 {\r
2010 #if 0\r
2011                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))\r
2012                         {\r
2013                                 ATLTRACE(_T("bugID = \"%s\"\n"), (LPCTSTR)m_logEntries[i]->sBugIDs);\r
2014                                 if (regex_search(wstring((LPCTSTR)m_logEntries[i]->sBugIDs), pat, flags)&&IsEntryInDateRange(i))\r
2015                                 {\r
2016                                         pShownlist->Add(m_logEntries[i]);\r
2017                                         continue;\r
2018                                 }\r
2019                         }\r
2020 #endif\r
2021                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))\r
2022                         {\r
2023                                 ATLTRACE(_T("messge = \"%s\"\n"),m_logEntries[i].m_Subject);\r
2024                                 if (regex_search(wstring((LPCTSTR)m_logEntries[i].m_Subject), pat, flags)&&IsEntryInDateRange(i))\r
2025                                 {\r
2026                                         pShownlist->Add(&m_logEntries[i]);\r
2027                                         continue;\r
2028                                 }\r
2029 \r
2030                                 ATLTRACE(_T("messge = \"%s\"\n"),m_logEntries[i].m_Body);\r
2031                                 if (regex_search(wstring((LPCTSTR)m_logEntries[i].m_Body), pat, flags)&&IsEntryInDateRange(i))\r
2032                                 {\r
2033                                         pShownlist->Add(&m_logEntries[i]);\r
2034                                         continue;\r
2035                                 }\r
2036                         }\r
2037 #if 0\r
2038                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))\r
2039                         {\r
2040                                 LogChangedPathArray * cpatharray = m_logEntries[i]->pArChangedPaths;\r
2041 \r
2042                                 bool bGoing = true;\r
2043                                 for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount() && bGoing; ++cpPathIndex)\r
2044                                 {\r
2045                                         LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);\r
2046                                         if (regex_search(wstring((LPCTSTR)cpath->sCopyFromPath), pat, flags)&&IsEntryInDateRange(i))\r
2047                                         {\r
2048                                                 pShownlist->Add(m_logEntries[i]);\r
2049                                                 bGoing = false;\r
2050                                                 continue;\r
2051                                         }\r
2052                                         if (regex_search(wstring((LPCTSTR)cpath->sPath), pat, flags)&&IsEntryInDateRange(i))\r
2053                                         {\r
2054                                                 pShownlist->Add(m_logEntries[i]);\r
2055                                                 bGoing = false;\r
2056                                                 continue;\r
2057                                         }\r
2058                                         if (regex_search(wstring((LPCTSTR)cpath->GetAction()), pat, flags)&&IsEntryInDateRange(i))\r
2059                                         {\r
2060                                                 pShownlist->Add(m_logEntries[i]);\r
2061                                                 bGoing = false;\r
2062                                                 continue;\r
2063                                         }\r
2064                                 }\r
2065                                 if (!bGoing)\r
2066                                         continue;\r
2067                         }\r
2068 #endif\r
2069                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))\r
2070                         {\r
2071                                 if (regex_search(wstring((LPCTSTR)m_logEntries[i].m_AuthorName), pat, flags)&&IsEntryInDateRange(i))\r
2072                                 {\r
2073                                         pShownlist->Add(&m_logEntries[i]);\r
2074                                         continue;\r
2075                                 }\r
2076                         }\r
2077                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))\r
2078                         {\r
2079                                 sRev.Format(_T("%s"), m_logEntries[i].m_CommitHash);\r
2080                                 if (regex_search(wstring((LPCTSTR)sRev), pat, flags)&&IsEntryInDateRange(i))\r
2081                                 {\r
2082                                         pShownlist->Add(&m_logEntries[i]);\r
2083                                         continue;\r
2084                                 }\r
2085                         }\r
2086                 } // if (bRegex)\r
2087                 else\r
2088                 {\r
2089                         CString find = m_sFilterText;\r
2090                         find.MakeLower();\r
2091 #if 0\r
2092                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))\r
2093                         {\r
2094                                 CString sBugIDs = m_logEntries[i]->sBugIDs;\r
2095 \r
2096                                 sBugIDs = sBugIDs.MakeLower();\r
2097                                 if ((sBugIDs.Find(find) >= 0)&&(IsEntryInDateRange(i)))\r
2098                                 {\r
2099                                         pShownlist->Add(m_logEntries[i]);\r
2100                                         continue;\r
2101                                 }\r
2102                         }\r
2103 #endif\r
2104                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))\r
2105                         {\r
2106                                 CString msg = m_logEntries[i].m_Subject;\r
2107 \r
2108                                 msg = msg.MakeLower();\r
2109                                 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))\r
2110                                 {\r
2111                                         pShownlist->Add(&m_logEntries[i]);\r
2112                                         continue;\r
2113                                 }\r
2114                                 msg = m_logEntries[i].m_Body;\r
2115 \r
2116                                 msg = msg.MakeLower();\r
2117                                 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))\r
2118                                 {\r
2119                                         pShownlist->Add(&m_logEntries[i]);\r
2120                                         continue;\r
2121                                 }\r
2122                         }\r
2123 #if 0\r
2124                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))\r
2125                         {\r
2126                                 LogChangedPathArray * cpatharray = m_logEntries[i]->pArChangedPaths;\r
2127 \r
2128                                 bool bGoing = true;\r
2129                                 for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount() && bGoing; ++cpPathIndex)\r
2130                                 {\r
2131                                         LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);\r
2132                                         CString path = cpath->sCopyFromPath;\r
2133                                         path.MakeLower();\r
2134                                         if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))\r
2135                                         {\r
2136                                                 pShownlist->Add(m_logEntries[i]);\r
2137                                                 bGoing = false;\r
2138                                                 continue;\r
2139                                         }\r
2140                                         path = cpath->sPath;\r
2141                                         path.MakeLower();\r
2142                                         if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))\r
2143                                         {\r
2144                                                 pShownlist->Add(m_logEntries[i]);\r
2145                                                 bGoing = false;\r
2146                                                 continue;\r
2147                                         }\r
2148                                         path = cpath->GetAction();\r
2149                                         path.MakeLower();\r
2150                                         if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))\r
2151                                         {\r
2152                                                 pShownlist->Add(m_logEntries[i]);\r
2153                                                 bGoing = false;\r
2154                                                 continue;\r
2155                                         }\r
2156                                 }\r
2157                         }\r
2158 #endif\r
2159                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))\r
2160                         {\r
2161                                 CString msg = m_logEntries[i].m_AuthorName;\r
2162                                 msg = msg.MakeLower();\r
2163                                 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))\r
2164                                 {\r
2165                                         pShownlist->Add(&m_logEntries[i]);\r
2166                                         continue;\r
2167                                 }\r
2168                         }\r
2169                         if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))\r
2170                         {\r
2171                                 sRev.Format(_T("%s"), m_logEntries[i].m_CommitHash);\r
2172                                 if ((sRev.Find(find) >= 0)&&(IsEntryInDateRange(i)))\r
2173                                 {\r
2174                                         pShownlist->Add(&m_logEntries[i]);\r
2175                                         continue;\r
2176                                 }\r
2177                         }\r
2178                 } // else (from if (bRegex))    \r
2179         } // for (DWORD i=0; i<m_logEntries.size(); ++i) \r
2180 \r
2181 }\r
2182 \r
2183 BOOL CGitLogListBase::IsEntryInDateRange(int i)\r
2184 {\r
2185         __time64_t time = m_logEntries[i].m_AuthorDate.GetTime();\r
2186         if ((time >= m_From.GetTime())&&(time <= m_To.GetTime()))\r
2187                 return TRUE;\r
2188 \r
2189         return FALSE;\r
2190 \r
2191 //      return TRUE;\r
2192 }\r
2193 void CGitLogListBase::StartFilter()\r
2194 {\r
2195         InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
2196         RecalculateShownList(&m_arShownList);\r
2197         InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
2198 \r
2199 \r
2200         DeleteAllItems();\r
2201         SetItemCountEx(ShownCountWithStopped());\r
2202         RedrawItems(0, ShownCountWithStopped());\r
2203         SetRedraw(false);\r
2204         //ResizeAllListCtrlCols();\r
2205         SetRedraw(true);\r
2206         Invalidate();\r
2207 \r
2208 }\r
2209 void CGitLogListBase::RemoveFilter()\r
2210 {\r
2211 \r
2212         InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
2213 \r
2214         m_arShownList.RemoveAll();\r
2215 \r
2216         // reset the time filter too\r
2217 #if 0\r
2218         m_timFrom = (__time64_t(m_tFrom));\r
2219         m_timTo = (__time64_t(m_tTo));\r
2220         m_DateFrom.SetTime(&m_timFrom);\r
2221         m_DateTo.SetTime(&m_timTo);\r
2222         m_DateFrom.SetRange(&m_timFrom, &m_timTo);\r
2223         m_DateTo.SetRange(&m_timFrom, &m_timTo);\r
2224 #endif\r
2225 \r
2226         for (DWORD i=0; i<m_logEntries.size(); ++i)\r
2227         {\r
2228                 if(this->m_IsOldFirst)\r
2229                 {\r
2230                         m_arShownList.Add(&m_logEntries[m_logEntries.size()-i-1]);\r
2231                 }else\r
2232                 {\r
2233                         m_arShownList.Add(&m_logEntries[i]);\r
2234                 }\r
2235         }\r
2236 //      InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
2237         DeleteAllItems();\r
2238         SetItemCountEx(ShownCountWithStopped());\r
2239         RedrawItems(0, ShownCountWithStopped());\r
2240 //      SetRedraw(false);\r
2241 //      ResizeAllListCtrlCols();\r
2242 //      SetRedraw(true);\r
2243 \r
2244         InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
2245 }\r
2246 \r
2247 void CGitLogListBase::Clear()\r
2248 {\r
2249         m_arShownList.RemoveAll();\r
2250         DeleteAllItems();\r
2251 \r
2252         m_logEntries.ClearAll();\r
2253 \r
2254 }\r
2255 \r
2256 void CGitLogListBase::OnDestroy()\r
2257 {\r
2258         // save the column widths to the registry\r
2259         SaveColumnWidths();\r
2260 \r
2261         if(this->m_bThreadRunning)\r
2262         {\r
2263                 this->m_bExitThread=true;\r
2264                 DWORD ret =::WaitForSingleObject(m_LoadingThread->m_hThread,20000);\r
2265                 if(ret == WAIT_TIMEOUT)\r
2266                         TerminateThread();\r
2267         }\r
2268         while(m_LogCache.SaveCache())\r
2269         {\r
2270                 if(CMessageBox::Show(NULL,_T("Cannot Save Log Cache to Disk. To retry click yes. To give up click no."),_T("TortoiseGit"),\r
2271                                                         MB_YESNO) == IDNO)\r
2272                                                         break;\r
2273         }\r
2274         CHintListCtrl::OnDestroy();\r
2275 }\r
2276 \r
2277 LRESULT CGitLogListBase::OnLoad(WPARAM wParam,LPARAM lParam)\r
2278 {\r
2279         CRect rect;\r
2280         int i=(int)wParam;\r
2281         this->GetItemRect(i,&rect,LVIR_BOUNDS);\r
2282         this->InvalidateRect(rect);\r
2283         return 0;\r
2284 }\r
2285 \r
2286 /**\r
2287  * Save column widths to the registry\r
2288  */\r
2289 void CGitLogListBase::SaveColumnWidths()\r
2290 {\r
2291         CHeaderCtrl* pHdrCtrl = (CHeaderCtrl*)(GetDlgItem(0));\r
2292         if (pHdrCtrl)\r
2293         {\r
2294                 int numcols = pHdrCtrl->GetItemCount();\r
2295                 for (int col = 0; col < numcols; col++)\r
2296                 {\r
2297                         int width = GetColumnWidth( col );\r
2298                         CString regentry;\r
2299                         regentry.Format( _T("Software\\TortoiseGit\\%s\\ColWidth%d"),m_ColumnRegKey, col);\r
2300                         CRegDWORD regwidth(regentry, 0);\r
2301                         regwidth = width;       // this writes it to reg\r
2302                 }\r
2303         }\r
2304 }\r
2305 \r
2306 int CGitLogListBase::GetHeadIndex()\r
2307 {\r
2308         if(m_HeadHash.IsEmpty())\r
2309                 return -1;\r
2310 \r
2311         for(int i=0;i<m_arShownList.GetCount();i++)\r
2312         {\r
2313                 GitRev *pRev = (GitRev*)m_arShownList[i];\r
2314                 if(pRev)\r
2315                 {\r
2316                         if(pRev->m_CommitHash == m_HeadHash )\r
2317                                 return i;\r
2318                 }\r
2319         }\r
2320         return -1;\r
2321 }