]> git.saurik.com Git - wxWidgets.git/blame - wxPython/samples/ide/activegrid/tool/SVNService.py
Docview and IDE patch from Morag Hua with fix for bug #1217890
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / SVNService.py
CommitLineData
6f1a3f9c
RD
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
13import wx
26ee3a06 14import os
6f1a3f9c
RD
15import os.path
16import ProjectEditor
17import MessageService
18
19import sys # for errors
20import traceback # for errors
21
22try:
23 import pysvn # python-subversion integration
24 SVN_INSTALLED = True
25except ImportError:
26 SVN_INSTALLED = False
27
28_ = wx.GetTranslation
29
30
31#----------------------------------------------------------------------------
32# Constants
33#----------------------------------------------------------------------------
34SVN_CONFIG_DIR = "SVNConfigDir"
26ee3a06 35SVN_REPOSITORY_URL = "SVNRepositoryURLs"
6f1a3f9c
RD
36
37SPACE = 10
38HALF_SPACE = 5
39
40
41#----------------------------------------------------------------------------
42# Classes
43#----------------------------------------------------------------------------
44
45class SVNService(wx.lib.pydocview.DocService):
26ee3a06 46 SVN_UPDATE_ALL_ID = wx.NewId()
6f1a3f9c
RD
47 SVN_UPDATE_ID = wx.NewId()
48 SVN_CHECKIN_ID = wx.NewId()
26ee3a06 49 SVN_CHECKIN_ALL_ID = wx.NewId()
6f1a3f9c
RD
50 SVN_CHECKOUT_ID = wx.NewId()
51 SVN_REVERT_ID = wx.NewId()
52 SVN_ADD_ID = wx.NewId()
53 SVN_DELETE_ID = wx.NewId()
26ee3a06 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]
6f1a3f9c
RD
55
56
57 def __init__(self):
58 self._defaultURL = "svn://"
59
60 global SVN_INSTALLED
61 if SVN_INSTALLED:
62 config = wx.ConfigBase_Get()
26ee3a06 63
6f1a3f9c 64 configDir = config.Read(SVN_CONFIG_DIR, "")
6f1a3f9c
RD
65 self._client = pysvn.Client(configDir)
66 try:
67 self._defaultURL = self._client.info('.').url
68 except:
69 pass
26ee3a06 70
6f1a3f9c
RD
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 if dlg.ShowModal() == wx.ID_OK:
103 retcode = True
104 message = dlg.GetValue()
105 else:
106 retcode = False
107 message = _("Cancel Action")
108
109 dlg.Destroy()
110
111 return retcode, message
112
113
114 def GetLogin(self, realm, username, maySave):
115 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Login"))
116
117 sizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
118 sizer.Add(wx.StaticText(dlg, -1, _("Username:")), 0, wx.ALIGN_CENTER_VERTICAL)
119 usernameTxt = wx.TextCtrl(dlg, -1, username, size = (200, -1))
120 sizer.Add(usernameTxt, 0, wx.ALIGN_CENTER_VERTICAL)
121 sizer.Add(wx.StaticText(dlg, -1, _("Password:")), 0, wx.ALIGN_CENTER_VERTICAL)
122 passwordTxt = wx.TextCtrl(dlg, -1, size=(200, -1), style=wx.TE_PASSWORD)
123 sizer.Add(passwordTxt, 0, wx.ALIGN_CENTER_VERTICAL)
124
125 savePasswordCheckbox = wx.CheckBox(dlg, -1, _("Remember Username and Password"))
126 if not maySave:
127 savePasswordCheckbox.Enable(False)
128
129 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
130 okBtn = wx.Button(dlg, wx.ID_OK)
131 okBtn.SetDefault()
132 buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
133 buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
134
135 contentSizer = wx.BoxSizer(wx.VERTICAL)
136 contentSizer.Add(sizer, 0, wx.LEFT|wx.TOP|wx.RIGHT, SPACE)
137 contentSizer.Add(savePasswordCheckbox, 0, wx.TOP|wx.LEFT|wx.BOTTOM, SPACE)
138 contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
139
140 dlg.SetSizer(contentSizer)
141 dlg.Fit()
142 dlg.Layout()
143
144 if dlg.ShowModal() == wx.ID_OK:
145 retcode = True
146 username = usernameTxt.GetValue().strip()
147 password = passwordTxt.GetValue()
148 save = savePasswordCheckBox.IsChecked()
149 else:
150 retcode = False
151 username = None
152 password = None
153 save = False
154
155 dlg.Destroy()
156 return retcode, username, password, save
157
158
159 def SSLServerPrompt(self):
160 """ Not implemented, as per pysvn documentation """
161 return
162
163
164 def GetSSLServerTrust(self, trustDict):
165 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SSL Server Certificate"))
166
167 sizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
168 for k in ['hostname', 'valid_from', 'valid_to', 'issuer_dname', 'realm']:
169 if trustDict.has_key(k):
170 sizer.Add(wx.StaticText(dlg, -1, "%s:" % k), 0, wx.ALIGN_CENTER_VERTICAL)
171 sizer.Add(wx.StaticText(dlg, -1, "%s" % trustDict[k]), 0, wx.ALIGN_CENTER_VERTICAL)
172
173 box = wx.StaticBoxSizer(wx.StaticBox(dlg, -1, _("Certificate Info")), wx.VERTICAL)
174 box.Add(sizer, 0, wx.EXPAND)
175
176 certRadio = wx.RadioBox(dlg, -1, _("Certificate"), choices=[_("Accept Always"), _("Accept Once"), _("Reject")], majorDimension=1, style=wx.RA_SPECIFY_COLS)
177
178 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
179 okBtn = wx.Button(dlg, wx.ID_OK)
180 okBtn.SetDefault()
181 buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
182 buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
183
184 contentSizer = wx.BoxSizer(wx.VERTICAL)
185 contentSizer.Add(box, 0, wx.TOP|wx.LEFT|wx.RIGHT|wx.EXPAND, SPACE)
186 contentSizer.Add(certRadio, 0, wx.TOP|wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.EXPAND, SPACE)
187 contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
188
189 dlg.SetSizer(contentSizer)
190 dlg.Fit()
191 dlg.Layout()
192
193 # default values for reject
194 retcode = False
195 acceptedFailures = 0
196 save = False
197
198 if dlg.ShowModal() == wx.ID_OK:
199 cert = certRadio.GetStringSelection()
200 if cert == _("Accept Always"):
201 retcode = True
202 acceptedFailures = trustDict.get('failures')
203 save = True
204 elif cert == _("Accept Once"):
205 retcode = True
206 acceptedFailures = trustDict.get('failures')
207 save = False
208
209 return retcode, acceptedFailures, save
210
211
212 def SSLClientPassword(self, realm, maySave):
213 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SSL Client Certificate Login"))
214
215 sizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
216 sizer.Add(wx.StaticText(dlg, -1, _("Realm:")), 0, wx.ALIGN_CENTER_VERTICAL)
217 sizer.Add(wx.StaticText(dlg, -1, realm), 0, wx.ALIGN_CENTER_VERTICAL)
218 sizer.Add(wx.StaticText(dlg, -1, _("Password:")), 0, wx.ALIGN_CENTER_VERTICAL)
219 passwordTxt = wx.TextCtrl(dlg, -1, size=(200, -1), style=wx.TE_PASSWORD)
220 sizer.Add(passwordTxt, 0, wx.ALIGN_CENTER_VERTICAL)
221
222 savePasswordCheckbox = wx.CheckBox(dlg, -1, _("Remember Password"))
223 if not maySave:
224 savePasswordCheckbox.Enable(False)
225
226 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
227 okBtn = wx.Button(dlg, wx.ID_OK)
228 okBtn.SetDefault()
229 buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
230 buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
231
232 contentSizer = wx.BoxSizer(wx.VERTICAL)
233 contentSizer.Add(sizer, 0, wx.LEFT|wx.TOP|wx.RIGHT, SPACE)
234 contentSizer.Add(savePasswordCheckbox, 0, wx.TOP|wx.LEFT|wx.BOTTOM, SPACE)
235 contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
236
237 dlg.SetSizer(contentSizer)
238 dlg.Fit()
239 dlg.Layout()
240
241 if dlg.ShowModal() == wx.ID_OK:
242 retcode = True
243 password = passwordTxt.GetValue()
244 save = savePasswordCheckBox.IsChecked()
245 else:
246 retcode = False
247 password = None
248 save = False
249
250 dlg.Destroy()
251 return retcode, password, save
252
253
254 def SSLClientCert(self):
255 dlg = wx.FileDialog(wx.GetApp().GetTopWindow(),
256 message="Choose certificate", defaultDir=os.getcwd(),
257 style=wx.OPEN|wx.CHANGE_DIR
258 )
259
260 if dlg.ShowModal() == wx.ID_OK:
261 retcode = True
262 certfile = dlg.GetPath()
263 else:
264 retcode = False
265 certfile = None
266
267 dlg.Destroy()
268 return retcode, certfile
269
270
271
272 #----------------------------------------------------------------------------
273 # Service Methods
274 #----------------------------------------------------------------------------
275
276 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
277 menu = menuBar.GetMenu(menuBar.FindMenu(_("Project")))
278
279 menu.AppendSeparator()
280
26ee3a06
RD
281 wx.EVT_MENU(frame, SVNService.SVN_UPDATE_ALL_ID, self.ProcessEvent)
282 wx.EVT_UPDATE_UI(frame, SVNService.SVN_UPDATE_ALL_ID, self.ProcessUpdateUIEvent)
283 menu.Append(SVNService.SVN_UPDATE_ALL_ID, _("SVN Update All in Project"), _("Update all files in a project from Subversion"))
284 wx.EVT_MENU(frame, SVNService.SVN_CHECKIN_ALL_ID, self.ProcessEvent)
285 wx.EVT_UPDATE_UI(frame, SVNService.SVN_CHECKIN_ALL_ID, self.ProcessUpdateUIEvent)
286 menu.Append(SVNService.SVN_CHECKIN_ALL_ID, _("SVN Commit All in Project..."), _("Commit all files changes in a project to Subversion"))
6f1a3f9c
RD
287 wx.EVT_MENU(frame, SVNService.SVN_UPDATE_ID, self.ProcessEvent)
288 wx.EVT_UPDATE_UI(frame, SVNService.SVN_UPDATE_ID, self.ProcessUpdateUIEvent)
289 menu.Append(SVNService.SVN_UPDATE_ID, _("SVN Update"), _("Update file from Subversion"))
290 wx.EVT_MENU(frame, SVNService.SVN_CHECKIN_ID, self.ProcessEvent)
291 wx.EVT_UPDATE_UI(frame, SVNService.SVN_CHECKIN_ID, self.ProcessUpdateUIEvent)
292 menu.Append(SVNService.SVN_CHECKIN_ID, _("SVN Commit..."), _("Commit file changes to Subversion"))
293 wx.EVT_MENU(frame, SVNService.SVN_CHECKOUT_ID, self.ProcessEvent)
294 wx.EVT_UPDATE_UI(frame, SVNService.SVN_CHECKOUT_ID, self.ProcessUpdateUIEvent)
295 menu.Append(SVNService.SVN_CHECKOUT_ID, _("SVN Checkout..."), _("Checkout file from Subversion"))
296 wx.EVT_MENU(frame, SVNService.SVN_REVERT_ID, self.ProcessEvent)
297 wx.EVT_UPDATE_UI(frame, SVNService.SVN_REVERT_ID, self.ProcessUpdateUIEvent)
298 menu.Append(SVNService.SVN_REVERT_ID, _("SVN Revert"), _("Revert file from Subversion"))
299
300 menu.AppendSeparator()
301
302 wx.EVT_MENU(frame, SVNService.SVN_ADD_ID, self.ProcessEvent)
303 wx.EVT_UPDATE_UI(frame, SVNService.SVN_ADD_ID, self.ProcessUpdateUIEvent)
304 menu.Append(SVNService.SVN_ADD_ID, _("SVN Add"), _("Add file to Subversion"))
305 wx.EVT_MENU(frame, SVNService.SVN_DELETE_ID, self.ProcessEvent)
306 wx.EVT_UPDATE_UI(frame, SVNService.SVN_DELETE_ID, self.ProcessUpdateUIEvent)
307 menu.Append(SVNService.SVN_DELETE_ID, _("SVN Delete"), _("Delete file from Subversion"))
308
309
310 def ProcessEvent(self, event):
311
312 id = event.GetId()
313
314 if not SVN_INSTALLED:
315 if id in SVNService.SVN_COMMAND_LIST:
316 wx.MessageBox(_("pysvn not found. Please install pysvn"), _("Python Subversion"))
317 return True
318 return False
319
320
321 if id == SVNService.SVN_UPDATE_ID:
6f1a3f9c 322 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
26ee3a06
RD
323
324 filenames = self.GetCurrentDocuments()[:]
325 filenames.sort(self.BasenameCaseInsensitiveCompare)
6f1a3f9c
RD
326
327 messageService = wx.GetApp().GetService(MessageService.MessageService)
328 messageService.ShowWindow()
329
330 view = messageService.GetView()
331 view.ClearLines()
332 view.AddLines(_("SVN Update:\n"))
333
334 for filename in filenames:
335 view.AddLines("%s\n" % filename)
336 try:
337 status = self._client.update(filename)
338
339 if status.number > 0:
340 view.AddLines(_("Updated to revision %s\n") % status.number)
341
342 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
343 for doc in openDocs:
344 if doc.GetFilename() == filename:
345 yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
346 _("Updated file '%s' is currently open. Close it?") % os.path.basename(filename),
347 _("Close File"),
348 wx.YES_NO|wx.ICON_QUESTION)
349 if yesNoMsg.ShowModal() == wx.ID_YES:
350 doc.DeleteAllViews()
351 break
352 else:
353 view.AddLines(_("Update failed.\n"))
354
355 except pysvn.ClientError, e:
356 view.AddLines("%s\n" % str(e))
357 wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
358 except:
359 extype, ex, tb = sys.exc_info()
360 view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
361 for line in traceback.format_tb(tb):
362 view.AddLines(line)
363
364 wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
365
366 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
367
368 return True
26ee3a06
RD
369
370 elif id == SVNService.SVN_UPDATE_ALL_ID:
371 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
372
373 messageService = wx.GetApp().GetService(MessageService.MessageService)
374 messageService.ShowWindow()
375
376 view = messageService.GetView()
377 view.ClearLines()
378 view.AddLines(_("SVN Update:\n"))
379
380 projects = self.GetCurrentProjects()
381 for project in projects:
382 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
383 for doc in openDocs:
384 if doc.GetFilename() == project:
385 filenames = doc.GetFiles()[:] # make a copy and sort it.
386 filenames.sort(self.BasenameCaseInsensitiveCompare)
387
388 for filename in filenames:
389 view.AddLines("%s\n" % filename)
390 try:
391 status = self._client.update(filename)
392
393 if status.number > 0:
394 view.AddLines(_("Updated to revision %s\n") % status.number)
395
396 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
397 for doc in openDocs:
398 if doc.GetFilename() == filename:
399 yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
400 _("Updated file '%s' is currently open. Close it?") % os.path.basename(filename),
401 _("Close File"),
402 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
403 status = yesNoMsg.ShowModal()
404 if status == wx.ID_YES:
405 doc.DeleteAllViews()
406 elif status == wx.ID_NO:
407 pass
408 else: # elif status == wx.CANCEL:
409 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
410 return True
411 break
412 else:
413 view.AddLines(_("Update failed.\n"))
414
415 except pysvn.ClientError, e:
416 view.AddLines("%s\n" % str(e))
417 wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
418 except:
419 extype, ex, tb = sys.exc_info()
420 view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
421 for line in traceback.format_tb(tb):
422 view.AddLines(line)
423
424 wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
425
426 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
427 return True
428
429 elif id == SVNService.SVN_CHECKIN_ALL_ID:
430 filenames = []
431 projects = self.GetCurrentProjects()
432 for project in projects:
433 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
434 for doc in openDocs:
435 if doc.GetFilename() == project:
436 for filename in doc.GetFiles():
437 if filename not in filenames:
438 filenames.append(filename)
439 filenames.sort(self.BasenameCaseInsensitiveCompare)
440
441 # ask user if dirty files should be saved first
442 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
443 for filename in filenames:
444 for doc in openDocs:
445 if doc.GetFilename() == filename and doc.IsModified():
446 yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
447 _("'%s' has unsaved modifications. Save it before commit?") % os.path.basename(filename),
448 _("SVN Commit"),
449 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
450 status = yesNoMsg.ShowModal()
451 if status == wx.ID_YES:
452 doc.Save()
453 elif status == wx.ID_NO:
454 pass
455 else: # elif status == wx.CANCEL:
456 return True
457 break
458
459 shortFilenames = []
460 for i, filename in enumerate(filenames):
461 shortFilename = os.path.basename(filename)
462 shortFilenames.append(shortFilename)
463
464 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
465
466 sizer = wx.BoxSizer(wx.VERTICAL)
467 sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
468 commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
469 sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
470
471 sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
472 fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
473 for i in range(fileList.GetCount()):
474 fileList.Check(i, True)
475 sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
476
477 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
478 okBtn = wx.Button(dlg, wx.ID_OK)
479 okBtn.SetDefault()
480 buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
481 buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
482
483 contentSizer = wx.BoxSizer(wx.VERTICAL)
484 contentSizer.Add(sizer, 0, wx.ALL, SPACE)
485 contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
486
487 dlg.SetSizer(contentSizer)
488 dlg.Fit()
489 dlg.Layout()
490
491 if dlg.ShowModal() == wx.ID_OK:
492 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
493
494 messageService = wx.GetApp().GetService(MessageService.MessageService)
495 messageService.ShowWindow()
496
497 view = messageService.GetView()
498 view.ClearLines()
499 view.AddLines(_("SVN Commit:\n"))
500
501 try:
502 selFilenames = []
503 for i in range(fileList.GetCount()):
504 if fileList.IsChecked(i):
505 selFilenames.append(filenames[i])
506 view.AddLines("%s\n" % filenames[i])
507
508 if len(selFilenames):
509 comment = commentText.GetValue()
510 status = self._client.checkin(selFilenames, comment)
511
512 if status is None:
513 view.AddLines(_("Nothing to commit.\n"))
514 elif status.number > 0:
515 view.AddLines(_("Committed as revision %s.\n") % status.number)
516 else:
517 view.AddLines(_("Commit failed.\n"))
518
519 except pysvn.ClientError, e:
520 view.AddLines("%s\n" % str(e))
521 wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
522 except:
523 extype, ex, tb = sys.exc_info()
524 view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
525 for line in traceback.format_tb(tb):
526 view.AddLines(line)
527 wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
528
529 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
530 dlg.Destroy()
531 return True
532
6f1a3f9c
RD
533
534 elif id == SVNService.SVN_CHECKIN_ID:
26ee3a06
RD
535 filenames = self.GetCurrentDocuments()[:]
536 filenames.sort(self.BasenameCaseInsensitiveCompare)
6f1a3f9c
RD
537
538 # ask user if dirty files should be saved first
539 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
540 for filename in filenames:
541 for doc in openDocs:
542 if doc.GetFilename() == filename and doc.IsModified():
543 yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
544 _("'%s' has unsaved modifications. Save it before commit?") % os.path.basename(filename),
545 _("SVN Commit"),
546 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
547 status = yesNoMsg.ShowModal()
548 if status == wx.ID_YES:
549 doc.Save()
550 elif status == wx.ID_NO:
551 pass
552 else: # elif status == wx.CANCEL:
553 return True
554 break
555
556 shortFilenames = []
557 for i, filename in enumerate(filenames):
558 shortFilename = os.path.basename(filename)
559 shortFilenames.append(shortFilename)
560
561 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
562
563 sizer = wx.BoxSizer(wx.VERTICAL)
564 sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
565 commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
566 sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
567
568 sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
569 fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
570 for i in range(fileList.GetCount()):
571 fileList.Check(i, True)
572 sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
573
574 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
575 okBtn = wx.Button(dlg, wx.ID_OK)
576 okBtn.SetDefault()
577 buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
578 buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
579
580 contentSizer = wx.BoxSizer(wx.VERTICAL)
581 contentSizer.Add(sizer, 0, wx.ALL, SPACE)
582 contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
583
584 dlg.SetSizer(contentSizer)
585 dlg.Fit()
586 dlg.Layout()
587
588 if dlg.ShowModal() == wx.ID_OK:
589 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
590
591 messageService = wx.GetApp().GetService(MessageService.MessageService)
592 messageService.ShowWindow()
593
594 view = messageService.GetView()
595 view.ClearLines()
596 view.AddLines(_("SVN Commit:\n"))
597
598 try:
599 selFilenames = []
600 for i in range(fileList.GetCount()):
601 if fileList.IsChecked(i):
602 selFilenames.append(filenames[i])
603 view.AddLines("%s\n" % filenames[i])
604
605 if len(selFilenames):
606 comment = commentText.GetValue()
607 status = self._client.checkin(selFilenames, comment)
608
609 if status is None:
610 view.AddLines(_("Nothing to commit.\n"))
611 elif status.number > 0:
612 view.AddLines(_("Committed as revision %s.\n") % status.number)
613 else:
614 view.AddLines(_("Commit failed.\n"))
615
616 except pysvn.ClientError, e:
617 view.AddLines("%s\n" % str(e))
618 wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
619 except:
620 extype, ex, tb = sys.exc_info()
621 view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
622 for line in traceback.format_tb(tb):
623 view.AddLines(line)
624 wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
625
626 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
627 dlg.Destroy()
628 return True
629
630 elif id == SVNService.SVN_CHECKOUT_ID:
631 config = wx.ConfigBase_Get()
632 svnUrl = config.Read(SVN_REPOSITORY_URL, self._defaultURL)
633
634 dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Checkout"))
635
636 gridSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
637 gridSizer.Add(wx.StaticText(dlg, -1, _("Repository URL:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
26ee3a06
RD
638 svnUrlList = ReadSvnUrlList()
639 svnURLCombobox = wx.ComboBox(dlg, -1, size=(200, -1), choices=svnUrlList, style=wx.CB_DROPDOWN)
640 if len(svnUrlList):
641 svnURLCombobox.SetToolTipString(svnUrlList[0])
642 svnURLCombobox.SetStringSelection(svnUrlList[0])
643 else:
644 svnURLCombobox.SetToolTipString(_("Set Repository URL"))
645 gridSizer.Add(svnURLCombobox, 0)
6f1a3f9c
RD
646
647 gridSizer.Add(wx.StaticText(dlg, -1, _("Checkout to dir:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
648 localPath = wx.TextCtrl(dlg, -1, size = (200, -1))
649 localPath.SetToolTipString(_("Path in local file system where files will be located."))
650 findDirButton = wx.Button(dlg, -1, _("Browse..."))
651
652 def OnBrowseButton(event):
653 dirDlg = wx.DirDialog(wx.GetApp().GetTopWindow(), _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
654 dir = localPath.GetValue()
655 if len(dir):
656 dirDlg.SetPath(dir)
657 if dirDlg.ShowModal() == wx.ID_OK:
658 localPath.SetValue(dirDlg.GetPath())
659 localPath.SetToolTipString(localPath.GetValue())
660 localPath.SetInsertionPointEnd()
661
662 dirDlg.Destroy()
663 wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
664
665 sizer = wx.BoxSizer(wx.HORIZONTAL)
666 sizer.Add(localPath, 1, wx.EXPAND)
667 sizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
668 gridSizer.Add(sizer, 0)
669
670 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
671 okBtn = wx.Button(dlg, wx.ID_OK)
672 okBtn.SetDefault()
673 buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
674 buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
675
676 contentSizer = wx.BoxSizer(wx.VERTICAL)
677 contentSizer.Add(gridSizer, 0, wx.ALL, SPACE)
678 contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
679
680 dlg.SetSizer(contentSizer)
681 dlg.Fit()
682 dlg.Layout()
683
684 if dlg.ShowModal() == wx.ID_OK:
685 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
26ee3a06
RD
686
687 WriteSvnUrlList(svnURLCombobox)
6f1a3f9c
RD
688
689 messageService = wx.GetApp().GetService(MessageService.MessageService)
690 messageService.ShowWindow()
691
692 view = messageService.GetView()
693 view.ClearLines()
694 view.AddLines(_("SVN Checkout:\n"))
695
26ee3a06 696 svnUrl = svnURLCombobox.GetValue()
6f1a3f9c
RD
697 toLocation = localPath.GetValue()
698 try:
699 self._client.checkout(svnUrl, toLocation)
700 view.AddLines(_("Checkout completed.\n"))
701 except pysvn.ClientError, e:
702 view.AddLines(_("Checkout failed. %s\n") % str(e))
703 wx.MessageBox(_("Checkout failed. %s") % str(e), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
704 except:
705 extype, ex, tb = sys.exc_info()
706 view.AddLines("Checkout failed: (%s) %s\n" % (extype, str(ex)))
707 for line in traceback.format_tb(tb):
708 view.AddLines(line)
709 wx.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
710
711 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
712 dlg.Destroy()
713 return True
714
715 elif id == SVNService.SVN_REVERT_ID:
716 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
717
718 filenames = self.GetCurrentDocuments()
719
720 messageService = wx.GetApp().GetService(MessageService.MessageService)
721 messageService.ShowWindow()
722
723 view = messageService.GetView()
724 view.ClearLines()
725 view.AddLines(_("SVN Revert:\n"))
726 for filename in filenames:
727 view.AddLines("%s\n" % filename)
728
729 try:
730 self._client.revert(filenames)
731 view.AddLines(_("Revert completed.\n"))
732
733 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
734 for doc in openDocs[:]: # need to make a copy of the list otherwise ordinality changes as we close the files
735 if doc.GetFilename() in filenames:
736 yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
737 _("Reverted file '%s' is currently open. Close it?") % os.path.basename(doc.GetFilename()),
738 _("Close File"),
739 wx.YES_NO|wx.ICON_QUESTION)
740 if yesNoMsg.ShowModal() == wx.ID_YES:
741 doc.DeleteAllViews()
742
743 except pysvn.ClientError, e:
744 view.AddLines("%s\n" % str(e))
745 wx.MessageBox(str(e), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
746 except:
747 extype, ex, tb = sys.exc_info()
748 view.AddLines("Revert failed: (%s) %s\n" % (extype, str(ex)))
749 for line in traceback.format_tb(tb):
750 view.AddLines(line)
751 wx.MessageBox(_("Revert failed."), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
752
753 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
754 return True
755
756 elif id == SVNService.SVN_ADD_ID:
757 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
758
759 filenames = self.GetCurrentDocuments()
760
761 messageService = wx.GetApp().GetService(MessageService.MessageService)
762 messageService.ShowWindow()
763
764 view = messageService.GetView()
765 view.ClearLines()
766 view.AddLines(_("SVN Add:\n"))
767 for filename in filenames:
768 view.AddLines("%s\n" % filename)
769
770 try:
771 self._client.add(filenames)
772 view.AddLines(_("Add completed.\n"))
773 except pysvn.ClientError, e:
774 view.AddLines("%s\n" % str(e))
775 wx.MessageBox(str(e), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
776 except:
777 extype, ex, tb = sys.exc_info()
778 view.AddLines("Add failed: (%s) %s\n" % (extype, str(ex)))
779 for line in traceback.format_tb(tb):
780 view.AddLines(line)
781 wx.MessageBox(_("Add failed."), _("SVN Add"), 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_DELETE_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 Delete:\n"))
797 for filename in filenames:
798 view.AddLines("%s\n" % filename)
799
800 try:
801 self._client.remove(filenames)
802 view.AddLines(_("Delete completed.\n"))
803 except pysvn.ClientError, e:
804 view.AddLines("%s\n" % str(e))
805 wx.MessageBox(str(e), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
806 except:
807 extype, ex, tb = sys.exc_info()
808 view.AddLines("Delete failed: (%s) %s\n" % (extype, str(ex)))
809 for line in traceback.format_tb(tb):
810 view.AddLines(line)
811 wx.MessageBox(_("Delete failed."), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
812
813 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
814 return True
815
816 return False
817
818
819 def ProcessUpdateUIEvent(self, event):
820 id = event.GetId()
821
822 if id in [SVNService.SVN_UPDATE_ID,
823 SVNService.SVN_CHECKIN_ID,
824 SVNService.SVN_REVERT_ID,
825 SVNService.SVN_ADD_ID,
826 SVNService.SVN_DELETE_ID]:
827 if self.GetCurrentDocuments():
828 event.Enable(True)
829 else:
830 event.Enable(False)
831 return True
832
833 elif id == SVNService.SVN_CHECKOUT_ID:
834 event.Enable(True)
835 return True
836
26ee3a06
RD
837 elif (id == SVNService.SVN_UPDATE_ALL_ID
838 or id == SVNService.SVN_CHECKIN_ALL_ID):
839 if self.GetCurrentProjects():
840 event.Enable(True)
841 else:
842 event.Enable(False)
843 return True
844
6f1a3f9c
RD
845 return False
846
847
26ee3a06
RD
848 def GetCurrentProjects(self):
849 projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
850 if projectService:
851 projView = projectService.GetView()
852
853 if projView.HasFocus():
854 filenames = projView.GetSelectedProjects()
855 if len(filenames):
856 return filenames
857 else:
858 return None
859 return None
860
861
6f1a3f9c
RD
862 def GetCurrentDocuments(self):
863
864 projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
865 if projectService:
866 projView = projectService.GetView()
867
868 if projView.HasFocus():
869 filenames = projView.GetSelectedFiles()
870 if len(filenames):
871 return filenames
872 else:
873 return None
874
875 doc = wx.GetApp().GetTopWindow().GetDocumentManager().GetCurrentDocument()
876 if doc:
877 filenames = [doc.GetFilename()]
878 else:
879 filenames = None
880
881 return filenames
882
883
26ee3a06
RD
884 def BasenameCaseInsensitiveCompare(self, s1, s2):
885 s1L = os.path.basename(s1).lower()
886 s2L = os.path.basename(s2).lower()
887 if s1L == s2L:
888 return 0
889 elif s1L < s2L:
890 return -1
891 else:
892 return 1
893
894
6f1a3f9c
RD
895class SVNOptionsPanel(wx.Panel):
896
897
898 def __init__(self, parent, id):
899 wx.Panel.__init__(self, parent, id)
900
901 config = wx.ConfigBase_Get()
6f1a3f9c
RD
902 configDir = config.Read(SVN_CONFIG_DIR, "")
903
904 borderSizer = wx.BoxSizer(wx.VERTICAL)
905 sizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
906
907 sizer.Add(wx.StaticText(self, -1, _("SVN Config Dir:")), 0, wx.ALIGN_CENTER_VERTICAL)
908
909 self._svnConfigDir = wx.TextCtrl(self, -1, configDir, size = (200, -1))
910 if configDir == "":
911 self._svnConfigDir.SetToolTipString(_("Path Subversion configuration directory."))
912 else:
913 self._svnConfigDir.SetToolTipString(configDir)
914
915 findDirButton = wx.Button(self, -1, _("Browse..."))
916
917 def OnBrowseButton(event):
918 dirDlg = wx.DirDialog(self, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
919 dir = self._svnConfigDir.GetValue()
920 if len(dir):
921 dirDlg.SetPath(dir)
922 if dirDlg.ShowModal() == wx.ID_OK:
923 self._svnConfigDir.SetValue(dirDlg.GetPath())
924 self._svnConfigDir.SetToolTipString(self._svnConfigDir.GetValue())
925 self._svnConfigDir.SetInsertionPointEnd()
926
927 dirDlg.Destroy()
928 wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
929
930 hsizer = wx.BoxSizer(wx.HORIZONTAL)
931 hsizer.Add(self._svnConfigDir, 1, wx.EXPAND)
932 hsizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
933 sizer.Add(hsizer, 0)
934
26ee3a06
RD
935
936 svnUrlList = ReadSvnUrlList()
6f1a3f9c 937 sizer.Add(wx.StaticText(self, -1, _("SVN URL:")), 0, wx.ALIGN_CENTER_VERTICAL)
26ee3a06
RD
938 self._svnURLCombobox = wx.ComboBox(self, -1, size=(200, -1), choices=svnUrlList, style=wx.CB_DROPDOWN)
939 if len(svnUrlList):
940 self._svnURLCombobox.SetToolTipString(svnUrlList[0])
941 self._svnURLCombobox.SetStringSelection(svnUrlList[0])
942 else:
943 self._svnURLCombobox.SetToolTipString(_("Set Repository URL"))
944 sizer.Add(self._svnURLCombobox, 0)
945
946
947 sizer.Add(wx.StaticText(self, -1, _("SVN_SSH:")), 0, wx.ALIGN_CENTER_VERTICAL)
948 svnSSH = os.getenv("SVN_SSH")
949 if not svnSSH or svnSSH == "":
950 self._svnSSH = wx.TextCtrl(self, -1, size = (200, -1))
951 else:
952 self._svnSSH = wx.TextCtrl(self, -1, svnSSH, size = (200, -1))
953 self._svnSSH.SetToolTipString(_("Override SVN_SSH environment variable temporarily."))
954
955 findSSHButton = wx.Button(self, -1, _("Browse..."))
956
957 def OnBrowseFileButton(event):
958 dirDlg = wx.FileDialog(self, _("Choose a file:"), style=wx.OPEN|wx.CHANGE_DIR)
959 if dirDlg.ShowModal() == wx.ID_OK:
960 self._svnSSH.SetValue(dirDlg.GetPath())
961 self._svnSSH.SetToolTipString(self._svnSSH.GetValue())
962 self._svnSSH.SetInsertionPointEnd()
963 dirDlg.Destroy()
964 wx.EVT_BUTTON(findSSHButton, -1, OnBrowseFileButton)
965
966 hsizer = wx.BoxSizer(wx.HORIZONTAL)
967 hsizer.Add(self._svnSSH, 1, wx.EXPAND)
968 hsizer.Add(findSSHButton, 0, wx.LEFT, HALF_SPACE)
969 sizer.Add(hsizer, 0)
970
6f1a3f9c
RD
971
972 borderSizer.Add(sizer, 0, wx.ALL, SPACE)
973 self.SetSizer(borderSizer)
974 self.Layout()
975 parent.AddPage(self, _("SVN"))
976
977
978 def OnOK(self, optionsDialog):
979 config = wx.ConfigBase_Get()
26ee3a06 980
6f1a3f9c 981 config.Write(SVN_CONFIG_DIR, self._svnConfigDir.GetValue())
26ee3a06
RD
982
983 WriteSvnUrlList(self._svnURLCombobox)
984
985 os.environ["SVN_SSH"] = self._svnSSH.GetValue()
986
987
988def ReadSvnUrlList():
989 """ Read in list of SNV repository URLs. First in list is the last one path used. """
990 config = wx.ConfigBase_Get()
991 urlStringList = config.Read(SVN_REPOSITORY_URL)
992 if len(urlStringList):
993 urlList = eval(urlStringList)
994 else:
995 urlList = []
996 if len(urlList) == 0:
997 svnService = wx.GetApp().GetService(SVNService)
998 if svnService and hasattr(svnService, "_defaultURL"):
999 urlList.append(svnService._defaultURL)
1000 return urlList
1001
1002
1003def WriteSvnUrlList(comboBox):
1004 """ Save out list of SVN repository URLs from combobox. Put on top the current selection. Only save out first 10 from the list """
1005 urlList = []
1006
1007 url = comboBox.GetValue()
1008 if len(url):
1009 urlList.append(url)
1010
1011 for i in range(min(comboBox.GetCount(), 10)):
1012 url = comboBox.GetString(i)
1013 if url not in urlList:
1014 urlList.append(url)
1015
1016 config = wx.ConfigBase_Get()
1017 config.Write(SVN_REPOSITORY_URL, urlList.__repr__())
1018
6f1a3f9c 1019