1 #---------------------------------------------------------------------------- 
   3 # Purpose:      Subversion Service for pydocview 
   9 # Copyright:    (c) 2005 ActiveGrid, Inc. 
  10 # License:      wxWindows License 
  11 #---------------------------------------------------------------------------- 
  19 import sys        
# for errors 
  20 import traceback  
# for errors 
  23     import pysvn  
# python-subversion integration 
  31 #---------------------------------------------------------------------------- 
  33 #---------------------------------------------------------------------------- 
  34 SVN_CONFIG_DIR 
= "SVNConfigDir" 
  35 SVN_REPOSITORY_URL 
= "SVNRepositoryURLs" 
  41 #---------------------------------------------------------------------------- 
  43 #---------------------------------------------------------------------------- 
  45 class SVNService(wx
.lib
.pydocview
.DocService
): 
  46     SVN_UPDATE_ALL_ID 
= wx
.NewId() 
  47     SVN_UPDATE_ID 
= wx
.NewId() 
  48     SVN_CHECKIN_ID 
= wx
.NewId() 
  49     SVN_CHECKIN_ALL_ID 
= wx
.NewId() 
  50     SVN_CHECKOUT_ID 
= wx
.NewId() 
  51     SVN_REVERT_ID 
= wx
.NewId() 
  52     SVN_ADD_ID 
= wx
.NewId() 
  53     SVN_DELETE_ID 
= wx
.NewId() 
  54     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
] 
  58         self
._defaultURL 
= "svn://" 
  62             config 
= wx
.ConfigBase_Get() 
  64             configDir 
= config
.Read(SVN_CONFIG_DIR
, "") 
  65             self
._client 
= pysvn
.Client(configDir
) 
  67                 self
._defaultURL 
= self
._client
.info('.').url
 
  71             self
._client
.callback_cancel 
= self
.IfCancel
 
  72             self
._client
.callback_notify 
= self
.UpdateStatus
 
  73             self
._client
.callback_get_log_message 
= self
.GetLogMessage
 
  74             self
._client
.callback_get_login 
= self
.GetLogin
 
  75             self
._client
.callback_ssl_server_trust_prompt 
= self
.GetSSLServerTrust
 
  76             self
._client
.callback_ssl_client_cert_password_prompt 
= self
.SSLClientPassword
 
  77             self
._client
.callback_ssl_client_cert_prompt 
= self
.SSLClientCert
 
  78             self
._client
.callback_ssl_server_prompt 
= self
.SSLServerPrompt
 
  80     #---------------------------------------------------------------------------- 
  81     # pysvn.Client() Callback Methods 
  82     #---------------------------------------------------------------------------- 
  85         """ return True if user wants to cancel current command """ 
  89     def UpdateStatus(self
, eventDict
): 
  90         messageService 
= wx
.GetApp().GetService(MessageService
.MessageService
) 
  91         messageService
.ShowWindow() 
  93         view 
= messageService
.GetView() 
  94         view
.AddLines(_("%s %s\n") % (eventDict
['action'], eventDict
['path'])) 
  97     def GetLogMessage(self
): 
  98         dlg 
= wx
.TextEntryDialog(wx
.GetApp().GetTopWindow(), 
 100                 _("SVN Log Message")) 
 102         if dlg
.ShowModal() == wx
.ID_OK
: 
 104             message 
= dlg
.GetValue() 
 107             message 
= _("Cancel Action") 
 111         return retcode
, message
 
 114     def GetLogin(self
, realm
, username
, maySave
): 
 115         dlg 
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SVN Login")) 
 117         sizer 
= wx
.FlexGridSizer(cols 
= 2, hgap 
= 5, vgap 
= 5) 
 118         sizer
.Add(wx
.StaticText(dlg
, -1, _("Username:")), 0, wx
.ALIGN_CENTER_VERTICAL
) 
 119         usernameTxt 
= wx
.TextCtrl(dlg
, -1, username
, size 
= (200, -1)) 
 120         sizer
.Add(usernameTxt
, 0, wx
.ALIGN_CENTER_VERTICAL
) 
 121         sizer
.Add(wx
.StaticText(dlg
, -1, _("Password:")), 0, wx
.ALIGN_CENTER_VERTICAL
) 
 122         passwordTxt 
= wx
.TextCtrl(dlg
, -1, size
=(200, -1), style
=wx
.TE_PASSWORD
) 
 123         sizer
.Add(passwordTxt
, 0, wx
.ALIGN_CENTER_VERTICAL
) 
 125         savePasswordCheckbox 
= wx
.CheckBox(dlg
, -1, _("Remember Username and Password")) 
 127             savePasswordCheckbox
.Enable(False) 
 129         buttonSizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 130         okBtn 
= wx
.Button(dlg
, wx
.ID_OK
) 
 132         buttonSizer
.Add(okBtn
, 0, wx
.RIGHT
, HALF_SPACE
) 
 133         buttonSizer
.Add(wx
.Button(dlg
, wx
.ID_CANCEL
), 0) 
 135         contentSizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 136         contentSizer
.Add(sizer
, 0, wx
.LEFT|wx
.TOP|wx
.RIGHT
, SPACE
) 
 137         contentSizer
.Add(savePasswordCheckbox
, 0, wx
.TOP|wx
.LEFT|wx
.BOTTOM
, SPACE
) 
 138         contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
) 
 140         dlg
.SetSizer(contentSizer
) 
 144         if dlg
.ShowModal() == wx
.ID_OK
: 
 146             username 
