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
__())