OSDN Git Service

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