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"))
328 if id == SVNService
.SVN_UPDATE_ID
:
329 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
331 filenames
= self
.GetCurrentDocuments()
333 filenames
= filenames
[:]
334 filenames
.sort(self
.BasenameCaseInsensitiveCompare
)
336 folderPath
= self
.GetCurrentFolder()
338 filenames
= [folderPath
]
340 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
341 messageService
.ShowWindow()
343 view
= messageService
.GetView()
345 view
.AddLines(_("SVN Update:\n"))
347 for filename
in filenames
:
348 view
.AddLines("%s\n" % filename
)
350 status
= self
._client
.update(filename
)
352 if status
.number
> 0:
353 view
.AddLines(_("Updated to revision %s\n") % status
.number
)
355 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
357 if doc
.GetFilename() == filename
:
358 yesNoMsg
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(),
359 _("Updated file '%s' is currently open. Close it?") % os
.path
.basename(filename
),
361 wx
.YES_NO|wx
.ICON_QUESTION
)
362 yesNoMsg
.CenterOnParent()
363 status
= yesNoMsg
.ShowModal()
365 if status
== wx
.ID_YES
:
369 view
.AddLines(_("Update failed.\n"))
371 except pysvn
.ClientError
, e
:
372 view
.AddLines("%s\n" % str(e
))
373 wx
.MessageBox(str(e
), _("SVN Update"), wx
.OK | wx
.ICON_EXCLAMATION
)
375 extype
, ex
, tb
= sys
.exc_info()
376 view
.AddLines("Update failed: (%s) %s\n" % (extype
, str(ex
)))
377 for line
in traceback
.format_tb(tb
):
380 wx
.MessageBox(_("Update failed."), _("SVN Update"), wx
.OK | wx
.ICON_EXCLAMATION
)
382 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
386 elif id == SVNService
.SVN_UPDATE_ALL_ID
:
387 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
389 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
390 messageService
.ShowWindow()
392 view
= messageService
.GetView()
394 view
.AddLines(_("SVN Update:\n"))
396 project
= self
.GetCurrentProject()
398 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
400 if doc
.GetFilename() == project
:
401 filenames
= doc
.GetFiles()[:] # make a copy and sort it.
402 filenames
.sort(self
.BasenameCaseInsensitiveCompare
)
404 for filename
in filenames
:
405 view
.AddLines("%s\n" % filename
)
407 status
= self
._client
.update(filename
)
409 if status
.number
> 0:
410 view
.AddLines(_("Updated to revision %s\n") % status
.number
)
412 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
414 if doc
.GetFilename() == filename
:
415 yesNoMsg
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(),
416 _("Updated file '%s' is currently open. Close it?") % os
.path
.basename(filename
),
418 wx
.YES_NO|wx
.CANCEL|wx
.ICON_QUESTION
)
419 yesNoMsg
.CenterOnParent()
420 status
= yesNoMsg
.ShowModal()
422 if status
== wx
.ID_YES
:
424 elif status
== wx
.ID_NO
:
426 else: # elif status == wx.CANCEL:
427 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
431 view
.AddLines(_("Update failed.\n"))
433 except pysvn
.ClientError
, e
:
434 view
.AddLines("%s\n" % str(e
))
435 wx
.MessageBox(str(e
), _("SVN Update"), wx
.OK | wx
.ICON_EXCLAMATION
)
437 extype
, ex
, tb
= sys
.exc_info()
438 view
.AddLines("Update failed: (%s) %s\n" % (extype
, str(ex
)))
439 for line
in traceback
.format_tb(tb
):
442 wx
.MessageBox(_("Update failed."), _("SVN Update"), wx
.OK | wx
.ICON_EXCLAMATION
)
444 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
447 elif id == SVNService
.SVN_CHECKIN_ALL_ID
:
449 project
= self
.GetCurrentProject()
451 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
453 if doc
.GetFilename() == project
:
454 for filename
in doc
.GetFiles():
455 if filename
not in filenames
:
456 filenames
.append(filename
)
457 filenames
.sort(self
.BasenameCaseInsensitiveCompare
)
459 # ask user if dirty files should be saved first
460 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
461 for filename
in filenames
:
463 if doc
.GetFilename() == filename
and doc
.IsModified():
464 yesNoMsg
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(),
465 _("'%s' has unsaved modifications. Save it before commit?") % os
.path
.basename(filename
),
467 wx
.YES_NO|wx
.CANCEL|wx
.ICON_QUESTION
)
468 yesNoMsg
.CenterOnParent()
469 status
= yesNoMsg
.ShowModal()
471 if status
== wx
.ID_YES
:
473 elif status
== wx
.ID_NO
:
475 else: # elif status == wx.CANCEL:
480 for i
, filename
in enumerate(filenames
):
481 shortFilename
= os
.path
.basename(filename
)
482 shortFilenames
.append(shortFilename
)
484 dlg
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SVN Commit"))
486 sizer
= wx
.BoxSizer(wx
.VERTICAL
)
487 sizer
.Add(wx
.StaticText(dlg
, -1, _("Comment:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
488 commentText
= wx
.TextCtrl(dlg
, -1, size
=(250,-1), style
=wx
.TE_MULTILINE
)
489 sizer
.Add(commentText
, 1, wx
.EXPAND|wx
.TOP
, HALF_SPACE
)
491 sizer
.Add(wx
.StaticText(dlg
, -1, _("Files:")), 0, wx
.ALIGN_CENTER_VERTICAL|wx
.TOP
, SPACE
)
492 fileList
= wx
.CheckListBox(dlg
, -1, choices
= shortFilenames
)
493 for i
in range(fileList
.GetCount()):
494 fileList
.Check(i
, True)
495 sizer
.Add(fileList
, 0, wx
.EXPAND|wx
.TOP
, HALF_SPACE
)
497 buttonSizer
= wx
.StdDialogButtonSizer()
498 okBtn
= wx
.Button(dlg
, wx
.ID_OK
)
500 buttonSizer
.AddButton(okBtn
)
501 buttonSizer
.AddButton(wx
.Button(dlg
, wx
.ID_CANCEL
))
502 buttonSizer
.Realize()
504 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
505 contentSizer
.Add(sizer
, 0, wx
.ALL
, SPACE
)
506 contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
)
508 dlg
.SetSizer(contentSizer
)
513 if dlg
.ShowModal() == wx
.ID_OK
:
514 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
516 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
517 messageService
.ShowWindow()
519 view
= messageService
.GetView()
521 view
.AddLines(_("SVN Commit:\n"))
525 for i
in range(fileList
.GetCount()):
526 if fileList
.IsChecked(i
):
527 selFilenames
.append(filenames
[i
])
528 view
.AddLines("%s\n" % filenames
[i
])
530 if len(selFilenames
):
531 comment
= commentText
.GetValue()
532 status
= self
._client
.checkin(selFilenames
, comment
)
535 view
.AddLines(_("Nothing to commit.\n"))
536 elif status
.number
> 0:
537 view
.AddLines(_("Committed as revision %s.\n") % status
.number
)
539 view
.AddLines(_("Commit failed.\n"))
541 except pysvn
.ClientError
, e
:
542 view
.AddLines("%s\n" % str(e
))
543 wx
.MessageBox(str(e
), _("SVN Commit"), wx
.OK | wx
.ICON_EXCLAMATION
)
545 extype
, ex
, tb
= sys
.exc_info()
546 view
.AddLines("Commit failed: (%s) %s\n" % (extype
, str(ex
)))
547 for line
in traceback
.format_tb(tb
):
549 wx
.MessageBox(_("Commit failed."), _("SVN Commit"), wx
.OK | wx
.ICON_EXCLAMATION
)
551 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
556 elif id == SVNService
.SVN_CHECKIN_ID
:
557 filenames
= self
.GetCurrentDocuments()[:]
558 filenames
.sort(self
.BasenameCaseInsensitiveCompare
)
560 # ask user if dirty files should be saved first
561 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
562 for filename
in filenames
:
564 if doc
.GetFilename() == filename
and doc
.IsModified():
565 yesNoMsg
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(),
566 _("'%s' has unsaved modifications. Save it before commit?") % os
.path
.basename(filename
),
568 wx
.YES_NO|wx
.CANCEL|wx
.ICON_QUESTION
)
569 yesNoMsg
.CenterOnParent()
570 status
= yesNoMsg
.ShowModal()
572 if status
== wx
.ID_YES
:
574 elif status
== wx
.ID_NO
:
576 else: # elif status == wx.CANCEL:
581 for i
, filename
in enumerate(filenames
):
582 shortFilename
= os
.path
.basename(filename
)
583 shortFilenames
.append(shortFilename
)
585 dlg
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SVN Commit"))
587 sizer
= wx
.BoxSizer(wx
.VERTICAL
)
588 sizer
.Add(wx
.StaticText(dlg
, -1, _("Comment:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
589 commentText
= wx
.TextCtrl(dlg
, -1, size
=(250,-1), style
=wx
.TE_MULTILINE
)
590 sizer
.Add(commentText
, 1, wx
.EXPAND|wx
.TOP
, HALF_SPACE
)
592 sizer
.Add(wx
.StaticText(dlg
, -1, _("Files:")), 0, wx
.ALIGN_CENTER_VERTICAL|wx
.TOP
, SPACE
)
593 fileList
= wx
.CheckListBox(dlg
, -1, choices
= shortFilenames
)
594 for i
in range(fileList
.GetCount()):
595 fileList
.Check(i
, True)
596 sizer
.Add(fileList
, 0, wx
.EXPAND|wx
.TOP
, HALF_SPACE
)
598 buttonSizer
= wx
.StdDialogButtonSizer()
599 okBtn
= wx
.Button(dlg
, wx
.ID_OK
)
601 buttonSizer
.AddButton(okBtn
)
602 buttonSizer
.AddButton(wx
.Button(dlg
, wx
.ID_CANCEL
))
603 buttonSizer
.Realize()
605 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
606 contentSizer
.Add(sizer
, 0, wx
.ALL
, SPACE
)
607 contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
)
609 dlg
.SetSizer(contentSizer
)
614 if dlg
.ShowModal() == wx
.ID_OK
:
615 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
617 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
618 messageService
.ShowWindow()
620 view
= messageService
.GetView()
622 view
.AddLines(_("SVN Commit:\n"))
626 for i
in range(fileList
.GetCount()):
627 if fileList
.IsChecked(i
):
628 selFilenames
.append(filenames
[i
])
629 view
.AddLines("%s\n" % filenames
[i
])
631 if len(selFilenames
):
632 comment
= commentText
.GetValue()
633 status
= self
._client
.checkin(selFilenames
, comment
)
636 view
.AddLines(_("Nothing to commit.\n"))
637 elif status
.number
> 0:
638 view
.AddLines(_("Committed as revision %s.\n") % status
.number
)
640 view
.AddLines(_("Commit failed.\n"))
642 except pysvn
.ClientError
, e
:
643 view
.AddLines("%s\n" % str(e
))
644 wx
.MessageBox(str(e
), _("SVN Commit"), wx
.OK | wx
.ICON_EXCLAMATION
)
646 extype
, ex
, tb
= sys
.exc_info()
647 view
.AddLines("Commit failed: (%s) %s\n" % (extype
, str(ex
)))
648 for line
in traceback
.format_tb(tb
):
650 wx
.MessageBox(_("Commit failed."), _("SVN Commit"), wx
.OK | wx
.ICON_EXCLAMATION
)
652 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
656 elif id == SVNService
.SVN_CHECKOUT_ID
:
657 config
= wx
.ConfigBase_Get()
658 svnUrl
= config
.Read(SVN_REPOSITORY_URL
, self
._defaultURL
)
660 dlg
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("SVN Checkout"))
662 gridSizer
= wx
.FlexGridSizer(cols
= 2, hgap
= 5, vgap
= 5)
663 gridSizer
.Add(wx
.StaticText(dlg
, -1, _("Repository URL:")), 0, wx
.ALIGN_CENTER_VERTICAL|wx
.RIGHT|wx
.TOP
, HALF_SPACE
)
664 svnUrlList
= ReadSvnUrlList()
665 svnURLCombobox
= wx
.ComboBox(dlg
, -1, size
=(200, -1), choices
=svnUrlList
, style
=wx
.CB_DROPDOWN
)
667 svnURLCombobox
.SetToolTipString(svnUrlList
[0])
668 svnURLCombobox
.SetStringSelection(svnUrlList
[0])
670 svnURLCombobox
.SetToolTipString(_("Set Repository URL"))
671 gridSizer
.Add(svnURLCombobox
, 0)
673 gridSizer
.Add(wx
.StaticText(dlg
, -1, _("Checkout to dir:")), 0, wx
.ALIGN_CENTER_VERTICAL|wx
.RIGHT|wx
.TOP
, HALF_SPACE
)
674 localPath
= wx
.TextCtrl(dlg
, -1, size
= (200, -1))
675 localPath
.SetToolTipString(_("Path in local file system where files will be located."))
676 findDirButton
= wx
.Button(dlg
, -1, _("Browse..."))
678 def OnBrowseButton(event
):
679 dirDlg
= wx
.DirDialog(wx
.GetApp().GetTopWindow(), _("Choose a directory:"), style
=wx
.DD_DEFAULT_STYLE
)
680 dir = localPath
.GetValue()
683 dirDlg
.CenterOnParent()
684 if dirDlg
.ShowModal() == wx
.ID_OK
:
685 localPath
.SetValue(dirDlg
.GetPath())
686 localPath
.SetToolTipString(localPath
.GetValue())
687 localPath
.SetInsertionPointEnd()
689 wx
.EVT_BUTTON(findDirButton
, -1, OnBrowseButton
)
691 sizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
692 sizer
.Add(localPath
, 1, wx
.EXPAND
)
693 sizer
.Add(findDirButton
, 0, wx
.LEFT
, HALF_SPACE
)
694 gridSizer
.Add(sizer
, 0)
696 buttonSizer
= wx
.StdDialogButtonSizer()
697 okBtn
= wx
.Button(dlg
, wx
.ID_OK
)
699 buttonSizer
.AddButton(okBtn
)
700 buttonSizer
.AddButton(wx
.Button(dlg
, wx
.ID_CANCEL
))
701 buttonSizer
.Realize()
703 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
704 contentSizer
.Add(gridSizer
, 0, wx
.ALL
, SPACE
)
705 contentSizer
.Add(buttonSizer
, 0, wx
.ALL|wx
.ALIGN_RIGHT
, SPACE
)
707 dlg
.SetSizer(contentSizer
)
712 if dlg
.ShowModal() == wx
.ID_OK
:
713 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
715 WriteSvnUrlList(svnURLCombobox
)
717 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
718 messageService
.ShowWindow()
720 view
= messageService
.GetView()
722 view
.AddLines(_("SVN Checkout:\n"))
724 svnUrl
= svnURLCombobox
.GetValue()
725 toLocation
= localPath
.GetValue()
727 self
._client
.checkout(svnUrl
, toLocation
)
728 view
.AddLines(_("Checkout completed.\n"))
729 except pysvn
.ClientError
, e
:
730 view
.AddLines(_("Checkout failed. %s\n") % str(e
))
731 wx
.MessageBox(_("Checkout failed. %s") % str(e
), _("SVN Checkout"), wx
.OK | wx
.ICON_EXCLAMATION
)
733 extype
, ex
, tb
= sys
.exc_info()
734 view
.AddLines("Checkout failed: (%s) %s\n" % (extype
, str(ex
)))
735 for line
in traceback
.format_tb(tb
):
737 wx
.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx
.OK | wx
.ICON_EXCLAMATION
)
739 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
743 elif id == SVNService
.SVN_REVERT_ID
:
744 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
746 filenames
= self
.GetCurrentDocuments()
748 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
749 messageService
.ShowWindow()
751 view
= messageService
.GetView()
753 view
.AddLines(_("SVN Revert:\n"))
754 for filename
in filenames
:
755 view
.AddLines("%s\n" % filename
)
758 self
._client
.revert(filenames
)
759 view
.AddLines(_("Revert completed.\n"))
761 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
762 for doc
in openDocs
[:]: # need to make a copy of the list otherwise ordinality changes as we close the files
763 if doc
.GetFilename() in filenames
:
764 yesNoMsg
= wx
.MessageDialog(wx
.GetApp().GetTopWindow(),
765 _("Reverted file '%s' is currently open. Close it?") % os
.path
.basename(doc
.GetFilename()),
767 wx
.YES_NO|wx
.ICON_QUESTION
)
768 yesNoMsg
.CenterOnParent()
769 status
= yesNoMsg
.ShowModal()
771 if status
== wx
.ID_YES
:
774 except pysvn
.ClientError
, e
:
775 view
.AddLines("%s\n" % str(e
))
776 wx
.MessageBox(str(e
), _("SVN Revert"), wx
.OK | wx
.ICON_EXCLAMATION
)
778 extype
, ex
, tb
= sys
.exc_info()
779 view
.AddLines("Revert failed: (%s) %s\n" % (extype
, str(ex
)))
780 for line
in traceback
.format_tb(tb
):
782 wx
.MessageBox(_("Revert failed."), _("SVN Revert"), wx
.OK | wx
.ICON_EXCLAMATION
)
784 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
787 elif id == SVNService
.SVN_ADD_ID
:
788 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
790 filenames
= self
.GetCurrentDocuments()
792 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
793 messageService
.ShowWindow()
795 view
= messageService
.GetView()
797 view
.AddLines(_("SVN Add:\n"))
798 for filename
in filenames
:
799 view
.AddLines("%s\n" % filename
)
802 self
._client
.add(filenames
)
803 view
.AddLines(_("Add completed.\n"))
804 except pysvn
.ClientError
, e
:
805 view
.AddLines("%s\n" % str(e
))
806 wx
.MessageBox(str(e
), _("SVN Add"), wx
.OK | wx
.ICON_EXCLAMATION
)
808 extype
, ex
, tb
= sys
.exc_info()
809 view
.AddLines("Add failed: (%s) %s\n" % (extype
, str(ex
)))
810 for line
in traceback
.format_tb(tb
):
812 wx
.MessageBox(_("Add failed."), _("SVN Add"), wx
.OK | wx
.ICON_EXCLAMATION
)
814 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
817 elif id == SVNService
.SVN_DELETE_ID
:
818 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
820 filenames
= self
.GetCurrentDocuments()
822 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
823 messageService
.ShowWindow()
825 view
= messageService
.GetView()
827 view
.AddLines(_("SVN Delete:\n"))
828 for filename
in filenames
:
829 view
.AddLines("%s\n" % filename
)
832 self
._client
.remove(filenames
)
833 view
.AddLines(_("Delete completed.\n"))
834 except pysvn
.ClientError
, e
:
835 view
.AddLines("%s\n" % str(e
))
836 wx
.MessageBox(str(e
), _("SVN Delete"), wx
.OK | wx
.ICON_EXCLAMATION
)
838 extype
, ex
, tb
= sys
.exc_info()
839 view
.AddLines("Delete failed: (%s) %s\n" % (extype
, str(ex
)))
840 for line
in traceback
.format_tb(tb
):
842 wx
.MessageBox(_("Delete failed."), _("SVN Delete"), wx
.OK | wx
.ICON_EXCLAMATION
)
844 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
849 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
852 def ProcessUpdateUIEvent(self
, event
):
855 if id in [SVNService
.SVN_CHECKIN_ID
,
856 SVNService
.SVN_REVERT_ID
,
857 SVNService
.SVN_ADD_ID
,
858 SVNService
.SVN_DELETE_ID
]:
859 if self
.GetCurrentDocuments():
865 elif id == SVNService
.SVN_UPDATE_ID
:
866 if self
.GetCurrentDocuments() or self
.GetCurrentFolder():
872 elif id == SVNService
.SVN_CHECKOUT_ID
:
876 elif (id == SVNService
.SVN_UPDATE_ALL_ID
877 or id == SVNService
.SVN_CHECKIN_ALL_ID
):
878 if self
.GetCurrentProject():
887 def GetCurrentProject(self
):
888 projectService
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
)
890 projView
= projectService
.GetView()
891 return projView
.GetSelectedProject()
895 def GetCurrentDocuments(self
):
896 projectService
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
)
898 projView
= projectService
.GetView()
900 if projView
.FilesHasFocus():
901 filenames
= projView
.GetSelectedFiles()
907 doc
= wx
.GetApp().GetTopWindow().GetDocumentManager().GetCurrentDocument()
909 filenames
= [doc
.GetFilename()]
916 def GetCurrentFolder(self
):
917 projectService
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
)
919 projView
= projectService
.GetView()
921 if projView
.FilesHasFocus():
922 folderPath
= projView
.GetSelectedPhysicalFolder()
929 def BasenameCaseInsensitiveCompare(self
, s1
, s2
):
930 s1L
= os
.path
.basename(s1
).lower()
931 s2L
= os
.path
.basename(s2
).lower()
940 class SVNOptionsPanel(wx
.Panel
):
943 def __init__(self
, parent
, id):
944 wx
.Panel
.__init
__(self
, parent
, id)
946 config
= wx
.ConfigBase_Get()
947 configDir
= config
.Read(SVN_CONFIG_DIR
, "")
949 borderSizer
= wx
.BoxSizer(wx
.VERTICAL
)
950 sizer
= wx
.FlexGridSizer(cols
= 2, hgap
= 5, vgap
= 5)
951 sizer
.AddGrowableCol(1, 1)
953 sizer
.Add(wx
.StaticText(self
, -1, _("SVN Config Dir:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
955 self
._svnConfigDir
= wx
.TextCtrl(self
, -1, configDir
, size
= (200, -1))
957 self
._svnConfigDir
.SetToolTipString(_("Path Subversion configuration directory."))
959 self
._svnConfigDir
.SetToolTipString(configDir
)
961 findDirButton
= wx
.Button(self
, -1, _("Browse..."))
963 def OnBrowseButton(event
):
964 dirDlg
= wx
.DirDialog(self
, _("Choose a directory:"), style
=wx
.DD_DEFAULT_STYLE
)
965 dir = self
._svnConfigDir
.GetValue()
968 dirDlg
.CenterOnParent()
969 if dirDlg
.ShowModal() == wx
.ID_OK
:
970 self
._svnConfigDir
.SetValue(dirDlg
.GetPath())
971 self
._svnConfigDir
.SetToolTipString(self
._svnConfigDir
.GetValue())
972 self
._svnConfigDir
.SetInsertionPointEnd()
974 wx
.EVT_BUTTON(findDirButton
, -1, OnBrowseButton
)
976 hsizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
977 hsizer
.Add(self
._svnConfigDir
, 1, wx
.EXPAND
)
978 hsizer
.Add(findDirButton
, 0, wx
.LEFT
, HALF_SPACE
)
979 sizer
.Add(hsizer
, 0, wx
.EXPAND
)
982 svnUrlList
= ReadSvnUrlList()
983 sizer
.Add(wx
.StaticText(self
, -1, _("SVN URL:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
984 self
._svnURLCombobox
= wx
.ComboBox(self
, -1, size
=(200, -1), choices
=svnUrlList
, style
=wx
.CB_DROPDOWN
)
986 self
._svnURLCombobox
.SetToolTipString(svnUrlList
[0])
987 self
._svnURLCombobox
.SetStringSelection(svnUrlList
[0])
989 self
._svnURLCombobox
.SetToolTipString(_("Set Repository URL"))
990 sizer
.Add(self
._svnURLCombobox
, 0, wx
.EXPAND
)
993 sizer
.Add(wx
.StaticText(self
, -1, _("SVN_SSH:")), 0, wx
.ALIGN_CENTER_VERTICAL
)
994 svnSSH
= os
.getenv("SVN_SSH")
995 if not svnSSH
or svnSSH
== "":
996 self
._svnSSH
= wx
.TextCtrl(self
, -1, size
= (200, -1))
998 self
._svnSSH
= wx
.TextCtrl(self
, -1, svnSSH
, size
= (200, -1))
999 self
._svnSSH
.SetToolTipString(_("Override SVN_SSH environment variable temporarily."))
1001 findSSHButton
= wx
.Button(self
, -1, _("Browse..."))
1003 def OnBrowseFileButton(event
):
1004 dirDlg
= wx
.FileDialog(self
, _("Choose a file:"), style
=wx
.OPEN|wx
.CHANGE_DIR
)
1005 # dirDlg.CenterOnParent() # wxBug: caused crash with wx.FileDialog
1006 if dirDlg
.ShowModal() == wx
.ID_OK
:
1007 self
._svnSSH
.SetValue(dirDlg
.GetPath())
1008 self
._svnSSH
.SetToolTipString(self
._svnSSH
.GetValue())
1009 self
._svnSSH
.SetInsertionPointEnd()
1011 wx
.EVT_BUTTON(findSSHButton
, -1, OnBrowseFileButton
)
1013 hsizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
1014 hsizer
.Add(self
._svnSSH
, 1, wx
.EXPAND
)
1015 hsizer
.Add(findSSHButton
, 0, wx
.LEFT
, HALF_SPACE
)
1016 sizer
.Add(hsizer
, 0, wx
.EXPAND
)
1019 borderSizer
.Add(sizer
, 0, wx
.ALL|wx
.EXPAND
, SPACE
)
1020 self
.SetSizer(borderSizer
)
1022 parent
.AddPage(self
, _("SVN"))
1029 def OnOK(self
, optionsDialog
):
1030 config
= wx
.ConfigBase_Get()
1032 config
.Write(SVN_CONFIG_DIR
, self
._svnConfigDir
.GetValue())
1034 WriteSvnUrlList(self
._svnURLCombobox
)
1036 os
.environ
["SVN_SSH"] = self
._svnSSH
.GetValue()
1039 def ReadSvnUrlList():
1040 """ Read in list of SNV repository URLs. First in list is the last one path used. """
1041 config
= wx
.ConfigBase_Get()
1042 urlStringList
= config
.Read(SVN_REPOSITORY_URL
)
1043 if len(urlStringList
):
1044 urlList
= eval(urlStringList
)
1047 if len(urlList
) == 0:
1048 svnService
= wx
.GetApp().GetService(SVNService
)
1049 if svnService
and hasattr(svnService
, "_defaultURL"):
1050 urlList
.append(svnService
._defaultURL
)
1054 def WriteSvnUrlList(comboBox
):
1055 """ Save out list of SVN repository URLs from combobox. Put on top the current selection. Only save out first 10 from the list """
1058 url
= comboBox
.GetValue()
1062 for i
in range(min(comboBox
.GetCount(), 10)):
1063 url
= comboBox
.GetString(i
)
1064 if url
not in urlList
:
1067 config
= wx
.ConfigBase_Get()
1068 config
.Write(SVN_REPOSITORY_URL
, urlList
.__repr
__())