]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/samples/ide/activegrid/tool/SVNService.py
removed code inside USE_SIZABLE_CALENDAR, we should allow making the main calendar...
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / SVNService.py
index 97206c5d9f4735fb138b540486bb110f9e669dc8..c5e2af652f52e01bb3716f5703974ed25a149455 100644 (file)
@@ -11,6 +11,7 @@
 #----------------------------------------------------------------------------
 
 import wx
+import os
 import os.path
 import ProjectEditor
 import MessageService
@@ -31,7 +32,7 @@ _ = wx.GetTranslation
 # Constants
 #----------------------------------------------------------------------------
 SVN_CONFIG_DIR = "SVNConfigDir"
-SVN_REPOSITORY_URL = "SVNRepositoryURL"
+SVN_REPOSITORY_URL = "SVNRepositoryURLs"
 
 SPACE = 10
 HALF_SPACE = 5
@@ -42,13 +43,15 @@ HALF_SPACE = 5
 #----------------------------------------------------------------------------
 
 class SVNService(wx.lib.pydocview.DocService):
+    SVN_UPDATE_ALL_ID = wx.NewId()
     SVN_UPDATE_ID = wx.NewId()
     SVN_CHECKIN_ID = wx.NewId()
+    SVN_CHECKIN_ALL_ID = wx.NewId()
     SVN_CHECKOUT_ID = wx.NewId()
     SVN_REVERT_ID = wx.NewId()
     SVN_ADD_ID = wx.NewId()
     SVN_DELETE_ID = wx.NewId()
-    SVN_COMMAND_LIST = [SVN_UPDATE_ID, SVN_CHECKIN_ID, SVN_CHECKOUT_ID, SVN_REVERT_ID, SVN_ADD_ID, SVN_DELETE_ID]
+    SVN_COMMAND_LIST = [SVN_UPDATE_ALL_ID, SVN_CHECKIN_ALL_ID, SVN_UPDATE_ID, SVN_CHECKIN_ID, SVN_CHECKOUT_ID, SVN_REVERT_ID, SVN_ADD_ID, SVN_DELETE_ID]
 
 
     def __init__(self):
@@ -57,13 +60,14 @@ class SVNService(wx.lib.pydocview.DocService):
         global SVN_INSTALLED
         if SVN_INSTALLED:
             config = wx.ConfigBase_Get()
+            
             configDir = config.Read(SVN_CONFIG_DIR, "")
-
             self._client = pysvn.Client(configDir)
             try:
                 self._defaultURL = self._client.info('.').url
             except:
                 pass
+                
             self._client.callback_cancel = self.IfCancel
             self._client.callback_notify = self.UpdateStatus
             self._client.callback_get_log_message = self.GetLogMessage
@@ -95,6 +99,7 @@ class SVNService(wx.lib.pydocview.DocService):
                 _("Comment"),
                 _("SVN Log Message"))
 
+        dlg.CenterOnParent()
         if dlg.ShowModal() == wx.ID_OK:
             retcode = True
             message = dlg.GetValue()
@@ -137,11 +142,12 @@ class SVNService(wx.lib.pydocview.DocService):
         dlg.Fit()
         dlg.Layout()
 
+        dlg.CenterOnParent()
         if dlg.ShowModal() == wx.ID_OK:
             retcode = True
             username = usernameTxt.GetValue().strip()
             password = passwordTxt.GetValue()
-            save = savePasswordCheckBox.IsChecked()
+            save = savePasswordCheckbox.IsChecked()
         else:
             retcode = False
             username = None
@@ -191,6 +197,7 @@ class SVNService(wx.lib.pydocview.DocService):
         acceptedFailures = 0
         save = False
 
+        dlg.CenterOnParent()
         if dlg.ShowModal() == wx.ID_OK:
             cert = certRadio.GetStringSelection()
             if cert == _("Accept Always"):
@@ -202,6 +209,7 @@ class SVNService(wx.lib.pydocview.DocService):
                 acceptedFailures = trustDict.get('failures')
                 save = False
 
+        dlg.Destroy()
         return retcode, acceptedFailures, save
 
 
@@ -234,10 +242,11 @@ class SVNService(wx.lib.pydocview.DocService):
         dlg.Fit()
         dlg.Layout()
 
+        dlg.CenterOnParent()
         if dlg.ShowModal() == wx.ID_OK:
             retcode = True
             password = passwordTxt.GetValue()
-            save = savePasswordCheckBox.IsChecked()
+            save = savePasswordCheckbox.IsChecked()
         else:
             retcode = False
             password = None
@@ -249,10 +258,11 @@ class SVNService(wx.lib.pydocview.DocService):
 
     def SSLClientCert(self):
         dlg = wx.FileDialog(wx.GetApp().GetTopWindow(),
-            message="Choose certificate", defaultDir=os.getcwd(), 
-            style=wx.OPEN|wx.CHANGE_DIR
+            message="Choose certificate",
+            style=wx.OPEN|wx.FILE_MUST_EXIST|wx.CHANGE_DIR
             )
             
+        # dlg.CenterOnParent()  # wxBug: caused crash with wx.FileDialog
         if dlg.ShowModal() == wx.ID_OK:
             retcode = True
             certfile = dlg.GetPath()