= usernameTxt
.GetValue().strip() 
 147             password 
= passwordTxt
.GetValue() 
 148             save 
= savePasswordCheckBox
.IsChecked() 
 156         return retcode
, username
, password
, save
 
 159     def SSLServerPrompt(self
): 
 160         """ Not implemented, as per pysvn documentation """ 
 164     def GetSSLServerTrust(self
, trustDict
): 
 165         dlg 
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SSL Server Certificate")) 
 167         sizer 
= wx
.FlexGridSizer(cols 
= 2, hgap 
= 5, vgap 
= 5) 
 168         for k 
in ['hostname', 'valid_from', 'valid_to', 'issuer_dname', 'realm']: 
 169             if trustDict
.has_key(k
): 
 170                 sizer
.Add(wx
.StaticText(dlg
, -1, "%s:" % k
), 0, wx
.ALIGN_CENTER_VERTICAL
) 
 171                 sizer
.Add(wx
.StaticText(dlg
, -1, "%s"  % trustDict
[k
]), 0, wx
.ALIGN_CENTER_VERTICAL
) 
 173         box 
= wx
.StaticBoxSizer(wx
.StaticBox(dlg
, -1, _("Certificate Info")), wx
.VERTICAL
) 
 174         box
.Add(sizer
, 0, wx
.EXPAND
) 
 176         certRadio 
= wx
.RadioBox(dlg
, -1, _("Certificate"), choices
=[_("Accept Always"), _("Accept Once"), _("Reject")], majorDimension
=1, style
=wx
.RA_SPECIFY_COLS
) 
 178         buttonSizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 179         okBtn 
= wx
.Button(dlg
, wx
.ID_OK
) 
 181         buttonSizer
.Add(okBtn
, 0, wx
.RIGHT
, HALF_SPACE
) 
 182         buttonSizer
.Add(wx
.Button(dlg
, wx
.ID_CANCEL
), 0) 
 184         contentSizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 185         contentSizer
.Add(box
, 0, wx
.TOP|wx
.LEFT|wx
.RIGHT|wx
.EXPAND
, SPACE
) 
 186         contentSizer
.Add(certRadio
, 0, wx
.TOP|wx
.LEFT|wx
.RIGHT|wx
.BOTTOM|wx
.EXPAND
, SPACE
) 
 187         contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
) 
 189         dlg
.SetSizer(contentSizer
) 
 193         # default values for reject 
 198         if dlg
.ShowModal() == wx
.ID_OK
: 
 199             cert 
= certRadio
.GetStringSelection() 
 200             if cert 
== _("Accept Always"): 
 202                 acceptedFailures 
= trustDict
.get('failures') 
 204             elif cert 
== _("Accept Once"): 
 206                 acceptedFailures 
= trustDict
.get('failures') 
 209         return retcode
, acceptedFailures
, save
 
 212     def SSLClientPassword(self
, realm
, maySave
): 
 213         dlg 
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SSL Client Certificate Login")) 
 215         sizer 
= wx
.FlexGridSizer(cols 
= 2, hgap 
= 5, vgap 
= 5) 
 216         sizer
.Add(wx
.StaticText(dlg
, -1, _("Realm:")), 0, wx
.ALIGN_CENTER_VERTICAL
) 
 217         sizer
.Add(wx
.StaticText(dlg
, -1, realm
), 0, wx
.ALIGN_CENTER_VERTICAL
) 
 218         sizer
.Add(wx
.StaticText(dlg
, -1, _("Password:")), 0, wx
.ALIGN_CENTER_VERTICAL
) 
 219         passwordTxt 
= wx
.TextCtrl(dlg
, -1, size
=(200, -1), style
=wx
.TE_PASSWORD
) 
 220         sizer
.Add(passwordTxt
, 0, wx
.ALIGN_CENTER_VERTICAL
) 
 222         savePasswordCheckbox 
= wx
.CheckBox(dlg
, -1, _("Remember Password")) 
 224             savePasswordCheckbox
.Enable(False) 
 226         buttonSizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 227         okBtn 
= wx
.Button(dlg
, wx
.ID_OK
) 
 229         buttonSizer
.Add(okBtn
, 0, wx
.RIGHT
, HALF_SPACE
) 
 230         buttonSizer
.Add(wx
.Button(dlg
, wx
.ID_CANCEL
), 0) 
 232         contentSizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 233         contentSizer
.Add(sizer
, 0, wx
.LEFT|wx
.TOP|wx
.RIGHT
, SPACE
) 
 234         contentSizer
.Add(savePasswordCheckbox
, 0, wx
.TOP|wx
.LEFT|wx
.BOTTOM
, SPACE
) 
 235         contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
) 
 237         dlg
.SetSizer(contentSizer
) 
 241         if dlg
.ShowModal() == wx
.ID_OK
: 
 243             password 
= passwordTxt
.GetValue() 
 244             save 
= savePasswordCheckBox
.IsChecked() 
 251         return retcode
, password
, save
 
 254     def SSLClientCert(self
): 
 255         dlg 
