OSDN Git Service

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