OSDN Git Service

Modify Git Clone Control Tab order
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / LogDlg.cpp
1 // TortoiseGit - a Windows shell extension for easy version control\r
2 // Copyright (C) 2003-2008 - TortoiseGit\r
3 // This program is free software; you can redistribute it and/or\r
4 // modify it under the terms of the GNU General Public License\r
5 // as published by the Free Software Foundation; either version 2\r
6 // of the License, or (at your option) any later version.\r
7 // This program is distributed in the hope that it will be useful,\r
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
10 // GNU General Public License for more details.\r
11 \r
12 // You should have received a copy of the GNU General Public License\r
13 // along with this program; if not, write to the Free Software Foundation,\r
14 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
15 //\r
16 #include "stdafx.h"\r
17 #include "TortoiseProc.h"\r
18 #include "cursor.h"\r
19 #include "InputDlg.h"\r
20 #include "PropDlg.h"\r
21 #include "SVNProgressDlg.h"\r
22 #include "ProgressDlg.h"\r
23 //#include "RepositoryBrowser.h"\r
24 //#include "CopyDlg.h"\r
25 #include "StatGraphDlg.h"\r
26 #include "Logdlg.h"\r
27 #include "MessageBox.h"\r
28 #include "Registry.h"\r
29 #include "AppUtils.h"\r
30 #include "PathUtils.h"\r
31 #include "StringUtils.h"\r
32 #include "UnicodeUtils.h"\r
33 #include "TempFile.h"\r
34 //#include "GitInfo.h"\r
35 //#include "GitDiff.h"\r
36 #include "IconMenu.h"\r
37 //#include "RevisionRangeDlg.h"\r
38 //#include "BrowseFolder.h"\r
39 //#include "BlameDlg.h"\r
40 //#include "Blame.h"\r
41 //#include "GitHelpers.h"\r
42 #include "GitStatus.h"\r
43 //#include "LogDlgHelper.h"\r
44 //#include "CachedLogInfo.h"\r
45 //#include "RepositoryInfo.h"\r
46 //#include "EditPropertiesDlg.h"\r
47 #include "FileDiffDlg.h"\r
48 \r
49 \r
50 const UINT CLogDlg::m_FindDialogMessage = RegisterWindowMessage(FINDMSGSTRING);\r
51 \r
52 \r
53 IMPLEMENT_DYNAMIC(CLogDlg, CResizableStandAloneDialog)\r
54 CLogDlg::CLogDlg(CWnd* pParent /*=NULL*/)\r
55         : CResizableStandAloneDialog(CLogDlg::IDD, pParent)\r
56         , m_logcounter(0)\r
57         , m_nSearchIndex(0)\r
58         , m_wParam(0)\r
59         , m_currentChangedArray(NULL)\r
60         , m_nSortColumn(0)\r
61         , m_bShowedAll(false)\r
62         , m_bSelect(false)\r
63         \r
64         , m_bSelectionMustBeContinuous(false)\r
65         , m_bShowBugtraqColumn(false)\r
66         , m_lowestRev(_T(""))\r
67         \r
68         , m_sLogInfo(_T(""))\r
69         , m_pFindDialog(NULL)\r
70         , m_bCancelled(FALSE)\r
71         , m_pNotifyWindow(NULL)\r
72         \r
73         , m_bAscending(FALSE)\r
74 \r
75         , m_limit(0)\r
76         , m_childCounter(0)\r
77         , m_maxChild(0)\r
78         , m_bIncludeMerges(FALSE)\r
79         , m_hAccel(NULL)\r
80         , m_bVista(false)\r
81 {\r
82         m_bFilterWithRegex = !!CRegDWORD(_T("Software\\TortoiseGit\\UseRegexFilter"), TRUE);\r
83         m_bAllBranch=FALSE;\r
84         m_bFirstParent=FALSE;\r
85         m_bWholeProject=FALSE;\r
86 }\r
87 \r
88 CLogDlg::~CLogDlg()\r
89 {\r
90         \r
91     m_CurrentFilteredChangedArray.RemoveAll();\r
92         \r
93 }\r
94 \r
95 void CLogDlg::DoDataExchange(CDataExchange* pDX)\r
96 {\r
97         CResizableStandAloneDialog::DoDataExchange(pDX);\r
98         DDX_Control(pDX, IDC_LOGLIST, m_LogList);\r
99         DDX_Control(pDX, IDC_LOGMSG, m_ChangedFileListCtrl);\r
100         DDX_Control(pDX, IDC_PROGRESS, m_LogProgress);\r
101         DDX_Control(pDX, IDC_SPLITTERTOP, m_wndSplitter1);\r
102         DDX_Control(pDX, IDC_SPLITTERBOTTOM, m_wndSplitter2);\r
103         DDX_Text(pDX, IDC_SEARCHEDIT, m_LogList.m_sFilterText);\r
104         DDX_Control(pDX, IDC_DATEFROM, m_DateFrom);\r
105         DDX_Control(pDX, IDC_DATETO, m_DateTo);\r
106         DDX_Control(pDX, IDC_HIDEPATHS, m_cHidePaths);\r
107         DDX_Text(pDX, IDC_LOGINFO, m_sLogInfo);\r
108         DDX_Check(pDX, IDC_LOG_FIRSTPARENT, m_bFirstParent);\r
109         DDX_Check(pDX, IDC_LOG_ALLBRANCH,m_bAllBranch);\r
110         DDX_Check(pDX, IDC_SHOWWHOLEPROJECT,m_bWholeProject);\r
111         DDX_Control(pDX, IDC_SEARCHEDIT, m_cFilter);\r
112 }\r
113 \r
114 BEGIN_MESSAGE_MAP(CLogDlg, CResizableStandAloneDialog)\r
115         ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage) \r
116         //ON_BN_CLICKED(IDC_GETALL, OnBnClickedGetall)\r
117         //ON_NOTIFY(NM_DBLCLK, IDC_LOGMSG, OnNMDblclkChangedFileList)\r
118         ON_WM_CONTEXTMENU()\r
119         ON_WM_SETCURSOR()\r
120         ON_BN_CLICKED(IDHELP, OnBnClickedHelp)\r
121         ON_NOTIFY(LVN_ITEMCHANGED, IDC_LOGLIST, OnLvnItemchangedLoglist)\r
122         ON_NOTIFY(EN_LINK, IDC_MSGVIEW, OnEnLinkMsgview)\r
123         ON_BN_CLICKED(IDC_STATBUTTON, OnBnClickedStatbutton)\r
124 \r
125         \r
126         ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)\r
127         ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED, OnClickedCancelFilter)\r
128 \r
129         ON_MESSAGE(MSG_LOAD_PERCENTAGE,OnLogListLoading)\r
130         \r
131         ON_EN_CHANGE(IDC_SEARCHEDIT, OnEnChangeSearchedit)\r
132         ON_WM_TIMER()\r
133         ON_NOTIFY(DTN_DATETIMECHANGE, IDC_DATETO, OnDtnDatetimechangeDateto)\r
134         ON_NOTIFY(DTN_DATETIMECHANGE, IDC_DATEFROM, OnDtnDatetimechangeDatefrom)\r
135         ON_BN_CLICKED(IDC_SHOWWHOLEPROJECT, OnBnClickShowWholeProject)\r
136         //ON_NOTIFY(NM_CUSTOMDRAW, IDC_LOGMSG, OnNMCustomdrawChangedFileList)\r
137         //ON_NOTIFY(LVN_GETDISPINFO, IDC_LOGMSG, OnLvnGetdispinfoChangedFileList)\r
138         ON_NOTIFY(LVN_COLUMNCLICK,IDC_LOGLIST   , OnLvnColumnclick)\r
139         //ON_NOTIFY(LVN_COLUMNCLICK, IDC_LOGMSG, OnLvnColumnclickChangedFileList)\r
140         ON_BN_CLICKED(IDC_HIDEPATHS, OnBnClickedHidepaths)\r
141         ON_BN_CLICKED(IDC_LOG_ALLBRANCH,OnBnClickedAllBranch)\r
142         \r
143         ON_NOTIFY(DTN_DROPDOWN, IDC_DATEFROM, &CLogDlg::OnDtnDropdownDatefrom)\r
144         ON_NOTIFY(DTN_DROPDOWN, IDC_DATETO, &CLogDlg::OnDtnDropdownDateto)\r
145         ON_WM_SIZE()\r
146         ON_BN_CLICKED(IDC_LOG_FIRSTPARENT, &CLogDlg::OnBnClickedFirstParent)\r
147         ON_BN_CLICKED(IDC_REFRESH, &CLogDlg::OnBnClickedRefresh)\r
148         ON_COMMAND(ID_LOGDLG_REFRESH,&CLogDlg::OnRefresh)\r
149         ON_COMMAND(ID_LOGDLG_FIND,&CLogDlg::OnFind)\r
150         ON_COMMAND(ID_LOGDLG_FOCUSFILTER,&CLogDlg::OnFocusFilter)\r
151         ON_COMMAND(ID_EDIT_COPY, &CLogDlg::OnEditCopy)\r
152 END_MESSAGE_MAP()\r
153 \r
154 void CLogDlg::SetParams(const CTGitPath& path, GitRev pegrev, GitRev startrev, GitRev endrev, int limit /* = FALSE */)\r
155 {\r
156         m_path = path;\r
157         m_pegrev = pegrev;\r
158         m_startrev = startrev;\r
159         m_LogRevision = startrev;\r
160         m_endrev = endrev;\r
161         m_hasWC = !path.IsUrl();\r
162         m_limit = limit;\r
163         if (::IsWindow(m_hWnd))\r
164                 UpdateData(FALSE);\r
165 }\r
166 \r
167 BOOL CLogDlg::OnInitDialog()\r
168 {\r
169         CString temp;\r
170         CResizableStandAloneDialog::OnInitDialog();\r
171 \r
172         m_hAccel = LoadAccelerators(AfxGetResourceHandle(),MAKEINTRESOURCE(IDR_ACC_LOGDLG));\r
173 \r
174         OSVERSIONINFOEX inf;\r
175         SecureZeroMemory(&inf, sizeof(OSVERSIONINFOEX));\r
176         inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\r
177         GetVersionEx((OSVERSIONINFO *)&inf);\r
178         WORD fullver = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);\r
179         m_bVista = (fullver >= 0x0600);\r
180 \r
181         // use the state of the "stop on copy/rename" option from the last time\r
182         UpdateData(FALSE);\r
183         \r
184         // set the font to use in the log message view, configured in the settings dialog\r
185         CAppUtils::CreateFontForLogs(m_logFont);\r
186         GetDlgItem(IDC_MSGVIEW)->SetFont(&m_logFont);\r
187         // automatically detect URLs in the log message and turn them into links\r
188         GetDlgItem(IDC_MSGVIEW)->SendMessage(EM_AUTOURLDETECT, TRUE, NULL);\r
189         // make the log message rich edit control send a message when the mouse pointer is over a link\r
190         GetDlgItem(IDC_MSGVIEW)->SendMessage(EM_SETEVENTMASK, NULL, ENM_LINK);\r
191         //m_LogList.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_SUBITEMIMAGES);\r
192 \r
193         // the "hide unrelated paths" checkbox should be indeterminate\r
194         m_cHidePaths.SetCheck(BST_INDETERMINATE);\r
195 \r
196         \r
197         // if there is a working copy, load the project properties\r
198         // to get information about the bugtraq: integration\r
199         if (m_hasWC)\r
200                 m_ProjectProperties.ReadProps(m_path);\r
201 \r
202         // the bugtraq issue id column is only shown if the bugtraq:url or bugtraq:regex is set\r
203         if ((!m_ProjectProperties.sUrl.IsEmpty())||(!m_ProjectProperties.sCheckRe.IsEmpty()))\r
204                 m_bShowBugtraqColumn = true;\r
205 \r
206         //theme.SetWindowTheme(m_LogList.GetSafeHwnd(), L"Explorer", NULL);\r
207         //theme.SetWindowTheme(m_ChangedFileListCtrl.GetSafeHwnd(), L"Explorer", NULL);\r
208 \r
209         // set up the columns\r
210         m_LogList.DeleteAllItems();\r
211         m_LogList.InsertGitColumn();\r
212 \r
213         m_ChangedFileListCtrl.Init(SVNSLC_COLEXT | SVNSLC_COLSTATUS |SVNSLC_COLADD|SVNSLC_COLDEL , _T("LogDlg"),(SVNSLC_POPALL ^ (SVNSLC_POPCOMMIT|SVNSLC_POPREVERT)),false);\r
214 \r
215         GetDlgItem(IDC_LOGLIST)->UpdateData(FALSE);\r
216 \r
217         m_logcounter = 0;\r
218         m_sMessageBuf.Preallocate(100000);\r
219 \r
220         // set the dialog title to "Log - path/to/whatever/we/show/the/log/for"\r
221         SetDlgTitle(false);\r
222 \r
223         m_tooltips.Create(this);\r
224         CheckRegexpTooltip();\r
225 \r
226         SetSplitterRange();\r
227         \r
228         // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap\r
229         m_cFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);\r
230         m_cFilter.SetInfoIcon(IDI_LOGFILTER);\r
231         m_cFilter.SetValidator(this);\r
232         \r
233         AdjustControlSize(IDC_HIDEPATHS);\r
234         AdjustControlSize(IDC_LOG_FIRSTPARENT);\r
235         AdjustControlSize(IDC_LOG_ALLBRANCH);\r
236 \r
237         GetClientRect(m_DlgOrigRect);\r
238         m_LogList.GetClientRect(m_LogListOrigRect);\r
239         GetDlgItem(IDC_MSGVIEW)->GetClientRect(m_MsgViewOrigRect);\r
240         m_ChangedFileListCtrl.GetClientRect(m_ChgOrigRect);\r
241 \r
242         m_DateFrom.SendMessage(DTM_SETMCSTYLE, 0, MCS_WEEKNUMBERS|MCS_NOTODAY|MCS_NOTRAILINGDATES|MCS_NOSELCHANGEONNAV);\r
243         m_DateTo.SendMessage(DTM_SETMCSTYLE, 0, MCS_WEEKNUMBERS|MCS_NOTODAY|MCS_NOTRAILINGDATES|MCS_NOSELCHANGEONNAV);\r
244 \r
245         // resizable stuff\r
246         AddAnchor(IDC_FROMLABEL, TOP_LEFT);\r
247         AddAnchor(IDC_DATEFROM, TOP_LEFT);\r
248         AddAnchor(IDC_TOLABEL, TOP_LEFT);\r
249         AddAnchor(IDC_DATETO, TOP_LEFT);\r
250 \r
251         SetFilterCueText();\r
252         AddAnchor(IDC_SEARCHEDIT, TOP_LEFT, TOP_RIGHT);\r
253         \r
254         AddAnchor(IDC_LOGLIST, TOP_LEFT, TOP_RIGHT);\r
255         AddAnchor(IDC_SPLITTERTOP, TOP_LEFT, TOP_RIGHT);\r
256         AddAnchor(IDC_MSGVIEW, TOP_LEFT, BOTTOM_RIGHT);\r
257         AddAnchor(IDC_SPLITTERBOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT);\r
258         AddAnchor(IDC_LOGMSG, BOTTOM_LEFT, BOTTOM_RIGHT);\r
259 \r
260         AddAnchor(IDC_LOGINFO, BOTTOM_LEFT, BOTTOM_RIGHT);      \r
261         AddAnchor(IDC_HIDEPATHS, BOTTOM_LEFT);  \r
262         AddAnchor(IDC_LOG_ALLBRANCH,BOTTOM_LEFT);\r
263         AddAnchor(IDC_LOG_FIRSTPARENT, BOTTOM_LEFT);\r
264         //AddAnchor(IDC_GETALL, BOTTOM_LEFT);\r
265         AddAnchor(IDC_SHOWWHOLEPROJECT, BOTTOM_LEFT);\r
266         AddAnchor(IDC_REFRESH, BOTTOM_LEFT);\r
267         AddAnchor(IDC_STATBUTTON, BOTTOM_RIGHT);\r
268         AddAnchor(IDC_PROGRESS, BOTTOM_LEFT, BOTTOM_RIGHT);\r
269         AddAnchor(IDOK, BOTTOM_RIGHT);\r
270         AddAnchor(IDCANCEL, BOTTOM_RIGHT);\r
271         AddAnchor(IDHELP, BOTTOM_RIGHT);\r
272 \r
273 //      SetPromptParentWindow(m_hWnd);\r
274 \r
275         if (hWndExplorer)\r
276                 CenterWindow(CWnd::FromHandle(hWndExplorer));\r
277         EnableSaveRestore(_T("LogDlg"));\r
278 \r
279         DWORD yPos1 = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer1"));\r
280         DWORD yPos2 = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer2"));\r
281         RECT rcDlg, rcLogList, rcChgMsg;\r
282         GetClientRect(&rcDlg);\r
283         m_LogList.GetWindowRect(&rcLogList);\r
284         ScreenToClient(&rcLogList);\r
285         m_ChangedFileListCtrl.GetWindowRect(&rcChgMsg);\r
286         ScreenToClient(&rcChgMsg);\r
287         if (yPos1)\r
288         {\r
289                 RECT rectSplitter;\r
290                 m_wndSplitter1.GetWindowRect(&rectSplitter);\r
291                 ScreenToClient(&rectSplitter);\r
292                 int delta = yPos1 - rectSplitter.top;\r
293 \r
294                 if ((rcLogList.bottom + delta > rcLogList.top)&&(rcLogList.bottom + delta < rcChgMsg.bottom - 30))\r
295                 {\r
296                         m_wndSplitter1.SetWindowPos(NULL, 0, yPos1, 0, 0, SWP_NOSIZE);\r
297                         DoSizeV1(delta);\r
298                 }\r
299         }\r
300         if (yPos2)\r
301         {\r
302                 RECT rectSplitter;\r
303                 m_wndSplitter2.GetWindowRect(&rectSplitter);\r
304                 ScreenToClient(&rectSplitter);\r
305                 int delta = yPos2 - rectSplitter.top;\r
306 \r
307                 if ((rcChgMsg.top + delta < rcChgMsg.bottom)&&(rcChgMsg.top + delta > rcLogList.top + 30))\r
308                 {\r
309                         m_wndSplitter2.SetWindowPos(NULL, 0, yPos2, 0, 0, SWP_NOSIZE);\r
310                         DoSizeV2(delta);\r
311                 }\r
312         }\r
313 \r
314         \r
315         if (m_bSelect)\r
316         {\r
317                 // the dialog is used to select revisions\r
318                 // enable the OK button if appropriate\r
319                 EnableOKButton();\r
320         }\r
321         else\r
322         {\r
323                 // the dialog is used to just view log messages\r
324                 // hide the OK button and set text on Cancel button to OK\r
325                 GetDlgItemText(IDOK, temp);\r
326                 SetDlgItemText(IDCANCEL, temp);\r
327                 GetDlgItem(IDOK)->ShowWindow(SW_HIDE);\r
328         }\r
329         \r
330         m_mergedRevs.clear();\r
331 \r
332         // first start a thread to obtain the log messages without\r
333         // blocking the dialog\r
334         //m_tTo = 0;\r
335         //m_tFrom = (DWORD)-1;\r
336 \r
337         m_LogList.m_Path=m_path;\r
338         m_LogList.FetchLogAsync(this);\r
339 \r
340         GetDlgItem(IDC_LOGLIST)->SetFocus();\r
341         return FALSE;\r
342 }\r
343 \r
344 LRESULT CLogDlg::OnLogListLoading(WPARAM wParam, LPARAM /*lParam*/)\r
345 {\r
346         int cur=(int)wParam;\r
347 \r
348         if( cur == GITLOG_START )\r
349         {\r
350                 CString temp;\r
351                 temp.LoadString(IDS_PROGRESSWAIT);\r
352 \r
353                 this->m_LogList.ShowText(temp, true);\r
354 \r
355                 // We use a progress bar while getting the logs \r
356                 m_LogProgress.SetRange32(0, 100);\r
357                 m_LogProgress.SetPos(0);\r
358 \r
359                 GetDlgItem(IDC_PROGRESS)->ShowWindow(TRUE);\r
360 \r
361                 //DialogEnableWindow(IDC_GETALL, FALSE);\r
362                 //DialogEnableWindow(IDC_SHOWWHOLEPROJECT, FALSE);\r
363                 //DialogEnableWindow(IDC_LOG_FIRSTPARENT, FALSE);\r
364                 DialogEnableWindow(IDC_STATBUTTON, FALSE);\r
365                 DialogEnableWindow(IDC_REFRESH, FALSE);\r
366                 DialogEnableWindow(IDC_HIDEPATHS,FALSE);\r
367 \r
368         }else if( cur == GITLOG_END)\r
369         {\r
370                 \r
371                 //if (!m_bShowedAll)\r
372                 DialogEnableWindow(IDC_SHOWWHOLEPROJECT, TRUE);\r
373 \r
374                 //DialogEnableWindow(IDC_GETALL, TRUE);\r
375                 DialogEnableWindow(IDC_LOG_FIRSTPARENT, TRUE);\r
376                 DialogEnableWindow(IDC_STATBUTTON, TRUE);\r
377                 DialogEnableWindow(IDC_REFRESH, TRUE);\r
378                 DialogEnableWindow(IDC_HIDEPATHS,TRUE);\r
379 \r
380 //              PostMessage(WM_TIMER, LOGFILTER_TIMER);\r
381                 GetDlgItem(IDC_PROGRESS)->ShowWindow(FALSE);\r
382                 //CTime time=m_LogList.GetOldestTime();\r
383                 CTime begin,end;\r
384                 m_LogList.GetTimeRange(begin,end);\r
385                 m_DateFrom.SetTime(&begin);\r
386                 m_DateTo.SetTime(&end);\r
387                 \r
388                 \r
389         \r
390         }else\r
391         {\r
392                 if(this->m_LogList.HasText())\r
393                 {\r
394                         this->m_LogList.ClearText();\r
395                         UpdateLogInfoLabel();\r
396                 }\r
397                 m_LogProgress.SetPos(cur);\r
398         }\r
399         return 0;\r
400 }\r
401 void CLogDlg::SetDlgTitle(bool bOffline)\r
402 {\r
403         if (m_sTitle.IsEmpty())\r
404                 GetWindowText(m_sTitle);\r
405 \r
406         if (bOffline)\r
407         {\r
408                 CString sTemp;\r
409                 if (m_path.IsUrl())\r
410                         sTemp.Format(IDS_LOG_DLGTITLEOFFLINE, (LPCTSTR)m_sTitle, (LPCTSTR)m_path.GetUIPathString());\r
411                 else if (m_path.IsDirectory())\r
412                         sTemp.Format(IDS_LOG_DLGTITLEOFFLINE, (LPCTSTR)m_sTitle, (LPCTSTR)m_path.GetWinPathString());\r
413                 else\r
414                         sTemp.Format(IDS_LOG_DLGTITLEOFFLINE, (LPCTSTR)m_sTitle, (LPCTSTR)m_path.GetFilename());\r
415                 SetWindowText(sTemp);\r
416         }\r
417         else\r
418         {\r
419                 if (m_path.IsUrl())\r
420                         SetWindowText(m_sTitle + _T(" - ") + m_path.GetUIPathString());\r
421                 else if (m_path.IsEmpty())\r
422                         SetWindowText(m_sTitle + _T(" - ") + CString(_T("Whole Project")));\r
423                 else if (m_path.IsDirectory())\r
424                         SetWindowText(m_sTitle + _T(" - ") + m_path.GetWinPathString());\r
425                 else\r
426                         SetWindowText(m_sTitle + _T(" - ") + m_path.GetFilename());\r
427         }\r
428 }\r
429 \r
430 void CLogDlg::CheckRegexpTooltip()\r
431 {\r
432         CWnd *pWnd = GetDlgItem(IDC_SEARCHEDIT);\r
433         // Since tooltip describes regexp features, show it only if regexps are enabled.\r
434         if (m_bFilterWithRegex)\r
435         {\r
436                 m_tooltips.AddTool(pWnd, IDS_LOG_FILTER_REGEX_TT);\r
437         }\r
438         else\r
439                 m_tooltips.DelTool(pWnd);\r
440 }\r
441 \r
442 void CLogDlg::EnableOKButton()\r
443 {\r
444         if (m_bSelect)\r
445         {\r
446                 // the dialog is used to select revisions\r
447                 if (m_bSelectionMustBeSingle)\r
448                 {\r
449                         // enable OK button if only a single revision is selected\r
450                         DialogEnableWindow(IDOK, (m_LogList.GetSelectedCount()==1));\r
451                 }\r
452                 else if (m_bSelectionMustBeContinuous)\r
453                         DialogEnableWindow(IDOK, (m_LogList.GetSelectedCount()!=0)&&(m_LogList.IsSelectionContinuous()));\r
454                 else\r
455                         DialogEnableWindow(IDOK, m_LogList.GetSelectedCount()!=0);\r
456         }\r
457         else\r
458                 DialogEnableWindow(IDOK, TRUE);\r
459 }\r
460 \r
461 void CLogDlg::FillLogMessageCtrl(bool bShow /* = true*/)\r
462 {\r
463         // we fill here the log message rich edit control,\r
464         // and also populate the changed files list control\r
465         // according to the selected revision(s).\r
466 \r
467         CWnd * pMsgView = GetDlgItem(IDC_MSGVIEW);\r
468         // empty the log message view\r
469         pMsgView->SetWindowText(_T(" "));\r
470         // empty the changed files list\r
471         m_ChangedFileListCtrl.SetRedraw(FALSE);\r
472 //      InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
473         m_currentChangedArray = NULL;\r
474         //m_ChangedFileListCtrl.SetExtendedStyle ( LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER );\r
475         m_ChangedFileListCtrl.DeleteAllItems();\r
476         \r
477         // if we're not here to really show a selected revision, just\r
478         // get out of here after clearing the views, which is what is intended\r
479         // if that flag is not set.\r
480         if (!bShow)\r
481         {\r
482                 // force a redraw\r
483                 m_ChangedFileListCtrl.Invalidate();\r
484 //              InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
485                 m_ChangedFileListCtrl.SetRedraw(TRUE);\r
486                 return;\r
487         }\r
488 \r
489         // depending on how many revisions are selected, we have to do different\r
490         // tasks.\r
491         int selCount = m_LogList.GetSelectedCount();\r
492         if (selCount == 0)\r
493         {\r
494                 // if nothing is selected, we have nothing more to do\r
495 //              InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
496                 m_ChangedFileListCtrl.SetRedraw(TRUE);\r
497                 return;\r
498         }\r
499         else if (selCount == 1)\r
500         {\r
501                 // if one revision is selected, we have to fill the log message view\r
502                 // with the corresponding log message, and also fill the changed files\r
503                 // list fully.\r
504                 POSITION pos = m_LogList.GetFirstSelectedItemPosition();\r
505                 int selIndex = m_LogList.GetNextSelectedItem(pos);\r
506                 if (selIndex >= m_LogList.m_arShownList.GetCount())\r
507                 {\r
508 //                      InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
509                         m_ChangedFileListCtrl.SetRedraw(TRUE);\r
510                         return;\r
511                 }\r
512                 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_LogList.m_arShownList.GetAt(selIndex));\r
513 \r
514                 if(!pLogEntry->m_IsFull)\r
515                 {\r
516                         pMsgView->SetWindowText(_T("load ..."));\r
517                 }else\r
518                 {\r
519                         // set the log message text\r
520                         pMsgView->SetWindowText(_T("Commit:")+pLogEntry->m_CommitHash+_T("\r\n\r\n*")+pLogEntry->m_Subject+_T("\n\n")+pLogEntry->m_Body);\r
521                         // turn bug ID's into links if the bugtraq: properties have been set\r
522                         // and we can find a match of those in the log message\r
523                         m_ProjectProperties.FindBugID(pLogEntry->m_Body, pMsgView);\r
524                         CAppUtils::FormatTextInRichEditControl(pMsgView);\r
525 \r
526                         int HidePaths=m_cHidePaths.GetState() & 0x0003;\r
527                         CString matchpath=this->m_path.GetGitPathString();\r
528 \r
529                         for(int i=0;i<pLogEntry->m_Files.GetCount() && (!matchpath.IsEmpty());i++)\r
530                         {\r
531                                 if( m_bWholeProject )\r
532                                         break;\r
533 \r
534                                 ((CTGitPath&)pLogEntry->m_Files[i]).m_Action &= ~(CTGitPath::LOGACTIONS_HIDE|CTGitPath::LOGACTIONS_GRAY);\r
535                                 \r
536                                 if(pLogEntry->m_Files[i].GetGitPathString().Left(matchpath.GetLength()) != matchpath)\r
537                                 {\r
538                                         if(HidePaths==BST_CHECKED)\r
539                                                 ((CTGitPath&)pLogEntry->m_Files[i]).m_Action |= CTGitPath::LOGACTIONS_HIDE;\r
540                                         if(HidePaths==BST_INDETERMINATE)\r
541                                                 ((CTGitPath&)pLogEntry->m_Files[i]).m_Action |= CTGitPath::LOGACTIONS_GRAY;\r
542                                 }\r
543                         }\r
544                         m_ChangedFileListCtrl.UpdateWithGitPathList(pLogEntry->m_Files);\r
545                         m_ChangedFileListCtrl.m_CurrentVersion=pLogEntry->m_CommitHash;\r
546                         m_ChangedFileListCtrl.Show(SVNSLC_SHOWVERSIONED);\r
547 \r
548                         m_ChangedFileListCtrl.SetRedraw(TRUE);\r
549                         return;\r
550                 }\r
551 \r
552         }\r
553         else\r
554         {\r
555                 // more than one revision is selected:\r
556                 // the log message view must be emptied\r
557                 // the changed files list contains all the changed paths from all\r
558                 // selected revisions, with 'doubles' removed\r
559                 m_currentChangedPathList = GetChangedPathsFromSelectedRevisions(true);\r
560         }\r
561         \r
562         // redraw the views\r
563 //      InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
564 #if 0\r
565         if (m_currentChangedArray)\r
566         {\r
567                 m_ChangedFileListCtrl.SetItemCountEx(m_currentChangedArray->GetCount());\r
568                 m_ChangedFileListCtrl.RedrawItems(0, m_currentChangedArray->GetCount());\r
569         }\r
570         else if (m_currentChangedPathList.GetCount())\r
571         {\r
572                 m_ChangedFileListCtrl.SetItemCountEx(m_currentChangedPathList.GetCount());\r
573                 m_ChangedFileListCtrl.RedrawItems(0, m_currentChangedPathList.GetCount());\r
574         }\r
575         else\r
576         {\r
577                 m_ChangedFileListCtrl.SetItemCountEx(0);\r
578                 m_ChangedFileListCtrl.Invalidate();\r
579         }\r
580 #endif\r
581         // sort according to the settings\r
582         if (m_nSortColumnPathList > 0)\r
583                 SetSortArrow(&m_ChangedFileListCtrl, m_nSortColumnPathList, m_bAscendingPathList);\r
584         else\r
585                 SetSortArrow(&m_ChangedFileListCtrl, -1, false);\r
586         m_ChangedFileListCtrl.SetRedraw(TRUE);\r
587 \r
588 }\r
589 \r
590 void CLogDlg::OnBnClickedRefresh()\r
591 {\r
592         m_limit = 0;\r
593         Refresh (true);\r
594 }\r
595 \r
596 void CLogDlg::Refresh (bool /*autoGoOnline*/)\r
597 {\r
598         m_LogList.Refresh();\r
599 }\r
600 \r
601 \r
602 \r
603 BOOL CLogDlg::Cancel()\r
604 {\r
605         return m_bCancelled;\r
606 }\r
607 \r
608 void CLogDlg::SaveSplitterPos()\r
609 {\r
610         if (!IsIconic())\r
611         {\r
612                 CRegDWORD regPos1 = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer1"));\r
613                 CRegDWORD regPos2 = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer2"));\r
614                 RECT rectSplitter;\r
615                 m_wndSplitter1.GetWindowRect(&rectSplitter);\r
616                 ScreenToClient(&rectSplitter);\r
617                 regPos1 = rectSplitter.top;\r
618                 m_wndSplitter2.GetWindowRect(&rectSplitter);\r
619                 ScreenToClient(&rectSplitter);\r
620                 regPos2 = rectSplitter.top;\r
621         }\r
622 }\r
623 \r
624 void CLogDlg::OnCancel()\r
625 {\r
626         // canceling means stopping the working thread if it's still running.\r
627         if (this->IsThreadRunning())\r
628         {\r
629                 m_LogList.TerminateThread();\r
630         }\r
631         UpdateData();\r
632         \r
633         SaveSplitterPos();\r
634         __super::OnCancel();\r
635 }\r
636 \r
637 CString CLogDlg::MakeShortMessage(const CString& message)\r
638 {\r
639         bool bFoundShort = true;\r
640         CString sShortMessage = m_ProjectProperties.GetLogSummary(message);\r
641         if (sShortMessage.IsEmpty())\r
642         {\r
643                 bFoundShort = false;\r
644                 sShortMessage = message;\r
645         }\r
646         // Remove newlines and tabs 'cause those are not shown nicely in the list control\r
647         sShortMessage.Replace(_T("\r"), _T(""));\r
648         sShortMessage.Replace(_T("\t"), _T(" "));\r
649         \r
650         // Suppose the first empty line separates 'summary' from the rest of the message.\r
651         int found = sShortMessage.Find(_T("\n\n"));\r
652         // To avoid too short 'short' messages \r
653         // (e.g. if the message looks something like "Bugfix:\n\n*done this\n*done that")\r
654         // only use the empty newline as a separator if it comes after at least 15 chars.\r
655         if ((!bFoundShort)&&(found >= 15))\r
656         {\r
657                 sShortMessage = sShortMessage.Left(found);\r
658         }\r
659         sShortMessage.Replace('\n', ' ');\r
660         return sShortMessage;\r
661 }\r
662 \r
663 BOOL CLogDlg::Log(git_revnum_t /*rev*/, const CString& /*author*/, const CString& /*date*/, const CString& /*message*/, LogChangedPathArray * /*cpaths*/,  int /*filechanges*/, BOOL /*copies*/, DWORD /*actions*/, BOOL /*haschildren*/)\r
664 {\r
665 #if 0\r
666         if (rev == SVN_INVALID_REVNUM)\r
667         {\r
668                 m_childCounter--;\r
669                 return TRUE;\r
670         }\r
671 \r
672         // this is the callback function which receives the data for every revision we ask the log for\r
673         // we store this information here one by one.\r
674         m_logcounter += 1;\r
675         if (m_startrev == -1)\r
676                 m_startrev = rev;\r
677         if (m_limit != 0)\r
678         {\r
679                 m_limitcounter--;\r
680                 m_LogProgress.SetPos(m_limit - m_limitcounter);\r
681         }\r
682         else if (m_startrev.IsNumber() && m_startrev.IsNumber())\r
683                 m_LogProgress.SetPos((git_revnum_t)m_startrev-rev+(git_revnum_t)m_endrev);\r
684         __time64_t ttime = time/1000000L;\r
685         if (m_tTo < (DWORD)ttime)\r
686                 m_tTo = (DWORD)ttime;\r
687         if (m_tFrom > (DWORD)ttime)\r
688                 m_tFrom = (DWORD)ttime;\r
689         if ((m_lowestRev > rev)||(m_lowestRev < 0))\r
690                 m_lowestRev = rev;\r
691         // Add as many characters from the log message to the list control\r
692         PLOGENTRYDATA pLogItem = new LOGENTRYDATA;\r
693         pLogItem->bCopies = !!copies;\r
694         \r
695         // find out if this item was copied in the revision\r
696         BOOL copiedself = FALSE;\r
697         if (copies)\r
698         {\r
699                 for (INT_PTR cpPathIndex = 0; cpPathIndex < cpaths->GetCount(); ++cpPathIndex)\r
700                 {\r
701                         LogChangedPath * cpath = cpaths->GetAt(cpPathIndex);\r
702                         if (!cpath->sCopyFromPath.IsEmpty() && (cpath->sPath.Compare(m_sSelfRelativeURL) == 0))\r
703                         {\r
704                                 // note: this only works if the log is fetched top-to-bottom\r
705                                 // but since we do that, it shouldn't be a problem\r
706                                 m_sSelfRelativeURL = cpath->sCopyFromPath;\r
707                                 copiedself = TRUE;\r
708                                 break;\r
709                         }\r
710                 }\r
711         }\r
712         pLogItem->bCopiedSelf = copiedself;\r
713         pLogItem->tmDate = ttime;\r
714         pLogItem->sAuthor = author;\r
715         pLogItem->sDate = date;\r
716         pLogItem->sShortMessage = MakeShortMessage(message);\r
717         pLogItem->dwFileChanges = filechanges;\r
718         pLogItem->actions = actions;\r
719         pLogItem->haschildren = haschildren;\r
720         pLogItem->childStackDepth = m_childCounter;\r
721         m_maxChild = max(m_childCounter, m_maxChild);\r
722         if (haschildren)\r
723                 m_childCounter++;\r
724         pLogItem->sBugIDs = m_ProjectProperties.FindBugID(message).Trim();\r
725         \r
726         // split multi line log entries and concatenate them\r
727         // again but this time with \r\n as line separators\r
728         // so that the edit control recognizes them\r
729         try\r
730         {\r
731                 if (message.GetLength()>0)\r
732                 {\r
733                         m_sMessageBuf = message;\r
734                         m_sMessageBuf.Replace(_T("\n\r"), _T("\n"));\r
735                         m_sMessageBuf.Replace(_T("\r\n"), _T("\n"));\r
736                         if (m_sMessageBuf.Right(1).Compare(_T("\n"))==0)\r
737                                 m_sMessageBuf = m_sMessageBuf.Left(m_sMessageBuf.GetLength()-1);\r
738                 }\r
739                 else\r
740                         m_sMessageBuf.Empty();\r
741         pLogItem->sMessage = m_sMessageBuf;\r
742         pLogItem->Rev = rev;\r
743 \r
744         // move-construct path array\r
745 \r
746         pLogItem->pArChangedPaths = new LogChangedPathArray (*cpaths);\r
747         cpaths->RemoveAll();\r
748         }\r
749         catch (CException * e)\r
750         {\r
751                 ::MessageBox(NULL, _T("not enough memory!"), _T("TortoiseGit"), MB_ICONERROR);\r
752                 e->Delete();\r
753                 m_bCancelled = TRUE;\r
754         }\r
755         m_logEntries.push_back(pLogItem);\r
756         m_arShownList.Add(pLogItem);\r
757 #endif\r
758         return TRUE;\r
759 }\r
760 \r
761 GitRev g_rev;\r
762 //this is the thread function which calls the subversion function\r
763 \r
764 \r
765 \r
766 \r
767 void CLogDlg::CopyChangedSelectionToClipBoard()\r
768 {\r
769 \r
770         POSITION pos = m_LogList.GetFirstSelectedItemPosition();\r
771         if (pos == NULL)\r
772                 return; // nothing is selected, get out of here\r
773 \r
774         CString sPaths;\r
775 \r
776 //      CGitRev* pLogEntry = reinterpret_cast<CGitRev* >(m_LogList.m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));\r
777 //      if (pos)\r
778         {\r
779                 POSITION pos = m_ChangedFileListCtrl.GetFirstSelectedItemPosition();\r
780                 while (pos)\r
781                 {\r
782                         int nItem = m_ChangedFileListCtrl.GetNextSelectedItem(pos);\r
783                         CTGitPath *path = (CTGitPath*)m_ChangedFileListCtrl.GetItemData(nItem);\r
784                         if(path)\r
785                                 sPaths += path->GetGitPathString();\r
786                         sPaths += _T("\r\n");\r
787                 }\r
788         }\r
789 #if 0\r
790         else\r
791         {\r
792                 // only one revision is selected in the log dialog top pane\r
793                 // but multiple items could be selected  in the changed items list\r
794                 POSITION pos = m_ChangedFileListCtrl.GetFirstSelectedItemPosition();\r
795                 while (pos)\r
796                 {\r
797                         int nItem = m_ChangedFileListCtrl.GetNextSelectedItem(pos);\r
798                         LogChangedPath * changedlogpath = pLogEntry->pArChangedPaths->GetAt(nItem);\r
799 \r
800                         if ((m_cHidePaths.GetState() & 0x0003)==BST_CHECKED)\r
801                         {\r
802                                 // some items are hidden! So find out which item the user really selected\r
803                                 INT_PTR selRealIndex = -1;\r
804                                 for (INT_PTR hiddenindex=0; hiddenindex<pLogEntry->pArChangedPaths->GetCount(); ++hiddenindex)\r
805                                 {\r
806                                         if (pLogEntry->pArChangedPaths->GetAt(hiddenindex)->sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)\r
807                                                 selRealIndex++;\r
808                                         if (selRealIndex == nItem)\r
809                                         {\r
810                                                 changedlogpath = pLogEntry->pArChangedPaths->GetAt(hiddenindex);\r
811                                                 break;\r
812                                         }\r
813                                 }\r
814                         }\r
815                         if (changedlogpath)\r
816                         {\r
817                                 sPaths += changedlogpath->sPath;\r
818                                 sPaths += _T("\r\n");\r
819                         }\r
820                 }\r
821         }\r
822 #endif\r
823         sPaths.Trim();\r
824         CStringUtils::WriteAsciiStringToClipboard(sPaths, GetSafeHwnd());\r
825 \r
826 }\r
827 \r
828 BOOL CLogDlg::IsDiffPossible(LogChangedPath * /*changedpath*/, git_revnum_t rev)\r
829 {\r
830 #if 0\r
831         CString added, deleted;\r
832         if (changedpath == NULL)\r
833                 return false;\r
834 \r
835         if ((rev > 1)&&(changedpath->action != LOGACTIONS_DELETED))\r
836         {\r
837                 if (changedpath->action == LOGACTIONS_ADDED) // file is added\r
838                 {\r
839                         if (changedpath->lCopyFromRev == 0)\r
840                                 return FALSE; // but file was not added with history\r
841                 }\r
842                 return TRUE;\r
843         }\r
844 #endif\r
845         return FALSE;\r
846 }\r
847 \r
848 void CLogDlg::OnContextMenu(CWnd* pWnd, CPoint point)\r
849 {\r
850         // we have two separate context menus:\r
851         // one shown on the log message list control,\r
852         // the other shown in the changed-files list control\r
853         int selCount = m_LogList.GetSelectedCount();\r
854         if (pWnd == &m_LogList)\r
855         {\r
856                 //ShowContextMenuForRevisions(pWnd, point);\r
857         }\r
858         else if (pWnd == &m_ChangedFileListCtrl)\r
859         {\r
860                 //ShowContextMenuForChangedpaths(pWnd, point);\r
861         }\r
862         else if ((selCount == 1)&&(pWnd == GetDlgItem(IDC_MSGVIEW)))\r
863         {\r
864                 POSITION pos = m_LogList.GetFirstSelectedItemPosition();\r
865                 int selIndex = -1;\r
866                 if (pos)\r
867                         selIndex = m_LogList.GetNextSelectedItem(pos);\r
868                 if ((point.x == -1) && (point.y == -1))\r
869                 {\r
870                         CRect rect;\r
871                         GetDlgItem(IDC_MSGVIEW)->GetClientRect(&rect);\r
872                         ClientToScreen(&rect);\r
873                         point = rect.CenterPoint();\r
874                 }\r
875                 CString sMenuItemText;\r
876                 CMenu popup;\r
877                 if (popup.CreatePopupMenu())\r
878                 {\r
879                         // add the 'default' entries\r
880                         sMenuItemText.LoadString(IDS_SCIEDIT_COPY);\r
881                         popup.AppendMenu(MF_STRING | MF_ENABLED, WM_COPY, sMenuItemText);\r
882                         sMenuItemText.LoadString(IDS_SCIEDIT_SELECTALL);\r
883                         popup.AppendMenu(MF_STRING | MF_ENABLED, EM_SETSEL, sMenuItemText);\r
884 \r
885                         //if (selIndex >= 0)\r
886                         //{\r
887                         //      popup.AppendMenu(MF_SEPARATOR);\r
888                         //      sMenuItemText.LoadString(IDS_LOG_POPUP_EDITLOG);\r
889                         //      popup.AppendMenu(MF_STRING | MF_ENABLED, CGitLogList::ID_EDITAUTHOR, sMenuItemText);\r
890                         //}\r
891 \r
892                         int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
893                         switch (cmd)\r
894                         {\r
895                         case 0:\r
896                                 break;  // no command selected\r
897                         case EM_SETSEL:\r
898                         case WM_COPY:\r
899                                 ::SendMessage(GetDlgItem(IDC_MSGVIEW)->GetSafeHwnd(), cmd, 0, -1);\r
900                                 break;\r
901                         case CGitLogList::ID_EDITAUTHOR:\r
902                                 EditLogMessage(selIndex);\r
903                                 break;\r
904                         }\r
905                 }\r
906         }\r
907 }\r
908 \r
909 \r
910 LRESULT CLogDlg::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/)\r
911 {\r
912 #if 0\r
913     ASSERT(m_pFindDialog != NULL);\r
914 \r
915     if (m_pFindDialog->IsTerminating())\r
916     {\r
917             // invalidate the handle identifying the dialog box.\r
918         m_pFindDialog = NULL;\r
919         return 0;\r
920     }\r
921 \r
922     if(m_pFindDialog->FindNext())\r
923     {\r
924         //read data from dialog\r
925         CString FindText = m_pFindDialog->GetFindString();\r
926         bool bMatchCase = (m_pFindDialog->MatchCase() == TRUE);\r
927                 bool bFound = false;\r
928                 tr1::wregex pat;\r
929                 bool bRegex = ValidateRegexp(FindText, pat, bMatchCase);\r
930 \r
931                 tr1::regex_constants::match_flag_type flags = tr1::regex_constants::match_not_null;\r
932 \r
933                 int i;\r
934                 for (i = this->m_nSearchIndex; i<m_arShownList.GetCount()&&!bFound; i++)\r
935                 {\r
936                         if (bRegex)\r
937                         {\r
938                                 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(i));\r
939 \r
940                                 if (regex_search(wstring((LPCTSTR)pLogEntry->sMessage), pat, flags))\r
941                                 {\r
942                                         bFound = true;\r
943                                         break;\r
944                                 }\r
945                                 LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;\r
946                                 for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount(); ++cpPathIndex)\r
947                                 {\r
948                                         LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);\r
949                                         if (regex_search(wstring((LPCTSTR)cpath->sCopyFromPath), pat, flags))\r
950                                         {\r
951                                                 bFound = true;\r
952                                                 --i;\r
953                                                 break;\r
954                                         }\r
955                                         if (regex_search(wstring((LPCTSTR)cpath->sPath), pat, flags))\r
956                                         {\r
957                                                 bFound = true;\r
958                                                 --i;\r
959                                                 break;\r
960                                         }\r
961                                 }\r
962                         }\r
963                         else\r
964                         {\r
965                                 if (bMatchCase)\r
966                                 {\r
967                                         if (m_logEntries[i]->sMessage.Find(FindText) >= 0)\r
968                                         {\r
969                                                 bFound = true;\r
970                                                 break;\r
971                                         }\r
972                                         PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(i));\r
973                                         LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;\r
974                                         for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount(); ++cpPathIndex)\r
975                                         {\r
976                                                 LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);\r
977                                                 if (cpath->sCopyFromPath.Find(FindText)>=0)\r
978                                                 {\r
979                                                         bFound = true;\r
980                                                         --i;\r
981                                                         break;\r
982                                                 }\r
983                                                 if (cpath->sPath.Find(FindText)>=0)\r
984                                                 {\r
985                                                         bFound = true;\r
986                                                         --i;\r
987                                                         break;\r
988                                                 }\r
989                                         }\r
990                                 }\r
991                                 else\r
992                                 {\r
993                                     PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(i));\r
994                                         CString msg = pLogEntry->sMessage;\r
995                                         msg = msg.MakeLower();\r
996                                         CString find = FindText.MakeLower();\r
997                                         if (msg.Find(find) >= 0)\r
998                                         {\r
999                                                 bFound = TRUE;\r
1000                                                 break;\r
1001                                         }\r
1002                                         LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;\r
1003                                         for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount(); ++cpPathIndex)\r
1004                                         {\r
1005                                                 LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);\r
1006                                                 CString lowerpath = cpath->sCopyFromPath;\r
1007                                                 lowerpath.MakeLower();\r
1008                                                 if (lowerpath.Find(find)>=0)\r
1009                                                 {\r
1010                                                         bFound = TRUE;\r
1011                                                         --i;\r
1012                                                         break;\r
1013                                                 }\r
1014                                                 lowerpath = cpath->sPath;\r
1015                                                 lowerpath.MakeLower();\r
1016                                                 if (lowerpath.Find(find)>=0)\r
1017                                                 {\r
1018                                                         bFound = TRUE;\r
1019                                                         --i;\r
1020                                                         break;\r
1021                                                 }\r
1022                                         }\r
1023                                 } \r
1024                         }\r
1025                 } // for (i = this->m_nSearchIndex; i<m_arShownList.GetItemCount()&&!bFound; i++)\r
1026                 if (bFound)\r
1027                 {\r
1028                         this->m_nSearchIndex = (i+1);\r
1029                         m_LogList.EnsureVisible(i, FALSE);\r
1030                         m_LogList.SetItemState(m_LogList.GetSelectionMark(), 0, LVIS_SELECTED);\r
1031                         m_LogList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);\r
1032                         m_LogList.SetSelectionMark(i);\r
1033                         FillLogMessageCtrl();\r
1034                         UpdateData(FALSE);\r
1035                         m_nSearchIndex++;\r
1036                         if (m_nSearchIndex >= m_arShownList.GetCount())\r
1037                                 m_nSearchIndex = (int)m_arShownList.GetCount()-1;\r
1038                 }\r
1039     } // if(m_pFindDialog->FindNext()) \r
1040         UpdateLogInfoLabel();\r
1041 #endif\r
1042     return 0;\r
1043 }\r
1044 \r
1045 void CLogDlg::OnOK()\r
1046 {\r
1047         // since the log dialog is also used to select revisions for other\r
1048         // dialogs, we have to do some work before closing this dialog\r
1049         if (GetFocus() != GetDlgItem(IDOK))\r
1050                 return; // if the "OK" button doesn't have the focus, do nothing: this prevents closing the dialog when pressing enter\r
1051 \r
1052         \r
1053         if (this->IsThreadRunning())\r
1054         {\r
1055                 m_LogList.TerminateThread();\r
1056         }\r
1057         UpdateData();\r
1058         // check that one and only one row is selected\r
1059         if (m_LogList.GetSelectedCount() == 1)\r
1060         {\r
1061                 // get the selected row\r
1062                 POSITION pos = m_LogList.GetFirstSelectedItemPosition();\r
1063                 int selIndex = m_LogList.GetNextSelectedItem(pos);\r
1064                 if (selIndex < m_LogList.m_arShownList.GetCount())\r
1065                 {\r
1066                         // all ok, pick up the revision\r
1067                         GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_LogList.m_arShownList.GetAt(selIndex));\r
1068                         // extract the hash\r
1069                         m_sSelectedHash = pLogEntry->m_CommitHash;\r
1070                 }\r
1071         }\r
1072         UpdateData(FALSE);\r
1073         SaveSplitterPos();\r
1074         __super::OnOK();\r
1075         \r
1076         #if 0 \r
1077         if (!GetDlgItem(IDOK)->IsWindowVisible() && GetFocus() != GetDlgItem(IDCANCEL))\r
1078                 return; // the Cancel button works as the OK button. But if the cancel button has not the focus, do nothing.\r
1079 \r
1080         CString temp;\r
1081         CString buttontext;\r
1082         GetDlgItemText(IDOK, buttontext);\r
1083         temp.LoadString(IDS_MSGBOX_CANCEL);\r
1084         if (temp.Compare(buttontext) != 0)\r
1085                 __super::OnOK();        // only exit if the button text matches, and that will match only if the thread isn't running anymore\r
1086         m_bCancelled = TRUE;\r
1087         m_selectedRevs.Clear();\r
1088         m_selectedRevsOneRange.Clear();\r
1089         if (m_pNotifyWindow)\r
1090         {\r
1091                 int selIndex = m_LogList.GetSelectionMark();\r
1092                 if (selIndex >= 0)\r
1093                 {       \r
1094                     PLOGENTRYDATA pLogEntry = NULL;\r
1095                         POSITION pos = m_LogList.GetFirstSelectedItemPosition();\r
1096                         pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));\r
1097                         m_selectedRevs.AddRevision(pLogEntry->Rev);\r
1098                         git_revnum_t lowerRev = pLogEntry->Rev;\r
1099                         git_revnum_t higherRev = lowerRev;\r
1100                         while (pos)\r
1101                         {\r
1102                             pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));\r
1103                                 git_revnum_t rev = pLogEntry->Rev;\r
1104                                 m_selectedRevs.AddRevision(pLogEntry->Rev);\r
1105                                 if (lowerRev > rev)\r
1106                                         lowerRev = rev;\r
1107                                 if (higherRev < rev)\r
1108                                         higherRev = rev;\r
1109                         }\r
1110                         if (m_sFilterText.IsEmpty() && m_nSortColumn == 0 && IsSelectionContinuous())\r
1111                         {\r
1112                                 m_selectedRevsOneRange.AddRevRange(lowerRev, higherRev);\r
1113                         }\r
1114                         BOOL bSentMessage = FALSE;\r
1115                         if (m_LogList.GetSelectedCount() == 1)\r
1116                         {\r
1117                                 // if only one revision is selected, check if the path/url with which the dialog was started\r
1118                                 // was directly affected in that revision. If it was, then check if our path was copied from somewhere.\r
1119                                 // if it was copied, use the copy from revision as lowerRev\r
1120                                 if ((pLogEntry)&&(pLogEntry->pArChangedPaths)&&(lowerRev == higherRev))\r
1121                                 {\r
1122                                         CString sUrl = m_path.GetGitPathString();\r
1123                                         if (!m_path.IsUrl())\r
1124                                         {\r
1125                                                 sUrl = GetURLFromPath(m_path);\r
1126                                         }\r
1127                                         sUrl = sUrl.Mid(m_sRepositoryRoot.GetLength());\r
1128                                         for (int cp = 0; cp < pLogEntry->pArChangedPaths->GetCount(); ++cp)\r
1129                                         {\r
1130                                                 LogChangedPath * pData = pLogEntry->pArChangedPaths->GetAt(cp);\r
1131                                                 if (pData)\r
1132                                                 {\r
1133                                                         if (sUrl.Compare(pData->sPath) == 0)\r
1134                                                         {\r
1135                                                                 if (!pData->sCopyFromPath.IsEmpty())\r
1136                                                                 {\r
1137                                                                         lowerRev = pData->lCopyFromRev;\r
1138                                                                         m_pNotifyWindow->SendMessage(WM_REVSELECTED, m_wParam & (MERGE_REVSELECTSTART), lowerRev);\r
1139                                                                         m_pNotifyWindow->SendMessage(WM_REVSELECTED, m_wParam & (MERGE_REVSELECTEND), higherRev);\r
1140                                                                         m_pNotifyWindow->SendMessage(WM_REVLIST, m_selectedRevs.GetCount(), (LPARAM)&m_selectedRevs);\r
1141                                                                         bSentMessage = TRUE;\r
1142                                                                 }\r
1143                                                         }\r
1144                                                 }\r
1145                                         }\r
1146                                 }\r
1147                         }\r
1148                         if ( !bSentMessage )\r
1149                         {\r
1150                                 m_pNotifyWindow->SendMessage(WM_REVSELECTED, m_wParam & (MERGE_REVSELECTSTART | MERGE_REVSELECTMINUSONE), lowerRev);\r
1151                                 m_pNotifyWindow->SendMessage(WM_REVSELECTED, m_wParam & (MERGE_REVSELECTEND | MERGE_REVSELECTMINUSONE), higherRev);\r
1152                                 m_pNotifyWindow->SendMessage(WM_REVLIST, m_selectedRevs.GetCount(), (LPARAM)&m_selectedRevs);\r
1153                                 if (m_selectedRevsOneRange.GetCount())\r
1154                                         m_pNotifyWindow->SendMessage(WM_REVLISTONERANGE, 0, (LPARAM)&m_selectedRevsOneRange);\r
1155                         }\r
1156                 }\r
1157         }\r
1158         UpdateData();\r
1159         CRegDWORD reg = CRegDWORD(_T("Software\\TortoiseGit\\ShowAllEntry"));\r
1160         reg = m_btnShow.GetCurrentEntry();\r
1161         SaveSplitterPos();\r
1162 #endif \r
1163 }\r
1164 \r
1165 void CLogDlg::OnNMDblclkChangedFileList(NMHDR * /*pNMHDR*/, LRESULT *pResult)\r
1166 {\r
1167         // a double click on an entry in the changed-files list has happened\r
1168         *pResult = 0;\r
1169 \r
1170         DiffSelectedFile();\r
1171 }\r
1172 \r
1173 void CLogDlg::DiffSelectedFile()\r
1174 {\r
1175 #if 0\r
1176         if (m_bThreadRunning)\r
1177                 return;\r
1178         UpdateLogInfoLabel();\r
1179         INT_PTR selIndex = m_ChangedFileListCtrl.GetSelectionMark();\r
1180         if (selIndex < 0)\r
1181                 return;\r
1182         if (m_ChangedFileListCtrl.GetSelectedCount() == 0)\r
1183                 return;\r
1184         // find out if there's an entry selected in the log list\r
1185         POSITION pos = m_LogList.GetFirstSelectedItemPosition();\r
1186         PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));\r
1187         git_revnum_t rev1 = pLogEntry->Rev;\r
1188         git_revnum_t rev2 = rev1;\r
1189         if (pos)\r
1190         {\r
1191                 while (pos)\r
1192                 {\r
1193                         // there's at least a second entry selected in the log list: several revisions selected!\r
1194                         pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));\r
1195                         if (pLogEntry)\r
1196                         {\r
1197                                 rev1 = max(rev1,(long)pLogEntry->Rev);\r
1198                                 rev2 = min(rev2,(long)pLogEntry->Rev);\r
1199                         }\r
1200                 }\r
1201                 rev2--;\r
1202                 // now we have both revisions selected in the log list, so we can do a diff of the selected\r
1203                 // entry in the changed files list with these two revisions.\r
1204                 DoDiffFromLog(selIndex, rev1, rev2, false, false);\r
1205         }\r
1206         else\r
1207         {\r
1208                 rev2 = rev1-1;\r
1209                 // nothing or only one revision selected in the log list\r
1210                 LogChangedPath * changedpath = pLogEntry->pArChangedPaths->GetAt(selIndex);\r
1211 \r
1212                 if ((m_cHidePaths.GetState() & 0x0003)==BST_CHECKED)\r
1213                 {\r
1214                         // some items are hidden! So find out which item the user really clicked on\r
1215                         INT_PTR selRealIndex = -1;\r
1216                         for (INT_PTR hiddenindex=0; hiddenindex<pLogEntry->pArChangedPaths->GetCount(); ++hiddenindex)\r
1217                         {\r
1218                                 if (pLogEntry->pArChangedPaths->GetAt(hiddenindex)->sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)\r
1219                                         selRealIndex++;\r
1220                                 if (selRealIndex == selIndex)\r
1221                                 {\r
1222                                         selIndex = hiddenindex;\r
1223                                         changedpath = pLogEntry->pArChangedPaths->GetAt(selIndex);\r
1224                                         break;\r
1225                                 }\r
1226                         }\r
1227                 }\r
1228 \r
1229                 if (IsDiffPossible(changedpath, rev1))\r
1230                 {\r
1231                         // diffs with renamed files are possible\r
1232                         if ((changedpath)&&(!changedpath->sCopyFromPath.IsEmpty()))\r
1233                                 rev2 = changedpath->lCopyFromRev;\r
1234                         else\r
1235                         {\r
1236                                 // if the path was modified but the parent path was 'added with history'\r
1237                                 // then we have to use the copy from revision of the parent path\r
1238                                 CTGitPath cpath = CTGitPath(changedpath->sPath);\r
1239                                 for (int flist = 0; flist < pLogEntry->pArChangedPaths->GetCount(); ++flist)\r
1240                                 {\r
1241                                         CTGitPath p = CTGitPath(pLogEntry->pArChangedPaths->GetAt(flist)->sPath);\r
1242                                         if (p.IsAncestorOf(cpath))\r
1243                                         {\r
1244                                                 if (!pLogEntry->pArChangedPaths->GetAt(flist)->sCopyFromPath.IsEmpty())\r
1245                                                         rev2 = pLogEntry->pArChangedPaths->GetAt(flist)->lCopyFromRev;\r
1246                                         }\r
1247                                 }\r
1248                         }\r
1249                         DoDiffFromLog(selIndex, rev1, rev2, false, false);\r
1250                 }\r
1251                 else \r
1252                 {\r
1253                         CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, CTGitPath(changedpath->sPath));\r
1254                         CTGitPath tempfile2 = CTempFiles::Instance().GetTempFilePath(false, CTGitPath(changedpath->sPath));\r
1255                         GitRev r = rev1;\r
1256                         // deleted files must be opened from the revision before the deletion\r
1257                         if (changedpath->action == LOGACTIONS_DELETED)\r
1258                                 r = rev1-1;\r
1259                         m_bCancelled = false;\r
1260 \r
1261                         CProgressDlg progDlg;\r
1262                         progDlg.SetTitle(IDS_APPNAME);\r
1263                         progDlg.SetAnimation(IDR_DOWNLOAD);\r
1264                         CString sInfoLine;\r
1265                         sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, (LPCTSTR)(m_sRepositoryRoot + changedpath->sPath), (LPCTSTR)r.ToString());\r
1266                         progDlg.SetLine(1, sInfoLine, true);\r
1267                         SetAndClearProgressInfo(&progDlg);\r
1268                         progDlg.ShowModeless(m_hWnd);\r
1269 \r
1270                         if (!Cat(CTGitPath(m_sRepositoryRoot + changedpath->sPath), r, r, tempfile))\r
1271                         {\r
1272                                 m_bCancelled = false;\r
1273                                 if (!Cat(CTGitPath(m_sRepositoryRoot + changedpath->sPath), GitRev::REV_HEAD, r, tempfile))\r
1274                                 {\r
1275                                         progDlg.Stop();\r
1276                                         SetAndClearProgressInfo((HWND)NULL);\r
1277                                         CMessageBox::Show(m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
1278                                         return;\r
1279                                 }\r
1280                         }\r
1281                         progDlg.Stop();\r
1282                         SetAndClearProgressInfo((HWND)NULL);\r
1283 \r
1284                         CString sName1, sName2;\r
1285                         sName1.Format(_T("%s - Revision %ld"), (LPCTSTR)CPathUtils::GetFileNameFromPath(changedpath->sPath), (git_revnum_t)rev1);\r
1286                         sName2.Format(_T("%s - Revision %ld"), (LPCTSTR)CPathUtils::GetFileNameFromPath(changedpath->sPath), (git_revnum_t)rev1-1);\r
1287                         CAppUtils::DiffFlags flags;\r
1288                         flags.AlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
1289                         if (changedpath->action == LOGACTIONS_DELETED)\r
1290                                 CAppUtils::StartExtDiff(tempfile, tempfile2, sName2, sName1, flags);\r
1291                         else\r
1292                                 CAppUtils::StartExtDiff(tempfile2, tempfile, sName2, sName1, flags);\r
1293                 }\r
1294         }\r
1295 #endif \r
1296 }\r
1297 \r
1298 \r
1299 void CLogDlg::DoDiffFromLog(INT_PTR selIndex, GitRev* rev1, GitRev* rev2, bool blame, bool unified)\r
1300 {\r
1301         DialogEnableWindow(IDOK, FALSE);\r
1302 //      SetPromptApp(&theApp);\r
1303         theApp.DoWaitCursor(1);\r
1304 \r
1305         CString temppath;\r
1306         GetTempPath(temppath);\r
1307         \r
1308         CString file1;\r
1309         file1.Format(_T("%s%s_%s%s"),\r
1310                                 temppath,                                               \r
1311                                 (*m_currentChangedArray)[selIndex].GetBaseFilename(),\r
1312                                 rev1->m_CommitHash.Left(6),\r
1313                                 (*m_currentChangedArray)[selIndex].GetFileExtension());\r
1314 \r
1315         CString file2;\r
1316         file2.Format(_T("%s\\%s_%s%s"),\r
1317                                 temppath,                                               \r
1318                                 (*m_currentChangedArray)[selIndex].GetBaseFilename(),\r
1319                                 rev2->m_CommitHash.Left(6),\r
1320                                 (*m_currentChangedArray)[selIndex].GetFileExtension());\r
1321 \r
1322         CString cmd;\r
1323 \r
1324         cmd.Format(_T("git.exe cat-file -p %s:%s"),rev1->m_CommitHash,(*m_currentChangedArray)[selIndex].GetGitPathString());\r
1325         g_Git.RunLogFile(cmd,file1);\r
1326         cmd.Format(_T("git.exe cat-file -p %s:%s"),rev2->m_CommitHash,(*m_currentChangedArray)[selIndex].GetGitPathString());\r
1327         g_Git.RunLogFile(cmd,file2);\r
1328 \r
1329         CAppUtils::DiffFlags flags;\r
1330         CAppUtils::StartExtDiff(file1,file2,_T("A"),_T("B"),flags);\r
1331 \r
1332 #if 0\r
1333         //get the filename\r
1334         CString filepath;\r
1335         if (Git::PathIsURL(m_path))\r
1336         {\r
1337                 filepath = m_path.GetGitPathString();\r
1338         }\r
1339         else\r
1340         {\r
1341                 filepath = GetURLFromPath(m_path);\r
1342                 if (filepath.IsEmpty())\r
1343                 {\r
1344                         theApp.DoWaitCursor(-1);\r
1345                         CString temp;\r
1346                         temp.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)filepath);\r
1347                         CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);\r
1348                         TRACE(_T("could not retrieve the URL of the file!\n"));\r
1349                         EnableOKButton();\r
1350                         theApp.DoWaitCursor(-11);\r
1351                         return;         //exit\r
1352                 }\r
1353         }\r
1354         m_bCancelled = FALSE;\r
1355         filepath = GetRepositoryRoot(CTGitPath(filepath));\r
1356 \r
1357         CString firstfile, secondfile;\r
1358         if (m_LogList.GetSelectedCount()==1)\r
1359         {\r
1360                 int s = m_LogList.GetSelectionMark();\r
1361                 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(s));\r
1362                 LogChangedPath * changedpath = pLogEntry->pArChangedPaths->GetAt(selIndex);\r
1363                 firstfile = changedpath->sPath;\r
1364                 secondfile = firstfile;\r
1365                 if ((rev2 == rev1-1)&&(changedpath->lCopyFromRev > 0)) // is it an added file with history?\r
1366                 {\r
1367                         secondfile = changedpath->sCopyFromPath;\r
1368                         rev2 = changedpath->lCopyFromRev;\r
1369                 }\r
1370         }\r
1371         else\r
1372         {\r
1373                 firstfile = m_currentChangedPathList[selIndex].GetGitPathString();\r
1374                 secondfile = firstfile;\r
1375         }\r
1376 \r
1377         firstfile = filepath + firstfile.Trim();\r
1378         secondfile = filepath + secondfile.Trim();\r
1379 \r
1380         GitDiff diff(this, this->m_hWnd, true);\r
1381         diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
1382         diff.SetHEADPeg(m_LogRevision);\r
1383         if (unified)\r
1384         {\r
1385                 if (PromptShown())\r
1386                         diff.ShowUnifiedDiff(CTGitPath(secondfile), rev2, CTGitPath(firstfile), rev1);\r
1387                 else\r
1388                         CAppUtils::StartShowUnifiedDiff(m_hWnd, CTGitPath(secondfile), rev2, CTGitPath(firstfile), rev1, GitRev(), m_LogRevision);\r
1389         }\r
1390         else\r
1391         {\r
1392                 if (diff.ShowCompare(CTGitPath(secondfile), rev2, CTGitPath(firstfile), rev1, GitRev(), false, blame))\r
1393                 {\r
1394                         if (firstfile.Compare(secondfile)==0)\r
1395                         {\r
1396                                 git_revnum_t baseRev = 0;\r
1397                                 diff.DiffProps(CTGitPath(firstfile), rev2, rev1, baseRev);\r
1398                         }\r
1399                 }\r
1400         }\r
1401 \r
1402 #endif\r
1403 \r
1404         theApp.DoWaitCursor(-1);\r
1405         EnableOKButton();\r
1406 }\r
1407 \r
1408 BOOL CLogDlg::Open(bool /*bOpenWith*/,CString changedpath, git_revnum_t rev)\r
1409 {\r
1410 #if 0\r
1411         DialogEnableWindow(IDOK, FALSE);\r
1412         SetPromptApp(&theApp);\r
1413         theApp.DoWaitCursor(1);\r
1414         CString filepath;\r
1415         if (Git::PathIsURL(m_path))\r
1416         {\r
1417                 filepath = m_path.GetGitPathString();\r
1418         }\r
1419         else\r
1420         {\r
1421                 filepath = GetURLFromPath(m_path);\r
1422                 if (filepath.IsEmpty())\r
1423                 {\r
1424                         theApp.DoWaitCursor(-1);\r
1425                         CString temp;\r
1426                         temp.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)filepath);\r
1427                         CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);\r
1428                         TRACE(_T("could not retrieve the URL of the file!\n"));\r
1429                         EnableOKButton();\r
1430                         return FALSE;\r
1431                 }\r
1432         }\r
1433         m_bCancelled = false;\r
1434         filepath = GetRepositoryRoot(CTGitPath(filepath));\r
1435         filepath += changedpath;\r
1436 \r
1437         CProgressDlg progDlg;\r
1438         progDlg.SetTitle(IDS_APPNAME);\r
1439         progDlg.SetAnimation(IDR_DOWNLOAD);\r
1440         CString sInfoLine;\r
1441         sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, (LPCTSTR)filepath, (LPCTSTR)GitRev(rev).ToString());\r
1442         progDlg.SetLine(1, sInfoLine, true);\r
1443         SetAndClearProgressInfo(&progDlg);\r
1444         progDlg.ShowModeless(m_hWnd);\r
1445 \r
1446         CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, CTGitPath(filepath), rev);\r
1447         m_bCancelled = false;\r
1448         if (!Cat(CTGitPath(filepath), GitRev(rev), rev, tempfile))\r
1449         {\r
1450                 progDlg.Stop();\r
1451                 SetAndClearProgressInfo((HWND)NULL);\r
1452                 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
1453                 EnableOKButton();\r
1454                 theApp.DoWaitCursor(-1);\r
1455                 return FALSE;\r
1456         }\r
1457         progDlg.Stop();\r
1458         SetAndClearProgressInfo((HWND)NULL);\r
1459         SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);\r
1460         if (!bOpenWith)\r
1461         {\r
1462                 int ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);\r
1463                 if (ret <= HINSTANCE_ERROR)\r
1464                         bOpenWith = true;\r
1465         }\r
1466         if (bOpenWith)\r
1467         {\r
1468                 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");\r
1469                 cmd += tempfile.GetWinPathString() + _T(" ");\r
1470                 CAppUtils::LaunchApplication(cmd, NULL, false);\r
1471         }\r
1472         EnableOKButton();\r
1473         theApp.DoWaitCursor(-1);\r
1474 #endif\r
1475         return TRUE;\r
1476 }\r
1477 \r
1478 void CLogDlg::EditAuthor(const CLogDataVector& /*logs*/)\r
1479 {\r
1480 #if 0\r
1481         CString url;\r
1482         CString name;\r
1483         if (logs.size() == 0)\r
1484                 return;\r
1485         DialogEnableWindow(IDOK, FALSE);\r
1486         SetPromptApp(&theApp);\r
1487         theApp.DoWaitCursor(1);\r
1488         if (Git::PathIsURL(m_path))\r
1489                 url = m_path.GetGitPathString();\r
1490         else\r
1491         {\r
1492                 url = GetURLFromPath(m_path);\r
1493         }\r
1494         name = Git_PROP_REVISION_AUTHOR;\r
1495 \r
1496         CString value = RevPropertyGet(name, CTGitPath(url), logs[0]->Rev);\r
1497         CString sOldValue = value;\r
1498         value.Replace(_T("\n"), _T("\r\n"));\r
1499         CInputDlg dlg(this);\r
1500         dlg.m_sHintText.LoadString(IDS_LOG_AUTHOR);\r
1501         dlg.m_sInputText = value;\r
1502         dlg.m_sTitle.LoadString(IDS_LOG_AUTHOREDITTITLE);\r
1503         dlg.m_pProjectProperties = &m_ProjectProperties;\r
1504         dlg.m_bUseLogWidth = false;\r
1505         if (dlg.DoModal() == IDOK)\r
1506         {\r
1507                 dlg.m_sInputText.Replace(_T("\r"), _T(""));\r
1508 \r
1509                 LogCache::CCachedLogInfo* toUpdate \r
1510                         = GetLogCache (CTGitPath (m_sRepositoryRoot));\r
1511 \r
1512                 CProgressDlg progDlg;\r
1513                 progDlg.SetTitle(IDS_APPNAME);\r
1514                 progDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));\r
1515                 progDlg.SetTime(true);\r
1516                 progDlg.SetShowProgressBar(true);\r
1517                 progDlg.ShowModeless(m_hWnd);\r
1518                 for (DWORD i=0; i<logs.size(); ++i)\r
1519                 {\r
1520                         if (!RevPropertySet(name, dlg.m_sInputText, sOldValue, CTGitPath(url), logs[i]->Rev))\r
1521                         {\r
1522                                 progDlg.Stop();\r
1523                                 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
1524                                 break;\r
1525                         }\r
1526                         else\r
1527                         {\r
1528 \r
1529                                 logs[i]->sAuthor = dlg.m_sInputText;\r
1530                                 m_LogList.Invalidate();\r
1531 \r
1532                                 // update the log cache \r
1533 \r
1534                                 if (toUpdate != NULL)\r
1535                                 {\r
1536                                         // log caching is active\r
1537 \r
1538                                         LogCache::CCachedLogInfo newInfo;\r
1539                                         newInfo.Insert ( logs[i]->Rev\r
1540                                                 , (const char*) CUnicodeUtils::GetUTF8 (logs[i]->sAuthor)\r
1541                                                 , ""\r
1542                                                 , 0\r
1543                                                 , LogCache::CRevisionInfoContainer::HAS_AUTHOR);\r
1544 \r
1545                                         toUpdate->Update (newInfo);\r
1546                                 }\r
1547                         }\r
1548                         progDlg.SetProgress64(i, logs.size());\r
1549                 }\r
1550                 progDlg.Stop();\r
1551         }\r
1552         theApp.DoWaitCursor(-1);\r
1553         EnableOKButton();\r
1554 #endif\r
1555 }\r
1556 \r
1557 void CLogDlg::EditLogMessage(int /*index*/)\r
1558 {\r
1559 #if 0\r
1560         CString url;\r
1561         CString name;\r
1562         DialogEnableWindow(IDOK, FALSE);\r
1563         SetPromptApp(&theApp);\r
1564         theApp.DoWaitCursor(1);\r
1565         if (Git::PathIsURL(m_path))\r
1566                 url = m_path.GetGitPathString();\r
1567         else\r
1568         {\r
1569                 url = GetURLFromPath(m_path);\r
1570         }\r
1571         name = Git_PROP_REVISION_LOG;\r
1572 \r
1573         PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(index));\r
1574         m_bCancelled = FALSE;\r
1575         CString value = RevPropertyGet(name, CTGitPath(url), pLogEntry->Rev);\r
1576         CString sOldValue = value;\r
1577         value.Replace(_T("\n"), _T("\r\n"));\r
1578         CInputDlg dlg(this);\r
1579         dlg.m_sHintText.LoadString(IDS_LOG_MESSAGE);\r
1580         dlg.m_sInputText = value;\r
1581         dlg.m_sTitle.LoadString(IDS_LOG_MESSAGEEDITTITLE);\r
1582         dlg.m_pProjectProperties = &m_ProjectProperties;\r
1583         dlg.m_bUseLogWidth = true;\r
1584         if (dlg.DoModal() == IDOK)\r
1585         {\r
1586                 dlg.m_sInputText.Replace(_T("\r"), _T(""));\r
1587                 if (!RevPropertySet(name, dlg.m_sInputText, sOldValue, CTGitPath(url), pLogEntry->Rev))\r
1588                 {\r
1589                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
1590                 }\r
1591                 else\r
1592                 {\r
1593                         pLogEntry->sShortMessage = MakeShortMessage(dlg.m_sInputText);\r
1594                         // split multi line log entries and concatenate them\r
1595                         // again but this time with \r\n as line separators\r
1596                         // so that the edit control recognizes them\r
1597                         if (dlg.m_sInputText.GetLength()>0)\r
1598                         {\r
1599                                 m_sMessageBuf = dlg.m_sInputText;\r
1600                                 dlg.m_sInputText.Replace(_T("\n\r"), _T("\n"));\r
1601                                 dlg.m_sInputText.Replace(_T("\r\n"), _T("\n"));\r
1602                                 if (dlg.m_sInputText.Right(1).Compare(_T("\n"))==0)\r
1603                                         dlg.m_sInputText = dlg.m_sInputText.Left(dlg.m_sInputText.GetLength()-1);\r
1604                         } \r
1605                         else\r
1606                                 dlg.m_sInputText.Empty();\r
1607                         pLogEntry->sMessage = dlg.m_sInputText;\r
1608                         pLogEntry->sBugIDs = m_ProjectProperties.FindBugID(dlg.m_sInputText);\r
1609                         CWnd * pMsgView = GetDlgItem(IDC_MSGVIEW);\r
1610                         pMsgView->SetWindowText(_T(" "));\r
1611                         pMsgView->SetWindowText(dlg.m_sInputText);\r
1612                         m_ProjectProperties.FindBugID(dlg.m_sInputText, pMsgView);\r
1613                         m_LogList.Invalidate();\r
1614         \r
1615             // update the log cache \r
1616 \r
1617             LogCache::CCachedLogInfo* toUpdate \r
1618                 = GetLogCache (CTGitPath (m_sRepositoryRoot));\r
1619             if (toUpdate != NULL)\r
1620             {\r
1621                 // log caching is active\r
1622 \r
1623                 LogCache::CCachedLogInfo newInfo;\r
1624                 newInfo.Insert ( pLogEntry->Rev\r
1625                                , ""\r
1626                                , (const char*) CUnicodeUtils::GetUTF8 (pLogEntry->sMessage)\r
1627                                , 0\r
1628                                , LogCache::CRevisionInfoContainer::HAS_COMMENT);\r
1629 \r
1630                 toUpdate->Update (newInfo);\r
1631             }\r
1632         }\r
1633         }\r
1634         theApp.DoWaitCursor(-1);\r
1635         EnableOKButton();\r
1636 #endif\r
1637 }\r
1638 \r
1639 BOOL CLogDlg::PreTranslateMessage(MSG* pMsg)\r
1640 {\r
1641         // Skip Ctrl-C when copying text out of the log message or search filter\r
1642         BOOL bSkipAccelerator = ( pMsg->message == WM_KEYDOWN && pMsg->wParam=='C' && (GetFocus()==GetDlgItem(IDC_MSGVIEW) || GetFocus()==GetDlgItem(IDC_SEARCHEDIT) ) && GetKeyState(VK_CONTROL)&0x8000 );\r
1643         if (pMsg->message == WM_KEYDOWN && pMsg->wParam=='\r')\r
1644         {\r
1645                 if (GetFocus()==GetDlgItem(IDC_LOGLIST))\r
1646                 {\r
1647                         if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))\r
1648                         {\r
1649                                 m_LogList.DiffSelectedRevWithPrevious();\r
1650                                 return TRUE;\r
1651                         }\r
1652                 }\r
1653                 if (GetFocus()==GetDlgItem(IDC_LOGMSG))\r
1654                 {\r
1655                         DiffSelectedFile();\r
1656                         return TRUE;\r
1657                 }\r
1658         }\r
1659         if (m_hAccel && !bSkipAccelerator)\r
1660         {\r
1661                 int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);\r
1662                 if (ret)\r
1663                         return TRUE;\r
1664         }\r
1665         \r
1666         if(::IsWindow(m_tooltips.m_hWnd))\r
1667                 m_tooltips.RelayEvent(pMsg);\r
1668         return __super::PreTranslateMessage(pMsg);\r
1669 }\r
1670 \r
1671 \r
1672 BOOL CLogDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)\r
1673 {\r
1674         //if (this->IsThreadRunning())\r
1675         if(m_LogList.m_bNoDispUpdates)\r
1676         {\r
1677                 // only show the wait cursor over the list control\r
1678                 if ((pWnd)&&\r
1679                         ((pWnd == GetDlgItem(IDC_LOGLIST))||\r
1680                         (pWnd == GetDlgItem(IDC_MSGVIEW))||\r
1681                         (pWnd == GetDlgItem(IDC_LOGMSG))))\r
1682                 {\r
1683                         HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));\r
1684                         SetCursor(hCur);\r
1685                         return TRUE;\r
1686                 }\r
1687         }\r
1688         if ((pWnd) && (pWnd == GetDlgItem(IDC_MSGVIEW)))\r
1689                 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);\r
1690 \r
1691         HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));\r
1692         SetCursor(hCur);\r
1693         return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);\r
1694 }\r
1695 \r
1696 void CLogDlg::OnBnClickedHelp()\r
1697 {\r
1698         OnHelp();\r
1699 }\r
1700 \r
1701 void CLogDlg::OnLvnItemchangedLoglist(NMHDR *pNMHDR, LRESULT *pResult)\r
1702 {\r
1703         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
1704         *pResult = 0;\r
1705         //if (this->IsThreadRunning())\r
1706         if(m_LogList.m_bNoDispUpdates)\r
1707                 return;\r
1708         if (pNMLV->iItem >= 0)\r
1709         {\r
1710                 m_nSearchIndex = pNMLV->iItem;\r
1711                 if (pNMLV->iSubItem != 0)\r
1712                         return;\r
1713                 if ((pNMLV->iItem == m_LogList.m_arShownList.GetCount()))\r
1714                 {\r
1715                         // remove the selected state\r
1716                         if (pNMLV->uChanged & LVIF_STATE)\r
1717                         {\r
1718                                 m_LogList.SetItemState(pNMLV->iItem, 0, LVIS_SELECTED);\r
1719                                 FillLogMessageCtrl();\r
1720                                 UpdateData(FALSE);\r
1721                                 UpdateLogInfoLabel();\r
1722                         }\r
1723                         return;\r
1724                 }\r
1725                 if (pNMLV->uChanged & LVIF_STATE)\r
1726                 {\r
1727                         FillLogMessageCtrl();\r
1728                         UpdateData(FALSE);\r
1729                 }\r
1730         }\r
1731         else\r
1732         {\r
1733                 FillLogMessageCtrl();\r
1734                 UpdateData(FALSE);\r
1735         }\r
1736         EnableOKButton();\r
1737         UpdateLogInfoLabel();\r
1738 }\r
1739 \r
1740 void CLogDlg::OnEnLinkMsgview(NMHDR *pNMHDR, LRESULT *pResult)\r
1741 {\r
1742         ENLINK *pEnLink = reinterpret_cast<ENLINK *>(pNMHDR);\r
1743         if (pEnLink->msg == WM_LBUTTONUP)\r
1744         {\r
1745                 CString url, msg;\r
1746                 GetDlgItemText(IDC_MSGVIEW, msg);\r
1747                 msg.Replace(_T("\r\n"), _T("\n"));\r
1748                 url = msg.Mid(pEnLink->chrg.cpMin, pEnLink->chrg.cpMax-pEnLink->chrg.cpMin);\r
1749                 if (!::PathIsURL(url))\r
1750                 {\r
1751                         url = m_ProjectProperties.GetBugIDUrl(url);\r
1752                         url = GetAbsoluteUrlFromRelativeUrl(url);\r
1753                 }\r
1754                 if (!url.IsEmpty())\r
1755                         ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);\r
1756         }\r
1757         *pResult = 0;\r
1758 }\r
1759 \r
1760 void CLogDlg::OnBnClickedStatbutton()\r
1761 {\r
1762 \r
1763         if (this->IsThreadRunning())\r
1764                 return;\r
1765         if (m_LogList.m_arShownList.IsEmpty())\r
1766                 return;         // nothing is shown, so no statistics.\r
1767         // the statistics dialog expects the log entries to be sorted by date\r
1768         SortByColumn(3, false);\r
1769         CPtrArray shownlist;\r
1770         m_LogList.RecalculateShownList(&shownlist);\r
1771         // create arrays which are aware of the current filter\r
1772         CStringArray m_arAuthorsFiltered;\r
1773         CDWordArray m_arDatesFiltered;\r
1774         CDWordArray m_arFileChangesFiltered;\r
1775         for (INT_PTR i=0; i<shownlist.GetCount(); ++i)\r
1776         {\r
1777                 GitRev* pLogEntry = reinterpret_cast<GitRev*>(shownlist.GetAt(i));\r
1778                 CString strAuthor = pLogEntry->m_AuthorName;\r
1779                 if ( strAuthor.IsEmpty() )\r
1780                 {\r
1781                         strAuthor.LoadString(IDS_STATGRAPH_EMPTYAUTHOR);\r
1782                 }\r
1783                 m_arAuthorsFiltered.Add(strAuthor);\r
1784                 m_arDatesFiltered.Add(pLogEntry->m_AuthorDate.GetTime());\r
1785                 m_arFileChangesFiltered.Add(pLogEntry->m_Files.GetCount());\r
1786         }\r
1787         CStatGraphDlg dlg;\r
1788         dlg.m_parAuthors = &m_arAuthorsFiltered;\r
1789         dlg.m_parDates = &m_arDatesFiltered;\r
1790         dlg.m_parFileChanges = &m_arFileChangesFiltered;\r
1791         dlg.m_path = m_path;\r
1792         dlg.DoModal();\r
1793         // restore the previous sorting\r
1794         SortByColumn(m_nSortColumn, m_bAscending);\r
1795         OnTimer(LOGFILTER_TIMER);\r
1796 \r
1797 }\r
1798 \r
1799 void CLogDlg::DoSizeV1(int delta)\r
1800 {\r
1801 \r
1802         RemoveAnchor(IDC_LOGLIST);\r
1803         RemoveAnchor(IDC_SPLITTERTOP);\r
1804         RemoveAnchor(IDC_MSGVIEW);\r
1805         RemoveAnchor(IDC_SPLITTERBOTTOM);\r
1806         RemoveAnchor(IDC_LOGMSG);\r
1807         CSplitterControl::ChangeHeight(&m_LogList, delta, CW_TOPALIGN);\r
1808         CSplitterControl::ChangeHeight(GetDlgItem(IDC_MSGVIEW), -delta, CW_BOTTOMALIGN);\r
1809         AddAnchor(IDC_LOGLIST, TOP_LEFT, TOP_RIGHT);\r
1810         AddAnchor(IDC_SPLITTERTOP, TOP_LEFT, TOP_RIGHT);\r
1811         AddAnchor(IDC_MSGVIEW, TOP_LEFT, BOTTOM_RIGHT);\r
1812         AddAnchor(IDC_SPLITTERBOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT);\r
1813         AddAnchor(IDC_LOGMSG, BOTTOM_LEFT, BOTTOM_RIGHT);\r
1814         ArrangeLayout();\r
1815         AdjustMinSize();\r
1816         SetSplitterRange();\r
1817         m_LogList.Invalidate();\r
1818         GetDlgItem(IDC_MSGVIEW)->Invalidate();\r
1819 \r
1820 }\r
1821 \r
1822 void CLogDlg::DoSizeV2(int delta)\r
1823 {\r
1824 \r
1825         RemoveAnchor(IDC_LOGLIST);\r
1826         RemoveAnchor(IDC_SPLITTERTOP);\r
1827         RemoveAnchor(IDC_MSGVIEW);\r
1828         RemoveAnchor(IDC_SPLITTERBOTTOM);\r
1829         RemoveAnchor(IDC_LOGMSG);\r
1830         CSplitterControl::ChangeHeight(GetDlgItem(IDC_MSGVIEW), delta, CW_TOPALIGN);\r
1831         CSplitterControl::ChangeHeight(&m_ChangedFileListCtrl, -delta, CW_BOTTOMALIGN);\r
1832         AddAnchor(IDC_LOGLIST, TOP_LEFT, TOP_RIGHT);\r
1833         AddAnchor(IDC_SPLITTERTOP, TOP_LEFT, TOP_RIGHT);\r
1834         AddAnchor(IDC_MSGVIEW, TOP_LEFT, BOTTOM_RIGHT);\r
1835         AddAnchor(IDC_SPLITTERBOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT);\r
1836         AddAnchor(IDC_LOGMSG, BOTTOM_LEFT, BOTTOM_RIGHT);\r
1837         ArrangeLayout();\r
1838         AdjustMinSize();\r
1839         SetSplitterRange();\r
1840         GetDlgItem(IDC_MSGVIEW)->Invalidate();\r
1841         m_ChangedFileListCtrl.Invalidate();\r
1842 \r
1843 }\r
1844 \r
1845 void CLogDlg::AdjustMinSize()\r
1846 {\r
1847         // adjust the minimum size of the dialog to prevent the resizing from\r
1848         // moving the list control too far down.\r
1849         CRect rcChgListView;\r
1850         m_ChangedFileListCtrl.GetClientRect(rcChgListView);\r
1851         CRect rcLogList;\r
1852         m_LogList.GetClientRect(rcLogList);\r
1853 \r
1854         SetMinTrackSize(CSize(m_DlgOrigRect.Width(), \r
1855                 m_DlgOrigRect.Height()-m_ChgOrigRect.Height()-m_LogListOrigRect.Height()-m_MsgViewOrigRect.Height()\r
1856                 +rcChgListView.Height()+rcLogList.Height()+60));\r
1857 }\r
1858 \r
1859 LRESULT CLogDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) \r
1860 {\r
1861         switch (message) {\r
1862         case WM_NOTIFY:\r
1863                 if (wParam == IDC_SPLITTERTOP)\r
1864                 { \r
1865                         SPC_NMHDR* pHdr = (SPC_NMHDR*) lParam;\r
1866                         DoSizeV1(pHdr->delta);\r
1867                 }\r
1868                 else if (wParam == IDC_SPLITTERBOTTOM)\r
1869                 { \r
1870                         SPC_NMHDR* pHdr = (SPC_NMHDR*) lParam;\r
1871                         DoSizeV2(pHdr->delta);\r
1872                 }\r
1873                 break;\r
1874         }\r
1875 \r
1876         return CResizableDialog::DefWindowProc(message, wParam, lParam);\r
1877 }\r
1878 \r
1879 void CLogDlg::SetSplitterRange()\r
1880 {\r
1881         if ((m_LogList)&&(m_ChangedFileListCtrl))\r
1882         {\r
1883                 CRect rcTop;\r
1884                 m_LogList.GetWindowRect(rcTop);\r
1885                 ScreenToClient(rcTop);\r
1886                 CRect rcMiddle;\r
1887                 GetDlgItem(IDC_MSGVIEW)->GetWindowRect(rcMiddle);\r
1888                 ScreenToClient(rcMiddle);\r
1889                 m_wndSplitter1.SetRange(rcTop.top+30, rcMiddle.bottom-20);\r
1890                 CRect rcBottom;\r
1891                 m_ChangedFileListCtrl.GetWindowRect(rcBottom);\r
1892                 ScreenToClient(rcBottom);\r
1893                 m_wndSplitter2.SetRange(rcMiddle.top+30, rcBottom.bottom-20);\r
1894         }\r
1895 }\r
1896 \r
1897 LRESULT CLogDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)\r
1898 {\r
1899         // FIXME: x64 version would get this function called with unexpected parameters.\r
1900         if (!lParam)\r
1901                 return 0;\r
1902 \r
1903         RECT * rect = (LPRECT)lParam;\r
1904         CPoint point;\r
1905         CString temp;\r
1906         point = CPoint(rect->left, rect->bottom);\r
1907 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_LogList.m_nSelectedFilter == x ? MF_CHECKED : MF_UNCHECKED))\r
1908         CMenu popup;\r
1909         if (popup.CreatePopupMenu())\r
1910         {\r
1911                 temp.LoadString(IDS_LOG_FILTER_ALL);\r
1912                 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_ALL), LOGFILTER_ALL, temp);\r
1913 \r
1914                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
1915                 \r
1916                 temp.LoadString(IDS_LOG_FILTER_MESSAGES);\r
1917                 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_MESSAGES), LOGFILTER_MESSAGES, temp);\r
1918                 temp.LoadString(IDS_LOG_FILTER_PATHS);\r
1919                 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_PATHS), LOGFILTER_PATHS, temp);\r
1920                 temp.LoadString(IDS_LOG_FILTER_AUTHORS);\r
1921                 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);\r
1922                 temp.LoadString(IDS_LOG_FILTER_REVS);\r
1923                 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);\r
1924                 temp.LoadString(IDS_LOG_FILTER_BUGIDS);\r
1925                 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_BUGID), LOGFILTER_BUGID, temp);\r
1926                 \r
1927                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
1928 \r
1929                 temp.LoadString(IDS_LOG_FILTER_REGEX);\r
1930                 popup.AppendMenu(MF_STRING | MF_ENABLED | (m_bFilterWithRegex ? MF_CHECKED : MF_UNCHECKED), LOGFILTER_REGEX, temp);\r
1931 \r
1932                 m_tooltips.Pop();\r
1933                 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
1934                 if (selection != 0)\r
1935                 {\r
1936 \r
1937                         if (selection == LOGFILTER_REGEX)\r
1938                         {\r
1939                                 m_bFilterWithRegex = !m_bFilterWithRegex;\r
1940                                 CRegDWORD b = CRegDWORD(_T("Software\\TortoiseGit\\UseRegexFilter"), TRUE);\r
1941                                 b = m_bFilterWithRegex;\r
1942                                 CheckRegexpTooltip();\r
1943                         }\r
1944                         else\r
1945                         {\r
1946                                 m_LogList.m_nSelectedFilter = selection;\r
1947                                 SetFilterCueText();\r
1948                         }\r
1949                         SetTimer(LOGFILTER_TIMER, 1000, NULL);\r
1950                 }\r
1951         }\r
1952         return 0L;\r
1953 }\r
1954 \r
1955 LRESULT CLogDlg::OnClickedCancelFilter(WPARAM /*wParam*/, LPARAM /*lParam*/)\r
1956 {\r
1957 \r
1958         KillTimer(LOGFILTER_TIMER);\r
1959 \r
1960         m_LogList.m_sFilterText.Empty();\r
1961         UpdateData(FALSE);\r
1962         theApp.DoWaitCursor(1);\r
1963         CStoreSelection storeselection(this);\r
1964         FillLogMessageCtrl(false);\r
1965 \r
1966         m_LogList.RemoveFilter();\r
1967 \r
1968         CTime begin,end;\r
1969         m_LogList.GetTimeRange(begin,end);\r
1970         m_DateFrom.SetTime(&begin);\r
1971         m_DateTo.SetTime(&end);\r
1972 \r
1973         theApp.DoWaitCursor(-1);\r
1974         GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_HIDE);\r
1975         GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_SHOW);\r
1976         GetDlgItem(IDC_SEARCHEDIT)->SetFocus();\r
1977         UpdateLogInfoLabel();\r
1978 \r
1979         return 0L;      \r
1980 }\r
1981 \r
1982 \r
1983 void CLogDlg::SetFilterCueText()\r
1984 {\r
1985         CString temp;\r
1986         switch (m_LogList.m_nSelectedFilter)\r
1987         {\r
1988         case LOGFILTER_ALL:\r
1989                 temp.LoadString(IDS_LOG_FILTER_ALL);\r
1990                 break;\r
1991         case LOGFILTER_MESSAGES:\r
1992                 temp.LoadString(IDS_LOG_FILTER_MESSAGES);\r
1993                 break;\r
1994         case LOGFILTER_PATHS:\r
1995                 temp.LoadString(IDS_LOG_FILTER_PATHS);\r
1996                 break;\r
1997         case LOGFILTER_AUTHORS:\r
1998                 temp.LoadString(IDS_LOG_FILTER_AUTHORS);\r
1999                 break;\r
2000         case LOGFILTER_REVS:\r
2001                 temp.LoadString(IDS_LOG_FILTER_REVS);\r
2002                 break;\r
2003         }\r
2004         // to make the cue banner text appear more to the right of the edit control\r
2005         temp = _T("   ")+temp;\r
2006         m_cFilter.SetCueBanner(temp);\r
2007 }\r
2008 \r
2009 bool CLogDlg::Validate(LPCTSTR string)\r
2010 {\r
2011         if (!m_bFilterWithRegex)\r
2012                 return true;\r
2013         tr1::wregex pat;\r
2014         return m_LogList.ValidateRegexp(string, pat, false);\r
2015 }\r
2016 \r
2017 \r
2018 void CLogDlg::OnTimer(UINT_PTR nIDEvent)\r
2019 {\r
2020         if (nIDEvent == LOGFILTER_TIMER)\r
2021         {\r
2022                 if (this->IsThreadRunning())\r
2023                 {\r
2024                         // thread still running! So just restart the timer.\r
2025                         SetTimer(LOGFILTER_TIMER, 1000, NULL);\r
2026                         return;\r
2027                 }\r
2028                 CWnd * focusWnd = GetFocus();\r
2029                 bool bSetFocusToFilterControl = ((focusWnd != GetDlgItem(IDC_DATEFROM))&&(focusWnd != GetDlgItem(IDC_DATETO))\r
2030                         && (focusWnd != GetDlgItem(IDC_LOGLIST)));\r
2031                 if (m_LogList.m_sFilterText.IsEmpty())\r
2032                 {\r
2033                         DialogEnableWindow(IDC_STATBUTTON, !(((this->IsThreadRunning())||(m_LogList.m_arShownList.IsEmpty()))));\r
2034                         // do not return here!\r
2035                         // we also need to run the filter if the filter text is empty:\r
2036                         // 1. to clear an existing filter\r
2037                         // 2. to rebuild the m_arShownList after sorting\r
2038                 }\r
2039                 theApp.DoWaitCursor(1);\r
2040                 CStoreSelection storeselection(this);\r
2041                 KillTimer(LOGFILTER_TIMER);\r
2042                 FillLogMessageCtrl(false);\r
2043 \r
2044                 // now start filter the log list\r
2045                 m_LogList.StartFilter();\r
2046 \r
2047                 if ( m_LogList.GetItemCount()==1 )\r
2048                 {\r
2049                         m_LogList.SetSelectionMark(0);\r
2050                         m_LogList.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);\r
2051                 }\r
2052                 theApp.DoWaitCursor(-1);\r
2053                 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_HIDE);\r
2054                 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_SHOW);\r
2055                 if (bSetFocusToFilterControl)\r
2056                         GetDlgItem(IDC_SEARCHEDIT)->SetFocus();\r
2057                 UpdateLogInfoLabel();\r
2058         } // if (nIDEvent == LOGFILTER_TIMER)\r
2059         DialogEnableWindow(IDC_STATBUTTON, !(((this->IsThreadRunning())||(m_LogList.m_arShownList.IsEmpty()))));\r
2060         __super::OnTimer(nIDEvent);\r
2061 }\r
2062 \r
2063 void CLogDlg::OnDtnDatetimechangeDateto(NMHDR * /*pNMHDR*/, LRESULT *pResult)\r
2064 {\r
2065         CTime _time;\r
2066         m_DateTo.GetTime(_time);\r
2067         try\r
2068         {\r
2069                 CTime time(_time.GetYear(), _time.GetMonth(), _time.GetDay(), 23, 59, 59);\r
2070                 if (time.GetTime() != m_LogList.m_To.GetTime())\r
2071                 {\r
2072                         m_LogList.m_To = (DWORD)time.GetTime();\r
2073                         SetTimer(LOGFILTER_TIMER, 10, NULL);\r
2074                 }\r
2075         }\r
2076         catch (CAtlException)\r
2077         {\r
2078         }\r
2079         \r
2080         *pResult = 0;\r
2081 }\r
2082 \r
2083 void CLogDlg::OnDtnDatetimechangeDatefrom(NMHDR * /*pNMHDR*/, LRESULT *pResult)\r
2084 {\r
2085         CTime _time;\r
2086         m_DateFrom.GetTime(_time);\r
2087         try\r
2088         {\r
2089                 CTime time(_time.GetYear(), _time.GetMonth(), _time.GetDay(), 0, 0, 0);\r
2090                 if (time.GetTime() != m_LogList.m_From.GetTime())\r
2091                 {\r
2092                         m_LogList.m_From = (DWORD)time.GetTime();\r
2093                         SetTimer(LOGFILTER_TIMER, 10, NULL);\r
2094                 }\r
2095         }\r
2096         catch (CAtlException)\r
2097         {\r
2098         }\r
2099         \r
2100         *pResult = 0;\r
2101 }\r
2102 \r
2103 \r
2104 \r
2105 CTGitPathList CLogDlg::GetChangedPathsFromSelectedRevisions(bool /*bRelativePaths*/ /* = false */, bool /*bUseFilter*/ /* = true */)\r
2106 {\r
2107         CTGitPathList pathList;\r
2108 #if 0\r
2109         \r
2110         if (m_sRepositoryRoot.IsEmpty() && (bRelativePaths == false))\r
2111         {\r
2112                 m_sRepositoryRoot = GetRepositoryRoot(m_path);\r
2113         }\r
2114         if (m_sRepositoryRoot.IsEmpty() && (bRelativePaths == false))\r
2115                 return pathList;\r
2116         \r
2117         POSITION pos = m_LogList.GetFirstSelectedItemPosition();\r
2118         if (pos != NULL)\r
2119         {\r
2120                 while (pos)\r
2121                 {\r
2122                         int nextpos = m_LogList.GetNextSelectedItem(pos);\r
2123                         if (nextpos >= m_arShownList.GetCount())\r
2124                                 continue;\r
2125                         PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(nextpos));\r
2126                         LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;\r
2127                         for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount(); ++cpPathIndex)\r
2128                         {\r
2129                                 LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);\r
2130                                 if (cpath == NULL)\r
2131                                         continue;\r
2132                                 CTGitPath path;\r
2133                                 if (!bRelativePaths)\r
2134                                         path.SetFromGit(m_sRepositoryRoot);\r
2135                                 path.AppendPathString(cpath->sPath);\r
2136                                 if ((!bUseFilter)||\r
2137                                         ((m_cHidePaths.GetState() & 0x0003)!=BST_CHECKED)||\r
2138                                         (cpath->sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0))\r
2139                                         pathList.AddPath(path);\r
2140                                 \r
2141                         }\r
2142                 }\r
2143         }\r
2144         pathList.RemoveDuplicates();\r
2145 #endif\r
2146         return pathList;\r
2147 }\r
2148 \r
2149 void CLogDlg::SortByColumn(int /*nSortColumn*/, bool /*bAscending*/)\r
2150 {\r
2151 #if 0\r
2152         switch(nSortColumn)\r
2153         {\r
2154         case 0: // Revision\r
2155                 {\r
2156                         if(bAscending)\r
2157                                 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscRevSort());\r
2158                         else\r
2159                                 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescRevSort());\r
2160                 }\r
2161                 break;\r
2162         case 1: // action\r
2163                 {\r
2164                         if(bAscending)\r
2165                                 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscActionSort());\r
2166                         else\r
2167                                 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescActionSort());\r
2168                 }\r
2169                 break;\r
2170         case 2: // Author\r
2171                 {\r
2172                         if(bAscending)\r
2173                                 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscAuthorSort());\r
2174                         else\r
2175                                 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescAuthorSort());\r
2176                 }\r
2177                 break;\r
2178         case 3: // Date\r
2179                 {\r
2180                         if(bAscending)\r
2181                                 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscDateSort());\r
2182                         else\r
2183                                 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescDateSort());\r
2184                 }\r
2185                 break;\r
2186         case 4: // Message or bug id\r
2187                 if (m_bShowBugtraqColumn)\r
2188                 {\r
2189                         if(bAscending)\r
2190                                 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscBugIDSort());\r
2191                         else\r
2192                                 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescBugIDSort());\r
2193                         break;\r
2194                 }\r
2195                 // fall through here\r
2196         case 5: // Message\r
2197                 {\r
2198                         if(bAscending)\r
2199                                 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscMessageSort());\r
2200                         else\r
2201                                 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescMessageSort());\r
2202                 }\r
2203                 break;\r
2204         default:\r
2205                 ATLASSERT(0);\r
2206                 break;\r
2207         }\r
2208 #endif\r
2209 }\r
2210 \r
2211 void CLogDlg::OnLvnColumnclick(NMHDR *pNMHDR, LRESULT *pResult)\r
2212 {\r
2213         if (this->IsThreadRunning())\r
2214                 return;         //no sorting while the arrays are filled\r
2215         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
2216         const int nColumn = pNMLV->iSubItem;\r
2217         m_bAscending = nColumn == m_nSortColumn ? !m_bAscending : TRUE;\r
2218         m_nSortColumn = nColumn;\r
2219         SortByColumn(m_nSortColumn, m_bAscending);\r
2220         SetSortArrow(&m_LogList, m_nSortColumn, !!m_bAscending);\r
2221         SortShownListArray();\r
2222         m_LogList.Invalidate();\r
2223         UpdateLogInfoLabel();\r
2224         \r
2225         *pResult = 0;\r
2226 }\r
2227 \r
2228 void CLogDlg::SortShownListArray()\r
2229 {\r
2230         // make sure the shown list still matches the filter after sorting.\r
2231     OnTimer(LOGFILTER_TIMER);\r
2232     // clear the selection states\r
2233         POSITION pos = m_LogList.GetFirstSelectedItemPosition();\r
2234         while (pos)\r
2235         {\r
2236                 m_LogList.SetItemState(m_LogList.GetNextSelectedItem(pos), 0, LVIS_SELECTED);\r
2237         }    \r
2238         m_LogList.SetSelectionMark(-1);\r
2239 }\r
2240 \r
2241 void CLogDlg::SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)\r
2242 {\r
2243         if (control == NULL)\r
2244                 return;\r
2245         // set the sort arrow\r
2246         CHeaderCtrl * pHeader = control->GetHeaderCtrl();\r
2247         HDITEM HeaderItem = {0};\r
2248         HeaderItem.mask = HDI_FORMAT;\r
2249         for (int i=0; i<pHeader->GetItemCount(); ++i)\r
2250         {\r
2251                 pHeader->GetItem(i, &HeaderItem);\r
2252                 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);\r
2253                 pHeader->SetItem(i, &HeaderItem);\r
2254         }\r
2255         if (nColumn >= 0)\r
2256         {\r
2257                 pHeader->GetItem(nColumn, &HeaderItem);\r
2258                 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);\r
2259                 pHeader->SetItem(nColumn, &HeaderItem);\r
2260         }\r
2261 }\r
2262 void CLogDlg::OnLvnColumnclickChangedFileList(NMHDR* /*pNMHDR*/, LRESULT* /*pResult*/)\r
2263 {\r
2264 #if 0\r
2265         if (this->IsThreadRunning())\r
2266                 return;         //no sorting while the arrays are filled\r
2267         if (m_currentChangedArray == NULL)\r
2268                 return;\r
2269         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
2270         const int nColumn = pNMLV->iSubItem;\r
2271         m_bAscendingPathList = nColumn == m_nSortColumnPathList ? !m_bAscendingPathList : TRUE;\r
2272         m_nSortColumnPathList = nColumn;\r
2273 //      qsort(m_currentChangedArray->GetData(), m_currentChangedArray->GetSize(), sizeof(LogChangedPath*), (GENERICCOMPAREFN)SortCompare);\r
2274 \r
2275         SetSortArrow(&m_ChangedFileListCtrl, m_nSortColumnPathList, m_bAscendingPathList);\r
2276         m_ChangedFileListCtrl.Invalidate();\r
2277         *pResult = 0;\r
2278 #endif\r
2279 }\r
2280 \r
2281 int CLogDlg::m_nSortColumnPathList = 0;\r
2282 bool CLogDlg::m_bAscendingPathList = false;\r
2283 \r
2284 int CLogDlg::SortCompare(const void * /*pElem1*/, const void * /*pElem2*/)\r
2285 {\r
2286 #if 0\r
2287         LogChangedPath * cpath1 = *((LogChangedPath**)pElem1);\r
2288         LogChangedPath * cpath2 = *((LogChangedPath**)pElem2);\r
2289 \r
2290         if (m_bAscendingPathList)\r
2291                 std::swap (cpath1, cpath2);\r
2292 \r
2293         int cmp = 0;\r
2294         switch (m_nSortColumnPathList)\r
2295         {\r
2296         case 0: // action\r
2297                         cmp = cpath2->GetAction().Compare(cpath1->GetAction());\r
2298                         if (cmp)\r
2299                                 return cmp;\r
2300                         // fall through\r
2301         case 1: // path\r
2302                         cmp = cpath2->sPath.CompareNoCase(cpath1->sPath);\r
2303                         if (cmp)\r
2304                                 return cmp;\r
2305                         // fall through\r
2306         case 2: // copy from path\r
2307                         cmp = cpath2->sCopyFromPath.Compare(cpath1->sCopyFromPath);\r
2308                         if (cmp)\r
2309                                 return cmp;\r
2310                         // fall through\r
2311         case 3: // copy from revision\r
2312                         return cpath2->lCopyFromRev > cpath1->lCopyFromRev;\r
2313         }\r
2314 #endif\r
2315         return 0;\r
2316 }\r
2317 \r
2318 void CLogDlg::OnBnClickedHidepaths()\r
2319 {\r
2320         FillLogMessageCtrl();\r
2321         m_ChangedFileListCtrl.Invalidate();\r
2322 }\r
2323 \r
2324 \r
2325 \r
2326 void CLogDlg::OnBnClickedCheckStoponcopy()\r
2327 {\r
2328 #if 0\r
2329         if (!GetDlgItem(IDC_GETALL)->IsWindowEnabled())\r
2330                 return;\r
2331 \r
2332         // ignore old fetch limits when switching\r
2333         // between copy-following and stop-on-copy\r
2334         // (otherwise stop-on-copy will limit what\r
2335         // we see immediately after switching to\r
2336         // copy-following)\r
2337 \r
2338         m_endrev = 0;\r
2339 \r
2340         // now, restart the query\r
2341 #endif\r
2342         Refresh();\r
2343 }\r
2344 \r
2345 \r
2346 void CLogDlg::UpdateLogInfoLabel()\r
2347 {\r
2348 \r
2349         git_revnum_t rev1 ;\r
2350         git_revnum_t rev2 ;\r
2351         long selectedrevs = 0;\r
2352         int count =m_LogList.m_arShownList.GetCount();\r
2353         if (count)\r
2354         {\r
2355                 rev1 = (reinterpret_cast<GitRev*>(m_LogList.m_arShownList.GetAt(0)))->m_CommitHash;\r
2356                 //pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_arShownList.GetCount()-1));\r
2357                 rev2 =  (reinterpret_cast<GitRev*>(m_LogList.m_arShownList.GetAt(count-1)))->m_CommitHash;\r
2358                 selectedrevs = m_LogList.GetSelectedCount();\r
2359         }\r
2360         CString sTemp;\r
2361         sTemp.Format(_T("Showing %ld revision(s), from revision %s to revision %s - %ld revision(s) selected"), count, rev2.Left(6), rev1.Left(6), selectedrevs);\r
2362         m_sLogInfo = sTemp;\r
2363 \r
2364         UpdateData(FALSE);\r
2365 }\r
2366 \r
2367 #if 0\r
2368 void CLogDlg::ShowContextMenuForChangedpaths(CWnd* /*pWnd*/, CPoint point)\r
2369 {\r
2370 \r
2371         int selIndex = m_ChangedFileListCtrl.GetSelectionMark();\r
2372         if ((point.x == -1) && (point.y == -1))\r
2373         {\r
2374                 CRect rect;\r
2375                 m_ChangedFileListCtrl.GetItemRect(selIndex, &rect, LVIR_LABEL);\r
2376                 m_ChangedFileListCtrl.ClientToScreen(&rect);\r
2377                 point = rect.CenterPoint();\r
2378         }\r
2379         if (selIndex < 0)\r
2380                 return;\r
2381         int s = m_LogList.GetSelectionMark();\r
2382         if (s < 0)\r
2383                 return;\r
2384         std::vector<CString> changedpaths;\r
2385         std::vector<LogChangedPath*> changedlogpaths;\r
2386         POSITION pos = m_LogList.GetFirstSelectedItemPosition();\r
2387         if (pos == NULL)\r
2388                 return; // nothing is selected, get out of here\r
2389 \r
2390         bool bOneRev = true;\r
2391         int sel=m_LogList.GetNextSelectedItem(pos);\r
2392         GitRev * pLogEntry = reinterpret_cast<GitRev *>(m_LogList.m_arShownList.GetAt(sel));\r
2393         GitRev * rev1 = pLogEntry;\r
2394         GitRev * rev2 = reinterpret_cast<GitRev *>(m_LogList.m_arShownList.GetAt(sel+1));\r
2395 #if 0\r
2396         bool bOneRev = true;\r
2397         if (pos)\r
2398         {\r
2399                 while (pos)\r
2400                 {\r
2401                         pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));\r
2402                         if (pLogEntry)\r
2403                         {\r
2404                                 rev1 = max(rev1,(git_revnum_t)pLogEntry->Rev);\r
2405                                 rev2 = min(rev2,(git_revnum_t)pLogEntry->Rev);\r
2406                                 bOneRev = false;\r
2407                         }                               \r
2408                 }\r
2409                 if (!bOneRev)\r
2410                         rev2--;\r
2411                 POSITION pos = m_ChangedFileListCtrl.GetFirstSelectedItemPosition();\r
2412                 while (pos)\r
2413                 {\r
2414                         int nItem = m_ChangedFileListCtrl.GetNextSelectedItem(pos);\r
2415                         changedpaths.push_back(m_currentChangedPathList[nItem].GetGitPathString());\r
2416                 }\r
2417         }\r
2418         else\r
2419         {\r
2420                 // only one revision is selected in the log dialog top pane\r
2421                 // but multiple items could be selected  in the changed items list\r
2422                 rev2 = rev1-1;\r
2423 \r
2424                 POSITION pos = m_ChangedFileListCtrl.GetFirstSelectedItemPosition();\r
2425                 while (pos)\r
2426                 {\r
2427                         int nItem = m_ChangedFileListCtrl.GetNextSelectedItem(pos);\r
2428                         LogChangedPath * changedlogpath = pLogEntry->pArChangedPaths->GetAt(nItem);\r
2429 \r
2430                         if (m_ChangedFileListCtrl.GetSelectedCount() == 1)\r
2431                         {\r
2432                                 if ((changedlogpath)&&(!changedlogpath->sCopyFromPath.IsEmpty()))\r
2433                                         rev2 = changedlogpath->lCopyFromRev;\r
2434                                 else\r
2435                                 {\r
2436                                         // if the path was modified but the parent path was 'added with history'\r
2437                                         // then we have to use the copy from revision of the parent path\r
2438                                         CTGitPath cpath = CTGitPath(changedlogpath->sPath);\r
2439                                         for (int flist = 0; flist < pLogEntry->pArChangedPaths->GetCount(); ++flist)\r
2440                                         {\r
2441                                                 CTGitPath p = CTGitPath(pLogEntry->pArChangedPaths->GetAt(flist)->sPath);\r
2442                                                 if (p.IsAncestorOf(cpath))\r
2443                                                 {\r
2444                                                         if (!pLogEntry->pArChangedPaths->GetAt(flist)->sCopyFromPath.IsEmpty())\r
2445                                                                 rev2 = pLogEntry->pArChangedPaths->GetAt(flist)->lCopyFromRev;\r
2446                                                 }\r
2447                                         }\r
2448                                 }\r
2449                         }\r
2450                         if ((m_cHidePaths.GetState() & 0x0003)==BST_CHECKED)\r
2451                         {\r
2452                                 // some items are hidden! So find out which item the user really clicked on\r
2453                                 INT_PTR selRealIndex = -1;\r
2454                                 for (INT_PTR hiddenindex=0; hiddenindex<pLogEntry->pArChangedPaths->GetCount(); ++hiddenindex)\r
2455                                 {\r
2456                                         if (pLogEntry->pArChangedPaths->GetAt(hiddenindex)->sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)\r
2457                                                 selRealIndex++;\r
2458                                         if (selRealIndex == nItem)\r
2459                                         {\r
2460                                                 selIndex = hiddenindex;\r
2461                                                 changedlogpath = pLogEntry->pArChangedPaths->GetAt(selIndex);\r
2462                                                 break;\r
2463                                         }\r
2464                                 }\r
2465                         }\r
2466                         if (changedlogpath)\r
2467                         {\r
2468                                 changedpaths.push_back(changedlogpath->sPath);\r
2469                                 changedlogpaths.push_back(changedlogpath);\r
2470                         }\r
2471                 }\r
2472         }\r
2473 #endif\r
2474         //entry is selected, now show the popup menu\r
2475         CIconMenu popup;\r
2476         if (popup.CreatePopupMenu())\r
2477         {\r
2478                 bool bEntryAdded = false;\r
2479                 if (m_ChangedFileListCtrl.GetSelectedCount() == 1)\r
2480                 {\r
2481 //                      if ((!bOneRev)||(IsDiffPossible(changedlogpaths[0], rev1)))\r
2482                         {\r
2483                                 popup.AppendMenuIcon(CGitLogList::ID_DIFF, IDS_LOG_POPUP_DIFF, IDI_DIFF);\r
2484                                 popup.AppendMenuIcon(CGitLogList::ID_BLAMEDIFF, IDS_LOG_POPUP_BLAMEDIFF, IDI_BLAME);\r
2485                                 popup.SetDefaultItem(CGitLogList::ID_DIFF, FALSE);\r
2486                                 popup.AppendMenuIcon(CGitLogList::ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);\r
2487                                 bEntryAdded = true;\r
2488                         }\r
2489 //                      if (rev2 == rev1-1)\r
2490                         {\r
2491                                 if (bEntryAdded)\r
2492                                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
2493                                 popup.AppendMenuIcon(CGitLogList::ID_OPEN, IDS_LOG_POPUP_OPEN, IDI_OPEN);\r
2494                                 popup.AppendMenuIcon(CGitLogList::ID_OPENWITH, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);\r
2495                                 popup.AppendMenuIcon(CGitLogList::ID_BLAME, IDS_LOG_POPUP_BLAME, IDI_BLAME);\r
2496                                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
2497                                 if (m_hasWC)\r
2498                                         popup.AppendMenuIcon(CGitLogList::ID_REVERTREV, IDS_LOG_POPUP_REVERTREV, IDI_REVERT);\r
2499                                 popup.AppendMenuIcon(CGitLogList::ID_POPPROPS, IDS_REPOBROWSE_SHOWPROP, IDI_PROPERTIES);                        // "Show Properties"\r
2500                                 popup.AppendMenuIcon(CGitLogList::ID_LOG, IDS_MENULOG, IDI_LOG);                                                // "Show Log"                           \r
2501                                 popup.AppendMenuIcon(CGitLogList::ID_GETMERGELOGS, IDS_LOG_POPUP_GETMERGELOGS, IDI_LOG);                // "Show merge log"\r
2502                                 popup.AppendMenuIcon(CGitLogList::ID_SAVEAS, IDS_LOG_POPUP_SAVE, IDI_SAVEAS);\r
2503                                 bEntryAdded = true;\r
2504                                 if (!m_ProjectProperties.sWebViewerPathRev.IsEmpty())\r
2505                                 {\r
2506                                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
2507                                         popup.AppendMenuIcon(CGitLogList::ID_VIEWPATHREV, IDS_LOG_POPUP_VIEWPATHREV);\r
2508                                 }\r
2509                                 if (popup.GetDefaultItem(0,FALSE)==-1)\r
2510                                         popup.SetDefaultItem(CGitLogList::ID_OPEN, FALSE);\r
2511                         }\r
2512                 }\r
2513                 else if (changedlogpaths.size())\r
2514                 {\r
2515                         // more than one entry is selected\r
2516                         popup.AppendMenuIcon(CGitLogList::ID_SAVEAS, IDS_LOG_POPUP_SAVE);\r
2517                         bEntryAdded = true;\r
2518                 }\r
2519 \r
2520                 if (!bEntryAdded)\r
2521                         return;\r
2522                 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
2523                 bool bOpenWith = false;\r
2524                 bool bMergeLog = false;\r
2525                 m_bCancelled = false;\r
2526                 \r
2527                 switch (cmd)\r
2528                 {\r
2529                 case CGitLogList::ID_DIFF:\r
2530                         {\r
2531                                 DoDiffFromLog(selIndex, rev1, rev2, false, false);\r
2532                         }\r
2533                         break;\r
2534 #if 0\r
2535                 case ID_BLAMEDIFF:\r
2536                         {\r
2537                                 DoDiffFromLog(selIndex, rev1, rev2, true, false);\r
2538                         }\r
2539                         break;\r
2540                 case ID_GNUDIFF1:\r
2541                         {\r
2542                                 DoDiffFromLog(selIndex, rev1, rev2, false, true);\r
2543                         }\r
2544                         break;\r
2545                 case ID_REVERTREV:\r
2546                         {\r
2547                                 SetPromptApp(&theApp);\r
2548                                 theApp.DoWaitCursor(1);\r
2549                                 CString sUrl;\r
2550                                 if (Git::PathIsURL(m_path))\r
2551                                 {\r
2552                                         sUrl = m_path.GetGitPathString();\r
2553                                 }\r
2554                                 else\r
2555                                 {\r
2556                                         sUrl = GetURLFromPath(m_path);\r
2557                                         if (sUrl.IsEmpty())\r
2558                                         {\r
2559                                                 theApp.DoWaitCursor(-1);\r
2560                                                 CString temp;\r
2561                                                 temp.Format(IDS_ERR_NOURLOFFILE, m_path.GetWinPath());\r
2562                                                 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);\r
2563                                                 EnableOKButton();\r
2564                                                 theApp.DoWaitCursor(-1);\r
2565                                                 break;          //exit\r
2566                                         }\r
2567                                 }\r
2568                                 // find the working copy path of the selected item from the URL\r
2569                                 m_bCancelled = false;\r
2570                                 CString sUrlRoot = GetRepositoryRoot(CTGitPath(sUrl));\r
2571 \r
2572                                 CString fileURL = changedpaths[0];\r
2573                                 fileURL = sUrlRoot + fileURL.Trim();\r
2574                                 // firstfile = (e.g.) http://mydomain.com/repos/trunk/folder/file1\r
2575                                 // sUrl = http://mydomain.com/repos/trunk/folder\r
2576                                 CString sUnescapedUrl = CPathUtils::PathUnescape(sUrl);\r
2577                                 // find out until which char the urls are identical\r
2578                                 int i=0;\r
2579                                 while ((i<fileURL.GetLength())&&(i<sUnescapedUrl.GetLength())&&(fileURL[i]==sUnescapedUrl[i]))\r
2580                                         i++;\r
2581                                 int leftcount = m_path.GetWinPathString().GetLength()-(sUnescapedUrl.GetLength()-i);\r
2582                                 CString wcPath = m_path.GetWinPathString().Left(leftcount);\r
2583                                 wcPath += fileURL.Mid(i);\r
2584                                 wcPath.Replace('/', '\\');\r
2585                                 CGitProgressDlg dlg;\r
2586                                 if (changedlogpaths[0]->action == LOGACTIONS_DELETED)\r
2587                                 {\r
2588                                         // a deleted path! Since the path isn't there anymore, merge\r
2589                                         // won't work. So just do a copy url->wc\r
2590                                         dlg.SetCommand(CGitProgressDlg::GitProgress_Copy);\r
2591                                         dlg.SetPathList(CTGitPathList(CTGitPath(fileURL)));\r
2592                                         dlg.SetUrl(wcPath);\r
2593                                         dlg.SetRevision(rev2);\r
2594                                 }\r
2595                                 else\r
2596                                 {\r
2597                                         if (!PathFileExists(wcPath))\r
2598                                         {\r
2599                                                 // seems the path got renamed\r
2600                                                 // tell the user how to work around this.\r
2601                                                 CMessageBox::Show(this->m_hWnd, IDS_LOG_REVERTREV_ERROR, IDS_APPNAME, MB_ICONERROR);\r
2602                                                 EnableOKButton();\r
2603                                                 theApp.DoWaitCursor(-1);\r
2604                                                 break;          //exit\r
2605                                         }\r
2606                                         dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);\r
2607                                         dlg.SetPathList(CTGitPathList(CTGitPath(wcPath)));\r
2608                                         dlg.SetUrl(fileURL);\r
2609                                         dlg.SetSecondUrl(fileURL);\r
2610                                         GitRevRangeArray revarray;\r
2611                                         revarray.AddRevRange(rev1, rev2);\r
2612                                         dlg.SetRevisionRanges(revarray);\r
2613                                 }\r
2614                                 CString msg;\r
2615                                 msg.Format(IDS_LOG_REVERT_CONFIRM, (LPCTSTR)wcPath);\r
2616                                 if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)\r
2617                                 {\r
2618                                         dlg.DoModal();\r
2619                                 }\r
2620                                 theApp.DoWaitCursor(-1);\r
2621                         }\r
2622                         break;\r
2623                 case ID_POPPROPS:\r
2624                         {\r
2625                                 DialogEnableWindow(IDOK, FALSE);\r
2626                                 SetPromptApp(&theApp);\r
2627                                 theApp.DoWaitCursor(1);\r
2628                                 CString filepath;\r
2629                                 if (Git::PathIsURL(m_path))\r
2630                                 {\r
2631                                         filepath = m_path.GetGitPathString();\r
2632                                 }\r
2633                                 else\r
2634                                 {\r
2635                                         filepath = GetURLFromPath(m_path);\r
2636                                         if (filepath.IsEmpty())\r
2637                                         {\r
2638                                                 theApp.DoWaitCursor(-1);\r
2639                                                 CString temp;\r
2640                                                 temp.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)filepath);\r
2641                                                 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);\r
2642                                                 TRACE(_T("could not retrieve the URL of the file!\n"));\r
2643                                                 EnableOKButton();\r
2644                                                 break;\r
2645                                         }\r
2646                                 }\r
2647                                 filepath = GetRepositoryRoot(CTGitPath(filepath));\r
2648                                 filepath += changedpaths[0];\r
2649                                 CPropDlg dlg;\r
2650                                 dlg.m_rev = rev1;\r
2651                                 dlg.m_Path = CTGitPath(filepath);\r
2652                                 dlg.DoModal();\r
2653                                 EnableOKButton();\r
2654                                 theApp.DoWaitCursor(-1);\r
2655                         }\r
2656                         break;\r
2657                 case ID_SAVEAS:\r
2658                         {\r
2659                                 DialogEnableWindow(IDOK, FALSE);\r
2660                                 SetPromptApp(&theApp);\r
2661                                 theApp.DoWaitCursor(1);\r
2662                                 CString filepath;\r
2663                                 if (Git::PathIsURL(m_path))\r
2664                                 {\r
2665                                         filepath = m_path.GetGitPathString();\r
2666                                 }\r
2667                                 else\r
2668                                 {\r
2669                                         filepath = GetURLFromPath(m_path);\r
2670                                         if (filepath.IsEmpty())\r
2671                                         {\r
2672                                                 theApp.DoWaitCursor(-1);\r
2673                                                 CString temp;\r
2674                                                 temp.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)filepath);\r
2675                                                 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);\r
2676                                                 TRACE(_T("could not retrieve the URL of the file!\n"));\r
2677                                                 EnableOKButton();\r
2678                                                 break;\r
2679                                         }\r
2680                                 }\r
2681                                 m_bCancelled = false;\r
2682                                 CString sRoot = GetRepositoryRoot(CTGitPath(filepath));\r
2683                                 // if more than one entry is selected, we save them\r
2684                                 // one by one into a folder the user has selected\r
2685                                 bool bTargetSelected = false;\r
2686                                 CTGitPath TargetPath;\r
2687                                 if (m_ChangedFileListCtrl.GetSelectedCount() > 1)\r
2688                                 {\r
2689                                         CBrowseFolder browseFolder;\r
2690                                         browseFolder.SetInfo(CString(MAKEINTRESOURCE(IDS_LOG_SAVEFOLDERTOHINT)));\r
2691                                         browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;\r
2692                                         CString strSaveAsDirectory;\r
2693                                         if (browseFolder.Show(GetSafeHwnd(), strSaveAsDirectory) == CBrowseFolder::OK) \r
2694                                         {\r
2695                                                 TargetPath = CTGitPath(strSaveAsDirectory);\r
2696                                                 bTargetSelected = true;\r
2697                                         }\r
2698                                 }\r
2699                                 else\r
2700                                 {\r
2701                                         // Display the Open dialog box. \r
2702                                         CString revFilename;\r
2703                                         CString temp;\r
2704                                         temp = CPathUtils::GetFileNameFromPath(changedpaths[0]);\r
2705                                         int rfind = temp.ReverseFind('.');\r
2706                                         if (rfind > 0)\r
2707                                                 revFilename.Format(_T("%s-%ld%s"), (LPCTSTR)temp.Left(rfind), rev1, (LPCTSTR)temp.Mid(rfind));\r
2708                                         else\r
2709                                                 revFilename.Format(_T("%s-%ld"), (LPCTSTR)temp, rev1);\r
2710                                         bTargetSelected = CAppUtils::FileOpenSave(revFilename, NULL, IDS_LOG_POPUP_SAVE, IDS_COMMONFILEFILTER, false, m_hWnd);\r
2711                                         TargetPath.SetFromWin(revFilename);\r
2712                                 }\r
2713                                 if (bTargetSelected)\r
2714                                 {\r
2715                                         CProgressDlg progDlg;\r
2716                                         progDlg.SetTitle(IDS_APPNAME);\r
2717                                         progDlg.SetAnimation(IDR_DOWNLOAD);\r
2718                                         for (std::vector<LogChangedPath*>::iterator it = changedlogpaths.begin(); it!= changedlogpaths.end(); ++it)\r
2719                                         {\r
2720                                                 GitRev getrev = ((*it)->action == LOGACTIONS_DELETED) ? rev2 : rev1;\r
2721 \r
2722                                                 CString sInfoLine;\r
2723                                                 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, (LPCTSTR)filepath, (LPCTSTR)getrev.ToString());\r
2724                                                 progDlg.SetLine(1, sInfoLine, true);\r
2725                                                 SetAndClearProgressInfo(&progDlg);\r
2726                                                 progDlg.ShowModeless(m_hWnd);\r
2727 \r
2728                                                 CTGitPath tempfile = TargetPath;\r
2729                                                 if (changedpaths.size() > 1)\r
2730                                                 {\r
2731                                                         // if multiple items are selected, then the TargetPath\r
2732                                                         // points to a folder and we have to append the filename\r
2733                                                         // to save to to that folder.\r
2734                                                         CString sName = (*it)->sPath;\r
2735                                                         int slashpos = sName.ReverseFind('/');\r
2736                                                         if (slashpos >= 0)\r
2737                                                                 sName = sName.Mid(slashpos);\r
2738                                                         tempfile.AppendPathString(sName);\r
2739                                                         // one problem here:\r
2740                                                         // a user could have selected multiple items which\r
2741                                                         // have the same filename but reside in different\r
2742                                                         // directories, e.g.\r
2743                                                         // /folder1/file1\r
2744                                                         // /folder2/file1\r
2745                                                         // in that case, the second 'file1' will overwrite\r
2746                                                         // the already saved 'file1'.\r
2747                                                         // \r
2748                                                         // we could maybe find the common root of all selected\r
2749                                                         // items and then create sub folders to save those files\r
2750                                                         // there.\r
2751                                                         // But I think we should just leave it that way: to check\r
2752                                                         // out multiple items at once, the better way is still to\r
2753                                                         // use the export command from the top pane of the log dialog.\r
2754                                                 }\r
2755                                                 filepath = sRoot + (*it)->sPath;\r
2756                                                 if (!Cat(CTGitPath(filepath), getrev, getrev, tempfile))\r
2757                                                 {\r
2758                                                         progDlg.Stop();\r
2759                                                         SetAndClearProgressInfo((HWND)NULL);\r
2760                                                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
2761                                                         EnableOKButton();\r
2762                                                         theApp.DoWaitCursor(-1);\r
2763                                                         break;\r
2764                                                 }\r
2765                                         }\r
2766                                         progDlg.Stop();\r
2767                                         SetAndClearProgressInfo((HWND)NULL);\r
2768                                 }\r
2769                                 EnableOKButton();\r
2770                                 theApp.DoWaitCursor(-1);\r
2771                         }\r
2772                         break;\r
2773                 case ID_OPENWITH:\r
2774                         bOpenWith = true;\r
2775                 case ID_OPEN:\r
2776                         {\r
2777                                 GitRev getrev = pLogEntry->pArChangedPaths->GetAt(selIndex)->action == LOGACTIONS_DELETED ? rev2 : rev1;\r
2778                                 Open(bOpenWith,changedpaths[0],getrev);\r
2779                         }\r
2780                         break;\r
2781                 case ID_BLAME:\r
2782                         {\r
2783                                 CString filepath;\r
2784                                 if (Git::PathIsURL(m_path))\r
2785                                 {\r
2786                                         filepath = m_path.GetGitPathString();\r
2787                                 }\r
2788                                 else\r
2789                                 {\r
2790                                         filepath = GetURLFromPath(m_path);\r
2791                                         if (filepath.IsEmpty())\r
2792                                         {\r
2793                                                 theApp.DoWaitCursor(-1);\r
2794                                                 CString temp;\r
2795                                                 temp.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)filepath);\r
2796                                                 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);\r
2797                                                 TRACE(_T("could not retrieve the URL of the file!\n"));\r
2798                                                 EnableOKButton();\r
2799                                                 break;\r
2800                                         }\r
2801                                 }\r
2802                                 filepath = GetRepositoryRoot(CTGitPath(filepath));\r
2803                                 filepath += changedpaths[0];\r
2804                                 CBlameDlg dlg;\r
2805                                 dlg.EndRev = rev1;\r
2806                                 if (dlg.DoModal() == IDOK)\r
2807                                 {\r
2808                                         CBlame blame;\r
2809                                         CString tempfile;\r
2810                                         CString logfile;\r
2811                                         tempfile = blame.BlameToTempFile(CTGitPath(filepath), dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);\r
2812                                         if (!tempfile.IsEmpty())\r
2813                                         {\r
2814                                                 if (dlg.m_bTextView)\r
2815                                                 {\r
2816                                                         //open the default text editor for the result file\r
2817                                                         CAppUtils::StartTextViewer(tempfile);\r
2818                                                 }\r
2819                                                 else\r
2820                                                 {\r
2821                                                         CString sParams = _T("/path:\"") + filepath + _T("\" ");\r
2822                                                         if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(filepath),sParams))\r
2823                                                         {\r
2824                                                                 break;\r
2825                                                         }\r
2826                                                 }\r
2827                                         }\r
2828                                         else\r
2829                                         {\r
2830                                                 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
2831                                         }\r
2832                                 }\r
2833                         }\r
2834                         break;\r
2835                 case ID_GETMERGELOGS:\r
2836                         bMergeLog = true;\r
2837                         // fall through\r
2838                 case ID_LOG:\r
2839                         {\r
2840                                 DialogEnableWindow(IDOK, FALSE);\r
2841                                 SetPromptApp(&theApp);\r
2842                                 theApp.DoWaitCursor(1);\r
2843                                 CString filepath;\r
2844                                 if (Git::PathIsURL(m_path))\r
2845                                 {\r
2846                                         filepath = m_path.GetGitPathString();\r
2847                                 }\r
2848                                 else\r
2849                                 {\r
2850                                         filepath = GetURLFromPath(m_path);\r
2851                                         if (filepath.IsEmpty())\r
2852                                         {\r
2853                                                 theApp.DoWaitCursor(-1);\r
2854                                                 CString temp;\r
2855                                                 temp.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)filepath);\r
2856                                                 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);\r
2857                                                 TRACE(_T("could not retrieve the URL of the file!\n"));\r
2858                                                 EnableOKButton();\r
2859                                                 break;\r
2860                                         }\r
2861                                 }\r
2862                                 m_bCancelled = false;\r
2863                                 filepath = GetRepositoryRoot(CTGitPath(filepath));\r
2864                                 filepath += changedpaths[0];\r
2865                                 git_revnum_t logrev = rev1;\r
2866                                 if (changedlogpaths[0]->action == LOGACTIONS_DELETED)\r
2867                                 {\r
2868                                         // if the item got deleted in this revision,\r
2869                                         // fetch the log from the previous revision where it\r
2870                                         // still existed.\r
2871                                         logrev--;\r
2872                                 }\r
2873                                 CString sCmd;\r
2874                                 sCmd.Format(_T("\"%s\" /command:log /path:\"%s\" /startrev:%ld"), (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), (LPCTSTR)filepath, logrev);\r
2875                                 if (bMergeLog)\r
2876                                         sCmd += _T(" /merge");\r
2877                                 CAppUtils::LaunchApplication(sCmd, NULL, false);\r
2878                                 EnableOKButton();\r
2879                                 theApp.DoWaitCursor(-1);\r
2880                         }\r
2881                         break;\r
2882                 case ID_VIEWPATHREV:\r
2883                         {\r
2884                                 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetSelectionMark()));\r
2885                                 GitRev rev = pLogEntry->Rev;\r
2886                                 CString relurl = changedpaths[0];\r
2887                                 CString url = m_ProjectProperties.sWebViewerPathRev;\r
2888                                 url.Replace(_T("%REVISION%"), rev.ToString());\r
2889                                 url.Replace(_T("%PATH%"), relurl);\r
2890                                 relurl = relurl.Mid(relurl.Find('/'));\r
2891                                 url.Replace(_T("%PATH1%"), relurl);\r
2892                                 if (!url.IsEmpty())\r
2893                                         ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);                                        \r
2894                         }\r
2895                         break;\r
2896 #endif\r
2897                 default:\r
2898                         break;\r
2899                 } // switch (cmd)\r
2900 \r
2901         } // if (popup.CreatePopupMenu())\r
2902 }\r
2903 #endif\r
2904 \r
2905 void CLogDlg::OnDtnDropdownDatefrom(NMHDR * /*pNMHDR*/, LRESULT *pResult)\r
2906 {\r
2907         // the date control should not show the "today" button\r
2908         CMonthCalCtrl * pCtrl = m_DateFrom.GetMonthCalCtrl();\r
2909         if (pCtrl)\r
2910                 SetWindowLongPtr(pCtrl->GetSafeHwnd(), GWL_STYLE, LONG_PTR(pCtrl->GetStyle() | MCS_NOTODAY));\r
2911         *pResult = 0;\r
2912 }\r
2913 \r
2914 void CLogDlg::OnDtnDropdownDateto(NMHDR * /*pNMHDR*/, LRESULT *pResult)\r
2915 {\r
2916         // the date control should not show the "today" button\r
2917         CMonthCalCtrl * pCtrl = m_DateTo.GetMonthCalCtrl();\r
2918         if (pCtrl)\r
2919                 SetWindowLongPtr(pCtrl->GetSafeHwnd(), GWL_STYLE, LONG_PTR(pCtrl->GetStyle() | MCS_NOTODAY));\r
2920         *pResult = 0;\r
2921 }\r
2922 \r
2923 void CLogDlg::OnSize(UINT nType, int cx, int cy)\r
2924 {\r
2925         __super::OnSize(nType, cx, cy);\r
2926         //set range\r
2927         SetSplitterRange();\r
2928 }\r
2929 \r
2930 void CLogDlg::OnRefresh()\r
2931 {\r
2932         //if (GetDlgItem(IDC_GETALL)->IsWindowEnabled())\r
2933         {\r
2934                 m_limit = 0;\r
2935                 this->m_LogProgress.SetPos(0);\r
2936                 \r
2937                 Refresh (true);\r
2938         }\r
2939 }\r
2940 \r
2941 void CLogDlg::OnFind()\r
2942 {\r
2943         if (!m_pFindDialog)\r
2944         {\r
2945                 m_pFindDialog = new CFindReplaceDialog();\r
2946                 m_pFindDialog->Create(TRUE, NULL, NULL, FR_HIDEUPDOWN | FR_HIDEWHOLEWORD, this);                                                                        \r
2947         }\r
2948 }\r
2949 \r
2950 void CLogDlg::OnFocusFilter()\r
2951 {\r
2952         GetDlgItem(IDC_SEARCHEDIT)->SetFocus(); \r
2953 }\r
2954 \r
2955 void CLogDlg::OnEditCopy()\r
2956 {\r
2957         if (GetFocus() == &m_ChangedFileListCtrl)\r
2958                 CopyChangedSelectionToClipBoard();\r
2959         else\r
2960                 m_LogList.CopySelectionToClipBoard();\r
2961 }\r
2962 \r
2963 CString CLogDlg::GetAbsoluteUrlFromRelativeUrl(const CString& url)\r
2964 {\r
2965         // is the URL a relative one?\r
2966         if (url.Left(2).Compare(_T("^/")) == 0)\r
2967         {\r
2968                 // URL is relative to the repository root\r
2969                 CString url1 = m_sRepositoryRoot + url.Mid(1);\r
2970                 TCHAR buf[INTERNET_MAX_URL_LENGTH];\r
2971                 DWORD len = url.GetLength();\r
2972                 if (UrlCanonicalize((LPCTSTR)url1, buf, &len, 0) == S_OK)\r
2973                         return CString(buf, len);\r
2974                 return url1;\r
2975         }\r
2976         else if (url[0] == '/')\r
2977         {\r
2978                 // URL is relative to the server's hostname\r
2979                 CString sHost;\r
2980                 // find the server's hostname\r
2981                 int schemepos = m_sRepositoryRoot.Find(_T("//"));\r
2982                 if (schemepos >= 0)\r
2983                 {\r
2984                         sHost = m_sRepositoryRoot.Left(m_sRepositoryRoot.Find('/', schemepos+3));\r
2985                         CString url1 = sHost + url;\r
2986                         TCHAR buf[INTERNET_MAX_URL_LENGTH];\r
2987                         DWORD len = url.GetLength();\r
2988                         if (UrlCanonicalize((LPCTSTR)url, buf, &len, 0) == S_OK)\r
2989                                 return CString(buf, len);\r
2990                         return url1;\r
2991                 }\r
2992         }\r
2993         return url;\r
2994 }\r
2995 \r
2996 \r
2997 void CLogDlg::OnEnChangeSearchedit()\r
2998 {\r
2999         UpdateData();\r
3000         if (m_LogList.m_sFilterText.IsEmpty())\r
3001         {\r
3002                 CStoreSelection storeselection(this);\r
3003                 // clear the filter, i.e. make all entries appear\r
3004                 theApp.DoWaitCursor(1);\r
3005                 KillTimer(LOGFILTER_TIMER);\r
3006                 FillLogMessageCtrl(false);\r
3007                 m_LogList.StartFilter();\r
3008 #if 0\r
3009                 InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
3010                 m_arShownList.RemoveAll();\r
3011                 for (DWORD i=0; i<m_logEntries.size(); ++i)\r
3012                 {\r
3013                         if (IsEntryInDateRange(i))\r
3014                                 m_arShownList.Add(m_logEntries[i]);\r
3015                 }\r
3016                 InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
3017                 m_LogList.DeleteAllItems();\r
3018                 m_LogList.SetItemCountEx(ShownCountWithStopped());\r
3019                 m_LogList.RedrawItems(0, ShownCountWithStopped());\r
3020                 m_LogList.SetRedraw(false);\r
3021                 ResizeAllListCtrlCols();\r
3022                 m_LogList.SetRedraw(true);\r
3023 #endif\r
3024                 theApp.DoWaitCursor(-1);\r
3025                 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_HIDE);\r
3026                 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_SHOW);\r
3027                 GetDlgItem(IDC_SEARCHEDIT)->SetFocus();\r
3028                 DialogEnableWindow(IDC_STATBUTTON, !(((this->IsThreadRunning())||(m_LogList.m_arShownList.IsEmpty()))));\r
3029                 return;\r
3030         }\r
3031         if (Validate(m_LogList.m_sFilterText))\r
3032                 SetTimer(LOGFILTER_TIMER, 1000, NULL);\r
3033         else\r
3034                 KillTimer(LOGFILTER_TIMER);\r
3035 \r
3036 }\r
3037 \r
3038 void CLogDlg::OnBnClickedAllBranch()\r
3039 {\r
3040         this->UpdateData();\r
3041 \r
3042         if(this->m_bAllBranch)\r
3043                 m_LogList.m_ShowMask|=CGit::LOG_INFO_ALL_BRANCH;\r
3044         else\r
3045                 m_LogList.m_ShowMask&=~CGit::LOG_INFO_ALL_BRANCH;\r
3046 \r
3047         OnRefresh();\r
3048 \r
3049         FillLogMessageCtrl(false);\r
3050 }\r
3051 \r
3052 \r
3053 void CLogDlg::OnBnClickedFirstParent()\r
3054 {\r
3055         this->UpdateData();\r
3056 \r
3057         if(this->m_bFirstParent)\r
3058                 m_LogList.m_ShowMask|=CGit::LOG_INFO_FIRST_PARENT;\r
3059         else\r
3060                 m_LogList.m_ShowMask&=~CGit::LOG_INFO_FIRST_PARENT;\r
3061 \r
3062         OnRefresh();\r
3063 \r
3064         FillLogMessageCtrl(false);\r
3065 \r
3066 }\r
3067 \r
3068 void CLogDlg::OnBnClickShowWholeProject()\r
3069 {\r
3070         this->UpdateData();\r
3071 \r
3072         if(this->m_bWholeProject)\r
3073         {\r
3074                 m_LogList.m_Path.Reset();\r
3075                 SetWindowText(m_sTitle + _T(" - ") + CString(_T("Whole Project")));\r
3076         }\r
3077         else\r
3078         {\r
3079                 m_LogList.m_Path=m_path;\r
3080                 if(!m_path.IsEmpty())\r
3081                         SetWindowText(m_sTitle + _T(" - ") + m_path.GetGitPathString());\r
3082         }\r
3083         \r
3084         OnRefresh();\r
3085 \r
3086         FillLogMessageCtrl(false);\r
3087 \r
3088 }\r