= wx
.FileDialog(wx
.GetApp().GetTopWindow(), 
 256             message
="Choose certificate", defaultDir
=os
.getcwd(),  
 257             style
=wx
.OPEN|wx
.CHANGE_DIR
 
 260         if dlg
.ShowModal() == wx
.ID_OK
: 
 262             certfile 
= dlg
.GetPath() 
 268         return retcode
, certfile
 
 272     #---------------------------------------------------------------------------- 
 274     #---------------------------------------------------------------------------- 
 276     def InstallControls(self
, frame
, menuBar 
= None, toolBar 
= None, statusBar 
= None, document 
= None): 
 277         menu 
= menuBar
.GetMenu(menuBar
.FindMenu(_("Project"))) 
 279         menu
.AppendSeparator() 
 281         wx
.EVT_MENU(frame
, SVNService
.SVN_UPDATE_ALL_ID
, self
.ProcessEvent
) 
 282         wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_UPDATE_ALL_ID
, self
.ProcessUpdateUIEvent
) 
 283         menu
.Append(SVNService
.SVN_UPDATE_ALL_ID
, _("SVN Update All in Project"), _("Update all files in a project from Subversion")) 
 284         wx
.EVT_MENU(frame
, SVNService
.SVN_CHECKIN_ALL_ID
, self
.ProcessEvent
) 
 285         wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_CHECKIN_ALL_ID
, self
.ProcessUpdateUIEvent
) 
 286         menu
.Append(SVNService
.SVN_CHECKIN_ALL_ID
, _("SVN Commit All in Project..."), _("Commit all files changes in a project to Subversion")) 
 287         wx
.EVT_MENU(frame
, SVNService
.SVN_UPDATE_ID
, self
.ProcessEvent
) 
 288         wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_UPDATE_ID
, self
.ProcessUpdateUIEvent
) 
 289         menu
.Append(SVNService
.SVN_UPDATE_ID
, _("SVN Update"), _("Update file from Subversion")) 
 290         wx
.EVT_MENU(frame
, SVNService
.SVN_CHECKIN_ID
, self
.ProcessEvent
) 
 291         wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_CHECKIN_ID
, self
.ProcessUpdateUIEvent
) 
 292         menu
.Append(SVNService
.SVN_CHECKIN_ID
, _("SVN Commit..."), _("Commit file changes to Subversion")) 
 293         wx
.EVT_MENU(frame
, SVNService
.SVN_CHECKOUT_ID
, self
.ProcessEvent
) 
 294         wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_CHECKOUT_ID
, self
.ProcessUpdateUIEvent
) 
 295         menu
.Append(SVNService
.SVN_CHECKOUT_ID
, _("SVN Checkout..."), _("Checkout file from Subversion")) 
 296         wx
.EVT_MENU(frame
, SVNService
.SVN_REVERT_ID
, self
.ProcessEvent
) 
 297         wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_REVERT_ID
, self
.ProcessUpdateUIEvent
) 
 298         menu
.Append(SVNService
.SVN_REVERT_ID
, _("SVN Revert"), _("Revert file from Subversion")) 
 300         menu
.AppendSeparator() 
 302         wx
.EVT_MENU(frame
, SVNService
.SVN_ADD_ID
, self
.ProcessEvent
) 
 303         wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_ADD_ID
, self
.ProcessUpdateUIEvent
) 
 304         menu
.Append(SVNService
.SVN_ADD_ID
, _("SVN Add"), _("Add file to Subversion")) 
 305         wx
.EVT_MENU(frame
, SVNService
.SVN_DELETE_ID
, self
.ProcessEvent
) 
 306         wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_DELETE_ID
, self
.ProcessUpdateUIEvent
) 
 307         menu
.Append(SVNService
.SVN_DELETE_ID
, _("SVN Delete"), _("Delete file from Subversion")) 
 310     def ProcessEvent(self
, event
): 
 314         if not SVN_INSTALLED
: 
 315             if id in SVNService
.SVN_COMMAND_LIST
: 
 316                 wx
.MessageBox(_("pysvn not found.  Please install pysvn"), _("Python Subversion")) 
 321         if id == SVNService
.SVN_UPDATE_ID
: 
 322             wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
)) 
 324             filenames 
= self
.GetCurrentDocuments()[:] 
 325             filenames
.sort(self
.BasenameCaseInsensitiveCompare
) 
 327             messageService 
= wx
.GetApp().GetService(MessageService
.MessageService
) 
 328             messageService
.ShowWindow() 
 330             view 
= messageService
.GetView() 
 332             view
.AddLines(_("SVN Update:\n")) 
 334             for filename 
in filenames
: 
 335                 view
.AddLines("%s\n" % filename
) 
 337                     status 
= self
._client
.update(filename
) 
 339                     if status
.number 
> 0: 
 340                         view
.AddLines(_("Updated to revision %s\n") % status
.number
) 
 342                         openDocs 
= wx
.GetApp().GetDocumentManager().GetDocuments() 
 344                             if doc
.GetFilename() == filename
: 
 345                                 yesNoMsg 
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(), 
 346                                                          _("Updated file '%s' is currently open.  Close it?") % os
.path
.basename(filename
), 
 348                                                          wx
.YES_NO|wx
.ICON_QUESTION
) 
 349                                 if yesNoMsg
.ShowModal() == wx
.ID_YES
: 
 353                         view
.AddLines(_("Update failed.\n")) 
 355                 except pysvn
.ClientError
, e
: 
 356                     view
.AddLines("%s\n" % str(e
)) 
 357                     wx
.MessageBox(str(e
), _("SVN Update"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 359                     extype
, ex
, tb 
= sys
.exc_info() 
 360                     view
.AddLines("Update failed: (%s) %s\n" % (extype
, str(ex
))) 
 361                     for line 
in traceback
.format_tb(tb
): 
 364                     wx
.MessageBox(_("Update failed."), _("SVN Update"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 366             wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
)) 
 370         elif id == SVNService
.SVN_UPDATE_ALL_ID
: 
 371             wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
)) 
 373             messageService 