@@ -274,6 +284,12 @@ class SVNService(wx.lib.pydocview.DocService):
 
         menu.AppendSeparator()
 
+        wx.EVT_MENU(frame, SVNService.SVN_UPDATE_ALL_ID, self.ProcessEvent)
+        wx.EVT_UPDATE_UI(frame, SVNService.SVN_UPDATE_ALL_ID, self.ProcessUpdateUIEvent)
+        menu.Append(SVNService.SVN_UPDATE_ALL_ID, _("SVN Update All in Project"), _("Update all files in a project from Subversion"))
+        wx.EVT_MENU(frame, SVNService.SVN_CHECKIN_ALL_ID, self.ProcessEvent)
+        wx.EVT_UPDATE_UI(frame, SVNService.SVN_CHECKIN_ALL_ID, self.ProcessUpdateUIEvent)
+        menu.Append(SVNService.SVN_CHECKIN_ALL_ID, _("SVN Commit All in Project..."), _("Commit all files changes in a project to Subversion"))
         wx.EVT_MENU(frame, SVNService.SVN_UPDATE_ID, self.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, SVNService.SVN_UPDATE_ID, self.ProcessUpdateUIEvent)
         menu.Append(SVNService.SVN_UPDATE_ID, _("SVN Update"), _("Update file from Subversion"))
@@ -308,336 +324,535 @@ class SVNService(wx.lib.pydocview.DocService):
             return False
 
 
