OSDN Git Service

Continued work on BrowserRefsDlg
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / RepositoryBrowser.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseSVN\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 #include "stdafx.h"\r
20 #include "TortoiseProc.h"\r
21 \r
22 #include "MessageBox.h"\r
23 #include "InputLogDlg.h"\r
24 #include "LogDlg.h"\r
25 #include "PropDlg.h"\r
26 #include "EditPropertiesDlg.h"\r
27 #include "Blame.h"\r
28 #include "BlameDlg.h"\r
29 #include "WaitCursorEx.h"\r
30 #include "Repositorybrowser.h"\r
31 #include "BrowseFolder.h"\r
32 #include "RenameDlg.h"\r
33 #include "RevisionGraph\RevisionGraphDlg.h"\r
34 #include "CheckoutDlg.h"\r
35 #include "ExportDlg.h"\r
36 #include "SVNProgressDlg.h"\r
37 #include "AppUtils.h"\r
38 #include "PathUtils.h"\r
39 #include "StringUtils.h"\r
40 #include "TempFile.h"\r
41 #include "UnicodeUtils.h"\r
42 #include "BrowseFolder.h"\r
43 #include "SVNDiff.h"\r
44 #include "SysImageList.h"\r
45 #include "RepoDrags.h"\r
46 #include "SVNInfo.h"\r
47 #include "SVNDataObject.h"\r
48 #include "SVNLogHelper.h"\r
49 #include "XPTheme.h"\r
50 #include "IconMenu.h"\r
51 \r
52 \r
53 enum RepoBrowserContextMenuCommands\r
54 {\r
55         // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu\r
56         ID_OPEN = 1,\r
57         ID_OPENWITH,\r
58         ID_SHOWLOG,\r
59         ID_REVGRAPH,\r
60         ID_BLAME,\r
61         ID_VIEWREV,\r
62         ID_VIEWPATHREV,\r
63         ID_EXPORT,\r
64         ID_CHECKOUT,\r
65         ID_REFRESH,\r
66         ID_SAVEAS,\r
67         ID_MKDIR,\r
68         ID_IMPORT,\r
69         ID_IMPORTFOLDER,\r
70         ID_BREAKLOCK,\r
71         ID_DELETE,\r
72         ID_RENAME,\r
73         ID_COPYTOWC,\r
74         ID_COPYTO,\r
75         ID_URLTOCLIPBOARD,\r
76         ID_PROPS,\r
77         ID_REVPROPS,\r
78         ID_GNUDIFF,\r
79         ID_DIFF,\r
80         ID_PREPAREDIFF,\r
81         ID_UPDATE,\r
82 \r
83 };\r
84 \r
85 IMPLEMENT_DYNAMIC(CRepositoryBrowser, CResizableStandAloneDialog)\r
86 \r
87 CRepositoryBrowser::CRepositoryBrowser(const CString& url, const SVNRev& rev)\r
88         : CResizableStandAloneDialog(CRepositoryBrowser::IDD, NULL)\r
89         , m_cnrRepositoryBar(&m_barRepository)\r
90         , m_bStandAlone(true)\r
91         , m_InitialUrl(url)\r
92         , m_initialRev(rev)\r
93         , m_bInitDone(false)\r
94         , m_blockEvents(false)\r
95         , m_bSortAscending(true)\r
96         , m_nSortedColumn(0)\r
97         , m_pTreeDropTarget(NULL)\r
98         , m_pListDropTarget(NULL)\r
99         , m_bCancelled(false)\r
100         , m_diffKind(svn_node_none)\r
101         , m_hAccel(NULL)\r
102     , bDragMode(FALSE)\r
103 {\r
104 }\r
105 \r
106 CRepositoryBrowser::CRepositoryBrowser(const CString& url, const SVNRev& rev, CWnd* pParent)\r
107         : CResizableStandAloneDialog(CRepositoryBrowser::IDD, pParent)\r
108         , m_cnrRepositoryBar(&m_barRepository)\r
109         , m_InitialUrl(url)\r
110         , m_initialRev(rev)\r
111         , m_bStandAlone(false)\r
112         , m_bInitDone(false)\r
113         , m_blockEvents(false)\r
114         , m_bSortAscending(true)\r
115         , m_nSortedColumn(0)\r
116         , m_pTreeDropTarget(NULL)\r
117         , m_pListDropTarget(NULL)\r
118         , m_bCancelled(false)\r
119         , m_diffKind(svn_node_none)\r
120 {\r
121 }\r
122 \r
123 CRepositoryBrowser::~CRepositoryBrowser()\r
124 {\r
125 }\r
126 \r
127 void CRepositoryBrowser::RecursiveRemove(HTREEITEM hItem, bool bChildrenOnly /* = false */)\r
128 {\r
129         HTREEITEM childItem;\r
130         if (m_RepoTree.ItemHasChildren(hItem))\r
131         {\r
132                 for (childItem = m_RepoTree.GetChildItem(hItem);childItem != NULL; childItem = m_RepoTree.GetNextItem(childItem, TVGN_NEXT))\r
133                 {\r
134                         RecursiveRemove(childItem);\r
135                         if (bChildrenOnly)\r
136                         {\r
137                                 CTreeItem * pTreeItem = (CTreeItem*)m_RepoTree.GetItemData(childItem);\r
138                                 delete pTreeItem;\r
139                                 m_RepoTree.SetItemData(childItem, 0);\r
140                                 m_RepoTree.DeleteItem(childItem);\r
141                         }\r
142                 }\r
143         }\r
144 \r
145         if ((hItem)&&(!bChildrenOnly))\r
146         {\r
147                 CTreeItem * pTreeItem = (CTreeItem*)m_RepoTree.GetItemData(hItem);\r
148                 delete pTreeItem;\r
149                 m_RepoTree.SetItemData(hItem, 0);\r
150         }\r
151 }\r
152 \r
153 void CRepositoryBrowser::DoDataExchange(CDataExchange* pDX)\r
154 {\r
155         CResizableStandAloneDialog::DoDataExchange(pDX);\r
156         DDX_Control(pDX, IDC_REPOTREE, m_RepoTree);\r
157         DDX_Control(pDX, IDC_REPOLIST, m_RepoList);\r
158 }\r
159 \r
160 BEGIN_MESSAGE_MAP(CRepositoryBrowser, CResizableStandAloneDialog)\r
161         ON_BN_CLICKED(IDHELP, OnBnClickedHelp)\r
162         ON_WM_SETCURSOR()\r
163         ON_REGISTERED_MESSAGE(WM_AFTERINIT, OnAfterInitDialog) \r
164         ON_WM_MOUSEMOVE()\r
165         ON_WM_LBUTTONDOWN()\r
166         ON_WM_LBUTTONUP()\r
167         ON_NOTIFY(TVN_SELCHANGED, IDC_REPOTREE, &CRepositoryBrowser::OnTvnSelchangedRepotree)\r
168         ON_NOTIFY(TVN_ITEMEXPANDING, IDC_REPOTREE, &CRepositoryBrowser::OnTvnItemexpandingRepotree)\r
169         ON_NOTIFY(NM_DBLCLK, IDC_REPOLIST, &CRepositoryBrowser::OnNMDblclkRepolist)\r
170         ON_NOTIFY(HDN_ITEMCLICK, 0, &CRepositoryBrowser::OnHdnItemclickRepolist)\r
171         ON_NOTIFY(LVN_ITEMCHANGED, IDC_REPOLIST, &CRepositoryBrowser::OnLvnItemchangedRepolist)\r
172         ON_NOTIFY(LVN_BEGINDRAG, IDC_REPOLIST, &CRepositoryBrowser::OnLvnBegindragRepolist)\r
173         ON_NOTIFY(LVN_BEGINRDRAG, IDC_REPOLIST, &CRepositoryBrowser::OnLvnBeginrdragRepolist)\r
174         ON_WM_CONTEXTMENU()\r
175         ON_NOTIFY(LVN_ENDLABELEDIT, IDC_REPOLIST, &CRepositoryBrowser::OnLvnEndlabeleditRepolist)\r
176         ON_NOTIFY(TVN_ENDLABELEDIT, IDC_REPOTREE, &CRepositoryBrowser::OnTvnEndlabeleditRepotree)\r
177         ON_WM_TIMER()\r
178         ON_COMMAND(ID_URL_FOCUS, &CRepositoryBrowser::OnUrlFocus)\r
179         ON_COMMAND(ID_EDIT_COPY, &CRepositoryBrowser::OnCopy)\r
180         ON_COMMAND(ID_INLINEEDIT, &CRepositoryBrowser::OnInlineedit)\r
181         ON_COMMAND(ID_REFRESHBROWSER, &CRepositoryBrowser::OnRefresh)\r
182         ON_COMMAND(ID_DELETEBROWSERITEM, &CRepositoryBrowser::OnDelete)\r
183         ON_COMMAND(ID_URL_UP, &CRepositoryBrowser::OnGoUp)\r
184         ON_NOTIFY(TVN_BEGINDRAG, IDC_REPOTREE, &CRepositoryBrowser::OnTvnBegindragRepotree)\r
185         ON_NOTIFY(TVN_BEGINRDRAG, IDC_REPOTREE, &CRepositoryBrowser::OnTvnBeginrdragRepotree)\r
186 END_MESSAGE_MAP()\r
187 \r
188 SVNRev CRepositoryBrowser::GetRevision() const\r
189 {\r
190         return m_barRepository.GetCurrentRev();\r
191 }\r
192 \r
193 CString CRepositoryBrowser::GetPath() const\r
194 {\r
195         return m_barRepository.GetCurrentUrl();\r
196 }\r
197 \r
198 BOOL CRepositoryBrowser::OnInitDialog()\r
199 {\r
200         CResizableStandAloneDialog::OnInitDialog();\r
201 \r
202         GetWindowText(m_origDlgTitle);\r
203 \r
204         m_hAccel = LoadAccelerators(AfxGetResourceHandle(),MAKEINTRESOURCE(IDR_ACC_REPOBROWSER));\r
205 \r
206         m_cnrRepositoryBar.SubclassDlgItem(IDC_REPOS_BAR_CNR, this);\r
207         m_barRepository.Create(&m_cnrRepositoryBar, 12345);\r
208         m_barRepository.SetIRepo(this);\r
209 \r
210         m_pTreeDropTarget = new CTreeDropTarget(this);\r
211         RegisterDragDrop(m_RepoTree.GetSafeHwnd(), m_pTreeDropTarget);\r
212         // create the supported formats:\r
213         FORMATETC ftetc={0}; \r
214         ftetc.cfFormat = CF_SVNURL; \r
215         ftetc.dwAspect = DVASPECT_CONTENT; \r
216         ftetc.lindex = -1; \r
217         ftetc.tymed = TYMED_HGLOBAL; \r
218         m_pTreeDropTarget->AddSuportedFormat(ftetc); \r
219         ftetc.cfFormat=CF_HDROP; \r
220         m_pTreeDropTarget->AddSuportedFormat(ftetc);\r
221 \r
222         m_pListDropTarget = new CListDropTarget(this);\r
223         RegisterDragDrop(m_RepoList.GetSafeHwnd(), m_pListDropTarget);\r
224         // create the supported formats:\r
225         ftetc.cfFormat = CF_SVNURL; \r
226         m_pListDropTarget->AddSuportedFormat(ftetc); \r
227         ftetc.cfFormat=CF_HDROP; \r
228         m_pListDropTarget->AddSuportedFormat(ftetc);\r
229 \r
230         if (m_bStandAlone)\r
231         {\r
232                 GetDlgItem(IDCANCEL)->ShowWindow(FALSE);\r
233 \r
234                 // reposition the buttons\r
235                 CRect rect_cancel;\r
236                 GetDlgItem(IDCANCEL)->GetWindowRect(rect_cancel);\r
237                 ScreenToClient(rect_cancel);\r
238                 GetDlgItem(IDOK)->MoveWindow(rect_cancel);\r
239         }\r
240 \r
241         m_nIconFolder = SYS_IMAGE_LIST().GetDirIconIndex();\r
242         m_nOpenIconFolder = SYS_IMAGE_LIST().GetDirOpenIconIndex();\r
243         // set up the list control\r
244         // set the extended style of the list control\r
245         // the style LVS_EX_FULLROWSELECT interferes with the background watermark image but it's more important to be able to select in the whole row.\r
246         CRegDWORD regFullRowSelect(_T("Software\\TortoiseGit\\FullRowSelect"), TRUE);\r
247         DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_SUBITEMIMAGES;\r
248         if (DWORD(regFullRowSelect))\r
249                 exStyle |= LVS_EX_FULLROWSELECT;\r
250         m_RepoList.SetExtendedStyle(exStyle);\r
251         m_RepoList.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL);\r
252         m_RepoList.ShowText(CString(MAKEINTRESOURCE(IDS_REPOBROWSE_INITWAIT)));\r
253 \r
254         m_RepoTree.SetImageList(&SYS_IMAGE_LIST(), TVSIL_NORMAL);\r
255 \r
256         CXPTheme theme;\r
257         theme.SetWindowTheme(m_RepoList.GetSafeHwnd(), L"Explorer", NULL);\r
258         theme.SetWindowTheme(m_RepoTree.GetSafeHwnd(), L"Explorer", NULL);\r
259 \r
260 \r
261         AddAnchor(IDC_REPOS_BAR_CNR, TOP_LEFT, TOP_RIGHT);\r
262         AddAnchor(IDC_F5HINT, BOTTOM_LEFT, BOTTOM_RIGHT);\r
263         AddAnchor(IDC_REPOTREE, TOP_LEFT, BOTTOM_LEFT);\r
264         AddAnchor(IDC_REPOLIST, TOP_LEFT, BOTTOM_RIGHT);\r
265         AddAnchor(IDCANCEL, BOTTOM_RIGHT);\r
266         AddAnchor(IDOK, BOTTOM_RIGHT);\r
267         AddAnchor(IDHELP, BOTTOM_RIGHT);\r
268         EnableSaveRestore(_T("RepositoryBrowser"));\r
269         if (hWndExplorer)\r
270                 CenterWindow(CWnd::FromHandle(hWndExplorer));\r
271         m_bThreadRunning = true;\r
272         if (AfxBeginThread(InitThreadEntry, this)==NULL)\r
273         {\r
274                 m_bThreadRunning = false;\r
275                 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
276         }\r
277         return TRUE;\r
278 }\r
279 \r
280 void CRepositoryBrowser::InitRepo()\r
281 {\r
282         CWaitCursorEx wait;\r
283 \r
284         m_InitialUrl = CPathUtils::PathUnescape(m_InitialUrl);\r
285         if (m_InitialUrl.Find('?')>=0)\r
286         {\r
287                 m_initialRev = SVNRev(m_InitialUrl.Mid(m_InitialUrl.Find('?')+1));\r
288                 m_InitialUrl = m_InitialUrl.Left(m_InitialUrl.Find('?'));\r
289         }\r
290 \r
291         // We don't know if the url passed to us points to a file or a folder,\r
292         // let's find out:\r
293         SVNInfo info;\r
294         const SVNInfoData * data = NULL;\r
295         CString error;  // contains the first error of GetFirstFileInfo()\r
296         do \r
297         {\r
298                 data = info.GetFirstFileInfo(CTSVNPath(m_InitialUrl),m_initialRev, m_initialRev);\r
299                 if ((data == NULL)||(data->kind != svn_node_dir))\r
300                 {\r
301                         // in case the url is not a valid directory, try the parent dir\r
302                         // until there's no more parent dir\r
303                         m_InitialUrl = m_InitialUrl.Left(m_InitialUrl.ReverseFind('/'));\r
304                         if ((m_InitialUrl.Compare(_T("http://")) == 0) ||\r
305                                 (m_InitialUrl.Compare(_T("https://")) == 0)||\r
306                                 (m_InitialUrl.Compare(_T("svn://")) == 0)||\r
307                                 (m_InitialUrl.Compare(_T("svn+ssh://")) == 0)||\r
308                                 (m_InitialUrl.Compare(_T("file:///")) == 0)||\r
309                                 (m_InitialUrl.Compare(_T("file://")) == 0))\r
310                         {\r
311                                 m_InitialUrl.Empty();\r
312                         }\r
313                         if (error.IsEmpty())\r
314                         {\r
315                                 if (((data)&&(data->kind == svn_node_dir))||(data == NULL))\r
316                                         error = info.GetLastErrorMsg();\r
317                         }\r
318                 }\r
319         } while(!m_InitialUrl.IsEmpty() && ((data == NULL) || (data->kind != svn_node_dir)));\r
320 \r
321         if (data == NULL)\r
322         {\r
323                 m_InitialUrl.Empty();\r
324                 m_RepoList.ShowText(error, true);\r
325                 return;\r
326         }\r
327         else if (m_initialRev.IsHead())\r
328         {\r
329                 m_barRepository.SetHeadRevision(data->rev);\r
330         }\r
331         m_InitialUrl.TrimRight('/');\r
332 \r
333         m_bCancelled = false;\r
334         m_strReposRoot = data->reposRoot;\r
335         m_sUUID = data->reposUUID;\r
336         m_strReposRoot = CPathUtils::PathUnescape(m_strReposRoot);\r
337         // the initial url can be in the format file:///\, but the\r
338         // repository root returned would still be file://\r
339         // to avoid string length comparison faults, we adjust\r
340         // the repository root here to match the initial url\r
341         if ((m_InitialUrl.Left(9).CompareNoCase(_T("file:///\\")) == 0) &&\r
342                 (m_strReposRoot.Left(9).CompareNoCase(_T("file:///\\")) != 0))\r
343                 m_strReposRoot.Replace(_T("file://"), _T("file:///\\"));\r
344         SetWindowText(m_strReposRoot + _T(" - ") + m_origDlgTitle);\r
345         // now check the repository root for the url type, then\r
346         // set the corresponding background image\r
347         if (!m_strReposRoot.IsEmpty())\r
348         {\r
349                 UINT nID = IDI_REPO_UNKNOWN;\r
350                 if (m_strReposRoot.Left(7).CompareNoCase(_T("http://"))==0)\r
351                         nID = IDI_REPO_HTTP;\r
352                 if (m_strReposRoot.Left(8).CompareNoCase(_T("https://"))==0)\r
353                         nID = IDI_REPO_HTTPS;\r
354                 if (m_strReposRoot.Left(6).CompareNoCase(_T("svn://"))==0)\r
355                         nID = IDI_REPO_SVN;\r
356                 if (m_strReposRoot.Left(10).CompareNoCase(_T("svn+ssh://"))==0)\r
357                         nID = IDI_REPO_SVNSSH;\r
358                 if (m_strReposRoot.Left(7).CompareNoCase(_T("file://"))==0)\r
359                         nID = IDI_REPO_FILE;\r
360                 CXPTheme theme;\r
361                 if (theme.IsAppThemed())\r
362                         CAppUtils::SetListCtrlBackgroundImage(m_RepoList.GetSafeHwnd(), nID);\r
363         }\r
364 }\r
365 \r
366 UINT CRepositoryBrowser::InitThreadEntry(LPVOID pVoid)\r
367 {\r
368         return ((CRepositoryBrowser*)pVoid)->InitThread();\r
369 }\r
370 \r
371 //this is the thread function which calls the subversion function\r
372 UINT CRepositoryBrowser::InitThread()\r
373 {\r
374         // In this thread, we try to find out the repository root.\r
375         // Since this is a remote operation, it can take a while, that's\r
376         // Why we do this inside a thread.\r
377 \r
378         // force the cursor to change\r
379         RefreshCursor();\r
380 \r
381         DialogEnableWindow(IDOK, FALSE);\r
382         DialogEnableWindow(IDCANCEL, FALSE);\r
383 \r
384         InitRepo();\r
385 \r
386         PostMessage(WM_AFTERINIT);\r
387         DialogEnableWindow(IDOK, TRUE);\r
388         DialogEnableWindow(IDCANCEL, TRUE);\r
389         \r
390         m_bThreadRunning = false;\r
391 \r
392         RefreshCursor();\r
393         return 0;\r
394 }\r
395 \r
396 LRESULT CRepositoryBrowser::OnAfterInitDialog(WPARAM /*wParam*/, LPARAM /*lParam*/)\r
397 {\r
398         if ((m_InitialUrl.IsEmpty())||(m_strReposRoot.IsEmpty()))\r
399         {\r
400                 return 0;\r
401         }\r
402 \r
403         m_barRepository.GotoUrl(m_InitialUrl, m_initialRev, true);\r
404         m_RepoList.ClearText();\r
405         m_bInitDone = TRUE;\r
406         return 0;\r
407 }\r
408 \r
409 void CRepositoryBrowser::OnOK()\r
410 {\r
411         RevokeDragDrop(m_RepoList.GetSafeHwnd());\r
412         RevokeDragDrop(m_RepoTree.GetSafeHwnd());\r
413 \r
414         SaveColumnWidths(true);\r
415 \r
416         HTREEITEM hItem = m_RepoTree.GetRootItem();\r
417         RecursiveRemove(hItem);\r
418 \r
419         m_barRepository.SaveHistory();\r
420         CResizableStandAloneDialog::OnOK();\r
421 }\r
422 \r
423 void CRepositoryBrowser::OnCancel()\r
424 {\r
425         RevokeDragDrop(m_RepoList.GetSafeHwnd());\r
426         RevokeDragDrop(m_RepoTree.GetSafeHwnd());\r
427 \r
428         SaveColumnWidths(true);\r
429 \r
430         HTREEITEM hItem = m_RepoTree.GetRootItem();\r
431         RecursiveRemove(hItem);\r
432 \r
433         __super::OnCancel();\r
434 }\r
435 \r
436 void CRepositoryBrowser::OnBnClickedHelp()\r
437 {\r
438         OnHelp();\r
439 }\r
440 \r
441 /******************************************************************************/\r
442 /* tree and list view resizing                                                */\r
443 /******************************************************************************/\r
444 \r
445 BOOL CRepositoryBrowser::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) \r
446 {\r
447         if (m_bThreadRunning)\r
448         {\r
449                 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));\r
450                 SetCursor(hCur);\r
451                 return TRUE;\r
452         }\r
453         if (pWnd == this)\r
454         {\r
455                 RECT rect;\r
456                 POINT pt;\r
457                 GetClientRect(&rect);\r
458                 GetCursorPos(&pt);\r
459                 ScreenToClient(&pt);\r
460                 if (PtInRect(&rect, pt))\r
461                 {\r
462                         ClientToScreen(&pt);\r
463                         // are we right of the tree control?\r
464                         GetDlgItem(IDC_REPOTREE)->GetWindowRect(&rect);\r
465                         if ((pt.x > rect.right)&&\r
466                                 (pt.y >= rect.top)&&\r
467                                 (pt.y <= rect.bottom))\r
468                         {\r
469                                 // but left of the list control?\r
470                                 GetDlgItem(IDC_REPOLIST)->GetWindowRect(&rect);\r
471                                 if (pt.x < rect.left)\r
472                                 {\r
473                                         HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZEWE));\r
474                                         SetCursor(hCur);\r
475                                         return TRUE;\r
476                                 }\r
477                         }\r
478                 }\r
479         }\r
480         return CStandAloneDialogTmpl<CResizableDialog>::OnSetCursor(pWnd, nHitTest, message);\r
481 }\r
482 \r
483 void CRepositoryBrowser::OnMouseMove(UINT nFlags, CPoint point)\r
484 {\r
485         CDC * pDC;\r
486         RECT rect, tree, list, treelist, treelistclient;\r
487 \r
488         if (bDragMode == FALSE)\r
489                 return;\r
490 \r
491         // create an union of the tree and list control rectangle\r
492         GetDlgItem(IDC_REPOLIST)->GetWindowRect(&list);\r
493         GetDlgItem(IDC_REPOTREE)->GetWindowRect(&tree);\r
494         UnionRect(&treelist, &tree, &list);\r
495         treelistclient = treelist;\r
496         ScreenToClient(&treelistclient);\r
497 \r
498         //convert the mouse coordinates relative to the top-left of\r
499         //the window\r
500         ClientToScreen(&point);\r
501         GetClientRect(&rect);\r
502         ClientToScreen(&rect);\r
503         point.x -= rect.left;\r
504         point.y -= treelist.top;\r
505 \r
506         //same for the window coordinates - make them relative to 0,0\r
507         OffsetRect(&treelist, -treelist.left, -treelist.top);\r
508 \r
509         if (point.x < treelist.left+REPOBROWSER_CTRL_MIN_WIDTH)\r
510                 point.x = treelist.left+REPOBROWSER_CTRL_MIN_WIDTH;\r
511         if (point.x > treelist.right-REPOBROWSER_CTRL_MIN_WIDTH) \r
512                 point.x = treelist.right-REPOBROWSER_CTRL_MIN_WIDTH;\r
513 \r
514         if ((nFlags & MK_LBUTTON) && (point.x != oldx))\r
515         {\r
516                 pDC = GetDC();\r
517 \r
518                 if (pDC)\r
519                 {\r
520                         DrawXorBar(pDC, oldx+2, treelistclient.top, 4, treelistclient.bottom-treelistclient.top-2);\r
521                         DrawXorBar(pDC, point.x+2, treelistclient.top, 4, treelistclient.bottom-treelistclient.top-2);\r
522 \r
523                         ReleaseDC(pDC);\r
524                 }\r
525 \r
526                 oldx = point.x;\r
527                 oldy = point.y;\r
528         }\r
529 \r
530         CStandAloneDialogTmpl<CResizableDialog>::OnMouseMove(nFlags, point);\r
531 }\r
532 \r
533 void CRepositoryBrowser::OnLButtonDown(UINT nFlags, CPoint point)\r
534 {\r
535         CDC * pDC;\r
536         RECT rect, tree, list, treelist, treelistclient;\r
537 \r
538         // create an union of the tree and list control rectangle\r
539         GetDlgItem(IDC_REPOLIST)->GetWindowRect(&list);\r
540         GetDlgItem(IDC_REPOTREE)->GetWindowRect(&tree);\r
541         UnionRect(&treelist, &tree, &list);\r
542         treelistclient = treelist;\r
543         ScreenToClient(&treelistclient);\r
544 \r
545         //convert the mouse coordinates relative to the top-left of\r
546         //the window\r
547         ClientToScreen(&point);\r
548         GetClientRect(&rect);\r
549         ClientToScreen(&rect);\r
550         point.x -= rect.left;\r
551         point.y -= treelist.top;\r
552 \r
553         //same for the window coordinates - make them relative to 0,0\r
554         OffsetRect(&treelist, -treelist.left, -treelist.top);\r
555 \r
556         if (point.x < treelist.left+REPOBROWSER_CTRL_MIN_WIDTH)\r
557                 point.x = treelist.left+REPOBROWSER_CTRL_MIN_WIDTH;\r
558     if (point.x > treelist.right-3) \r
559         return CStandAloneDialogTmpl<CResizableDialog>::OnLButtonDown(nFlags, point);\r
560         if (point.x > treelist.right-REPOBROWSER_CTRL_MIN_WIDTH) \r
561                 point.x = treelist.right-REPOBROWSER_CTRL_MIN_WIDTH;\r
562 \r
563         if ((point.y < treelist.top) || \r
564                 (point.y > treelist.bottom))\r
565                 return CStandAloneDialogTmpl<CResizableDialog>::OnLButtonDown(nFlags, point);\r
566 \r
567         bDragMode = true;\r
568 \r
569         SetCapture();\r
570 \r
571         pDC = GetDC();\r
572         DrawXorBar(pDC, point.x+2, treelistclient.top, 4, treelistclient.bottom-treelistclient.top-2);\r
573         ReleaseDC(pDC);\r
574 \r
575         oldx = point.x;\r
576         oldy = point.y;\r
577 \r
578         CStandAloneDialogTmpl<CResizableDialog>::OnLButtonDown(nFlags, point);\r
579 }\r
580 \r
581 void CRepositoryBrowser::OnLButtonUp(UINT nFlags, CPoint point)\r
582 {\r
583         CDC * pDC;\r
584         RECT rect, tree, list, treelist, treelistclient;\r
585 \r
586         if (bDragMode == FALSE)\r
587                 return;\r
588 \r
589         // create an union of the tree and list control rectangle\r
590         GetDlgItem(IDC_REPOLIST)->GetWindowRect(&list);\r
591         GetDlgItem(IDC_REPOTREE)->GetWindowRect(&tree);\r
592         UnionRect(&treelist, &tree, &list);\r
593         treelistclient = treelist;\r
594         ScreenToClient(&treelistclient);\r
595 \r
596         ClientToScreen(&point);\r
597         GetClientRect(&rect);\r
598         ClientToScreen(&rect);\r
599 \r
600         CPoint point2 = point;\r
601         if (point2.x < treelist.left+REPOBROWSER_CTRL_MIN_WIDTH)\r
602                 point2.x = treelist.left+REPOBROWSER_CTRL_MIN_WIDTH;\r
603         if (point2.x > treelist.right-REPOBROWSER_CTRL_MIN_WIDTH) \r
604                 point2.x = treelist.right-REPOBROWSER_CTRL_MIN_WIDTH;\r
605 \r
606         point.x -= rect.left;\r
607         point.y -= treelist.top;\r
608 \r
609         OffsetRect(&treelist, -treelist.left, -treelist.top);\r
610 \r
611         if (point.x < treelist.left+REPOBROWSER_CTRL_MIN_WIDTH)\r
612                 point.x = treelist.left+REPOBROWSER_CTRL_MIN_WIDTH;\r
613         if (point.x > treelist.right-REPOBROWSER_CTRL_MIN_WIDTH) \r
614                 point.x = treelist.right-REPOBROWSER_CTRL_MIN_WIDTH;\r
615 \r
616         pDC = GetDC();\r
617         DrawXorBar(pDC, oldx+2, treelistclient.top, 4, treelistclient.bottom-treelistclient.top-2);                     \r
618         ReleaseDC(pDC);\r
619 \r
620         oldx = point.x;\r
621         oldy = point.y;\r
622 \r
623         bDragMode = false;\r
624         ReleaseCapture();\r
625 \r
626         //position the child controls\r
627         GetDlgItem(IDC_REPOTREE)->GetWindowRect(&treelist);\r
628         treelist.right = point2.x - 2;\r
629         ScreenToClient(&treelist);\r
630         RemoveAnchor(IDC_REPOTREE);\r
631         GetDlgItem(IDC_REPOTREE)->MoveWindow(&treelist);\r
632         GetDlgItem(IDC_REPOLIST)->GetWindowRect(&treelist);\r
633         treelist.left = point2.x + 2;\r
634         ScreenToClient(&treelist);\r
635         RemoveAnchor(IDC_REPOLIST);\r
636         GetDlgItem(IDC_REPOLIST)->MoveWindow(&treelist);\r
637 \r
638         AddAnchor(IDC_REPOTREE, TOP_LEFT, BOTTOM_LEFT);\r
639         AddAnchor(IDC_REPOLIST, TOP_LEFT, BOTTOM_RIGHT);\r
640 \r
641         CStandAloneDialogTmpl<CResizableDialog>::OnLButtonUp(nFlags, point);\r
642 }\r
643 \r
644 void CRepositoryBrowser::DrawXorBar(CDC * pDC, int x1, int y1, int width, int height)\r
645 {\r
646         static WORD _dotPatternBmp[8] = \r
647         { \r
648                 0x0055, 0x00aa, 0x0055, 0x00aa, \r
649                 0x0055, 0x00aa, 0x0055, 0x00aa\r
650         };\r
651 \r
652         HBITMAP hbm;\r
653         HBRUSH  hbr, hbrushOld;\r
654 \r
655         hbm = CreateBitmap(8, 8, 1, 1, _dotPatternBmp);\r
656         hbr = CreatePatternBrush(hbm);\r
657 \r
658         pDC->SetBrushOrg(x1, y1);\r
659         hbrushOld = (HBRUSH)pDC->SelectObject(hbr);\r
660 \r
661         PatBlt(pDC->GetSafeHdc(), x1, y1, width, height, PATINVERT);\r
662 \r
663         pDC->SelectObject(hbrushOld);\r
664 \r
665         DeleteObject(hbr);\r
666         DeleteObject(hbm);\r
667 }\r
668 \r
669 /******************************************************************************/\r
670 /* repository information gathering                                           */\r
671 /******************************************************************************/\r
672 \r
673 BOOL CRepositoryBrowser::ReportList(const CString& path, svn_node_kind_t kind, \r
674                                                                         svn_filesize_t size, bool has_props, \r
675                                                                         svn_revnum_t created_rev, apr_time_t time, \r
676                                                                         const CString& author, const CString& locktoken, \r
677                                                                         const CString& lockowner, const CString& lockcomment, \r
678                                                                         bool is_dav_comment, apr_time_t lock_creationdate, \r
679                                                                         apr_time_t lock_expirationdate, \r
680                                                                         const CString& absolutepath)\r
681 {\r
682         static deque<CItem> * pDirList = NULL;\r
683         static CTreeItem * pTreeItem = NULL;\r
684         static CString dirPath;\r
685 \r
686         CString sParent = absolutepath;\r
687         int slashpos = path.ReverseFind('/');\r
688         bool abspath_has_slash = (absolutepath.GetAt(absolutepath.GetLength()-1) == '/');\r
689         if ((slashpos > 0) && (!abspath_has_slash))\r
690                 sParent += _T("/");\r
691         sParent += path.Left(slashpos);\r
692         if (sParent.Compare(_T("/"))==0)\r
693                 sParent.Empty();\r
694         if ((path.IsEmpty())||\r
695                 (pDirList == NULL)||\r
696                 (sParent.Compare(dirPath)))\r
697         {\r
698                 HTREEITEM hItem = FindUrl(m_strReposRoot + sParent);\r
699                 pTreeItem = (CTreeItem*)m_RepoTree.GetItemData(hItem);\r
700                 pDirList = &(pTreeItem->children);\r
701 \r
702                 dirPath = sParent;\r
703         }\r
704         if (path.IsEmpty())\r
705                 return TRUE;\r
706 \r
707         if (kind == svn_node_dir)\r
708         {\r
709                 FindUrl(m_strReposRoot + absolutepath + (abspath_has_slash ? _T("") : _T("/")) + path);\r
710                 if (pTreeItem)\r
711                         pTreeItem->has_child_folders = true;\r
712         }\r
713         pDirList->push_back(CItem(path.Mid(slashpos+1), kind, size, has_props,\r
714                 created_rev, time, author, locktoken,\r
715                 lockowner, lockcomment, is_dav_comment,\r
716                 lock_creationdate, lock_expirationdate,\r
717                 m_strReposRoot+absolutepath+(abspath_has_slash ? _T("") : _T("/"))+path));\r
718         if (pTreeItem)\r
719                 pTreeItem->children_fetched = true;\r
720         return TRUE;\r
721 }\r
722 \r
723 bool CRepositoryBrowser::ChangeToUrl(CString& url, SVNRev& rev, bool bAlreadyChecked)\r
724 {\r
725         CWaitCursorEx wait;\r
726         if (!bAlreadyChecked)\r
727         {\r
728                 // check if the entered url is valid\r
729                 SVNInfo info;\r
730                 const SVNInfoData * data = NULL;\r
731                 CString orig_url = url;\r
732                 m_bCancelled = false;\r
733                 do \r
734                 {\r
735                         data = info.GetFirstFileInfo(CTSVNPath(url), rev, rev);\r
736                         if (data && rev.IsHead())\r
737                         {\r
738                                 rev = data->rev;\r
739                         }\r
740                         if ((data == NULL)||(data->kind != svn_node_dir))\r
741                         {\r
742                                 // in case the url is not a valid directory, try the parent dir\r
743                                 // until there's no more parent dir\r
744                                 url = url.Left(url.ReverseFind('/'));\r
745                         }\r
746                 } while(!m_bCancelled && !url.IsEmpty() && ((data == NULL) || (data->kind != svn_node_dir)));\r
747                 if (url.IsEmpty())\r
748                         url = orig_url;\r
749         }\r
750         CString partUrl = url;\r
751         HTREEITEM hItem = m_RepoTree.GetRootItem();\r
752         if ((LONG(rev) != LONG(m_initialRev))||\r
753                 (m_strReposRoot.IsEmpty())||\r
754                 (m_strReposRoot.Compare(url.Left(m_strReposRoot.GetLength())))||\r
755                 (url.GetAt(m_strReposRoot.GetLength()) != '/'))\r
756         {\r
757                 // if the revision changed, then invalidate everything\r
758                 RecursiveRemove(hItem);\r
759                 m_RepoTree.DeleteAllItems();\r
760                 m_RepoList.DeleteAllItems();\r
761                 m_RepoList.ShowText(CString(MAKEINTRESOURCE(IDS_REPOBROWSE_WAIT)), true);\r
762                 hItem = m_RepoTree.GetRootItem();\r
763                 if ((m_strReposRoot.IsEmpty())||(m_strReposRoot.Compare(url.Left(m_strReposRoot.GetLength())))||\r
764                         (url.GetAt(m_strReposRoot.GetLength()) != '/'))\r
765                 {\r
766                         // if the repository root has changed, initialize all data from scratch\r
767                         // and clear the project properties we might have loaded previously\r
768                         m_ProjectProperties = ProjectProperties();\r
769                         m_InitialUrl = url;\r
770                         InitRepo();\r
771                         if ((m_InitialUrl.IsEmpty())||(m_strReposRoot.IsEmpty()))\r
772                                 return false;\r
773                 }\r
774         }\r
775         if (hItem == NULL)\r
776         {\r
777                 // the tree view is empty, just fill in the repository root\r
778                 CTreeItem * pTreeItem = new CTreeItem();\r
779                 pTreeItem->unescapedname = m_strReposRoot;\r
780                 pTreeItem->url = m_strReposRoot;\r
781 \r
782                 TVINSERTSTRUCT tvinsert = {0};\r
783                 tvinsert.hParent = TVI_ROOT;\r
784                 tvinsert.hInsertAfter = TVI_ROOT;\r
785                 tvinsert.itemex.mask = TVIF_CHILDREN | TVIF_DI_SETITEM | TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;\r
786                 tvinsert.itemex.pszText = m_strReposRoot.GetBuffer(m_strReposRoot.GetLength());\r
787                 tvinsert.itemex.cChildren = 1;\r
788                 tvinsert.itemex.lParam = (LPARAM)pTreeItem;\r
789                 tvinsert.itemex.iImage = m_nIconFolder;\r
790                 tvinsert.itemex.iSelectedImage = m_nOpenIconFolder;\r
791 \r
792                 hItem = m_RepoTree.InsertItem(&tvinsert);\r
793                 m_strReposRoot.ReleaseBuffer();\r
794         }\r
795         if (hItem == NULL)\r
796         {\r
797                 // something terrible happened!\r
798                 return false;\r
799         }\r
800         hItem = FindUrl(url);\r
801         if (hItem == NULL)\r
802                 return false;\r
803 \r
804         CTreeItem * pTreeItem = (CTreeItem *)m_RepoTree.GetItemData(hItem);\r
805         if (pTreeItem == NULL)\r
806                 return FALSE;\r
807 \r
808         if (!m_RepoList.HasText())\r
809                 m_RepoList.ShowText(_T(" "), true);\r
810 \r
811         RefreshNode(hItem);\r
812         m_RepoTree.Expand(hItem, TVE_EXPAND);\r
813         FillList(&pTreeItem->children);\r
814 \r
815         m_blockEvents = true;\r
816         m_RepoTree.EnsureVisible(hItem);\r
817         m_RepoTree.SelectItem(hItem);\r
818         m_blockEvents = false;\r
819 \r
820         m_RepoList.ClearText();\r
821 \r
822         return true;\r
823 }\r
824 \r
825 void CRepositoryBrowser::FillList(deque<CItem> * pItems)\r
826 {\r
827         if (pItems == NULL)\r
828                 return;\r
829         CWaitCursorEx wait;\r
830         m_RepoList.SetRedraw(false);\r
831         m_RepoList.DeleteAllItems();\r
832 \r
833         int c = ((CHeaderCtrl*)(m_RepoList.GetDlgItem(0)))->GetItemCount()-1;\r
834         while (c>=0)\r
835                 m_RepoList.DeleteColumn(c--);\r
836 \r
837         c = 0;\r
838         CString temp;\r
839         //\r
840         // column 0: contains tree\r
841         temp.LoadString(IDS_LOG_FILE);\r
842         m_RepoList.InsertColumn(c++, temp);\r
843         //\r
844         // column 1: file extension\r
845         temp.LoadString(IDS_STATUSLIST_COLEXT);\r
846         m_RepoList.InsertColumn(c++, temp);\r
847         //\r
848         // column 2: revision number\r
849         temp.LoadString(IDS_LOG_REVISION);\r
850         m_RepoList.InsertColumn(c++, temp, LVCFMT_RIGHT);\r
851         //\r
852         // column 3: author\r
853         temp.LoadString(IDS_LOG_AUTHOR);\r
854         m_RepoList.InsertColumn(c++, temp);\r
855         //\r
856         // column 4: size\r
857         temp.LoadString(IDS_LOG_SIZE);\r
858         m_RepoList.InsertColumn(c++, temp, LVCFMT_RIGHT);\r
859         //\r
860         // column 5: date\r
861         temp.LoadString(IDS_LOG_DATE);\r
862         m_RepoList.InsertColumn(c++, temp);\r
863         //\r
864         // column 6: lock owner\r
865         temp.LoadString(IDS_STATUSLIST_COLLOCK);\r
866         m_RepoList.InsertColumn(c++, temp);\r
867 \r
868         // now fill in the data\r
869 \r
870         TCHAR date_native[SVN_DATE_BUFFER];\r
871         int nCount = 0;\r
872         for (deque<CItem>::const_iterator it = pItems->begin(); it != pItems->end(); ++it)\r
873         {\r
874                 int icon_idx;\r
875                 if (it->kind == svn_node_dir)\r
876                         icon_idx =      m_nIconFolder;\r
877                 else\r
878                         icon_idx = SYS_IMAGE_LIST().GetFileIconIndex(it->path);\r
879                 int index = m_RepoList.InsertItem(nCount, it->path, icon_idx);\r
880                 // extension\r
881                 temp = CPathUtils::GetFileExtFromPath(it->path);\r
882                 if (it->kind == svn_node_file)\r
883                         m_RepoList.SetItemText(index, 1, temp);\r
884                 // revision\r
885                 temp.Format(_T("%ld"), it->created_rev);\r
886                 m_RepoList.SetItemText(index, 2, temp);\r
887                 // author\r
888                 m_RepoList.SetItemText(index, 3, it->author);\r
889                 // size\r
890                 if (it->kind == svn_node_file)\r
891                 {\r
892                         StrFormatByteSize(it->size, temp.GetBuffer(20), 20);\r
893                         temp.ReleaseBuffer();\r
894                         m_RepoList.SetItemText(index, 4, temp);\r
895                 }\r
896                 // date\r
897                 SVN::formatDate(date_native, (apr_time_t&)it->time, true);\r
898                 m_RepoList.SetItemText(index, 5, date_native);\r
899                 // lock owner\r
900                 m_RepoList.SetItemText(index, 6, it->lockowner);\r
901                 m_RepoList.SetItemData(index, (DWORD_PTR)&(*it));\r
902         }\r
903 \r
904         ListView_SortItemsEx(m_RepoList, ListSort, this);\r
905         SetSortArrow();\r
906 \r
907         for (int col = 0; col <= (((CHeaderCtrl*)(m_RepoList.GetDlgItem(0)))->GetItemCount()-1); col++)\r
908         {\r
909                 m_RepoList.SetColumnWidth(col, LVSCW_AUTOSIZE_USEHEADER);\r
910         }\r
911         for (int col = 0; col <= (((CHeaderCtrl*)(m_RepoList.GetDlgItem(0)))->GetItemCount()-1); col++)\r
912         {\r
913                 m_arColumnAutoWidths[col] = m_RepoList.GetColumnWidth(col);\r
914         }\r
915 \r
916         CRegString regColWidths(_T("Software\\TortoiseGit\\RepoBrowserColumnWidth"));\r
917         if (!CString(regColWidths).IsEmpty())\r
918         {\r
919                 StringToWidthArray(regColWidths, m_arColumnWidths);\r
920                 \r
921                 int maxcol = ((CHeaderCtrl*)(m_RepoList.GetDlgItem(0)))->GetItemCount()-1;\r
922                 for (int col = 1; col <= maxcol; col++)\r
923                 {\r
924                         if (m_arColumnWidths[col] == 0)\r
925                                 m_RepoList.SetColumnWidth(col, LVSCW_AUTOSIZE_USEHEADER);\r
926                         else\r
927                                 m_RepoList.SetColumnWidth(col, m_arColumnWidths[col]);\r
928                 }\r
929         }\r
930 \r
931         m_RepoList.SetRedraw(true);\r
932 }\r
933 \r
934 HTREEITEM CRepositoryBrowser::FindUrl(const CString& fullurl, bool create /* = true */)\r
935 {\r
936         return FindUrl(fullurl, fullurl, create, TVI_ROOT);\r
937 }\r
938 \r
939 HTREEITEM CRepositoryBrowser::FindUrl(const CString& fullurl, const CString& url, bool create /* true */, HTREEITEM hItem /* = TVI_ROOT */)\r
940 {\r
941         if (hItem == TVI_ROOT)\r
942         {\r
943                 hItem = m_RepoTree.GetRootItem();\r
944                 if (fullurl.Compare(m_strReposRoot)==0)\r
945                         return hItem;\r
946                 return FindUrl(fullurl, url.Mid(m_strReposRoot.GetLength()+1), create, hItem);\r
947         }\r
948         HTREEITEM hSibling = hItem;\r
949         if (m_RepoTree.GetNextItem(hItem, TVGN_CHILD))\r
950         {\r
951                 hSibling = m_RepoTree.GetNextItem(hItem, TVGN_CHILD);\r
952                 do\r
953                 {\r
954                         CTreeItem * pTItem = ((CTreeItem*)m_RepoTree.GetItemData(hSibling));\r
955                         if (pTItem)\r
956                         {\r
957                                 CString sSibling = pTItem->unescapedname;\r
958                                 if (sSibling.Compare(url.Left(sSibling.GetLength()))==0)\r
959                                 {\r
960                                         if (sSibling.GetLength() == url.GetLength())\r
961                                                 return hSibling;\r
962                                         if (url.GetAt(sSibling.GetLength()) == '/')\r
963                                                 return FindUrl(fullurl, url.Mid(sSibling.GetLength()+1), create, hSibling);\r
964                                 }\r
965                         }\r
966                 } while ((hSibling = m_RepoTree.GetNextItem(hSibling, TVGN_NEXT)) != NULL);     \r
967         }\r
968         if (!create)\r
969                 return NULL;\r
970         // create tree items for every path part in the url\r
971         CString sUrl = url;\r
972         int slash = -1;\r
973         HTREEITEM hNewItem = hItem;\r
974         CString sTemp;\r
975         while ((slash=sUrl.Find('/')) >= 0)\r
976         {\r
977                 CTreeItem * pTreeItem = new CTreeItem();\r
978                 sTemp = sUrl.Left(slash);\r
979                 pTreeItem->unescapedname = sTemp;\r
980                 pTreeItem->url = fullurl.Left(fullurl.GetLength()-sUrl.GetLength()+slash);\r
981                 UINT state = pTreeItem->url.CompareNoCase(m_diffURL.GetSVNPathString()) ? 0 : TVIS_BOLD;\r
982                 TVINSERTSTRUCT tvinsert = {0};\r
983                 tvinsert.hParent = hNewItem;\r
984                 tvinsert.hInsertAfter = TVI_SORT;\r
985                 tvinsert.itemex.mask = TVIF_CHILDREN | TVIF_DI_SETITEM | TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE;\r
986                 tvinsert.itemex.state = state;\r
987                 tvinsert.itemex.stateMask = state;\r
988                 tvinsert.itemex.pszText = sTemp.GetBuffer(sTemp.GetLength());\r
989                 tvinsert.itemex.cChildren = 1;\r
990                 tvinsert.itemex.lParam = (LPARAM)pTreeItem;\r
991                 tvinsert.itemex.iImage = m_nIconFolder;\r
992                 tvinsert.itemex.iSelectedImage = m_nOpenIconFolder;\r
993 \r
994                 hNewItem = m_RepoTree.InsertItem(&tvinsert);\r
995                 sTemp.ReleaseBuffer();\r
996                 sUrl = sUrl.Mid(slash+1);\r
997                 ATLTRACE(_T("created tree entry %s, url %s\n"), sTemp, pTreeItem->url);\r
998         }\r
999         if (!sUrl.IsEmpty())\r
1000         {\r
1001                 CTreeItem * pTreeItem = new CTreeItem();\r
1002                 sTemp = sUrl;\r
1003                 pTreeItem->unescapedname = sTemp;\r
1004                 pTreeItem->url = fullurl;\r
1005                 UINT state = pTreeItem->url.CompareNoCase(m_diffURL.GetSVNPathString()) ? 0 : TVIS_BOLD;\r
1006                 TVINSERTSTRUCT tvinsert = {0};\r
1007                 tvinsert.hParent = hNewItem;\r
1008                 tvinsert.hInsertAfter = TVI_SORT;\r
1009                 tvinsert.itemex.mask = TVIF_CHILDREN | TVIF_DI_SETITEM | TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE;\r
1010                 tvinsert.itemex.state = state;\r
1011                 tvinsert.itemex.stateMask = state;\r
1012                 tvinsert.itemex.pszText = sTemp.GetBuffer(sTemp.GetLength());\r
1013                 tvinsert.itemex.cChildren = 1;\r
1014                 tvinsert.itemex.lParam = (LPARAM)pTreeItem;\r
1015                 tvinsert.itemex.iImage = m_nIconFolder;\r
1016                 tvinsert.itemex.iSelectedImage = m_nOpenIconFolder;\r
1017 \r
1018                 hNewItem = m_RepoTree.InsertItem(&tvinsert);\r
1019                 sTemp.ReleaseBuffer();\r
1020                 m_RepoTree.SortChildren(hNewItem);\r
1021                 return hNewItem;\r
1022         }\r
1023         return NULL;\r
1024 }\r
1025 \r
1026 bool CRepositoryBrowser::RefreshNode(const CString& url, bool force /* = false*/, bool recursive /* = false*/)\r
1027 {\r
1028         HTREEITEM hNode = FindUrl(url);\r
1029         return RefreshNode(hNode, force, recursive);\r
1030 }\r
1031 \r
1032 bool CRepositoryBrowser::RefreshNode(HTREEITEM hNode, bool force /* = false*/, bool recursive /* = false*/)\r
1033 {\r
1034         if (hNode == NULL)\r
1035                 return false;\r
1036         SaveColumnWidths();\r
1037         CWaitCursorEx wait;\r
1038         CTreeItem * pTreeItem = (CTreeItem *)m_RepoTree.GetItemData(hNode);\r
1039         HTREEITEM hSel1 = m_RepoTree.GetSelectedItem();\r
1040         if (m_RepoTree.ItemHasChildren(hNode))\r
1041         {\r
1042                 HTREEITEM hChild = m_RepoTree.GetChildItem(hNode);\r
1043                 HTREEITEM hNext;\r
1044                 m_blockEvents = true;\r
1045                 while (hChild)\r
1046                 {\r
1047                         hNext = m_RepoTree.GetNextItem(hChild, TVGN_NEXT);\r
1048                         RecursiveRemove(hChild);\r
1049                         m_RepoTree.DeleteItem(hChild);\r
1050                         hChild = hNext;\r
1051                 }\r
1052                 m_blockEvents = false;\r
1053         }\r
1054         if (pTreeItem == NULL)\r
1055                 return false;\r
1056         pTreeItem->children.clear();\r
1057         pTreeItem->has_child_folders = false;\r
1058         m_bCancelled = false;\r
1059         if (!List(CTSVNPath(pTreeItem->url), GetRevision(), GetRevision(), recursive ? svn_depth_infinity : svn_depth_immediates, true))\r
1060         {\r
1061                 // error during list()\r
1062                 m_RepoList.ShowText(GetLastErrorMessage());\r
1063                 return false;\r
1064         }\r
1065         pTreeItem->children_fetched = true;\r
1066         // if there are no child folders, remove the '+' in front of the node\r
1067         {\r
1068                 TVITEM tvitem = {0};\r
1069                 tvitem.hItem = hNode;\r
1070                 tvitem.mask = TVIF_CHILDREN;\r
1071                 tvitem.cChildren = pTreeItem->has_child_folders ? 1 : 0;\r
1072                 m_RepoTree.SetItem(&tvitem);\r
1073         }\r
1074         if ((force)||(hSel1 == hNode)||(hSel1 != m_RepoTree.GetSelectedItem()))\r
1075         {\r
1076                 FillList(&pTreeItem->children);\r
1077         }\r
1078         return true;\r
1079 }\r
1080 \r
1081 BOOL CRepositoryBrowser::PreTranslateMessage(MSG* pMsg)\r
1082 {\r
1083         if (pMsg->message>=WM_KEYFIRST && pMsg->message<=WM_KEYLAST)\r
1084         {\r
1085                 // Check if there is an in place Edit active:\r
1086                 // in place edits are done with an edit control, where the parent\r
1087                 // is the control with the editable item (tree or list control here)\r
1088                 HWND hWndFocus = ::GetFocus();\r
1089                 if (hWndFocus)\r
1090                         hWndFocus = ::GetParent(hWndFocus);\r
1091                 if (hWndFocus && ((hWndFocus == m_RepoTree.GetSafeHwnd())||(hWndFocus == m_RepoList.GetSafeHwnd())))\r
1092                 {\r
1093                         // Do a direct translation.\r
1094                         ::TranslateMessage(pMsg);\r
1095                         ::DispatchMessage(pMsg);\r
1096                         return TRUE;\r
1097                 }\r
1098                 if (m_hAccel)\r
1099                 {\r
1100                         if (pMsg->message == WM_KEYDOWN)\r
1101                         {\r
1102                                 switch (pMsg->wParam)\r
1103                                 {\r
1104                                 case 'C':\r
1105                                 case VK_INSERT:\r
1106                                 case VK_DELETE:\r
1107                                 case VK_BACK:\r
1108                                         {\r
1109                                                 if ((pMsg->hwnd == m_barRepository.GetSafeHwnd())||(::IsChild(m_barRepository.GetSafeHwnd(), pMsg->hwnd)))\r
1110                                                         return __super::PreTranslateMessage(pMsg);\r
1111                                         }\r
1112                                         break;\r
1113                                 }\r
1114                         }\r
1115                         int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);\r
1116                         if (ret)\r
1117                                 return TRUE;\r
1118                 }\r
1119         }\r
1120         return __super::PreTranslateMessage(pMsg);\r
1121 }\r
1122 \r
1123 void CRepositoryBrowser::OnDelete()\r
1124 {\r
1125         CTSVNPathList urlList;\r
1126         bool bTreeItem = false;\r
1127 \r
1128         POSITION pos = m_RepoList.GetFirstSelectedItemPosition();\r
1129         int index = -1;\r
1130         while ((index = m_RepoList.GetNextSelectedItem(pos))>=0)\r
1131         {\r
1132                 CItem * pItem = (CItem *)m_RepoList.GetItemData(index);\r
1133                 CString absPath = pItem->absolutepath;\r
1134                 absPath.Replace(_T("\\"), _T("%5C"));\r
1135                 urlList.AddPath(CTSVNPath(absPath));\r
1136         }\r
1137         if ((urlList.GetCount() == 0))\r
1138         {\r
1139                 HTREEITEM hItem = m_RepoTree.GetSelectedItem();\r
1140                 CTreeItem * pTreeItem = (CTreeItem *)m_RepoTree.GetItemData(hItem);\r
1141                 if (pTreeItem)\r
1142                 {\r
1143                         urlList.AddPath(CTSVNPath(pTreeItem->url));\r
1144                         bTreeItem = true;\r
1145                 }\r
1146         }\r
1147 \r
1148         if (urlList.GetCount() == 0)\r
1149                 return;\r
1150 \r
1151 \r
1152         CWaitCursorEx wait_cursor;\r
1153         CInputLogDlg input(this);\r
1154         input.SetUUID(m_sUUID);\r
1155         input.SetProjectProperties(&m_ProjectProperties);\r
1156         CString hint;\r
1157         if (urlList.GetCount() == 1)\r
1158                 hint.Format(IDS_INPUT_REMOVEONE, (LPCTSTR)urlList[0].GetFileOrDirectoryName());\r
1159         else\r
1160                 hint.Format(IDS_INPUT_REMOVEMORE, urlList.GetCount());\r
1161         input.SetActionText(hint);\r
1162         if (input.DoModal() == IDOK)\r
1163         {\r
1164                 if (!Remove(urlList, true, false, input.GetLogMessage()))\r
1165                 {\r
1166                         wait_cursor.Hide();\r
1167                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
1168                         return;\r
1169                 }\r
1170                 if (bTreeItem)\r
1171                         RefreshNode(m_RepoTree.GetParentItem(m_RepoTree.GetSelectedItem()), true);\r
1172                 else\r
1173                         RefreshNode(m_RepoTree.GetSelectedItem(), true);\r
1174         }\r
1175 }\r
1176 \r
1177 void CRepositoryBrowser::OnGoUp()\r
1178 {\r
1179         m_barRepository.OnGoUp();\r
1180 }\r
1181 \r
1182 void CRepositoryBrowser::OnUrlFocus()\r
1183 {\r
1184         m_barRepository.SetFocusToURL();\r
1185 }\r
1186 \r
1187 void CRepositoryBrowser::OnCopy()\r
1188 {\r
1189         // Ctrl-C : copy the selected item urls to the clipboard\r
1190         CString url;\r
1191         POSITION pos = m_RepoList.GetFirstSelectedItemPosition();\r
1192         int index = -1;\r
1193         while ((index = m_RepoList.GetNextSelectedItem(pos))>=0)\r
1194         {\r
1195                 CItem * pItem = (CItem *)m_RepoList.GetItemData(index);\r
1196                 url += CUnicodeUtils::GetUnicode(CPathUtils::PathEscape(CUnicodeUtils::GetUTF8(pItem->absolutepath))) + _T("\r\n");\r
1197         }\r
1198         if (!url.IsEmpty())\r
1199         {\r
1200                 url.TrimRight(_T("\r\n"));\r
1201                 CStringUtils::WriteAsciiStringToClipboard(url);\r
1202         }\r
1203 }\r
1204 \r
1205 void CRepositoryBrowser::OnInlineedit()\r
1206 {\r
1207         POSITION pos = m_RepoList.GetFirstSelectedItemPosition();\r
1208         int selIndex = m_RepoList.GetNextSelectedItem(pos);\r
1209         m_blockEvents = true;\r
1210         if (selIndex >= 0)\r
1211         {\r
1212                 m_RepoList.SetFocus();\r
1213                 m_RepoList.EditLabel(selIndex);\r
1214         }\r
1215         else\r
1216         {\r
1217                 m_RepoTree.SetFocus();\r
1218                 HTREEITEM hTreeItem = m_RepoTree.GetSelectedItem();\r
1219                 if (hTreeItem != m_RepoTree.GetRootItem())\r
1220                         m_RepoTree.EditLabel(hTreeItem);\r
1221         }\r
1222         m_blockEvents = false;\r
1223 }\r
1224 \r
1225 void CRepositoryBrowser::OnRefresh()\r
1226 {\r
1227         m_blockEvents = true;\r
1228         RefreshNode(m_RepoTree.GetSelectedItem(), true, !!(GetKeyState(VK_CONTROL)&0x8000));\r
1229         m_blockEvents = false;\r
1230 }\r
1231 \r
1232 void CRepositoryBrowser::OnTvnSelchangedRepotree(NMHDR *pNMHDR, LRESULT *pResult)\r
1233 {\r
1234         LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);\r
1235         *pResult = 0;\r
1236 \r
1237         if (m_blockEvents)\r
1238                 return;\r
1239 \r
1240         if (pNMTreeView->action == TVC_BYKEYBOARD)\r
1241                 SetTimer(REPOBROWSER_FETCHTIMER, 300, NULL);\r
1242         else\r
1243                 OnTimer(REPOBROWSER_FETCHTIMER);\r
1244 }\r
1245 \r
1246 void CRepositoryBrowser::OnTimer(UINT_PTR nIDEvent)\r
1247 {\r
1248         if (nIDEvent == REPOBROWSER_FETCHTIMER)\r
1249         {\r
1250                 KillTimer(REPOBROWSER_FETCHTIMER);\r
1251                 // find the currently selected item\r
1252                 HTREEITEM hSelItem = m_RepoTree.GetSelectedItem();\r
1253                 if (hSelItem)\r
1254                 {\r
1255                         CTreeItem * pTreeItem = (CTreeItem *)m_RepoTree.GetItemData(hSelItem);\r
1256                         if (pTreeItem)\r
1257                         {\r
1258                                 if (!pTreeItem->children_fetched)\r
1259                                 {\r
1260                                         m_RepoList.ShowText(_T(" "), true);\r
1261                                         RefreshNode(hSelItem);\r
1262                                         m_RepoList.ClearText();\r
1263                                 }\r
1264 \r
1265                                 FillList(&pTreeItem->children);\r
1266                                 m_barRepository.ShowUrl(pTreeItem->url, GetRevision());\r
1267                         }\r
1268                 }\r
1269         }\r
1270 \r
1271         __super::OnTimer(nIDEvent);\r
1272 }\r
1273 \r
1274 void CRepositoryBrowser::OnTvnItemexpandingRepotree(NMHDR *pNMHDR, LRESULT *pResult)\r
1275 {\r
1276         LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);\r
1277         *pResult = 0;\r
1278 \r
1279         if (m_blockEvents)\r
1280                 return;\r
1281 \r
1282         CTreeItem * pTreeItem = (CTreeItem *)pNMTreeView->itemNew.lParam;\r
1283 \r
1284         if (pTreeItem == NULL)\r
1285                 return;\r
1286 \r
1287         if (pNMTreeView->action == TVE_COLLAPSE)\r
1288         {\r
1289                 // user wants to collapse a tree node.\r
1290                 // if we don't know anything about the children\r
1291                 // of the node, we suppress the collapsing but fetch the info instead\r
1292                 if (!pTreeItem->children_fetched)\r
1293                 {\r
1294                         RefreshNode(pNMTreeView->itemNew.hItem);\r
1295                         *pResult = 1;\r
1296                         return;\r
1297                 }\r
1298                 return;\r
1299         }\r
1300 \r
1301         // user wants to expand a tree node.\r
1302         // check if we already know its children - if not we have to ask the repository!\r
1303 \r
1304         if (!pTreeItem->children_fetched)\r
1305         {\r
1306                 RefreshNode(pNMTreeView->itemNew.hItem);\r
1307         }\r
1308         else\r
1309         {\r
1310                 // if there are no child folders, remove the '+' in front of the node\r
1311                 if (!pTreeItem->has_child_folders)\r
1312                 {\r
1313                         TVITEM tvitem = {0};\r
1314                         tvitem.hItem = pNMTreeView->itemNew.hItem;\r
1315                         tvitem.mask = TVIF_CHILDREN;\r
1316                         tvitem.cChildren = 0;\r
1317                         m_RepoTree.SetItem(&tvitem);\r
1318                 }\r
1319         }\r
1320 }\r
1321 \r
1322 void CRepositoryBrowser::OnNMDblclkRepolist(NMHDR *pNMHDR, LRESULT *pResult)\r
1323 {\r
1324         LPNMITEMACTIVATE pNmItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);\r
1325         *pResult = 0;\r
1326 \r
1327         if (m_blockEvents)\r
1328                 return;\r
1329 \r
1330         if (pNmItemActivate->iItem < 0)\r
1331                 return;\r
1332         CItem * pItem = (CItem*)m_RepoList.GetItemData(pNmItemActivate->iItem);\r
1333         if ((pItem)&&(pItem->kind == svn_node_dir))\r
1334         {\r
1335                 // a double click on a folder results in selecting that folder\r
1336                 ChangeToUrl(pItem->absolutepath, m_initialRev, true);\r
1337         }\r
1338 }\r
1339 \r
1340 void CRepositoryBrowser::OnHdnItemclickRepolist(NMHDR *pNMHDR, LRESULT *pResult)\r
1341 {\r
1342         LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);\r
1343         // a click on a header means sorting the items\r
1344         if (m_nSortedColumn != phdr->iItem)\r
1345                 m_bSortAscending = true;\r
1346         else\r
1347                 m_bSortAscending = !m_bSortAscending;\r
1348         m_nSortedColumn = phdr->iItem;\r
1349 \r
1350         m_blockEvents = true;\r
1351         ListView_SortItemsEx(m_RepoList, ListSort, this);\r
1352         SetSortArrow();\r
1353         m_blockEvents = false;\r
1354         *pResult = 0;\r
1355 }\r
1356 \r
1357 int CRepositoryBrowser::ListSort(LPARAM lParam1, LPARAM lParam2, LPARAM lParam3)\r
1358 {\r
1359         CRepositoryBrowser * pThis = (CRepositoryBrowser*)lParam3;\r
1360         CItem * pItem1 = (CItem*)pThis->m_RepoList.GetItemData(static_cast<int>(lParam1));\r
1361         CItem * pItem2 = (CItem*)pThis->m_RepoList.GetItemData(static_cast<int>(lParam2));\r
1362         int nRet = 0;\r
1363         switch (pThis->m_nSortedColumn)\r
1364         {\r
1365         case 1: // extension\r
1366                 nRet = pThis->m_RepoList.GetItemText(static_cast<int>(lParam1), 1)\r
1367                  .CompareNoCase(pThis->m_RepoList.GetItemText(static_cast<int>(lParam2), 1));\r
1368                 if (nRet != 0)\r
1369                         break;\r
1370                 // fall through\r
1371         case 2: // revision number\r
1372                 nRet = pItem1->created_rev - pItem2->created_rev;\r
1373                 if (nRet != 0)\r
1374                         break;\r
1375                 // fall through\r
1376         case 3: // author\r
1377                 nRet = pItem1->author.CompareNoCase(pItem2->author);\r
1378                 if (nRet != 0)\r
1379                         break;\r
1380                 // fall through\r
1381         case 4: // size\r
1382                 nRet = int(pItem1->size - pItem2->size);\r
1383                 if (nRet != 0)\r
1384                         break;\r
1385                 // fall through\r
1386         case 5: // date\r
1387                 nRet = (pItem1->time - pItem2->time) > 0 ? 1 : -1;\r
1388                 if (nRet != 0)\r
1389                         break;\r
1390                 // fall through\r
1391         case 6: // lock owner\r
1392                 nRet = pItem1->lockowner.CompareNoCase(pItem2->lockowner);\r
1393                 if (nRet != 0)\r
1394                         break;\r
1395                 // fall through\r
1396         case 0: // filename\r
1397                 nRet = CStringUtils::CompareNumerical(pItem1->path, pItem2->path);\r
1398                 break;\r
1399         }\r
1400 \r
1401         if (!pThis->m_bSortAscending)\r
1402                 nRet = -nRet;\r
1403 \r
1404         // we want folders on top, then the files\r
1405         if (pItem1->kind != pItem2->kind)\r
1406         {\r
1407                 if (pItem1->kind == svn_node_dir)\r
1408                         nRet = -1;\r
1409                 else\r
1410                         nRet = 1;\r
1411         }\r
1412 \r
1413         return nRet;\r
1414 }\r
1415 \r
1416 void CRepositoryBrowser::SetSortArrow()\r
1417 {\r
1418         CHeaderCtrl * pHeader = m_RepoList.GetHeaderCtrl();\r
1419         HDITEM HeaderItem = {0};\r
1420         HeaderItem.mask = HDI_FORMAT;\r
1421         for (int i=0; i<pHeader->GetItemCount(); ++i)\r
1422         {\r
1423                 pHeader->GetItem(i, &HeaderItem);\r
1424                 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);\r
1425                 pHeader->SetItem(i, &HeaderItem);\r
1426         }\r
1427 \r
1428         pHeader->GetItem(m_nSortedColumn, &HeaderItem);\r
1429         HeaderItem.fmt |= (m_bSortAscending ? HDF_SORTUP : HDF_SORTDOWN);\r
1430         pHeader->SetItem(m_nSortedColumn, &HeaderItem);\r
1431 }\r
1432 \r
1433 void CRepositoryBrowser::OnLvnItemchangedRepolist(NMHDR *pNMHDR, LRESULT *pResult)\r
1434 {\r
1435         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
1436         *pResult = 0;\r
1437         if (m_blockEvents)\r
1438                 return;\r
1439         if (m_RepoList.HasText())\r
1440                 return;\r
1441         if (pNMLV->uChanged & LVIF_STATE)\r
1442         {\r
1443                 if (pNMLV->uNewState & LVIS_SELECTED)\r
1444                 {\r
1445                         CItem * pItem = (CItem*)m_RepoList.GetItemData(pNMLV->iItem);\r
1446                         if (pItem)\r
1447                                 m_barRepository.ShowUrl(pItem->absolutepath, GetRevision());\r
1448                 }\r
1449         }\r
1450 }\r
1451 \r
1452 void CRepositoryBrowser::OnLvnEndlabeleditRepolist(NMHDR *pNMHDR, LRESULT *pResult)\r
1453 {\r
1454         NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);\r
1455         *pResult = 0;\r
1456         if (pDispInfo->item.pszText == NULL)\r
1457                 return;\r
1458         // rename the item in the repository\r
1459         CItem * pItem = (CItem *)m_RepoList.GetItemData(pDispInfo->item.iItem);\r
1460 \r
1461         CWaitCursorEx wait_cursor;\r
1462         CInputLogDlg input(this);\r
1463         input.SetUUID(m_sUUID);\r
1464         input.SetProjectProperties(&m_ProjectProperties);\r
1465         CTSVNPath targetUrl = CTSVNPath(EscapeUrl(CTSVNPath(pItem->absolutepath.Left(pItem->absolutepath.ReverseFind('/')+1)+pDispInfo->item.pszText)));\r
1466         if (!targetUrl.IsValidOnWindows())\r
1467         {\r
1468                 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH, IDS_APPNAME, MB_ICONINFORMATION|MB_YESNO) != IDYES)\r
1469                         return;\r
1470         }\r
1471         CString sHint;\r
1472         sHint.Format(IDS_INPUT_RENAME, (LPCTSTR)(pItem->absolutepath), (LPCTSTR)targetUrl.GetSVNPathString());\r
1473         input.SetActionText(sHint);\r
1474         if (input.DoModal() == IDOK)\r
1475         {\r
1476                 m_bCancelled = false;\r
1477                 if (!Move(CTSVNPathList(CTSVNPath(EscapeUrl(CTSVNPath(pItem->absolutepath)))),\r
1478                         targetUrl,\r
1479                         true, input.GetLogMessage()))\r
1480                 {\r
1481                         wait_cursor.Hide();\r
1482                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
1483                         return;\r
1484                 }\r
1485                 *pResult = TRUE;\r
1486                 RefreshNode(m_RepoTree.GetSelectedItem(), true);\r
1487         }\r
1488 }\r
1489 \r
1490 void CRepositoryBrowser::OnTvnEndlabeleditRepotree(NMHDR *pNMHDR, LRESULT *pResult)\r
1491 {\r
1492         LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);\r
1493         *pResult = 0;\r
1494         if (pTVDispInfo->item.pszText == NULL)\r
1495                 return;\r
1496 \r
1497         // rename the item in the repository\r
1498         HTREEITEM hSelectedItem = pTVDispInfo->item.hItem;\r
1499         CTreeItem * pItem = (CTreeItem *)m_RepoTree.GetItemData(hSelectedItem);\r
1500         if (pItem == NULL)\r
1501                 return;\r
1502 \r
1503         CWaitCursorEx wait_cursor;\r
1504         CInputLogDlg input(this);\r
1505         input.SetUUID(m_sUUID);\r
1506         input.SetProjectProperties(&m_ProjectProperties);\r
1507         CTSVNPath targetUrl = CTSVNPath(EscapeUrl(CTSVNPath(pItem->url.Left(pItem->url.ReverseFind('/')+1)+pTVDispInfo->item.pszText)));\r
1508         if (!targetUrl.IsValidOnWindows())\r
1509         {\r
1510                 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH, IDS_APPNAME, MB_ICONINFORMATION|MB_YESNO) != IDYES)\r
1511                         return;\r
1512         }\r
1513         CString sHint;\r
1514         sHint.Format(IDS_INPUT_RENAME, (LPCTSTR)(pItem->url), (LPCTSTR)targetUrl.GetSVNPathString());\r
1515         input.SetActionText(sHint);\r
1516         if (input.DoModal() == IDOK)\r
1517         {\r
1518                 m_bCancelled = false;\r
1519                 if (!Move(CTSVNPathList(CTSVNPath(EscapeUrl(CTSVNPath(pItem->url)))),\r
1520                         targetUrl,\r
1521                         true, input.GetLogMessage()))\r
1522                 {\r
1523                         wait_cursor.Hide();\r
1524                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
1525                         return;\r
1526                 }\r
1527                 *pResult = TRUE;\r
1528                 pItem->url = targetUrl.GetSVNPathString();\r
1529                 pItem->unescapedname = pTVDispInfo->item.pszText;\r
1530                 m_RepoTree.SetItemData(hSelectedItem, (DWORD_PTR)pItem);\r
1531                 if (hSelectedItem == m_RepoTree.GetSelectedItem())\r
1532                         RefreshNode(hSelectedItem, true);\r
1533         }\r
1534 }\r
1535 \r
1536 void CRepositoryBrowser::OnLvnBeginrdragRepolist(NMHDR *pNMHDR, LRESULT *pResult)\r
1537 {\r
1538         m_bRightDrag = true;\r
1539         *pResult = 0;\r
1540         OnBeginDrag(pNMHDR);\r
1541 }\r
1542 \r
1543 void CRepositoryBrowser::OnLvnBegindragRepolist(NMHDR *pNMHDR, LRESULT *pResult)\r
1544 {\r
1545         m_bRightDrag = false;\r
1546         *pResult = 0;\r
1547         OnBeginDrag(pNMHDR);\r
1548 }\r
1549 \r
1550 void CRepositoryBrowser::OnBeginDrag(NMHDR *pNMHDR)\r
1551 {\r
1552         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
1553 \r
1554         if (m_RepoList.HasText())\r
1555                 return;\r
1556         CIDropSource* pdsrc = new CIDropSource;\r
1557         if (pdsrc == NULL)\r
1558                 return;\r
1559         pdsrc->AddRef();\r
1560 \r
1561         CTSVNPathList sourceURLs;\r
1562         POSITION pos = m_RepoList.GetFirstSelectedItemPosition();\r
1563         int index = -1;\r
1564         while ((index = m_RepoList.GetNextSelectedItem(pos))>=0)\r
1565         {\r
1566                 CItem * pItem = (CItem *)m_RepoList.GetItemData(index);\r
1567                 if (pItem)\r
1568                         sourceURLs.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(pItem->absolutepath))));\r
1569         }\r
1570 \r
1571         SVNDataObject* pdobj = new SVNDataObject(sourceURLs, GetRevision(), GetRevision());\r
1572         if (pdobj == NULL)\r
1573         {\r
1574                 delete pdsrc;\r
1575                 return;\r
1576         }\r
1577         pdobj->AddRef();\r
1578         pdobj->SetAsyncMode(TRUE);\r
1579 \r
1580         CDragSourceHelper dragsrchelper;\r
1581         dragsrchelper.InitializeFromWindow(m_RepoList.GetSafeHwnd(), pNMLV->ptAction, pdobj);\r
1582         // Initiate the Drag & Drop\r
1583         DWORD dwEffect;\r
1584         ::DoDragDrop(pdobj, pdsrc, DROPEFFECT_MOVE|DROPEFFECT_COPY, &dwEffect);\r
1585         pdsrc->Release();\r
1586         pdobj->Release();\r
1587 }\r
1588 \r
1589 void CRepositoryBrowser::OnTvnBegindragRepotree(NMHDR *pNMHDR, LRESULT *pResult)\r
1590 {\r
1591         m_bRightDrag = false;\r
1592         *pResult = 0;\r
1593         OnBeginDragTree(pNMHDR);\r
1594 }\r
1595 \r
1596 void CRepositoryBrowser::OnTvnBeginrdragRepotree(NMHDR *pNMHDR, LRESULT *pResult)\r
1597 {\r
1598         m_bRightDrag = true;\r
1599         *pResult = 0;\r
1600         OnBeginDragTree(pNMHDR);\r
1601 }\r
1602 \r
1603 void CRepositoryBrowser::OnBeginDragTree(NMHDR *pNMHDR)\r
1604 {\r
1605         LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);\r
1606 \r
1607         if (m_blockEvents)\r
1608                 return;\r
1609 \r
1610         CTreeItem * pTreeItem = (CTreeItem *)pNMTreeView->itemNew.lParam;\r
1611 \r
1612         if (pTreeItem == NULL)\r
1613                 return;\r
1614 \r
1615         CIDropSource* pdsrc = new CIDropSource;\r
1616         if (pdsrc == NULL)\r
1617                 return;\r
1618         pdsrc->AddRef();\r
1619 \r
1620         CTSVNPathList sourceURLs;\r
1621         sourceURLs.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(pTreeItem->url))));\r
1622 \r
1623         SVNDataObject* pdobj = new SVNDataObject(sourceURLs, GetRevision(), GetRevision());\r
1624         if (pdobj == NULL)\r
1625         {\r
1626                 delete pdsrc;\r
1627                 return;\r
1628         }\r
1629         pdobj->AddRef();\r
1630 \r
1631         CDragSourceHelper dragsrchelper;\r
1632         dragsrchelper.InitializeFromWindow(m_RepoTree.GetSafeHwnd(), pNMTreeView->ptDrag, pdobj);\r
1633         // Initiate the Drag & Drop\r
1634         DWORD dwEffect;\r
1635         ::DoDragDrop(pdobj, pdsrc, DROPEFFECT_MOVE|DROPEFFECT_COPY, &dwEffect);\r
1636         pdsrc->Release();\r
1637         pdobj->Release();\r
1638 }\r
1639 \r
1640 \r
1641 bool CRepositoryBrowser::OnDrop(const CTSVNPath& target, const CTSVNPathList& pathlist, const SVNRev& srcRev, DWORD dwEffect, POINTL /*pt*/)\r
1642 {\r
1643         ATLTRACE(_T("dropped %ld items on %s, source revision is %s, dwEffect is %ld\n"), pathlist.GetCount(), (LPCTSTR)target.GetSVNPathString(), srcRev.ToString(), dwEffect);\r
1644         if (pathlist.GetCount() == 0)\r
1645                 return false;\r
1646 \r
1647         CString targetName = pathlist[0].GetFileOrDirectoryName();\r
1648         if (m_bRightDrag)\r
1649         {\r
1650                 // right dragging means we have to show a context menu\r
1651                 POINT pt;\r
1652                 DWORD ptW = GetMessagePos();\r
1653                 pt.x = GET_X_LPARAM(ptW);\r
1654                 pt.y = GET_Y_LPARAM(ptW);\r
1655                 CMenu popup;\r
1656                 if (popup.CreatePopupMenu())\r
1657                 {\r
1658                         CString temp(MAKEINTRESOURCE(IDS_REPOBROWSE_COPYDROP));\r
1659                         popup.AppendMenu(MF_STRING | MF_ENABLED, 1, temp);\r
1660                         temp.LoadString(IDS_REPOBROWSE_MOVEDROP);\r
1661                         popup.AppendMenu(MF_STRING | MF_ENABLED, 2, temp);\r
1662                         if ((pathlist.GetCount() == 1)&&(PathIsURL(pathlist[0])))\r
1663                         {\r
1664                                 // these entries are only shown if *one* item was dragged, and if the\r
1665                                 // item is not one dropped from e.g. the explorer but from the repository\r
1666                                 // browser itself.\r
1667                                 popup.AppendMenu(MF_SEPARATOR, 3);\r
1668                                 temp.LoadString(IDS_REPOBROWSE_COPYRENAMEDROP);\r
1669                                 popup.AppendMenu(MF_STRING | MF_ENABLED, 4, temp);\r
1670                                 temp.LoadString(IDS_REPOBROWSE_MOVERENAMEDROP);\r
1671                                 popup.AppendMenu(MF_STRING | MF_ENABLED, 5, temp);\r
1672                         }\r
1673                         int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, pt.x, pt.y, this, 0);\r
1674                         switch (cmd)\r
1675                         {\r
1676                         default:// nothing clicked\r
1677                                 return false;\r
1678                         case 1: // copy drop\r
1679                                 dwEffect = DROPEFFECT_COPY;\r
1680                                 break;\r
1681                         case 2: // move drop\r
1682                                 dwEffect = DROPEFFECT_MOVE;\r
1683                                 break;\r
1684                         case 4: // copy rename drop\r
1685                                 {\r
1686                                         dwEffect = DROPEFFECT_COPY;\r
1687                                         CRenameDlg dlg;\r
1688                                         dlg.m_name = targetName;\r
1689                                         dlg.m_windowtitle.LoadString(IDS_REPOBROWSE_RENAME);\r
1690                                         CStringUtils::RemoveAccelerators(dlg.m_windowtitle);\r
1691                                         if (dlg.DoModal() != IDOK)\r
1692                                                 return false;\r
1693                                         targetName = dlg.m_name;\r
1694                                         if (!CTSVNPath(targetName).IsValidOnWindows())\r
1695                                         {\r
1696                                                 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH, IDS_APPNAME, MB_ICONINFORMATION|MB_YESNO) != IDYES)\r
1697                                                         return false;\r
1698                                         }\r
1699                                 }\r
1700                                 break;\r
1701                         case 5: // move rename drop\r
1702                                 {\r
1703                                         dwEffect = DROPEFFECT_MOVE;\r
1704                                         CRenameDlg dlg;\r
1705                                         dlg.m_name = targetName;\r
1706                                         dlg.m_windowtitle.LoadString(IDS_REPOBROWSE_RENAME);\r
1707                                         CStringUtils::RemoveAccelerators(dlg.m_windowtitle);\r
1708                                         if (dlg.DoModal() != IDOK)\r
1709                                                 return false;\r
1710                                         targetName = dlg.m_name;\r
1711                                         if (!CTSVNPath(targetName).IsValidOnWindows())\r
1712                                         {\r
1713                                                 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH, IDS_APPNAME, MB_ICONINFORMATION|MB_YESNO) != IDYES)\r
1714                                                         return false;\r
1715                                         }\r
1716                                 }\r
1717                                 break;\r
1718                         }\r
1719                 }\r
1720 \r
1721         }\r
1722         // check the first item in the path list:\r
1723         // if it's an url, we do a copy or move operation\r
1724         // if it's a local path, we do an import\r
1725         if (PathIsURL(pathlist[0]))\r
1726         {\r
1727                 // If any of the paths are 'special' (branches, tags, or trunk) and we are\r
1728                 // about to perform a move, we should warn the user and get them to confirm\r
1729                 // that this is what they intended. Yes, I *have* accidentally moved the\r
1730                 // trunk when I was trying to create a tag! :)\r
1731                 if (DROPEFFECT_COPY != dwEffect)\r
1732                 {\r
1733                         bool pathListIsSpecial = false;\r
1734                         int pathCount = pathlist.GetCount();\r
1735                         for (int i=0 ; i<pathCount ; ++i)\r
1736                         {\r
1737                                 const CTSVNPath& path = pathlist[i];\r
1738                                 if (path.IsSpecialDirectory())\r
1739                                 {\r
1740                                         pathListIsSpecial = true;\r
1741                                         break;\r
1742                                 }\r
1743                         }\r
1744                         if (pathListIsSpecial)\r
1745                         {\r
1746                                 UINT msgResult = CMessageBox::Show(GetSafeHwnd(), IDS_WARN_CONFIRM_MOVE_SPECIAL_DIRECTORY, IDS_APPNAME, MB_ICONQUESTION | MB_YESNO);\r
1747                                 if (IDYES != msgResult)\r
1748                                 {\r
1749                                         return false;\r
1750                                 }\r
1751                         }\r
1752                 }\r
1753 \r
1754                 // drag-n-drop inside the repobrowser\r
1755                 CInputLogDlg input(this);\r
1756                 input.SetUUID(m_sUUID);\r
1757                 input.SetProjectProperties(&m_ProjectProperties);\r
1758                 CString sHint;\r
1759                 if (pathlist.GetCount() == 1)\r
1760                 {\r
1761                         if (dwEffect == DROPEFFECT_COPY)\r
1762                                 sHint.Format(IDS_INPUT_COPY, (LPCTSTR)pathlist[0].GetSVNPathString(), (LPCTSTR)(target.GetSVNPathString()+_T("/")+targetName));\r
1763                         else\r
1764                                 sHint.Format(IDS_INPUT_MOVE, (LPCTSTR)pathlist[0].GetSVNPathString(), (LPCTSTR)(target.GetSVNPathString()+_T("/")+targetName));\r
1765                 }\r
1766                 else\r
1767                 {\r
1768                         if (dwEffect == DROPEFFECT_COPY)\r
1769                                 sHint.Format(IDS_INPUT_COPYMORE, pathlist.GetCount(), (LPCTSTR)target.GetSVNPathString());\r
1770                         else\r
1771                                 sHint.Format(IDS_INPUT_MOVEMORE, pathlist.GetCount(), (LPCTSTR)target.GetSVNPathString());\r
1772                 }\r
1773                 input.SetActionText(sHint);\r
1774                 if (input.DoModal() == IDOK)\r
1775                 {\r
1776                         m_bCancelled = false;\r
1777                         CWaitCursorEx wait_cursor;\r
1778                         BOOL bRet = FALSE;\r
1779                         if (dwEffect == DROPEFFECT_COPY)\r
1780                                 if (pathlist.GetCount() == 1)\r
1781                                         bRet = Copy(pathlist, CTSVNPath(target.GetSVNPathString() + _T("/") + targetName), srcRev, srcRev, input.GetLogMessage(), false);\r
1782                                 else\r
1783                                         bRet = Copy(pathlist, target, srcRev, srcRev, input.GetLogMessage(), true);\r
1784                         else\r
1785                                 if (pathlist.GetCount() == 1)\r
1786                                         bRet = Move(pathlist, CTSVNPath(target.GetSVNPathString() + _T("/") + targetName), TRUE, input.GetLogMessage(), false);\r
1787                                 else\r
1788                                         bRet = Move(pathlist, target, TRUE, input.GetLogMessage(), true);\r
1789                         if (!bRet)\r
1790                         {\r
1791                                 wait_cursor.Hide();\r
1792                                 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
1793                         }\r
1794                         else if (GetRevision().IsHead())\r
1795                         {\r
1796                                 // mark the target as dirty\r
1797                                 HTREEITEM hTarget = FindUrl(target.GetSVNPathString(), false);\r
1798                                 if (hTarget)\r
1799                                 {\r
1800                                         CTreeItem * pItem = (CTreeItem*)m_RepoTree.GetItemData(hTarget);\r
1801                                         if (pItem)\r
1802                                         {\r
1803                                                 // mark the target as 'dirty'\r
1804                                                 pItem->children_fetched = false;\r
1805                                                 RecursiveRemove(hTarget, true);\r
1806                                                 TVITEM tvitem = {0};\r
1807                                                 tvitem.hItem = hTarget;\r
1808                                                 tvitem.mask = TVIF_CHILDREN;\r
1809                                                 tvitem.cChildren = 1;\r
1810                                                 m_RepoTree.SetItem(&tvitem);\r
1811                                         }\r
1812                                 }\r
1813                                 if (dwEffect == DROPEFFECT_MOVE)\r
1814                                 {\r
1815                                         // if items were moved, we have to\r
1816                                         // invalidate all sources too\r
1817                                         for (int i=0; i<pathlist.GetCount(); ++i)\r
1818                                         {\r
1819                                                 HTREEITEM hSource = FindUrl(pathlist[i].GetSVNPathString(), false);\r
1820                                                 if (hSource)\r
1821                                                 {\r
1822                                                         CTreeItem * pItem = (CTreeItem*)m_RepoTree.GetItemData(hSource);\r
1823                                                         if (pItem)\r
1824                                                         {\r
1825                                                                 // the source has moved, so remove it!\r
1826                                                                 RecursiveRemove(hSource);\r
1827                                                                 m_RepoTree.DeleteItem(hSource);\r
1828                                                         }\r
1829                                                 }\r
1830                                         }\r
1831                                 }\r
1832 \r
1833                                 // if the copy/move operation was to the currently shown url,\r
1834                                 // update the current view. Otherwise mark the target URL as 'not fetched'.\r
1835                                 HTREEITEM hSelected = m_RepoTree.GetSelectedItem();\r
1836                                 if (hSelected)\r
1837                                 {\r
1838                                         CTreeItem * pItem = (CTreeItem*)m_RepoTree.GetItemData(hSelected);\r
1839                                         if (pItem)\r
1840                                         {\r
1841                                                 // mark the target as 'dirty'\r
1842                                                 pItem->children_fetched = false;\r
1843                                                 if ((dwEffect == DROPEFFECT_MOVE)||(pItem->url.Compare(target.GetSVNPathString())==0))\r
1844                                                 {\r
1845                                                         // Refresh the current view\r
1846                                                         RefreshNode(hSelected, true);\r
1847                                                 }\r
1848                                         }\r
1849                                 }\r
1850                         }\r
1851                 }\r
1852         }\r
1853         else\r
1854         {\r
1855                 // import files dragged onto us\r
1856                 if (pathlist.GetCount() > 1)\r
1857                 {\r
1858                         if (CMessageBox::Show(m_hWnd, IDS_REPOBROWSE_MULTIIMPORT, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION)!=IDYES)\r
1859                                 return false;\r
1860                 }\r
1861 \r
1862                 CInputLogDlg input(this);\r
1863                 input.SetProjectProperties(&m_ProjectProperties);\r
1864                 input.SetUUID(m_sUUID);\r
1865                 CString sHint;\r
1866                 if (pathlist.GetCount() == 1)\r
1867                         sHint.Format(IDS_INPUT_IMPORTFILEFULL, pathlist[0].GetWinPath(), (LPCTSTR)(target.GetSVNPathString() + _T("/") + pathlist[0].GetFileOrDirectoryName()));\r
1868                 else\r
1869                         sHint.Format(IDS_INPUT_IMPORTFILES, pathlist.GetCount());\r
1870                 input.SetActionText(sHint);\r
1871 \r
1872                 if (input.DoModal() == IDOK)\r
1873                 {\r
1874                         m_bCancelled = false;\r
1875                         for (int importindex = 0; importindex<pathlist.GetCount(); ++importindex)\r
1876                         {\r
1877                                 CString filename = pathlist[importindex].GetFileOrDirectoryName();\r
1878                                 if (!Import(pathlist[importindex], \r
1879                                         CTSVNPath(target.GetSVNPathString()+_T("/")+filename), \r
1880                                         input.GetLogMessage(), &m_ProjectProperties, svn_depth_infinity, TRUE, FALSE))\r
1881                                 {\r
1882                                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
1883                                         return false;\r
1884                                 }\r
1885                         }\r
1886                         if (GetRevision().IsHead())\r
1887                         {\r
1888                                 // if the import operation was to the currently shown url,\r
1889                                 // update the current view. Otherwise mark the target URL as 'not fetched'.\r
1890                                 HTREEITEM hSelected = m_RepoTree.GetSelectedItem();\r
1891                                 if (hSelected)\r
1892                                 {\r
1893                                         CTreeItem * pItem = (CTreeItem*)m_RepoTree.GetItemData(hSelected);\r
1894                                         if (pItem)\r
1895                                         {\r
1896                                                 if (pItem->url.Compare(target.GetSVNPathString())==0)\r
1897                                                 {\r
1898                                                         // Refresh the current view\r
1899                                                         RefreshNode(hSelected, true);\r
1900                                                 }\r
1901                                                 else\r
1902                                                 {\r
1903                                                         // only mark the target as 'dirty'\r
1904                                                         pItem->children_fetched = false;\r
1905                                                 }\r
1906                                         }\r
1907                                 }\r
1908                         }\r
1909                 }\r
1910 \r
1911         }\r
1912         return true;\r
1913 }\r
1914 \r
1915 CString CRepositoryBrowser::EscapeUrl(const CTSVNPath& url)\r
1916 {\r
1917         return CUnicodeUtils::GetUnicode(CPathUtils::PathEscape(CUnicodeUtils::GetUTF8(url.GetSVNPathString())));\r
1918 }\r
1919 \r
1920 void CRepositoryBrowser::OnContextMenu(CWnd* pWnd, CPoint point)\r
1921 {\r
1922         HTREEITEM hSelectedTreeItem = NULL;\r
1923         HTREEITEM hChosenTreeItem = NULL;\r
1924         if ((point.x == -1) && (point.y == -1))\r
1925         {\r
1926                 if (pWnd == &m_RepoTree)\r
1927                 {\r
1928                         CRect rect;\r
1929                         m_RepoTree.GetItemRect(m_RepoTree.GetSelectedItem(), &rect, TRUE);\r
1930                         m_RepoTree.ClientToScreen(&rect);\r
1931                         point = rect.CenterPoint();\r
1932                 }\r
1933                 else\r
1934                 {\r
1935                         CRect rect;\r
1936                         POSITION pos = m_RepoList.GetFirstSelectedItemPosition();\r
1937                         m_RepoList.GetItemRect(m_RepoList.GetNextSelectedItem(pos), &rect, LVIR_LABEL);\r
1938                         m_RepoList.ClientToScreen(&rect);\r
1939                         point = rect.CenterPoint();\r
1940                 }\r
1941         }\r
1942         m_bCancelled = false;\r
1943         CTSVNPathList urlList;\r
1944         CTSVNPathList urlListEscaped;\r
1945         int nFolders = 0;\r
1946         int nLocked = 0;\r
1947         if (pWnd == &m_RepoList)\r
1948         {\r
1949                 CString urls;\r
1950 \r
1951                 POSITION pos = m_RepoList.GetFirstSelectedItemPosition();\r
1952                 int index = -1;\r
1953                 while ((index = m_RepoList.GetNextSelectedItem(pos))>=0)\r
1954                 {\r
1955                         CItem * pItem = (CItem *)m_RepoList.GetItemData(index);\r
1956                         CString absPath = pItem->absolutepath;\r
1957                         absPath.Replace(_T("\\"), _T("%5C"));\r
1958                         urlList.AddPath(CTSVNPath(absPath));\r
1959                         urlListEscaped.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(absPath))));\r
1960                         if (pItem->kind == svn_node_dir)\r
1961                                 nFolders++;\r
1962                         if (!pItem->locktoken.IsEmpty())\r
1963                                 nLocked++;\r
1964                 }\r
1965                 if (urlList.GetCount() == 0)\r
1966                 {\r
1967                         // Right-click outside any list control items. It may be the background,\r
1968                         // but it also could be the list control headers.\r
1969                         CRect hr;\r
1970                         m_RepoList.GetHeaderCtrl()->GetWindowRect(&hr);\r
1971                         if (!hr.PtInRect(point))\r
1972                         {\r
1973                                 // Seems to be a right-click on the list view background.\r
1974                                 // Use the currently selected item in the tree view as the source.\r
1975                                 m_blockEvents = true;\r
1976                                 hSelectedTreeItem = m_RepoTree.GetSelectedItem();\r
1977                                 if (hSelectedTreeItem)\r
1978                                 {\r
1979                                         m_RepoTree.SetItemState(hSelectedTreeItem, 0, TVIS_SELECTED);\r
1980                                         m_blockEvents = false;\r
1981                                         m_RepoTree.SetItemState(hSelectedTreeItem, TVIS_DROPHILITED, TVIS_DROPHILITED);\r
1982                                         CTreeItem * pTreeItem = (CTreeItem *)m_RepoTree.GetItemData(hSelectedTreeItem);\r
1983                                         if (pTreeItem)\r
1984                                         {\r
1985                                                 urlList.AddPath(CTSVNPath(pTreeItem->url));\r
1986                                                 urlListEscaped.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(pTreeItem->url))));\r
1987                                                 nFolders++;\r
1988                                         }\r
1989                                 }\r
1990                         }\r
1991                 }\r
1992         }\r
1993         if ((pWnd == &m_RepoTree)||(urlList.GetCount() == 0))\r
1994         {\r
1995                 UINT uFlags;\r
1996                 CPoint ptTree = point;\r
1997                 m_RepoTree.ScreenToClient(&ptTree);\r
1998                 HTREEITEM hItem = m_RepoTree.HitTest(ptTree, &uFlags);\r
1999                 // in case the right-clicked item is not the selected one,\r
2000                 // use the TVIS_DROPHILITED style to indicate on which item\r
2001                 // the context menu will work on\r
2002                 if ((hItem) && (uFlags & TVHT_ONITEM) && (hItem != m_RepoTree.GetSelectedItem()))\r
2003                 {\r
2004                         m_blockEvents = true;\r
2005                         hSelectedTreeItem = m_RepoTree.GetSelectedItem();\r
2006                         m_RepoTree.SetItemState(hSelectedTreeItem, 0, TVIS_SELECTED);\r
2007                         m_blockEvents = false;\r
2008                         m_RepoTree.SetItemState(hItem, TVIS_DROPHILITED, TVIS_DROPHILITED);\r
2009                 }\r
2010                 if (hItem)\r
2011                 {\r
2012                         hChosenTreeItem = hItem;\r
2013                         CTreeItem * pTreeItem = (CTreeItem *)m_RepoTree.GetItemData(hItem);\r
2014                         if (pTreeItem)\r
2015                         {\r
2016                                 urlList.AddPath(CTSVNPath(pTreeItem->url));\r
2017                                 urlListEscaped.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(pTreeItem->url))));\r
2018                                 nFolders++;\r
2019                         }\r
2020                 }\r
2021         }\r
2022 \r
2023         if (urlList.GetCount() == 0)\r
2024                 return;\r
2025 \r
2026         CIconMenu popup;\r
2027         if (popup.CreatePopupMenu())\r
2028         {\r
2029                 if (urlList.GetCount() == 1)\r
2030                 {\r
2031                         if (nFolders == 0)\r
2032                         {\r
2033                                 // Let "Open" be the very first entry, like in Explorer\r
2034                                 popup.AppendMenuIcon(ID_OPEN, IDS_REPOBROWSE_OPEN, IDI_OPEN);           // "open"\r
2035                                 popup.AppendMenuIcon(ID_OPENWITH, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);    // "open with..."\r
2036                                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
2037                         }\r
2038                         popup.AppendMenuIcon(ID_SHOWLOG, IDS_REPOBROWSE_SHOWLOG, IDI_LOG);                      // "Show Log..."\r
2039                         // the revision graph on the repository root would be empty. We\r
2040                         // don't show the context menu entry there.\r
2041                         if (urlList[0].GetSVNPathString().Compare(m_strReposRoot)!=0)\r
2042                         {\r
2043                                 popup.AppendMenuIcon(ID_REVGRAPH, IDS_MENUREVISIONGRAPH, IDI_REVISIONGRAPH); // "Revision graph"\r
2044                         }\r
2045                         if (nFolders == 0)\r
2046                         {\r
2047                                 popup.AppendMenuIcon(ID_BLAME, IDS_MENUBLAME, IDI_BLAME);               // "Blame..."\r
2048                         }\r
2049                         if (!m_ProjectProperties.sWebViewerRev.IsEmpty())\r
2050                         {\r
2051                                 popup.AppendMenuIcon(ID_VIEWREV, IDS_LOG_POPUP_VIEWREV);                // "View revision in webviewer"\r
2052                         }\r
2053                         if (!m_ProjectProperties.sWebViewerPathRev.IsEmpty())\r
2054                         {\r
2055                                 popup.AppendMenuIcon(ID_VIEWPATHREV, IDS_LOG_POPUP_VIEWPATHREV);        // "View revision for path in webviewer"\r
2056                         }\r
2057                         if ((!m_ProjectProperties.sWebViewerPathRev.IsEmpty())||\r
2058                                 (!m_ProjectProperties.sWebViewerRev.IsEmpty()))\r
2059                         {\r
2060                                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
2061                         }\r
2062                         if (nFolders)\r
2063                         {\r
2064                                 popup.AppendMenuIcon(ID_EXPORT, IDS_MENUEXPORT, IDI_EXPORT);            // "Export"\r
2065                         }\r
2066                 }\r
2067                 // We allow checkout of multiple folders at once (we do that one by one)\r
2068                 if (nFolders == urlList.GetCount())\r
2069                 {\r
2070                         popup.AppendMenuIcon(ID_CHECKOUT, IDS_MENUCHECKOUT, IDI_CHECKOUT);              // "Checkout.."\r
2071                 }\r
2072                 if (urlList.GetCount() == 1)\r
2073                 {\r
2074                         if (nFolders)\r
2075                         {\r
2076                                 popup.AppendMenuIcon(ID_REFRESH, IDS_REPOBROWSE_REFRESH, IDI_REFRESH);          // "Refresh"\r
2077                         }\r
2078                         popup.AppendMenu(MF_SEPARATOR, NULL);                           \r
2079 \r
2080                         if (GetRevision().IsHead())\r
2081                         {\r
2082                                 if (nFolders)\r
2083                                 {\r
2084                                         popup.AppendMenuIcon(ID_MKDIR, IDS_REPOBROWSE_MKDIR, IDI_MKDIR);        // "create directory"\r
2085                                         popup.AppendMenuIcon(ID_IMPORT, IDS_REPOBROWSE_IMPORT, IDI_IMPORT);     // "Add/Import File"\r
2086                                         popup.AppendMenuIcon(ID_IMPORTFOLDER, IDS_REPOBROWSE_IMPORTFOLDER, IDI_IMPORT); // "Add/Import Folder"\r
2087                                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
2088                                 }\r
2089 \r
2090                                 popup.AppendMenuIcon(ID_RENAME, IDS_REPOBROWSE_RENAME, IDI_RENAME);             // "Rename"\r
2091                         }\r
2092                         if (nLocked)\r
2093                         {\r
2094                                 popup.AppendMenuIcon(ID_BREAKLOCK, IDS_MENU_UNLOCKFORCE, IDI_UNLOCK);   // "Break Lock"\r
2095                         }\r
2096                 }\r
2097                 if (urlList.GetCount() > 0)\r
2098                 {\r
2099                         if (GetRevision().IsHead())\r
2100                         {\r
2101                                 popup.AppendMenuIcon(ID_DELETE, IDS_REPOBROWSE_DELETE, IDI_DELETE);             // "Remove"\r
2102                         }\r
2103                         if (nFolders == 0)\r
2104                         {\r
2105                                 popup.AppendMenuIcon(ID_SAVEAS, IDS_REPOBROWSE_SAVEAS, IDI_SAVEAS);             // "Save as..."\r
2106                         }\r
2107                         if ((urlList.GetCount() == nFolders)||(nFolders == 0))\r
2108                         {\r
2109                                 popup.AppendMenuIcon(ID_COPYTOWC, IDS_REPOBROWSE_COPYTOWC);     // "Copy To Working Copy..."\r
2110                         }\r
2111                 }\r
2112                 if (urlList.GetCount() == 1)\r
2113                 {\r
2114                         popup.AppendMenuIcon(ID_COPYTO, IDS_REPOBROWSE_COPY, IDI_COPY);                 // "Copy To..."\r
2115                         popup.AppendMenuIcon(ID_URLTOCLIPBOARD, IDS_REPOBROWSE_URLTOCLIPBOARD, IDI_COPYCLIP);   // "Copy URL to clipboard"\r
2116                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
2117                         popup.AppendMenuIcon(ID_PROPS, IDS_REPOBROWSE_SHOWPROP, IDI_PROPERTIES);                        // "Show Properties"\r
2118                         // Revision properties are not associated to paths\r
2119                         // so we only show that context menu on the repository root\r
2120                         if (urlList[0].GetSVNPathString().Compare(m_strReposRoot)==0)\r
2121                         {\r
2122                                 popup.AppendMenuIcon(ID_REVPROPS, IDS_REPOBROWSE_SHOWREVPROP, IDI_PROPERTIES);  // "Show Revision Properties"\r
2123                         }\r
2124                         if (nFolders == 1)\r
2125                         {\r
2126                                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
2127                                 popup.AppendMenuIcon(ID_PREPAREDIFF, IDS_REPOBROWSE_PREPAREDIFF);       // "Mark for comparison"\r
2128 \r
2129                                 if ((m_diffKind == svn_node_dir)&&(!m_diffURL.IsEquivalentTo(urlList[0])))\r
2130                                 {\r
2131                                         popup.AppendMenuIcon(ID_GNUDIFF, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);              // "Show differences as unified diff"\r
2132                                         popup.AppendMenuIcon(ID_DIFF, IDS_REPOBROWSE_SHOWDIFF, IDI_DIFF);               // "Compare URLs"\r
2133                                 }\r
2134                         }\r
2135                 }\r
2136                 if (urlList.GetCount() == 2)\r
2137                 {\r
2138                         if ((nFolders == 2)||(nFolders == 0))\r
2139                         {\r
2140                                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
2141                                 popup.AppendMenuIcon(ID_GNUDIFF, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);              // "Show differences as unified diff"\r
2142                                 popup.AppendMenuIcon(ID_DIFF, IDS_REPOBROWSE_SHOWDIFF, ID_DIFF);                // "Compare URLs"\r
2143                                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
2144                         }\r
2145                         popup.AppendMenuIcon(ID_SHOWLOG, IDS_MENULOG, IDI_LOG);         // "Show Log..."\r
2146                 }\r
2147                 if ((urlList.GetCount() == 1) &&\r
2148                         m_path.Exists() && \r
2149                         CTSVNPath(m_InitialUrl).IsAncestorOf(urlList[0]))\r
2150                 {\r
2151                         CTSVNPath wcPath = m_path;\r
2152                         wcPath.AppendPathString(urlList[0].GetWinPathString().Mid(m_InitialUrl.GetLength()));\r
2153                         if (!wcPath.Exists())\r
2154                         {\r
2155                                 bool bWCPresent = false;\r
2156                                 while (!bWCPresent && m_path.IsAncestorOf(wcPath))\r
2157                                 {\r
2158                                         bWCPresent = wcPath.GetContainingDirectory().Exists();\r
2159                                         wcPath = wcPath.GetContainingDirectory();\r
2160                                 }\r
2161                                 if (bWCPresent)\r
2162                                 {\r
2163                                         popup.AppendMenu(MF_SEPARATOR, NULL);\r
2164                                         popup.AppendMenuIcon(ID_UPDATE, IDS_LOG_POPUP_UPDATE, IDI_UPDATE);              // "Update item to revision"\r
2165                                 }\r
2166                         }\r
2167                 }\r
2168                 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
2169 \r
2170                 if (pWnd == &m_RepoTree)\r
2171                 {\r
2172                         UINT uFlags;\r
2173                         CPoint ptTree = point;\r
2174                         m_RepoTree.ScreenToClient(&ptTree);\r
2175                         HTREEITEM hItem = m_RepoTree.HitTest(ptTree, &uFlags);\r
2176                         // restore the previously selected item state\r
2177                         if ((hItem) && (uFlags & TVHT_ONITEM) && (hItem != m_RepoTree.GetSelectedItem()))\r
2178                         {\r
2179                                 m_blockEvents = true;\r
2180                                 m_RepoTree.SetItemState(hSelectedTreeItem, TVIS_SELECTED, TVIS_SELECTED);\r
2181                                 m_blockEvents = false;\r
2182                                 m_RepoTree.SetItemState(hItem, 0, TVIS_DROPHILITED);\r
2183                         }\r
2184                 }\r
2185                 if (hSelectedTreeItem)\r
2186                 {\r
2187                         m_blockEvents = true;\r
2188                         m_RepoTree.SetItemState(hSelectedTreeItem, 0, TVIS_DROPHILITED);\r
2189                         m_RepoTree.SetItemState(hSelectedTreeItem, TVIS_SELECTED, TVIS_SELECTED);\r
2190                         m_blockEvents = false;\r
2191                 }\r
2192                 DialogEnableWindow(IDOK, FALSE);\r
2193                 bool bOpenWith = false;\r
2194                 switch (cmd)\r
2195                 {\r
2196                 case ID_UPDATE:\r
2197                         {\r
2198                                 CTSVNPath wcPath = m_path;\r
2199                                 wcPath.AppendPathString(urlList[0].GetWinPathString().Mid(m_InitialUrl.GetLength()));\r
2200                                 CString sCmd;\r
2201                                 sCmd.Format(_T("\"%s\" /command:update /path:\"%s\" /rev"),\r
2202                                         (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), wcPath.GetWinPath());\r
2203 \r
2204                                 CAppUtils::LaunchApplication(sCmd, NULL, false);\r
2205                         }\r
2206                         break;\r
2207                 case ID_PREPAREDIFF:\r
2208                         {\r
2209                                 m_RepoTree.SetItemState(FindUrl(m_diffURL.GetSVNPathString(), false), 0, TVIS_BOLD);\r
2210                                 if (urlList.GetCount() == 1)\r
2211                                 {\r
2212                                         m_diffURL = urlList[0];\r
2213                                         m_diffKind = nFolders ? svn_node_dir : svn_node_file;\r
2214                                         // make the marked tree item bold\r
2215                                         if (m_diffKind == svn_node_dir)\r
2216                                         {\r
2217                                                 m_RepoTree.SetItemState(FindUrl(m_diffURL.GetSVNPathString(), false), TVIS_BOLD, TVIS_BOLD);\r
2218                                         }\r
2219                                 }\r
2220                                 else\r
2221                                 {\r
2222                                         m_diffURL.Reset();\r
2223                                         m_diffKind = svn_node_none;\r
2224                                 }\r
2225                         }\r
2226                         break;\r
2227                 case ID_URLTOCLIPBOARD:\r
2228                         {\r
2229                                 CString url;\r
2230                                 for (int i=0; i<urlList.GetCount(); ++i)\r
2231                                         url += CUnicodeUtils::GetUnicode(CPathUtils::PathEscape(CUnicodeUtils::GetUTF8(urlList[i].GetSVNPathString()))) + _T("\r\n");\r
2232                                 url.TrimRight(_T("\r\n"));\r
2233                                 CStringUtils::WriteAsciiStringToClipboard(url);\r
2234                         }\r
2235                         break;\r
2236                 case ID_SAVEAS:\r
2237                         {\r
2238                                 CTSVNPath tempfile;\r
2239                                 bool bSavePathOK = AskForSavePath(urlList, tempfile, nFolders > 0);\r
2240                                 if (bSavePathOK)\r
2241                                 {\r
2242                                         CWaitCursorEx wait_cursor;\r
2243 \r
2244                                         CString saveurl;\r
2245                                         CProgressDlg progDlg;\r
2246                                         int counter = 0;                // the file counter\r
2247                                         progDlg.SetTitle(IDS_REPOBROWSE_SAVEASPROGTITLE);\r
2248                                         progDlg.SetAnimation(IDR_DOWNLOAD);\r
2249                                         progDlg.ShowModeless(GetSafeHwnd());\r
2250                                         progDlg.SetProgress((DWORD)0, (DWORD)urlList.GetCount());\r
2251                                         SetAndClearProgressInfo(&progDlg);\r
2252                                         for (int i=0; i<urlList.GetCount(); ++i)\r
2253                                         {\r
2254                                                 saveurl = EscapeUrl(urlList[i]);\r
2255                                                 CTSVNPath savepath = tempfile;\r
2256                                                 if (tempfile.IsDirectory())\r
2257                                                         savepath.AppendPathString(urlList[i].GetFileOrDirectoryName());\r
2258                                                 CString sInfoLine;\r
2259                                                 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, (LPCTSTR)saveurl, (LPCTSTR)GetRevision().ToString());\r
2260                                                 progDlg.SetLine(1, sInfoLine, true);\r
2261                                                 if (!Cat(CTSVNPath(saveurl), GetRevision(), GetRevision(), savepath)||(progDlg.HasUserCancelled()))\r
2262                                                 {\r
2263                                                         wait_cursor.Hide();\r
2264                                                         progDlg.Stop();\r
2265                                                         SetAndClearProgressInfo((HWND)NULL);\r
2266                                                         if (!progDlg.HasUserCancelled())\r
2267                                                                 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2268                                                         return;\r
2269                                                 }\r
2270                                                 counter++;\r
2271                                                 progDlg.SetProgress((DWORD)counter, (DWORD)urlList.GetCount());\r
2272                                         }\r
2273                                         progDlg.Stop();\r
2274                                         SetAndClearProgressInfo((HWND)NULL);\r
2275                                 }\r
2276                         }\r
2277                         break;\r
2278                 case ID_SHOWLOG:\r
2279                         {\r
2280                                 if (urlList.GetCount() == 2)\r
2281                                 {\r
2282                                         // get log of first URL\r
2283                                         CString sCopyFrom1, sCopyFrom2;\r
2284                                         SVNLogHelper helper;\r
2285                                         helper.SetRepositoryRoot(m_strReposRoot);\r
2286                                         SVNRev rev1 = helper.GetCopyFromRev(CTSVNPath(EscapeUrl(urlList[0])), GetRevision(), sCopyFrom1);\r
2287                                         if (!rev1.IsValid())\r
2288                                         {\r
2289                                                 CMessageBox::Show(this->m_hWnd, helper.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2290                                                 break;\r
2291                                         }\r
2292                                         SVNRev rev2 = helper.GetCopyFromRev(CTSVNPath(EscapeUrl(urlList[1])), GetRevision(), sCopyFrom2);\r
2293                                         if (!rev2.IsValid())\r
2294                                         {\r
2295                                                 CMessageBox::Show(this->m_hWnd, helper.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2296                                                 break;\r
2297                                         }\r
2298                                         if ((sCopyFrom1.IsEmpty())||(sCopyFrom1.Compare(sCopyFrom2)!=0))\r
2299                                         {\r
2300                                                 // no common copy from URL, so showing a log between\r
2301                                                 // the two urls is not possible.\r
2302                                                 CMessageBox::Show(m_hWnd, IDS_ERR_NOCOMMONCOPYFROM, IDS_APPNAME, MB_ICONERROR);\r
2303                                                 break;                                                  \r
2304                                         }\r
2305                                         if ((LONG)rev1 < (LONG)rev2)\r
2306                                         {\r
2307                                                 SVNRev temp = rev1;\r
2308                                                 rev1 = rev2;\r
2309                                                 rev2 = temp;\r
2310                                         }\r
2311                                         CString sCmd;\r
2312                                         sCmd.Format(_T("\"%s\" /command:log /path:\"%s\" /startrev:%s /endrev:%s"),\r
2313                                                 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), (LPCTSTR)sCopyFrom1, (LPCTSTR)rev1.ToString(), (LPCTSTR)rev2.ToString());\r
2314 \r
2315                                         ATLTRACE(sCmd);\r
2316                                         if (!m_path.IsUrl())\r
2317                                         {\r
2318                                                 sCmd += _T(" /propspath:\"");\r
2319                                                 sCmd += m_path.GetWinPathString();\r
2320                                                 sCmd += _T("\"");\r
2321                                         }       \r
2322 \r
2323                                         CAppUtils::LaunchApplication(sCmd, NULL, false);\r
2324                                 }\r
2325                                 else\r
2326                                 {\r
2327                                         CString sCmd;\r
2328                                         sCmd.Format(_T("\"%s\" /command:log /path:\"%s\" /startrev:%s"), \r
2329                                                 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), (LPCTSTR)EscapeUrl(urlList[0]), (LPCTSTR)GetRevision().ToString());\r
2330 \r
2331                                         if (!m_path.IsUrl())\r
2332                                         {\r
2333                                                 sCmd += _T(" /propspath:\"");\r
2334                                                 sCmd += m_path.GetWinPathString();\r
2335                                                 sCmd += _T("\"");\r
2336                                         }       \r
2337 \r
2338                                         CAppUtils::LaunchApplication(sCmd, NULL, false);\r
2339                                 }\r
2340                         }\r
2341                         break;\r
2342                 case ID_VIEWREV:\r
2343                         {\r
2344                                 CString url = m_ProjectProperties.sWebViewerRev;\r
2345                                 url.Replace(_T("%REVISION%"), GetRevision().ToString());\r
2346                                 if (!url.IsEmpty())\r
2347                                         ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);                                        \r
2348                         }\r
2349                         break;\r
2350                 case ID_VIEWPATHREV:\r
2351                         {\r
2352                                 CString relurl = EscapeUrl(urlList[0]);\r
2353                                 relurl = relurl.Mid(m_strReposRoot.GetLength());\r
2354                                 CString weburl = m_ProjectProperties.sWebViewerPathRev;\r
2355                                 weburl.Replace(_T("%REVISION%"), GetRevision().ToString());\r
2356                                 weburl.Replace(_T("%PATH%"), relurl);\r
2357                                 if (!weburl.IsEmpty())\r
2358                                         ShellExecute(this->m_hWnd, _T("open"), weburl, NULL, NULL, SW_SHOWDEFAULT);                                     \r
2359                         }\r
2360                         break;\r
2361                 case ID_CHECKOUT:\r
2362                         {\r
2363                                 CString itemsToCheckout;\r
2364                                 for (int i=0; i<urlList.GetCount(); ++i)\r
2365                                 {\r
2366                                         itemsToCheckout += EscapeUrl(urlList[i]) + _T("*");\r
2367                                 }\r
2368                                 itemsToCheckout.TrimRight('*');\r
2369                                 CString sCmd;\r
2370                                 sCmd.Format(_T("\"%s\" /command:checkout /url:\"%s\" /revision:%s"), \r
2371                                         (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), (LPCTSTR)itemsToCheckout, (LPCTSTR)GetRevision().ToString());\r
2372 \r
2373                                 CAppUtils::LaunchApplication(sCmd, NULL, false);\r
2374                         }\r
2375                         break;\r
2376                 case ID_EXPORT:\r
2377                         {\r
2378                                 CExportDlg dlg;\r
2379                                 dlg.m_URL = EscapeUrl(urlList[0]);\r
2380                                 dlg.Revision = GetRevision();\r
2381                                 if (dlg.DoModal()==IDOK)\r
2382                                 {\r
2383                                         CTSVNPath exportDirectory;\r
2384                                         exportDirectory.SetFromWin(dlg.m_strExportDirectory, true);\r
2385 \r
2386                                         CSVNProgressDlg progDlg;\r
2387                                         int opts = 0;\r
2388                                         if (dlg.m_bNoExternals)\r
2389                                                 opts |= ProgOptIgnoreExternals;\r
2390                                         if (dlg.m_eolStyle.CompareNoCase(_T("CRLF"))==0)\r
2391                                                 opts |= ProgOptEolCRLF;\r
2392                                         if (dlg.m_eolStyle.CompareNoCase(_T("CR"))==0)\r
2393                                                 opts |= ProgOptEolCR;\r
2394                                         if (dlg.m_eolStyle.CompareNoCase(_T("LF"))==0)\r
2395                                                 opts |= ProgOptEolLF;\r
2396                                         progDlg.SetCommand(CSVNProgressDlg::SVNProgress_Export);\r
2397                                         progDlg.SetOptions(opts);\r
2398                                         progDlg.SetPathList(CTSVNPathList(exportDirectory));\r
2399                                         progDlg.SetUrl(dlg.m_URL);\r
2400                                         progDlg.SetRevision(dlg.Revision);\r
2401                                         progDlg.SetDepth(dlg.m_depth);\r
2402                                         progDlg.DoModal();\r
2403                                 }\r
2404                         }\r
2405                         break;\r
2406                 case ID_REVGRAPH:\r
2407                         {\r
2408                                 CRevisionGraphDlg dlg;\r
2409                                 dlg.SetPath(EscapeUrl(urlList[0]));\r
2410                 dlg.SetPegRevision(GetRevision());\r
2411                                 dlg.DoModal();\r
2412                         }\r
2413                         break;\r
2414                 case ID_OPENWITH:\r
2415                         bOpenWith = true;\r
2416                 case ID_OPEN:\r
2417                         {\r
2418                                 // if we're on HEAD and the repository is available via http or https,\r
2419                                 // we just open the browser with that url.\r
2420                                 if (GetRevision().IsHead() && (bOpenWith==false))\r
2421                                 {\r
2422                                         if (urlList[0].GetSVNPathString().Left(4).CompareNoCase(_T("http")) == 0)\r
2423                                         {\r
2424                                                 CString sBrowserUrl = EscapeUrl(urlList[0]);\r
2425 \r
2426                                                 ShellExecute(NULL, _T("open"), sBrowserUrl, NULL, NULL, SW_SHOWNORMAL);\r
2427                                                 break;\r
2428                                         }\r
2429                                 }\r
2430                                 // in all other cases, we have to 'cat' the file and open it.\r
2431                                 CTSVNPath tempfile = CTempFiles::Instance().GetTempFilePath(false, urlList[0], GetRevision());\r
2432                                 CWaitCursorEx wait_cursor;\r
2433                                 CProgressDlg progDlg;\r
2434                                 progDlg.SetTitle(IDS_APPNAME);\r
2435                                 progDlg.SetAnimation(IDR_DOWNLOAD);\r
2436                                 CString sInfoLine;\r
2437                                 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, (LPCTSTR)urlList[0].GetFileOrDirectoryName(), (LPCTSTR)GetRevision().ToString());\r
2438                                 progDlg.SetLine(1, sInfoLine, true);\r
2439                                 SetAndClearProgressInfo(&progDlg);\r
2440                                 progDlg.ShowModeless(m_hWnd);\r
2441                                 if (!Cat(urlList[0], GetRevision(), GetRevision(), tempfile))\r
2442                                 {\r
2443                                         progDlg.Stop();\r
2444                                         SetAndClearProgressInfo((HWND)NULL);\r
2445                                         wait_cursor.Hide();\r
2446                                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2447                                         break;;\r
2448                                 }\r
2449                                 progDlg.Stop();\r
2450                                 SetAndClearProgressInfo((HWND)NULL);\r
2451                                 // set the file as read-only to tell the app which opens the file that it's only\r
2452                                 // a temporary file and must not be edited.\r
2453                                 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);\r
2454                                 if (!bOpenWith)\r
2455                                 {\r
2456                                         int ret = (int)ShellExecute(NULL, _T("open"), tempfile.GetWinPathString(), NULL, NULL, SW_SHOWNORMAL);\r
2457                                         if (ret <= HINSTANCE_ERROR)\r
2458                                                 bOpenWith = true;\r
2459                                 }\r
2460                                 else\r
2461                                 {\r
2462                                         CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");\r
2463                                         cmd += tempfile.GetWinPathString() + _T(" ");\r
2464                                         CAppUtils::LaunchApplication(cmd, NULL, false);\r
2465                                 }\r
2466                         }\r
2467                         break;\r
2468                 case ID_DELETE:\r
2469                         {\r
2470                                 CWaitCursorEx wait_cursor;\r
2471                                 CInputLogDlg input(this);\r
2472                                 input.SetUUID(m_sUUID);\r
2473                                 input.SetProjectProperties(&m_ProjectProperties);\r
2474                                 CString hint;\r
2475                                 if (urlList.GetCount() == 1)\r
2476                                         hint.Format(IDS_INPUT_REMOVEONE, (LPCTSTR)urlList[0].GetFileOrDirectoryName());\r
2477                                 else\r
2478                                         hint.Format(IDS_INPUT_REMOVEMORE, urlList.GetCount());\r
2479                                 input.SetActionText(hint);\r
2480                                 if (input.DoModal() == IDOK)\r
2481                                 {\r
2482                                         if (!Remove(urlList, true, false, input.GetLogMessage()))\r
2483                                         {\r
2484                                                 wait_cursor.Hide();\r
2485                                                 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2486                                                 return;\r
2487                                         }\r
2488                                         if (hChosenTreeItem)\r
2489                                         {\r
2490                                                 HTREEITEM hParent = m_RepoTree.GetParentItem(hChosenTreeItem);\r
2491                                                 RecursiveRemove(hChosenTreeItem);\r
2492                                                 RefreshNode(hParent);\r
2493                                         }\r
2494                                         else\r
2495                                                 RefreshNode(m_RepoTree.GetSelectedItem(), true);\r
2496                                 }\r
2497                         }\r
2498                         break;\r
2499                 case ID_BREAKLOCK:\r
2500                         {\r
2501                                 if (!Unlock(urlListEscaped, TRUE))\r
2502                                 {\r
2503                                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2504                                         return;\r
2505                                 }\r
2506                                 RefreshNode(m_RepoTree.GetSelectedItem(), true);\r
2507                         }\r
2508                         break;\r
2509                 case ID_IMPORTFOLDER:\r
2510                         {\r
2511                                 CString path;\r
2512                                 CBrowseFolder folderBrowser;\r
2513                                 folderBrowser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;\r
2514                                 if (folderBrowser.Show(GetSafeHwnd(), path)==CBrowseFolder::OK)\r
2515                                 {\r
2516                                         CTSVNPath svnPath(path);\r
2517                                         CWaitCursorEx wait_cursor;\r
2518                                         CString filename = svnPath.GetFileOrDirectoryName();\r
2519                                         CInputLogDlg input(this);\r
2520                                         input.SetUUID(m_sUUID);\r
2521                                         input.SetProjectProperties(&m_ProjectProperties);\r
2522                                         CString sHint;\r
2523                                         sHint.Format(IDS_INPUT_IMPORTFOLDER, (LPCTSTR)svnPath.GetSVNPathString(), (LPCTSTR)(urlList[0].GetSVNPathString()+_T("/")+filename));\r
2524                                         input.SetActionText(sHint);\r
2525                                         if (input.DoModal() == IDOK)\r
2526                                         {\r
2527                                                 CProgressDlg progDlg;\r
2528                                                 progDlg.SetTitle(IDS_APPNAME);\r
2529                                                 CString sInfoLine;\r
2530                                                 sInfoLine.Format(IDS_PROGRESSIMPORT, (LPCTSTR)filename);\r
2531                                                 progDlg.SetLine(1, sInfoLine, true);\r
2532                                                 SetAndClearProgressInfo(&progDlg);\r
2533                                                 progDlg.ShowModeless(m_hWnd);\r
2534                                                 if (!Import(svnPath, \r
2535                                                         CTSVNPath(EscapeUrl(CTSVNPath(urlList[0].GetSVNPathString()+_T("/")+filename))), \r
2536                                                         input.GetLogMessage(), \r
2537                                                         &m_ProjectProperties, \r
2538                                                         svn_depth_infinity, \r
2539                                                         FALSE, FALSE))\r
2540                                                 {\r
2541                                                         progDlg.Stop();\r
2542                                                         SetAndClearProgressInfo((HWND)NULL);\r
2543                                                         wait_cursor.Hide();\r
2544                                                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2545                                                         return;\r
2546                                                 }\r
2547                                                 progDlg.Stop();\r
2548                                                 SetAndClearProgressInfo((HWND)NULL);\r
2549                                                 RefreshNode(m_RepoTree.GetSelectedItem(), true);\r
2550                                         }\r
2551                                 }\r
2552                         }\r
2553                         break;\r
2554                 case ID_IMPORT:\r
2555                         {\r
2556                                 // Display the Open dialog box. \r
2557                                 CString openPath;\r
2558                                 if (CAppUtils::FileOpenSave(openPath, NULL, IDS_REPOBROWSE_IMPORT, IDS_COMMONFILEFILTER, true, m_hWnd))\r
2559                                 {\r
2560                                         CTSVNPath path(openPath);\r
2561                                         CWaitCursorEx wait_cursor;\r
2562                                         CString filename = path.GetFileOrDirectoryName();\r
2563                                         CInputLogDlg input(this);\r
2564                                         input.SetUUID(m_sUUID);\r
2565                                         input.SetProjectProperties(&m_ProjectProperties);\r
2566                                         CString sHint;\r
2567                                         sHint.Format(IDS_INPUT_IMPORTFILEFULL, path.GetWinPath(), (LPCTSTR)(urlList[0].GetSVNPathString()+_T("/")+filename));\r
2568                                         input.SetActionText(sHint);\r
2569                                         if (input.DoModal() == IDOK)\r
2570                                         {\r
2571                                                 CProgressDlg progDlg;\r
2572                                                 progDlg.SetTitle(IDS_APPNAME);\r
2573                                                 CString sInfoLine;\r
2574                                                 sInfoLine.Format(IDS_PROGRESSIMPORT, (LPCTSTR)filename);\r
2575                                                 progDlg.SetLine(1, sInfoLine, true);\r
2576                                                 SetAndClearProgressInfo(&progDlg);\r
2577                                                 progDlg.ShowModeless(m_hWnd);\r
2578                                                 if (!Import(path, \r
2579                                                         CTSVNPath(EscapeUrl(CTSVNPath(urlList[0].GetSVNPathString()+_T("/")+filename))), \r
2580                                                         input.GetLogMessage(), \r
2581                                                         &m_ProjectProperties,\r
2582                                                         svn_depth_empty, \r
2583                                                         TRUE, FALSE))\r
2584                                                 {\r
2585                                                         progDlg.Stop();\r
2586                                                         SetAndClearProgressInfo((HWND)NULL);\r
2587                                                         wait_cursor.Hide();\r
2588                                                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2589                                                         return;\r
2590                                                 }\r
2591                                                 progDlg.Stop();\r
2592                                                 SetAndClearProgressInfo((HWND)NULL);\r
2593                                                 RefreshNode(m_RepoTree.GetSelectedItem(), true);\r
2594                                         }\r
2595                                 }\r
2596                         }\r
2597                         break;\r
2598                 case ID_RENAME:\r
2599                         {\r
2600                                 if (pWnd == &m_RepoList)\r
2601                                 {\r
2602                                         POSITION pos = m_RepoList.GetFirstSelectedItemPosition();\r
2603                                         int selIndex = m_RepoList.GetNextSelectedItem(pos);\r
2604                                         if (selIndex >= 0)\r
2605                                         {\r
2606                                                 m_RepoList.SetFocus();\r
2607                                                 m_RepoList.EditLabel(selIndex);\r
2608                                         }\r
2609                                         else\r
2610                                         {\r
2611                                                 m_RepoTree.SetFocus();\r
2612                                                 HTREEITEM hTreeItem = m_RepoTree.GetSelectedItem();\r
2613                                                 if (hTreeItem != m_RepoTree.GetRootItem())\r
2614                                                         m_RepoTree.EditLabel(hTreeItem);\r
2615                                         }\r
2616                                 }\r
2617                                 else if (pWnd == &m_RepoTree)\r
2618                                 {\r
2619                                         m_RepoTree.SetFocus();\r
2620                                         if (hChosenTreeItem != m_RepoTree.GetRootItem())\r
2621                                                 m_RepoTree.EditLabel(hChosenTreeItem);\r
2622                                 }\r
2623                         }\r
2624                         break;\r
2625                 case ID_COPYTO:\r
2626                         {\r
2627                                 CRenameDlg dlg;\r
2628                                 dlg.m_name = urlList[0].GetSVNPathString();\r
2629                                 dlg.m_windowtitle.LoadString(IDS_REPOBROWSE_COPY);\r
2630                                 CStringUtils::RemoveAccelerators(dlg.m_windowtitle);\r
2631                                 if (dlg.DoModal() == IDOK)\r
2632                                 {\r
2633                                         CWaitCursorEx wait_cursor;\r
2634                                         CInputLogDlg input(this);\r
2635                                         input.SetUUID(m_sUUID);\r
2636                                         input.SetProjectProperties(&m_ProjectProperties);\r
2637                                         CString sHint;\r
2638                                         sHint.Format(IDS_INPUT_COPY, (LPCTSTR)urlList[0].GetSVNPathString(), (LPCTSTR)dlg.m_name);\r
2639                                         input.SetActionText(sHint);\r
2640                                         if (!CTSVNPath(dlg.m_name).IsValidOnWindows())\r
2641                                         {\r
2642                                                 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH, IDS_APPNAME, MB_ICONINFORMATION|MB_YESNO) != IDYES)\r
2643                                                         break;\r
2644                                         }\r
2645                                         if (input.DoModal() == IDOK)\r
2646                                         {\r
2647                                                 if (!Copy(urlList, CTSVNPath(dlg.m_name), GetRevision(), GetRevision(), input.GetLogMessage()))\r
2648                                                 {\r
2649                                                         wait_cursor.Hide();\r
2650                                                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2651                                                         return;\r
2652                                                 }\r
2653                                                 if (GetRevision().IsHead())\r
2654                                                 {\r
2655                                                         RefreshNode(m_RepoTree.GetSelectedItem(), true);\r
2656                                                 }\r
2657                                         }\r
2658                                 }\r
2659                         }\r
2660                         break;\r
2661                 case ID_COPYTOWC:\r
2662                         {\r
2663                                 CTSVNPath tempfile;\r
2664                                 bool bSavePathOK = AskForSavePath(urlList, tempfile, nFolders > 0);\r
2665                                 if (bSavePathOK)\r
2666                                 {\r
2667                                         CWaitCursorEx wait_cursor;\r
2668 \r
2669                                         CProgressDlg progDlg;\r
2670                                         progDlg.SetAnimation(IDR_DOWNLOAD);\r
2671                                         progDlg.SetTitle(IDS_APPNAME);\r
2672                                         SetAndClearProgressInfo(&progDlg);\r
2673                                         progDlg.ShowModeless(m_hWnd);\r
2674 \r
2675                                         bool bCopyAsChild = (urlList.GetCount() > 1);\r
2676                                         if (!Copy(urlList, tempfile, GetRevision(), GetRevision(), CString(), bCopyAsChild)||(progDlg.HasUserCancelled()))\r
2677                                         {\r
2678                                                 progDlg.Stop();\r
2679                                                 SetAndClearProgressInfo((HWND)NULL);\r
2680                                                 wait_cursor.Hide();\r
2681                                                 progDlg.Stop();\r
2682                                                 if (!progDlg.HasUserCancelled())\r
2683                                                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2684                                                 return;\r
2685                                         }\r
2686                                         progDlg.Stop();\r
2687                                         SetAndClearProgressInfo((HWND)NULL);\r
2688                                 }\r
2689                         }\r
2690                         break;\r
2691                 case ID_MKDIR:\r
2692                         {\r
2693                                 CRenameDlg dlg;\r
2694                                 dlg.m_name = _T("");\r
2695                                 dlg.m_windowtitle.LoadString(IDS_REPOBROWSE_MKDIR);\r
2696                                 CStringUtils::RemoveAccelerators(dlg.m_windowtitle);\r
2697                                 if (dlg.DoModal() == IDOK)\r
2698                                 {\r
2699                                         CWaitCursorEx wait_cursor;\r
2700                                         CInputLogDlg input(this);\r
2701                                         input.SetUUID(m_sUUID);\r
2702                                         input.SetProjectProperties(&m_ProjectProperties);\r
2703                                         CString sHint;\r
2704                                         sHint.Format(IDS_INPUT_MKDIR, (LPCTSTR)(urlList[0].GetSVNPathString()+_T("/")+dlg.m_name.Trim()));\r
2705                                         input.SetActionText(sHint);\r
2706                                         if (input.DoModal() == IDOK)\r
2707                                         {\r
2708                                                 // when creating the new folder, also trim any whitespace chars from it\r
2709                                                 if (!MakeDir(CTSVNPathList(CTSVNPath(EscapeUrl(CTSVNPath(urlList[0].GetSVNPathString()+_T("/")+dlg.m_name.Trim())))), input.GetLogMessage(), true))\r
2710                                                 {\r
2711                                                         wait_cursor.Hide();\r
2712                                                         CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2713                                                         return;\r
2714                                                 }\r
2715                                                 RefreshNode(m_RepoTree.GetSelectedItem(), true);\r
2716                                         }\r
2717                                 }\r
2718                         }\r
2719                         break;\r
2720                 case ID_REFRESH:\r
2721                         {\r
2722                                 RefreshNode(urlList[0].GetSVNPathString(), true);\r
2723                         }\r
2724                         break;\r
2725                 case ID_GNUDIFF:\r
2726                         {\r
2727                                 m_bCancelled = false;\r
2728                                 SVNDiff diff(this, this->m_hWnd, true);\r
2729                                 if (urlList.GetCount() == 1)\r
2730                                 {\r
2731                                         if (PromptShown())\r
2732                                                 diff.ShowUnifiedDiff(CTSVNPath(EscapeUrl(urlList[0])), GetRevision(), \r
2733                                                                                         CTSVNPath(EscapeUrl(m_diffURL)), GetRevision());\r
2734                                         else\r
2735                                                 CAppUtils::StartShowUnifiedDiff(m_hWnd, CTSVNPath(EscapeUrl(urlList[0])), GetRevision(), \r
2736                                                                                         CTSVNPath(EscapeUrl(m_diffURL)), GetRevision());\r
2737                                 }\r
2738                                 else\r
2739                                 {\r
2740                                         if (PromptShown())\r
2741                                                 diff.ShowUnifiedDiff(CTSVNPath(EscapeUrl(urlList[0])), GetRevision(), \r
2742                                                                                         CTSVNPath(EscapeUrl(urlList[1])), GetRevision());\r
2743                                         else\r
2744                                                 CAppUtils::StartShowUnifiedDiff(m_hWnd, CTSVNPath(EscapeUrl(urlList[0])), GetRevision(), \r
2745                                                                                         CTSVNPath(EscapeUrl(urlList[1])), GetRevision());\r
2746                                 }\r
2747                         }\r
2748                         break;\r
2749                 case ID_DIFF:\r
2750                         {\r
2751                                 m_bCancelled = false;\r
2752                                 SVNDiff diff(this, this->m_hWnd, true);\r
2753                                 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
2754                                 if (urlList.GetCount() == 1)\r
2755                                 {\r
2756                                         if (PromptShown())\r
2757                                                 diff.ShowCompare(CTSVNPath(EscapeUrl(urlList[0])), GetRevision(), \r
2758                                                                                 CTSVNPath(EscapeUrl(m_diffURL)), GetRevision(), SVNRev(), true);\r
2759                                         else\r
2760                                                 CAppUtils::StartShowCompare(m_hWnd, CTSVNPath(EscapeUrl(urlList[0])), GetRevision(), \r
2761                                                                                 CTSVNPath(EscapeUrl(m_diffURL)), GetRevision(), SVNRev(), SVNRev(), \r
2762                                                                                 !!(GetAsyncKeyState(VK_SHIFT) & 0x8000), true);\r
2763                                 }\r
2764                                 else\r
2765                                 {\r
2766                                         if (PromptShown())\r
2767                                                 diff.ShowCompare(CTSVNPath(EscapeUrl(urlList[0])), GetRevision(), \r
2768                                                                                 CTSVNPath(EscapeUrl(urlList[1])), GetRevision(), SVNRev(), true);\r
2769                                         else\r
2770                                                 CAppUtils::StartShowCompare(m_hWnd, CTSVNPath(EscapeUrl(urlList[0])), GetRevision(), \r
2771                                                                                 CTSVNPath(EscapeUrl(urlList[1])), GetRevision(), SVNRev(), SVNRev(), \r
2772                                                                                 !!(GetAsyncKeyState(VK_SHIFT) & 0x8000), true);\r
2773                                 }\r
2774                         }\r
2775                         break;\r
2776                 case ID_PROPS:\r
2777                         {\r
2778                                 if (GetRevision().IsHead())\r
2779                                 {\r
2780                                         CEditPropertiesDlg dlg;\r
2781                                         dlg.SetProjectProperties(&m_ProjectProperties);\r
2782                                         dlg.SetUUID(m_sUUID);\r
2783                                         CTSVNPathList escapedlist;\r
2784                                         for (int i=0; i<urlList.GetCount(); ++i)\r
2785                                         {\r
2786                                                 escapedlist.AddPath(CTSVNPath(EscapeUrl(urlList[i])));\r
2787                                         }\r
2788                                         dlg.SetPathList(escapedlist);\r
2789                                         dlg.SetRevision(GetHEADRevision(urlList[0]));\r
2790                                         dlg.DoModal();\r
2791                                 }\r
2792                                 else\r
2793                                 {\r
2794                                         CPropDlg dlg;\r
2795                                         dlg.m_rev = GetRevision();\r
2796                                         dlg.m_Path = CTSVNPath(EscapeUrl(urlList[0]));\r
2797                                         dlg.DoModal();\r
2798                                 }\r
2799                         }\r
2800                         break;\r
2801                 case ID_REVPROPS:\r
2802                         {\r
2803                                 CEditPropertiesDlg dlg;\r
2804                                 dlg.SetProjectProperties(&m_ProjectProperties);\r
2805                                 dlg.SetUUID(m_sUUID);\r
2806                                 CTSVNPathList escapedlist;\r
2807                                 for (int i=0; i<urlList.GetCount(); ++i)\r
2808                                 {\r
2809                                         escapedlist.AddPath(CTSVNPath(EscapeUrl(urlList[i])));\r
2810                                 }\r
2811                                 dlg.SetPathList(escapedlist);\r
2812                                 dlg.SetRevision(GetRevision());\r
2813                                 dlg.RevProps(true);\r
2814                                 dlg.DoModal();\r
2815                         }\r
2816                         break;\r
2817                 case ID_BLAME:\r
2818                         {\r
2819                                 CBlameDlg dlg;\r
2820                                 dlg.EndRev = GetRevision();\r
2821                                 if (dlg.DoModal() == IDOK)\r
2822                                 {\r
2823                                         CBlame blame;\r
2824                                         CString tempfile;\r
2825                                         CString logfile;\r
2826                                         tempfile = blame.BlameToTempFile(CTSVNPath(EscapeUrl(urlList[0])), dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, SVN::GetOptionsString(dlg.m_bIgnoreEOL, dlg.m_IgnoreSpaces), dlg.m_bIncludeMerge, TRUE, TRUE);\r
2827                                         if (!tempfile.IsEmpty())\r
2828                                         {\r
2829                                                 if (dlg.m_bTextView)\r
2830                                                 {\r
2831                                                         //open the default text editor for the result file\r
2832                                                         CAppUtils::StartTextViewer(tempfile);\r
2833                                                 }\r
2834                                                 else\r
2835                                                 {\r
2836                                                         CString sParams = _T("/path:\"") + urlList[0].GetSVNPathString() + _T("\" ");\r
2837                                                         if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(urlList[0].GetFileOrDirectoryName()),sParams))\r
2838                                                         {\r
2839                                                                 break;\r
2840                                                         }\r
2841                                                 }\r
2842                                         }\r
2843                                         else\r
2844                                         {\r
2845                                                 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);\r
2846                                         }\r
2847                                 }\r
2848 \r
2849                         }\r
2850                 default:\r
2851                         break;\r
2852                 }\r
2853                 DialogEnableWindow(IDOK, TRUE);\r
2854         }\r
2855 }\r
2856 \r
2857 \r
2858 bool CRepositoryBrowser::AskForSavePath(const CTSVNPathList& urlList, CTSVNPath &tempfile, bool bFolder)\r
2859 {\r
2860         bool bSavePathOK = false;\r
2861         if ((!bFolder)&&(urlList.GetCount() == 1))\r
2862         {\r
2863                 CString savePath = urlList[0].GetFilename();\r
2864                 bSavePathOK = CAppUtils::FileOpenSave(savePath, NULL, IDS_REPOBROWSE_SAVEAS, IDS_COMMONFILEFILTER, false, m_hWnd);\r
2865                 if (bSavePathOK)\r
2866                         tempfile.SetFromWin(savePath);\r
2867         }\r
2868         else\r
2869         {\r
2870                 CBrowseFolder browser;\r
2871                 CString sTempfile;\r
2872                 browser.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;\r
2873                 browser.Show(GetSafeHwnd(), sTempfile);\r
2874                 if (!sTempfile.IsEmpty())\r
2875                 {\r
2876                         bSavePathOK = true;\r
2877                         tempfile.SetFromWin(sTempfile);\r
2878                 }\r
2879         }\r
2880         return bSavePathOK;\r
2881 }\r
2882 \r
2883 bool CRepositoryBrowser::StringToWidthArray(const CString& WidthString, int WidthArray[])\r
2884 {\r
2885         TCHAR * endchar;\r
2886         for (int i=0; i<7; ++i)\r
2887         {\r
2888                 CString hex = WidthString.Mid(i*8, 8);\r
2889                 if ( hex.IsEmpty() )\r
2890                 {\r
2891                         // This case only occurs when upgrading from an older\r
2892                         // TSVN version in which there were fewer columns.\r
2893                         WidthArray[i] = 0;\r
2894                 }\r
2895                 else\r
2896                 {\r
2897                         WidthArray[i] = _tcstol(hex, &endchar, 16);\r
2898                 }\r
2899         }\r
2900         return true;\r
2901 }\r
2902 \r
2903 CString CRepositoryBrowser::WidthArrayToString(int WidthArray[])\r
2904 {\r
2905         CString sResult;\r
2906         TCHAR buf[10];\r
2907         for (int i=0; i<7; ++i)\r
2908         {\r
2909                 _stprintf_s(buf, 10, _T("%08X"), WidthArray[i]);\r
2910                 sResult += buf;\r
2911         }\r
2912         return sResult;\r
2913 }\r
2914 \r
2915 void CRepositoryBrowser::SaveColumnWidths(bool bSaveToRegistry /* = false */)\r
2916 {\r
2917         CRegString regColWidth(_T("Software\\TortoiseGit\\RepoBrowserColumnWidth"));\r
2918         int maxcol = ((CHeaderCtrl*)(m_RepoList.GetDlgItem(0)))->GetItemCount()-1;\r
2919         // first clear the width array\r
2920         for (int col = 0; col < 7; ++col)\r
2921                 m_arColumnWidths[col] = 0;\r
2922         for (int col = 0; col <= maxcol; ++col)\r
2923         {\r
2924                 m_arColumnWidths[col] = m_RepoList.GetColumnWidth(col);\r
2925                 if (m_arColumnWidths[col] == m_arColumnAutoWidths[col])\r
2926                         m_arColumnWidths[col] = 0;\r
2927         }\r
2928         if (bSaveToRegistry)\r
2929         {\r
2930                 CString sWidths = WidthArrayToString(m_arColumnWidths);\r
2931                 regColWidth = sWidths;\r
2932         }\r
2933 }\r
2934 \r
2935 \r