1 // TortoiseSVN - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "TortoiseProc.h"
22 #include "MessageBox.h"
23 #include "InputLogDlg.h"
26 #include "EditPropertiesDlg.h"
29 #include "WaitCursorEx.h"
30 #include "Repositorybrowser.h"
31 #include "BrowseFolder.h"
32 #include "RenameDlg.h"
33 #include "RevisionGraph\RevisionGraphDlg.h"
34 #include "CheckoutDlg.h"
35 #include "ExportDlg.h"
36 #include "SVNProgressDlg.h"
38 #include "PathUtils.h"
39 #include "StringUtils.h"
41 #include "UnicodeUtils.h"
42 #include "BrowseFolder.h"
44 #include "SysImageList.h"
45 #include "RepoDrags.h"
47 #include "SVNDataObject.h"
48 #include "SVNLogHelper.h"
53 enum RepoBrowserContextMenuCommands
55 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
85 IMPLEMENT_DYNAMIC(CRepositoryBrowser
, CResizableStandAloneDialog
)
87 CRepositoryBrowser::CRepositoryBrowser(const CString
& url
, const SVNRev
& rev
)
88 : CResizableStandAloneDialog(CRepositoryBrowser::IDD
, NULL
)
89 , m_cnrRepositoryBar(&m_barRepository
)
94 , m_blockEvents(false)
95 , m_bSortAscending(true)
97 , m_pTreeDropTarget(NULL
)
98 , m_pListDropTarget(NULL
)
100 , m_diffKind(svn_node_none
)
106 CRepositoryBrowser::CRepositoryBrowser(const CString
& url
, const SVNRev
& rev
, CWnd
* pParent
)
107 : CResizableStandAloneDialog(CRepositoryBrowser::IDD
, pParent
)
108 , m_cnrRepositoryBar(&m_barRepository
)
111 , m_bStandAlone(false)
113 , m_blockEvents(false)
114 , m_bSortAscending(true)
116 , m_pTreeDropTarget(NULL
)
117 , m_pListDropTarget(NULL
)
118 , m_bCancelled(false)
119 , m_diffKind(svn_node_none
)
123 CRepositoryBrowser::~CRepositoryBrowser()
127 void CRepositoryBrowser::RecursiveRemove(HTREEITEM hItem
, bool bChildrenOnly
/* = false */)
130 if (m_RepoTree
.ItemHasChildren(hItem
))
132 for (childItem
= m_RepoTree
.GetChildItem(hItem
);childItem
!= NULL
; childItem
= m_RepoTree
.GetNextItem(childItem
, TVGN_NEXT
))
134 RecursiveRemove(childItem
);
137 CTreeItem
* pTreeItem
= (CTreeItem
*)m_RepoTree
.GetItemData(childItem
);
139 m_RepoTree
.SetItemData(childItem
, 0);
140 m_RepoTree
.DeleteItem(childItem
);
145 if ((hItem
)&&(!bChildrenOnly
))
147 CTreeItem
* pTreeItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hItem
);
149 m_RepoTree
.SetItemData(hItem
, 0);
153 void CRepositoryBrowser::DoDataExchange(CDataExchange
* pDX
)
155 CResizableStandAloneDialog::DoDataExchange(pDX
);
156 DDX_Control(pDX
, IDC_REPOTREE
, m_RepoTree
);
157 DDX_Control(pDX
, IDC_REPOLIST
, m_RepoList
);
160 BEGIN_MESSAGE_MAP(CRepositoryBrowser
, CResizableStandAloneDialog
)
161 ON_BN_CLICKED(IDHELP
, OnBnClickedHelp
)
163 ON_REGISTERED_MESSAGE(WM_AFTERINIT
, OnAfterInitDialog
)
167 ON_NOTIFY(TVN_SELCHANGED
, IDC_REPOTREE
, &CRepositoryBrowser::OnTvnSelchangedRepotree
)
168 ON_NOTIFY(TVN_ITEMEXPANDING
, IDC_REPOTREE
, &CRepositoryBrowser::OnTvnItemexpandingRepotree
)
169 ON_NOTIFY(NM_DBLCLK
, IDC_REPOLIST
, &CRepositoryBrowser::OnNMDblclkRepolist
)
170 ON_NOTIFY(HDN_ITEMCLICK
, 0, &CRepositoryBrowser::OnHdnItemclickRepolist
)
171 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_REPOLIST
, &CRepositoryBrowser::OnLvnItemchangedRepolist
)
172 ON_NOTIFY(LVN_BEGINDRAG
, IDC_REPOLIST
, &CRepositoryBrowser::OnLvnBegindragRepolist
)
173 ON_NOTIFY(LVN_BEGINRDRAG
, IDC_REPOLIST
, &CRepositoryBrowser::OnLvnBeginrdragRepolist
)
175 ON_NOTIFY(LVN_ENDLABELEDIT
, IDC_REPOLIST
, &CRepositoryBrowser::OnLvnEndlabeleditRepolist
)
176 ON_NOTIFY(TVN_ENDLABELEDIT
, IDC_REPOTREE
, &CRepositoryBrowser::OnTvnEndlabeleditRepotree
)
178 ON_COMMAND(ID_URL_FOCUS
, &CRepositoryBrowser::OnUrlFocus
)
179 ON_COMMAND(ID_EDIT_COPY
, &CRepositoryBrowser::OnCopy
)
180 ON_COMMAND(ID_INLINEEDIT
, &CRepositoryBrowser::OnInlineedit
)
181 ON_COMMAND(ID_REFRESHBROWSER
, &CRepositoryBrowser::OnRefresh
)
182 ON_COMMAND(ID_DELETEBROWSERITEM
, &CRepositoryBrowser::OnDelete
)
183 ON_COMMAND(ID_URL_UP
, &CRepositoryBrowser::OnGoUp
)
184 ON_NOTIFY(TVN_BEGINDRAG
, IDC_REPOTREE
, &CRepositoryBrowser::OnTvnBegindragRepotree
)
185 ON_NOTIFY(TVN_BEGINRDRAG
, IDC_REPOTREE
, &CRepositoryBrowser::OnTvnBeginrdragRepotree
)
188 SVNRev
CRepositoryBrowser::GetRevision() const
190 return m_barRepository
.GetCurrentRev();
193 CString
CRepositoryBrowser::GetPath() const
195 return m_barRepository
.GetCurrentUrl();
198 BOOL
CRepositoryBrowser::OnInitDialog()
200 CResizableStandAloneDialog::OnInitDialog();
202 GetWindowText(m_origDlgTitle
);
204 m_hAccel
= LoadAccelerators(AfxGetResourceHandle(),MAKEINTRESOURCE(IDR_ACC_REPOBROWSER
));
206 m_cnrRepositoryBar
.SubclassDlgItem(IDC_REPOS_BAR_CNR
, this);
207 m_barRepository
.Create(&m_cnrRepositoryBar
, 12345);
208 m_barRepository
.SetIRepo(this);
210 m_pTreeDropTarget
= new CTreeDropTarget(this);
211 RegisterDragDrop(m_RepoTree
.GetSafeHwnd(), m_pTreeDropTarget
);
212 // create the supported formats:
214 ftetc
.cfFormat
= CF_SVNURL
;
215 ftetc
.dwAspect
= DVASPECT_CONTENT
;
217 ftetc
.tymed
= TYMED_HGLOBAL
;
218 m_pTreeDropTarget
->AddSuportedFormat(ftetc
);
219 ftetc
.cfFormat
=CF_HDROP
;
220 m_pTreeDropTarget
->AddSuportedFormat(ftetc
);
222 m_pListDropTarget
= new CListDropTarget(this);
223 RegisterDragDrop(m_RepoList
.GetSafeHwnd(), m_pListDropTarget
);
224 // create the supported formats:
225 ftetc
.cfFormat
= CF_SVNURL
;
226 m_pListDropTarget
->AddSuportedFormat(ftetc
);
227 ftetc
.cfFormat
=CF_HDROP
;
228 m_pListDropTarget
->AddSuportedFormat(ftetc
);
232 GetDlgItem(IDCANCEL
)->ShowWindow(FALSE
);
234 // reposition the buttons
236 GetDlgItem(IDCANCEL
)->GetWindowRect(rect_cancel
);
237 ScreenToClient(rect_cancel
);
238 GetDlgItem(IDOK
)->MoveWindow(rect_cancel
);
241 m_nIconFolder
= SYS_IMAGE_LIST().GetDirIconIndex();
242 m_nOpenIconFolder
= SYS_IMAGE_LIST().GetDirOpenIconIndex();
243 // set up the list control
244 // set the extended style of the list control
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.
246 CRegDWORD
regFullRowSelect(_T("Software\\TortoiseGit\\FullRowSelect"), TRUE
);
247 DWORD exStyle
= LVS_EX_HEADERDRAGDROP
| LVS_EX_DOUBLEBUFFER
| LVS_EX_INFOTIP
| LVS_EX_SUBITEMIMAGES
;
248 if (DWORD(regFullRowSelect
))
249 exStyle
|= LVS_EX_FULLROWSELECT
;
250 m_RepoList
.SetExtendedStyle(exStyle
);
251 m_RepoList
.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL
);
252 m_RepoList
.ShowText(CString(MAKEINTRESOURCE(IDS_REPOBROWSE_INITWAIT
)));
254 m_RepoTree
.SetImageList(&SYS_IMAGE_LIST(), TVSIL_NORMAL
);
257 theme
.SetWindowTheme(m_RepoList
.GetSafeHwnd(), L
"Explorer", NULL
);
258 theme
.SetWindowTheme(m_RepoTree
.GetSafeHwnd(), L
"Explorer", NULL
);
261 AddAnchor(IDC_REPOS_BAR_CNR
, TOP_LEFT
, TOP_RIGHT
);
262 AddAnchor(IDC_F5HINT
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
263 AddAnchor(IDC_REPOTREE
, TOP_LEFT
, BOTTOM_LEFT
);
264 AddAnchor(IDC_REPOLIST
, TOP_LEFT
, BOTTOM_RIGHT
);
265 AddAnchor(IDCANCEL
, BOTTOM_RIGHT
);
266 AddAnchor(IDOK
, BOTTOM_RIGHT
);
267 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
268 EnableSaveRestore(_T("RepositoryBrowser"));
270 CenterWindow(CWnd::FromHandle(hWndExplorer
));
271 m_bThreadRunning
= true;
272 if (AfxBeginThread(InitThreadEntry
, this)==NULL
)
274 m_bThreadRunning
= false;
275 CMessageBox::Show(NULL
, IDS_ERR_THREADSTARTFAILED
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
280 void CRepositoryBrowser::InitRepo()
284 m_InitialUrl
= CPathUtils::PathUnescape(m_InitialUrl
);
285 if (m_InitialUrl
.Find('?')>=0)
287 m_initialRev
= SVNRev(m_InitialUrl
.Mid(m_InitialUrl
.Find('?')+1));
288 m_InitialUrl
= m_InitialUrl
.Left(m_InitialUrl
.Find('?'));
291 // We don't know if the url passed to us points to a file or a folder,
294 const SVNInfoData
* data
= NULL
;
295 CString error
; // contains the first error of GetFirstFileInfo()
298 data
= info
.GetFirstFileInfo(CTSVNPath(m_InitialUrl
),m_initialRev
, m_initialRev
);
299 if ((data
== NULL
)||(data
->kind
!= svn_node_dir
))
301 // in case the url is not a valid directory, try the parent dir
302 // until there's no more parent dir
303 m_InitialUrl
= m_InitialUrl
.Left(m_InitialUrl
.ReverseFind('/'));
304 if ((m_InitialUrl
.Compare(_T("http://")) == 0) ||
305 (m_InitialUrl
.Compare(_T("https://")) == 0)||
306 (m_InitialUrl
.Compare(_T("svn://")) == 0)||
307 (m_InitialUrl
.Compare(_T("svn+ssh://")) == 0)||
308 (m_InitialUrl
.Compare(_T("file:///")) == 0)||
309 (m_InitialUrl
.Compare(_T("file://")) == 0))
311 m_InitialUrl
.Empty();
315 if (((data
)&&(data
->kind
== svn_node_dir
))||(data
== NULL
))
316 error
= info
.GetLastErrorMsg();
319 } while(!m_InitialUrl
.IsEmpty() && ((data
== NULL
) || (data
->kind
!= svn_node_dir
)));
323 m_InitialUrl
.Empty();
324 m_RepoList
.ShowText(error
, true);
327 else if (m_initialRev
.IsHead())
329 m_barRepository
.SetHeadRevision(data
->rev
);
331 m_InitialUrl
.TrimRight('/');
333 m_bCancelled
= false;
334 m_strReposRoot
= data
->reposRoot
;
335 m_sUUID
= data
->reposUUID
;
336 m_strReposRoot
= CPathUtils::PathUnescape(m_strReposRoot
);
337 // the initial url can be in the format file:///\, but the
338 // repository root returned would still be file://
339 // to avoid string length comparison faults, we adjust
340 // the repository root here to match the initial url
341 if ((m_InitialUrl
.Left(9).CompareNoCase(_T("file:///\\")) == 0) &&
342 (m_strReposRoot
.Left(9).CompareNoCase(_T("file:///\\")) != 0))
343 m_strReposRoot
.Replace(_T("file://"), _T("file:///\\"));
344 SetWindowText(m_strReposRoot
+ _T(" - ") + m_origDlgTitle
);
345 // now check the repository root for the url type, then
346 // set the corresponding background image
347 if (!m_strReposRoot
.IsEmpty())
349 UINT nID
= IDI_REPO_UNKNOWN
;
350 if (m_strReposRoot
.Left(7).CompareNoCase(_T("http://"))==0)
352 if (m_strReposRoot
.Left(8).CompareNoCase(_T("https://"))==0)
353 nID
= IDI_REPO_HTTPS
;
354 if (m_strReposRoot
.Left(6).CompareNoCase(_T("svn://"))==0)
356 if (m_strReposRoot
.Left(10).CompareNoCase(_T("svn+ssh://"))==0)
357 nID
= IDI_REPO_SVNSSH
;
358 if (m_strReposRoot
.Left(7).CompareNoCase(_T("file://"))==0)
361 if (theme
.IsAppThemed())
362 CAppUtils::SetListCtrlBackgroundImage(m_RepoList
.GetSafeHwnd(), nID
);
366 UINT
CRepositoryBrowser::InitThreadEntry(LPVOID pVoid
)
368 return ((CRepositoryBrowser
*)pVoid
)->InitThread();
371 //this is the thread function which calls the subversion function
372 UINT
CRepositoryBrowser::InitThread()
374 // In this thread, we try to find out the repository root.
375 // Since this is a remote operation, it can take a while, that's
376 // Why we do this inside a thread.
378 // force the cursor to change
381 DialogEnableWindow(IDOK
, FALSE
);
382 DialogEnableWindow(IDCANCEL
, FALSE
);
386 PostMessage(WM_AFTERINIT
);
387 DialogEnableWindow(IDOK
, TRUE
);
388 DialogEnableWindow(IDCANCEL
, TRUE
);
390 m_bThreadRunning
= false;
396 LRESULT
CRepositoryBrowser::OnAfterInitDialog(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
398 if ((m_InitialUrl
.IsEmpty())||(m_strReposRoot
.IsEmpty()))
403 m_barRepository
.GotoUrl(m_InitialUrl
, m_initialRev
, true);
404 m_RepoList
.ClearText();
409 void CRepositoryBrowser::OnOK()
411 RevokeDragDrop(m_RepoList
.GetSafeHwnd());
412 RevokeDragDrop(m_RepoTree
.GetSafeHwnd());
414 SaveColumnWidths(true);
416 HTREEITEM hItem
= m_RepoTree
.GetRootItem();
417 RecursiveRemove(hItem
);
419 m_barRepository
.SaveHistory();
420 CResizableStandAloneDialog::OnOK();
423 void CRepositoryBrowser::OnCancel()
425 RevokeDragDrop(m_RepoList
.GetSafeHwnd());
426 RevokeDragDrop(m_RepoTree
.GetSafeHwnd());
428 SaveColumnWidths(true);
430 HTREEITEM hItem
= m_RepoTree
.GetRootItem();
431 RecursiveRemove(hItem
);
436 void CRepositoryBrowser::OnBnClickedHelp()
441 /******************************************************************************/
442 /* tree and list view resizing */
443 /******************************************************************************/
445 BOOL
CRepositoryBrowser::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
447 if (m_bThreadRunning
)
449 HCURSOR hCur
= LoadCursor(NULL
, MAKEINTRESOURCE(IDC_WAIT
));
457 GetClientRect(&rect
);
460 if (PtInRect(&rect
, pt
))
463 // are we right of the tree control?
464 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&rect
);
465 if ((pt
.x
> rect
.right
)&&
467 (pt
.y
<= rect
.bottom
))
469 // but left of the list control?
470 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&rect
);
471 if (pt
.x
< rect
.left
)
473 HCURSOR hCur
= LoadCursor(NULL
, MAKEINTRESOURCE(IDC_SIZEWE
));
480 return CStandAloneDialogTmpl
<CResizableDialog
>::OnSetCursor(pWnd
, nHitTest
, message
);
483 void CRepositoryBrowser::OnMouseMove(UINT nFlags
, CPoint point
)
486 RECT rect
, tree
, list
, treelist
, treelistclient
;
488 if (bDragMode
== FALSE
)
491 // create an union of the tree and list control rectangle
492 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&list
);
493 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&tree
);
494 UnionRect(&treelist
, &tree
, &list
);
495 treelistclient
= treelist
;
496 ScreenToClient(&treelistclient
);
498 //convert the mouse coordinates relative to the top-left of
500 ClientToScreen(&point
);
501 GetClientRect(&rect
);
502 ClientToScreen(&rect
);
503 point
.x
-= rect
.left
;
504 point
.y
-= treelist
.top
;
506 //same for the window coordinates - make them relative to 0,0
507 OffsetRect(&treelist
, -treelist
.left
, -treelist
.top
);
509 if (point
.x
< treelist
.left
+REPOBROWSER_CTRL_MIN_WIDTH
)
510 point
.x
= treelist
.left
+REPOBROWSER_CTRL_MIN_WIDTH
;
511 if (point
.x
> treelist
.right
-REPOBROWSER_CTRL_MIN_WIDTH
)
512 point
.x
= treelist
.right
-REPOBROWSER_CTRL_MIN_WIDTH
;
514 if ((nFlags
& MK_LBUTTON
) && (point
.x
!= oldx
))
520 DrawXorBar(pDC
, oldx
+2, treelistclient
.top
, 4, treelistclient
.bottom
-treelistclient
.top
-2);
521 DrawXorBar(pDC
, point
.x
+2, treelistclient
.top
, 4, treelistclient
.bottom
-treelistclient
.top
-2);
530 CStandAloneDialogTmpl
<CResizableDialog
>::OnMouseMove(nFlags
, point
);
533 void CRepositoryBrowser::OnLButtonDown(UINT nFlags
, CPoint point
)
536 RECT rect
, tree
, list
, treelist
, treelistclient
;
538 // create an union of the tree and list control rectangle
539 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&list
);
540 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&tree
);
541 UnionRect(&treelist
, &tree
, &list
);
542 treelistclient
= treelist
;
543 ScreenToClient(&treelistclient
);
545 //convert the mouse coordinates relative to the top-left of
547 ClientToScreen(&point
);
548 GetClientRect(&rect
);
549 ClientToScreen(&rect
);
550 point
.x
-= rect
.left
;
551 point
.y
-= treelist
.top
;
553 //same for the window coordinates - make them relative to 0,0
554 OffsetRect(&treelist
, -treelist
.left
, -treelist
.top
);
556 if (point
.x
< treelist
.left
+REPOBROWSER_CTRL_MIN_WIDTH
)
557 point
.x
= treelist
.left
+REPOBROWSER_CTRL_MIN_WIDTH
;
558 if (point
.x
> treelist
.right
-3)
559 return CStandAloneDialogTmpl
<CResizableDialog
>::OnLButtonDown(nFlags
, point
);
560 if (point
.x
> treelist
.right
-REPOBROWSER_CTRL_MIN_WIDTH
)
561 point
.x
= treelist
.right
-REPOBROWSER_CTRL_MIN_WIDTH
;
563 if ((point
.y
< treelist
.top
) ||
564 (point
.y
> treelist
.bottom
))
565 return CStandAloneDialogTmpl
<CResizableDialog
>::OnLButtonDown(nFlags
, point
);
572 DrawXorBar(pDC
, point
.x
+2, treelistclient
.top
, 4, treelistclient
.bottom
-treelistclient
.top
-2);
578 CStandAloneDialogTmpl
<CResizableDialog
>::OnLButtonDown(nFlags
, point
);
581 void CRepositoryBrowser::OnLButtonUp(UINT nFlags
, CPoint point
)
584 RECT rect
, tree
, list
, treelist
, treelistclient
;
586 if (bDragMode
== FALSE
)
589 // create an union of the tree and list control rectangle
590 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&list
);
591 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&tree
);
592 UnionRect(&treelist
, &tree
, &list
);
593 treelistclient
= treelist
;
594 ScreenToClient(&treelistclient
);
596 ClientToScreen(&point
);
597 GetClientRect(&rect
);
598 ClientToScreen(&rect
);
600 CPoint point2
= point
;
601 if (point2
.x
< treelist
.left
+REPOBROWSER_CTRL_MIN_WIDTH
)
602 point2
.x
= treelist
.left
+REPOBROWSER_CTRL_MIN_WIDTH
;
603 if (point2
.x
> treelist
.right
-REPOBROWSER_CTRL_MIN_WIDTH
)
604 point2
.x
= treelist
.right
-REPOBROWSER_CTRL_MIN_WIDTH
;
606 point
.x
-= rect
.left
;
607 point
.y
-= treelist
.top
;
609 OffsetRect(&treelist
, -treelist
.left
, -treelist
.top
);
611 if (point
.x
< treelist
.left
+REPOBROWSER_CTRL_MIN_WIDTH
)
612 point
.x
= treelist
.left
+REPOBROWSER_CTRL_MIN_WIDTH
;
613 if (point
.x
> treelist
.right
-REPOBROWSER_CTRL_MIN_WIDTH
)
614 point
.x
= treelist
.right
-REPOBROWSER_CTRL_MIN_WIDTH
;
617 DrawXorBar(pDC
, oldx
+2, treelistclient
.top
, 4, treelistclient
.bottom
-treelistclient
.top
-2);
626 //position the child controls
627 GetDlgItem(IDC_REPOTREE
)->GetWindowRect(&treelist
);
628 treelist
.right
= point2
.x
- 2;
629 ScreenToClient(&treelist
);
630 RemoveAnchor(IDC_REPOTREE
);
631 GetDlgItem(IDC_REPOTREE
)->MoveWindow(&treelist
);
632 GetDlgItem(IDC_REPOLIST
)->GetWindowRect(&treelist
);
633 treelist
.left
= point2
.x
+ 2;
634 ScreenToClient(&treelist
);
635 RemoveAnchor(IDC_REPOLIST
);
636 GetDlgItem(IDC_REPOLIST
)->MoveWindow(&treelist
);
638 AddAnchor(IDC_REPOTREE
, TOP_LEFT
, BOTTOM_LEFT
);
639 AddAnchor(IDC_REPOLIST
, TOP_LEFT
, BOTTOM_RIGHT
);
641 CStandAloneDialogTmpl
<CResizableDialog
>::OnLButtonUp(nFlags
, point
);
644 void CRepositoryBrowser::DrawXorBar(CDC
* pDC
, int x1
, int y1
, int width
, int height
)
646 static WORD _dotPatternBmp
[8] =
648 0x0055, 0x00aa, 0x0055, 0x00aa,
649 0x0055, 0x00aa, 0x0055, 0x00aa
653 HBRUSH hbr
, hbrushOld
;
655 hbm
= CreateBitmap(8, 8, 1, 1, _dotPatternBmp
);
656 hbr
= CreatePatternBrush(hbm
);
658 pDC
->SetBrushOrg(x1
, y1
);
659 hbrushOld
= (HBRUSH
)pDC
->SelectObject(hbr
);
661 PatBlt(pDC
->GetSafeHdc(), x1
, y1
, width
, height
, PATINVERT
);
663 pDC
->SelectObject(hbrushOld
);
669 /******************************************************************************/
670 /* repository information gathering */
671 /******************************************************************************/
673 BOOL
CRepositoryBrowser::ReportList(const CString
& path
, svn_node_kind_t kind
,
674 svn_filesize_t size
, bool has_props
,
675 svn_revnum_t created_rev
, apr_time_t time
,
676 const CString
& author
, const CString
& locktoken
,
677 const CString
& lockowner
, const CString
& lockcomment
,
678 bool is_dav_comment
, apr_time_t lock_creationdate
,
679 apr_time_t lock_expirationdate
,
680 const CString
& absolutepath
)
682 static deque
<CItem
> * pDirList
= NULL
;
683 static CTreeItem
* pTreeItem
= NULL
;
684 static CString dirPath
;
686 CString sParent
= absolutepath
;
687 int slashpos
= path
.ReverseFind('/');
688 bool abspath_has_slash
= (absolutepath
.GetAt(absolutepath
.GetLength()-1) == '/');
689 if ((slashpos
> 0) && (!abspath_has_slash
))
691 sParent
+= path
.Left(slashpos
);
692 if (sParent
.Compare(_T("/"))==0)
694 if ((path
.IsEmpty())||
696 (sParent
.Compare(dirPath
)))
698 HTREEITEM hItem
= FindUrl(m_strReposRoot
+ sParent
);
699 pTreeItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hItem
);
700 pDirList
= &(pTreeItem
->children
);
707 if (kind
== svn_node_dir
)
709 FindUrl(m_strReposRoot
+ absolutepath
+ (abspath_has_slash
? _T("") : _T("/")) + path
);
711 pTreeItem
->has_child_folders
= true;
713 pDirList
->push_back(CItem(path
.Mid(slashpos
+1), kind
, size
, has_props
,
714 created_rev
, time
, author
, locktoken
,
715 lockowner
, lockcomment
, is_dav_comment
,
716 lock_creationdate
, lock_expirationdate
,
717 m_strReposRoot
+absolutepath
+(abspath_has_slash
? _T("") : _T("/"))+path
));
719 pTreeItem
->children_fetched
= true;
723 bool CRepositoryBrowser::ChangeToUrl(CString
& url
, SVNRev
& rev
, bool bAlreadyChecked
)
726 if (!bAlreadyChecked
)
728 // check if the entered url is valid
730 const SVNInfoData
* data
= NULL
;
731 CString orig_url
= url
;
732 m_bCancelled
= false;
735 data
= info
.GetFirstFileInfo(CTSVNPath(url
), rev
, rev
);
736 if (data
&& rev
.IsHead())
740 if ((data
== NULL
)||(data
->kind
!= svn_node_dir
))
742 // in case the url is not a valid directory, try the parent dir
743 // until there's no more parent dir
744 url
= url
.Left(url
.ReverseFind('/'));
746 } while(!m_bCancelled
&& !url
.IsEmpty() && ((data
== NULL
) || (data
->kind
!= svn_node_dir
)));
750 CString partUrl
= url
;
751 HTREEITEM hItem
= m_RepoTree
.GetRootItem();
752 if ((LONG(rev
) != LONG(m_initialRev
))||
753 (m_strReposRoot
.IsEmpty())||
754 (m_strReposRoot
.Compare(url
.Left(m_strReposRoot
.GetLength())))||
755 (url
.GetAt(m_strReposRoot
.GetLength()) != '/'))
757 // if the revision changed, then invalidate everything
758 RecursiveRemove(hItem
);
759 m_RepoTree
.DeleteAllItems();
760 m_RepoList
.DeleteAllItems();
761 m_RepoList
.ShowText(CString(MAKEINTRESOURCE(IDS_REPOBROWSE_WAIT
)), true);
762 hItem
= m_RepoTree
.GetRootItem();
763 if ((m_strReposRoot
.IsEmpty())||(m_strReposRoot
.Compare(url
.Left(m_strReposRoot
.GetLength())))||
764 (url
.GetAt(m_strReposRoot
.GetLength()) != '/'))
766 // if the repository root has changed, initialize all data from scratch
767 // and clear the project properties we might have loaded previously
768 m_ProjectProperties
= ProjectProperties();
771 if ((m_InitialUrl
.IsEmpty())||(m_strReposRoot
.IsEmpty()))
777 // the tree view is empty, just fill in the repository root
778 CTreeItem
* pTreeItem
= new CTreeItem();
779 pTreeItem
->unescapedname
= m_strReposRoot
;
780 pTreeItem
->url
= m_strReposRoot
;
782 TVINSERTSTRUCT tvinsert
= {0};
783 tvinsert
.hParent
= TVI_ROOT
;
784 tvinsert
.hInsertAfter
= TVI_ROOT
;
785 tvinsert
.itemex
.mask
= TVIF_CHILDREN
| TVIF_DI_SETITEM
| TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
786 tvinsert
.itemex
.pszText
= m_strReposRoot
.GetBuffer(m_strReposRoot
.GetLength());
787 tvinsert
.itemex
.cChildren
= 1;
788 tvinsert
.itemex
.lParam
= (LPARAM
)pTreeItem
;
789 tvinsert
.itemex
.iImage
= m_nIconFolder
;
790 tvinsert
.itemex
.iSelectedImage
= m_nOpenIconFolder
;
792 hItem
= m_RepoTree
.InsertItem(&tvinsert
);
793 m_strReposRoot
.ReleaseBuffer();
797 // something terrible happened!
800 hItem
= FindUrl(url
);
804 CTreeItem
* pTreeItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hItem
);
805 if (pTreeItem
== NULL
)
808 if (!m_RepoList
.HasText())
809 m_RepoList
.ShowText(_T(" "), true);
812 m_RepoTree
.Expand(hItem
, TVE_EXPAND
);
813 FillList(&pTreeItem
->children
);
815 m_blockEvents
= true;
816 m_RepoTree
.EnsureVisible(hItem
);
817 m_RepoTree
.SelectItem(hItem
);
818 m_blockEvents
= false;
820 m_RepoList
.ClearText();
825 void CRepositoryBrowser::FillList(deque
<CItem
> * pItems
)
830 m_RepoList
.SetRedraw(false);
831 m_RepoList
.DeleteAllItems();
833 int c
= ((CHeaderCtrl
*)(m_RepoList
.GetDlgItem(0)))->GetItemCount()-1;
835 m_RepoList
.DeleteColumn(c
--);
840 // column 0: contains tree
841 temp
.LoadString(IDS_LOG_FILE
);
842 m_RepoList
.InsertColumn(c
++, temp
);
844 // column 1: file extension
845 temp
.LoadString(IDS_STATUSLIST_COLEXT
);
846 m_RepoList
.InsertColumn(c
++, temp
);
848 // column 2: revision number
849 temp
.LoadString(IDS_LOG_REVISION
);
850 m_RepoList
.InsertColumn(c
++, temp
, LVCFMT_RIGHT
);
853 temp
.LoadString(IDS_LOG_AUTHOR
);
854 m_RepoList
.InsertColumn(c
++, temp
);
857 temp
.LoadString(IDS_LOG_SIZE
);
858 m_RepoList
.InsertColumn(c
++, temp
, LVCFMT_RIGHT
);
861 temp
.LoadString(IDS_LOG_DATE
);
862 m_RepoList
.InsertColumn(c
++, temp
);
864 // column 6: lock owner
865 temp
.LoadString(IDS_STATUSLIST_COLLOCK
);
866 m_RepoList
.InsertColumn(c
++, temp
);
868 // now fill in the data
870 TCHAR date_native
[SVN_DATE_BUFFER
];
872 for (deque
<CItem
>::const_iterator it
= pItems
->begin(); it
!= pItems
->end(); ++it
)
875 if (it
->kind
== svn_node_dir
)
876 icon_idx
= m_nIconFolder
;
878 icon_idx
= SYS_IMAGE_LIST().GetFileIconIndex(it
->path
);
879 int index
= m_RepoList
.InsertItem(nCount
, it
->path
, icon_idx
);
881 temp
= CPathUtils::GetFileExtFromPath(it
->path
);
882 if (it
->kind
== svn_node_file
)
883 m_RepoList
.SetItemText(index
, 1, temp
);
885 temp
.Format(_T("%ld"), it
->created_rev
);
886 m_RepoList
.SetItemText(index
, 2, temp
);
888 m_RepoList
.SetItemText(index
, 3, it
->author
);
890 if (it
->kind
== svn_node_file
)
892 StrFormatByteSize(it
->size
, temp
.GetBuffer(20), 20);
893 temp
.ReleaseBuffer();
894 m_RepoList
.SetItemText(index
, 4, temp
);
897 SVN::formatDate(date_native
, (apr_time_t
&)it
->time
, true);
898 m_RepoList
.SetItemText(index
, 5, date_native
);
900 m_RepoList
.SetItemText(index
, 6, it
->lockowner
);
901 m_RepoList
.SetItemData(index
, (DWORD_PTR
)&(*it
));
904 ListView_SortItemsEx(m_RepoList
, ListSort
, this);
907 for (int col
= 0; col
<= (((CHeaderCtrl
*)(m_RepoList
.GetDlgItem(0)))->GetItemCount()-1); col
++)
909 m_RepoList
.SetColumnWidth(col
, LVSCW_AUTOSIZE_USEHEADER
);
911 for (int col
= 0; col
<= (((CHeaderCtrl
*)(m_RepoList
.GetDlgItem(0)))->GetItemCount()-1); col
++)
913 m_arColumnAutoWidths
[col
] = m_RepoList
.GetColumnWidth(col
);
916 CRegString
regColWidths(_T("Software\\TortoiseGit\\RepoBrowserColumnWidth"));
917 if (!CString(regColWidths
).IsEmpty())
919 StringToWidthArray(regColWidths
, m_arColumnWidths
);
921 int maxcol
= ((CHeaderCtrl
*)(m_RepoList
.GetDlgItem(0)))->GetItemCount()-1;
922 for (int col
= 1; col
<= maxcol
; col
++)
924 if (m_arColumnWidths
[col
] == 0)
925 m_RepoList
.SetColumnWidth(col
, LVSCW_AUTOSIZE_USEHEADER
);
927 m_RepoList
.SetColumnWidth(col
, m_arColumnWidths
[col
]);
931 m_RepoList
.SetRedraw(true);
934 HTREEITEM
CRepositoryBrowser::FindUrl(const CString
& fullurl
, bool create
/* = true */)
936 return FindUrl(fullurl
, fullurl
, create
, TVI_ROOT
);
939 HTREEITEM
CRepositoryBrowser::FindUrl(const CString
& fullurl
, const CString
& url
, bool create
/* true */, HTREEITEM hItem
/* = TVI_ROOT */)
941 if (hItem
== TVI_ROOT
)
943 hItem
= m_RepoTree
.GetRootItem();
944 if (fullurl
.Compare(m_strReposRoot
)==0)
946 return FindUrl(fullurl
, url
.Mid(m_strReposRoot
.GetLength()+1), create
, hItem
);
948 HTREEITEM hSibling
= hItem
;
949 if (m_RepoTree
.GetNextItem(hItem
, TVGN_CHILD
))
951 hSibling
= m_RepoTree
.GetNextItem(hItem
, TVGN_CHILD
);
954 CTreeItem
* pTItem
= ((CTreeItem
*)m_RepoTree
.GetItemData(hSibling
));
957 CString sSibling
= pTItem
->unescapedname
;
958 if (sSibling
.Compare(url
.Left(sSibling
.GetLength()))==0)
960 if (sSibling
.GetLength() == url
.GetLength())
962 if (url
.GetAt(sSibling
.GetLength()) == '/')
963 return FindUrl(fullurl
, url
.Mid(sSibling
.GetLength()+1), create
, hSibling
);
966 } while ((hSibling
= m_RepoTree
.GetNextItem(hSibling
, TVGN_NEXT
)) != NULL
);
970 // create tree items for every path part in the url
973 HTREEITEM hNewItem
= hItem
;
975 while ((slash
=sUrl
.Find('/')) >= 0)
977 CTreeItem
* pTreeItem
= new CTreeItem();
978 sTemp
= sUrl
.Left(slash
);
979 pTreeItem
->unescapedname
= sTemp
;
980 pTreeItem
->url
= fullurl
.Left(fullurl
.GetLength()-sUrl
.GetLength()+slash
);
981 UINT state
= pTreeItem
->url
.CompareNoCase(m_diffURL
.GetSVNPathString()) ? 0 : TVIS_BOLD
;
982 TVINSERTSTRUCT tvinsert
= {0};
983 tvinsert
.hParent
= hNewItem
;
984 tvinsert
.hInsertAfter
= TVI_SORT
;
985 tvinsert
.itemex
.mask
= TVIF_CHILDREN
| TVIF_DI_SETITEM
| TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_STATE
;
986 tvinsert
.itemex
.state
= state
;
987 tvinsert
.itemex
.stateMask
= state
;
988 tvinsert
.itemex
.pszText
= sTemp
.GetBuffer(sTemp
.GetLength());
989 tvinsert
.itemex
.cChildren
= 1;
990 tvinsert
.itemex
.lParam
= (LPARAM
)pTreeItem
;
991 tvinsert
.itemex
.iImage
= m_nIconFolder
;
992 tvinsert
.itemex
.iSelectedImage
= m_nOpenIconFolder
;
994 hNewItem
= m_RepoTree
.InsertItem(&tvinsert
);
995 sTemp
.ReleaseBuffer();
996 sUrl
= sUrl
.Mid(slash
+1);
997 ATLTRACE(_T("created tree entry %s, url %s\n"), sTemp
, pTreeItem
->url
);
1001 CTreeItem
* pTreeItem
= new CTreeItem();
1003 pTreeItem
->unescapedname
= sTemp
;
1004 pTreeItem
->url
= fullurl
;
1005 UINT state
= pTreeItem
->url
.CompareNoCase(m_diffURL
.GetSVNPathString()) ? 0 : TVIS_BOLD
;
1006 TVINSERTSTRUCT tvinsert
= {0};
1007 tvinsert
.hParent
= hNewItem
;
1008 tvinsert
.hInsertAfter
= TVI_SORT
;
1009 tvinsert
.itemex
.mask
= TVIF_CHILDREN
| TVIF_DI_SETITEM
| TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_STATE
;
1010 tvinsert
.itemex
.state
= state
;
1011 tvinsert
.itemex
.stateMask
= state
;
1012 tvinsert
.itemex
.pszText
= sTemp
.GetBuffer(sTemp
.GetLength());
1013 tvinsert
.itemex
.cChildren
= 1;
1014 tvinsert
.itemex
.lParam
= (LPARAM
)pTreeItem
;
1015 tvinsert
.itemex
.iImage
= m_nIconFolder
;
1016 tvinsert
.itemex
.iSelectedImage
= m_nOpenIconFolder
;
1018 hNewItem
= m_RepoTree
.InsertItem(&tvinsert
);
1019 sTemp
.ReleaseBuffer();
1020 m_RepoTree
.SortChildren(hNewItem
);
1026 bool CRepositoryBrowser::RefreshNode(const CString
& url
, bool force
/* = false*/, bool recursive
/* = false*/)
1028 HTREEITEM hNode
= FindUrl(url
);
1029 return RefreshNode(hNode
, force
, recursive
);
1032 bool CRepositoryBrowser::RefreshNode(HTREEITEM hNode
, bool force
/* = false*/, bool recursive
/* = false*/)
1038 CTreeItem
* pTreeItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hNode
);
1039 HTREEITEM hSel1
= m_RepoTree
.GetSelectedItem();
1040 if (m_RepoTree
.ItemHasChildren(hNode
))
1042 HTREEITEM hChild
= m_RepoTree
.GetChildItem(hNode
);
1044 m_blockEvents
= true;
1047 hNext
= m_RepoTree
.GetNextItem(hChild
, TVGN_NEXT
);
1048 RecursiveRemove(hChild
);
1049 m_RepoTree
.DeleteItem(hChild
);
1052 m_blockEvents
= false;
1054 if (pTreeItem
== NULL
)
1056 pTreeItem
->children
.clear();
1057 pTreeItem
->has_child_folders
= false;
1058 m_bCancelled
= false;
1059 if (!List(CTSVNPath(pTreeItem
->url
), GetRevision(), GetRevision(), recursive
? svn_depth_infinity
: svn_depth_immediates
, true))
1061 // error during list()
1062 m_RepoList
.ShowText(GetLastErrorMessage());
1065 pTreeItem
->children_fetched
= true;
1066 // if there are no child folders, remove the '+' in front of the node
1068 TVITEM tvitem
= {0};
1069 tvitem
.hItem
= hNode
;
1070 tvitem
.mask
= TVIF_CHILDREN
;
1071 tvitem
.cChildren
= pTreeItem
->has_child_folders
? 1 : 0;
1072 m_RepoTree
.SetItem(&tvitem
);
1074 if ((force
)||(hSel1
== hNode
)||(hSel1
!= m_RepoTree
.GetSelectedItem()))
1076 FillList(&pTreeItem
->children
);
1081 BOOL
CRepositoryBrowser::PreTranslateMessage(MSG
* pMsg
)
1083 if (pMsg
->message
>=WM_KEYFIRST
&& pMsg
->message
<=WM_KEYLAST
)
1085 // Check if there is an in place Edit active:
1086 // in place edits are done with an edit control, where the parent
1087 // is the control with the editable item (tree or list control here)
1088 HWND hWndFocus
= ::GetFocus();
1090 hWndFocus
= ::GetParent(hWndFocus
);
1091 if (hWndFocus
&& ((hWndFocus
== m_RepoTree
.GetSafeHwnd())||(hWndFocus
== m_RepoList
.GetSafeHwnd())))
1093 // Do a direct translation.
1094 ::TranslateMessage(pMsg
);
1095 ::DispatchMessage(pMsg
);
1100 if (pMsg
->message
== WM_KEYDOWN
)
1102 switch (pMsg
->wParam
)
1109 if ((pMsg
->hwnd
== m_barRepository
.GetSafeHwnd())||(::IsChild(m_barRepository
.GetSafeHwnd(), pMsg
->hwnd
)))
1110 return __super::PreTranslateMessage(pMsg
);
1115 int ret
= TranslateAccelerator(m_hWnd
, m_hAccel
, pMsg
);
1120 return __super::PreTranslateMessage(pMsg
);
1123 void CRepositoryBrowser::OnDelete()
1125 CTSVNPathList urlList
;
1126 bool bTreeItem
= false;
1128 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
1130 while ((index
= m_RepoList
.GetNextSelectedItem(pos
))>=0)
1132 CItem
* pItem
= (CItem
*)m_RepoList
.GetItemData(index
);
1133 CString absPath
= pItem
->absolutepath
;
1134 absPath
.Replace(_T("\\"), _T("%5C"));
1135 urlList
.AddPath(CTSVNPath(absPath
));
1137 if ((urlList
.GetCount() == 0))
1139 HTREEITEM hItem
= m_RepoTree
.GetSelectedItem();
1140 CTreeItem
* pTreeItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hItem
);
1143 urlList
.AddPath(CTSVNPath(pTreeItem
->url
));
1148 if (urlList
.GetCount() == 0)
1152 CWaitCursorEx wait_cursor
;
1153 CInputLogDlg
input(this);
1154 input
.SetUUID(m_sUUID
);
1155 input
.SetProjectProperties(&m_ProjectProperties
);
1157 if (urlList
.GetCount() == 1)
1158 hint
.Format(IDS_INPUT_REMOVEONE
, (LPCTSTR
)urlList
[0].GetFileOrDirectoryName());
1160 hint
.Format(IDS_INPUT_REMOVEMORE
, urlList
.GetCount());
1161 input
.SetActionText(hint
);
1162 if (input
.DoModal() == IDOK
)
1164 if (!Remove(urlList
, true, false, input
.GetLogMessage()))
1167 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
1171 RefreshNode(m_RepoTree
.GetParentItem(m_RepoTree
.GetSelectedItem()), true);
1173 RefreshNode(m_RepoTree
.GetSelectedItem(), true);
1177 void CRepositoryBrowser::OnGoUp()
1179 m_barRepository
.OnGoUp();
1182 void CRepositoryBrowser::OnUrlFocus()
1184 m_barRepository
.SetFocusToURL();
1187 void CRepositoryBrowser::OnCopy()
1189 // Ctrl-C : copy the selected item urls to the clipboard
1191 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
1193 while ((index
= m_RepoList
.GetNextSelectedItem(pos
))>=0)
1195 CItem
* pItem
= (CItem
*)m_RepoList
.GetItemData(index
);
1196 url
+= CUnicodeUtils::GetUnicode(CPathUtils::PathEscape(CUnicodeUtils::GetUTF8(pItem
->absolutepath
))) + _T("\r\n");
1200 url
.TrimRight(_T("\r\n"));
1201 CStringUtils::WriteAsciiStringToClipboard(url
);
1205 void CRepositoryBrowser::OnInlineedit()
1207 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
1208 int selIndex
= m_RepoList
.GetNextSelectedItem(pos
);
1209 m_blockEvents
= true;
1212 m_RepoList
.SetFocus();
1213 m_RepoList
.EditLabel(selIndex
);
1217 m_RepoTree
.SetFocus();
1218 HTREEITEM hTreeItem
= m_RepoTree
.GetSelectedItem();
1219 if (hTreeItem
!= m_RepoTree
.GetRootItem())
1220 m_RepoTree
.EditLabel(hTreeItem
);
1222 m_blockEvents
= false;
1225 void CRepositoryBrowser::OnRefresh()
1227 m_blockEvents
= true;
1228 RefreshNode(m_RepoTree
.GetSelectedItem(), true, !!(GetKeyState(VK_CONTROL
)&0x8000));
1229 m_blockEvents
= false;
1232 void CRepositoryBrowser::OnTvnSelchangedRepotree(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1234 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
1240 if (pNMTreeView
->action
== TVC_BYKEYBOARD
)
1241 SetTimer(REPOBROWSER_FETCHTIMER
, 300, NULL
);
1243 OnTimer(REPOBROWSER_FETCHTIMER
);
1246 void CRepositoryBrowser::OnTimer(UINT_PTR nIDEvent
)
1248 if (nIDEvent
== REPOBROWSER_FETCHTIMER
)
1250 KillTimer(REPOBROWSER_FETCHTIMER
);
1251 // find the currently selected item
1252 HTREEITEM hSelItem
= m_RepoTree
.GetSelectedItem();
1255 CTreeItem
* pTreeItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hSelItem
);
1258 if (!pTreeItem
->children_fetched
)
1260 m_RepoList
.ShowText(_T(" "), true);
1261 RefreshNode(hSelItem
);
1262 m_RepoList
.ClearText();
1265 FillList(&pTreeItem
->children
);
1266 m_barRepository
.ShowUrl(pTreeItem
->url
, GetRevision());
1271 __super::OnTimer(nIDEvent
);
1274 void CRepositoryBrowser::OnTvnItemexpandingRepotree(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1276 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
1282 CTreeItem
* pTreeItem
= (CTreeItem
*)pNMTreeView
->itemNew
.lParam
;
1284 if (pTreeItem
== NULL
)
1287 if (pNMTreeView
->action
== TVE_COLLAPSE
)
1289 // user wants to collapse a tree node.
1290 // if we don't know anything about the children
1291 // of the node, we suppress the collapsing but fetch the info instead
1292 if (!pTreeItem
->children_fetched
)
1294 RefreshNode(pNMTreeView
->itemNew
.hItem
);
1301 // user wants to expand a tree node.
1302 // check if we already know its children - if not we have to ask the repository!
1304 if (!pTreeItem
->children_fetched
)
1306 RefreshNode(pNMTreeView
->itemNew
.hItem
);
1310 // if there are no child folders, remove the '+' in front of the node
1311 if (!pTreeItem
->has_child_folders
)
1313 TVITEM tvitem
= {0};
1314 tvitem
.hItem
= pNMTreeView
->itemNew
.hItem
;
1315 tvitem
.mask
= TVIF_CHILDREN
;
1316 tvitem
.cChildren
= 0;
1317 m_RepoTree
.SetItem(&tvitem
);
1322 void CRepositoryBrowser::OnNMDblclkRepolist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1324 LPNMITEMACTIVATE pNmItemActivate
= reinterpret_cast<LPNMITEMACTIVATE
>(pNMHDR
);
1330 if (pNmItemActivate
->iItem
< 0)
1332 CItem
* pItem
= (CItem
*)m_RepoList
.GetItemData(pNmItemActivate
->iItem
);
1333 if ((pItem
)&&(pItem
->kind
== svn_node_dir
))
1335 // a double click on a folder results in selecting that folder
1336 ChangeToUrl(pItem
->absolutepath
, m_initialRev
, true);
1340 void CRepositoryBrowser::OnHdnItemclickRepolist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1342 LPNMHEADER phdr
= reinterpret_cast<LPNMHEADER
>(pNMHDR
);
1343 // a click on a header means sorting the items
1344 if (m_nSortedColumn
!= phdr
->iItem
)
1345 m_bSortAscending
= true;
1347 m_bSortAscending
= !m_bSortAscending
;
1348 m_nSortedColumn
= phdr
->iItem
;
1350 m_blockEvents
= true;
1351 ListView_SortItemsEx(m_RepoList
, ListSort
, this);
1353 m_blockEvents
= false;
1357 int CRepositoryBrowser::ListSort(LPARAM lParam1
, LPARAM lParam2
, LPARAM lParam3
)
1359 CRepositoryBrowser
* pThis
= (CRepositoryBrowser
*)lParam3
;
1360 CItem
* pItem1
= (CItem
*)pThis
->m_RepoList
.GetItemData(static_cast<int>(lParam1
));
1361 CItem
* pItem2
= (CItem
*)pThis
->m_RepoList
.GetItemData(static_cast<int>(lParam2
));
1363 switch (pThis
->m_nSortedColumn
)
1365 case 1: // extension
1366 nRet
= pThis
->m_RepoList
.GetItemText(static_cast<int>(lParam1
), 1)
1367 .CompareNoCase(pThis
->m_RepoList
.GetItemText(static_cast<int>(lParam2
), 1));
1371 case 2: // revision number
1372 nRet
= pItem1
->created_rev
- pItem2
->created_rev
;
1377 nRet
= pItem1
->author
.CompareNoCase(pItem2
->author
);
1382 nRet
= int(pItem1
->size
- pItem2
->size
);
1387 nRet
= (pItem1
->time
- pItem2
->time
) > 0 ? 1 : -1;
1391 case 6: // lock owner
1392 nRet
= pItem1
->lockowner
.CompareNoCase(pItem2
->lockowner
);
1397 nRet
= CStringUtils::CompareNumerical(pItem1
->path
, pItem2
->path
);
1401 if (!pThis
->m_bSortAscending
)
1404 // we want folders on top, then the files
1405 if (pItem1
->kind
!= pItem2
->kind
)
1407 if (pItem1
->kind
== svn_node_dir
)
1416 void CRepositoryBrowser::SetSortArrow()
1418 CHeaderCtrl
* pHeader
= m_RepoList
.GetHeaderCtrl();
1419 HDITEM HeaderItem
= {0};
1420 HeaderItem
.mask
= HDI_FORMAT
;
1421 for (int i
=0; i
<pHeader
->GetItemCount(); ++i
)
1423 pHeader
->GetItem(i
, &HeaderItem
);
1424 HeaderItem
.fmt
&= ~(HDF_SORTDOWN
| HDF_SORTUP
);
1425 pHeader
->SetItem(i
, &HeaderItem
);
1428 pHeader
->GetItem(m_nSortedColumn
, &HeaderItem
);
1429 HeaderItem
.fmt
|= (m_bSortAscending
? HDF_SORTUP
: HDF_SORTDOWN
);
1430 pHeader
->SetItem(m_nSortedColumn
, &HeaderItem
);
1433 void CRepositoryBrowser::OnLvnItemchangedRepolist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1435 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1439 if (m_RepoList
.HasText())
1441 if (pNMLV
->uChanged
& LVIF_STATE
)
1443 if (pNMLV
->uNewState
& LVIS_SELECTED
)
1445 CItem
* pItem
= (CItem
*)m_RepoList
.GetItemData(pNMLV
->iItem
);
1447 m_barRepository
.ShowUrl(pItem
->absolutepath
, GetRevision());
1452 void CRepositoryBrowser::OnLvnEndlabeleditRepolist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1454 NMLVDISPINFO
*pDispInfo
= reinterpret_cast<NMLVDISPINFO
*>(pNMHDR
);
1456 if (pDispInfo
->item
.pszText
== NULL
)
1458 // rename the item in the repository
1459 CItem
* pItem
= (CItem
*)m_RepoList
.GetItemData(pDispInfo
->item
.iItem
);
1461 CWaitCursorEx wait_cursor
;
1462 CInputLogDlg
input(this);
1463 input
.SetUUID(m_sUUID
);
1464 input
.SetProjectProperties(&m_ProjectProperties
);
1465 CTSVNPath targetUrl
= CTSVNPath(EscapeUrl(CTSVNPath(pItem
->absolutepath
.Left(pItem
->absolutepath
.ReverseFind('/')+1)+pDispInfo
->item
.pszText
)));
1466 if (!targetUrl
.IsValidOnWindows())
1468 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH
, IDS_APPNAME
, MB_ICONINFORMATION
|MB_YESNO
) != IDYES
)
1472 sHint
.Format(IDS_INPUT_RENAME
, (LPCTSTR
)(pItem
->absolutepath
), (LPCTSTR
)targetUrl
.GetSVNPathString());
1473 input
.SetActionText(sHint
);
1474 if (input
.DoModal() == IDOK
)
1476 m_bCancelled
= false;
1477 if (!Move(CTSVNPathList(CTSVNPath(EscapeUrl(CTSVNPath(pItem
->absolutepath
)))),
1479 true, input
.GetLogMessage()))
1482 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
1486 RefreshNode(m_RepoTree
.GetSelectedItem(), true);
1490 void CRepositoryBrowser::OnTvnEndlabeleditRepotree(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1492 LPNMTVDISPINFO pTVDispInfo
= reinterpret_cast<LPNMTVDISPINFO
>(pNMHDR
);
1494 if (pTVDispInfo
->item
.pszText
== NULL
)
1497 // rename the item in the repository
1498 HTREEITEM hSelectedItem
= pTVDispInfo
->item
.hItem
;
1499 CTreeItem
* pItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hSelectedItem
);
1503 CWaitCursorEx wait_cursor
;
1504 CInputLogDlg
input(this);
1505 input
.SetUUID(m_sUUID
);
1506 input
.SetProjectProperties(&m_ProjectProperties
);
1507 CTSVNPath targetUrl
= CTSVNPath(EscapeUrl(CTSVNPath(pItem
->url
.Left(pItem
->url
.ReverseFind('/')+1)+pTVDispInfo
->item
.pszText
)));
1508 if (!targetUrl
.IsValidOnWindows())
1510 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH
, IDS_APPNAME
, MB_ICONINFORMATION
|MB_YESNO
) != IDYES
)
1514 sHint
.Format(IDS_INPUT_RENAME
, (LPCTSTR
)(pItem
->url
), (LPCTSTR
)targetUrl
.GetSVNPathString());
1515 input
.SetActionText(sHint
);
1516 if (input
.DoModal() == IDOK
)
1518 m_bCancelled
= false;
1519 if (!Move(CTSVNPathList(CTSVNPath(EscapeUrl(CTSVNPath(pItem
->url
)))),
1521 true, input
.GetLogMessage()))
1524 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
1528 pItem
->url
= targetUrl
.GetSVNPathString();
1529 pItem
->unescapedname
= pTVDispInfo
->item
.pszText
;
1530 m_RepoTree
.SetItemData(hSelectedItem
, (DWORD_PTR
)pItem
);
1531 if (hSelectedItem
== m_RepoTree
.GetSelectedItem())
1532 RefreshNode(hSelectedItem
, true);
1536 void CRepositoryBrowser::OnLvnBeginrdragRepolist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1538 m_bRightDrag
= true;
1540 OnBeginDrag(pNMHDR
);
1543 void CRepositoryBrowser::OnLvnBegindragRepolist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1545 m_bRightDrag
= false;
1547 OnBeginDrag(pNMHDR
);
1550 void CRepositoryBrowser::OnBeginDrag(NMHDR
*pNMHDR
)
1552 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1554 if (m_RepoList
.HasText())
1556 CIDropSource
* pdsrc
= new CIDropSource
;
1561 CTSVNPathList sourceURLs
;
1562 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
1564 while ((index
= m_RepoList
.GetNextSelectedItem(pos
))>=0)
1566 CItem
* pItem
= (CItem
*)m_RepoList
.GetItemData(index
);
1568 sourceURLs
.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(pItem
->absolutepath
))));
1571 SVNDataObject
* pdobj
= new SVNDataObject(sourceURLs
, GetRevision(), GetRevision());
1578 pdobj
->SetAsyncMode(TRUE
);
1580 CDragSourceHelper dragsrchelper
;
1581 dragsrchelper
.InitializeFromWindow(m_RepoList
.GetSafeHwnd(), pNMLV
->ptAction
, pdobj
);
1582 // Initiate the Drag & Drop
1584 ::DoDragDrop(pdobj
, pdsrc
, DROPEFFECT_MOVE
|DROPEFFECT_COPY
, &dwEffect
);
1589 void CRepositoryBrowser::OnTvnBegindragRepotree(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1591 m_bRightDrag
= false;
1593 OnBeginDragTree(pNMHDR
);
1596 void CRepositoryBrowser::OnTvnBeginrdragRepotree(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1598 m_bRightDrag
= true;
1600 OnBeginDragTree(pNMHDR
);
1603 void CRepositoryBrowser::OnBeginDragTree(NMHDR
*pNMHDR
)
1605 LPNMTREEVIEW pNMTreeView
= reinterpret_cast<LPNMTREEVIEW
>(pNMHDR
);
1610 CTreeItem
* pTreeItem
= (CTreeItem
*)pNMTreeView
->itemNew
.lParam
;
1612 if (pTreeItem
== NULL
)
1615 CIDropSource
* pdsrc
= new CIDropSource
;
1620 CTSVNPathList sourceURLs
;
1621 sourceURLs
.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(pTreeItem
->url
))));
1623 SVNDataObject
* pdobj
= new SVNDataObject(sourceURLs
, GetRevision(), GetRevision());
1631 CDragSourceHelper dragsrchelper
;
1632 dragsrchelper
.InitializeFromWindow(m_RepoTree
.GetSafeHwnd(), pNMTreeView
->ptDrag
, pdobj
);
1633 // Initiate the Drag & Drop
1635 ::DoDragDrop(pdobj
, pdsrc
, DROPEFFECT_MOVE
|DROPEFFECT_COPY
, &dwEffect
);
1641 bool CRepositoryBrowser::OnDrop(const CTSVNPath
& target
, const CTSVNPathList
& pathlist
, const SVNRev
& srcRev
, DWORD dwEffect
, POINTL
/*pt*/)
1643 ATLTRACE(_T("dropped %ld items on %s, source revision is %s, dwEffect is %ld\n"), pathlist
.GetCount(), (LPCTSTR
)target
.GetSVNPathString(), srcRev
.ToString(), dwEffect
);
1644 if (pathlist
.GetCount() == 0)
1647 CString targetName
= pathlist
[0].GetFileOrDirectoryName();
1650 // right dragging means we have to show a context menu
1652 DWORD ptW
= GetMessagePos();
1653 pt
.x
= GET_X_LPARAM(ptW
);
1654 pt
.y
= GET_Y_LPARAM(ptW
);
1656 if (popup
.CreatePopupMenu())
1658 CString
temp(MAKEINTRESOURCE(IDS_REPOBROWSE_COPYDROP
));
1659 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, 1, temp
);
1660 temp
.LoadString(IDS_REPOBROWSE_MOVEDROP
);
1661 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, 2, temp
);
1662 if ((pathlist
.GetCount() == 1)&&(PathIsURL(pathlist
[0])))
1664 // these entries are only shown if *one* item was dragged, and if the
1665 // item is not one dropped from e.g. the explorer but from the repository
1667 popup
.AppendMenu(MF_SEPARATOR
, 3);
1668 temp
.LoadString(IDS_REPOBROWSE_COPYRENAMEDROP
);
1669 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, 4, temp
);
1670 temp
.LoadString(IDS_REPOBROWSE_MOVERENAMEDROP
);
1671 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, 5, temp
);
1673 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, pt
.x
, pt
.y
, this, 0);
1676 default:// nothing clicked
1678 case 1: // copy drop
1679 dwEffect
= DROPEFFECT_COPY
;
1681 case 2: // move drop
1682 dwEffect
= DROPEFFECT_MOVE
;
1684 case 4: // copy rename drop
1686 dwEffect
= DROPEFFECT_COPY
;
1688 dlg
.m_name
= targetName
;
1689 dlg
.m_windowtitle
.LoadString(IDS_REPOBROWSE_RENAME
);
1690 CStringUtils::RemoveAccelerators(dlg
.m_windowtitle
);
1691 if (dlg
.DoModal() != IDOK
)
1693 targetName
= dlg
.m_name
;
1694 if (!CTSVNPath(targetName
).IsValidOnWindows())
1696 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH
, IDS_APPNAME
, MB_ICONINFORMATION
|MB_YESNO
) != IDYES
)
1701 case 5: // move rename drop
1703 dwEffect
= DROPEFFECT_MOVE
;
1705 dlg
.m_name
= targetName
;
1706 dlg
.m_windowtitle
.LoadString(IDS_REPOBROWSE_RENAME
);
1707 CStringUtils::RemoveAccelerators(dlg
.m_windowtitle
);
1708 if (dlg
.DoModal() != IDOK
)
1710 targetName
= dlg
.m_name
;
1711 if (!CTSVNPath(targetName
).IsValidOnWindows())
1713 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH
, IDS_APPNAME
, MB_ICONINFORMATION
|MB_YESNO
) != IDYES
)
1722 // check the first item in the path list:
1723 // if it's an url, we do a copy or move operation
1724 // if it's a local path, we do an import
1725 if (PathIsURL(pathlist
[0]))
1727 // If any of the paths are 'special' (branches, tags, or trunk) and we are
1728 // about to perform a move, we should warn the user and get them to confirm
1729 // that this is what they intended. Yes, I *have* accidentally moved the
1730 // trunk when I was trying to create a tag! :)
1731 if (DROPEFFECT_COPY
!= dwEffect
)
1733 bool pathListIsSpecial
= false;
1734 int pathCount
= pathlist
.GetCount();
1735 for (int i
=0 ; i
<pathCount
; ++i
)
1737 const CTSVNPath
& path
= pathlist
[i
];
1738 if (path
.IsSpecialDirectory())
1740 pathListIsSpecial
= true;
1744 if (pathListIsSpecial
)
1746 UINT msgResult
= CMessageBox::Show(GetSafeHwnd(), IDS_WARN_CONFIRM_MOVE_SPECIAL_DIRECTORY
, IDS_APPNAME
, MB_ICONQUESTION
| MB_YESNO
);
1747 if (IDYES
!= msgResult
)
1754 // drag-n-drop inside the repobrowser
1755 CInputLogDlg
input(this);
1756 input
.SetUUID(m_sUUID
);
1757 input
.SetProjectProperties(&m_ProjectProperties
);
1759 if (pathlist
.GetCount() == 1)
1761 if (dwEffect
== DROPEFFECT_COPY
)
1762 sHint
.Format(IDS_INPUT_COPY
, (LPCTSTR
)pathlist
[0].GetSVNPathString(), (LPCTSTR
)(target
.GetSVNPathString()+_T("/")+targetName
));
1764 sHint
.Format(IDS_INPUT_MOVE
, (LPCTSTR
)pathlist
[0].GetSVNPathString(), (LPCTSTR
)(target
.GetSVNPathString()+_T("/")+targetName
));
1768 if (dwEffect
== DROPEFFECT_COPY
)
1769 sHint
.Format(IDS_INPUT_COPYMORE
, pathlist
.GetCount(), (LPCTSTR
)target
.GetSVNPathString());
1771 sHint
.Format(IDS_INPUT_MOVEMORE
, pathlist
.GetCount(), (LPCTSTR
)target
.GetSVNPathString());
1773 input
.SetActionText(sHint
);
1774 if (input
.DoModal() == IDOK
)
1776 m_bCancelled
= false;
1777 CWaitCursorEx wait_cursor
;
1779 if (dwEffect
== DROPEFFECT_COPY
)
1780 if (pathlist
.GetCount() == 1)
1781 bRet
= Copy(pathlist
, CTSVNPath(target
.GetSVNPathString() + _T("/") + targetName
), srcRev
, srcRev
, input
.GetLogMessage(), false);
1783 bRet
= Copy(pathlist
, target
, srcRev
, srcRev
, input
.GetLogMessage(), true);
1785 if (pathlist
.GetCount() == 1)
1786 bRet
= Move(pathlist
, CTSVNPath(target
.GetSVNPathString() + _T("/") + targetName
), TRUE
, input
.GetLogMessage(), false);
1788 bRet
= Move(pathlist
, target
, TRUE
, input
.GetLogMessage(), true);
1792 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
1794 else if (GetRevision().IsHead())
1796 // mark the target as dirty
1797 HTREEITEM hTarget
= FindUrl(target
.GetSVNPathString(), false);
1800 CTreeItem
* pItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hTarget
);
1803 // mark the target as 'dirty'
1804 pItem
->children_fetched
= false;
1805 RecursiveRemove(hTarget
, true);
1806 TVITEM tvitem
= {0};
1807 tvitem
.hItem
= hTarget
;
1808 tvitem
.mask
= TVIF_CHILDREN
;
1809 tvitem
.cChildren
= 1;
1810 m_RepoTree
.SetItem(&tvitem
);
1813 if (dwEffect
== DROPEFFECT_MOVE
)
1815 // if items were moved, we have to
1816 // invalidate all sources too
1817 for (int i
=0; i
<pathlist
.GetCount(); ++i
)
1819 HTREEITEM hSource
= FindUrl(pathlist
[i
].GetSVNPathString(), false);
1822 CTreeItem
* pItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hSource
);
1825 // the source has moved, so remove it!
1826 RecursiveRemove(hSource
);
1827 m_RepoTree
.DeleteItem(hSource
);
1833 // if the copy/move operation was to the currently shown url,
1834 // update the current view. Otherwise mark the target URL as 'not fetched'.
1835 HTREEITEM hSelected
= m_RepoTree
.GetSelectedItem();
1838 CTreeItem
* pItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hSelected
);
1841 // mark the target as 'dirty'
1842 pItem
->children_fetched
= false;
1843 if ((dwEffect
== DROPEFFECT_MOVE
)||(pItem
->url
.Compare(target
.GetSVNPathString())==0))
1845 // Refresh the current view
1846 RefreshNode(hSelected
, true);
1855 // import files dragged onto us
1856 if (pathlist
.GetCount() > 1)
1858 if (CMessageBox::Show(m_hWnd
, IDS_REPOBROWSE_MULTIIMPORT
, IDS_APPNAME
, MB_YESNO
| MB_ICONQUESTION
)!=IDYES
)
1862 CInputLogDlg
input(this);
1863 input
.SetProjectProperties(&m_ProjectProperties
);
1864 input
.SetUUID(m_sUUID
);
1866 if (pathlist
.GetCount() == 1)
1867 sHint
.Format(IDS_INPUT_IMPORTFILEFULL
, pathlist
[0].GetWinPath(), (LPCTSTR
)(target
.GetSVNPathString() + _T("/") + pathlist
[0].GetFileOrDirectoryName()));
1869 sHint
.Format(IDS_INPUT_IMPORTFILES
, pathlist
.GetCount());
1870 input
.SetActionText(sHint
);
1872 if (input
.DoModal() == IDOK
)
1874 m_bCancelled
= false;
1875 for (int importindex
= 0; importindex
<pathlist
.GetCount(); ++importindex
)
1877 CString filename
= pathlist
[importindex
].GetFileOrDirectoryName();
1878 if (!Import(pathlist
[importindex
],
1879 CTSVNPath(target
.GetSVNPathString()+_T("/")+filename
),
1880 input
.GetLogMessage(), &m_ProjectProperties
, svn_depth_infinity
, TRUE
, FALSE
))
1882 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
1886 if (GetRevision().IsHead())
1888 // if the import operation was to the currently shown url,
1889 // update the current view. Otherwise mark the target URL as 'not fetched'.
1890 HTREEITEM hSelected
= m_RepoTree
.GetSelectedItem();
1893 CTreeItem
* pItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hSelected
);
1896 if (pItem
->url
.Compare(target
.GetSVNPathString())==0)
1898 // Refresh the current view
1899 RefreshNode(hSelected
, true);
1903 // only mark the target as 'dirty'
1904 pItem
->children_fetched
= false;
1915 CString
CRepositoryBrowser::EscapeUrl(const CTSVNPath
& url
)
1917 return CUnicodeUtils::GetUnicode(CPathUtils::PathEscape(CUnicodeUtils::GetUTF8(url
.GetSVNPathString())));
1920 void CRepositoryBrowser::OnContextMenu(CWnd
* pWnd
, CPoint point
)
1922 HTREEITEM hSelectedTreeItem
= NULL
;
1923 HTREEITEM hChosenTreeItem
= NULL
;
1924 if ((point
.x
== -1) && (point
.y
== -1))
1926 if (pWnd
== &m_RepoTree
)
1929 m_RepoTree
.GetItemRect(m_RepoTree
.GetSelectedItem(), &rect
, TRUE
);
1930 m_RepoTree
.ClientToScreen(&rect
);
1931 point
= rect
.CenterPoint();
1936 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
1937 m_RepoList
.GetItemRect(m_RepoList
.GetNextSelectedItem(pos
), &rect
, LVIR_LABEL
);
1938 m_RepoList
.ClientToScreen(&rect
);
1939 point
= rect
.CenterPoint();
1942 m_bCancelled
= false;
1943 CTSVNPathList urlList
;
1944 CTSVNPathList urlListEscaped
;
1947 if (pWnd
== &m_RepoList
)
1951 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
1953 while ((index
= m_RepoList
.GetNextSelectedItem(pos
))>=0)
1955 CItem
* pItem
= (CItem
*)m_RepoList
.GetItemData(index
);
1956 CString absPath
= pItem
->absolutepath
;
1957 absPath
.Replace(_T("\\"), _T("%5C"));
1958 urlList
.AddPath(CTSVNPath(absPath
));
1959 urlListEscaped
.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(absPath
))));
1960 if (pItem
->kind
== svn_node_dir
)
1962 if (!pItem
->locktoken
.IsEmpty())
1965 if (urlList
.GetCount() == 0)
1967 // Right-click outside any list control items. It may be the background,
1968 // but it also could be the list control headers.
1970 m_RepoList
.GetHeaderCtrl()->GetWindowRect(&hr
);
1971 if (!hr
.PtInRect(point
))
1973 // Seems to be a right-click on the list view background.
1974 // Use the currently selected item in the tree view as the source.
1975 m_blockEvents
= true;
1976 hSelectedTreeItem
= m_RepoTree
.GetSelectedItem();
1977 if (hSelectedTreeItem
)
1979 m_RepoTree
.SetItemState(hSelectedTreeItem
, 0, TVIS_SELECTED
);
1980 m_blockEvents
= false;
1981 m_RepoTree
.SetItemState(hSelectedTreeItem
, TVIS_DROPHILITED
, TVIS_DROPHILITED
);
1982 CTreeItem
* pTreeItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hSelectedTreeItem
);
1985 urlList
.AddPath(CTSVNPath(pTreeItem
->url
));
1986 urlListEscaped
.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(pTreeItem
->url
))));
1993 if ((pWnd
== &m_RepoTree
)||(urlList
.GetCount() == 0))
1996 CPoint ptTree
= point
;
1997 m_RepoTree
.ScreenToClient(&ptTree
);
1998 HTREEITEM hItem
= m_RepoTree
.HitTest(ptTree
, &uFlags
);
1999 // in case the right-clicked item is not the selected one,
2000 // use the TVIS_DROPHILITED style to indicate on which item
2001 // the context menu will work on
2002 if ((hItem
) && (uFlags
& TVHT_ONITEM
) && (hItem
!= m_RepoTree
.GetSelectedItem()))
2004 m_blockEvents
= true;
2005 hSelectedTreeItem
= m_RepoTree
.GetSelectedItem();
2006 m_RepoTree
.SetItemState(hSelectedTreeItem
, 0, TVIS_SELECTED
);
2007 m_blockEvents
= false;
2008 m_RepoTree
.SetItemState(hItem
, TVIS_DROPHILITED
, TVIS_DROPHILITED
);
2012 hChosenTreeItem
= hItem
;
2013 CTreeItem
* pTreeItem
= (CTreeItem
*)m_RepoTree
.GetItemData(hItem
);
2016 urlList
.AddPath(CTSVNPath(pTreeItem
->url
));
2017 urlListEscaped
.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(pTreeItem
->url
))));
2023 if (urlList
.GetCount() == 0)
2027 if (popup
.CreatePopupMenu())
2029 if (urlList
.GetCount() == 1)
2033 // Let "Open" be the very first entry, like in Explorer
2034 popup
.AppendMenuIcon(ID_OPEN
, IDS_REPOBROWSE_OPEN
, IDI_OPEN
); // "open"
2035 popup
.AppendMenuIcon(ID_OPENWITH
, IDS_LOG_POPUP_OPENWITH
, IDI_OPEN
); // "open with..."
2036 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2038 popup
.AppendMenuIcon(ID_SHOWLOG
, IDS_REPOBROWSE_SHOWLOG
, IDI_LOG
); // "Show Log..."
2039 // the revision graph on the repository root would be empty. We
2040 // don't show the context menu entry there.
2041 if (urlList
[0].GetSVNPathString().Compare(m_strReposRoot
)!=0)
2043 popup
.AppendMenuIcon(ID_REVGRAPH
, IDS_MENUREVISIONGRAPH
, IDI_REVISIONGRAPH
); // "Revision graph"
2047 popup
.AppendMenuIcon(ID_BLAME
, IDS_MENUBLAME
, IDI_BLAME
); // "Blame..."
2049 if (!m_ProjectProperties
.sWebViewerRev
.IsEmpty())
2051 popup
.AppendMenuIcon(ID_VIEWREV
, IDS_LOG_POPUP_VIEWREV
); // "View revision in webviewer"
2053 if (!m_ProjectProperties
.sWebViewerPathRev
.IsEmpty())
2055 popup
.AppendMenuIcon(ID_VIEWPATHREV
, IDS_LOG_POPUP_VIEWPATHREV
); // "View revision for path in webviewer"
2057 if ((!m_ProjectProperties
.sWebViewerPathRev
.IsEmpty())||
2058 (!m_ProjectProperties
.sWebViewerRev
.IsEmpty()))
2060 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2064 popup
.AppendMenuIcon(ID_EXPORT
, IDS_MENUEXPORT
, IDI_EXPORT
); // "Export"
2067 // We allow checkout of multiple folders at once (we do that one by one)
2068 if (nFolders
== urlList
.GetCount())
2070 popup
.AppendMenuIcon(ID_CHECKOUT
, IDS_MENUCHECKOUT
, IDI_CHECKOUT
); // "Checkout.."
2072 if (urlList
.GetCount() == 1)
2076 popup
.AppendMenuIcon(ID_REFRESH
, IDS_REPOBROWSE_REFRESH
, IDI_REFRESH
); // "Refresh"
2078 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2080 if (GetRevision().IsHead())
2084 popup
.AppendMenuIcon(ID_MKDIR
, IDS_REPOBROWSE_MKDIR
, IDI_MKDIR
); // "create directory"
2085 popup
.AppendMenuIcon(ID_IMPORT
, IDS_REPOBROWSE_IMPORT
, IDI_IMPORT
); // "Add/Import File"
2086 popup
.AppendMenuIcon(ID_IMPORTFOLDER
, IDS_REPOBROWSE_IMPORTFOLDER
, IDI_IMPORT
); // "Add/Import Folder"
2087 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2090 popup
.AppendMenuIcon(ID_RENAME
, IDS_REPOBROWSE_RENAME
, IDI_RENAME
); // "Rename"
2094 popup
.AppendMenuIcon(ID_BREAKLOCK
, IDS_MENU_UNLOCKFORCE
, IDI_UNLOCK
); // "Break Lock"
2097 if (urlList
.GetCount() > 0)
2099 if (GetRevision().IsHead())
2101 popup
.AppendMenuIcon(ID_DELETE
, IDS_REPOBROWSE_DELETE
, IDI_DELETE
); // "Remove"
2105 popup
.AppendMenuIcon(ID_SAVEAS
, IDS_REPOBROWSE_SAVEAS
, IDI_SAVEAS
); // "Save as..."
2107 if ((urlList
.GetCount() == nFolders
)||(nFolders
== 0))
2109 popup
.AppendMenuIcon(ID_COPYTOWC
, IDS_REPOBROWSE_COPYTOWC
); // "Copy To Working Copy..."
2112 if (urlList
.GetCount() == 1)
2114 popup
.AppendMenuIcon(ID_COPYTO
, IDS_REPOBROWSE_COPY
, IDI_COPY
); // "Copy To..."
2115 popup
.AppendMenuIcon(ID_URLTOCLIPBOARD
, IDS_REPOBROWSE_URLTOCLIPBOARD
, IDI_COPYCLIP
); // "Copy URL to clipboard"
2116 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2117 popup
.AppendMenuIcon(ID_PROPS
, IDS_REPOBROWSE_SHOWPROP
, IDI_PROPERTIES
); // "Show Properties"
2118 // Revision properties are not associated to paths
2119 // so we only show that context menu on the repository root
2120 if (urlList
[0].GetSVNPathString().Compare(m_strReposRoot
)==0)
2122 popup
.AppendMenuIcon(ID_REVPROPS
, IDS_REPOBROWSE_SHOWREVPROP
, IDI_PROPERTIES
); // "Show Revision Properties"
2126 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2127 popup
.AppendMenuIcon(ID_PREPAREDIFF
, IDS_REPOBROWSE_PREPAREDIFF
); // "Mark for comparison"
2129 if ((m_diffKind
== svn_node_dir
)&&(!m_diffURL
.IsEquivalentTo(urlList
[0])))
2131 popup
.AppendMenuIcon(ID_GNUDIFF
, IDS_LOG_POPUP_GNUDIFF
, IDI_DIFF
); // "Show differences as unified diff"
2132 popup
.AppendMenuIcon(ID_DIFF
, IDS_REPOBROWSE_SHOWDIFF
, IDI_DIFF
); // "Compare URLs"
2136 if (urlList
.GetCount() == 2)
2138 if ((nFolders
== 2)||(nFolders
== 0))
2140 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2141 popup
.AppendMenuIcon(ID_GNUDIFF
, IDS_LOG_POPUP_GNUDIFF
, IDI_DIFF
); // "Show differences as unified diff"
2142 popup
.AppendMenuIcon(ID_DIFF
, IDS_REPOBROWSE_SHOWDIFF
, ID_DIFF
); // "Compare URLs"
2143 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2145 popup
.AppendMenuIcon(ID_SHOWLOG
, IDS_MENULOG
, IDI_LOG
); // "Show Log..."
2147 if ((urlList
.GetCount() == 1) &&
2149 CTSVNPath(m_InitialUrl
).IsAncestorOf(urlList
[0]))
2151 CTSVNPath wcPath
= m_path
;
2152 wcPath
.AppendPathString(urlList
[0].GetWinPathString().Mid(m_InitialUrl
.GetLength()));
2153 if (!wcPath
.Exists())
2155 bool bWCPresent
= false;
2156 while (!bWCPresent
&& m_path
.IsAncestorOf(wcPath
))
2158 bWCPresent
= wcPath
.GetContainingDirectory().Exists();
2159 wcPath
= wcPath
.GetContainingDirectory();
2163 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2164 popup
.AppendMenuIcon(ID_UPDATE
, IDS_LOG_POPUP_UPDATE
, IDI_UPDATE
); // "Update item to revision"
2168 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
2170 if (pWnd
== &m_RepoTree
)
2173 CPoint ptTree
= point
;
2174 m_RepoTree
.ScreenToClient(&ptTree
);
2175 HTREEITEM hItem
= m_RepoTree
.HitTest(ptTree
, &uFlags
);
2176 // restore the previously selected item state
2177 if ((hItem
) && (uFlags
& TVHT_ONITEM
) && (hItem
!= m_RepoTree
.GetSelectedItem()))
2179 m_blockEvents
= true;
2180 m_RepoTree
.SetItemState(hSelectedTreeItem
, TVIS_SELECTED
, TVIS_SELECTED
);
2181 m_blockEvents
= false;
2182 m_RepoTree
.SetItemState(hItem
, 0, TVIS_DROPHILITED
);
2185 if (hSelectedTreeItem
)
2187 m_blockEvents
= true;
2188 m_RepoTree
.SetItemState(hSelectedTreeItem
, 0, TVIS_DROPHILITED
);
2189 m_RepoTree
.SetItemState(hSelectedTreeItem
, TVIS_SELECTED
, TVIS_SELECTED
);
2190 m_blockEvents
= false;
2192 DialogEnableWindow(IDOK
, FALSE
);
2193 bool bOpenWith
= false;
2198 CTSVNPath wcPath
= m_path
;
2199 wcPath
.AppendPathString(urlList
[0].GetWinPathString().Mid(m_InitialUrl
.GetLength()));
2201 sCmd
.Format(_T("\"%s\" /command:update /path:\"%s\" /rev"),
2202 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), wcPath
.GetWinPath());
2204 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
2207 case ID_PREPAREDIFF
:
2209 m_RepoTree
.SetItemState(FindUrl(m_diffURL
.GetSVNPathString(), false), 0, TVIS_BOLD
);
2210 if (urlList
.GetCount() == 1)
2212 m_diffURL
= urlList
[0];
2213 m_diffKind
= nFolders
? svn_node_dir
: svn_node_file
;
2214 // make the marked tree item bold
2215 if (m_diffKind
== svn_node_dir
)
2217 m_RepoTree
.SetItemState(FindUrl(m_diffURL
.GetSVNPathString(), false), TVIS_BOLD
, TVIS_BOLD
);
2223 m_diffKind
= svn_node_none
;
2227 case ID_URLTOCLIPBOARD
:
2230 for (int i
=0; i
<urlList
.GetCount(); ++i
)
2231 url
+= CUnicodeUtils::GetUnicode(CPathUtils::PathEscape(CUnicodeUtils::GetUTF8(urlList
[i
].GetSVNPathString()))) + _T("\r\n");
2232 url
.TrimRight(_T("\r\n"));
2233 CStringUtils::WriteAsciiStringToClipboard(url
);
2239 bool bSavePathOK
= AskForSavePath(urlList
, tempfile
, nFolders
> 0);
2242 CWaitCursorEx wait_cursor
;
2245 CProgressDlg progDlg
;
2246 int counter
= 0; // the file counter
2247 progDlg
.SetTitle(IDS_REPOBROWSE_SAVEASPROGTITLE
);
2248 progDlg
.SetAnimation(IDR_DOWNLOAD
);
2249 progDlg
.ShowModeless(GetSafeHwnd());
2250 progDlg
.SetProgress((DWORD
)0, (DWORD
)urlList
.GetCount());
2251 SetAndClearProgressInfo(&progDlg
);
2252 for (int i
=0; i
<urlList
.GetCount(); ++i
)
2254 saveurl
= EscapeUrl(urlList
[i
]);
2255 CTSVNPath savepath
= tempfile
;
2256 if (tempfile
.IsDirectory())
2257 savepath
.AppendPathString(urlList
[i
].GetFileOrDirectoryName());
2259 sInfoLine
.Format(IDS_PROGRESSGETFILEREVISION
, (LPCTSTR
)saveurl
, (LPCTSTR
)GetRevision().ToString());
2260 progDlg
.SetLine(1, sInfoLine
, true);
2261 if (!Cat(CTSVNPath(saveurl
), GetRevision(), GetRevision(), savepath
)||(progDlg
.HasUserCancelled()))
2265 SetAndClearProgressInfo((HWND
)NULL
);
2266 if (!progDlg
.HasUserCancelled())
2267 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
2271 progDlg
.SetProgress((DWORD
)counter
, (DWORD
)urlList
.GetCount());
2274 SetAndClearProgressInfo((HWND
)NULL
);
2280 if (urlList
.GetCount() == 2)
2282 // get log of first URL
2283 CString sCopyFrom1
, sCopyFrom2
;
2284 SVNLogHelper helper
;
2285 helper
.SetRepositoryRoot(m_strReposRoot
);
2286 SVNRev rev1
= helper
.GetCopyFromRev(CTSVNPath(EscapeUrl(urlList
[0])), GetRevision(), sCopyFrom1
);
2287 if (!rev1
.IsValid())
2289 CMessageBox::Show(this->m_hWnd
, helper
.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
2292 SVNRev rev2
= helper
.GetCopyFromRev(CTSVNPath(EscapeUrl(urlList
[1])), GetRevision(), sCopyFrom2
);
2293 if (!rev2
.IsValid())
2295 CMessageBox::Show(this->m_hWnd
, helper
.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
2298 if ((sCopyFrom1
.IsEmpty())||(sCopyFrom1
.Compare(sCopyFrom2
)!=0))
2300 // no common copy from URL, so showing a log between
2301 // the two urls is not possible.
2302 CMessageBox::Show(m_hWnd
, IDS_ERR_NOCOMMONCOPYFROM
, IDS_APPNAME
, MB_ICONERROR
);
2305 if ((LONG
)rev1
< (LONG
)rev2
)
2312 sCmd
.Format(_T("\"%s\" /command:log /path:\"%s\" /startrev:%s /endrev:%s"),
2313 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), (LPCTSTR
)sCopyFrom1
, (LPCTSTR
)rev1
.ToString(), (LPCTSTR
)rev2
.ToString());
2316 if (!m_path
.IsUrl())
2318 sCmd
+= _T(" /propspath:\"");
2319 sCmd
+= m_path
.GetWinPathString();
2323 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
2328 sCmd
.Format(_T("\"%s\" /command:log /path:\"%s\" /startrev:%s"),
2329 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), (LPCTSTR
)EscapeUrl(urlList
[0]), (LPCTSTR
)GetRevision().ToString());
2331 if (!m_path
.IsUrl())
2333 sCmd
+= _T(" /propspath:\"");
2334 sCmd
+= m_path
.GetWinPathString();
2338 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
2344 CString url
= m_ProjectProperties
.sWebViewerRev
;
2345 url
.Replace(_T("%REVISION%"), GetRevision().ToString());
2347 ShellExecute(this->m_hWnd
, _T("open"), url
, NULL
, NULL
, SW_SHOWDEFAULT
);
2350 case ID_VIEWPATHREV
:
2352 CString relurl
= EscapeUrl(urlList
[0]);
2353 relurl
= relurl
.Mid(m_strReposRoot
.GetLength());
2354 CString weburl
= m_ProjectProperties
.sWebViewerPathRev
;
2355 weburl
.Replace(_T("%REVISION%"), GetRevision().ToString());
2356 weburl
.Replace(_T("%PATH%"), relurl
);
2357 if (!weburl
.IsEmpty())
2358 ShellExecute(this->m_hWnd
, _T("open"), weburl
, NULL
, NULL
, SW_SHOWDEFAULT
);
2363 CString itemsToCheckout
;
2364 for (int i
=0; i
<urlList
.GetCount(); ++i
)
2366 itemsToCheckout
+= EscapeUrl(urlList
[i
]) + _T("*");
2368 itemsToCheckout
.TrimRight('*');
2370 sCmd
.Format(_T("\"%s\" /command:checkout /url:\"%s\" /revision:%s"),
2371 (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), (LPCTSTR
)itemsToCheckout
, (LPCTSTR
)GetRevision().ToString());
2373 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
2379 dlg
.m_URL
= EscapeUrl(urlList
[0]);
2380 dlg
.Revision
= GetRevision();
2381 if (dlg
.DoModal()==IDOK
)
2383 CTSVNPath exportDirectory
;
2384 exportDirectory
.SetFromWin(dlg
.m_strExportDirectory
, true);
2386 CSVNProgressDlg progDlg
;
2388 if (dlg
.m_bNoExternals
)
2389 opts
|= ProgOptIgnoreExternals
;
2390 if (dlg
.m_eolStyle
.CompareNoCase(_T("CRLF"))==0)
2391 opts
|= ProgOptEolCRLF
;
2392 if (dlg
.m_eolStyle
.CompareNoCase(_T("CR"))==0)
2393 opts
|= ProgOptEolCR
;
2394 if (dlg
.m_eolStyle
.CompareNoCase(_T("LF"))==0)
2395 opts
|= ProgOptEolLF
;
2396 progDlg
.SetCommand(CSVNProgressDlg::SVNProgress_Export
);
2397 progDlg
.SetOptions(opts
);
2398 progDlg
.SetPathList(CTSVNPathList(exportDirectory
));
2399 progDlg
.SetUrl(dlg
.m_URL
);
2400 progDlg
.SetRevision(dlg
.Revision
);
2401 progDlg
.SetDepth(dlg
.m_depth
);
2408 CRevisionGraphDlg dlg
;
2409 dlg
.SetPath(EscapeUrl(urlList
[0]));
2410 dlg
.SetPegRevision(GetRevision());
2418 // if we're on HEAD and the repository is available via http or https,
2419 // we just open the browser with that url.
2420 if (GetRevision().IsHead() && (bOpenWith
==false))
2422 if (urlList
[0].GetSVNPathString().Left(4).CompareNoCase(_T("http")) == 0)
2424 CString sBrowserUrl
= EscapeUrl(urlList
[0]);
2426 ShellExecute(NULL
, _T("open"), sBrowserUrl
, NULL
, NULL
, SW_SHOWNORMAL
);
2430 // in all other cases, we have to 'cat' the file and open it.
2431 CTSVNPath tempfile
= CTempFiles::Instance().GetTempFilePath(false, urlList
[0], GetRevision());
2432 CWaitCursorEx wait_cursor
;
2433 CProgressDlg progDlg
;
2434 progDlg
.SetTitle(IDS_APPNAME
);
2435 progDlg
.SetAnimation(IDR_DOWNLOAD
);
2437 sInfoLine
.Format(IDS_PROGRESSGETFILEREVISION
, (LPCTSTR
)urlList
[0].GetFileOrDirectoryName(), (LPCTSTR
)GetRevision().ToString());
2438 progDlg
.SetLine(1, sInfoLine
, true);
2439 SetAndClearProgressInfo(&progDlg
);
2440 progDlg
.ShowModeless(m_hWnd
);
2441 if (!Cat(urlList
[0], GetRevision(), GetRevision(), tempfile
))
2444 SetAndClearProgressInfo((HWND
)NULL
);
2446 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
2450 SetAndClearProgressInfo((HWND
)NULL
);
2451 // set the file as read-only to tell the app which opens the file that it's only
2452 // a temporary file and must not be edited.
2453 SetFileAttributes(tempfile
.GetWinPath(), FILE_ATTRIBUTE_READONLY
);
2456 int ret
= (int)ShellExecute(NULL
, _T("open"), tempfile
.GetWinPathString(), NULL
, NULL
, SW_SHOWNORMAL
);
2457 if (ret
<= HINSTANCE_ERROR
)
2462 CString cmd
= _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
2463 cmd
+= tempfile
.GetWinPathString() + _T(" ");
2464 CAppUtils::LaunchApplication(cmd
, NULL
, false);
2470 CWaitCursorEx wait_cursor
;
2471 CInputLogDlg
input(this);
2472 input
.SetUUID(m_sUUID
);
2473 input
.SetProjectProperties(&m_ProjectProperties
);
2475 if (urlList
.GetCount() == 1)
2476 hint
.Format(IDS_INPUT_REMOVEONE
, (LPCTSTR
)urlList
[0].GetFileOrDirectoryName());
2478 hint
.Format(IDS_INPUT_REMOVEMORE
, urlList
.GetCount());
2479 input
.SetActionText(hint
);
2480 if (input
.DoModal() == IDOK
)
2482 if (!Remove(urlList
, true, false, input
.GetLogMessage()))
2485 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
2488 if (hChosenTreeItem
)
2490 HTREEITEM hParent
= m_RepoTree
.GetParentItem(hChosenTreeItem
);
2491 RecursiveRemove(hChosenTreeItem
);
2492 RefreshNode(hParent
);
2495 RefreshNode(m_RepoTree
.GetSelectedItem(), true);
2501 if (!Unlock(urlListEscaped
, TRUE
))
2503 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
2506 RefreshNode(m_RepoTree
.GetSelectedItem(), true);
2509 case ID_IMPORTFOLDER
:
2512 CBrowseFolder folderBrowser
;
2513 folderBrowser
.m_style
= BIF_EDITBOX
| BIF_NEWDIALOGSTYLE
| BIF_RETURNFSANCESTORS
| BIF_RETURNONLYFSDIRS
;
2514 if (folderBrowser
.Show(GetSafeHwnd(), path
)==CBrowseFolder::OK
)
2516 CTSVNPath
svnPath(path
);
2517 CWaitCursorEx wait_cursor
;
2518 CString filename
= svnPath
.GetFileOrDirectoryName();
2519 CInputLogDlg
input(this);
2520 input
.SetUUID(m_sUUID
);
2521 input
.SetProjectProperties(&m_ProjectProperties
);
2523 sHint
.Format(IDS_INPUT_IMPORTFOLDER
, (LPCTSTR
)svnPath
.GetSVNPathString(), (LPCTSTR
)(urlList
[0].GetSVNPathString()+_T("/")+filename
));
2524 input
.SetActionText(sHint
);
2525 if (input
.DoModal() == IDOK
)
2527 CProgressDlg progDlg
;
2528 progDlg
.SetTitle(IDS_APPNAME
);
2530 sInfoLine
.Format(IDS_PROGRESSIMPORT
, (LPCTSTR
)filename
);
2531 progDlg
.SetLine(1, sInfoLine
, true);
2532 SetAndClearProgressInfo(&progDlg
);
2533 progDlg
.ShowModeless(m_hWnd
);
2534 if (!Import(svnPath
,
2535 CTSVNPath(EscapeUrl(CTSVNPath(urlList
[0].GetSVNPathString()+_T("/")+filename
))),
2536 input
.GetLogMessage(),
2537 &m_ProjectProperties
,
2542 SetAndClearProgressInfo((HWND
)NULL
);
2544 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
2548 SetAndClearProgressInfo((HWND
)NULL
);
2549 RefreshNode(m_RepoTree
.GetSelectedItem(), true);
2556 // Display the Open dialog box.
2558 if (CAppUtils::FileOpenSave(openPath
, NULL
, IDS_REPOBROWSE_IMPORT
, IDS_COMMONFILEFILTER
, true, m_hWnd
))
2560 CTSVNPath
path(openPath
);
2561 CWaitCursorEx wait_cursor
;
2562 CString filename
= path
.GetFileOrDirectoryName();
2563 CInputLogDlg
input(this);
2564 input
.SetUUID(m_sUUID
);
2565 input
.SetProjectProperties(&m_ProjectProperties
);
2567 sHint
.Format(IDS_INPUT_IMPORTFILEFULL
, path
.GetWinPath(), (LPCTSTR
)(urlList
[0].GetSVNPathString()+_T("/")+filename
));
2568 input
.SetActionText(sHint
);
2569 if (input
.DoModal() == IDOK
)
2571 CProgressDlg progDlg
;
2572 progDlg
.SetTitle(IDS_APPNAME
);
2574 sInfoLine
.Format(IDS_PROGRESSIMPORT
, (LPCTSTR
)filename
);
2575 progDlg
.SetLine(1, sInfoLine
, true);
2576 SetAndClearProgressInfo(&progDlg
);
2577 progDlg
.ShowModeless(m_hWnd
);
2579 CTSVNPath(EscapeUrl(CTSVNPath(urlList
[0].GetSVNPathString()+_T("/")+filename
))),
2580 input
.GetLogMessage(),
2581 &m_ProjectProperties
,
2586 SetAndClearProgressInfo((HWND
)NULL
);
2588 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
2592 SetAndClearProgressInfo((HWND
)NULL
);
2593 RefreshNode(m_RepoTree
.GetSelectedItem(), true);
2600 if (pWnd
== &m_RepoList
)
2602 POSITION pos
= m_RepoList
.GetFirstSelectedItemPosition();
2603 int selIndex
= m_RepoList
.GetNextSelectedItem(pos
);
2606 m_RepoList
.SetFocus();
2607 m_RepoList
.EditLabel(selIndex
);
2611 m_RepoTree
.SetFocus();
2612 HTREEITEM hTreeItem
= m_RepoTree
.GetSelectedItem();
2613 if (hTreeItem
!= m_RepoTree
.GetRootItem())
2614 m_RepoTree
.EditLabel(hTreeItem
);
2617 else if (pWnd
== &m_RepoTree
)
2619 m_RepoTree
.SetFocus();
2620 if (hChosenTreeItem
!= m_RepoTree
.GetRootItem())
2621 m_RepoTree
.EditLabel(hChosenTreeItem
);
2628 dlg
.m_name
= urlList
[0].GetSVNPathString();
2629 dlg
.m_windowtitle
.LoadString(IDS_REPOBROWSE_COPY
);
2630 CStringUtils::RemoveAccelerators(dlg
.m_windowtitle
);
2631 if (dlg
.DoModal() == IDOK
)
2633 CWaitCursorEx wait_cursor
;
2634 CInputLogDlg
input(this);
2635 input
.SetUUID(m_sUUID
);
2636 input
.SetProjectProperties(&m_ProjectProperties
);
2638 sHint
.Format(IDS_INPUT_COPY
, (LPCTSTR
)urlList
[0].GetSVNPathString(), (LPCTSTR
)dlg
.m_name
);
2639 input
.SetActionText(sHint
);
2640 if (!CTSVNPath(dlg
.m_name
).IsValidOnWindows())
2642 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH
, IDS_APPNAME
, MB_ICONINFORMATION
|MB_YESNO
) != IDYES
)
2645 if (input
.DoModal() == IDOK
)
2647 if (!Copy(urlList
, CTSVNPath(dlg
.m_name
), GetRevision(), GetRevision(), input
.GetLogMessage()))
2650 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
2653 if (GetRevision().IsHead())
2655 RefreshNode(m_RepoTree
.GetSelectedItem(), true);
2664 bool bSavePathOK
= AskForSavePath(urlList
, tempfile
, nFolders
> 0);
2667 CWaitCursorEx wait_cursor
;
2669 CProgressDlg progDlg
;
2670 progDlg
.SetAnimation(IDR_DOWNLOAD
);
2671 progDlg
.SetTitle(IDS_APPNAME
);
2672 SetAndClearProgressInfo(&progDlg
);
2673 progDlg
.ShowModeless(m_hWnd
);
2675 bool bCopyAsChild
= (urlList
.GetCount() > 1);
2676 if (!Copy(urlList
, tempfile
, GetRevision(), GetRevision(), CString(), bCopyAsChild
)||(progDlg
.HasUserCancelled()))
2679 SetAndClearProgressInfo((HWND
)NULL
);
2682 if (!progDlg
.HasUserCancelled())
2683 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
2687 SetAndClearProgressInfo((HWND
)NULL
);
2694 dlg
.m_name
= _T("");
2695 dlg
.m_windowtitle
.LoadString(IDS_REPOBROWSE_MKDIR
);
2696 CStringUtils::RemoveAccelerators(dlg
.m_windowtitle
);
2697 if (dlg
.DoModal() == IDOK
)
2699 CWaitCursorEx wait_cursor
;
2700 CInputLogDlg
input(this);
2701 input
.SetUUID(m_sUUID
);
2702 input
.SetProjectProperties(&m_ProjectProperties
);
2704 sHint
.Format(IDS_INPUT_MKDIR
, (LPCTSTR
)(urlList
[0].GetSVNPathString()+_T("/")+dlg
.m_name
.Trim()));
2705 input
.SetActionText(sHint
);
2706 if (input
.DoModal() == IDOK
)
2708 // when creating the new folder, also trim any whitespace chars from it
2709 if (!MakeDir(CTSVNPathList(CTSVNPath(EscapeUrl(CTSVNPath(urlList
[0].GetSVNPathString()+_T("/")+dlg
.m_name
.Trim())))), input
.GetLogMessage(), true))
2712 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
2715 RefreshNode(m_RepoTree
.GetSelectedItem(), true);
2722 RefreshNode(urlList
[0].GetSVNPathString(), true);
2727 m_bCancelled
= false;
2728 SVNDiff
diff(this, this->m_hWnd
, true);
2729 if (urlList
.GetCount() == 1)
2732 diff
.ShowUnifiedDiff(CTSVNPath(EscapeUrl(urlList
[0])), GetRevision(),
2733 CTSVNPath(EscapeUrl(m_diffURL
)), GetRevision());
2735 CAppUtils::StartShowUnifiedDiff(m_hWnd
, CTSVNPath(EscapeUrl(urlList
[0])), GetRevision(),
2736 CTSVNPath(EscapeUrl(m_diffURL
)), GetRevision());
2741 diff
.ShowUnifiedDiff(CTSVNPath(EscapeUrl(urlList
[0])), GetRevision(),
2742 CTSVNPath(EscapeUrl(urlList
[1])), GetRevision());
2744 CAppUtils::StartShowUnifiedDiff(m_hWnd
, CTSVNPath(EscapeUrl(urlList
[0])), GetRevision(),
2745 CTSVNPath(EscapeUrl(urlList
[1])), GetRevision());
2751 m_bCancelled
= false;
2752 SVNDiff
diff(this, this->m_hWnd
, true);
2753 diff
.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT
) & 0x8000));
2754 if (urlList
.GetCount() == 1)
2757 diff
.ShowCompare(CTSVNPath(EscapeUrl(urlList
[0])), GetRevision(),
2758 CTSVNPath(EscapeUrl(m_diffURL
)), GetRevision(), SVNRev(), true);
2760 CAppUtils::StartShowCompare(m_hWnd
, CTSVNPath(EscapeUrl(urlList
[0])), GetRevision(),
2761 CTSVNPath(EscapeUrl(m_diffURL
)), GetRevision(), SVNRev(), SVNRev(),
2762 !!(GetAsyncKeyState(VK_SHIFT
) & 0x8000), true);
2767 diff
.ShowCompare(CTSVNPath(EscapeUrl(urlList
[0])), GetRevision(),
2768 CTSVNPath(EscapeUrl(urlList
[1])), GetRevision(), SVNRev(), true);
2770 CAppUtils::StartShowCompare(m_hWnd
, CTSVNPath(EscapeUrl(urlList
[0])), GetRevision(),
2771 CTSVNPath(EscapeUrl(urlList
[1])), GetRevision(), SVNRev(), SVNRev(),
2772 !!(GetAsyncKeyState(VK_SHIFT
) & 0x8000), true);
2778 if (GetRevision().IsHead())
2780 CEditPropertiesDlg dlg
;
2781 dlg
.SetProjectProperties(&m_ProjectProperties
);
2782 dlg
.SetUUID(m_sUUID
);
2783 CTSVNPathList escapedlist
;
2784 for (int i
=0; i
<urlList
.GetCount(); ++i
)
2786 escapedlist
.AddPath(CTSVNPath(EscapeUrl(urlList
[i
])));
2788 dlg
.SetPathList(escapedlist
);
2789 dlg
.SetRevision(GetHEADRevision(urlList
[0]));
2795 dlg
.m_rev
= GetRevision();
2796 dlg
.m_Path
= CTSVNPath(EscapeUrl(urlList
[0]));
2803 CEditPropertiesDlg dlg
;
2804 dlg
.SetProjectProperties(&m_ProjectProperties
);
2805 dlg
.SetUUID(m_sUUID
);
2806 CTSVNPathList escapedlist
;
2807 for (int i
=0; i
<urlList
.GetCount(); ++i
)
2809 escapedlist
.AddPath(CTSVNPath(EscapeUrl(urlList
[i
])));
2811 dlg
.SetPathList(escapedlist
);
2812 dlg
.SetRevision(GetRevision());
2820 dlg
.EndRev
= GetRevision();
2821 if (dlg
.DoModal() == IDOK
)
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
);
2827 if (!tempfile
.IsEmpty())
2829 if (dlg
.m_bTextView
)
2831 //open the default text editor for the result file
2832 CAppUtils::StartTextViewer(tempfile
);
2836 CString sParams
= _T("/path:\"") + urlList
[0].GetSVNPathString() + _T("\" ");
2837 if(!CAppUtils::LaunchTortoiseBlame(tempfile
, logfile
, CPathUtils::GetFileNameFromPath(urlList
[0].GetFileOrDirectoryName()),sParams
))
2845 CMessageBox::Show(this->m_hWnd
, blame
.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR
);
2853 DialogEnableWindow(IDOK
, TRUE
);
2858 bool CRepositoryBrowser::AskForSavePath(const CTSVNPathList
& urlList
, CTSVNPath
&tempfile
, bool bFolder
)
2860 bool bSavePathOK
= false;
2861 if ((!bFolder
)&&(urlList
.GetCount() == 1))
2863 CString savePath
= urlList
[0].GetFilename();
2864 bSavePathOK
= CAppUtils::FileOpenSave(savePath
, NULL
, IDS_REPOBROWSE_SAVEAS
, IDS_COMMONFILEFILTER
, false, m_hWnd
);
2866 tempfile
.SetFromWin(savePath
);
2870 CBrowseFolder browser
;
2872 browser
.m_style
= BIF_EDITBOX
| BIF_NEWDIALOGSTYLE
| BIF_RETURNFSANCESTORS
| BIF_RETURNONLYFSDIRS
;
2873 browser
.Show(GetSafeHwnd(), sTempfile
);
2874 if (!sTempfile
.IsEmpty())
2877 tempfile
.SetFromWin(sTempfile
);
2883 bool CRepositoryBrowser::StringToWidthArray(const CString
& WidthString
, int WidthArray
[])
2886 for (int i
=0; i
<7; ++i
)
2888 CString hex
= WidthString
.Mid(i
*8, 8);
2889 if ( hex
.IsEmpty() )
2891 // This case only occurs when upgrading from an older
2892 // TSVN version in which there were fewer columns.
2897 WidthArray
[i
] = _tcstol(hex
, &endchar
, 16);
2903 CString
CRepositoryBrowser::WidthArrayToString(int WidthArray
[])
2907 for (int i
=0; i
<7; ++i
)
2909 _stprintf_s(buf
, 10, _T("%08X"), WidthArray
[i
]);
2915 void CRepositoryBrowser::SaveColumnWidths(bool bSaveToRegistry
/* = false */)
2917 CRegString
regColWidth(_T("Software\\TortoiseGit\\RepoBrowserColumnWidth"));
2918 int maxcol
= ((CHeaderCtrl
*)(m_RepoList
.GetDlgItem(0)))->GetItemCount()-1;
2919 // first clear the width array
2920 for (int col
= 0; col
< 7; ++col
)
2921 m_arColumnWidths
[col
] = 0;
2922 for (int col
= 0; col
<= maxcol
; ++col
)
2924 m_arColumnWidths
[col
] = m_RepoList
.GetColumnWidth(col
);
2925 if (m_arColumnWidths
[col
] == m_arColumnAutoWidths
[col
])
2926 m_arColumnWidths
[col
] = 0;
2928 if (bSaveToRegistry
)
2930 CString sWidths
= WidthArrayToString(m_arColumnWidths
);
2931 regColWidth
= sWidths
;