-        if id == SVNService.SVN_UPDATE_ID:
-            filenames = self.GetCurrentDocuments()
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Update:\n"))
-
-            for filename in filenames:
-                view.AddLines("%s\n" % filename)
+        try:
+            if id == SVNService.SVN_UPDATE_ID:
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+                
+                filenames = self.GetCurrentDocuments()
+                if filenames:
+                    filenames = filenames[:]
+                    filenames.sort(self.BasenameCaseInsensitiveCompare)
+                else:
+                    folderPath = self.GetCurrentFolder()
+                    if folderPath:
+                        filenames = [folderPath]
+    
+                messageService = wx.GetApp().GetService(MessageService.MessageService)
+                messageService.ShowWindow()
+    
+                view = messageService.GetView()
+                view.ClearLines()
+                view.AddLines(_("SVN Update:\n"))
+    
+                for filename in filenames:
+                    view.AddLines("%s\n" % filename)
+                    try:
+                        status = self._client.update(filename)
+        
+                        if status.number > 0:
+                            view.AddLines(_("Updated to revision %s\n") % status.number)
+    
+                            openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                            for doc in openDocs:
+                                if doc.GetFilename() == filename:
+                                    yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                             _("Updated file '%s' is currently open.  Close it?") % os.path.basename(filename),
+                                                             _("Close File"),
+                                                             wx.YES_NO|wx.ICON_QUESTION)
+                                    yesNoMsg.CenterOnParent()
+                                    status = yesNoMsg.ShowModal()
+                                    yesNoMsg.Destroy()
+                                    if status == wx.ID_YES:
+                                        doc.DeleteAllViews()
+                                    break
+                        else:
+                            view.AddLines(_("Update failed.\n"))
+        
+                    except pysvn.ClientError, e:
+                        view.AddLines("%s\n" % str(e))
+                        wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+                    except:
+                        extype, ex, tb = sys.exc_info()
+                        view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
+                        for line in traceback.format_tb(tb):
+                            view.AddLines(line)
+        
+                        wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+    
+                return True
+                
+            elif id == SVNService.SVN_UPDATE_ALL_ID:
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+    
+                messageService = wx.GetApp().GetService(MessageService.MessageService)
+                messageService.ShowWindow()
+    
+                view = messageService.GetView()
+                view.ClearLines()
+                view.AddLines(_("SVN Update:\n"))
+                
+                project = self.GetCurrentProject()
+                if project:
+                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                    for doc in openDocs:
+                        if doc.GetFilename() == project:
+                            filenames = doc.GetFiles()[:]  # make a copy and sort it.
+                            filenames.sort(self.BasenameCaseInsensitiveCompare)
+    
+                            for filename in filenames:
+                                view.AddLines("%s\n" % filename)
+                                try:
+                                    status = self._client.update(filename)
+                    
+                                    if status.number > 0:
+                                        view.AddLines(_("Updated to revision %s\n") % status.number)
+                
+                                        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                                        for doc in openDocs:
+                                            if doc.GetFilename() == filename:
+                                                yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                                         _("Updated file '%s' is currently open.  Close it?") % os.path.basename(filename),
+                                                                         _("Close File"),
+                                                                         wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
+                                                yesNoMsg.CenterOnParent()
+                                                status = yesNoMsg.ShowModal()
+                                                yesNoMsg.Destroy()
+                                                if status == wx.ID_YES:
+                                                    doc.DeleteAllViews()
+                                                elif status == wx.ID_NO:
+                                                    pass
+                                                else: # elif status == wx.CANCEL:
+                                                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                                                    return True
+                                                break
+                                    else:
+                                        view.AddLines(_("Update failed.\n"))
+                    
+                                except pysvn.ClientError, e:
+                                    view.AddLines("%s\n" % str(e))
+                                    wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+                                except:
+                                    extype, ex, tb = sys.exc_info()
+                                    view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
+                                    for line in traceback.format_tb(tb):
+                                        view.AddLines(line)
+                    
+                                    wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                return True
+    
+            elif id == SVNService.SVN_CHECKIN_ALL_ID:
+                filenames = []
+                project = self.GetCurrentProject()
+                if project:
+                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                    for doc in openDocs:
+                        if doc.GetFilename() == project:
+                            for filename in doc.GetFiles():
+                                if filename not in filenames:
+                                    filenames.append(filename)
+                filenames.sort(self.BasenameCaseInsensitiveCompare)
+    
+                # ask user if dirty files should be saved first
+                openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                for filename in filenames:
+                    for doc in openDocs:
+                        if doc.GetFilename() == filename and doc.IsModified():
+                            yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                     _("'%s' has unsaved modifications.  Save it before commit?") % os.path.basename(filename),
+                                                     _("SVN Commit"),
+                                                     wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
+                            yesNoMsg.CenterOnParent()
+                            status = yesNoMsg.ShowModal()
+                            yesNoMsg.Destroy()
+                            if status == wx.ID_YES:
+                                doc.Save()
+                            elif status == wx.ID_NO:
+                                pass
+                            else: # elif status == wx.CANCEL:
+                                return True
+                            break
+    
+                shortFilenames = []
+                for i, filename in enumerate(filenames):
+                    shortFilename = os.path.basename(filename)
+                    shortFilenames.append(shortFilename)
+    
+                dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
+    
+                sizer = wx.BoxSizer(wx.VERTICAL)
+                sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
+                commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
+                sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
+    
+                sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
+                fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
+                for i in range(fileList.GetCount()):
+                    fileList.Check(i, True)
+                sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
+    
+                buttonSizer = wx.StdDialogButtonSizer()
+                okBtn = wx.Button(dlg, wx.ID_OK)
+                okBtn.SetDefault()
+                buttonSizer.AddButton(okBtn)
+                buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
+                buttonSizer.Realize()
+    
+                contentSizer = wx.BoxSizer(wx.VERTICAL)
+                contentSizer.Add(sizer, 0, wx.ALL, SPACE)
+                contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
+    
+                dlg.SetSizer(contentSizer)
+                dlg.Fit()
+                dlg.Layout()
+    
+                dlg.CenterOnParent()
+                if dlg.ShowModal() == wx.ID_OK:
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+    
+                    messageService = wx.GetApp().GetService(MessageService.MessageService)
+                    messageService.ShowWindow()
+    
+                    view = messageService.GetView()
+                    view.ClearLines()
+                    view.AddLines(_("SVN Commit:\n"))
+    
+                    try:
+                        selFilenames = []
+                        for i in range(fileList.GetCount()):
+                            if fileList.IsChecked(i):
+                                selFilenames.append(filenames[i])
+                                view.AddLines("%s\n" % filenames[i])
+                            
+                        if len(selFilenames):
+                            comment = commentText.GetValue()
+                            status = self._client.checkin(selFilenames, comment)
+    
+                            if status is None:
+                                view.AddLines(_("Nothing to commit.\n"))
+                            elif status.number > 0:
+                                view.AddLines(_("Committed as revision %s.\n") % status.number)
+                            else:
+                                view.AddLines(_("Commit failed.\n"))
+    
+                    except pysvn.ClientError, e:
+                        view.AddLines("%s\n" % str(e))
+                        wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+                    except:
+                        extype, ex, tb = sys.exc_info()
+                        view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
+                        for line in traceback.format_tb(tb):
+                            view.AddLines(line)
+                        wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                dlg.Destroy()
+                return True
+    
+    
+            elif id == SVNService.SVN_CHECKIN_ID:
+                filenames = self.GetCurrentDocuments()[:]
+                filenames.sort(self.BasenameCaseInsensitiveCompare)            
+    
+                # ask user if dirty files should be saved first
+                openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                for filename in filenames:
+                    for doc in openDocs:
+                        if doc.GetFilename() == filename and doc.IsModified():
+                            yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                     _("'%s' has unsaved modifications.  Save it before commit?") % os.path.basename(filename),
+                                                     _("SVN Commit"),
+                                                     wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
+                            yesNoMsg.CenterOnParent()
+                            status = yesNoMsg.ShowModal()
+                            yesNoMsg.Destroy()
+                            if status == wx.ID_YES:
+                                doc.Save()
+                            elif status == wx.ID_NO:
+                                pass
+                            else: # elif status == wx.CANCEL:
+                                return True
+                            break
+    
+                shortFilenames = []
+                for i, filename in enumerate(filenames):
+                    shortFilename = os.path.basename(filename)
+                    shortFilenames.append(shortFilename)
+    
+                dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
+    
+                sizer = wx.BoxSizer(wx.VERTICAL)
+                sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
+                commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
+                sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
+    
+                sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
+                fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
+                for i in range(fileList.GetCount()):
+                    fileList.Check(i, True)
+                sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
+    
+                buttonSizer = wx.StdDialogButtonSizer()
+                okBtn = wx.Button(dlg, wx.ID_OK)
+                okBtn.SetDefault()
+                buttonSizer.AddButton(okBtn)
+                buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
+                buttonSizer.Realize()
+                
+                contentSizer = wx.BoxSizer(wx.VERTICAL)
+                contentSizer.Add(sizer, 0, wx.ALL, SPACE)
+                contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
+    
+                dlg.SetSizer(contentSizer)
+                dlg.Fit()
+                dlg.Layout()
+    
+                dlg.CenterOnParent()
+                if dlg.ShowModal() == wx.ID_OK:
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+    
+                    messageService = wx.GetApp().GetService(MessageService.MessageService)
+                    messageService.ShowWindow()
+    
+                    view = messageService.GetView()
+                    view.ClearLines()
+                    view.AddLines(_("SVN Commit:\n"))
+    
+                    try:
+                        selFilenames = []
+                        for i in range(fileList.GetCount()):
+                            if fileList.IsChecked(i):
+                                selFilenames.append(filenames[i])
+                                view.AddLines("%s\n" % filenames[i])
+                            
+                        if len(selFilenames):
+                            comment = commentText.GetValue()
+                            status = self._client.checkin(selFilenames, comment)
+    
+                            if status is None:
+                                view.AddLines(_("Nothing to commit.\n"))
+                            elif status.number > 0:
+                                view.AddLines(_("Committed as revision %s.\n") % status.number)
+                            else:
+                                view.AddLines(_("Commit failed.\n"))
+    
+                    except pysvn.ClientError, e:
+                        view.AddLines("%s\n" % str(e))
+                        wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+                    except:
+                        extype, ex, tb = sys.exc_info()
+                        view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
+                        for line in traceback.format_tb(tb):
+                            view.AddLines(line)
+                        wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                dlg.Destroy()
+                return True
+    
+            elif id == SVNService.SVN_CHECKOUT_ID:
+                config = wx.ConfigBase_Get()
+                svnUrl = config.Read(SVN_REPOSITORY_URL, self._defaultURL)
+    
+                dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Checkout"))
+    
+                gridSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
+                gridSizer.Add(wx.StaticText(dlg, -1, _("Repository URL:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
+                svnUrlList = ReadSvnUrlList()
+                svnURLCombobox = wx.ComboBox(dlg, -1, size=(200, -1), choices=svnUrlList, style=wx.CB_DROPDOWN)
+                if len(svnUrlList):
+                    svnURLCombobox.SetToolTipString(svnUrlList[0])
+                    svnURLCombobox.SetStringSelection(svnUrlList[0])
+                else:
+                    svnURLCombobox.SetToolTipString(_("Set Repository URL"))
+                gridSizer.Add(svnURLCombobox, 0)
+    
+                gridSizer.Add(wx.StaticText(dlg, -1, _("Checkout to dir:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
+                localPath = wx.TextCtrl(dlg, -1, size = (200, -1))
+                localPath.SetToolTipString(_("Path in local file system where files will be located."))
+                findDirButton = wx.Button(dlg, -1, _("Browse..."))
+    
+                def OnBrowseButton(event):
+                    dirDlg = wx.DirDialog(wx.GetApp().GetTopWindow(), _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
+                    dir = localPath.GetValue()
+                    if len(dir):
+                        dirDlg.SetPath(dir)
+                    dirDlg.CenterOnParent()
+                    if dirDlg.ShowModal() == wx.ID_OK:
+                        localPath.SetValue(dirDlg.GetPath())
+                        localPath.SetToolTipString(localPath.GetValue())
+                        localPath.SetInsertionPointEnd()
+                    dirDlg.Destroy()
+                wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
+    
+                sizer = wx.BoxSizer(wx.HORIZONTAL)
+                sizer.Add(localPath, 1, wx.EXPAND)
+                sizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
+                gridSizer.Add(sizer, 0)
+    
+                buttonSizer = wx.StdDialogButtonSizer()
+                okBtn = wx.Button(dlg, wx.ID_OK)
+                okBtn.SetDefault()
+                buttonSizer.AddButton(okBtn)
+                buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
+                buttonSizer.Realize()
+    
+                contentSizer = wx.BoxSizer(wx.VERTICAL)
+                contentSizer.Add(gridSizer, 0, wx.ALL, SPACE)
+                contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
+    
+                dlg.SetSizer(contentSizer)
+                dlg.Fit()
+                dlg.Layout()
+    
+                dlg.CenterOnParent()
+                if dlg.ShowModal() == wx.ID_OK:
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+                    
+                    WriteSvnUrlList(svnURLCombobox)
+    
+                    messageService = wx.GetApp().GetService(MessageService.MessageService)
+                    messageService.ShowWindow()
+    
+                    view = messageService.GetView()
+                    view.ClearLines()
+                    view.AddLines(_("SVN Checkout:\n"))
+    
+                    svnUrl = svnURLCombobox.GetValue()
+                    toLocation = localPath.GetValue()
+                    try:
+                        self._client.checkout(svnUrl, toLocation)
+                        view.AddLines(_("Checkout completed.\n"))
+                    except pysvn.ClientError, e:
+                        view.AddLines(_("Checkout failed.  %s\n") % str(e))
+                        wx.MessageBox(_("Checkout failed.  %s") % str(e), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
+                    except:
+                        extype, ex, tb = sys.exc_info()
+                        view.AddLines("Checkout failed: (%s) %s\n" % (extype, str(ex)))
+                        for line in traceback.format_tb(tb):
+                            view.AddLines(line)
+                        wx.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                dlg.Destroy()
+                return True
+    
+            elif id == SVNService.SVN_REVERT_ID:
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+    
+                filenames = self.GetCurrentDocuments()
+    
+                messageService = wx.GetApp().GetService(MessageService.MessageService)
+                messageService.ShowWindow()
+    
+                view = messageService.GetView()
+                view.ClearLines()
+                view.AddLines(_("SVN Revert:\n"))
+                for filename in filenames:
+                    view.AddLines("%s\n" % filename)
+    
                 try:
-                    status = self._client.update(filename)
-    
-                    if status.number > 0:
-                        view.AddLines(_("Updated to revision %s\n") % status.number)
-
-                        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                        for doc in openDocs:
-                            if doc.GetFilename() == filename:
-                                yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                         _("Updated file '%s' is currently open.  Close it?") % os.path.basename(filename),
-                                                         _("Close File"),
-                                                         wx.YES_NO|wx.ICON_QUESTION)
-                                if yesNoMsg.ShowModal() == wx.ID_YES:
-                                    doc.DeleteAllViews()
-                                break
-                    else:
-                        view.AddLines(_("Update failed.\n"))
+                    self._client.revert(filenames)
+                    view.AddLines(_("Revert completed.\n"))
+                    
+                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                    for doc in openDocs[:]:   # need to make a copy of the list otherwise ordinality changes as we close the files
+                        if doc.GetFilename() in filenames:
+                            yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                     _("Reverted file '%s' is currently open.  Close it?") % os.path.basename(doc.GetFilename()),
+                                                     _("Close File"),
+                                                     wx.YES_NO|wx.ICON_QUESTION)
+                            yesNoMsg.CenterOnParent()
+                            status = yesNoMsg.ShowModal()
+                            yesNoMsg.Destroy()
+                            if status == wx.ID_YES:
+                                doc.DeleteAllViews()
     
                 except pysvn.ClientError, e:
                     view.AddLines("%s\n" % str(e))
-                    wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+                    wx.MessageBox(str(e), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
                 except:
                     extype, ex, tb = sys.exc_info()
-                    view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
+                    view.AddLines("Revert failed: (%s) %s\n" % (extype, str(ex)))
                     for line in traceback.format_tb(tb):
                         view.AddLines(line)
+                    wx.MessageBox(_("Revert failed."), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
     
-                    wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-
-            return True
-
-        elif id == SVNService.SVN_CHECKIN_ID:
-            filenames = self.GetCurrentDocuments()
-
-            # ask user if dirty files should be saved first
-            openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-            for filename in filenames:
-                for doc in openDocs:
-                    if doc.GetFilename() == filename and doc.IsModified():
-                        yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                 _("'%s' has unsaved modifications.  Save it before commit?") % os.path.basename(filename),
-                                                 _("SVN Commit"),
-                                                 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
-                        status = yesNoMsg.ShowModal()
-                        if status == wx.ID_YES:
-                            doc.Save()
-                        elif status == wx.ID_NO:
-                            pass
-                        else: # elif status == wx.CANCEL:
-                            return True
-                        break
-
-            shortFilenames = []
-            for i, filename in enumerate(filenames):
-                shortFilename = os.path.basename(filename)
-                shortFilenames.append(shortFilename)
-
-            dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
-
-            sizer = wx.BoxSizer(wx.VERTICAL)
-            sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
-            commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
-            sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
-
-            sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
-            fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
-            for i in range(fileList.GetCount()):
-                fileList.Check(i, True)
-            sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
-
-            buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
-            okBtn = wx.Button(dlg, wx.ID_OK)
-            okBtn.SetDefault()
-            buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
-            buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
-
-            contentSizer = wx.BoxSizer(wx.VERTICAL)
-            contentSizer.Add(sizer, 0, wx.ALL, SPACE)
-            contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
-
-            dlg.SetSizer(contentSizer)
-            dlg.Fit()
-            dlg.Layout()
-
-            if dlg.ShowModal() == wx.ID_OK:
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                return True
+    
+            elif id == SVNService.SVN_ADD_ID:
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
+    
+                filenames = self.GetCurrentDocuments()
+    
                 messageService = wx.GetApp().GetService(MessageService.MessageService)
                 messageService.ShowWindow()
-
+    
                 view = messageService.GetView()
                 view.ClearLines()
-                view.AddLines(_("SVN Commit:\n"))
-
+                view.AddLines(_("SVN Add:\n"))
+                for filename in filenames:
+                    view.AddLines("%s\n" % filename)
+    
                 try:
-                    selFilenames = []
-                    for i in range(fileList.GetCount()):
-                        if fileList.IsChecked(i):
-                            selFilenames.append(filenames[i])
-                            view.AddLines("%s\n" % filenames[i])
-                        
-                    if len(selFilenames):
-                        comment = commentText.GetValue()
-                        status = self._client.checkin(selFilenames, comment)
-
-                        if status is None:
-                            view.AddLines(_("Nothing to commit.\n"))
-                        elif status.number > 0:
-                            view.AddLines(_("Committed as revision %s.\n") % status.number)
-                        else:
-                            view.AddLines(_("Commit failed.\n"))
-
+                    self._client.add(filenames)
+                    view.AddLines(_("Add completed.\n"))
                 except pysvn.ClientError, e:
                     view.AddLines("%s\n" % str(e))
-                    wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+                    wx.MessageBox(str(e), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
                 except:
                     extype, ex, tb = sys.exc_info()
-                    view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
+                    view.AddLines("Add failed: (%s) %s\n" % (extype, str(ex)))
                     for line in traceback.format_tb(tb):
                         view.AddLines(line)
-                    wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
-
+                    wx.MessageBox(_("Add failed."), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
+    
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            dlg.Destroy()
-            return True
-
-        elif id == SVNService.SVN_CHECKOUT_ID:
-            config = wx.ConfigBase_Get()
-            svnUrl = config.Read(SVN_REPOSITORY_URL, self._defaultURL)
-
-            dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Checkout"))
-
-            gridSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
-            gridSizer.Add(wx.StaticText(dlg, -1, _("Repository URL:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
-            svnURLText = wx.TextCtrl(dlg, -1, svnUrl, size = (200, -1))
-            svnURLText.SetToolTipString(svnUrl)
-            gridSizer.Add(svnURLText, 0)
-
-            gridSizer.Add(wx.StaticText(dlg, -1, _("Checkout to dir:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
-            localPath = wx.TextCtrl(dlg, -1, size = (200, -1))
-            localPath.SetToolTipString(_("Path in local file system where files will be located."))
-            findDirButton = wx.Button(dlg, -1, _("Browse..."))
-
-            def OnBrowseButton(event):
-                dirDlg = wx.DirDialog(wx.GetApp().GetTopWindow(), _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
-                dir = localPath.GetValue()
-                if len(dir):
-                    dirDlg.SetPath(dir)
-                if dirDlg.ShowModal() == wx.ID_OK:
-                    localPath.SetValue(dirDlg.GetPath())
-                    localPath.SetToolTipString(localPath.GetValue())
-                    localPath.SetInsertionPointEnd()
-
-                dirDlg.Destroy()
-            wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
-
-            sizer = wx.BoxSizer(wx.HORIZONTAL)
-            sizer.Add(localPath, 1, wx.EXPAND)
-            sizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
-            gridSizer.Add(sizer, 0)
-
-            buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
-            okBtn = wx.Button(dlg, wx.ID_OK)
-            okBtn.SetDefault()
-            buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
-            buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
-
-            contentSizer = wx.BoxSizer(wx.VERTICAL)
-            contentSizer.Add(gridSizer, 0, wx.ALL, SPACE)
-            contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
-
-            dlg.SetSizer(contentSizer)
-            dlg.Fit()
-            dlg.Layout()
-
-            if dlg.ShowModal() == wx.ID_OK:
+                return True
+    
+            elif id == SVNService.SVN_DELETE_ID:
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
+    
+                filenames = self.GetCurrentDocuments()
+    
                 messageService = wx.GetApp().GetService(MessageService.MessageService)
                 messageService.ShowWindow()
-
+    
                 view = messageService.GetView()
                 view.ClearLines()
-                view.AddLines(_("SVN Checkout:\n"))
-
-                svnUrl = svnURLText.GetValue()
-                toLocation = localPath.GetValue()
+                view.AddLines(_("SVN Delete:\n"))
+                for filename in filenames:
+                    view.AddLines("%s\n" % filename)
+    
                 try:
-                    self._client.checkout(svnUrl, toLocation)
-                    view.AddLines(_("Checkout completed.\n"))
+                    self._client.remove(filenames)
+                    view.AddLines(_("Delete completed.\n"))
                 except pysvn.ClientError, e:
-                    view.AddLines(_("Checkout failed.  %s\n") % str(e))
-                    wx.MessageBox(_("Checkout failed.  %s") % str(e), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
+                    view.AddLines("%s\n" % str(e))
+                    wx.MessageBox(str(e), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
                 except:
                     extype, ex, tb = sys.exc_info()
-                    view.AddLines("Checkout failed: (%s) %s\n" % (extype, str(ex)))
+                    view.AddLines("Delete failed: (%s) %s\n" % (extype, str(ex)))
                     for line in traceback.format_tb(tb):
                         view.AddLines(line)
-                    wx.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
-
+                    wx.MessageBox(_("Delete failed."), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
+    
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            dlg.Destroy()
-            return True
-
-        elif id == SVNService.SVN_REVERT_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
-            filenames = self.GetCurrentDocuments()
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Revert:\n"))
-            for filename in filenames:
-                view.AddLines("%s\n" % filename)
-
-            try:
-                self._client.revert(filenames)
-                view.AddLines(_("Revert completed.\n"))
-                
-                openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                for doc in openDocs[:]:   # need to make a copy of the list otherwise ordinality changes as we close the files
-                    if doc.GetFilename() in filenames:
-                        yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                 _("Reverted file '%s' is currently open.  Close it?") % os.path.basename(doc.GetFilename()),
-                                                 _("Close File"),
-                                                 wx.YES_NO|wx.ICON_QUESTION)
-                        if yesNoMsg.ShowModal() == wx.ID_YES:
-                            doc.DeleteAllViews()
-
-            except pysvn.ClientError, e:
-                view.AddLines("%s\n" % str(e))
-                wx.MessageBox(str(e), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
-            except:
-                extype, ex, tb = sys.exc_info()
-                view.AddLines("Revert failed: (%s) %s\n" % (extype, str(ex)))
-                for line in traceback.format_tb(tb):
-                    view.AddLines(line)
-                wx.MessageBox(_("Revert failed."), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            return True
-
-        elif id == SVNService.SVN_ADD_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
-            filenames = self.GetCurrentDocuments()
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Add:\n"))
-            for filename in filenames:
-                view.AddLines("%s\n" % filename)
-
-            try:
-                self._client.add(filenames)
-                view.AddLines(_("Add completed.\n"))
-            except pysvn.ClientError, e:
-                view.AddLines("%s\n" % str(e))
-                wx.MessageBox(str(e), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
-            except:
-                extype, ex, tb = sys.exc_info()
-                view.AddLines("Add failed: (%s) %s\n" % (extype, str(ex)))
-                for line in traceback.format_tb(tb):
-                    view.AddLines(line)
-                wx.MessageBox(_("Add failed."), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
-
+                return True
+    
+            return False
+        finally:
             wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            return True
-
-        elif id == SVNService.SVN_DELETE_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
-            filenames = self.GetCurrentDocuments()
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Delete:\n"))
-            for filename in filenames:
-                view.AddLines("%s\n" % filename)
-
-            try:
-                self._client.remove(filenames)
-                view.AddLines(_("Delete completed.\n"))
-            except pysvn.ClientError, e:
-                view.AddLines("%s\n" % str(e))
-                wx.MessageBox(str(e), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
-            except:
-                extype, ex, tb = sys.exc_info()
-                view.AddLines("Delete failed: (%s) %s\n" % (extype, str(ex)))
-                for line in traceback.format_tb(tb):
-                    view.AddLines(line)
-                wx.MessageBox(_("Delete failed."), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            return True
-
-        return False
 
 
     def ProcessUpdateUIEvent(self, event):
         id = event.GetId()
 
-        if id in [SVNService.SVN_UPDATE_ID,
-                  SVNService.SVN_CHECKIN_ID,
+        if id in [SVNService.SVN_CHECKIN_ID,
                   SVNService.SVN_REVERT_ID,
                   SVNService.SVN_ADD_ID,
                   SVNService.SVN_DELETE_ID]:
@@ -647,20 +862,42 @@ class SVNService(wx.lib.pydocview.DocService):
                 event.Enable(False)
             return True
 
+        elif id == SVNService.SVN_UPDATE_ID:
+            if self.GetCurrentDocuments() or self.GetCurrentFolder():
+                event.Enable(True)
+            else:
+                event.Enable(False)
+            return True
+
         elif id == SVNService.SVN_CHECKOUT_ID:
             event.Enable(True)
             return True
 
+        elif (id == SVNService.SVN_UPDATE_ALL_ID
+        or id == SVNService.SVN_CHECKIN_ALL_ID):
+            if self.GetCurrentProject():
+                event.Enable(True)
+            else:
+                event.Enable(False)
+            return True
+
         return False
 
 
-    def GetCurrentDocuments(self):
+    def GetCurrentProject(self):
+        projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
+        if projectService:
+            projView = projectService.GetView()
+            return projView.GetSelectedProject()
+        return None
 
+
+    def GetCurrentDocuments(self):
         projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
         if projectService:
             projView = projectService.GetView()
 
-            if projView.HasFocus():
+            if projView.FilesHasFocus():
                 filenames = projView.GetSelectedFiles()
                 if len(filenames):
                     return filenames
@@ -676,6 +913,30 @@ class SVNService(wx.lib.pydocview.DocService):
         return filenames
 
 
+    def GetCurrentFolder(self):
+        projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
+        if projectService:
+            projView = projectService.GetView()
+
+            if projView.FilesHasFocus():
+                folderPath = projView.GetSelectedPhysicalFolder()
+                if folderPath:
+                    return folderPath
+
+        return None
+
+
+    def BasenameCaseInsensitiveCompare(self, s1, s2):
+        s1L = os.path.basename(s1).lower()
+        s2L = os.path.basename(s2).lower()
+        if s1L == s2L:
+            return 0
+        elif s1L < s2L:
+            return -1
+        else:
+            return 1
+
+
 class SVNOptionsPanel(wx.Panel):
 
 
@@ -683,12 +944,11 @@ class SVNOptionsPanel(wx.Panel):
         wx.Panel.__init__(self, parent, id)
 
         config = wx.ConfigBase_Get()
-        svnService = wx.GetApp().GetService(SVNService)
-        svnUrl = config.Read(SVN_REPOSITORY_URL, svnService._defaultURL)
         configDir = config.Read(SVN_CONFIG_DIR, "")
 
         borderSizer = wx.BoxSizer(wx.VERTICAL)
         sizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
+        sizer.AddGrowableCol(1, 1)
 
         sizer.Add(wx.StaticText(self, -1, _("SVN Config Dir:")), 0, wx.ALIGN_CENTER_VERTICAL)
 
@@ -705,32 +965,106 @@ class SVNOptionsPanel(wx.Panel):
             dir = self._svnConfigDir.GetValue()
             if len(dir):
                 dirDlg.SetPath(dir)
+            dirDlg.CenterOnParent()
             if dirDlg.ShowModal() == wx.ID_OK:
                 self._svnConfigDir.SetValue(dirDlg.GetPath())
                 self._svnConfigDir.SetToolTipString(self._svnConfigDir.GetValue())
                 self._svnConfigDir.SetInsertionPointEnd()
-
             dirDlg.Destroy()
         wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
 
         hsizer = wx.BoxSizer(wx.HORIZONTAL)
         hsizer.Add(self._svnConfigDir, 1, wx.EXPAND)
         hsizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
-        sizer.Add(hsizer, 0)
+        sizer.Add(hsizer, 0, wx.EXPAND)
 
+
+        svnUrlList = ReadSvnUrlList()
         sizer.Add(wx.StaticText(self, -1, _("SVN URL:")), 0, wx.ALIGN_CENTER_VERTICAL)
-        self._svnURLText = wx.TextCtrl(self, -1, svnUrl, size = (200, -1))
-        self._svnURLText.SetToolTipString(svnUrl)
-        sizer.Add(self._svnURLText, 0)
+        self._svnURLCombobox = wx.ComboBox(self, -1, size=(200, -1), choices=svnUrlList, style=wx.CB_DROPDOWN)
+        if len(svnUrlList):
+            self._svnURLCombobox.SetToolTipString(svnUrlList[0])
+            self._svnURLCombobox.SetStringSelection(svnUrlList[0])
+        else:
+            self._svnURLCombobox.SetToolTipString(_("Set Repository URL"))
+        sizer.Add(self._svnURLCombobox, 0, wx.EXPAND)
+
+
+        sizer.Add(wx.StaticText(self, -1, _("SVN_SSH:")), 0, wx.ALIGN_CENTER_VERTICAL)
+        svnSSH = os.getenv("SVN_SSH")
+        if not svnSSH or svnSSH == "":
+            self._svnSSH = wx.TextCtrl(self, -1, size = (200, -1))
+        else:
+            self._svnSSH = wx.TextCtrl(self, -1, svnSSH, size = (200, -1))
+        self._svnSSH.SetToolTipString(_("Override SVN_SSH environment variable temporarily."))
+        
+        findSSHButton = wx.Button(self, -1, _("Browse..."))
+
+        def OnBrowseFileButton(event):
+            dirDlg = wx.FileDialog(self, _("Choose a file:"), style=wx.OPEN|wx.CHANGE_DIR)
+            # dirDlg.CenterOnParent()  # wxBug: caused crash with wx.FileDialog
+            if dirDlg.ShowModal() == wx.ID_OK:
+                self._svnSSH.SetValue(dirDlg.GetPath())
+                self._svnSSH.SetToolTipString(self._svnSSH.GetValue())
+                self._svnSSH.SetInsertionPointEnd()
+            dirDlg.Destroy()
+        wx.EVT_BUTTON(findSSHButton, -1, OnBrowseFileButton)
+
+        hsizer = wx.BoxSizer(wx.HORIZONTAL)
+        hsizer.Add(self._svnSSH, 1, wx.EXPAND)
+        hsizer.Add(findSSHButton, 0, wx.LEFT, HALF_SPACE)
+        sizer.Add(hsizer, 0, wx.EXPAND)
+
 
-        borderSizer.Add(sizer, 0, wx.ALL, SPACE)
+        borderSizer.Add(sizer, 0, wx.ALL|wx.EXPAND, SPACE)
         self.SetSizer(borderSizer)
         self.Layout()
         parent.AddPage(self, _("SVN"))
 
 
+    def GetIcon(self):
+        return wx.NullIcon
+
+
     def OnOK(self, optionsDialog):
         config = wx.ConfigBase_Get()
+        
         config.Write(SVN_CONFIG_DIR, self._svnConfigDir.GetValue())
-        config.Write(SVN_REPOSITORY_URL, self._svnURLText.GetValue())
+        
+        WriteSvnUrlList(self._svnURLCombobox)
+        
+        os.environ["SVN_SSH"] = self._svnSSH.GetValue()
+        
+
+def ReadSvnUrlList():
+    """ Read in list of SNV repository URLs.  First in list is the last one path used. """
+    config = wx.ConfigBase_Get()
+    urlStringList = config.Read(SVN_REPOSITORY_URL)
+    if len(urlStringList):
+        urlList = eval(urlStringList)
+    else:
+        urlList = []
+    if len(urlList) == 0:
+        svnService = wx.GetApp().GetService(SVNService)
+        if svnService and hasattr(svnService, "_defaultURL"):
+            urlList.append(svnService._defaultURL)
+    return urlList
+
+
+def WriteSvnUrlList(comboBox):
+    """ Save out list of SVN repository URLs from combobox.  Put on top the current selection.  Only save out first 10 from the list """
+    urlList = []
+    
+    url = comboBox.GetValue()
+    if len(url):
+        urlList.append(url)
+        
+    for i in range(min(comboBox.GetCount(), 10)):
+        url = comboBox.GetString(i)
+        if url not in urlList:
+            urlList.append(url)
+
+    config = wx.ConfigBase_Get()
+    config.Write(SVN_REPOSITORY_URL, urlList.__repr__())
+