OSDN Git Service

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