]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/SVNService.py
DocView and ActiveGrid IDE updates from Morgan Hua:
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / SVNService.py
1 #----------------------------------------------------------------------------
2 # Name: SVNService.py
3 # Purpose: Subversion Service for pydocview
4 #
5 # Author: Morgan Hua
6 #
7 # Created: 5/13/05
8 # CVS-ID: $Id$
9 # Copyright: (c) 2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12
13 import wx
14 import os
15 import os.path
16 import ProjectEditor
17 import MessageService
18
19 import sys # for errors
20 import traceback # for errors
21
22 try:
23 import pysvn # python-subversion integration
24 SVN_INSTALLED = True
25 except ImportError:
26 SVN_INSTALLED = False
27
28 _ = wx.GetTranslation
29
30
31 #----------------------------------------------------------------------------
32 # Constants
33 #----------------------------------------------------------------------------
34 SVN_CONFIG_DIR = "SVNConfigDir"
35 SVN_REPOSITORY_URL = "SVNRepositoryURLs"
36
37 SPACE = 10
38 HALF_SPACE = 5
39
40
41 #----------------------------------------------------------------------------
42 # Classes
43 #----------------------------------------------------------------------------
44
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]
55
56
57 def __init__(self):
58 self._defaultURL = "svn://"
59
60 global SVN_INSTALLED
61 if SVN_INSTALLED:
62 config = wx.ConfigBase_Get()
63
64 configDir = config.Read(SVN_CONFIG_DIR, "")
65 self._client = pysvn.Client(configDir)
66 try:
67 self._defaultURL = self._client.info('.').url
68 except:
69 pass
70
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
79
80 #----------------------------------------------------------------------------
81 # pysvn.Client() Callback Methods
82 #----------------------------------------------------------------------------
83
84 def IfCancel(self):
85 """ return True if user wants to cancel current command """
86 return False
87
88
89 def UpdateStatus(self, eventDict):
90 messageService = wx.GetApp().GetService(MessageService.MessageService)
91 messageService.ShowWindow()
92
93 view = messageService.GetView()
94 view.AddLines(_("%s %s\n") % (eventDict['action'], eventDict['path']))
95
96
97 def GetLogMessage(self):
98 dlg = wx.TextEntryDialog(wx.GetApp().GetTopWindow(),
99 _("Comment"),
100 _("SVN Log Message"))
101
102 dlg.CenterOnParent()
103 if dlg.ShowModal() == wx.ID_OK:
104 retcode = True
105 message = dlg.GetValue()
106 else:
107 retcode = False
108 message = _("Cancel Action")
109
110 dlg.Destroy()
111
112 return retcode, message
113
114
115 def GetLogin(self, realm, username, maySave):
116 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Login"))
117
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)
125
126 savePasswordCheckbox = wx.CheckBox(dlg, -1, _("Remember Username and Password"))
127 if not maySave:
128 savePasswordCheckbox.Enable(False)
129
130 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
131 okBtn = wx.Button(dlg, wx.ID_OK)
132 okBtn.SetDefault()
133 buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
134 buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
135
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)
140
141 dlg.SetSizer(contentSizer)
142 dlg.Fit()
143 dlg.Layout()
144
145 dlg.CenterOnParent()
146 if dlg.ShowModal() == wx.ID_OK:
147 retcode = True
148 username = usernameTxt.GetValue().strip()
149 password = passwordTxt.GetValue()
150 save = savePasswordCheckbox.IsChecked()
151 else:
152 retcode = False
153 username = None
154 password = None
155 save = False
156
157 dlg.Destroy()
158 return retcode, username, password, save
159
160
161 def SSLServerPrompt(self):
162 """ Not implemented, as per pysvn documentation """
163 return
164
165
166 def GetSSLServerTrust(self, trustDict):
167 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SSL Server Certificate"))
168
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)
174
175 box = wx.StaticBoxSizer(wx.StaticBox(dlg, -1, _("Certificate Info")), wx.VERTICAL)
176 box.Add(sizer, 0, wx.EXPAND)
177
178 certRadio = wx.RadioBox(dlg, -1, _("Certificate"), choices=[_("Accept Always"), _("Accept Once"), _("Reject")], majorDimension=1, style=wx.RA_SPECIFY_COLS)
179
180 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
181 okBtn = wx.Button(dlg, wx.ID_OK)
182 okBtn.SetDefault()
183 buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
184 buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
185
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)
190
191 dlg.SetSizer(contentSizer)
192 dlg.Fit()
193 dlg.Layout()
194
195 # default values for reject
196 retcode = False
197 acceptedFailures = 0
198 save = False
199
200 dlg.CenterOnParent()
201 if dlg.ShowModal() == wx.ID_OK:
202 cert = certRadio.GetStringSelection()
203 if cert == _("Accept Always"):
204 retcode = True
205 acceptedFailures = trustDict.get('failures')
206 save = True
207 elif cert == _("Accept Once"):
208 retcode = True
209 acceptedFailures = trustDict.get('failures')
210 save = False
211
212 dlg.Destroy()
213 return retcode, acceptedFailures, save
214
215
216 def SSLClientPassword(self, realm, maySave):
217 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SSL Client Certificate Login"))
218
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)
225
226 savePasswordCheckbox = wx.CheckBox(dlg, -1, _("Remember Password"))
227 if not maySave:
228 savePasswordCheckbox.Enable(False)
229
230 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
231 okBtn = wx.Button(dlg, wx.ID_OK)
232 okBtn.SetDefault()
233 buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
234 buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
235
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)
240
241 dlg.SetSizer(contentSizer)
242 dlg.Fit()
243 dlg.Layout()
244
245 dlg.CenterOnParent()
246 if dlg.ShowModal() == wx.ID_OK:
247 retcode = True
248 password = passwordTxt.GetValue()
249 save = savePasswordCheckbox.IsChecked()
250 else:
251 retcode = False
252 password = None
253 save = False
254
255 dlg.Destroy()
256 return retcode, password, save
257
258
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
263 )
264
265 # dlg.CenterOnParent() # wxBug: caused crash with wx.FileDialog
266 if dlg.ShowModal() == wx.ID_OK:
267 retcode = True
268 certfile = dlg.GetPath()
269 else:
270 retcode = False
271 certfile = None
272
273 dlg.Destroy()
274 return retcode, certfile
275
276
277
278 #----------------------------------------------------------------------------
279 # Service Methods
280 #----------------------------------------------------------------------------
281
282 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
283 menu = menuBar.GetMenu(menuBar.FindMenu(_("Project")))
284
285 menu.AppendSeparator()
286
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"))
305
306 menu.AppendSeparator()
307
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"))
314
315
316 def ProcessEvent(self, event):
317
318 id = event.GetId()
319
320 if not SVN_INSTALLED:
321 if id in SVNService.SVN_COMMAND_LIST:
322 wx.MessageBox(_("pysvn not found. Please install pysvn"), _("Python Subversion"))
323 return True
324 return False
325
326
327 try:
328 if id == SVNService.SVN_UPDATE_ID:
329 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
330
331 filenames = self.GetCurrentDocuments()
332 if filenames:
333 filenames = filenames[:]
334 filenames.sort(self.BasenameCaseInsensitiveCompare)
335 else:
336 folderPath = self.GetCurrentFolder()
337 if folderPath:
338 filenames = [folderPath]
339
340 messageService = wx.GetApp().GetService(MessageService.MessageService)
341 messageService.ShowWindow()
342
343 view = messageService.GetView()
344 view.ClearLines()
345 view.AddLines(_("SVN Update:\n"))
346
347 for filename in filenames:
348 view.AddLines("%s\n" % filename)
349 try:
350 status = self._client.update(filename)
351
352 if status.number > 0:
353 view.AddLines(_("Updated to revision %s\n") % status.number)
354
355 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
356 for doc in openDocs:
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),
360 _("Close File"),
361 wx.YES_NO|wx.ICON_QUESTION)
362 yesNoMsg.CenterOnParent()
363 status = yesNoMsg.ShowModal()
364 yesNoMsg.Destroy()
365 if status == wx.ID_YES:
366 doc.DeleteAllViews()
367 break
368 else:
369 view.AddLines(_("Update failed.\n"))
370
371 except pysvn.ClientError, e:
372 view.AddLines("%s\n" % str(e))
373 wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
374 except:
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):
378 view.AddLines(line)
379
380 wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
381
382 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
383
384 return True
385
386 elif id == SVNService.SVN_UPDATE_ALL_ID:
387 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
388
389 messageService = wx.GetApp().GetService(MessageService.MessageService)
390 messageService.ShowWindow()
391
392 view = messageService.GetView()
393 view.ClearLines()
394 view.AddLines(_("SVN Update:\n"))
395
396 project = self.GetCurrentProject()
397 if project:
398 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
399 for doc in openDocs:
400 if doc.GetFilename() == project:
401 filenames = doc.GetFiles()[:] # make a copy and sort it.
402 filenames.sort(self.BasenameCaseInsensitiveCompare)
403
404 for filename in filenames:
405 view.AddLines("%s\n" % filename)
406 try:
407 status = self._client.update(filename)
408
409 if status.number > 0:
410 view.AddLines(_("Updated to revision %s\n") % status.number)
411
412 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
413 for doc in openDocs:
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),
417 _("Close File"),
418 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
419 yesNoMsg.CenterOnParent()
420 status = yesNoMsg.ShowModal()
421 yesNoMsg.Destroy()
422 if status == wx.ID_YES:
423 doc.DeleteAllViews()
424 elif status == wx.ID_NO:
425 pass
426 else: # elif status == wx.CANCEL:
427 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
428 return True
429 break
430 else:
431 view.AddLines(_("Update failed.\n"))
432
433 except pysvn.ClientError, e:
434 view.AddLines("%s\n" % str(e))
435 wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
436 except:
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):
440 view.AddLines(line)
441
442 wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
443
444 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
445 return True
446
447 elif id == SVNService.SVN_CHECKIN_ALL_ID:
448 filenames = []
449 project = self.GetCurrentProject()
450 if project:
451 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
452 for doc in openDocs:
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)
458
459 # ask user if dirty files should be saved first
460 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
461 for filename in filenames:
462 for doc in openDocs:
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),
466 _("SVN Commit"),
467 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
468 yesNoMsg.CenterOnParent()
469 status = yesNoMsg.ShowModal()
470 yesNoMsg.Destroy()
471 if status == wx.ID_YES:
472 doc.Save()
473 elif status == wx.ID_NO:
474 pass
475 else: # elif status == wx.CANCEL:
476 return True
477 break
478
479 shortFilenames = []
480 for i, filename in enumerate(filenames):
481 shortFilename = os.path.basename(filename)
482 shortFilenames.append(shortFilename)
483
484 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
485
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)
490
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)
496
497 buttonSizer = wx.StdDialogButtonSizer()
498 okBtn = wx.Button(dlg, wx.ID_OK)
499 okBtn.SetDefault()
500 buttonSizer.AddButton(okBtn)
501 buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
502 buttonSizer.Realize()
503
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)
507
508 dlg.SetSizer(contentSizer)
509 dlg.Fit()
510 dlg.Layout()
511
512 dlg.CenterOnParent()
513 if dlg.ShowModal() == wx.ID_OK:
514 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
515
516 messageService = wx.GetApp().GetService(MessageService.MessageService)
517 messageService.ShowWindow()
518
519 view = messageService.GetView()
520 view.ClearLines()
521 view.AddLines(_("SVN Commit:\n"))
522
523 try:
524 selFilenames = []
525 for i in range(fileList.GetCount()):
526 if fileList.IsChecked(i):
527 selFilenames.append(filenames[i])
528 view.AddLines("%s\n" % filenames[i])
529
530 if len(selFilenames):
531 comment = commentText.GetValue()
532 status = self._client.checkin(selFilenames, comment)
533
534 if status is None:
535 view.AddLines(_("Nothing to commit.\n"))
536 elif status.number > 0:
537 view.AddLines(_("Committed as revision %s.\n") % status.number)
538 else:
539 view.AddLines(_("Commit failed.\n"))
540
541 except pysvn.ClientError, e:
542 view.AddLines("%s\n" % str(e))
543 wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
544 except:
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):
548 view.AddLines(line)
549 wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
550
551 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
552 dlg.Destroy()
553 return True
554
555
556 elif id == SVNService.SVN_CHECKIN_ID:
557 filenames = self.GetCurrentDocuments()[:]
558 filenames.sort(self.BasenameCaseInsensitiveCompare)
559
560 # ask user if dirty files should be saved first
561 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
562 for filename in filenames:
563 for doc in openDocs:
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),
567 _("SVN Commit"),
568 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
569 yesNoMsg.CenterOnParent()
570 status = yesNoMsg.ShowModal()
571 yesNoMsg.Destroy()
572 if status == wx.ID_YES:
573 doc.Save()
574 elif status == wx.ID_NO:
575 pass
576 else: # elif status == wx.CANCEL:
577 return True
578 break
579
580 shortFilenames = []
581 for i, filename in enumerate(filenames):
582 shortFilename = os.path.basename(filename)
583 shortFilenames.append(shortFilename)
584
585 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
586
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)
591
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)
597
598 buttonSizer = wx.StdDialogButtonSizer()
599 okBtn = wx.Button(dlg, wx.ID_OK)
600 okBtn.SetDefault()
601 buttonSizer.AddButton(okBtn)
602 buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
603 buttonSizer.Realize()
604
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)
608
609 dlg.SetSizer(contentSizer)
610 dlg.Fit()
611 dlg.Layout()
612
613 dlg.CenterOnParent()
614 if dlg.ShowModal() == wx.ID_OK:
615 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
616
617 messageService = wx.GetApp().GetService(MessageService.MessageService)
618 messageService.ShowWindow()
619
620 view = messageService.GetView()
621 view.ClearLines()
622 view.AddLines(_("SVN Commit:\n"))
623
624 try:
625 selFilenames = []
626 for i in range(fileList.GetCount()):
627 if fileList.IsChecked(i):
628 selFilenames.append(filenames[i])
629 view.AddLines("%s\n" % filenames[i])
630
631 if len(selFilenames):
632 comment = commentText.GetValue()
633 status = self._client.checkin(selFilenames, comment)
634
635 if status is None:
636 view.AddLines(_("Nothing to commit.\n"))
637 elif status.number > 0:
638 view.AddLines(_("Committed as revision %s.\n") % status.number)
639 else:
640 view.AddLines(_("Commit failed.\n"))
641
642 except pysvn.ClientError, e:
643 view.AddLines("%s\n" % str(e))
644 wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
645 except:
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):
649 view.AddLines(line)
650 wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
651
652 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
653 dlg.Destroy()
654 return True
655
656 elif id == SVNService.SVN_CHECKOUT_ID:
657 config = wx.ConfigBase_Get()
658 svnUrl = config.Read(SVN_REPOSITORY_URL, self._defaultURL)
659
660 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Checkout"))
661
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)
666 if len(svnUrlList):
667 svnURLCombobox.SetToolTipString(svnUrlList[0])
668 svnURLCombobox.SetStringSelection(svnUrlList[0])
669 else:
670 svnURLCombobox.SetToolTipString(_("Set Repository URL"))
671 gridSizer.Add(svnURLCombobox, 0)
672
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..."))
677
678 def OnBrowseButton(event):
679 dirDlg = wx.DirDialog(wx.GetApp().GetTopWindow(), _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
680 dir = localPath.GetValue()
681 if len(dir):
682 dirDlg.SetPath(dir)
683 dirDlg.CenterOnParent()
684 if dirDlg.ShowModal() == wx.ID_OK:
685 localPath.SetValue(dirDlg.GetPath())
686 localPath.SetToolTipString(localPath.GetValue())
687 localPath.SetInsertionPointEnd()
688 dirDlg.Destroy()
689 wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
690
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)
695
696 buttonSizer = wx.StdDialogButtonSizer()
697 okBtn = wx.Button(dlg, wx.ID_OK)
698 okBtn.SetDefault()
699 buttonSizer.AddButton(okBtn)
700 buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
701 buttonSizer.Realize()
702
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)
706
707 dlg.SetSizer(contentSizer)
708 dlg.Fit()
709 dlg.Layout()
710
711 dlg.CenterOnParent()
712 if dlg.ShowModal() == wx.ID_OK:
713 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
714
715 WriteSvnUrlList(svnURLCombobox)
716
717 messageService = wx.GetApp().GetService(MessageService.MessageService)
718 messageService.ShowWindow()
719
720 view = messageService.GetView()
721 view.ClearLines()
722 view.AddLines(_("SVN Checkout:\n"))
723
724 svnUrl = svnURLCombobox.GetValue()
725 toLocation = localPath.GetValue()
726 try:
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)
732 except:
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):
736 view.AddLines(line)
737 wx.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
738
739 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
740 dlg.Destroy()
741 return True
742
743 elif id == SVNService.SVN_REVERT_ID:
744 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
745
746 filenames = self.GetCurrentDocuments()
747
748 messageService = wx.GetApp().GetService(MessageService.MessageService)
749 messageService.ShowWindow()
750
751 view = messageService.GetView()
752 view.ClearLines()
753 view.AddLines(_("SVN Revert:\n"))
754 for filename in filenames:
755 view.AddLines("%s\n" % filename)
756
757 try:
758 self._client.revert(filenames)
759 view.AddLines(_("Revert completed.\n"))
760
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()),
766 _("Close File"),
767 wx.YES_NO|wx.ICON_QUESTION)
768 yesNoMsg.CenterOnParent()
769 status = yesNoMsg.ShowModal()
770 yesNoMsg.Destroy()
771 if status == wx.ID_YES:
772 doc.DeleteAllViews()
773
774 except pysvn.ClientError, e:
775 view.AddLines("%s\n" % str(e))
776 wx.MessageBox(str(e), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
777 except:
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):
781 view.AddLines(line)
782 wx.MessageBox(_("Revert failed."), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
783
784 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
785 return True
786
787 elif id == SVNService.SVN_ADD_ID:
788 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
789
790 filenames = self.GetCurrentDocuments()
791
792 messageService = wx.GetApp().GetService(MessageService.MessageService)
793 messageService.ShowWindow()
794
795 view = messageService.GetView()
796 view.ClearLines()
797 view.AddLines(_("SVN Add:\n"))
798 for filename in filenames:
799 view.AddLines("%s\n" % filename)
800
801 try:
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)
807 except:
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):
811 view.AddLines(line)
812 wx.MessageBox(_("Add failed."), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
813
814 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
815 return True
816
817 elif id == SVNService.SVN_DELETE_ID:
818 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
819
820 filenames = self.GetCurrentDocuments()
821
822 messageService = wx.GetApp().GetService(MessageService.MessageService)
823 messageService.ShowWindow()
824
825 view = messageService.GetView()
826 view.ClearLines()
827 view.AddLines(_("SVN Delete:\n"))
828 for filename in filenames:
829 view.AddLines("%s\n" % filename)
830
831 try:
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)
837 except:
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):
841 view.AddLines(line)
842 wx.MessageBox(_("Delete failed."), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
843
844 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
845 return True
846
847 return False
848 finally:
849 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
850
851
852 def ProcessUpdateUIEvent(self, event):
853 id = event.GetId()
854
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():
860 event.Enable(True)
861 else:
862 event.Enable(False)
863 return True
864
865 elif id == SVNService.SVN_UPDATE_ID:
866 if self.GetCurrentDocuments() or self.GetCurrentFolder():
867 event.Enable(True)
868 else:
869 event.Enable(False)
870 return True
871
872 elif id == SVNService.SVN_CHECKOUT_ID:
873 event.Enable(True)
874 return True
875
876 elif (id == SVNService.SVN_UPDATE_ALL_ID
877 or id == SVNService.SVN_CHECKIN_ALL_ID):
878 if self.GetCurrentProject():
879 event.Enable(True)
880 else:
881 event.Enable(False)
882 return True
883
884 return False
885
886
887 def GetCurrentProject(self):
888 projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
889 if projectService:
890 projView = projectService.GetView()
891 return projView.GetSelectedProject()
892 return None
893
894
895 def GetCurrentDocuments(self):
896 projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
897 if projectService:
898 projView = projectService.GetView()
899
900 if projView.FilesHasFocus():
901 filenames = projView.GetSelectedFiles()
902 if len(filenames):
903 return filenames
904 else:
905 return None
906
907 doc = wx.GetApp().GetTopWindow().GetDocumentManager().GetCurrentDocument()
908 if doc:
909 filenames = [doc.GetFilename()]
910 else:
911 filenames = None
912
913 return filenames
914
915
916 def GetCurrentFolder(self):
917 projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
918 if projectService:
919 projView = projectService.GetView()
920
921 if projView.FilesHasFocus():
922 folderPath = projView.GetSelectedPhysicalFolder()
923 if folderPath:
924 return folderPath
925
926 return None
927
928
929 def BasenameCaseInsensitiveCompare(self, s1, s2):
930 s1L = os.path.basename(s1).lower()
931 s2L = os.path.basename(s2).lower()
932 if s1L == s2L:
933 return 0
934 elif s1L < s2L:
935 return -1
936 else:
937 return 1
938
939
940 class SVNOptionsPanel(wx.Panel):
941
942
943 def __init__(self, parent, id):
944 wx.Panel.__init__(self, parent, id)
945
946 config = wx.ConfigBase_Get()
947 configDir = config.Read(SVN_CONFIG_DIR, "")
948
949 borderSizer = wx.BoxSizer(wx.VERTICAL)
950 sizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
951 sizer.AddGrowableCol(1, 1)
952
953 sizer.Add(wx.StaticText(self, -1, _("SVN Config Dir:")), 0, wx.ALIGN_CENTER_VERTICAL)
954
955 self._svnConfigDir = wx.TextCtrl(self, -1, configDir, size = (200, -1))
956 if configDir == "":
957 self._svnConfigDir.SetToolTipString(_("Path Subversion configuration directory."))
958 else:
959 self._svnConfigDir.SetToolTipString(configDir)
960
961 findDirButton = wx.Button(self, -1, _("Browse..."))
962
963 def OnBrowseButton(event):
964 dirDlg = wx.DirDialog(self, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
965 dir = self._svnConfigDir.GetValue()
966 if len(dir):
967 dirDlg.SetPath(dir)
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()
973 dirDlg.Destroy()
974 wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
975
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)
980
981
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)
985 if len(svnUrlList):
986 self._svnURLCombobox.SetToolTipString(svnUrlList[0])
987 self._svnURLCombobox.SetStringSelection(svnUrlList[0])
988 else:
989 self._svnURLCombobox.SetToolTipString(_("Set Repository URL"))
990 sizer.Add(self._svnURLCombobox, 0, wx.EXPAND)
991
992
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))
997 else:
998 self._svnSSH = wx.TextCtrl(self, -1, svnSSH, size = (200, -1))
999 self._svnSSH.SetToolTipString(_("Override SVN_SSH environment variable temporarily."))
1000
1001 findSSHButton = wx.Button(self, -1, _("Browse..."))
1002
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()
1010 dirDlg.Destroy()
1011 wx.EVT_BUTTON(findSSHButton, -1, OnBrowseFileButton)
1012
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)
1017
1018
1019 borderSizer.Add(sizer, 0, wx.ALL|wx.EXPAND, SPACE)
1020 self.SetSizer(borderSizer)
1021 self.Layout()
1022 parent.AddPage(self, _("SVN"))
1023
1024
1025 def GetIcon(self):
1026 return wx.NullIcon
1027
1028
1029 def OnOK(self, optionsDialog):
1030 config = wx.ConfigBase_Get()
1031
1032 config.Write(SVN_CONFIG_DIR, self._svnConfigDir.GetValue())
1033
1034 WriteSvnUrlList(self._svnURLCombobox)
1035
1036 os.environ["SVN_SSH"] = self._svnSSH.GetValue()
1037
1038
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)
1045 else:
1046 urlList = []
1047 if len(urlList) == 0:
1048 svnService = wx.GetApp().GetService(SVNService)
1049 if svnService and hasattr(svnService, "_defaultURL"):
1050 urlList.append(svnService._defaultURL)
1051 return urlList
1052
1053
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 """
1056 urlList = []
1057
1058 url = comboBox.GetValue()
1059 if len(url):
1060 urlList.append(url)
1061
1062 for i in range(min(comboBox.GetCount(), 10)):
1063 url = comboBox.GetString(i)
1064 if url not in urlList:
1065 urlList.append(url)
1066
1067 config = wx.ConfigBase_Get()
1068 config.Write(SVN_REPOSITORY_URL, urlList.__repr__())
1069
1070