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"))
103 if dlg
.ShowModal() == wx
.ID_OK
:
105 message
= dlg
.GetValue()
108 message
= _("Cancel Action")
112 return retcode
, message
115 def GetLogin(self
, realm
, username
, maySave
):
116 dlg
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SVN Login"))
118 sizer
= wx
.FlexGridSizer(cols
= 2, hgap
= 5, vgap
= 5)
119 sizer
.Add(wx
.StaticText(dlg
, -1, _("Username:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
120 usernameTxt
= wx
.TextCtrl(dlg
, -1, username
, size
= (200, -1))
121 sizer
.Add(usernameTxt
, 0, wx
.ALIGN_CENTER_VERTICAL
)
122 sizer
.Add(wx
.StaticText(dlg
, -1, _("Password:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
123 passwordTxt
= wx
.TextCtrl(dlg
, -1, size
=(200, -1), style
=wx
.TE_PASSWORD
)
124 sizer
.Add(passwordTxt
, 0, wx
.ALIGN_CENTER_VERTICAL
)
126 savePasswordCheckbox
= wx
.CheckBox(dlg
, -1, _("Remember Username and Password"))
128 savePasswordCheckbox
.Enable(False)
130 buttonSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
131 okBtn
= wx
.Button(dlg
, wx
.ID_OK
)
133 buttonSizer
.Add(okBtn
, 0, wx
.RIGHT
, HALF_SPACE
)
134 buttonSizer
.Add(wx
.Button(dlg
, wx
.ID_CANCEL
), 0)
136 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
137 contentSizer
.Add(sizer
, 0, wx
.LEFT|wx
.TOP|wx
.RIGHT
, SPACE
)
138 contentSizer
.Add(savePasswordCheckbox
, 0, wx
.TOP|wx
.LEFT|wx
.BOTTOM
, SPACE
)
139 contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
)
141 dlg
.SetSizer(contentSizer
)
146 if dlg
.ShowModal() == wx
.ID_OK
:
148 username
= usernameTxt
.GetValue().strip()
149 password
= passwordTxt
.GetValue()
150 save
= savePasswordCheckbox
.IsChecked()
158 return retcode
, username
, password
, save
161 def SSLServerPrompt(self
):
162 """ Not implemented, as per pysvn documentation """
166 def GetSSLServerTrust(self
, trustDict
):
167 dlg
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SSL Server Certificate"))
169 sizer
= wx
.FlexGridSizer(cols
= 2, hgap
= 5, vgap
= 5)
170 for k
in ['hostname', 'valid_from', 'valid_to', 'issuer_dname', 'realm']:
171 if trustDict
.has_key(k
):
172 sizer
.Add(wx
.StaticText(dlg
, -1, "%s:" % k
), 0, wx
.ALIGN_CENTER_VERTICAL
)
173 sizer
.Add(wx
.StaticText(dlg
, -1, "%s" % trustDict
[k
]), 0, wx
.ALIGN_CENTER_VERTICAL
)
175 box
= wx
.StaticBoxSizer(wx
.StaticBox(dlg
, -1, _("Certificate Info")), wx
.VERTICAL
)
176 box
.Add(sizer
, 0, wx
.EXPAND
)
178 certRadio
= wx
.RadioBox(dlg
, -1, _("Certificate"), choices
=[_("Accept Always"), _("Accept Once"), _("Reject")], majorDimension
=1, style
=wx
.RA_SPECIFY_COLS
)
180 buttonSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
181 okBtn
= wx
.Button(dlg
, wx
.ID_OK
)
183 buttonSizer
.Add(okBtn
, 0, wx
.RIGHT
, HALF_SPACE
)
184 buttonSizer
.Add(wx
.Button(dlg
, wx
.ID_CANCEL
), 0)
186 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
187 contentSizer
.Add(box
, 0, wx
.TOP|wx
.LEFT|wx
.RIGHT|wx
.EXPAND
, SPACE
)
188 contentSizer
.Add(certRadio
, 0, wx
.TOP|wx
.LEFT|wx
.RIGHT|wx
.BOTTOM|wx
.EXPAND
, SPACE
)
189 contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
)
191 dlg
.SetSizer(contentSizer
)
195 # default values for reject
201 if dlg
.ShowModal() == wx
.ID_OK
:
202 cert
= certRadio
.GetStringSelection()
203 if cert
== _("Accept Always"):
205 acceptedFailures
= trustDict
.get('failures')
207 elif cert
== _("Accept Once"):
209 acceptedFailures
= trustDict
.get('failures')
213 return retcode
, acceptedFailures
, save
216 def SSLClientPassword(self
, realm
, maySave
):
217 dlg
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SSL Client Certificate Login"))
219 sizer
= wx
.FlexGridSizer(cols
= 2, hgap
= 5, vgap
= 5)
220 sizer
.Add(wx
.StaticText(dlg
, -1, _("Realm:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
221 sizer
.Add(wx
.StaticText(dlg
, -1, realm
), 0, wx
.ALIGN_CENTER_VERTICAL
)
222 sizer
.Add(wx
.StaticText(dlg
, -1, _("Password:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
223 passwordTxt
= wx
.TextCtrl(dlg
, -1, size
=(200, -1), style
=wx
.TE_PASSWORD
)
224 sizer
.Add(passwordTxt
, 0, wx
.ALIGN_CENTER_VERTICAL
)
226 savePasswordCheckbox
= wx
.CheckBox(dlg
, -1, _("Remember Password"))
228 savePasswordCheckbox
.Enable(False)
230 buttonSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
231 okBtn
= wx
.Button(dlg
, wx
.ID_OK
)
233 buttonSizer
.Add(okBtn
, 0, wx
.RIGHT
, HALF_SPACE
)
234 buttonSizer
.Add(wx
.Button(dlg
, wx
.ID_CANCEL
), 0)
236 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
237 contentSizer
.Add(sizer
, 0, wx
.LEFT|wx
.TOP|wx
.RIGHT
, SPACE
)
238 contentSizer
.Add(savePasswordCheckbox
, 0, wx
.TOP|wx
.LEFT|wx
.BOTTOM
, SPACE
)
239 contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
)
241 dlg
.SetSizer(contentSizer
)
246 if dlg
.ShowModal() == wx
.ID_OK
:
248 password
= passwordTxt
.GetValue()
249 save
= savePasswordCheckbox
.IsChecked()
256 return retcode
, password
, save
259 def SSLClientCert(self
):
260 dlg
= wx
.FileDialog(wx
.GetApp().GetTopWindow(),
261 message
="Choose certificate",
262 style
=wx
.OPEN|wx
.FILE_MUST_EXIST|wx
.CHANGE_DIR
265 # dlg.CenterOnParent() # wxBug: caused crash with wx.FileDialog
266 if dlg
.ShowModal() == wx
.ID_OK
:
268 certfile
= dlg
.GetPath()
274 return retcode
, certfile
278 #----------------------------------------------------------------------------
280 #----------------------------------------------------------------------------
282 def InstallControls(self
, frame
, menuBar
= None, toolBar
= None, statusBar
= None, document
= None):
283 menu
= menuBar
.GetMenu(menuBar
.FindMenu(_("Project")))
285 menu
.AppendSeparator()
287 wx
.EVT_MENU(frame
, SVNService
.SVN_UPDATE_ALL_ID
, self
.ProcessEvent
)
288 wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_UPDATE_ALL_ID
, self
.ProcessUpdateUIEvent
)
289 menu
.Append(SVNService
.SVN_UPDATE_ALL_ID
, _("SVN Update All in Project"), _("Update all files in a project from Subversion"))
290 wx
.EVT_MENU(frame
, SVNService
.SVN_CHECKIN_ALL_ID
, self
.ProcessEvent
)
291 wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_CHECKIN_ALL_ID
, self
.ProcessUpdateUIEvent
)
292 menu
.Append(SVNService
.SVN_CHECKIN_ALL_ID
, _("SVN Commit All in Project..."), _("Commit all files changes in a project to Subversion"))
293 wx
.EVT_MENU(frame
, SVNService
.SVN_UPDATE_ID
, self
.ProcessEvent
)
294 wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_UPDATE_ID
, self
.ProcessUpdateUIEvent
)
295 menu
.Append(SVNService
.SVN_UPDATE_ID
, _("SVN Update"), _("Update file from Subversion"))
296 wx
.EVT_MENU(frame
, SVNService
.SVN_CHECKIN_ID
, self
.ProcessEvent
)
297 wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_CHECKIN_ID
, self
.ProcessUpdateUIEvent
)
298 menu
.Append(SVNService
.SVN_CHECKIN_ID
, _("SVN Commit..."), _("Commit file changes to Subversion"))
299 wx
.EVT_MENU(frame
, SVNService
.SVN_CHECKOUT_ID
, self
.ProcessEvent
)
300 wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_CHECKOUT_ID
, self
.ProcessUpdateUIEvent
)
301 menu
.Append(SVNService
.SVN_CHECKOUT_ID
, _("SVN Checkout..."), _("Checkout file from Subversion"))
302 wx
.EVT_MENU(frame
, SVNService
.SVN_REVERT_ID
, self
.ProcessEvent
)
303 wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_REVERT_ID
, self
.ProcessUpdateUIEvent
)
304 menu
.Append(SVNService
.SVN_REVERT_ID
, _("SVN Revert"), _("Revert file from Subversion"))
306 menu
.AppendSeparator()
308 wx
.EVT_MENU(frame
, SVNService
.SVN_ADD_ID
, self
.ProcessEvent
)
309 wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_ADD_ID
, self
.ProcessUpdateUIEvent
)
310 menu
.Append(SVNService
.SVN_ADD_ID
, _("SVN Add"), _("Add file to Subversion"))
311 wx
.EVT_MENU(frame
, SVNService
.SVN_DELETE_ID
, self
.ProcessEvent
)
312 wx
.EVT_UPDATE_UI(frame
, SVNService
.SVN_DELETE_ID
, self
.ProcessUpdateUIEvent
)
313 menu
.Append(SVNService
.SVN_DELETE_ID
, _("SVN Delete"), _("Delete file from Subversion"))
316 def ProcessEvent(self
, event
):
320 if not SVN_INSTALLED
:
321 if id in SVNService
.SVN_COMMAND_LIST
:
322 wx
.MessageBox(_("pysvn not found. Please install pysvn"), _("Python Subversion"))
327 if id == SVNService
.SVN_UPDATE_ID
:
328 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
330 filenames
= self
.GetCurrentDocuments()
332 filenames
= filenames
[:]
333 filenames
.sort(self
.BasenameCaseInsensitiveCompare
)
335 folderPath
= self
.GetCurrentFolder()
337 filenames
= [folderPath
]
339 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
340 messageService
.ShowWindow()
342 view
= messageService
.GetView()
344 view
.AddLines(_("SVN Update:\n"))
346 for filename
in filenames
:
347 view
.AddLines("%s\n" % filename
)
349 status
= self
._client
.update(filename
)
351 if status
.number
> 0:
352 view
.AddLines(_("Updated to revision %s\n") % status
.number
)
354 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
356 if doc
.GetFilename() == filename
:
357 yesNoMsg
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(),
358 _("Updated file '%s' is currently open. Close it?") % os
.path
.basename(filename
),
360 wx
.YES_NO|wx
.ICON_QUESTION
)
361 yesNoMsg
.CenterOnParent()
362 status
= yesNoMsg
.ShowModal()
364 if status
== wx
.ID_YES
:
368 view
.AddLines(_("Update failed.\n"))
370 except pysvn
.ClientError
, e
:
371 view
.AddLines("%s\n" % str(e
))
372 wx
.MessageBox(str(e
), _("SVN Update"), wx
.OK | wx
.ICON_EXCLAMATION
)
374 extype
, ex
, tb
= sys
.exc_info()
375 view
.AddLines("Update failed: (%s) %s\n" % (extype
, str(ex
)))
376 for line
in traceback
.format_tb(tb
):
379 wx
.MessageBox(_("Update failed."), _("SVN Update"), wx
.OK | wx
.ICON_EXCLAMATION
)
381 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
385 elif id == SVNService
.SVN_UPDATE_ALL_ID
:
386 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
388 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
389 messageService
.ShowWindow()
391 view
= messageService
.GetView()
393 view
.AddLines(_("SVN Update:\n"))
395 project
= self
.GetCurrentProject()
397 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
399 if doc
.GetFilename() == project
:
400 filenames
= doc
.GetFiles()[:] # make a copy and sort it.
401 filenames
.sort(self
.BasenameCaseInsensitiveCompare
)
403 for filename
in filenames
:
404 view
.AddLines("%s\n" % filename
)
406 status
= self
._client
.update(filename
)
408 if status
.number
> 0:
409 view
.AddLines(_("Updated to revision %s\n") % status
.number
)
411 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
413 if doc
.GetFilename() == filename
:
414 yesNoMsg
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(),
415 _("Updated file '%s' is currently open. Close it?") % os
.path
.basename(filename
),
417 wx
.YES_NO|wx
.CANCEL|wx
.ICON_QUESTION
)
418 yesNoMsg
.CenterOnParent()
419 status
= yesNoMsg
.ShowModal()
421 if status
== wx
.ID_YES
:
423 elif status
== wx
.ID_NO
:
425 else: # elif status == wx.CANCEL:
426 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
430 view
.AddLines(_("Update failed.\n"))
432 except pysvn
.ClientError
, e
:
433 view
.AddLines("%s\n" % str(e
))
434 wx
.MessageBox(str(e
), _("SVN Update"), wx
.OK | wx
.ICON_EXCLAMATION
)
436 extype
, ex
, tb
= sys
.exc_info()
437 view
.AddLines("Update failed: (%s) %s\n" % (extype
, str(ex
)))
438 for line
in traceback
.format_tb(tb
):
441 wx
.MessageBox(_("Update failed."), _("SVN Update"), wx
.OK | wx
.ICON_EXCLAMATION
)
443 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
446 elif id == SVNService
.SVN_CHECKIN_ALL_ID
:
448 project
= self
.GetCurrentProject()
450 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
452 if doc
.GetFilename() == project
:
453 for filename
in doc
.GetFiles():
454 if filename
not in filenames
:
455 filenames
.append(filename
)
456 filenames
.sort(self
.BasenameCaseInsensitiveCompare
)
458 # ask user if dirty files should be saved first
459 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
460 for filename
in filenames
:
462 if doc
.GetFilename() == filename
and doc
.IsModified():
463 yesNoMsg
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(),
464 _("'%s' has unsaved modifications. Save it before commit?") % os
.path
.basename(filename
),
466 wx
.YES_NO|wx
.CANCEL|wx
.ICON_QUESTION
)
467 yesNoMsg
.CenterOnParent()
468 status
= yesNoMsg
.ShowModal()
470 if status
== wx
.ID_YES
:
472 elif status
== wx
.ID_NO
:
474 else: # elif status == wx.CANCEL:
479 for i
, filename
in enumerate(filenames
):
480 shortFilename
= os
.path
.basename(filename
)
481 shortFilenames
.append(shortFilename
)
483 dlg
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SVN Commit"))
485 sizer
= wx
.BoxSizer(wx
.VERTICAL
)
486 sizer
.Add(wx
.StaticText(dlg
, -1, _("Comment:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
487 commentText
= wx
.TextCtrl(dlg
, -1, size
=(250,-1), style
=wx
.TE_MULTILINE
)
488 sizer
.Add(commentText
, 1, wx
.EXPAND|wx
.TOP
, HALF_SPACE
)
490 sizer
.Add(wx
.StaticText(dlg
, -1, _("Files:")), 0, wx
.ALIGN_CENTER_VERTICAL|wx
.TOP
, SPACE
)
491 fileList
= wx
.CheckListBox(dlg
, -1, choices
= shortFilenames
)
492 for i
in range(fileList
.GetCount()):
493 fileList
.Check(i
, True)
494 sizer
.Add(fileList
, 0, wx
.EXPAND|wx
.TOP
, HALF_SPACE
)
496 buttonSizer
= wx
.StdDialogButtonSizer()
497 okBtn
= wx
.Button(dlg
, wx
.ID_OK
)
499 buttonSizer
.AddButton(okBtn
)
500 buttonSizer
.AddButton(wx
.Button(dlg
, wx
.ID_CANCEL
))
501 buttonSizer
.Realize()
503 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
504 contentSizer
.Add(sizer
, 0, wx
.ALL
, SPACE
)
505 contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
)
507 dlg
.SetSizer(contentSizer
)
512 if dlg
.ShowModal() == wx
.ID_OK
:
513 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
515 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
516 messageService
.ShowWindow()
518 view
= messageService
.GetView()
520 view
.AddLines(_("SVN Commit:\n"))
524 for i
in range(fileList
.GetCount()):
525 if fileList
.IsChecked(i
):
526 selFilenames
.append(filenames
[i
])
527 view
.AddLines("%s\n" % filenames
[i
])
529 if len(selFilenames
):
530 comment
= commentText
.GetValue()
531 status
= self
._client
.checkin(selFilenames
, comment
)
534 view
.AddLines(_("Nothing to commit.\n"))
535 elif status
.number
> 0:
536 view
.AddLines(_("Committed as revision %s.\n") % status
.number
)
538 view
.AddLines(_("Commit failed.\n"))
540 except pysvn
.ClientError
, e
:
541 view
.AddLines("%s\n" % str(e
))
542 wx
.MessageBox(str(e
), _("SVN Commit"), wx
.OK | wx
.ICON_EXCLAMATION
)
544 extype
, ex
, tb
= sys
.exc_info()
545 view
.AddLines("Commit failed: (%s) %s\n" % (extype
, str(ex
)))
546 for line
in traceback
.format_tb(tb
):
548 wx
.MessageBox(_("Commit failed."), _("SVN Commit"), wx
.OK | wx
.ICON_EXCLAMATION
)
550 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
555 elif id == SVNService
.SVN_CHECKIN_ID
:
556 filenames
= self
.GetCurrentDocuments()[:]
557 filenames
.sort(self
.BasenameCaseInsensitiveCompare
)
559 # ask user if dirty files should be saved first
560 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
561 for filename
in filenames
:
563 if doc
.GetFilename() == filename
and doc
.IsModified():
564 yesNoMsg
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(),
565 _("'%s' has unsaved modifications. Save it before commit?") % os
.path
.basename(filename
),
567 wx
.YES_NO|wx
.CANCEL|wx
.ICON_QUESTION
)
568 yesNoMsg
.CenterOnParent()
569 status
= yesNoMsg
.ShowModal()
571 if status
== wx
.ID_YES
:
573 elif status
== wx
.ID_NO
:
575 else: # elif status == wx.CANCEL:
580 for i
, filename
in enumerate(filenames
):
581 shortFilename
= os
.path
.basename(filename
)
582 shortFilenames
.append(shortFilename
)
584 dlg
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SVN Commit"))
586 sizer
= wx
.BoxSizer(wx
.VERTICAL
)
587 sizer
.Add(wx
.StaticText(dlg
, -1, _("Comment:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
588 commentText
= wx
.TextCtrl(dlg
, -1, size
=(250,-1), style
=wx
.TE_MULTILINE
)
589 sizer
.Add(commentText
, 1, wx
.EXPAND|wx
.TOP
, HALF_SPACE
)
591 sizer
.Add(wx
.StaticText(dlg
, -1, _("Files:")), 0, wx
.ALIGN_CENTER_VERTICAL|wx
.TOP
, SPACE
)
592 fileList
= wx
.CheckListBox(dlg
, -1, choices
= shortFilenames
)
593 for i
in range(fileList
.GetCount()):
594 fileList
.Check(i
, True)
595 sizer
.Add(fileList
, 0, wx
.EXPAND|wx
.TOP
, HALF_SPACE
)
597 buttonSizer
= wx
.StdDialogButtonSizer()
598 okBtn
= wx
.Button(dlg
, wx
.ID_OK
)
600 buttonSizer
.AddButton(okBtn
)
601 buttonSizer
.AddButton(wx
.Button(dlg
, wx
.ID_CANCEL
))
602 buttonSizer
.Realize()
604 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
605 contentSizer
.Add(sizer
, 0, wx
.ALL
, SPACE
)
606 contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
)
608 dlg
.SetSizer(contentSizer
)
613 if dlg
.ShowModal() == wx
.ID_OK
:
614 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
616 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
617 messageService
.ShowWindow()
619 view
= messageService
.GetView()
621 view
.AddLines(_("SVN Commit:\n"))
625 for i
in range(fileList
.GetCount()):
626 if fileList
.IsChecked(i
):
627 selFilenames
.append(filenames
[i
])
628 view
.AddLines("%s\n" % filenames
[i
])
630 if len(selFilenames
):
631 comment
= commentText
.GetValue()
632 status
= self
._client
.checkin(selFilenames
, comment
)
635 view
.AddLines(_("Nothing to commit.\n"))
636 elif status
.number
> 0:
637 view
.AddLines(_("Committed as revision %s.\n") % status
.number
)
639 view
.AddLines(_("Commit failed.\n"))
641 except pysvn
.ClientError
, e
:
642 view
.AddLines("%s\n" % str(e
))
643 wx
.MessageBox(str(e
), _("SVN Commit"), wx
.OK | wx
.ICON_EXCLAMATION
)
645 extype
, ex
, tb
= sys
.exc_info()
646 view
.AddLines("Commit failed: (%s) %s\n" % (extype
, str(ex
)))
647 for line
in traceback
.format_tb(tb
):
649 wx
.MessageBox(_("Commit failed."), _("SVN Commit"), wx
.OK | wx
.ICON_EXCLAMATION
)
651 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
655 elif id == SVNService
.SVN_CHECKOUT_ID
:
656 config
= wx
.ConfigBase_Get()
657 svnUrl
= config
.Read(SVN_REPOSITORY_URL
, self
._defaultURL
)
659 dlg
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SVN Checkout"))
661 gridSizer
= wx
.FlexGridSizer(cols
= 2, hgap
= 5, vgap
= 5)
662 gridSizer
.Add(wx
.StaticText(dlg
, -1, _("Repository URL:")), 0, wx
.ALIGN_CENTER_VERTICAL|wx
.RIGHT|wx
.TOP
, HALF_SPACE
)
663 svnUrlList
= ReadSvnUrlList()
664 svnURLCombobox
= wx
.ComboBox(dlg
, -1, size
=(200, -1), choices
=svnUrlList
, style
=wx
.CB_DROPDOWN
)
666 svnURLCombobox
.SetToolTipString(svnUrlList
[0])
667 svnURLCombobox
.SetStringSelection(svnUrlList
[0])
669 svnURLCombobox
.SetToolTipString(_("Set Repository URL"))
670 gridSizer
.Add(svnURLCombobox
, 0)
672 gridSizer
.Add(wx
.StaticText(dlg
, -1, _("Checkout to dir:")), 0, wx
.ALIGN_CENTER_VERTICAL|wx
.RIGHT|wx
.TOP
, HALF_SPACE
)
673 localPath
= wx
.TextCtrl(dlg
, -1, size
= (200, -1))
674 localPath
.SetToolTipString(_("Path in local file system where files will be located."))
675 findDirButton
= wx
.Button(dlg
, -1, _("Browse..."))
677 def OnBrowseButton(event
):
678 dirDlg
= wx
.DirDialog(wx
.GetApp().GetTopWindow(), _("Choose a directory:"), style
=wx
.DD_DEFAULT_STYLE
)
679 dir = localPath
.GetValue()
682 dirDlg
.CenterOnParent()
683 if dirDlg
.ShowModal() == wx
.ID_OK
:
684 localPath
.SetValue(dirDlg
.GetPath())
685 localPath
.SetToolTipString(localPath
.GetValue())
686 localPath
.SetInsertionPointEnd()
688 wx
.EVT_BUTTON(findDirButton
, -1, OnBrowseButton
)
690 sizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
691 sizer
.Add(localPath
, 1, wx
.EXPAND
)
692 sizer
.Add(findDirButton
, 0, wx
.LEFT
, HALF_SPACE
)
693 gridSizer
.Add(sizer
, 0)
695 buttonSizer
= wx
.StdDialogButtonSizer()
696 okBtn
= wx
.Button(dlg
, wx
.ID_OK
)
698 buttonSizer
.AddButton(okBtn
)
699 buttonSizer
.AddButton(wx
.Button(dlg
, wx
.ID_CANCEL
))
700 buttonSizer
.Realize()
702 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
703 contentSizer
.Add(gridSizer
, 0, wx
.ALL
, SPACE
)
704 contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
)
706 dlg
.SetSizer(contentSizer
)
711 if dlg
.ShowModal() == wx
.ID_OK
:
712 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
714 WriteSvnUrlList(svnURLCombobox
)
716 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
717 messageService
.ShowWindow()
719 view
= messageService
.GetView()
721 view
.AddLines(_("SVN Checkout:\n"))
723 svnUrl
= svnURLCombobox
.GetValue()
724 toLocation
= localPath
.GetValue()
726 self
._client
.checkout(svnUrl
, toLocation
)
727 view
.AddLines(_("Checkout completed.\n"))
728 except pysvn
.ClientError
, e
:
729 view
.AddLines(_("Checkout failed. %s\n") % str(e
))
730 wx
.MessageBox(_("Checkout failed. %s") % str(e
), _("SVN Checkout"), wx
.OK | wx
.ICON_EXCLAMATION
)
732 extype
, ex
, tb
= sys
.exc_info()
733 view
.AddLines("Checkout failed: (%s) %s\n" % (extype
, str(ex
)))
734 for line
in traceback
.format_tb(tb
):
736 wx
.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx
.OK | wx
.ICON_EXCLAMATION
)
738 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
742 elif id == SVNService
.SVN_REVERT_ID
:
743 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
745 filenames
= self
.GetCurrentDocuments()
747 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
748 messageService
.ShowWindow()
750 view
= messageService
.GetView()
752 view
.AddLines(_("SVN Revert:\n"))
753 for filename
in filenames
:
754 view
.AddLines("%s\n" % filename
)
757 self
._client
.revert(filenames
)
758 view
.AddLines(_("Revert completed.\n"))
760 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
761 for doc
in openDocs
[:]: # need to make a copy of the list otherwise ordinality changes as we close the files
762 if doc
.GetFilename() in filenames
:
763 yesNoMsg
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(),
764 _("Reverted file '%s' is currently open. Close it?") % os
.path
.basename(doc
.GetFilename()),
766 wx
.YES_NO|wx
.ICON_QUESTION
)
767 yesNoMsg
.CenterOnParent()
768 status
= yesNoMsg
.ShowModal()
770 if status
== wx
.ID_YES
:
773 except pysvn
.ClientError
, e
:
774 view
.AddLines("%s\n" % str(e
))
775 wx
.MessageBox(str(e
), _("SVN Revert"), wx
.OK | wx
.ICON_EXCLAMATION
)
777 extype
, ex
, tb
= sys
.exc_info()
778 view
.AddLines("Revert failed: (%s) %s\n" % (extype
, str(ex
)))
779 for line
in traceback
.format_tb(tb
):
781 wx
.MessageBox(_("Revert failed."), _("SVN Revert"), wx
.OK | wx
.ICON_EXCLAMATION
)
783 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
786 elif id == SVNService
.SVN_ADD_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 Add:\n"))
797 for filename
in filenames
:
798 view
.AddLines("%s\n" % filename
)
801 self
._client
.add(filenames
)
802 view
.AddLines(_("Add completed.\n"))
803 except pysvn
.ClientError
, e
:
804 view
.AddLines("%s\n" % str(e
))
805 wx
.MessageBox(str(e
), _("SVN Add"), wx
.OK | wx
.ICON_EXCLAMATION
)
807 extype
, ex
, tb
= sys
.exc_info()
808 view
.AddLines("Add failed: (%s) %s\n" % (extype
, str(ex
)))
809 for line
in traceback
.format_tb(tb
):
811 wx
.MessageBox(_("Add failed."), _("SVN Add"), wx
.OK | wx
.ICON_EXCLAMATION
)
813 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
816 elif id == SVNService
.SVN_DELETE_ID
:
817 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
819 filenames
= self
.GetCurrentDocuments()
821 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
822 messageService
.ShowWindow()
824 view
= messageService
.GetView()
826 view
.AddLines(_("SVN Delete:\n"))
827 for filename
in filenames
:
828 view
.AddLines("%s\n" % filename
)
831 self
._client
.remove(filenames
)
832 view
.AddLines(_("Delete completed.\n"))
833 except pysvn
.ClientError
, e
:
834 view
.AddLines("%s\n" % str(e
))
835 wx
.MessageBox(str(e
), _("SVN Delete"), wx
.OK | wx
.ICON_EXCLAMATION
)
837 extype
, ex
, tb
= sys
.exc_info()
838 view
.AddLines("Delete failed: (%s) %s\n" % (extype
, str(ex
)))
839 for line
in traceback
.format_tb(tb
):
841 wx
.MessageBox(_("Delete failed."), _("SVN Delete"), wx
.OK | wx
.ICON_EXCLAMATION
)
843 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
849 def ProcessUpdateUIEvent(self
, event
):
852 if id in [SVNService
.SVN_CHECKIN_ID
,
853 SVNService
.SVN_REVERT_ID
,
854 SVNService
.SVN_ADD_ID
,
855 SVNService
.SVN_DELETE_ID
]:
856 if self
.GetCurrentDocuments():
862 elif id == SVNService
.SVN_UPDATE_ID
:
863 if self
.GetCurrentDocuments() or self
.GetCurrentFolder():
869 elif id == SVNService
.SVN_CHECKOUT_ID
:
873 elif (id == SVNService
.SVN_UPDATE_ALL_ID
874 or id == SVNService
.SVN_CHECKIN_ALL_ID
):
875 if self
.GetCurrentProject():
884 def GetCurrentProject(self
):
885 projectService
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
)
887 projView
= projectService
.GetView()
888 return projView
.GetSelectedProject()
892 def GetCurrentDocuments(self
):
893 projectService
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
)
895 projView
= projectService
.GetView()
897 if projView
.FilesHasFocus():
898 filenames
= projView
.GetSelectedFiles()
904 doc
= wx
.GetApp().GetTopWindow().GetDocumentManager().GetCurrentDocument()
906 filenames
= [doc
.GetFilename()]
913 def GetCurrentFolder(self
):
914 projectService
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
)
916 projView
= projectService
.GetView()
918 if projView
.FilesHasFocus():
919 folderPath
= projView
.GetSelectedPhysicalFolder()
926 def BasenameCaseInsensitiveCompare(self
, s1
, s2
):
927 s1L
= os
.path
.basename(s1
).lower()
928 s2L
= os
.path
.basename(s2
).lower()
937 class SVNOptionsPanel(wx
.Panel
):
940 def __init__(self
, parent
, id):
941 wx
.Panel
.__init
__(self
, parent
, id)
943 config
= wx
.ConfigBase_Get()
944 configDir
= config
.Read(SVN_CONFIG_DIR
, "")
946 borderSizer
= wx
.BoxSizer(wx
.VERTICAL
)
947 sizer
= wx
.FlexGridSizer(cols
= 2, hgap
= 5, vgap
= 5)
948 sizer
.AddGrowableCol(1, 1)
950 sizer
.Add(wx
.StaticText(self
, -1, _("SVN Config Dir:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
952 self
._svnConfigDir
= wx
.TextCtrl(self
, -1, configDir
, size
= (200, -1))
954 self
._svnConfigDir
.SetToolTipString(_("Path Subversion configuration directory."))
956 self
._svnConfigDir
.SetToolTipString(configDir
)
958 findDirButton
= wx
.Button(self
, -1, _("Browse..."))
960 def OnBrowseButton(event
):
961 dirDlg
= wx
.DirDialog(self
, _("Choose a directory:"), style
=wx
.DD_DEFAULT_STYLE
)
962 dir = self
._svnConfigDir
.GetValue()
965 dirDlg
.CenterOnParent()
966 if dirDlg
.ShowModal() == wx
.ID_OK
:
967 self
._svnConfigDir
.SetValue(dirDlg
.GetPath())
968 self
._svnConfigDir
.SetToolTipString(self
._svnConfigDir
.GetValue())
969 self
._svnConfigDir
.SetInsertionPointEnd()
971 wx
.EVT_BUTTON(findDirButton
, -1, OnBrowseButton
)
973 hsizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
974 hsizer
.Add(self
._svnConfigDir
, 1, wx
.EXPAND
)
975 hsizer
.Add(findDirButton
, 0, wx
.LEFT
, HALF_SPACE
)
976 sizer
.Add(hsizer
, 0, wx
.EXPAND
)
979 svnUrlList
= ReadSvnUrlList()
980 sizer
.Add(wx
.StaticText(self
, -1, _("SVN URL:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
981 self
._svnURLCombobox
= wx
.ComboBox(self
, -1, size
=(200, -1), choices
=svnUrlList
, style
=wx
.CB_DROPDOWN
)
983 self
._svnURLCombobox
.SetToolTipString(svnUrlList
[0])
984 self
._svnURLCombobox
.SetStringSelection(svnUrlList
[0])
986 self
._svnURLCombobox
.SetToolTipString(_("Set Repository URL"))
987 sizer
.Add(self
._svnURLCombobox
, 0, wx
.EXPAND
)
990 sizer
.Add(wx
.StaticText(self
, -1, _("SVN_SSH:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
991 svnSSH
= os
.getenv("SVN_SSH")
992 if not svnSSH
or svnSSH
== "":
993 self
._svnSSH
= wx
.TextCtrl(self
, -1, size
= (200, -1))
995 self
._svnSSH
= wx
.TextCtrl(self
, -1, svnSSH
, size
= (200, -1))
996 self
._svnSSH
.SetToolTipString(_("Override SVN_SSH environment variable temporarily."))
998 findSSHButton
= wx
.Button(self
, -1, _("Browse..."))
1000 def OnBrowseFileButton(event
):
1001 dirDlg
= wx
.FileDialog(self
, _("Choose a file:"), style
=wx
.OPEN|wx
.CHANGE_DIR
)
1002 # dirDlg.CenterOnParent() # wxBug: caused crash with wx.FileDialog
1003 if dirDlg
.ShowModal() == wx
.ID_OK
:
1004 self
._svnSSH
.SetValue(dirDlg
.GetPath())
1005 self
._svnSSH
.SetToolTipString(self
._svnSSH
.GetValue())
1006 self
._svnSSH
.SetInsertionPointEnd()
1008 wx
.EVT_BUTTON(findSSHButton
, -1, OnBrowseFileButton
)
1010 hsizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
1011 hsizer
.Add(self
._svnSSH
, 1, wx
.EXPAND
)
1012 hsizer
.Add(findSSHButton
, 0, wx
.LEFT
, HALF_SPACE
)
1013 sizer
.Add(hsizer
, 0, wx
.EXPAND
)
1016 borderSizer
.Add(sizer
, 0, wx
.ALL|wx
.EXPAND
, SPACE
)
1017 self
.SetSizer(borderSizer
)
1019 parent
.AddPage(self
, _("SVN"))
1026 def OnOK(self
, optionsDialog
):
1027 config
= wx
.ConfigBase_Get()
1029 config
.Write(SVN_CONFIG_DIR
, self
._svnConfigDir
.GetValue())
1031 WriteSvnUrlList(self
._svnURLCombobox
)
1033 os
.environ
["SVN_SSH"] = self
._svnSSH
.GetValue()
1036 def ReadSvnUrlList():
1037 """ Read in list of SNV repository URLs. First in list is the last one path used. """
1038 config
= wx
.ConfigBase_Get()
1039 urlStringList
= config
.Read(SVN_REPOSITORY_URL
)
1040 if len(urlStringList
):
1041 urlList
= eval(urlStringList
)
1044 if len(urlList
) == 0:
1045 svnService
= wx
.GetApp().GetService(SVNService
)
1046 if svnService
and hasattr(svnService
, "_defaultURL"):
1047 urlList
.append(svnService
._defaultURL
)
1051 def WriteSvnUrlList(comboBox
):
1052 """ Save out list of SVN repository URLs from combobox. Put on top the current selection. Only save out first 10 from the list """
1055 url
= comboBox
.GetValue()
1059 for i
in range(min(comboBox
.GetCount(), 10)):
1060 url
= comboBox
.GetString(i
)
1061 if url
not in urlList
:
1064 config
= wx
.ConfigBase_Get()
1065 config
.Write(SVN_REPOSITORY_URL
, urlList
.__repr
__())