]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/SVNService.py
Tweaks for demos on MacOSX
[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 if id == SVNService.SVN_UPDATE_ID:
328 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
329
330 filenames = self.GetCurrentDocuments()
331 if filenames:
332 filenames = filenames[:]
333 filenames.sort(self.BasenameCaseInsensitiveCompare)
334 else:
335 folderPath = self.GetCurrentFolder()
336 if folderPath:
337 filenames = [folderPath]
338
339 messageService = wx.GetApp().GetService(MessageService.MessageService)
340 messageService.ShowWindow()
341
342 view = messageService.GetView()
343 view.ClearLines()
344 view.AddLines(_("SVN Update:\n"))
345
346 for filename in filenames:
347 view.AddLines("%s\n" % filename)
348 try:
349 status = self._client.update(filename)
350
351 if status.number > 0:
352 view.AddLines(_("Updated to revision %s\n") % status.number)
353
354 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
355 for doc in openDocs:
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),
359 _("Close File"),
360 wx.YES_NO|wx.ICON_QUESTION)
361 yesNoMsg.CenterOnParent()
362 status = yesNoMsg.ShowModal()
363 yesNoMsg.Destroy()
364 if status == wx.ID_YES:
365 doc.DeleteAllViews()
366 break
367 else:
368 view.AddLines(_("Update failed.\n"))
369
370 except pysvn.ClientError, e:
371 view.AddLines("%s\n" % str(e))
372 wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
373 except:
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):
377 view.AddLines(line)
378
379 wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
380
381 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
382
383 return True
384
385 elif id == SVNService.SVN_UPDATE_ALL_ID:
386 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
387
388 messageService = wx.GetApp().GetService(MessageService.MessageService)
389 messageService.ShowWindow()
390
391 view = messageService.GetView()
392 view.ClearLines()
393 view.AddLines(_("SVN Update:\n"))
394
395 project = self.GetCurrentProject()
396 if project:
397 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
398 for doc in openDocs:
399 if doc.GetFilename() == project:
400 filenames = doc.GetFiles()[:] # make a copy and sort it.
401 filenames.sort(self.BasenameCaseInsensitiveCompare)
402
403 for filename in filenames:
404 view.AddLines("%s\n" % filename)
405 try:
406 status = self._client.update(filename)
407
408 if status.number > 0:
409 view.AddLines(_("Updated to revision %s\n") % status.number)
410
411 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
412 for doc in openDocs:
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),
416 _("Close File"),
417 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
418 yesNoMsg.CenterOnParent()
419 status = yesNoMsg.ShowModal()
420 yesNoMsg.Destroy()
421 if status == wx.ID_YES:
422 doc.DeleteAllViews()
423 elif status == wx.ID_NO:
424 pass
425 else: # elif status == wx.CANCEL:
426 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
427 return True
428 break
429 else:
430 view.AddLines(_("Update failed.\n"))
431
432 except pysvn.ClientError, e:
433 view.AddLines("%s\n" % str(e))
434 wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
435 except:
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):
439 view.AddLines(line)
440
441 wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
442
443 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
444 return True
445
446 elif id == SVNService.SVN_CHECKIN_ALL_ID:
447 filenames = []
448 project = self.GetCurrentProject()
449 if project:
450 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
451 for doc in openDocs:
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)
457
458 # ask user if dirty files should be saved first
459 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
460 for filename in filenames:
461 for doc in openDocs:
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),
465 _("SVN Commit"),
466 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
467 yesNoMsg.CenterOnParent()
468 status = yesNoMsg.ShowModal()
469 yesNoMsg.Destroy()
470 if status == wx.ID_YES:
471 doc.Save()
472 elif status == wx.ID_NO:
473 pass
474 else: # elif status == wx.CANCEL:
475 return True
476 break
477
478 shortFilenames = []
479 for i, filename in enumerate(filenames):
480 shortFilename = os.path.basename(filename)
481 shortFilenames.append(shortFilename)
482
483 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
484
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)
489
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)
495
496 buttonSizer = wx.StdDialogButtonSizer()
497 okBtn = wx.Button(dlg, wx.ID_OK)
498 okBtn.SetDefault()
499 buttonSizer.AddButton(okBtn)
500 buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
501 buttonSizer.Realize()
502
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)
506
507 dlg.SetSizer(contentSizer)
508 dlg.Fit()
509 dlg.Layout()
510
511 dlg.CenterOnParent()
512 if dlg.ShowModal() == wx.ID_OK:
513 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
514
515 messageService = wx.GetApp().GetService(MessageService.MessageService)
516 messageService.ShowWindow()
517
518 view = messageService.GetView()
519 view.ClearLines()
520 view.AddLines(_("SVN Commit:\n"))
521
522 try:
523 selFilenames = []
524 for i in range(fileList.GetCount()):
525 if fileList.IsChecked(i):
526 selFilenames.append(filenames[i])
527 view.AddLines("%s\n" % filenames[i])
528
529 if len(selFilenames):
530 comment = commentText.GetValue()
531 status = self._client.checkin(selFilenames, comment)
532
533 if status is None:
534 view.AddLines(_("Nothing to commit.\n"))
535 elif status.number > 0:
536 view.AddLines(_("Committed as revision %s.\n") % status.number)
537 else:
538 view.AddLines(_("Commit failed.\n"))
539
540 except pysvn.ClientError, e:
541 view.AddLines("%s\n" % str(e))
542 wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
543 except:
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):
547 view.AddLines(line)
548 wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
549
550 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
551 dlg.Destroy()
552 return True
553
554
555 elif id == SVNService.SVN_CHECKIN_ID:
556 filenames = self.GetCurrentDocuments()[:]
557 filenames.sort(self.BasenameCaseInsensitiveCompare)
558
559 # ask user if dirty files should be saved first
560 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
561 for filename in filenames:
562 for doc in openDocs:
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),
566 _("SVN Commit"),
567 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
568 yesNoMsg.CenterOnParent()
569 status = yesNoMsg.ShowModal()
570 yesNoMsg.Destroy()
571 if status == wx.ID_YES:
572 doc.Save()
573 elif status == wx.ID_NO:
574 pass
575 else: # elif status == wx.CANCEL:
576 return True
577 break
578
579 shortFilenames = []
580 for i, filename in enumerate(filenames):
581 shortFilename = os.path.basename(filename)
582 shortFilenames.append(shortFilename)
583
584 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
585
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)
590
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)
596
597 buttonSizer = wx.StdDialogButtonSizer()
598 okBtn = wx.Button(dlg, wx.ID_OK)
599 okBtn.SetDefault()
600 buttonSizer.AddButton(okBtn)
601 buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
602 buttonSizer.Realize()
603
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)
607
608 dlg.SetSizer(contentSizer)
609 dlg.Fit()
610 dlg.Layout()
611
612 dlg.CenterOnParent()
613 if dlg.ShowModal() == wx.ID_OK:
614 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
615
616 messageService = wx.GetApp().GetService(MessageService.MessageService)
617 messageService.ShowWindow()
618
619 view = messageService.GetView()
620 view.ClearLines()
621 view.AddLines(_("SVN Commit:\n"))
622
623 try:
624 selFilenames = []
625 for i in range(fileList.GetCount()):
626 if fileList.IsChecked(i):
627 selFilenames.append(filenames[i])
628 view.AddLines("%s\n" % filenames[i])
629
630 if len(selFilenames):
631 comment = commentText.GetValue()
632 status = self._client.checkin(selFilenames, comment)
633
634 if status is None:
635 view.AddLines(_("Nothing to commit.\n"))
636 elif status.number > 0:
637 view.AddLines(_("Committed as revision %s.\n") % status.number)
638 else:
639 view.AddLines(_("Commit failed.\n"))
640
641 except pysvn.ClientError, e:
642 view.AddLines("%s\n" % str(e))
643 wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
644 except:
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):
648 view.AddLines(line)
649 wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
650
651 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
652 dlg.Destroy()
653 return True
654
655 elif id == SVNService.SVN_CHECKOUT_ID:
656 config = wx.ConfigBase_Get()
657 svnUrl = config.Read(SVN_REPOSITORY_URL, self._defaultURL)
658
659 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Checkout"))
660
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)
665 if len(svnUrlList):
666 svnURLCombobox.SetToolTipString(svnUrlList[0])
667 svnURLCombobox.SetStringSelection(svnUrlList[0])
668 else:
669 svnURLCombobox.SetToolTipString(_("Set Repository URL"))
670 gridSizer.Add(svnURLCombobox, 0)
671
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..."))
676
677 def OnBrowseButton(event):
678 dirDlg = wx.DirDialog(wx.GetApp().GetTopWindow(), _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
679 dir = localPath.GetValue()
680 if len(dir):
681 dirDlg.SetPath(dir)
682 dirDlg.CenterOnParent()
683 if dirDlg.ShowModal() == wx.ID_OK:
684 localPath.SetValue(dirDlg.GetPath())
685 localPath.SetToolTipString(localPath.GetValue())
686 localPath.SetInsertionPointEnd()
687 dirDlg.Destroy()
688 wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
689
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)
694
695 buttonSizer = wx.StdDialogButtonSizer()
696 okBtn = wx.Button(dlg, wx.ID_OK)
697 okBtn.SetDefault()
698 buttonSizer.AddButton(okBtn)
699 buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
700 buttonSizer.Realize()
701
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)
705
706 dlg.SetSizer(contentSizer)
707 dlg.Fit()
708 dlg.Layout()
709
710 dlg.CenterOnParent()
711 if dlg.ShowModal() == wx.ID_OK:
712 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
713
714 WriteSvnUrlList(svnURLCombobox)
715
716 messageService = wx.GetApp().GetService(MessageService.MessageService)
717 messageService.ShowWindow()
718
719 view = messageService.GetView()
720 view.ClearLines()
721 view.AddLines(_("SVN Checkout:\n"))
722
723 svnUrl = svnURLCombobox.GetValue()
724 toLocation = localPath.GetValue()
725 try:
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)
731 except:
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):
735 view.AddLines(line)
736 wx.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
737
738 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
739 dlg.Destroy()
740 return True
741
742 elif id == SVNService.SVN_REVERT_ID:
743 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
744
745 filenames = self.GetCurrentDocuments()
746
747 messageService = wx.GetApp().GetService(MessageService.MessageService)
748 messageService.ShowWindow()
749
750 view = messageService.GetView()
751 view.ClearLines()
752 view.AddLines(_("SVN Revert:\n"))
753 for filename in filenames:
754 view.AddLines("%s\n" % filename)
755
756 try:
757 self._client.revert(filenames)
758 view.AddLines(_("Revert completed.\n"))
759
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()),
765 _("Close File"),
766 wx.YES_NO|wx.ICON_QUESTION)
767 yesNoMsg.CenterOnParent()
768 status = yesNoMsg.ShowModal()
769 yesNoMsg.Destroy()
770 if status == wx.ID_YES:
771 doc.DeleteAllViews()
772
773 except pysvn.ClientError, e:
774 view.AddLines("%s\n" % str(e))
775 wx.MessageBox(str(e), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
776 except:
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):
780 view.AddLines(line)
781 wx.MessageBox(_("Revert failed."), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
782
783 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
784 return True
785
786 elif id == SVNService.SVN_ADD_ID:
787 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
788
789 filenames = self.GetCurrentDocuments()
790
791 messageService = wx.GetApp().GetService(MessageService.MessageService)
792 messageService.ShowWindow()
793
794 view = messageService.GetView()
795 view.ClearLines()
796 view.AddLines(_("SVN Add:\n"))
797 for filename in filenames:
798 view.AddLines("%s\n" % filename)
799
800 try:
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)
806 except:
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):
810 view.AddLines(line)
811 wx.MessageBox(_("Add failed."), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
812
813 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
814 return True
815
816 elif id == SVNService.SVN_DELETE_ID:
817 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
818
819 filenames = self.GetCurrentDocuments()
820
821 messageService = wx.GetApp().GetService(MessageService.MessageService)
822 messageService.ShowWindow()
823
824 view = messageService.GetView()
825 view.ClearLines()
826 view.AddLines(_("SVN Delete:\n"))
827 for filename in filenames:
828 view.AddLines("%s\n" % filename)
829
830 try:
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)
836 except:
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):
840 view.AddLines(line)
841 wx.MessageBox(_("Delete failed."), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
842
843 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
844 return True
845
846 return False
847
848
849 def ProcessUpdateUIEvent(self, event):
850 id = event.GetId()
851
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():
857 event.Enable(True)
858 else:
859 event.Enable(False)
860 return True
861
862 elif id == SVNService.SVN_UPDATE_ID:
863 if self.GetCurrentDocuments() or self.GetCurrentFolder():
864 event.Enable(True)
865 else:
866 event.Enable(False)
867 return True
868
869 elif id == SVNService.SVN_CHECKOUT_ID:
870 event.Enable(True)
871 return True
872
873 elif (id == SVNService.SVN_UPDATE_ALL_ID
874 or id == SVNService.SVN_CHECKIN_ALL_ID):
875 if self.GetCurrentProject():
876 event.Enable(True)
877 else:
878 event.Enable(False)
879 return True
880
881 return False
882
883
884 def GetCurrentProject(self):
885 projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
886 if projectService:
887 projView = projectService.GetView()
888 return projView.GetSelectedProject()
889 return None
890
891
892 def GetCurrentDocuments(self):
893 projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
894 if projectService:
895 projView = projectService.GetView()
896
897 if projView.FilesHasFocus():
898 filenames = projView.GetSelectedFiles()
899 if len(filenames):
900 return filenames
901 else:
902 return None
903
904 doc = wx.GetApp().GetTopWindow().GetDocumentManager().GetCurrentDocument()
905 if doc:
906 filenames = [doc.GetFilename()]
907 else:
908 filenames = None
909
910 return filenames
911
912
913 def GetCurrentFolder(self):
914 projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
915 if projectService:
916 projView = projectService.GetView()
917
918 if projView.FilesHasFocus():
919 folderPath = projView.GetSelectedPhysicalFolder()
920 if folderPath:
921 return folderPath
922
923 return None
924
925
926 def BasenameCaseInsensitiveCompare(self, s1, s2):
927 s1L = os.path.basename(s1).lower()
928 s2L = os.path.basename(s2).lower()
929 if s1L == s2L:
930 return 0
931 elif s1L < s2L:
932 return -1
933 else:
934 return 1
935
936
937 class SVNOptionsPanel(wx.Panel):
938
939
940 def __init__(self, parent, id):
941 wx.Panel.__init__(self, parent, id)
942
943 config = wx.ConfigBase_Get()
944 configDir = config.Read(SVN_CONFIG_DIR, "")
945
946 borderSizer = wx.BoxSizer(wx.VERTICAL)
947 sizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
948 sizer.AddGrowableCol(1, 1)
949
950 sizer.Add(wx.StaticText(self, -1, _("SVN Config Dir:")), 0, wx.ALIGN_CENTER_VERTICAL)
951
952 self._svnConfigDir = wx.TextCtrl(self, -1, configDir, size = (200, -1))
953 if configDir == "":
954 self._svnConfigDir.SetToolTipString(_("Path Subversion configuration directory."))
955 else:
956 self._svnConfigDir.SetToolTipString(configDir)
957
958 findDirButton = wx.Button(self, -1, _("Browse..."))
959
960 def OnBrowseButton(event):
961 dirDlg = wx.DirDialog(self, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
962 dir = self._svnConfigDir.GetValue()
963 if len(dir):
964 dirDlg.SetPath(dir)
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()
970 dirDlg.Destroy()
971 wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
972
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)
977
978
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)
982 if len(svnUrlList):
983 self._svnURLCombobox.SetToolTipString(svnUrlList[0])
984 self._svnURLCombobox.SetStringSelection(svnUrlList[0])
985 else:
986 self._svnURLCombobox.SetToolTipString(_("Set Repository URL"))
987 sizer.Add(self._svnURLCombobox, 0, wx.EXPAND)
988
989
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))
994 else:
995 self._svnSSH = wx.TextCtrl(self, -1, svnSSH, size = (200, -1))
996 self._svnSSH.SetToolTipString(_("Override SVN_SSH environment variable temporarily."))
997
998 findSSHButton = wx.Button(self, -1, _("Browse..."))
999
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()
1007 dirDlg.Destroy()
1008 wx.EVT_BUTTON(findSSHButton, -1, OnBrowseFileButton)
1009
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)
1014
1015
1016 borderSizer.Add(sizer, 0, wx.ALL|wx.EXPAND, SPACE)
1017 self.SetSizer(borderSizer)
1018 self.Layout()
1019 parent.AddPage(self, _("SVN"))
1020
1021
1022 def GetIcon(self):
1023 return wx.NullIcon
1024
1025
1026 def OnOK(self, optionsDialog):
1027 config = wx.ConfigBase_Get()
1028
1029 config.Write(SVN_CONFIG_DIR, self._svnConfigDir.GetValue())
1030
1031 WriteSvnUrlList(self._svnURLCombobox)
1032
1033 os.environ["SVN_SSH"] = self._svnSSH.GetValue()
1034
1035
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)
1042 else:
1043 urlList = []
1044 if len(urlList) == 0:
1045 svnService = wx.GetApp().GetService(SVNService)
1046 if svnService and hasattr(svnService, "_defaultURL"):
1047 urlList.append(svnService._defaultURL)
1048 return urlList
1049
1050
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 """
1053 urlList = []
1054
1055 url = comboBox.GetValue()
1056 if len(url):
1057 urlList.append(url)
1058
1059 for i in range(min(comboBox.GetCount(), 10)):
1060 url = comboBox.GetString(i)
1061 if url not in urlList:
1062 urlList.append(url)
1063
1064 config = wx.ConfigBase_Get()
1065 config.Write(SVN_REPOSITORY_URL, urlList.__repr__())
1066
1067