= wx
.GetApp().GetService(MessageService
.MessageService
) 
 374             messageService
.ShowWindow() 
 376             view 
= messageService
.GetView() 
 378             view
.AddLines(_("SVN Update:\n")) 
 380             projects 
= self
.GetCurrentProjects() 
 381             for project 
in projects
: 
 382                 openDocs 
= wx
.GetApp().GetDocumentManager().GetDocuments() 
 384                     if doc
.GetFilename() == project
: 
 385                         filenames 
= doc
.GetFiles()[:]  # make a copy and sort it. 
 386                         filenames
.sort(self
.BasenameCaseInsensitiveCompare
) 
 388                         for filename 
in filenames
: 
 389                             view
.AddLines("%s\n" % filename
) 
 391                                 status 
= self
._client
.update(filename
) 
 393                                 if status
.number 
> 0: 
 394                                     view
.AddLines(_("Updated to revision %s\n") % status
.number
) 
 396                                     openDocs 
= wx
.GetApp().GetDocumentManager().GetDocuments() 
 398                                         if doc
.GetFilename() == filename
: 
 399                                             yesNoMsg 
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(), 
 400                                                                      _("Updated file '%s' is currently open.  Close it?") % os
.path
.basename(filename
), 
 402                                                                      wx
.YES_NO|wx
.CANCEL|wx
.ICON_QUESTION
) 
 403                                             status 
= yesNoMsg
.ShowModal() 
 404                                             if status 
== wx
.ID_YES
: 
 406                                             elif status 
== wx
.ID_NO
: 
 408                                             else: # elif status == wx.CANCEL: 
 409                                                 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
)) 
 413                                     view
.AddLines(_("Update failed.\n")) 
 415                             except pysvn
.ClientError
, e
: 
 416                                 view
.AddLines("%s\n" % str(e
)) 
 417                                 wx
.MessageBox(str(e
), _("SVN Update"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 419                                 extype
, ex
, tb 
= sys
.exc_info() 
 420                                 view
.AddLines("Update failed: (%s) %s\n" % (extype
, str(ex
))) 
 421                                 for line 
in traceback
.format_tb(tb
): 
 424                                 wx
.MessageBox(_("Update failed."), _("SVN Update"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 426             wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
)) 
 429         elif id == SVNService
.SVN_CHECKIN_ALL_ID
: 
 431             projects 
= self
.GetCurrentProjects() 
 432             for project 
in projects
: 
 433                 openDocs 
= wx
.GetApp().GetDocumentManager().GetDocuments() 
 435                     if doc
.GetFilename() == project
: 
 436                         for filename 
in doc
.GetFiles(): 
 437                             if filename 
not in filenames
: 
 438                                 filenames
.append(filename
) 
 439             filenames
.sort(self
.BasenameCaseInsensitiveCompare
) 
 441             # ask user if dirty files should be saved first 
 442             openDocs 
= wx
.GetApp().GetDocumentManager().GetDocuments() 
 443             for filename 
in filenames
: 
 445                     if doc
.GetFilename() == filename 
and doc
.IsModified(): 
 446                         yesNoMsg 
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(), 
 447                                                  _("'%s' has unsaved modifications.  Save it before commit?") % os
.path
.basename(filename
), 
 449                                                  wx
.YES_NO|wx
.CANCEL|wx
.ICON_QUESTION
) 
 450                         status 
= yesNoMsg
.ShowModal() 
 451                         if status 
== wx
.ID_YES
: 
 453                         elif status 
== wx
.ID_NO
: 
 455                         else: # elif status == wx.CANCEL: 
 460             for i
, filename 
in enumerate(filenames
): 
 461                 shortFilename 
= os
.path
.basename(filename
) 
 462                 shortFilenames
.append(shortFilename
) 
 464             dlg 
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SVN Commit")) 
 466             sizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 467             sizer
.Add(wx
.StaticText(dlg
, -1, _("Comment:")), 0, wx
.ALIGN_CENTER_VERTICAL
) 
 468             commentText 
= wx
.TextCtrl(dlg
, -1, size
=(250,-1), style
=wx
.TE_MULTILINE
) 
 469             sizer
.Add(commentText
, 1, wx
.EXPAND|wx
.TOP
, HALF_SPACE
) 
 471             sizer
.Add(wx
.StaticText(dlg
, -1, _("Files:")), 0, wx
.ALIGN_CENTER_VERTICAL|wx
.TOP
, SPACE
) 
 472             fileList 
= wx
.CheckListBox(dlg
, -1, choices 
= shortFilenames
) 
 473             for i 
in range(fileList
.GetCount()): 
 474                 fileList
.Check(i
, True) 
 475             sizer
.Add(fileList
, 0, wx
.EXPAND|wx
.TOP
, HALF_SPACE
) 
 477             buttonSizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 478             okBtn 
= wx
.Button(dlg
, wx
.ID_OK
) 
 480             buttonSizer
.Add(okBtn
, 0, wx
.RIGHT
, HALF_SPACE
) 
 481             buttonSizer
.Add(wx
.Button(dlg
, wx
.ID_CANCEL
), 0) 
 483             contentSizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 484             contentSizer
.Add(sizer
, 0, wx
.ALL
, SPACE
) 
 485             contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
) 
 487             dlg
.SetSizer(contentSizer
) 
 491             if dlg
.ShowModal() == wx
.ID_OK
: 
 492                 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
)) 
 494                 messageService 
