|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2005 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the base class of the VCS project helper. |
|
8 """ |
|
9 |
|
10 import os |
|
11 import sys |
|
12 import shutil |
|
13 import copy |
|
14 |
|
15 from PyQt4.QtCore import * |
|
16 from PyQt4.QtGui import * |
|
17 |
|
18 from E4Gui.E4Application import e4App |
|
19 |
|
20 import VCS |
|
21 from CommandOptionsDialog import vcsCommandOptionsDialog |
|
22 from RepositoryInfoDialog import VcsRepositoryInfoDialog |
|
23 |
|
24 from E4Gui.E4Action import E4Action |
|
25 |
|
26 import Utilities |
|
27 import Preferences |
|
28 |
|
29 class VcsProjectHelper(QObject): |
|
30 """ |
|
31 Class implementing the base class of the VCS project helper. |
|
32 """ |
|
33 def __init__(self, vcsObject, projectObject, parent = None, name = None): |
|
34 """ |
|
35 Constructor |
|
36 |
|
37 @param vcsObject reference to the vcs object |
|
38 @param projectObject reference to the project object |
|
39 @param parent parent widget (QWidget) |
|
40 @param name name of this object (string) |
|
41 """ |
|
42 QObject.__init__(self, parent) |
|
43 if name: |
|
44 self.setObjectName(name) |
|
45 |
|
46 self.vcs = vcsObject |
|
47 self.project = projectObject |
|
48 |
|
49 self.actions = [] |
|
50 |
|
51 self.initActions() |
|
52 |
|
53 def setObjects(self, vcsObject, projectObject): |
|
54 """ |
|
55 Public method to set references to the vcs and project objects. |
|
56 |
|
57 @param vcsObject reference to the vcs object |
|
58 @param projectObject reference to the project object |
|
59 """ |
|
60 self.vcs = vcsObject |
|
61 self.project = projectObject |
|
62 |
|
63 def initActions(self): |
|
64 """ |
|
65 Public method to generate the action objects. |
|
66 """ |
|
67 self.vcsNewAct = E4Action(self.trUtf8('New from repository'), |
|
68 self.trUtf8('&New from repository...'), 0, 0, self, 'vcs_new') |
|
69 self.vcsNewAct.setStatusTip(self.trUtf8( |
|
70 'Create a new project from the VCS repository' |
|
71 )) |
|
72 self.vcsNewAct.setWhatsThis(self.trUtf8( |
|
73 """<b>New from repository</b>""" |
|
74 """<p>This creates a new local project from the VCS repository.</p>""" |
|
75 )) |
|
76 self.connect(self.vcsNewAct, SIGNAL('triggered()'), self._vcsCheckout) |
|
77 self.actions.append(self.vcsNewAct) |
|
78 |
|
79 self.vcsExportAct = E4Action(self.trUtf8('Export from repository'), |
|
80 self.trUtf8('&Export from repository...'), 0, 0, self, 'vcs_export') |
|
81 self.vcsExportAct.setStatusTip(self.trUtf8( |
|
82 'Export a project from the repository' |
|
83 )) |
|
84 self.vcsExportAct.setWhatsThis(self.trUtf8( |
|
85 """<b>Export from repository</b>""" |
|
86 """<p>This exports a project from the repository.</p>""" |
|
87 )) |
|
88 self.connect(self.vcsExportAct, SIGNAL('triggered()'), self._vcsExport) |
|
89 self.actions.append(self.vcsExportAct) |
|
90 |
|
91 self.vcsAddAct = E4Action(self.trUtf8('Add to repository'), |
|
92 self.trUtf8('&Add to repository...'), 0, 0, self, 'vcs_add') |
|
93 self.vcsAddAct.setStatusTip(self.trUtf8( |
|
94 'Add the local project to the VCS repository' |
|
95 )) |
|
96 self.vcsAddAct.setWhatsThis(self.trUtf8( |
|
97 """<b>Add to repository</b>""" |
|
98 """<p>This adds (imports) the local project to the VCS repository.</p>""" |
|
99 )) |
|
100 self.connect(self.vcsAddAct, SIGNAL('triggered()'), self._vcsImport) |
|
101 self.actions.append(self.vcsAddAct) |
|
102 |
|
103 def initMenu(self, menu): |
|
104 """ |
|
105 Public method to generate the VCS menu. |
|
106 |
|
107 @param menu reference to the menu to be populated (QMenu) |
|
108 """ |
|
109 menu.clear() |
|
110 |
|
111 menu.addAction(self.vcsNewAct) |
|
112 menu.addAction(self.vcsExportAct) |
|
113 menu.addSeparator() |
|
114 menu.addAction(self.vcsAddAct) |
|
115 menu.addSeparator() |
|
116 |
|
117 def showMenu(self): |
|
118 """ |
|
119 Public slot called before the vcs menu is shown. |
|
120 """ |
|
121 self.vcsAddAct.setEnabled(self.project.isOpen()) |
|
122 |
|
123 def _vcsCheckout(self, export = False): |
|
124 """ |
|
125 Protected slot used to create a local project from the repository. |
|
126 |
|
127 @param export flag indicating whether an export or a checkout |
|
128 should be performed |
|
129 """ |
|
130 if not self.project.checkDirty(): |
|
131 return |
|
132 |
|
133 vcsSystemsDict = e4App().getObject("PluginManager")\ |
|
134 .getPluginDisplayStrings("version_control") |
|
135 vcsSystemsDisplay = [] |
|
136 keys = sorted(vcsSystemsDict.keys()) |
|
137 for key in keys: |
|
138 vcsSystemsDisplay.append(vcsSystemsDict[key]) |
|
139 vcsSelected, ok = QInputDialog.getItem(\ |
|
140 None, |
|
141 self.trUtf8("New Project"), |
|
142 self.trUtf8("Select version control system for the project"), |
|
143 vcsSystemsDisplay, |
|
144 0, False) |
|
145 if not ok: |
|
146 return |
|
147 for vcsSystem, vcsSystemDisplay in vcsSystemsDict.items(): |
|
148 if vcsSystemDisplay == vcsSelected: |
|
149 break |
|
150 |
|
151 self.project.pdata["VCS"] = [vcsSystem] |
|
152 self.project.vcs = self.project.initVCS(vcsSystem) |
|
153 if self.project.vcs is not None: |
|
154 vcsdlg = self.project.vcs.vcsNewProjectOptionsDialog() |
|
155 if vcsdlg.exec_() == QDialog.Accepted: |
|
156 self.project.closeProject() |
|
157 projectdir, vcsDataDict = vcsdlg.getData() |
|
158 self.project.pdata["VCS"] = [vcsSystem] |
|
159 self.project.vcs = self.project.initVCS(vcsSystem) |
|
160 # edit VCS command options |
|
161 vcores = QMessageBox.question(None, |
|
162 self.trUtf8("New Project"), |
|
163 self.trUtf8("""Would you like to edit the VCS command options?"""), |
|
164 QMessageBox.StandardButtons(\ |
|
165 QMessageBox.No | \ |
|
166 QMessageBox.Yes), |
|
167 QMessageBox.No) |
|
168 if vcores == QMessageBox.Yes: |
|
169 codlg = vcsCommandOptionsDialog(self.project.vcs) |
|
170 if codlg.exec_() == QDialog.Accepted: |
|
171 self.project.vcs.vcsSetOptions(codlg.getOptions()) |
|
172 |
|
173 # create the project directory if it doesn't exist already |
|
174 if not os.path.isdir(projectdir): |
|
175 try: |
|
176 os.makedirs(projectdir) |
|
177 except EnvironmentError: |
|
178 QMessageBox.critical(None, |
|
179 self.trUtf8("Create project directory"), |
|
180 self.trUtf8("<p>The project directory <b>{0}</b> could not" |
|
181 " be created.</p>").format(projectdir)) |
|
182 self.project.pdata["VCS"] = ['None'] |
|
183 self.project.vcs = self.project.initVCS() |
|
184 return |
|
185 |
|
186 # create the project from the VCS |
|
187 self.project.vcs.vcsSetDataFromDict(vcsDataDict) |
|
188 if export: |
|
189 ok = self.project.vcs.vcsExport(vcsDataDict, projectdir) |
|
190 else: |
|
191 ok = self.project.vcs.vcsCheckout(vcsDataDict, projectdir, False) |
|
192 if ok: |
|
193 projectdir = os.path.normpath(projectdir) |
|
194 filters = ["*.e4p", "*.e4pz", "*.e3p", "*.e3pz"] |
|
195 d = QDir(projectdir) |
|
196 plist = d.entryInfoList(filters) |
|
197 if len(plist): |
|
198 if len(plist) == 1: |
|
199 self.project.openProject(plist[0].absoluteFilePath()) |
|
200 self.project.emit(SIGNAL('newProject')) |
|
201 else: |
|
202 pfilenamelist = d.entryList(filters) |
|
203 pfilename, ok = QInputDialog.getItem( |
|
204 None, |
|
205 self.trUtf8("New project from repository"), |
|
206 self.trUtf8("Select a project file to open."), |
|
207 pfilenamelist, 0, False) |
|
208 if ok: |
|
209 self.project.openProject(\ |
|
210 QFileInfo(d, pfilename).absoluteFilePath()) |
|
211 self.project.emit(SIGNAL('newProject')) |
|
212 if export: |
|
213 self.project.pdata["VCS"] = ['None'] |
|
214 self.project.vcs = self.project.initVCS() |
|
215 self.project.setDirty(True) |
|
216 self.project.saveProject() |
|
217 else: |
|
218 res = QMessageBox.question(None, |
|
219 self.trUtf8("New project from repository"), |
|
220 self.trUtf8("The project retrieved from the repository" |
|
221 " does not contain an eric project file" |
|
222 " (*.e4p *.e4pz *.e3p *.e3pz)." |
|
223 " Create it?"), |
|
224 QMessageBox.StandardButtons(\ |
|
225 QMessageBox.No | \ |
|
226 QMessageBox.Yes), |
|
227 QMessageBox.Yes) |
|
228 if res == QMessageBox.Yes: |
|
229 self.project.ppath = projectdir |
|
230 self.project.opened = True |
|
231 |
|
232 from Project.PropertiesDialog import PropertiesDialog |
|
233 dlg = PropertiesDialog(self.project, False) |
|
234 if dlg.exec_() == QDialog.Accepted: |
|
235 dlg.storeData() |
|
236 self.project.initFileTypes() |
|
237 self.project.setDirty(True) |
|
238 try: |
|
239 ms = os.path.join(self.project.ppath, |
|
240 self.project.pdata["MAINSCRIPT"][0]) |
|
241 if os.path.exists(ms): |
|
242 self.project.appendFile(ms) |
|
243 except IndexError: |
|
244 ms = "" |
|
245 self.project.newProjectAddFiles(ms) |
|
246 self.project.saveProject() |
|
247 self.project.openProject(self.project.pfile) |
|
248 if not export: |
|
249 res = QMessageBox.question(None, |
|
250 self.trUtf8("New project from repository"), |
|
251 self.trUtf8("Shall the project file be added to" |
|
252 " the repository?"), |
|
253 QMessageBox.StandardButtons(\ |
|
254 QMessageBox.No | \ |
|
255 QMessageBox.Yes), |
|
256 QMessageBox.Yes) |
|
257 if res == QMessageBox.Yes: |
|
258 self.project.vcs.vcsAdd(self.project.pfile) |
|
259 else: |
|
260 QMessageBox.critical(None, |
|
261 self.trUtf8("New project from repository"), |
|
262 self.trUtf8("""The project could not be retrieved from""" |
|
263 """ the repository.""")) |
|
264 self.project.pdata["VCS"] = ['None'] |
|
265 self.project.vcs = self.project.initVCS() |
|
266 else: |
|
267 self.project.pdata["VCS"] = ['None'] |
|
268 self.project.vcs = self.project.initVCS() |
|
269 |
|
270 def _vcsExport(self): |
|
271 """ |
|
272 Protected slot used to export a project from the repository. |
|
273 """ |
|
274 self._vcsCheckout(True) |
|
275 |
|
276 def _vcsImport(self): |
|
277 """ |
|
278 Protected slot used to import the local project into the repository. |
|
279 |
|
280 <b>NOTE</b>: |
|
281 This does not necessarily make the local project a vcs controlled |
|
282 project. You may have to checkout the project from the repository in |
|
283 order to accomplish that. |
|
284 """ |
|
285 def revertChanges(): |
|
286 """ |
|
287 Local function to revert the changes made to the project object. |
|
288 """ |
|
289 self.project.pdata["VCS"] = pdata_vcs[:] |
|
290 self.project.pdata["VCSOPTIONS"] = copy.deepcopy(pdata_vcsoptions) |
|
291 self.project.pdata["VCSOTHERDATA"] = copy.deepcopy(pdata_vcsother) |
|
292 self.project.vcs = vcs |
|
293 self.project.vcsProjectHelper = vcsHelper |
|
294 self.project.vcsBasicHelper = vcs is None |
|
295 self.initMenu(self.project.vcsMenu) |
|
296 self.project.setDirty(True) |
|
297 self.project.saveProject() |
|
298 |
|
299 pdata_vcs = self.project.pdata["VCS"][:] |
|
300 pdata_vcsoptions = copy.deepcopy(self.project.pdata["VCSOPTIONS"]) |
|
301 pdata_vcsother = copy.deepcopy(self.project.pdata["VCSOTHERDATA"]) |
|
302 vcs = self.project.vcs |
|
303 vcsHelper = self.project.vcsProjectHelper |
|
304 vcsSystemsDict = e4App().getObject("PluginManager")\ |
|
305 .getPluginDisplayStrings("version_control") |
|
306 vcsSystemsDisplay = [] |
|
307 keys = vcsSystemsDict.keys() |
|
308 keys.sort() |
|
309 for key in keys: |
|
310 vcsSystemsDisplay.append(vcsSystemsDict[key]) |
|
311 vcsSelected, ok = QInputDialog.getItem(\ |
|
312 None, |
|
313 self.trUtf8("Import Project"), |
|
314 self.trUtf8("Select version control system for the project"), |
|
315 vcsSystemsDisplay, |
|
316 0, False) |
|
317 if not ok: |
|
318 return |
|
319 for vcsSystem, vcsSystemDisplay in vcsSystemsDict.items(): |
|
320 if vcsSystemDisplay == vcsSelected: |
|
321 break |
|
322 |
|
323 self.project.pdata["VCS"] = [vcsSystem] |
|
324 self.project.vcs = self.project.initVCS(vcsSystem) |
|
325 if self.project.vcs is not None: |
|
326 vcsdlg = self.project.vcs.vcsOptionsDialog(self.project, self.project.name, 1) |
|
327 if vcsdlg.exec_() == QDialog.Accepted: |
|
328 vcsDataDict = vcsdlg.getData() |
|
329 # edit VCS command options |
|
330 vcores = QMessageBox.question(None, |
|
331 self.trUtf8("Import Project"), |
|
332 self.trUtf8("""Would you like to edit the VCS command options?"""), |
|
333 QMessageBox.StandardButtons(\ |
|
334 QMessageBox.No | \ |
|
335 QMessageBox.Yes), |
|
336 QMessageBox.No) |
|
337 if vcores == QMessageBox.Yes: |
|
338 codlg = vcsCommandOptionsDialog(self.project.vcs) |
|
339 if codlg.exec_() == QDialog.Accepted: |
|
340 self.project.vcs.vcsSetOptions(codlg.getOptions()) |
|
341 self.project.setDirty(True) |
|
342 self.project.vcs.vcsSetDataFromDict(vcsDataDict) |
|
343 self.project.saveProject() |
|
344 isVcsControlled = \ |
|
345 self.project.vcs.vcsImport(vcsDataDict, self.project.ppath)[1] |
|
346 if isVcsControlled: |
|
347 # reopen the project |
|
348 self.project.openProject(self.project.pfile) |
|
349 else: |
|
350 # revert the changes to the local project |
|
351 # because the project dir is not a VCS directory |
|
352 revertChanges() |
|
353 else: |
|
354 # revert the changes because user cancelled |
|
355 revertChanges() |
|
356 |
|
357 def _vcsUpdate(self): |
|
358 """ |
|
359 Protected slot used to update the local project from the repository. |
|
360 """ |
|
361 shouldReopen = self.vcs.vcsUpdate(self.project.ppath) |
|
362 if shouldReopen: |
|
363 res = QMessageBox.information(None, |
|
364 self.trUtf8("Update"), |
|
365 self.trUtf8("""The project should be reread. Do this now?"""), |
|
366 QMessageBox.StandardButtons(\ |
|
367 QMessageBox.No | \ |
|
368 QMessageBox.Yes), |
|
369 QMessageBox.Yes) |
|
370 if res == QMessageBox.Yes: |
|
371 self.project.reopenProject() |
|
372 |
|
373 def _vcsCommit(self): |
|
374 """ |
|
375 Protected slot used to commit changes to the local project to the repository. |
|
376 """ |
|
377 if Preferences.getVCS("AutoSaveProject"): |
|
378 self.project.saveProject() |
|
379 if Preferences.getVCS("AutoSaveFiles"): |
|
380 self.project.saveAllScripts() |
|
381 self.vcs.vcsCommit(self.project.ppath, '') |
|
382 |
|
383 def _vcsRemove(self): |
|
384 """ |
|
385 Protected slot used to remove the local project from the repository. |
|
386 |
|
387 Depending on the parameters set in the vcs object the project |
|
388 may be removed from the local disk as well. |
|
389 """ |
|
390 res = QMessageBox.warning(None, |
|
391 self.trUtf8("Remove project from repository"), |
|
392 self.trUtf8("Dou you really want to remove this project from" |
|
393 " the repository (and disk)?"), |
|
394 QMessageBox.StandardButtons(\ |
|
395 QMessageBox.No | \ |
|
396 QMessageBox.Yes), |
|
397 QMessageBox.No) |
|
398 if res == QMessageBox.Yes: |
|
399 self.vcs.vcsRemove(self.project.ppath, True) |
|
400 self._vcsCommit() |
|
401 if not os.path.exists(self.project.pfile): |
|
402 ppath = self.project.ppath |
|
403 self.setDirty(False) |
|
404 self.project.closeProject() |
|
405 shutil.rmtree(ppath, True) |
|
406 |
|
407 def _vcsCommandOptions(self): |
|
408 """ |
|
409 Protected slot to edit the VCS command options. |
|
410 """ |
|
411 codlg = vcsCommandOptionsDialog(self.vcs) |
|
412 if codlg.exec_() == QDialog.Accepted: |
|
413 self.vcs.vcsSetOptions(codlg.getOptions()) |
|
414 self.project.setDirty(True) |
|
415 |
|
416 def _vcsLog(self): |
|
417 """ |
|
418 Protected slot used to show the log of the local project. |
|
419 """ |
|
420 self.vcs.vcsLog(self.project.ppath) |
|
421 |
|
422 def _vcsDiff(self): |
|
423 """ |
|
424 Protected slot used to show the difference of the local project to the repository. |
|
425 """ |
|
426 self.vcs.vcsDiff(self.project.ppath) |
|
427 |
|
428 def _vcsStatus(self): |
|
429 """ |
|
430 Protected slot used to show the status of the local project. |
|
431 """ |
|
432 self.vcs.vcsStatus(self.project.ppath) |
|
433 |
|
434 def _vcsTag(self): |
|
435 """ |
|
436 Protected slot used to tag the local project in the repository. |
|
437 """ |
|
438 self.vcs.vcsTag(self.project.ppath) |
|
439 |
|
440 def _vcsRevert(self): |
|
441 """ |
|
442 Protected slot used to revert changes made to the local project. |
|
443 """ |
|
444 self.vcs.vcsRevert(self.project.ppath) |
|
445 |
|
446 def _vcsSwitch(self): |
|
447 """ |
|
448 Protected slot used to switch the local project to another tag/branch. |
|
449 """ |
|
450 self.vcs.vcsSwitch(self.project.ppath) |
|
451 |
|
452 def _vcsMerge(self): |
|
453 """ |
|
454 Protected slot used to merge changes of a tag/revision into the local project. |
|
455 """ |
|
456 self.vcs.vcsMerge(self.project.ppath) |
|
457 |
|
458 def _vcsCleanup(self): |
|
459 """ |
|
460 Protected slot used to cleanup the local project. |
|
461 """ |
|
462 self.vcs.vcsCleanup(self.project.ppath) |
|
463 |
|
464 def _vcsCommand(self): |
|
465 """ |
|
466 Protected slot used to execute an arbitrary vcs command. |
|
467 """ |
|
468 self.vcs.vcsCommandLine(self.project.ppath) |
|
469 |
|
470 def _vcsInfoDisplay(self): |
|
471 """ |
|
472 Protected slot called to show some vcs information. |
|
473 """ |
|
474 info = self.vcs.vcsRepositoryInfos(self.project.ppath) |
|
475 dlg = VcsRepositoryInfoDialog(None, info) |
|
476 dlg.exec_() |