]>
Commit | Line | Data |
---|---|---|
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 | ||
13 | import wx | |
14 | import os.path | |
15 | import ProjectEditor | |
16 | import MessageService | |
17 | ||
18 | import sys # for errors | |
19 | import traceback # for errors | |
20 | ||
21 | try: | |
22 | import pysvn # python-subversion integration | |
23 | SVN_INSTALLED = True | |
24 | except ImportError: | |
25 | SVN_INSTALLED = False | |
26 | ||
27 | _ = wx.GetTranslation | |
28 | ||
29 | ||
30 | #---------------------------------------------------------------------------- | |
31 | # Constants | |
32 | #---------------------------------------------------------------------------- | |
33 | SVN_CONFIG_DIR = "SVNConfigDir" | |
34 | SVN_REPOSITORY_URL = "SVNRepositoryURL" | |
35 | ||
36 | SPACE = 10 | |
37 | HALF_SPACE = 5 | |
38 | ||
39 | ||
40 | #---------------------------------------------------------------------------- | |
41 | # Classes | |
42 | #---------------------------------------------------------------------------- | |
43 | ||
44 | class SVNService(wx.lib.pydocview.DocService): | |
45 | SVN_UPDATE_ID = wx.NewId() | |
46 | SVN_CHECKIN_ID = wx.NewId() | |
47 | SVN_CHECKOUT_ID = wx.NewId() | |
48 | SVN_REVERT_ID = wx.NewId() | |
49 | SVN_ADD_ID = wx.NewId() | |
50 | SVN_DELETE_ID = wx.NewId() | |
51 | SVN_COMMAND_LIST = [SVN_UPDATE_ID, SVN_CHECKIN_ID, SVN_CHECKOUT_ID, SVN_REVERT_ID, SVN_ADD_ID, SVN_DELETE_ID] | |
52 | ||
53 | ||
54 | def __init__(self): | |
55 | self._defaultURL = "svn://" | |
56 | ||
57 | global SVN_INSTALLED | |
58 | if SVN_INSTALLED: | |
59 | config = wx.ConfigBase_Get() | |
60 | configDir = config.Read(SVN_CONFIG_DIR, "") | |
61 | ||
62 | self._client = pysvn.Client(configDir) | |
63 | try: | |
64 | self._defaultURL = self._client.info('.').url | |
65 | except: | |
66 | pass | |
67 | self._client.callback_cancel = self.IfCancel | |
68 | self._client.callback_notify = self.UpdateStatus | |
69 | self._client.callback_get_log_message = self.GetLogMessage | |
70 | self._client.callback_get_login = self.GetLogin | |
71 | self._client.callback_ssl_server_trust_prompt = self.GetSSLServerTrust | |
72 | self._client.callback_ssl_client_cert_password_prompt = self.SSLClientPassword | |
73 | self._client.callback_ssl_client_cert_prompt = self.SSLClientCert | |
74 | self._client.callback_ssl_server_prompt = self.SSLServerPrompt | |
75 | ||
76 | #---------------------------------------------------------------------------- | |
77 | # pysvn.Client() Callback Methods | |
78 | #---------------------------------------------------------------------------- | |
79 | ||
80 | def IfCancel(self): | |
81 | """ return True if user wants to cancel current command """ | |
82 | return False | |
83 | ||
84 | ||
85 | def UpdateStatus(self, eventDict): | |
86 | messageService = wx.GetApp().GetService(MessageService.MessageService) | |
87 | messageService.ShowWindow() | |
88 | ||
89 | view = messageService.GetView() | |
90 | view.AddLines(_("%s %s\n") % (eventDict['action'], eventDict['path'])) | |
91 | ||
92 | ||
93 | def GetLogMessage(self): | |
94 | dlg = wx.TextEntryDialog(wx.GetApp().GetTopWindow(), | |
95 | _("Comment"), | |
96 | _("SVN Log Message")) | |
97 | ||
98 | if dlg.ShowModal() == wx.ID_OK: | |
99 | retcode = True | |
100 | message = dlg.GetValue() | |
101 | else: | |
102 | retcode = False | |
103 | message = _("Cancel Action") | |
104 | ||
105 | dlg.Destroy() | |
106 | ||
107 | return retcode, message | |
108 | ||
109 | ||
110 | def GetLogin(self, realm, username, maySave): | |
111 | dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Login")) | |
112 | ||
113 | sizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5) | |
114 | sizer.Add(wx.StaticText(dlg, -1, _("Username:")), 0, wx.ALIGN_CENTER_VERTICAL) | |
115 | usernameTxt = wx.TextCtrl(dlg, -1, username, size = (200, -1)) | |
116 | sizer.Add(usernameTxt, 0, wx.ALIGN_CENTER_VERTICAL) | |
117 | sizer.Add(wx.StaticText(dlg, -1, _("Password:")), 0, wx.ALIGN_CENTER_VERTICAL) | |
118 | passwordTxt = wx.TextCtrl(dlg, -1, size=(200, -1), style=wx.TE_PASSWORD) | |
119 | sizer.Add(passwordTxt, 0, wx.ALIGN_CENTER_VERTICAL) | |
120 | ||
121 | savePasswordCheckbox = wx.CheckBox(dlg, -1, _("Remember Username and Password")) | |
122 | if not maySave: | |
123 | savePasswordCheckbox.Enable(False) | |
124 | ||
125 | buttonSizer = wx.BoxSizer(wx.HORIZONTAL) | |
126 | okBtn = wx.Button(dlg, wx.ID_OK) | |
127 | okBtn.SetDefault() | |
128 | buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE) | |
129 | buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0) | |
130 | ||
131 | contentSizer = wx.BoxSizer(wx.VERTICAL) | |
132 | contentSizer.Add(sizer, 0, wx.LEFT|wx.TOP|wx.RIGHT, SPACE) | |
133 | contentSizer.Add(savePasswordCheckbox, 0, wx.TOP|wx.LEFT|wx.BOTTOM, SPACE) | |
134 | contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE) | |
135 | ||
136 | dlg.SetSizer(contentSizer) | |
137 | dlg.Fit() | |
138 | dlg.Layout() | |
139 | ||
140 | if dlg.ShowModal() == wx.ID_OK: | |
141 | retcode = True | |
142 | username = usernameTxt.GetValue().strip() | |
143 | password = passwordTxt.GetValue() | |
144 | save = savePasswordCheckBox.IsChecked() | |
145 | else: | |
146 | retcode = False | |
147 | username = None | |
148 | password = None | |
149 | save = False | |
150 | ||
151 | dlg.Destroy() | |
152 | return retcode, username, password, save | |
153 | ||
154 | ||
155 | def SSLServerPrompt(self): | |
156 | """ Not implemented, as per pysvn documentation """ | |
157 | return | |
158 | ||
159 | ||
160 | def GetSSLServerTrust(self, trustDict): | |
161 | dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SSL Server Certificate")) | |
162 | ||
163 | sizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5) | |
164 | for k in ['hostname', 'valid_from', 'valid_to', 'issuer_dname', 'realm']: | |
165 | if trustDict.has_key(k): | |
166 | sizer.Add(wx.StaticText(dlg, -1, "%s:" % k), 0, wx.ALIGN_CENTER_VERTICAL) | |
167 | sizer.Add(wx.StaticText(dlg, -1, "%s" % trustDict[k]), 0, wx.ALIGN_CENTER_VERTICAL) | |
168 | ||
169 | box = wx.StaticBoxSizer(wx.StaticBox(dlg, -1, _("Certificate Info")), wx.VERTICAL) | |
170 | box.Add(sizer, 0, wx.EXPAND) | |
171 | ||
172 | certRadio = wx.RadioBox(dlg, -1, _("Certificate"), choices=[_("Accept Always"), _("Accept Once"), _("Reject")], majorDimension=1, style=wx.RA_SPECIFY_COLS) | |
173 | ||
174 | buttonSizer = wx.BoxSizer(wx.HORIZONTAL) | |
175 | okBtn = wx.Button(dlg, wx.ID_OK) | |
176 | okBtn.SetDefault() | |
177 | buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE) | |
178 | buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0) | |
179 | ||
180 | contentSizer = wx.BoxSizer(wx.VERTICAL) | |
181 | contentSizer.Add(box, 0, wx.TOP|wx.LEFT|wx.RIGHT|wx.EXPAND, SPACE) | |
182 | contentSizer.Add(certRadio, 0, wx.TOP|wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.EXPAND, SPACE) | |
183 | contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE) | |
184 | ||
185 | dlg.SetSizer(contentSizer) | |
186 | dlg.Fit() | |
187 | dlg.Layout() | |
188 | ||
189 | # default values for reject | |
190 | retcode = False | |
191 | acceptedFailures = 0 | |
192 | save = False | |
193 | ||
194 | if dlg.ShowModal() == wx.ID_OK: | |
195 | cert = certRadio.GetStringSelection() | |
196 | if cert == _("Accept Always"): | |
197 | retcode = True | |
198 | acceptedFailures = trustDict.get('failures') | |
199 | save = True | |
200 | elif cert == _("Accept Once"): | |
201 | retcode = True | |
202 | acceptedFailures = trustDict.get('failures') | |
203 | save = False | |
204 | ||
205 | return retcode, acceptedFailures, save | |
206 | ||
207 | ||
208 | def SSLClientPassword(self, realm, maySave): | |
209 | dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SSL Client Certificate Login")) | |
210 | ||
211 | sizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5) | |
212 | sizer.Add(wx.StaticText(dlg, -1, _("Realm:")), 0, wx.ALIGN_CENTER_VERTICAL) | |
213 | sizer.Add(wx.StaticText(dlg, -1, realm), 0, wx.ALIGN_CENTER_VERTICAL) | |
214 | sizer.Add(wx.StaticText(dlg, -1, _("Password:")), 0, wx.ALIGN_CENTER_VERTICAL) | |
215 | passwordTxt = wx.TextCtrl(dlg, -1, size=(200, -1), style=wx.TE_PASSWORD) | |
216 | sizer.Add(passwordTxt, 0, wx.ALIGN_CENTER_VERTICAL) | |
217 | ||
218 | savePasswordCheckbox = wx.CheckBox(dlg, -1, _("Remember Password")) | |
219 | if not maySave: | |
220 | savePasswordCheckbox.Enable(False) | |
221 | ||
222 | buttonSizer = wx.BoxSizer(wx.HORIZONTAL) | |
223 | okBtn = wx.Button(dlg, wx.ID_OK) | |
224 | okBtn.SetDefault() | |
225 | buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE) | |
226 | buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0) | |
227 | ||
228 | contentSizer = wx.BoxSizer(wx.VERTICAL) | |
229 | contentSizer.Add(sizer, 0, wx.LEFT|wx.TOP|wx.RIGHT, SPACE) | |
230 | contentSizer.Add(savePasswordCheckbox, 0, wx.TOP|wx.LEFT|wx.BOTTOM, SPACE) | |
231 | contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE) | |
232 | ||
233 | dlg.SetSizer(contentSizer) | |
234 | dlg.Fit() | |
235 | dlg.Layout() | |
236 | ||
237 | if dlg.ShowModal() == wx.ID_OK: | |
238 | retcode = True | |
239 | password = passwordTxt.GetValue() | |
240 | save = savePasswordCheckBox.IsChecked() | |
241 | else: | |
242 | retcode = False | |
243 | password = None | |
244 | save = False | |
245 | ||
246 | dlg.Destroy() | |
247 | return retcode, password, save | |
248 | ||
249 | ||
250 | def SSLClientCert(self): | |
251 | dlg = wx.FileDialog(wx.GetApp().GetTopWindow(), | |
252 | message="Choose certificate", defaultDir=os.getcwd(), | |
253 | style=wx.OPEN|wx.CHANGE_DIR | |
254 | ) | |
255 | ||
256 | if dlg.ShowModal() == wx.ID_OK: | |
257 | retcode = True | |
258 | certfile = dlg.GetPath() | |
259 | else: | |
260 | retcode = False | |
261 | certfile = None | |
262 | ||
263 | dlg.Destroy() | |
264 | return retcode, certfile | |
265 | ||
266 | ||
267 | ||
268 | #---------------------------------------------------------------------------- | |
269 | # Service Methods | |
270 | #---------------------------------------------------------------------------- | |
271 | ||
272 | def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None): | |
273 | menu = menuBar.GetMenu(menuBar.FindMenu(_("Project"))) | |
274 | ||
275 | menu.AppendSeparator() | |
276 | ||
277 | wx.EVT_MENU(frame, SVNService.SVN_UPDATE_ID, self.ProcessEvent) | |
278 | wx.EVT_UPDATE_UI(frame, SVNService.SVN_UPDATE_ID, self.ProcessUpdateUIEvent) | |
279 | menu.Append(SVNService.SVN_UPDATE_ID, _("SVN Update"), _("Update file from Subversion")) | |
280 | wx.EVT_MENU(frame, SVNService.SVN_CHECKIN_ID, self.ProcessEvent) | |
281 | wx.EVT_UPDATE_UI(frame, SVNService.SVN_CHECKIN_ID, self.ProcessUpdateUIEvent) | |
282 | menu.Append(SVNService.SVN_CHECKIN_ID, _("SVN Commit..."), _("Commit file changes to Subversion")) | |
283 | wx.EVT_MENU(frame, SVNService.SVN_CHECKOUT_ID, self.ProcessEvent) | |
284 | wx.EVT_UPDATE_UI(frame, SVNService.SVN_CHECKOUT_ID, self.ProcessUpdateUIEvent) | |
285 | menu.Append(SVNService.SVN_CHECKOUT_ID, _("SVN Checkout..."), _("Checkout file from Subversion")) | |
286 | wx.EVT_MENU(frame, SVNService.SVN_REVERT_ID, self.ProcessEvent) | |
287 | wx.EVT_UPDATE_UI(frame, SVNService.SVN_REVERT_ID, self.ProcessUpdateUIEvent) | |
288 | menu.Append(SVNService.SVN_REVERT_ID, _("SVN Revert"), _("Revert file from Subversion")) | |
289 | ||
290 | menu.AppendSeparator() | |
291 | ||
292 | wx.EVT_MENU(frame, SVNService.SVN_ADD_ID, self.ProcessEvent) | |
293 | wx.EVT_UPDATE_UI(frame, SVNService.SVN_ADD_ID, self.ProcessUpdateUIEvent) | |
294 | menu.Append(SVNService.SVN_ADD_ID, _("SVN Add"), _("Add file to Subversion")) | |
295 | wx.EVT_MENU(frame, SVNService.SVN_DELETE_ID, self.ProcessEvent) | |
296 | wx.EVT_UPDATE_UI(frame, SVNService.SVN_DELETE_ID, self.ProcessUpdateUIEvent) | |
297 | menu.Append(SVNService.SVN_DELETE_ID, _("SVN Delete"), _("Delete file from Subversion")) | |
298 | ||
299 | ||
300 | def ProcessEvent(self, event): | |
301 | ||
302 | id = event.GetId() | |
303 | ||
304 | if not SVN_INSTALLED: | |
305 | if id in SVNService.SVN_COMMAND_LIST: | |
306 | wx.MessageBox(_("pysvn not found. Please install pysvn"), _("Python Subversion")) | |
307 | return True | |
308 | return False | |
309 | ||
310 | ||
311 | if id == SVNService.SVN_UPDATE_ID: | |
312 | filenames = self.GetCurrentDocuments() | |
313 | ||
314 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) | |
315 | ||
316 | messageService = wx.GetApp().GetService(MessageService.MessageService) | |
317 | messageService.ShowWindow() | |
318 | ||
319 | view = messageService.GetView() | |
320 | view.ClearLines() | |
321 | view.AddLines(_("SVN Update:\n")) | |
322 | ||
323 | for filename in filenames: | |
324 | view.AddLines("%s\n" % filename) | |
325 | try: | |
326 | status = self._client.update(filename) | |
327 | ||
328 | if status.number > 0: | |
329 | view.AddLines(_("Updated to revision %s\n") % status.number) | |
330 | ||
331 | openDocs = wx.GetApp().GetDocumentManager().GetDocuments() | |
332 | for doc in openDocs: | |
333 | if doc.GetFilename() == filename: | |
334 | yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(), | |
335 | _("Updated file '%s' is currently open. Close it?") % os.path.basename(filename), | |
336 | _("Close File"), | |
337 | wx.YES_NO|wx.ICON_QUESTION) | |
338 | if yesNoMsg.ShowModal() == wx.ID_YES: | |
339 | doc.DeleteAllViews() | |
340 | break | |
341 | else: | |
342 | view.AddLines(_("Update failed.\n")) | |
343 | ||
344 | except pysvn.ClientError, e: | |
345 | view.AddLines("%s\n" % str(e)) | |
346 | wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION) | |
347 | except: | |
348 | extype, ex, tb = sys.exc_info() | |
349 | view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex))) | |
350 | for line in traceback.format_tb(tb): | |
351 | view.AddLines(line) | |
352 | ||
353 | wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION) | |
354 | ||
355 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) | |
356 | ||
357 | return True | |
358 | ||
359 | elif id == SVNService.SVN_CHECKIN_ID: | |
360 | filenames = self.GetCurrentDocuments() | |
361 | ||
362 | # ask user if dirty files should be saved first | |
363 | openDocs = wx.GetApp().GetDocumentManager().GetDocuments() | |
364 | for filename in filenames: | |
365 | for doc in openDocs: | |
366 | if doc.GetFilename() == filename and doc.IsModified(): | |
367 | yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(), | |
368 | _("'%s' has unsaved modifications. Save it before commit?") % os.path.basename(filename), | |
369 | _("SVN Commit"), | |
370 | wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) | |
371 | status = yesNoMsg.ShowModal() | |
372 | if status == wx.ID_YES: | |
373 | doc.Save() | |
374 | elif status == wx.ID_NO: | |
375 | pass | |
376 | else: # elif status == wx.CANCEL: | |
377 | return True | |
378 | break | |
379 | ||
380 | shortFilenames = [] | |
381 | for i, filename in enumerate(filenames): | |
382 | shortFilename = os.path.basename(filename) | |
383 | shortFilenames.append(shortFilename) | |
384 | ||
385 | dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit")) | |
386 | ||
387 | sizer = wx.BoxSizer(wx.VERTICAL) | |
388 | sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL) | |
389 | commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE) | |
390 | sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE) | |
391 | ||
392 | sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE) | |
393 | fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames) | |
394 | for i in range(fileList.GetCount()): | |
395 | fileList.Check(i, True) | |
396 | sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE) | |
397 | ||
398 | buttonSizer = wx.BoxSizer(wx.HORIZONTAL) | |
399 | okBtn = wx.Button(dlg, wx.ID_OK) | |
400 | okBtn.SetDefault() | |
401 | buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE) | |
402 | buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0) | |
403 | ||
404 | contentSizer = wx.BoxSizer(wx.VERTICAL) | |
405 | contentSizer.Add(sizer, 0, wx.ALL, SPACE) | |
406 | contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE) | |
407 | ||
408 | dlg.SetSizer(contentSizer) | |
409 | dlg.Fit() | |
410 | dlg.Layout() | |
411 | ||
412 | if dlg.ShowModal() == wx.ID_OK: | |
413 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) | |
414 | ||
415 | messageService = wx.GetApp().GetService(MessageService.MessageService) | |
416 | messageService.ShowWindow() | |
417 | ||
418 | view = messageService.GetView() | |
419 | view.ClearLines() | |
420 | view.AddLines(_("SVN Commit:\n")) | |
421 | ||
422 | try: | |
423 | selFilenames = [] | |
424 | for i in range(fileList.GetCount()): | |
425 | if fileList.IsChecked(i): | |
426 | selFilenames.append(filenames[i]) | |
427 | view.AddLines("%s\n" % filenames[i]) | |
428 | ||
429 | if len(selFilenames): | |
430 | comment = commentText.GetValue() | |
431 | status = self._client.checkin(selFilenames, comment) | |
432 | ||
433 | if status is None: | |
434 | view.AddLines(_("Nothing to commit.\n")) | |
435 | elif status.number > 0: | |
436 | view.AddLines(_("Committed as revision %s.\n") % status.number) | |
437 | else: | |
438 | view.AddLines(_("Commit failed.\n")) | |
439 | ||
440 | except pysvn.ClientError, e: | |
441 | view.AddLines("%s\n" % str(e)) | |
442 | wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION) | |
443 | except: | |
444 | extype, ex, tb = sys.exc_info() | |
445 | view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex))) | |
446 | for line in traceback.format_tb(tb): | |
447 | view.AddLines(line) | |
448 | wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION) | |
449 | ||
450 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) | |
451 | dlg.Destroy() | |
452 | return True | |
453 | ||
454 | elif id == SVNService.SVN_CHECKOUT_ID: | |
455 | config = wx.ConfigBase_Get() | |
456 | svnUrl = config.Read(SVN_REPOSITORY_URL, self._defaultURL) | |
457 | ||
458 | dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Checkout")) | |
459 | ||
460 | gridSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5) | |
461 | gridSizer.Add(wx.StaticText(dlg, -1, _("Repository URL:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE) | |
462 | svnURLText = wx.TextCtrl(dlg, -1, svnUrl, size = (200, -1)) | |
463 | svnURLText.SetToolTipString(svnUrl) | |
464 | gridSizer.Add(svnURLText, 0) | |
465 | ||
466 | gridSizer.Add(wx.StaticText(dlg, -1, _("Checkout to dir:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE) | |
467 | localPath = wx.TextCtrl(dlg, -1, size = (200, -1)) | |
468 | localPath.SetToolTipString(_("Path in local file system where files will be located.")) | |
469 | findDirButton = wx.Button(dlg, -1, _("Browse...")) | |
470 | ||
471 | def OnBrowseButton(event): | |
472 | dirDlg = wx.DirDialog(wx.GetApp().GetTopWindow(), _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE) | |
473 | dir = localPath.GetValue() | |
474 | if len(dir): | |
475 | dirDlg.SetPath(dir) | |
476 | if dirDlg.ShowModal() == wx.ID_OK: | |
477 | localPath.SetValue(dirDlg.GetPath()) | |
478 | localPath.SetToolTipString(localPath.GetValue()) | |
479 | localPath.SetInsertionPointEnd() | |
480 | ||
481 | dirDlg.Destroy() | |
482 | wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton) | |
483 | ||
484 | sizer = wx.BoxSizer(wx.HORIZONTAL) | |
485 | sizer.Add(localPath, 1, wx.EXPAND) | |
486 | sizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE) | |
487 | gridSizer.Add(sizer, 0) | |
488 | ||
489 | buttonSizer = wx.BoxSizer(wx.HORIZONTAL) | |
490 | okBtn = wx.Button(dlg, wx.ID_OK) | |
491 | okBtn.SetDefault() | |
492 | buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE) | |
493 | buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0) | |
494 | ||
495 | contentSizer = wx.BoxSizer(wx.VERTICAL) | |
496 | contentSizer.Add(gridSizer, 0, wx.ALL, SPACE) | |
497 | contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE) | |
498 | ||
499 | dlg.SetSizer(contentSizer) | |
500 | dlg.Fit() | |
501 | dlg.Layout() | |
502 | ||
503 | if dlg.ShowModal() == wx.ID_OK: | |
504 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) | |
505 | ||
506 | messageService = wx.GetApp().GetService(MessageService.MessageService) | |
507 | messageService.ShowWindow() | |
508 | ||
509 | view = messageService.GetView() | |
510 | view.ClearLines() | |
511 | view.AddLines(_("SVN Checkout:\n")) | |
512 | ||
513 | svnUrl = svnURLText.GetValue() | |
514 | toLocation = localPath.GetValue() | |
515 | try: | |
516 | self._client.checkout(svnUrl, toLocation) | |
517 | view.AddLines(_("Checkout completed.\n")) | |
518 | except pysvn.ClientError, e: | |
519 | view.AddLines(_("Checkout failed. %s\n") % str(e)) | |
520 | wx.MessageBox(_("Checkout failed. %s") % str(e), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION) | |
521 | except: | |
522 | extype, ex, tb = sys.exc_info() | |
523 | view.AddLines("Checkout failed: (%s) %s\n" % (extype, str(ex))) | |
524 | for line in traceback.format_tb(tb): | |
525 | view.AddLines(line) | |
526 | wx.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION) | |
527 | ||
528 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) | |
529 | dlg.Destroy() | |
530 | return True | |
531 | ||
532 | elif id == SVNService.SVN_REVERT_ID: | |
533 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) | |
534 | ||
535 | filenames = self.GetCurrentDocuments() | |
536 | ||
537 | messageService = wx.GetApp().GetService(MessageService.MessageService) | |
538 | messageService.ShowWindow() | |
539 | ||
540 | view = messageService.GetView() | |
541 | view.ClearLines() | |
542 | view.AddLines(_("SVN Revert:\n")) | |
543 | for filename in filenames: | |
544 | view.AddLines("%s\n" % filename) | |
545 | ||
546 | try: | |
547 | self._client.revert(filenames) | |
548 | view.AddLines(_("Revert completed.\n")) | |
549 | ||
550 | openDocs = wx.GetApp().GetDocumentManager().GetDocuments() | |
551 | for doc in openDocs[:]: # need to make a copy of the list otherwise ordinality changes as we close the files | |
552 | if doc.GetFilename() in filenames: | |
553 | yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(), | |
554 | _("Reverted file '%s' is currently open. Close it?") % os.path.basename(doc.GetFilename()), | |
555 | _("Close File"), | |
556 | wx.YES_NO|wx.ICON_QUESTION) | |
557 | if yesNoMsg.ShowModal() == wx.ID_YES: | |
558 | doc.DeleteAllViews() | |
559 | ||
560 | except pysvn.ClientError, e: | |
561 | view.AddLines("%s\n" % str(e)) | |
562 | wx.MessageBox(str(e), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION) | |
563 | except: | |
564 | extype, ex, tb = sys.exc_info() | |
565 | view.AddLines("Revert failed: (%s) %s\n" % (extype, str(ex))) | |
566 | for line in traceback.format_tb(tb): | |
567 | view.AddLines(line) | |
568 | wx.MessageBox(_("Revert failed."), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION) | |
569 | ||
570 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) | |
571 | return True | |
572 | ||
573 | elif id == SVNService.SVN_ADD_ID: | |
574 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) | |
575 | ||
576 | filenames = self.GetCurrentDocuments() | |
577 | ||
578 | messageService = wx.GetApp().GetService(MessageService.MessageService) | |
579 | messageService.ShowWindow() | |
580 | ||
581 | view = messageService.GetView() | |
582 | view.ClearLines() | |
583 | view.AddLines(_("SVN Add:\n")) | |
584 | for filename in filenames: | |
585 | view.AddLines("%s\n" % filename) | |
586 | ||
587 | try: | |
588 | self._client.add(filenames) | |
589 | view.AddLines(_("Add completed.\n")) | |
590 | except pysvn.ClientError, e: | |
591 | view.AddLines("%s\n" % str(e)) | |
592 | wx.MessageBox(str(e), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION) | |
593 | except: | |
594 | extype, ex, tb = sys.exc_info() | |
595 | view.AddLines("Add failed: (%s) %s\n" % (extype, str(ex))) | |
596 | for line in traceback.format_tb(tb): | |
597 | view.AddLines(line) | |
598 | wx.MessageBox(_("Add failed."), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION) | |
599 | ||
600 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) | |
601 | return True | |
602 | ||
603 | elif id == SVNService.SVN_DELETE_ID: | |
604 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) | |
605 | ||
606 | filenames = self.GetCurrentDocuments() | |
607 | ||
608 | messageService = wx.GetApp().GetService(MessageService.MessageService) | |
609 | messageService.ShowWindow() | |
610 | ||
611 | view = messageService.GetView() | |
612 | view.ClearLines() | |
613 | view.AddLines(_("SVN Delete:\n")) | |
614 | for filename in filenames: | |
615 | view.AddLines("%s\n" % filename) | |
616 | ||
617 | try: | |
618 | self._client.remove(filenames) | |
619 | view.AddLines(_("Delete completed.\n")) | |
620 | except pysvn.ClientError, e: | |
621 | view.AddLines("%s\n" % str(e)) | |
622 | wx.MessageBox(str(e), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION) | |
623 | except: | |
624 | extype, ex, tb = sys.exc_info() | |
625 | view.AddLines("Delete failed: (%s) %s\n" % (extype, str(ex))) | |
626 | for line in traceback.format_tb(tb): | |
627 | view.AddLines(line) | |
628 | wx.MessageBox(_("Delete failed."), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION) | |
629 | ||
630 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) | |
631 | return True | |
632 | ||
633 | return False | |
634 | ||
635 | ||
636 | def ProcessUpdateUIEvent(self, event): | |
637 | id = event.GetId() | |
638 | ||
639 | if id in [SVNService.SVN_UPDATE_ID, | |
640 | SVNService.SVN_CHECKIN_ID, | |
641 | SVNService.SVN_REVERT_ID, | |
642 | SVNService.SVN_ADD_ID, | |
643 | SVNService.SVN_DELETE_ID]: | |
644 | if self.GetCurrentDocuments(): | |
645 | event.Enable(True) | |
646 | else: | |
647 | event.Enable(False) | |
648 | return True | |
649 | ||
650 | elif id == SVNService.SVN_CHECKOUT_ID: | |
651 | event.Enable(True) | |
652 | return True | |
653 | ||
654 | return False | |
655 | ||
656 | ||
657 | def GetCurrentDocuments(self): | |
658 | ||
659 | projectService = wx.GetApp().GetService(ProjectEditor.ProjectService) | |
660 | if projectService: | |
661 | projView = projectService.GetView() | |
662 | ||
663 | if projView.HasFocus(): | |
664 | filenames = projView.GetSelectedFiles() | |
665 | if len(filenames): | |
666 | return filenames | |
667 | else: | |
668 | return None | |
669 | ||
670 | doc = wx.GetApp().GetTopWindow().GetDocumentManager().GetCurrentDocument() | |
671 | if doc: | |
672 | filenames = [doc.GetFilename()] | |
673 | else: | |
674 | filenames = None | |
675 | ||
676 | return filenames | |
677 | ||
678 | ||
679 | class SVNOptionsPanel(wx.Panel): | |
680 | ||
681 | ||
682 | def __init__(self, parent, id): | |
683 | wx.Panel.__init__(self, parent, id) | |
684 | ||
685 | config = wx.ConfigBase_Get() | |
686 | svnService = wx.GetApp().GetService(SVNService) | |
687 | svnUrl = config.Read(SVN_REPOSITORY_URL, svnService._defaultURL) | |
688 | configDir = config.Read(SVN_CONFIG_DIR, "") | |
689 | ||
690 | borderSizer = wx.BoxSizer(wx.VERTICAL) | |
691 | sizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5) | |
692 | ||
693 | sizer.Add(wx.StaticText(self, -1, _("SVN Config Dir:")), 0, wx.ALIGN_CENTER_VERTICAL) | |
694 | ||
695 | self._svnConfigDir = wx.TextCtrl(self, -1, configDir, size = (200, -1)) | |
696 | if configDir == "": | |
697 | self._svnConfigDir.SetToolTipString(_("Path Subversion configuration directory.")) | |
698 | else: | |
699 | self._svnConfigDir.SetToolTipString(configDir) | |
700 | ||
701 | findDirButton = wx.Button(self, -1, _("Browse...")) | |
702 | ||
703 | def OnBrowseButton(event): | |
704 | dirDlg = wx.DirDialog(self, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE) | |
705 | dir = self._svnConfigDir.GetValue() | |
706 | if len(dir): | |
707 | dirDlg.SetPath(dir) | |
708 | if dirDlg.ShowModal() == wx.ID_OK: | |
709 | self._svnConfigDir.SetValue(dirDlg.GetPath()) | |
710 | self._svnConfigDir.SetToolTipString(self._svnConfigDir.GetValue()) | |
711 | self._svnConfigDir.SetInsertionPointEnd() | |
712 | ||
713 | dirDlg.Destroy() | |
714 | wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton) | |
715 | ||
716 | hsizer = wx.BoxSizer(wx.HORIZONTAL) | |
717 | hsizer.Add(self._svnConfigDir, 1, wx.EXPAND) | |
718 | hsizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE) | |
719 | sizer.Add(hsizer, 0) | |
720 | ||
721 | sizer.Add(wx.StaticText(self, -1, _("SVN URL:")), 0, wx.ALIGN_CENTER_VERTICAL) | |
722 | self._svnURLText = wx.TextCtrl(self, -1, svnUrl, size = (200, -1)) | |
723 | self._svnURLText.SetToolTipString(svnUrl) | |
724 | sizer.Add(self._svnURLText, 0) | |
725 | ||
726 | borderSizer.Add(sizer, 0, wx.ALL, SPACE) | |
727 | self.SetSizer(borderSizer) | |
728 | self.Layout() | |
729 | parent.AddPage(self, _("SVN")) | |
730 | ||
731 | ||
732 | def OnOK(self, optionsDialog): | |
733 | config = wx.ConfigBase_Get() | |
734 | config.Write(SVN_CONFIG_DIR, self._svnConfigDir.GetValue()) | |
735 | config.Write(SVN_REPOSITORY_URL, self._svnURLText.GetValue()) | |
736 |