= wx
.GetApp().GetService(MessageService
.MessageService
) 
 495                 messageService
.ShowWindow() 
 497                 view 
= messageService
.GetView() 
 499                 view
.AddLines(_("SVN Commit:\n")) 
 503                     for i 
in range(fileList
.GetCount()): 
 504                         if fileList
.IsChecked(i
): 
 505                             selFilenames
.append(filenames
[i
]) 
 506                             view
.AddLines("%s\n" % filenames
[i
]) 
 508                     if len(selFilenames
): 
 509                         comment 
= commentText
.GetValue() 
 510                         status 
= self
._client
.checkin(selFilenames
, comment
) 
 513                             view
.AddLines(_("Nothing to commit.\n")) 
 514                         elif status
.number 
> 0: 
 515                             view
.AddLines(_("Committed as revision %s.\n") % status
.number
) 
 517                             view
.AddLines(_("Commit failed.\n")) 
 519                 except pysvn
.ClientError
, e
: 
 520                     view
.AddLines("%s\n" % str(e
)) 
 521                     wx
.MessageBox(str(e
), _("SVN Commit"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 523                     extype
, ex
, tb 
= sys
.exc_info() 
 524                     view
.AddLines("Commit failed: (%s) %s\n" % (extype
, str(ex
))) 
 525                     for line 
in traceback
.format_tb(tb
): 
 527                     wx
.MessageBox(_("Commit failed."), _("SVN Commit"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 529                 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
)) 
 534         elif id == SVNService
.SVN_CHECKIN_ID
: 
 535             filenames 
= self
.GetCurrentDocuments()[:] 
 536             filenames
.sort(self
.BasenameCaseInsensitiveCompare
)             
 538             # ask user if dirty files should be saved first 
 539             openDocs 
= wx
.GetApp().GetDocumentManager().GetDocuments() 
 540             for filename 
in filenames
: 
 542                     if doc
.GetFilename() == filename 
and doc
.IsModified(): 
 543                         yesNoMsg 
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(), 
 544                                                  _("'%s' has unsaved modifications.  Save it before commit?") % os
.path
.basename(filename
), 
 546                                                  wx
.YES_NO|wx
.CANCEL|wx
.ICON_QUESTION
) 
 547                         status 
= yesNoMsg
.ShowModal() 
 548                         if status 
== wx
.ID_YES
: 
 550                         elif status 
== wx
.ID_NO
: 
 552                         else: # elif status == wx.CANCEL: 
 557             for i
, filename 
in enumerate(filenames
): 
 558                 shortFilename 
= os
.path
.basename(filename
) 
 559                 shortFilenames
.append(shortFilename
) 
 561             dlg 
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SVN Commit")) 
 563             sizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 564             sizer
.Add(wx
.StaticText(dlg
, -1, _("Comment:")), 0, wx
.ALIGN_CENTER_VERTICAL
) 
 565             commentText 
= wx
.TextCtrl(dlg
, -1, size
=(250,-1), style
=wx
.TE_MULTILINE
) 
 566             sizer
.Add(commentText
, 1, wx
.EXPAND|wx
.TOP
, HALF_SPACE
) 
 568             sizer
.Add(wx
.StaticText(dlg
, -1, _("Files:")), 0, wx
.ALIGN_CENTER_VERTICAL|wx
.TOP
, SPACE
) 
 569             fileList 
= wx
.CheckListBox(dlg
, -1, choices 
= shortFilenames
) 
 570             for i 
in range(fileList
.GetCount()): 
 571                 fileList
.Check(i
, True) 
 572             sizer
.Add(fileList
, 0, wx
.EXPAND|wx
.TOP
, HALF_SPACE
) 
 574             buttonSizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 575             okBtn 
= wx
.Button(dlg
, wx
.ID_OK
) 
 577             buttonSizer
.Add(okBtn
, 0, wx
.RIGHT
, HALF_SPACE
) 
 578             buttonSizer
.Add(wx
.Button(dlg
, wx
.ID_CANCEL
), 0) 
 580             contentSizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 581             contentSizer
.Add(sizer
, 0, wx
.ALL
, SPACE
) 
 582             contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
) 
 584             dlg
.SetSizer(contentSizer
) 
 588             if dlg
.ShowModal() == wx
.ID_OK
: 
 589                 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
)) 
 591                 messageService 
= wx
.GetApp().GetService(MessageService
.MessageService
) 
 592                 messageService
.ShowWindow() 
 594                 view 
= messageService
.GetView() 
 596                 view
.AddLines(_("SVN Commit:\n")) 
 600                     for i 
in range(fileList
.GetCount()): 
 601                         if fileList
.IsChecked(i
): 
 602                             selFilenames
.append(filenames
[i
]) 
 603                             view
.AddLines("%s\n" % filenames
[i
]) 
 605                     if len(selFilenames
): 
 606                         comment 
= commentText
.GetValue() 
 607                         status 
= self
._client
.checkin(selFilenames
, comment
) 
 610                             view
.AddLines(_("Nothing to commit.\n")) 
 611                         elif status
.number 
> 0: 
 612                             view
.AddLines(_("Committed as revision %s.\n") % status
.number
) 
 614                             view
.AddLines(_("Commit failed.\n")) 
 616                 except pysvn
.ClientError
, e
: 
 617                     view
.AddLines("%s\n" % str(e
)) 
 618                     wx
