OSDN Git Service

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