1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // Copyright (C) 2003-2008 - TortoiseSVN
\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
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
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
20 #include "TortoiseProc.h"
\r
22 #include "MessageBox.h"
\r
23 #include "InputLogDlg.h"
\r
25 #include "PropDlg.h"
\r
26 #include "EditPropertiesDlg.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
53 enum RepoBrowserContextMenuCommands
\r
55 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
\r
85 IMPLEMENT_DYNAMIC(CRepositoryBrowser, CResizableStandAloneDialog)
\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
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
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
123 CRepositoryBrowser::~CRepositoryBrowser()
\r
127 void CRepositoryBrowser::RecursiveRemove(HTREEITEM hItem, bool bChildrenOnly /* = false */)
\r
129 HTREEITEM childItem;
\r
130 if (m_RepoTree.ItemHasChildren(hItem))
\r
132 for (childItem = m_RepoTree.GetChildItem(hItem);childItem != NULL; childItem = m_RepoTree.GetNextItem(childItem, TVGN_NEXT))
\r
134 RecursiveRemove(childItem);
\r
137 CTreeItem * pTreeItem = (CTreeItem*)m_RepoTree.GetItemData(childItem);
\r
139 m_RepoTree.SetItemData(childItem, 0);
\r
140 m_RepoTree.DeleteItem(childItem);
\r
145 if ((hItem)&&(!bChildrenOnly))
\r
147 CTreeItem * pTreeItem = (CTreeItem*)m_RepoTree.GetItemData(hItem);
\r
149 m_RepoTree.SetItemData(hItem, 0);
\r
153 void CRepositoryBrowser::DoDataExchange(CDataExchange* pDX)
\r
155 CResizableStandAloneDialog::DoDataExchange(pDX);
\r
156 DDX_Control(pDX, IDC_REPOTREE, m_RepoTree);
\r
157 DDX_Control(pDX, IDC_REPOLIST, m_RepoList);
\r
160 BEGIN_MESSAGE_MAP(CRepositoryBrowser, CResizableStandAloneDialog)
\r
161 ON_BN_CLICKED(IDHELP, OnBnClickedHelp)
\r
163 ON_REGISTERED_MESSAGE(WM_AFTERINIT, OnAfterInitDialog)
\r
165 ON_WM_LBUTTONDOWN()
\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
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
188 SVNRev CRepositoryBrowser::GetRevision() const
\r
190 return m_barRepository.GetCurrentRev();
\r
193 CString CRepositoryBrowser::GetPath() const
\r
195 return m_barRepository.GetCurrentUrl();
\r
198 BOOL CRepositoryBrowser::OnInitDialog()
\r
200 CResizableStandAloneDialog::OnInitDialog();
\r
202 GetWindowText(m_origDlgTitle);
\r
204 m_hAccel = LoadAccelerators(AfxGetResourceHandle(),MAKEINTRESOURCE(IDR_ACC_REPOBROWSER));
\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
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
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
232 GetDlgItem(IDCANCEL)->ShowWindow(FALSE);
\r
234 // reposition the buttons
\r
236 GetDlgItem(IDCANCEL)->GetWindowRect(rect_cancel);
\r
237 ScreenToClient(rect_cancel);
\r
238 GetDlgItem(IDOK)->MoveWindow(rect_cancel);
\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
254 m_RepoTree.SetImageList(&SYS_IMAGE_LIST(), TVSIL_NORMAL);
\r
257 theme.SetWindowTheme(m_RepoList.GetSafeHwnd(), L"Explorer", NULL);
\r
258 theme.SetWindowTheme(m_RepoTree.GetSafeHwnd(), L"Explorer", NULL);
\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
270 CenterWindow(CWnd::FromHandle(hWndExplorer));
\r
271 m_bThreadRunning = true;
\r
272 if (AfxBeginThread(InitThreadEntry, this)==NULL)
\r
274 m_bThreadRunning = false;
\r
275 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
\r
280 void CRepositoryBrowser::InitRepo()
\r
282 CWaitCursorEx wait;
\r
284 m_InitialUrl = CPathUtils::PathUnescape(m_InitialUrl);
\r
285 if (m_InitialUrl.Find('?')>=0)
\r
287 m_initialRev = SVNRev(m_InitialUrl.Mid(m_InitialUrl.Find('?')+1));
\r
288 m_InitialUrl = m_InitialUrl.Left(m_InitialUrl.Find('?'));
\r
291 // We don't know if the url passed to us points to a file or a folder,
\r
294 const SVNInfoData * data = NULL;
\r
295 CString error; // contains the first error of GetFirstFileInfo()
\r
298 data = info.GetFirstFileInfo(CTSVNPath(m_InitialUrl),m_initialRev, m_initialRev);
\r
299 if ((data == NULL)||(data->kind != svn_node_dir))
\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
311 m_InitialUrl.Empty();
\r
313 if (error.IsEmpty())
\r
315 if (((data)&&(data->kind == svn_node_dir))||(data == NULL))
\r
316 error = info.GetLastErrorMsg();
\r
319 } while(!m_InitialUrl.IsEmpty() && ((data == NULL) || (data->kind != svn_node_dir)));
\r
323 m_InitialUrl.Empty();
\r
324 m_RepoList.ShowText(error, true);
\r
327 else if (m_initialRev.IsHead())
\r
329 m_barRepository.SetHeadRevision(data->rev);
\r
331 m_InitialUrl.TrimRight('/');
\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
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
361 if (theme.IsAppThemed())
\r
362 CAppUtils::SetListCtrlBackgroundImage(m_RepoList.GetSafeHwnd(), nID);
\r
366 UINT CRepositoryBrowser::InitThreadEntry(LPVOID pVoid)
\r
368 return ((CRepositoryBrowser*)pVoid)->InitThread();
\r
371 //this is the thread function which calls the subversion function
\r
372 UINT CRepositoryBrowser::InitThread()
\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
378 // force the cursor to change
\r
381 DialogEnableWindow(IDOK, FALSE);
\r
382 DialogEnableWindow(IDCANCEL, FALSE);
\r
386 PostMessage(WM_AFTERINIT);
\r
387 DialogEnableWindow(IDOK, TRUE);
\r
388 DialogEnableWindow(IDCANCEL, TRUE);
\r
390 m_bThreadRunning = false;
\r
396 LRESULT CRepositoryBrowser::OnAfterInitDialog(WPARAM /*wParam*/, LPARAM /*lParam*/)
\r
398 if ((m_InitialUrl.IsEmpty())||(m_strReposRoot.IsEmpty()))
\r
403 m_barRepository.GotoUrl(m_InitialUrl, m_initialRev, true);
\r
404 m_RepoList.ClearText();
\r
405 m_bInitDone = TRUE;
\r
409 void CRepositoryBrowser::OnOK()
\r
411 RevokeDragDrop(m_RepoList.GetSafeHwnd());
\r
412 RevokeDragDrop(m_RepoTree.GetSafeHwnd());
\r
414 SaveColumnWidths(true);
\r
416 HTREEITEM hItem = m_RepoTree.GetRootItem();
\r
417 RecursiveRemove(hItem);
\r
419 m_barRepository.SaveHistory();
\r
420 CResizableStandAloneDialog::OnOK();
\r
423 void CRepositoryBrowser::OnCancel()
\r
425 RevokeDragDrop(m_RepoList.GetSafeHwnd());
\r
426 RevokeDragDrop(m_RepoTree.GetSafeHwnd());
\r
428 SaveColumnWidths(true);
\r
430 HTREEITEM hItem = m_RepoTree.GetRootItem();
\r
431 RecursiveRemove(hItem);
\r
433 __super::OnCancel();
\r
436 void CRepositoryBrowser::OnBnClickedHelp()
\r
441 /******************************************************************************/
\r
442 /* tree and list view resizing */
\r
443 /******************************************************************************/
\r
445 BOOL CRepositoryBrowser::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
\r
447 if (m_bThreadRunning)
\r
449 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));
\r
457 GetClientRect(&rect);
\r
459 ScreenToClient(&pt);
\r
460 if (PtInRect(&rect, pt))
\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
469 // but left of the list control?
\r
470 GetDlgItem(IDC_REPOLIST)->GetWindowRect(&rect);
\r
471 if (pt.x < rect.left)
\r
473 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZEWE));
\r
480 return CStandAloneDialogTmpl<CResizableDialog>::OnSetCursor(pWnd, nHitTest, message);
\r
483 void CRepositoryBrowser::OnMouseMove(UINT nFlags, CPoint point)
\r
486 RECT rect, tree, list, treelist, treelistclient;
\r
488 if (bDragMode == FALSE)
\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
498 //convert the mouse coordinates relative to the top-left of
\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
506 //same for the window coordinates - make them relative to 0,0
\r
507 OffsetRect(&treelist, -treelist.left, -treelist.top);
\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
514 if ((nFlags & MK_LBUTTON) && (point.x != oldx))
\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
530 CStandAloneDialogTmpl<CResizableDialog>::OnMouseMove(nFlags, point);
\r
533 void CRepositoryBrowser::OnLButtonDown(UINT nFlags, CPoint point)
\r
536 RECT rect, tree, list, treelist, treelistclient;
\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
545 //convert the mouse coordinates relative to the top-left of
\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
553 //same for the window coordinates - make them relative to 0,0
\r
554 OffsetRect(&treelist, -treelist.left, -treelist.top);
\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
563 if ((point.y < treelist.top) ||
\r
564 (point.y > treelist.bottom))
\r
565 return CStandAloneDialogTmpl<CResizableDialog>::OnLButtonDown(nFlags, point);
\r
572 DrawXorBar(pDC, point.x+2, treelistclient.top, 4, treelistclient.bottom-treelistclient.top-2);
\r
578 CStandAloneDialogTmpl<CResizableDialog>::OnLButtonDown(nFlags, point);
\r
581 void CRepositoryBrowser::OnLButtonUp(UINT nFlags, CPoint point)
\r
584 RECT rect, tree, list, treelist, treelistclient;
\r
586 if (bDragMode == FALSE)
\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
596 ClientToScreen(&point);
\r
597 GetClientRect(&rect);
\r
598 ClientToScreen(&rect);
\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
606 point.x -= rect.left;
\r
607 point.y -= treelist.top;
\r
609 OffsetRect(&treelist, -treelist.left, -treelist.top);
\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
617 DrawXorBar(pDC, oldx+2, treelistclient.top, 4, treelistclient.bottom-treelistclient.top-2);
\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
638 AddAnchor(IDC_REPOTREE, TOP_LEFT, BOTTOM_LEFT);
\r
639 AddAnchor(IDC_REPOLIST, TOP_LEFT, BOTTOM_RIGHT);
\r
641 CStandAloneDialogTmpl<CResizableDialog>::OnLButtonUp(nFlags, point);
\r
644 void CRepositoryBrowser::DrawXorBar(CDC * pDC, int x1, int y1, int width, int height)
\r
646 static WORD _dotPatternBmp[8] =
\r
648 0x0055, 0x00aa, 0x0055, 0x00aa,
\r
649 0x0055, 0x00aa, 0x0055, 0x00aa
\r
653 HBRUSH hbr, hbrushOld;
\r
655 hbm = CreateBitmap(8, 8, 1, 1, _dotPatternBmp);
\r
656 hbr = CreatePatternBrush(hbm);
\r
658 pDC->SetBrushOrg(x1, y1);
\r
659 hbrushOld = (HBRUSH)pDC->SelectObject(hbr);
\r
661 PatBlt(pDC->GetSafeHdc(), x1, y1, width, height, PATINVERT);
\r
663 pDC->SelectObject(hbrushOld);
\r
669 /******************************************************************************/
\r
670 /* repository information gathering */
\r
671 /******************************************************************************/
\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
682 static deque<CItem> * pDirList = NULL;
\r
683 static CTreeItem * pTreeItem = NULL;
\r
684 static CString dirPath;
\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
694 if ((path.IsEmpty())||
\r
695 (pDirList == NULL)||
\r
696 (sParent.Compare(dirPath)))
\r
698 HTREEITEM hItem = FindUrl(m_strReposRoot + sParent);
\r
699 pTreeItem = (CTreeItem*)m_RepoTree.GetItemData(hItem);
\r
700 pDirList = &(pTreeItem->children);
\r
704 if (path.IsEmpty())
\r
707 if (kind == svn_node_dir)
\r
709 FindUrl(m_strReposRoot + absolutepath + (abspath_has_slash ? _T("") : _T("/")) + path);
\r
711 pTreeItem->has_child_folders = true;
\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
719 pTreeItem->children_fetched = true;
\r
723 bool CRepositoryBrowser::ChangeToUrl(CString& url, SVNRev& rev, bool bAlreadyChecked)
\r
725 CWaitCursorEx wait;
\r
726 if (!bAlreadyChecked)
\r
728 // check if the entered url is valid
\r
730 const SVNInfoData * data = NULL;
\r
731 CString orig_url = url;
\r
732 m_bCancelled = false;
\r
735 data = info.GetFirstFileInfo(CTSVNPath(url), rev, rev);
\r
736 if (data && rev.IsHead())
\r
740 if ((data == NULL)||(data->kind != svn_node_dir))
\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
746 } while(!m_bCancelled && !url.IsEmpty() && ((data == NULL) || (data->kind != svn_node_dir)));
\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
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
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
771 if ((m_InitialUrl.IsEmpty())||(m_strReposRoot.IsEmpty()))
\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
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
792 hItem = m_RepoTree.InsertItem(&tvinsert);
\r
793 m_strReposRoot.ReleaseBuffer();
\r
797 // something terrible happened!
\r
800 hItem = FindUrl(url);
\r
804 CTreeItem * pTreeItem = (CTreeItem *)m_RepoTree.GetItemData(hItem);
\r
805 if (pTreeItem == NULL)
\r
808 if (!m_RepoList.HasText())
\r
809 m_RepoList.ShowText(_T(" "), true);
\r
811 RefreshNode(hItem);
\r
812 m_RepoTree.Expand(hItem, TVE_EXPAND);
\r
813 FillList(&pTreeItem->children);
\r
815 m_blockEvents = true;
\r
816 m_RepoTree.EnsureVisible(hItem);
\r
817 m_RepoTree.SelectItem(hItem);
\r
818 m_blockEvents = false;
\r
820 m_RepoList.ClearText();
\r
825 void CRepositoryBrowser::FillList(deque<CItem> * pItems)
\r
827 if (pItems == NULL)
\r
829 CWaitCursorEx wait;
\r
830 m_RepoList.SetRedraw(false);
\r
831 m_RepoList.DeleteAllItems();
\r
833 int c = ((CHeaderCtrl*)(m_RepoList.GetDlgItem(0)))->GetItemCount()-1;
\r
835 m_RepoList.DeleteColumn(c--);
\r
840 // column 0: contains tree
\r
841 temp.LoadString(IDS_LOG_FILE);
\r
842 m_RepoList.InsertColumn(c++, temp);
\r
844 // column 1: file extension
\r
845 temp.LoadString(IDS_STATUSLIST_COLEXT);
\r
846 m_RepoList.InsertColumn(c++, temp);
\r
848 // column 2: revision number
\r
849 temp.LoadString(IDS_LOG_REVISION);
\r
850 m_RepoList.InsertColumn(c++, temp, LVCFMT_RIGHT);
\r
852 // column 3: author
\r
853 temp.LoadString(IDS_LOG_AUTHOR);
\r
854 m_RepoList.InsertColumn(c++, temp);
\r
857 temp.LoadString(IDS_LOG_SIZE);
\r
858 m_RepoList.InsertColumn(c++, temp, LVCFMT_RIGHT);
\r
861 temp.LoadString(IDS_LOG_DATE);
\r
862 m_RepoList.InsertColumn(c++, temp);
\r
864 // column 6: lock owner
\r
865 temp.LoadString(IDS_STATUSLIST_COLLOCK);
\r
866 m_RepoList.InsertColumn(c++, temp);
\r
868 // now fill in the data
\r
870 TCHAR date_native[SVN_DATE_BUFFER];
\r
872 for (deque<CItem>::const_iterator it = pItems->begin(); it != pItems->end(); ++it)
\r
875 if (it->kind == svn_node_dir)
\r
876 icon_idx = m_nIconFolder;
\r
878 icon_idx = SYS_IMAGE_LIST().GetFileIconIndex(it->path);
\r
879 int index = m_RepoList.InsertItem(nCount, it->path, icon_idx);
\r
881 temp = CPathUtils::GetFileExtFromPath(it->path);
\r
882 if (it->kind == svn_node_file)
\r
883 m_RepoList.SetItemText(index, 1, temp);
\r
885 temp.Format(_T("%ld"), it->created_rev);
\r
886 m_RepoList.SetItemText(index, 2, temp);
\r
888 m_RepoList.SetItemText(index, 3, it->author);
\r
890 if (it->kind == svn_node_file)
\r
892 StrFormatByteSize(it->size, temp.GetBuffer(20), 20);
\r
893 temp.ReleaseBuffer();
\r
894 m_RepoList.SetItemText(index, 4, temp);
\r
897 SVN::formatDate(date_native, (apr_time_t&)it->time, true);
\r
898 m_RepoList.SetItemText(index, 5, date_native);
\r
900 m_RepoList.SetItemText(index, 6, it->lockowner);
\r
901 m_RepoList.SetItemData(index, (DWORD_PTR)&(*it));
\r
904 ListView_SortItemsEx(m_RepoList, ListSort, this);
\r
907 for (int col = 0; col <= (((CHeaderCtrl*)(m_RepoList.GetDlgItem(0)))->GetItemCount()-1); col++)
\r
909 m_RepoList.SetColumnWidth(col, LVSCW_AUTOSIZE_USEHEADER);
\r
911 for (int col = 0; col <= (((CHeaderCtrl*)(m_RepoList.GetDlgItem(0)))->GetItemCount()-1); col++)
\r
913 m_arColumnAutoWidths[col] = m_RepoList.GetColumnWidth(col);
\r
916 CRegString regColWidths(_T("Software\\TortoiseGit\\RepoBrowserColumnWidth"));
\r
917 if (!CString(regColWidths).IsEmpty())
\r
919 StringToWidthArray(regColWidths, m_arColumnWidths);
\r
921 int maxcol = ((CHeaderCtrl*)(m_RepoList.GetDlgItem(0)))->GetItemCount()-1;
\r
922 for (int col = 1; col <= maxcol; col++)
\r
924 if (m_arColumnWidths[col] == 0)
\r
925 m_RepoList.SetColumnWidth(col, LVSCW_AUTOSIZE_USEHEADER);
\r
927 m_RepoList.SetColumnWidth(col, m_arColumnWidths[col]);
\r
931 m_RepoList.SetRedraw(true);
\r
934 HTREEITEM CRepositoryBrowser::FindUrl(const CString& fullurl, bool create /* = true */)
\r
936 return FindUrl(fullurl, fullurl, create, TVI_ROOT);
\r
939 HTREEITEM CRepositoryBrowser::FindUrl(const CString& fullurl, const CString& url, bool create /* true */, HTREEITEM hItem /* = TVI_ROOT */)
\r
941 if (hItem == TVI_ROOT)
\r
943 hItem = m_RepoTree.GetRootItem();
\r
944 if (fullurl.Compare(m_strReposRoot)==0)
\r
946 return FindUrl(fullurl, url.Mid(m_strReposRoot.GetLength()+1), create, hItem);
\r
948 HTREEITEM hSibling = hItem;
\r
949 if (m_RepoTree.GetNextItem(hItem, TVGN_CHILD))
\r
951 hSibling = m_RepoTree.GetNextItem(hItem, TVGN_CHILD);
\r
954 CTreeItem * pTItem = ((CTreeItem*)m_RepoTree.GetItemData(hSibling));
\r
957 CString sSibling = pTItem->unescapedname;
\r
958 if (sSibling.Compare(url.Left(sSibling.GetLength()))==0)
\r
960 if (sSibling.GetLength() == url.GetLength())
\r
962 if (url.GetAt(sSibling.GetLength()) == '/')
\r
963 return FindUrl(fullurl, url.Mid(sSibling.GetLength()+1), create, hSibling);
\r
966 } while ((hSibling = m_RepoTree.GetNextItem(hSibling, TVGN_NEXT)) != NULL);
\r
970 // create tree items for every path part in the url
\r
971 CString sUrl = url;
\r
973 HTREEITEM hNewItem = hItem;
\r
975 while ((slash=sUrl.Find('/')) >= 0)
\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
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
999 if (!sUrl.IsEmpty())
\r
1001 CTreeItem * pTreeItem = new CTreeItem();
\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
1018 hNewItem = m_RepoTree.InsertItem(&tvinsert);
\r
1019 sTemp.ReleaseBuffer();
\r
1020 m_RepoTree.SortChildren(hNewItem);
\r
1026 bool CRepositoryBrowser::RefreshNode(const CString& url, bool force /* = false*/, bool recursive /* = false*/)
\r
1028 HTREEITEM hNode = FindUrl(url);
\r
1029 return RefreshNode(hNode, force, recursive);
\r
1032 bool CRepositoryBrowser::RefreshNode(HTREEITEM hNode, bool force /* = false*/, bool recursive /* = false*/)
\r
1034 if (hNode == NULL)
\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
1042 HTREEITEM hChild = m_RepoTree.GetChildItem(hNode);
\r
1044 m_blockEvents = true;
\r
1047 hNext = m_RepoTree.GetNextItem(hChild, TVGN_NEXT);
\r
1048 RecursiveRemove(hChild);
\r
1049 m_RepoTree.DeleteItem(hChild);
\r
1052 m_blockEvents = false;
\r
1054 if (pTreeItem == NULL)
\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
1061 // error during list()
\r
1062 m_RepoList.ShowText(GetLastErrorMessage());
\r
1065 pTreeItem->children_fetched = true;
\r
1066 // if there are no child folders, remove the '+' in front of the node
\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
1074 if ((force)||(hSel1 == hNode)||(hSel1 != m_RepoTree.GetSelectedItem()))
\r
1076 FillList(&pTreeItem->children);
\r
1081 BOOL CRepositoryBrowser::PreTranslateMessage(MSG* pMsg)
\r
1083 if (pMsg->message>=WM_KEYFIRST && pMsg->message<=WM_KEYLAST)
\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
1090 hWndFocus = ::GetParent(hWndFocus);
\r
1091 if (hWndFocus && ((hWndFocus == m_RepoTree.GetSafeHwnd())||(hWndFocus == m_RepoList.GetSafeHwnd())))
\r
1093 // Do a direct translation.
\r
1094 ::TranslateMessage(pMsg);
\r
1095 ::DispatchMessage(pMsg);
\r
1100 if (pMsg->message == WM_KEYDOWN)
\r
1102 switch (pMsg->wParam)
\r
1109 if ((pMsg->hwnd == m_barRepository.GetSafeHwnd())||(::IsChild(m_barRepository.GetSafeHwnd(), pMsg->hwnd)))
\r
1110 return __super::PreTranslateMessage(pMsg);
\r
1115 int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);
\r
1120 return __super::PreTranslateMessage(pMsg);
\r
1123 void CRepositoryBrowser::OnDelete()
\r
1125 CTSVNPathList urlList;
\r
1126 bool bTreeItem = false;
\r
1128 POSITION pos = m_RepoList.GetFirstSelectedItemPosition();
\r
1130 while ((index = m_RepoList.GetNextSelectedItem(pos))>=0)
\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
1137 if ((urlList.GetCount() == 0))
\r
1139 HTREEITEM hItem = m_RepoTree.GetSelectedItem();
\r
1140 CTreeItem * pTreeItem = (CTreeItem *)m_RepoTree.GetItemData(hItem);
\r
1143 urlList.AddPath(CTSVNPath(pTreeItem->url));
\r
1148 if (urlList.GetCount() == 0)
\r
1152 CWaitCursorEx wait_cursor;
\r
1153 CInputLogDlg input(this);
\r
1154 input.SetUUID(m_sUUID);
\r
1155 input.SetProjectProperties(&m_ProjectProperties);
\r
1157 if (urlList.GetCount() == 1)
\r
1158 hint.Format(IDS_INPUT_REMOVEONE, (LPCTSTR)urlList[0].GetFileOrDirectoryName());
\r
1160 hint.Format(IDS_INPUT_REMOVEMORE, urlList.GetCount());
\r
1161 input.SetActionText(hint);
\r
1162 if (input.DoModal() == IDOK)
\r
1164 if (!Remove(urlList, true, false, input.GetLogMessage()))
\r
1166 wait_cursor.Hide();
\r
1167 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
1171 RefreshNode(m_RepoTree.GetParentItem(m_RepoTree.GetSelectedItem()), true);
\r
1173 RefreshNode(m_RepoTree.GetSelectedItem(), true);
\r
1177 void CRepositoryBrowser::OnGoUp()
\r
1179 m_barRepository.OnGoUp();
\r
1182 void CRepositoryBrowser::OnUrlFocus()
\r
1184 m_barRepository.SetFocusToURL();
\r
1187 void CRepositoryBrowser::OnCopy()
\r
1189 // Ctrl-C : copy the selected item urls to the clipboard
\r
1191 POSITION pos = m_RepoList.GetFirstSelectedItemPosition();
\r
1193 while ((index = m_RepoList.GetNextSelectedItem(pos))>=0)
\r
1195 CItem * pItem = (CItem *)m_RepoList.GetItemData(index);
\r
1196 url += CUnicodeUtils::GetUnicode(CPathUtils::PathEscape(CUnicodeUtils::GetUTF8(pItem->absolutepath))) + _T("\r\n");
\r
1198 if (!url.IsEmpty())
\r
1200 url.TrimRight(_T("\r\n"));
\r
1201 CStringUtils::WriteAsciiStringToClipboard(url);
\r
1205 void CRepositoryBrowser::OnInlineedit()
\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
1212 m_RepoList.SetFocus();
\r
1213 m_RepoList.EditLabel(selIndex);
\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
1222 m_blockEvents = false;
\r
1225 void CRepositoryBrowser::OnRefresh()
\r
1227 m_blockEvents = true;
\r
1228 RefreshNode(m_RepoTree.GetSelectedItem(), true, !!(GetKeyState(VK_CONTROL)&0x8000));
\r
1229 m_blockEvents = false;
\r
1232 void CRepositoryBrowser::OnTvnSelchangedRepotree(NMHDR *pNMHDR, LRESULT *pResult)
\r
1234 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
\r
1237 if (m_blockEvents)
\r
1240 if (pNMTreeView->action == TVC_BYKEYBOARD)
\r
1241 SetTimer(REPOBROWSER_FETCHTIMER, 300, NULL);
\r
1243 OnTimer(REPOBROWSER_FETCHTIMER);
\r
1246 void CRepositoryBrowser::OnTimer(UINT_PTR nIDEvent)
\r
1248 if (nIDEvent == REPOBROWSER_FETCHTIMER)
\r
1250 KillTimer(REPOBROWSER_FETCHTIMER);
\r
1251 // find the currently selected item
\r
1252 HTREEITEM hSelItem = m_RepoTree.GetSelectedItem();
\r
1255 CTreeItem * pTreeItem = (CTreeItem *)m_RepoTree.GetItemData(hSelItem);
\r
1258 if (!pTreeItem->children_fetched)
\r
1260 m_RepoList.ShowText(_T(" "), true);
\r
1261 RefreshNode(hSelItem);
\r
1262 m_RepoList.ClearText();
\r
1265 FillList(&pTreeItem->children);
\r
1266 m_barRepository.ShowUrl(pTreeItem->url, GetRevision());
\r
1271 __super::OnTimer(nIDEvent);
\r
1274 void CRepositoryBrowser::OnTvnItemexpandingRepotree(NMHDR *pNMHDR, LRESULT *pResult)
\r
1276 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
\r
1279 if (m_blockEvents)
\r
1282 CTreeItem * pTreeItem = (CTreeItem *)pNMTreeView->itemNew.lParam;
\r
1284 if (pTreeItem == NULL)
\r
1287 if (pNMTreeView->action == TVE_COLLAPSE)
\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
1294 RefreshNode(pNMTreeView->itemNew.hItem);
\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
1304 if (!pTreeItem->children_fetched)
\r
1306 RefreshNode(pNMTreeView->itemNew.hItem);
\r
1310 // if there are no child folders, remove the '+' in front of the node
\r
1311 if (!pTreeItem->has_child_folders)
\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
1322 void CRepositoryBrowser::OnNMDblclkRepolist(NMHDR *pNMHDR, LRESULT *pResult)
\r
1324 LPNMITEMACTIVATE pNmItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
\r
1327 if (m_blockEvents)
\r
1330 if (pNmItemActivate->iItem < 0)
\r
1332 CItem * pItem = (CItem*)m_RepoList.GetItemData(pNmItemActivate->iItem);
\r
1333 if ((pItem)&&(pItem->kind == svn_node_dir))
\r
1335 // a double click on a folder results in selecting that folder
\r
1336 ChangeToUrl(pItem->absolutepath, m_initialRev, true);
\r
1340 void CRepositoryBrowser::OnHdnItemclickRepolist(NMHDR *pNMHDR, LRESULT *pResult)
\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
1347 m_bSortAscending = !m_bSortAscending;
\r
1348 m_nSortedColumn = phdr->iItem;
\r
1350 m_blockEvents = true;
\r
1351 ListView_SortItemsEx(m_RepoList, ListSort, this);
\r
1353 m_blockEvents = false;
\r
1357 int CRepositoryBrowser::ListSort(LPARAM lParam1, LPARAM lParam2, LPARAM lParam3)
\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
1363 switch (pThis->m_nSortedColumn)
\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
1371 case 2: // revision number
\r
1372 nRet = pItem1->created_rev - pItem2->created_rev;
\r
1377 nRet = pItem1->author.CompareNoCase(pItem2->author);
\r
1382 nRet = int(pItem1->size - pItem2->size);
\r
1387 nRet = (pItem1->time - pItem2->time) > 0 ? 1 : -1;
\r
1391 case 6: // lock owner
\r
1392 nRet = pItem1->lockowner.CompareNoCase(pItem2->lockowner);
\r
1396 case 0: // filename
\r
1397 nRet = CStringUtils::CompareNumerical(pItem1->path, pItem2->path);
\r
1401 if (!pThis->m_bSortAscending)
\r
1404 // we want folders on top, then the files
\r
1405 if (pItem1->kind != pItem2->kind)
\r
1407 if (pItem1->kind == svn_node_dir)
\r
1416 void CRepositoryBrowser::SetSortArrow()
\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
1423 pHeader->GetItem(i, &HeaderItem);
\r
1424 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
\r
1425 pHeader->SetItem(i, &HeaderItem);
\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
1433 void CRepositoryBrowser::OnLvnItemchangedRepolist(NMHDR *pNMHDR, LRESULT *pResult)
\r
1435 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
\r
1437 if (m_blockEvents)
\r
1439 if (m_RepoList.HasText())
\r
1441 if (pNMLV->uChanged & LVIF_STATE)
\r
1443 if (pNMLV->uNewState & LVIS_SELECTED)
\r
1445 CItem * pItem = (CItem*)m_RepoList.GetItemData(pNMLV->iItem);
\r
1447 m_barRepository.ShowUrl(pItem->absolutepath, GetRevision());
\r
1452 void CRepositoryBrowser::OnLvnEndlabeleditRepolist(NMHDR *pNMHDR, LRESULT *pResult)
\r
1454 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
\r
1456 if (pDispInfo->item.pszText == NULL)
\r
1458 // rename the item in the repository
\r
1459 CItem * pItem = (CItem *)m_RepoList.GetItemData(pDispInfo->item.iItem);
\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
1468 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH, IDS_APPNAME, MB_ICONINFORMATION|MB_YESNO) != IDYES)
\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
1476 m_bCancelled = false;
\r
1477 if (!Move(CTSVNPathList(CTSVNPath(EscapeUrl(CTSVNPath(pItem->absolutepath)))),
\r
1479 true, input.GetLogMessage()))
\r
1481 wait_cursor.Hide();
\r
1482 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
1486 RefreshNode(m_RepoTree.GetSelectedItem(), true);
\r
1490 void CRepositoryBrowser::OnTvnEndlabeleditRepotree(NMHDR *pNMHDR, LRESULT *pResult)
\r
1492 LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
\r
1494 if (pTVDispInfo->item.pszText == NULL)
\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
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
1510 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH, IDS_APPNAME, MB_ICONINFORMATION|MB_YESNO) != IDYES)
\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
1518 m_bCancelled = false;
\r
1519 if (!Move(CTSVNPathList(CTSVNPath(EscapeUrl(CTSVNPath(pItem->url)))),
\r
1521 true, input.GetLogMessage()))
\r
1523 wait_cursor.Hide();
\r
1524 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\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
1536 void CRepositoryBrowser::OnLvnBeginrdragRepolist(NMHDR *pNMHDR, LRESULT *pResult)
\r
1538 m_bRightDrag = true;
\r
1540 OnBeginDrag(pNMHDR);
\r
1543 void CRepositoryBrowser::OnLvnBegindragRepolist(NMHDR *pNMHDR, LRESULT *pResult)
\r
1545 m_bRightDrag = false;
\r
1547 OnBeginDrag(pNMHDR);
\r
1550 void CRepositoryBrowser::OnBeginDrag(NMHDR *pNMHDR)
\r
1552 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
\r
1554 if (m_RepoList.HasText())
\r
1556 CIDropSource* pdsrc = new CIDropSource;
\r
1557 if (pdsrc == NULL)
\r
1561 CTSVNPathList sourceURLs;
\r
1562 POSITION pos = m_RepoList.GetFirstSelectedItemPosition();
\r
1564 while ((index = m_RepoList.GetNextSelectedItem(pos))>=0)
\r
1566 CItem * pItem = (CItem *)m_RepoList.GetItemData(index);
\r
1568 sourceURLs.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(pItem->absolutepath))));
\r
1571 SVNDataObject* pdobj = new SVNDataObject(sourceURLs, GetRevision(), GetRevision());
\r
1572 if (pdobj == NULL)
\r
1578 pdobj->SetAsyncMode(TRUE);
\r
1580 CDragSourceHelper dragsrchelper;
\r
1581 dragsrchelper.InitializeFromWindow(m_RepoList.GetSafeHwnd(), pNMLV->ptAction, pdobj);
\r
1582 // Initiate the Drag & Drop
\r
1584 ::DoDragDrop(pdobj, pdsrc, DROPEFFECT_MOVE|DROPEFFECT_COPY, &dwEffect);
\r
1589 void CRepositoryBrowser::OnTvnBegindragRepotree(NMHDR *pNMHDR, LRESULT *pResult)
\r
1591 m_bRightDrag = false;
\r
1593 OnBeginDragTree(pNMHDR);
\r
1596 void CRepositoryBrowser::OnTvnBeginrdragRepotree(NMHDR *pNMHDR, LRESULT *pResult)
\r
1598 m_bRightDrag = true;
\r
1600 OnBeginDragTree(pNMHDR);
\r
1603 void CRepositoryBrowser::OnBeginDragTree(NMHDR *pNMHDR)
\r
1605 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
\r
1607 if (m_blockEvents)
\r
1610 CTreeItem * pTreeItem = (CTreeItem *)pNMTreeView->itemNew.lParam;
\r
1612 if (pTreeItem == NULL)
\r
1615 CIDropSource* pdsrc = new CIDropSource;
\r
1616 if (pdsrc == NULL)
\r
1620 CTSVNPathList sourceURLs;
\r
1621 sourceURLs.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(pTreeItem->url))));
\r
1623 SVNDataObject* pdobj = new SVNDataObject(sourceURLs, GetRevision(), GetRevision());
\r
1624 if (pdobj == NULL)
\r
1631 CDragSourceHelper dragsrchelper;
\r
1632 dragsrchelper.InitializeFromWindow(m_RepoTree.GetSafeHwnd(), pNMTreeView->ptDrag, pdobj);
\r
1633 // Initiate the Drag & Drop
\r
1635 ::DoDragDrop(pdobj, pdsrc, DROPEFFECT_MOVE|DROPEFFECT_COPY, &dwEffect);
\r
1641 bool CRepositoryBrowser::OnDrop(const CTSVNPath& target, const CTSVNPathList& pathlist, const SVNRev& srcRev, DWORD dwEffect, POINTL /*pt*/)
\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
1647 CString targetName = pathlist[0].GetFileOrDirectoryName();
\r
1650 // right dragging means we have to show a context menu
\r
1652 DWORD ptW = GetMessagePos();
\r
1653 pt.x = GET_X_LPARAM(ptW);
\r
1654 pt.y = GET_Y_LPARAM(ptW);
\r
1656 if (popup.CreatePopupMenu())
\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
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
1673 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, pt.x, pt.y, this, 0);
\r
1676 default:// nothing clicked
\r
1678 case 1: // copy drop
\r
1679 dwEffect = DROPEFFECT_COPY;
\r
1681 case 2: // move drop
\r
1682 dwEffect = DROPEFFECT_MOVE;
\r
1684 case 4: // copy rename drop
\r
1686 dwEffect = DROPEFFECT_COPY;
\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
1693 targetName = dlg.m_name;
\r
1694 if (!CTSVNPath(targetName).IsValidOnWindows())
\r
1696 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH, IDS_APPNAME, MB_ICONINFORMATION|MB_YESNO) != IDYES)
\r
1701 case 5: // move rename drop
\r
1703 dwEffect = DROPEFFECT_MOVE;
\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
1710 targetName = dlg.m_name;
\r
1711 if (!CTSVNPath(targetName).IsValidOnWindows())
\r
1713 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH, IDS_APPNAME, MB_ICONINFORMATION|MB_YESNO) != IDYES)
\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
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
1733 bool pathListIsSpecial = false;
\r
1734 int pathCount = pathlist.GetCount();
\r
1735 for (int i=0 ; i<pathCount ; ++i)
\r
1737 const CTSVNPath& path = pathlist[i];
\r
1738 if (path.IsSpecialDirectory())
\r
1740 pathListIsSpecial = true;
\r
1744 if (pathListIsSpecial)
\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
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
1759 if (pathlist.GetCount() == 1)
\r
1761 if (dwEffect == DROPEFFECT_COPY)
\r
1762 sHint.Format(IDS_INPUT_COPY, (LPCTSTR)pathlist[0].GetSVNPathString(), (LPCTSTR)(target.GetSVNPathString()+_T("/")+targetName));
\r
1764 sHint.Format(IDS_INPUT_MOVE, (LPCTSTR)pathlist[0].GetSVNPathString(), (LPCTSTR)(target.GetSVNPathString()+_T("/")+targetName));
\r
1768 if (dwEffect == DROPEFFECT_COPY)
\r
1769 sHint.Format(IDS_INPUT_COPYMORE, pathlist.GetCount(), (LPCTSTR)target.GetSVNPathString());
\r
1771 sHint.Format(IDS_INPUT_MOVEMORE, pathlist.GetCount(), (LPCTSTR)target.GetSVNPathString());
\r
1773 input.SetActionText(sHint);
\r
1774 if (input.DoModal() == IDOK)
\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
1783 bRet = Copy(pathlist, target, srcRev, srcRev, input.GetLogMessage(), true);
\r
1785 if (pathlist.GetCount() == 1)
\r
1786 bRet = Move(pathlist, CTSVNPath(target.GetSVNPathString() + _T("/") + targetName), TRUE, input.GetLogMessage(), false);
\r
1788 bRet = Move(pathlist, target, TRUE, input.GetLogMessage(), true);
\r
1791 wait_cursor.Hide();
\r
1792 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
1794 else if (GetRevision().IsHead())
\r
1796 // mark the target as dirty
\r
1797 HTREEITEM hTarget = FindUrl(target.GetSVNPathString(), false);
\r
1800 CTreeItem * pItem = (CTreeItem*)m_RepoTree.GetItemData(hTarget);
\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
1813 if (dwEffect == DROPEFFECT_MOVE)
\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
1819 HTREEITEM hSource = FindUrl(pathlist[i].GetSVNPathString(), false);
\r
1822 CTreeItem * pItem = (CTreeItem*)m_RepoTree.GetItemData(hSource);
\r
1825 // the source has moved, so remove it!
\r
1826 RecursiveRemove(hSource);
\r
1827 m_RepoTree.DeleteItem(hSource);
\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
1838 CTreeItem * pItem = (CTreeItem*)m_RepoTree.GetItemData(hSelected);
\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
1845 // Refresh the current view
\r
1846 RefreshNode(hSelected, true);
\r
1855 // import files dragged onto us
\r
1856 if (pathlist.GetCount() > 1)
\r
1858 if (CMessageBox::Show(m_hWnd, IDS_REPOBROWSE_MULTIIMPORT, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION)!=IDYES)
\r
1862 CInputLogDlg input(this);
\r
1863 input.SetProjectProperties(&m_ProjectProperties);
\r
1864 input.SetUUID(m_sUUID);
\r
1866 if (pathlist.GetCount() == 1)
\r
1867 sHint.Format(IDS_INPUT_IMPORTFILEFULL, pathlist[0].GetWinPath(), (LPCTSTR)(target.GetSVNPathString() + _T("/") + pathlist[0].GetFileOrDirectoryName()));
\r
1869 sHint.Format(IDS_INPUT_IMPORTFILES, pathlist.GetCount());
\r
1870 input.SetActionText(sHint);
\r
1872 if (input.DoModal() == IDOK)
\r
1874 m_bCancelled = false;
\r
1875 for (int importindex = 0; importindex<pathlist.GetCount(); ++importindex)
\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
1882 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
1886 if (GetRevision().IsHead())
\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
1893 CTreeItem * pItem = (CTreeItem*)m_RepoTree.GetItemData(hSelected);
\r
1896 if (pItem->url.Compare(target.GetSVNPathString())==0)
\r
1898 // Refresh the current view
\r
1899 RefreshNode(hSelected, true);
\r
1903 // only mark the target as 'dirty'
\r
1904 pItem->children_fetched = false;
\r
1915 CString CRepositoryBrowser::EscapeUrl(const CTSVNPath& url)
\r
1917 return CUnicodeUtils::GetUnicode(CPathUtils::PathEscape(CUnicodeUtils::GetUTF8(url.GetSVNPathString())));
\r
1920 void CRepositoryBrowser::OnContextMenu(CWnd* pWnd, CPoint point)
\r
1922 HTREEITEM hSelectedTreeItem = NULL;
\r
1923 HTREEITEM hChosenTreeItem = NULL;
\r
1924 if ((point.x == -1) && (point.y == -1))
\r
1926 if (pWnd == &m_RepoTree)
\r
1929 m_RepoTree.GetItemRect(m_RepoTree.GetSelectedItem(), &rect, TRUE);
\r
1930 m_RepoTree.ClientToScreen(&rect);
\r
1931 point = rect.CenterPoint();
\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
1942 m_bCancelled = false;
\r
1943 CTSVNPathList urlList;
\r
1944 CTSVNPathList urlListEscaped;
\r
1947 if (pWnd == &m_RepoList)
\r
1951 POSITION pos = m_RepoList.GetFirstSelectedItemPosition();
\r
1953 while ((index = m_RepoList.GetNextSelectedItem(pos))>=0)
\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
1962 if (!pItem->locktoken.IsEmpty())
\r
1965 if (urlList.GetCount() == 0)
\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
1970 m_RepoList.GetHeaderCtrl()->GetWindowRect(&hr);
\r
1971 if (!hr.PtInRect(point))
\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
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
1985 urlList.AddPath(CTSVNPath(pTreeItem->url));
\r
1986 urlListEscaped.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(pTreeItem->url))));
\r
1993 if ((pWnd == &m_RepoTree)||(urlList.GetCount() == 0))
\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
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
2012 hChosenTreeItem = hItem;
\r
2013 CTreeItem * pTreeItem = (CTreeItem *)m_RepoTree.GetItemData(hItem);
\r
2016 urlList.AddPath(CTSVNPath(pTreeItem->url));
\r
2017 urlListEscaped.AddPath(CTSVNPath(EscapeUrl(CTSVNPath(pTreeItem->url))));
\r
2023 if (urlList.GetCount() == 0)
\r
2027 if (popup.CreatePopupMenu())
\r
2029 if (urlList.GetCount() == 1)
\r
2031 if (nFolders == 0)
\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
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
2043 popup.AppendMenuIcon(ID_REVGRAPH, IDS_MENUREVISIONGRAPH, IDI_REVISIONGRAPH); // "Revision graph"
\r
2045 if (nFolders == 0)
\r
2047 popup.AppendMenuIcon(ID_BLAME, IDS_MENUBLAME, IDI_BLAME); // "Blame..."
\r
2049 if (!m_ProjectProperties.sWebViewerRev.IsEmpty())
\r
2051 popup.AppendMenuIcon(ID_VIEWREV, IDS_LOG_POPUP_VIEWREV); // "View revision in webviewer"
\r
2053 if (!m_ProjectProperties.sWebViewerPathRev.IsEmpty())
\r
2055 popup.AppendMenuIcon(ID_VIEWPATHREV, IDS_LOG_POPUP_VIEWPATHREV); // "View revision for path in webviewer"
\r
2057 if ((!m_ProjectProperties.sWebViewerPathRev.IsEmpty())||
\r
2058 (!m_ProjectProperties.sWebViewerRev.IsEmpty()))
\r
2060 popup.AppendMenu(MF_SEPARATOR, NULL);
\r
2064 popup.AppendMenuIcon(ID_EXPORT, IDS_MENUEXPORT, IDI_EXPORT); // "Export"
\r
2067 // We allow checkout of multiple folders at once (we do that one by one)
\r
2068 if (nFolders == urlList.GetCount())
\r
2070 popup.AppendMenuIcon(ID_CHECKOUT, IDS_MENUCHECKOUT, IDI_CHECKOUT); // "Checkout.."
\r
2072 if (urlList.GetCount() == 1)
\r
2076 popup.AppendMenuIcon(ID_REFRESH, IDS_REPOBROWSE_REFRESH, IDI_REFRESH); // "Refresh"
\r
2078 popup.AppendMenu(MF_SEPARATOR, NULL);
\r
2080 if (GetRevision().IsHead())
\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
2090 popup.AppendMenuIcon(ID_RENAME, IDS_REPOBROWSE_RENAME, IDI_RENAME); // "Rename"
\r
2094 popup.AppendMenuIcon(ID_BREAKLOCK, IDS_MENU_UNLOCKFORCE, IDI_UNLOCK); // "Break Lock"
\r
2097 if (urlList.GetCount() > 0)
\r
2099 if (GetRevision().IsHead())
\r
2101 popup.AppendMenuIcon(ID_DELETE, IDS_REPOBROWSE_DELETE, IDI_DELETE); // "Remove"
\r
2103 if (nFolders == 0)
\r
2105 popup.AppendMenuIcon(ID_SAVEAS, IDS_REPOBROWSE_SAVEAS, IDI_SAVEAS); // "Save as..."
\r
2107 if ((urlList.GetCount() == nFolders)||(nFolders == 0))
\r
2109 popup.AppendMenuIcon(ID_COPYTOWC, IDS_REPOBROWSE_COPYTOWC); // "Copy To Working Copy..."
\r
2112 if (urlList.GetCount() == 1)
\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
2122 popup.AppendMenuIcon(ID_REVPROPS, IDS_REPOBROWSE_SHOWREVPROP, IDI_PROPERTIES); // "Show Revision Properties"
\r
2124 if (nFolders == 1)
\r
2126 popup.AppendMenu(MF_SEPARATOR, NULL);
\r
2127 popup.AppendMenuIcon(ID_PREPAREDIFF, IDS_REPOBROWSE_PREPAREDIFF); // "Mark for comparison"
\r
2129 if ((m_diffKind == svn_node_dir)&&(!m_diffURL.IsEquivalentTo(urlList[0])))
\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
2136 if (urlList.GetCount() == 2)
\r
2138 if ((nFolders == 2)||(nFolders == 0))
\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
2145 popup.AppendMenuIcon(ID_SHOWLOG, IDS_MENULOG, IDI_LOG); // "Show Log..."
\r
2147 if ((urlList.GetCount() == 1) &&
\r
2148 m_path.Exists() &&
\r
2149 CTSVNPath(m_InitialUrl).IsAncestorOf(urlList[0]))
\r
2151 CTSVNPath wcPath = m_path;
\r
2152 wcPath.AppendPathString(urlList[0].GetWinPathString().Mid(m_InitialUrl.GetLength()));
\r
2153 if (!wcPath.Exists())
\r
2155 bool bWCPresent = false;
\r
2156 while (!bWCPresent && m_path.IsAncestorOf(wcPath))
\r
2158 bWCPresent = wcPath.GetContainingDirectory().Exists();
\r
2159 wcPath = wcPath.GetContainingDirectory();
\r
2163 popup.AppendMenu(MF_SEPARATOR, NULL);
\r
2164 popup.AppendMenuIcon(ID_UPDATE, IDS_LOG_POPUP_UPDATE, IDI_UPDATE); // "Update item to revision"
\r
2168 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
\r
2170 if (pWnd == &m_RepoTree)
\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
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
2185 if (hSelectedTreeItem)
\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
2192 DialogEnableWindow(IDOK, FALSE);
\r
2193 bool bOpenWith = false;
\r
2198 CTSVNPath wcPath = m_path;
\r
2199 wcPath.AppendPathString(urlList[0].GetWinPathString().Mid(m_InitialUrl.GetLength()));
\r
2201 sCmd.Format(_T("\"%s\" /command:update /path:\"%s\" /rev"),
\r
2202 (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), wcPath.GetWinPath());
\r
2204 CAppUtils::LaunchApplication(sCmd, NULL, false);
\r
2207 case ID_PREPAREDIFF:
\r
2209 m_RepoTree.SetItemState(FindUrl(m_diffURL.GetSVNPathString(), false), 0, TVIS_BOLD);
\r
2210 if (urlList.GetCount() == 1)
\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
2217 m_RepoTree.SetItemState(FindUrl(m_diffURL.GetSVNPathString(), false), TVIS_BOLD, TVIS_BOLD);
\r
2222 m_diffURL.Reset();
\r
2223 m_diffKind = svn_node_none;
\r
2227 case ID_URLTOCLIPBOARD:
\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
2238 CTSVNPath tempfile;
\r
2239 bool bSavePathOK = AskForSavePath(urlList, tempfile, nFolders > 0);
\r
2242 CWaitCursorEx wait_cursor;
\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
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
2263 wait_cursor.Hide();
\r
2265 SetAndClearProgressInfo((HWND)NULL);
\r
2266 if (!progDlg.HasUserCancelled())
\r
2267 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
2271 progDlg.SetProgress((DWORD)counter, (DWORD)urlList.GetCount());
\r
2274 SetAndClearProgressInfo((HWND)NULL);
\r
2280 if (urlList.GetCount() == 2)
\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
2289 CMessageBox::Show(this->m_hWnd, helper.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
2292 SVNRev rev2 = helper.GetCopyFromRev(CTSVNPath(EscapeUrl(urlList[1])), GetRevision(), sCopyFrom2);
\r
2293 if (!rev2.IsValid())
\r
2295 CMessageBox::Show(this->m_hWnd, helper.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
2298 if ((sCopyFrom1.IsEmpty())||(sCopyFrom1.Compare(sCopyFrom2)!=0))
\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
2305 if ((LONG)rev1 < (LONG)rev2)
\r
2307 SVNRev temp = rev1;
\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
2316 if (!m_path.IsUrl())
\r
2318 sCmd += _T(" /propspath:\"");
\r
2319 sCmd += m_path.GetWinPathString();
\r
2323 CAppUtils::LaunchApplication(sCmd, NULL, false);
\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
2331 if (!m_path.IsUrl())
\r
2333 sCmd += _T(" /propspath:\"");
\r
2334 sCmd += m_path.GetWinPathString();
\r
2338 CAppUtils::LaunchApplication(sCmd, NULL, false);
\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
2350 case ID_VIEWPATHREV:
\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
2363 CString itemsToCheckout;
\r
2364 for (int i=0; i<urlList.GetCount(); ++i)
\r
2366 itemsToCheckout += EscapeUrl(urlList[i]) + _T("*");
\r
2368 itemsToCheckout.TrimRight('*');
\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
2373 CAppUtils::LaunchApplication(sCmd, NULL, false);
\r
2379 dlg.m_URL = EscapeUrl(urlList[0]);
\r
2380 dlg.Revision = GetRevision();
\r
2381 if (dlg.DoModal()==IDOK)
\r
2383 CTSVNPath exportDirectory;
\r
2384 exportDirectory.SetFromWin(dlg.m_strExportDirectory, true);
\r
2386 CSVNProgressDlg progDlg;
\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
2408 CRevisionGraphDlg dlg;
\r
2409 dlg.SetPath(EscapeUrl(urlList[0]));
\r
2410 dlg.SetPegRevision(GetRevision());
\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
2422 if (urlList[0].GetSVNPathString().Left(4).CompareNoCase(_T("http")) == 0)
\r
2424 CString sBrowserUrl = EscapeUrl(urlList[0]);
\r
2426 ShellExecute(NULL, _T("open"), sBrowserUrl, NULL, NULL, SW_SHOWNORMAL);
\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
2444 SetAndClearProgressInfo((HWND)NULL);
\r
2445 wait_cursor.Hide();
\r
2446 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\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
2456 int ret = (int)ShellExecute(NULL, _T("open"), tempfile.GetWinPathString(), NULL, NULL, SW_SHOWNORMAL);
\r
2457 if (ret <= HINSTANCE_ERROR)
\r
2462 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
\r
2463 cmd += tempfile.GetWinPathString() + _T(" ");
\r
2464 CAppUtils::LaunchApplication(cmd, NULL, false);
\r
2470 CWaitCursorEx wait_cursor;
\r
2471 CInputLogDlg input(this);
\r
2472 input.SetUUID(m_sUUID);
\r
2473 input.SetProjectProperties(&m_ProjectProperties);
\r
2475 if (urlList.GetCount() == 1)
\r
2476 hint.Format(IDS_INPUT_REMOVEONE, (LPCTSTR)urlList[0].GetFileOrDirectoryName());
\r
2478 hint.Format(IDS_INPUT_REMOVEMORE, urlList.GetCount());
\r
2479 input.SetActionText(hint);
\r
2480 if (input.DoModal() == IDOK)
\r
2482 if (!Remove(urlList, true, false, input.GetLogMessage()))
\r
2484 wait_cursor.Hide();
\r
2485 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
2488 if (hChosenTreeItem)
\r
2490 HTREEITEM hParent = m_RepoTree.GetParentItem(hChosenTreeItem);
\r
2491 RecursiveRemove(hChosenTreeItem);
\r
2492 RefreshNode(hParent);
\r
2495 RefreshNode(m_RepoTree.GetSelectedItem(), true);
\r
2499 case ID_BREAKLOCK:
\r
2501 if (!Unlock(urlListEscaped, TRUE))
\r
2503 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
2506 RefreshNode(m_RepoTree.GetSelectedItem(), true);
\r
2509 case ID_IMPORTFOLDER:
\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
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
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
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
2542 SetAndClearProgressInfo((HWND)NULL);
\r
2543 wait_cursor.Hide();
\r
2544 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
2548 SetAndClearProgressInfo((HWND)NULL);
\r
2549 RefreshNode(m_RepoTree.GetSelectedItem(), true);
\r
2556 // Display the Open dialog box.
\r
2558 if (CAppUtils::FileOpenSave(openPath, NULL, IDS_REPOBROWSE_IMPORT, IDS_COMMONFILEFILTER, true, m_hWnd))
\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
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
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
2586 SetAndClearProgressInfo((HWND)NULL);
\r
2587 wait_cursor.Hide();
\r
2588 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
2592 SetAndClearProgressInfo((HWND)NULL);
\r
2593 RefreshNode(m_RepoTree.GetSelectedItem(), true);
\r
2600 if (pWnd == &m_RepoList)
\r
2602 POSITION pos = m_RepoList.GetFirstSelectedItemPosition();
\r
2603 int selIndex = m_RepoList.GetNextSelectedItem(pos);
\r
2604 if (selIndex >= 0)
\r
2606 m_RepoList.SetFocus();
\r
2607 m_RepoList.EditLabel(selIndex);
\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
2617 else if (pWnd == &m_RepoTree)
\r
2619 m_RepoTree.SetFocus();
\r
2620 if (hChosenTreeItem != m_RepoTree.GetRootItem())
\r
2621 m_RepoTree.EditLabel(hChosenTreeItem);
\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
2633 CWaitCursorEx wait_cursor;
\r
2634 CInputLogDlg input(this);
\r
2635 input.SetUUID(m_sUUID);
\r
2636 input.SetProjectProperties(&m_ProjectProperties);
\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
2642 if (CMessageBox::Show(GetSafeHwnd(), IDS_WARN_NOVALIDPATH, IDS_APPNAME, MB_ICONINFORMATION|MB_YESNO) != IDYES)
\r
2645 if (input.DoModal() == IDOK)
\r
2647 if (!Copy(urlList, CTSVNPath(dlg.m_name), GetRevision(), GetRevision(), input.GetLogMessage()))
\r
2649 wait_cursor.Hide();
\r
2650 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
2653 if (GetRevision().IsHead())
\r
2655 RefreshNode(m_RepoTree.GetSelectedItem(), true);
\r
2663 CTSVNPath tempfile;
\r
2664 bool bSavePathOK = AskForSavePath(urlList, tempfile, nFolders > 0);
\r
2667 CWaitCursorEx wait_cursor;
\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
2675 bool bCopyAsChild = (urlList.GetCount() > 1);
\r
2676 if (!Copy(urlList, tempfile, GetRevision(), GetRevision(), CString(), bCopyAsChild)||(progDlg.HasUserCancelled()))
\r
2679 SetAndClearProgressInfo((HWND)NULL);
\r
2680 wait_cursor.Hide();
\r
2682 if (!progDlg.HasUserCancelled())
\r
2683 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
2687 SetAndClearProgressInfo((HWND)NULL);
\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
2699 CWaitCursorEx wait_cursor;
\r
2700 CInputLogDlg input(this);
\r
2701 input.SetUUID(m_sUUID);
\r
2702 input.SetProjectProperties(&m_ProjectProperties);
\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
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
2711 wait_cursor.Hide();
\r
2712 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
2715 RefreshNode(m_RepoTree.GetSelectedItem(), true);
\r
2722 RefreshNode(urlList[0].GetSVNPathString(), true);
\r
2727 m_bCancelled = false;
\r
2728 SVNDiff diff(this, this->m_hWnd, true);
\r
2729 if (urlList.GetCount() == 1)
\r
2731 if (PromptShown())
\r
2732 diff.ShowUnifiedDiff(CTSVNPath(EscapeUrl(urlList[0])), GetRevision(),
\r
2733 CTSVNPath(EscapeUrl(m_diffURL)), GetRevision());
\r
2735 CAppUtils::StartShowUnifiedDiff(m_hWnd, CTSVNPath(EscapeUrl(urlList[0])), GetRevision(),
\r
2736 CTSVNPath(EscapeUrl(m_diffURL)), GetRevision());
\r
2740 if (PromptShown())
\r
2741 diff.ShowUnifiedDiff(CTSVNPath(EscapeUrl(urlList[0])), GetRevision(),
\r
2742 CTSVNPath(EscapeUrl(urlList[1])), GetRevision());
\r
2744 CAppUtils::StartShowUnifiedDiff(m_hWnd, CTSVNPath(EscapeUrl(urlList[0])), GetRevision(),
\r
2745 CTSVNPath(EscapeUrl(urlList[1])), GetRevision());
\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
2756 if (PromptShown())
\r
2757 diff.ShowCompare(CTSVNPath(EscapeUrl(urlList[0])), GetRevision(),
\r
2758 CTSVNPath(EscapeUrl(m_diffURL)), GetRevision(), SVNRev(), true);
\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
2766 if (PromptShown())
\r
2767 diff.ShowCompare(CTSVNPath(EscapeUrl(urlList[0])), GetRevision(),
\r
2768 CTSVNPath(EscapeUrl(urlList[1])), GetRevision(), SVNRev(), true);
\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
2778 if (GetRevision().IsHead())
\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
2786 escapedlist.AddPath(CTSVNPath(EscapeUrl(urlList[i])));
\r
2788 dlg.SetPathList(escapedlist);
\r
2789 dlg.SetRevision(GetHEADRevision(urlList[0]));
\r
2795 dlg.m_rev = GetRevision();
\r
2796 dlg.m_Path = CTSVNPath(EscapeUrl(urlList[0]));
\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
2809 escapedlist.AddPath(CTSVNPath(EscapeUrl(urlList[i])));
\r
2811 dlg.SetPathList(escapedlist);
\r
2812 dlg.SetRevision(GetRevision());
\r
2813 dlg.RevProps(true);
\r
2820 dlg.EndRev = GetRevision();
\r
2821 if (dlg.DoModal() == IDOK)
\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
2829 if (dlg.m_bTextView)
\r
2831 //open the default text editor for the result file
\r
2832 CAppUtils::StartTextViewer(tempfile);
\r
2836 CString sParams = _T("/path:\"") + urlList[0].GetSVNPathString() + _T("\" ");
\r
2837 if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(urlList[0].GetFileOrDirectoryName()),sParams))
\r
2845 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseSVN"), MB_ICONERROR);
\r
2853 DialogEnableWindow(IDOK, TRUE);
\r
2858 bool CRepositoryBrowser::AskForSavePath(const CTSVNPathList& urlList, CTSVNPath &tempfile, bool bFolder)
\r
2860 bool bSavePathOK = false;
\r
2861 if ((!bFolder)&&(urlList.GetCount() == 1))
\r
2863 CString savePath = urlList[0].GetFilename();
\r
2864 bSavePathOK = CAppUtils::FileOpenSave(savePath, NULL, IDS_REPOBROWSE_SAVEAS, IDS_COMMONFILEFILTER, false, m_hWnd);
\r
2866 tempfile.SetFromWin(savePath);
\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
2876 bSavePathOK = true;
\r
2877 tempfile.SetFromWin(sTempfile);
\r
2880 return bSavePathOK;
\r
2883 bool CRepositoryBrowser::StringToWidthArray(const CString& WidthString, int WidthArray[])
\r
2886 for (int i=0; i<7; ++i)
\r
2888 CString hex = WidthString.Mid(i*8, 8);
\r
2889 if ( hex.IsEmpty() )
\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
2897 WidthArray[i] = _tcstol(hex, &endchar, 16);
\r
2903 CString CRepositoryBrowser::WidthArrayToString(int WidthArray[])
\r
2907 for (int i=0; i<7; ++i)
\r
2909 _stprintf_s(buf, 10, _T("%08X"), WidthArray[i]);
\r
2915 void CRepositoryBrowser::SaveColumnWidths(bool bSaveToRegistry /* = false */)
\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
2924 m_arColumnWidths[col] = m_RepoList.GetColumnWidth(col);
\r
2925 if (m_arColumnWidths[col] == m_arColumnAutoWidths[col])
\r
2926 m_arColumnWidths[col] = 0;
\r
2928 if (bSaveToRegistry)
\r
2930 CString sWidths = WidthArrayToString(m_arColumnWidths);
\r
2931 regColWidth = sWidths;
\r