.MessageBox(str(e
), _("SVN Commit"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 620                     extype
, ex
, tb 
= sys
.exc_info() 
 621                     view
.AddLines("Commit failed: (%s) %s\n" % (extype
, str(ex
))) 
 622                     for line 
in traceback
.format_tb(tb
): 
 624                     wx
.MessageBox(_("Commit failed."), _("SVN Commit"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 626                 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
)) 
 630         elif id == SVNService
.SVN_CHECKOUT_ID
: 
 631             config 
= wx
.ConfigBase_Get() 
 632             svnUrl 
= config
.Read(SVN_REPOSITORY_URL
, self
._defaultURL
) 
 634             dlg 
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SVN Checkout")) 
 636             gridSizer 
= wx
.FlexGridSizer(cols 
= 2, hgap 
= 5, vgap 
= 5) 
 637             gridSizer
.Add(wx
.StaticText(dlg
, -1, _("Repository URL:")), 0, wx
.ALIGN_CENTER_VERTICAL|wx
.RIGHT|wx
.TOP
, HALF_SPACE
) 
 638             svnUrlList 
= ReadSvnUrlList() 
 639             svnURLCombobox 
= wx
.ComboBox(dlg
, -1, size
=(200, -1), choices
=svnUrlList
, style
=wx
.CB_DROPDOWN
) 
 641                 svnURLCombobox
.SetToolTipString(svnUrlList
[0]) 
 642                 svnURLCombobox
.SetStringSelection(svnUrlList
[0]) 
 644                 svnURLCombobox
.SetToolTipString(_("Set Repository URL")) 
 645             gridSizer
.Add(svnURLCombobox
, 0) 
 647             gridSizer
.Add(wx
.StaticText(dlg
, -1, _("Checkout to dir:")), 0, wx
.ALIGN_CENTER_VERTICAL|wx
.RIGHT|wx
.TOP
, HALF_SPACE
) 
 648             localPath 
= wx
.TextCtrl(dlg
, -1, size 
= (200, -1)) 
 649             localPath
.SetToolTipString(_("Path in local file system where files will be located.")) 
 650             findDirButton 
= wx
.Button(dlg
, -1, _("Browse...")) 
 652             def OnBrowseButton(event
): 
 653                 dirDlg 
= wx
.DirDialog(wx
.GetApp().GetTopWindow(), _("Choose a directory:"), style
=wx
.DD_DEFAULT_STYLE
) 
 654                 dir = localPath
.GetValue() 
 657                 if dirDlg
.ShowModal() == wx
.ID_OK
: 
 658                     localPath
.SetValue(dirDlg
.GetPath()) 
 659                     localPath
.SetToolTipString(localPath
.GetValue()) 
 660                     localPath
.SetInsertionPointEnd() 
 663             wx
.EVT_BUTTON(findDirButton
, -1, OnBrowseButton
) 
 665             sizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 666             sizer
.Add(localPath
, 1, wx
.EXPAND
) 
 667             sizer
.Add(findDirButton
, 0, wx
.LEFT
, HALF_SPACE
) 
 668             gridSizer
.Add(sizer
, 0) 
 670             buttonSizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 671             okBtn 
= wx
.Button(dlg
, wx
.ID_OK
) 
 673             buttonSizer
.Add(okBtn
, 0, wx
.RIGHT
, HALF_SPACE
) 
 674             buttonSizer
.Add(wx
.Button(dlg
, wx
.ID_CANCEL
), 0) 
 676             contentSizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 677             contentSizer
.Add(gridSizer
, 0, wx
.ALL
, SPACE
) 
 678             contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
) 
 680             dlg
.SetSizer(contentSizer
) 
 684             if dlg
.ShowModal() == wx
.ID_OK
: 
 685                 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
)) 
 687                 WriteSvnUrlList(svnURLCombobox
) 
 689                 messageService 
= wx
.GetApp().GetService(MessageService
.MessageService
) 
 690                 messageService
.ShowWindow() 
 692                 view 
= messageService
.GetView() 
 694                 view
.AddLines(_("SVN Checkout:\n")) 
 696                 svnUrl 
= svnURLCombobox
.GetValue() 
 697                 toLocation 
= localPath
.GetValue() 
 699                     self
._client
.checkout(svnUrl
, toLocation
) 
 700                     view
.AddLines(_("Checkout completed.\n")) 
 701                 except pysvn
.ClientError
, e
: 
 702                     view
.AddLines(_("Checkout failed.  %s\n") % str(e
)) 
 703                     wx
.MessageBox(_("Checkout failed.  %s") % str(e
), _("SVN Checkout"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 705                     extype
, ex
, tb 
= sys
.exc_info() 
 706                     view
.AddLines("Checkout failed: (%s) %s\n" % (extype
, str(ex
))) 
 707                     for line 
in traceback
.format_tb(tb
): 
 709                     wx
.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 711                 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
)) 
 715         elif id == SVNService
.SVN_REVERT_ID
: 
 716             wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
)) 
 718             filenames 
= self
.GetCurrentDocuments() 
 720             messageService 
= wx
.GetApp().GetService(MessageService
.MessageService
) 
 721             messageService
.ShowWindow() 
 723             view 
= messageService
.GetView() 
 725             view
.AddLines(_("SVN Revert:\n")) 
 726             for filename 
in filenames
: 
 727                 view
.AddLines("%s\n" % filename
) 
 730                 self
._client
.revert(filenames
) 
 731                 view
.AddLines(_("Revert completed.\n")) 
 733                 openDocs 
= wx
.GetApp().GetDocumentManager().GetDocuments() 
 734                 for doc 
in openDocs
[:]:   # need to make a copy of the list otherwise ordinality changes as we close the files 
 735                     if doc
