OSDN Git Service

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