OSDN Git Service

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