.GetFilename() in filenames
: 
 736                         yesNoMsg 
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(), 
 737                                                  _("Reverted file '%s' is currently open.  Close it?") % os
.path
.basename(doc
.GetFilename()), 
 739                                                  wx
.YES_NO|wx
.ICON_QUESTION
) 
 740                         if yesNoMsg
.ShowModal() == wx
.ID_YES
: 
 743             except pysvn
.ClientError
, e
: 
 744                 view
.AddLines("%s\n" % str(e
)) 
 745                 wx
.MessageBox(str(e
), _("SVN Revert"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 747                 extype
, ex
, tb 
= sys
.exc_info() 
 748                 view
.AddLines("Revert failed: (%s) %s\n" % (extype
, str(ex
))) 
 749                 for line 
in traceback
.format_tb(tb
): 
 751                 wx
.MessageBox(_("Revert failed."), _("SVN Revert"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 753             wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
)) 
 756         elif id == SVNService
.SVN_ADD_ID
: 
 757             wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
)) 
 759             filenames 
= self
.GetCurrentDocuments() 
 761             messageService 
= wx
.GetApp().GetService(MessageService
.MessageService
) 
 762             messageService
.ShowWindow() 
 764             view 
= messageService
.GetView() 
 766             view
.AddLines(_("SVN Add:\n")) 
 767             for filename 
in filenames
: 
 768                 view
.AddLines("%s\n" % filename
) 
 771                 self
._client
.add(filenames
) 
 772                 view
.AddLines(_("Add completed.\n")) 
 773             except pysvn
.ClientError
, e
: 
 774                 view
.AddLines("%s\n" % str(e
)) 
 775                 wx
.MessageBox(str(e
), _("SVN Add"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 777                 extype
, ex
, tb 
= sys
.exc_info() 
 778                 view
.AddLines("Add failed: (%s) %s\n" % (extype
, str(ex
))) 
 779                 for line 
in traceback
.format_tb(tb
): 
 781                 wx
.MessageBox(_("Add failed."), _("SVN Add"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 783             wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
)) 
 786         elif id == SVNService
.SVN_DELETE_ID
: 
 787             wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
)) 
 789             filenames 
= self
.GetCurrentDocuments() 
 791             messageService 
= wx
.GetApp().GetService(MessageService
.MessageService
) 
 792             messageService
.ShowWindow() 
 794             view 
= messageService
.GetView() 
 796             view
.AddLines(_("SVN Delete:\n")) 
 797             for filename 
in filenames
: 
 798                 view
.AddLines("%s\n" % filename
) 
 801                 self
._client
.remove(filenames
) 
 802                 view
.AddLines(_("Delete completed.\n")) 
 803             except pysvn
.ClientError
, e
: 
 804                 view
.AddLines("%s\n" % str(e
)) 
 805                 wx
.MessageBox(str(e
), _("SVN Delete"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 807                 extype
, ex
, tb 
= sys
.exc_info() 
 808                 view
.AddLines("Delete failed: (%s) %s\n" % (extype
, str(ex
))) 
 809                 for line 
in traceback
.format_tb(tb
): 
 811                 wx
.MessageBox(_("Delete failed."), _("SVN Delete"), wx
.OK | wx
.ICON_EXCLAMATION
) 
 813             wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
)) 
 819     def ProcessUpdateUIEvent(self
, event
): 
 822         if id in [SVNService
.SVN_UPDATE_ID
, 
 823                   SVNService
.SVN_CHECKIN_ID
, 
 824                   SVNService
.SVN_REVERT_ID
, 
 825                   SVNService
.SVN_ADD_ID
, 
 826                   SVNService
.SVN_DELETE_ID
]: 
 827             if self
.GetCurrentDocuments(): 
 833         elif id == SVNService
.SVN_CHECKOUT_ID
: 
 837         elif (id == SVNService
.SVN_UPDATE_ALL_ID
 
 838         or id == SVNService
.SVN_CHECKIN_ALL_ID
): 
 839             if self
.GetCurrentProjects(): 
 848     def GetCurrentProjects(self
): 
 849         projectService 
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
) 
 851             projView 
= projectService
.GetView() 
 853             if projView
.HasFocus(): 
 854                 filenames 
= projView
.GetSelectedProjects() 
 862     def GetCurrentDocuments(self
): 
 864         projectService 
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
) 
 866             projView 
= projectService
.GetView() 
 868             if projView
.HasFocus(): 
 869                 filenames 
= projView
.GetSelectedFiles() 
 875         doc 
= wx
.GetApp().GetTopWindow().GetDocumentManager().GetCurrentDocument() 
 877             filenames 
= [doc
.GetFilename()] 
 884     def BasenameCaseInsensitiveCompare(self
, s1
, s2
): 
 885         s1L 
= os
.path
.basename(s1
).lower() 
 886         s2L 
= os
.path
.basename(s2
).lower() 
 895 class SVNOptionsPanel(wx
.Panel
): 
 898     def __init__(self
, parent
, id): 
 899         wx
.Panel
.__init
__(self
, parent
, id) 
 901         config 
= wx
.ConfigBase_Get() 
 902         configDir 
= config
.Read(SVN_CONFIG_DIR
, "") 
 904         borderSizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 905         sizer 
= wx
.FlexGridSizer(cols 
= 2, hgap 
= 5, vgap 
= 5) 
 907         sizer
.Add(wx
.StaticText(self
, -1, _("SVN Config Dir:")), 0, wx
.ALIGN_CENTER_VERTICAL
) 
 909         self
._svnConfigDir 
= wx
.TextCtrl(self
, -1, configDir
, size 
= (200, -1)) 
 911             self
._svnConfigDir
.SetToolTipString(_("Path Subversion configuration directory.")) 
 913             self
._svnConfigDir
.SetToolTipString(configDir
) 
 915         findDirButton 
= wx
.Button(self
, -1, _("Browse...")) 
 917         def OnBrowseButton(event
): 
 918             dirDlg 
= wx
.DirDialog(self
, _("Choose a directory:"), style
=wx
.DD_DEFAULT_STYLE
) 
 919             dir = self
._svnConfigDir
.GetValue() 
 922             if dirDlg
.ShowModal() == wx
.ID_OK
: 
 923                 self
._svnConfigDir
.SetValue(dirDlg
.GetPath()) 
 924                 self
._svnConfigDir
.SetToolTipString(self
._svnConfigDir
.GetValue()) 
 925                 self
._svnConfigDir
.SetInsertionPointEnd() 
 928         wx
.EVT_BUTTON(findDirButton
, -1, OnBrowseButton
) 
 930         hsizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 931         hsizer
.Add(self
._svnConfigDir
, 1, wx
.EXPAND
) 
 932         hsizer
.Add(findDirButton
, 0, wx
.LEFT
, HALF_SPACE
) 
 936         svnUrlList 
= ReadSvnUrlList() 
 937         sizer
.Add(wx
.StaticText(self
, -1, _("SVN URL:")), 0, wx
.ALIGN_CENTER_VERTICAL
) 
 938         self
._svnURLCombobox 
= wx
.ComboBox(self
, -1, size
=(200, -1), choices
=svnUrlList
, style
=wx
.CB_DROPDOWN
) 
 940             self
._svnURLCombobox
.SetToolTipString(svnUrlList
[0]) 
 941             self
._svnURLCombobox
.SetStringSelection(svnUrlList
[0]) 
 943             self
._svnURLCombobox
.SetToolTipString(_("Set Repository URL")) 
 944         sizer
.Add(self
._svnURLCombobox
, 0) 
 947         sizer
.Add(wx
.StaticText(self
, -1, _("SVN_SSH:")), 0, wx
.ALIGN_CENTER_VERTICAL
) 
 948         svnSSH 
= os
.getenv("SVN_SSH") 
 949         if not svnSSH 
or svnSSH 
== "": 
 950             self
._svnSSH 
= wx
.TextCtrl(self
, -1, size 
= (200, -1)) 
 952             self
._svnSSH 
= wx
.TextCtrl(self
, -1, svnSSH
, size 
= (200, -1)) 
 953         self
._svnSSH
.SetToolTipString(_("Override SVN_SSH environment variable temporarily.")) 
 955         findSSHButton 
= wx
.Button(self
, -1, _("Browse...")) 
 957         def OnBrowseFileButton(event
): 
 958             dirDlg 
= wx
.FileDialog(self
, _("Choose a file:"), style
=wx
.OPEN|wx
.CHANGE_DIR
) 
 959             if dirDlg
.ShowModal() == wx
.ID_OK
: 
 960                 self
._svnSSH
.SetValue(dirDlg
.GetPath()) 
 961                 self
._svnSSH
.SetToolTipString(self
._svnSSH
.GetValue()) 
 962                 self
._svnSSH
.SetInsertionPointEnd() 
 964         wx
.EVT_BUTTON(findSSHButton
, -1, OnBrowseFileButton
) 
 966         hsizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) 
 967         hsizer
.Add(self
._svnSSH
, 1, wx
.EXPAND
) 
 968         hsizer
.Add(findSSHButton
, 0, wx
.LEFT
, HALF_SPACE
) 
 972         borderSizer
.Add(sizer
, 0, wx
.ALL
, SPACE
) 
 973         self
.SetSizer(borderSizer
) 
 975         parent
.AddPage(self
, _("SVN")) 
 978     def OnOK(self
, optionsDialog
): 
 979         config 
= wx
.ConfigBase_Get() 
 981         config
.Write(SVN_CONFIG_DIR
, self
._svnConfigDir
.GetValue()) 
 983         WriteSvnUrlList(self
._svnURLCombobox
) 
 985         os
.environ
["SVN_SSH"] = self
._svnSSH
.GetValue() 
 988 def ReadSvnUrlList(): 
 989     """ Read in list of SNV repository URLs.  First in list is the last one path used. """ 
 990     config 
= wx
.ConfigBase_Get() 
 991     urlStringList 
= config
.Read(SVN_REPOSITORY_URL
) 
 992     if len(urlStringList
): 
 993         urlList 
= eval(urlStringList
) 
 996     if len(urlList
) == 0: 
 997         svnService 
= wx
.GetApp().GetService(SVNService
) 
 998         if svnService 
and hasattr(svnService
, "_defaultURL"): 
 999             urlList
.append(svnService
._defaultURL
) 
1003 def WriteSvnUrlList(comboBox
): 
1004     """ Save out list of SVN repository URLs from combobox.  Put on top the current selection.  Only save out first 10 from the list """ 
1007     url 
= comboBox
.GetValue() 
1011     for i 
in range(min(comboBox
.GetCount(), 10)): 
1012         url 
= comboBox
.GetString(i
) 
1013         if url 
not in urlList
: 
1016     config 
= wx
.ConfigBase_Get() 
1017     config
.Write(SVN_REPOSITORY_URL
, urlList
.__repr
__())