|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2002 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the project management functionality. |
|
8 """ |
|
9 |
|
10 import os |
|
11 import sys |
|
12 import re |
|
13 import time |
|
14 import shutil |
|
15 import glob |
|
16 import fnmatch |
|
17 import copy |
|
18 import zipfile |
|
19 import cStringIO |
|
20 |
|
21 from PyQt4.QtCore import * |
|
22 from PyQt4.QtGui import * |
|
23 |
|
24 from E4Gui.E4Application import e4App |
|
25 |
|
26 from Globals import recentNameProject |
|
27 |
|
28 from ProjectBrowserModel import ProjectBrowserModel |
|
29 |
|
30 from AddLanguageDialog import AddLanguageDialog |
|
31 from AddFileDialog import AddFileDialog |
|
32 from AddDirectoryDialog import AddDirectoryDialog |
|
33 from PropertiesDialog import PropertiesDialog |
|
34 from AddFoundFilesDialog import AddFoundFilesDialog |
|
35 from DebuggerPropertiesDialog import DebuggerPropertiesDialog |
|
36 from FiletypeAssociationDialog import FiletypeAssociationDialog |
|
37 from LexerAssociationDialog import LexerAssociationDialog |
|
38 from UserPropertiesDialog import UserPropertiesDialog |
|
39 |
|
40 from E4XML.XMLUtilities import make_parser |
|
41 from E4XML.XMLErrorHandler import XMLErrorHandler, XMLFatalParseError |
|
42 from E4XML.XMLEntityResolver import XMLEntityResolver |
|
43 |
|
44 from E4XML.ProjectHandler import ProjectHandler |
|
45 from E4XML.ProjectWriter import ProjectWriter |
|
46 from E4XML.UserProjectHandler import UserProjectHandler |
|
47 from E4XML.UserProjectWriter import UserProjectWriter |
|
48 from E4XML.SessionHandler import SessionHandler |
|
49 from E4XML.SessionWriter import SessionWriter |
|
50 from E4XML.TasksHandler import TasksHandler |
|
51 from E4XML.TasksWriter import TasksWriter |
|
52 from E4XML.DebuggerPropertiesHandler import DebuggerPropertiesHandler |
|
53 from E4XML.DebuggerPropertiesWriter import DebuggerPropertiesWriter |
|
54 |
|
55 import VCS |
|
56 from VCS.CommandOptionsDialog import vcsCommandOptionsDialog |
|
57 from VCS.ProjectHelper import VcsProjectHelper |
|
58 |
|
59 from Graphics.ApplicationDiagram import ApplicationDiagram |
|
60 |
|
61 from DataViews.CodeMetricsDialog import CodeMetricsDialog |
|
62 from DataViews.PyCoverageDialog import PyCoverageDialog |
|
63 from DataViews.PyProfileDialog import PyProfileDialog |
|
64 |
|
65 import UI.PixmapCache |
|
66 |
|
67 from E4Gui.E4Action import E4Action, createActionGroup |
|
68 |
|
69 import Preferences |
|
70 import Utilities |
|
71 |
|
72 from eric4config import getConfig |
|
73 |
|
74 class Project(QObject): |
|
75 """ |
|
76 Class implementing the project management functionality. |
|
77 |
|
78 @signal dirty(int) emitted when the dirty state changes |
|
79 @signal projectSessionLoaded() emitted after a project session file was loaded |
|
80 @signal projectLanguageAdded(string) emitted after a new language was added |
|
81 @signal projectLanguageAddedByCode(string) emitted after a new language was added. |
|
82 The language code is sent by this signal. |
|
83 @signal projectFormAdded(string) emitted after a new form was added |
|
84 @signal projectSourceAdded(string) emitted after a new source file was added |
|
85 @signal projectInterfaceAdded(string) emitted after a new IDL file was added |
|
86 @signal projectResourceAdded(string) emitted after a new resource file was added |
|
87 @signal projectAboutToBeCreated() emitted just before the project will be created |
|
88 @signal newProjectHooks() emitted after a new project was generated but before |
|
89 the newProject() signal is sent |
|
90 @signal newProject() emitted after a new project was generated |
|
91 @signal sourceFile(string) emitted after a project file was read to |
|
92 open the main script |
|
93 @signal projectOpenedHooks() emitted after a project file was read but before the |
|
94 projectOpened() signal is sent |
|
95 @signal projectOpened() emitted after a project file was read |
|
96 @signal projectClosedHooks() emitted after a project file was clsoed but before the |
|
97 projectClosed() signal is sent |
|
98 @signal projectClosed() emitted after a project was closed |
|
99 @signal projectOthersAdded(string) emitted after a file or directory was added |
|
100 to the OTHERS project data area |
|
101 @signal projectFileRenamed(string, string) emitted after a file of the project |
|
102 has been renamed |
|
103 @signal projectPropertiesChanged() emitted after the project properties were changed |
|
104 @signal directoryRemoved(string) emitted after a directory has been removed from |
|
105 the project |
|
106 @signal prepareRepopulateItem(string) emitted before an item of the model is |
|
107 repopulated |
|
108 @signal completeRepopulateItem(string) emitted after an item of the model was |
|
109 repopulated |
|
110 @signal vcsStatusMonitorStatus(QString, QString) emitted to signal the status of the |
|
111 monitoring thread (ok, nok, op, off) and a status message |
|
112 @signal reinitVCS() emitted after the VCS has been reinitialized |
|
113 @signal showMenu(string, QMenu) emitted when a menu is about to be shown. The name |
|
114 of the menu and a reference to the menu are given. |
|
115 @signal lexerAssociationsChanged() emitted after the lexer associations have been |
|
116 changed |
|
117 """ |
|
118 keynames = [ |
|
119 "PROGLANGUAGE", "MIXEDLANGUAGE", "PROJECTTYPE", |
|
120 "SPELLLANGUAGE", "SPELLWORDS", "SPELLEXCLUDES", |
|
121 "DESCRIPTION", "VERSION", |
|
122 "AUTHOR", "EMAIL", |
|
123 "SOURCES", "FORMS", "RESOURCES", |
|
124 "TRANSLATIONS", "TRANSLATIONPATTERN", "TRANSLATIONSBINPATH", |
|
125 "TRANSLATIONEXCEPTIONS", |
|
126 "MAINSCRIPT", |
|
127 "VCS", "VCSOPTIONS", "VCSOTHERDATA", |
|
128 "OTHERS", "INTERFACES", |
|
129 "FILETYPES", "LEXERASSOCS", |
|
130 "PROJECTTYPESPECIFICDATA", |
|
131 "DOCUMENTATIONPARMS", |
|
132 "PACKAGERSPARMS", |
|
133 "CHECKERSPARMS", |
|
134 "OTHERTOOLSPARMS", |
|
135 ] |
|
136 |
|
137 dbgKeynames = [ |
|
138 "INTERPRETER", "DEBUGCLIENT", |
|
139 "ENVIRONMENTOVERRIDE", "ENVIRONMENTSTRING", |
|
140 "REMOTEDEBUGGER", "REMOTEHOST", "REMOTECOMMAND", |
|
141 "PATHTRANSLATION", "REMOTEPATH", "LOCALPATH", |
|
142 "CONSOLEDEBUGGER", "CONSOLECOMMAND", |
|
143 "REDIRECT", "NOENCODING", |
|
144 ] |
|
145 |
|
146 userKeynames = [ |
|
147 "VCSOVERRIDE", "VCSSTATUSMONITORINTERVAL", |
|
148 ] |
|
149 |
|
150 def __init__(self, parent = None, filename = None): |
|
151 """ |
|
152 Constructor |
|
153 |
|
154 @param parent parent widget (usually the ui object) (QWidget) |
|
155 @param filename optional filename of a project file to open (string) |
|
156 """ |
|
157 QObject.__init__(self, parent) |
|
158 |
|
159 self.ui = parent |
|
160 |
|
161 self.sourceExtensions = { |
|
162 "Python" : ['.py', '.ptl', '.pyw'], |
|
163 "Python3" : ['.py', '.pyw'], |
|
164 "Ruby" : ['.rb'], |
|
165 "Mixed" : ['.py', '.ptl', '.rb'] |
|
166 } |
|
167 |
|
168 self.dbgFilters = { |
|
169 "Python" : self.trUtf8(\ |
|
170 "Python Files (*.py);;" |
|
171 "Python GUI Files (*.pyw);;"), |
|
172 "Python3" : self.trUtf8(\ |
|
173 "Python3 Files (*.py3);;" |
|
174 "Python3 GUI Files (*.pyw3);;"), |
|
175 "Ruby" : self.trUtf8("Ruby Files (*.rb);;"), |
|
176 } |
|
177 |
|
178 self.vcsMenu = None |
|
179 |
|
180 self.__initProjectTypes() |
|
181 |
|
182 self.__initData() |
|
183 |
|
184 self.recent = [] |
|
185 self.__loadRecent() |
|
186 |
|
187 if filename is not None: |
|
188 self.openProject(filename) |
|
189 else: |
|
190 self.vcs = self.initVCS() |
|
191 |
|
192 self.__model = ProjectBrowserModel(self) |
|
193 |
|
194 self.codemetrics = None |
|
195 self.codecoverage = None |
|
196 self.profiledata = None |
|
197 self.applicationDiagram = None |
|
198 |
|
199 def __initProjectTypes(self): |
|
200 """ |
|
201 Private method to initialize the list of supported project types. |
|
202 """ |
|
203 self.__projectTypes = {} |
|
204 self.__fileTypeCallbacks = {} |
|
205 self.__lexerAssociationCallbacks = {} |
|
206 self.__binaryTranslationsCallbacks = {} |
|
207 self.__projectTypes["Qt4"] = self.trUtf8("Qt4 GUI") |
|
208 self.__projectTypes["Qt4C"] = self.trUtf8("Qt4 Console") |
|
209 self.__projectTypes["E4Plugin"] = self.trUtf8("Eric4 Plugin") |
|
210 self.__projectTypes["Console"] = self.trUtf8("Console") |
|
211 self.__projectTypes["Other"] = self.trUtf8("Other") |
|
212 try: |
|
213 import PySide |
|
214 self.__projectTypes["PySide"] = self.trUtf8("PySide GUI") |
|
215 self.__projectTypes["PySideC"] = self.trUtf8("PySide Console") |
|
216 del PySide |
|
217 except ImportError: |
|
218 pass |
|
219 |
|
220 def getProjectTypes(self): |
|
221 """ |
|
222 Public method to get the list of supported project types. |
|
223 |
|
224 @return reference to the dictionary of project types. |
|
225 """ |
|
226 return self.__projectTypes |
|
227 |
|
228 def hasProjectType(self, type_): |
|
229 """ |
|
230 Public method to check, if a project type is already registered. |
|
231 |
|
232 @param type_ internal type designator to be unregistered (string) |
|
233 """ |
|
234 return type_ in self.__projectTypes |
|
235 |
|
236 def registerProjectType(self, type_, description, fileTypeCallback = None, |
|
237 binaryTranslationsCallback = None, lexerAssociationCallback = None): |
|
238 """ |
|
239 Public method to register a project type. |
|
240 |
|
241 @param type_ internal type designator to be registered (string) |
|
242 @param description more verbose type name (display string) (string) |
|
243 @keyparam fileTypeCallback reference to a method returning a dictionary |
|
244 of filetype associations. |
|
245 @keyparam binaryTranslationsCallback reference to a method returning the |
|
246 name of the binary translation file given the name of the raw |
|
247 translation file |
|
248 @keyparam lexerAssociationCallback reference to a method returning the |
|
249 lexer type to be used for syntax highlighting given the name of |
|
250 a file |
|
251 """ |
|
252 if type_ in self.__projectTypes: |
|
253 QMessageBox.critical(None, |
|
254 self.trUtf8("Registering Project Type"), |
|
255 self.trUtf8("""<p>The Project type <b>{0}</b> already exists.</p>""")\ |
|
256 .format(type_) |
|
257 ) |
|
258 else: |
|
259 self.__projectTypes[type_] = description |
|
260 self.__fileTypeCallbacks[type_] = fileTypeCallback |
|
261 self.__lexerAssociationCallbacks[type_] = lexerAssociationCallback |
|
262 self.__binaryTranslationsCallbacks[type_] = binaryTranslationsCallback |
|
263 |
|
264 def unregisterProjectType(self, type_): |
|
265 """ |
|
266 Public method to unregister a project type. |
|
267 |
|
268 @param type_ internal type designator to be unregistered (string) |
|
269 """ |
|
270 if type_ in self.__projectTypes: |
|
271 del self.__projectTypes[type_] |
|
272 if type_ in self.__fileTypeCallbacks: |
|
273 del self.__fileTypeCallbacks[type_] |
|
274 if type_ in self.__lexerAssociationCallbacks: |
|
275 del self.__lexerAssociationCallbacks[type_] |
|
276 if type_ in self.__binaryTranslationsCallbacks: |
|
277 del self.__binaryTranslationsCallbacks[type_] |
|
278 |
|
279 def __initData(self): |
|
280 """ |
|
281 Private method to initialize the project data part. |
|
282 """ |
|
283 self.loaded = False # flag for the loaded status |
|
284 self.dirty = False # dirty flag |
|
285 self.pfile = "" # name of the project file |
|
286 self.ppath = "" # name of the project directory |
|
287 self.translationsRoot = "" # the translations prefix |
|
288 self.name = "" |
|
289 self.opened = False |
|
290 self.subdirs = [""] # record the project dir as a relative path (i.e. empty path) |
|
291 self.otherssubdirs = [] |
|
292 self.vcs = None |
|
293 self.dbgCmdline = '' |
|
294 self.dbgWd = '' |
|
295 self.dbgEnv = '' |
|
296 self.dbgReportExceptions = True |
|
297 self.dbgExcList = [] |
|
298 self.dbgExcIgnoreList = [] |
|
299 self.dbgAutoClearShell = True |
|
300 self.dbgTracePython = False |
|
301 self.dbgAutoContinue = True |
|
302 |
|
303 self.pdata = {} |
|
304 for key in self.__class__.keynames: |
|
305 self.pdata[key] = [] |
|
306 self.pdata["AUTHOR"] = [''] |
|
307 self.pdata["EMAIL"] = [''] |
|
308 self.pdata["PROGLANGUAGE"] = ["Python"] |
|
309 self.pdata["MIXEDLANGUAGE"] = [False] |
|
310 self.pdata["PROJECTTYPE"] = ["Qt4"] |
|
311 self.pdata["SPELLLANGUAGE"] = \ |
|
312 [Preferences.getEditor("SpellCheckingDefaultLanguage")] |
|
313 self.pdata["SPELLWORDS"] = [''] |
|
314 self.pdata["SPELLEXCLUDES"] = [''] |
|
315 self.pdata["FILETYPES"] = {} |
|
316 self.pdata["LEXERASSOCS"] = {} |
|
317 self.pdata["PROJECTTYPESPECIFICDATA"] = {} |
|
318 self.pdata["CHECKERSPARMS"] = {} |
|
319 self.pdata["PACKAGERSPARMS"] = {} |
|
320 self.pdata["DOCUMENTATIONPARMS"] = {} |
|
321 self.pdata["OTHERTOOLSPARMS"] = {} |
|
322 |
|
323 self.__initDebugProperties() |
|
324 |
|
325 self.pudata = {} |
|
326 for key in self.__class__.userKeynames: |
|
327 self.pudata[key] = [] |
|
328 |
|
329 self.vcs = self.initVCS() |
|
330 |
|
331 def getData(self, category, key): |
|
332 """ |
|
333 Public method to get data out of the project data store. |
|
334 |
|
335 @param category category of the data to get (string, one of |
|
336 PROJECTTYPESPECIFICDATA, CHECKERSPARMS, PACKAGERSPARMS, DOCUMENTATIONPARMS |
|
337 or OTHERTOOLSPARMS) |
|
338 @param key key of the data entry to get (string). |
|
339 @return a copy of the requested data or None |
|
340 """ |
|
341 if category in ["PROJECTTYPESPECIFICDATA","CHECKERSPARMS", "PACKAGERSPARMS", |
|
342 "DOCUMENTATIONPARMS", "OTHERTOOLSPARMS"] and \ |
|
343 key in self.pdata[category]: |
|
344 return copy.deepcopy(self.pdata[category][key]) |
|
345 else: |
|
346 return None |
|
347 |
|
348 def setData(self, category, key, data): |
|
349 """ |
|
350 Public method to store data in the project data store. |
|
351 |
|
352 @param category category of the data to get (string, one of |
|
353 PROJECTTYPESPECIFICDATA, CHECKERSPARMS, PACKAGERSPARMS, DOCUMENTATIONPARMS |
|
354 or OTHERTOOLSPARMS) |
|
355 @param key key of the data entry to get (string). |
|
356 @param data data to be stored |
|
357 @return flag indicating success (boolean) |
|
358 """ |
|
359 if category not in ["PROJECTTYPESPECIFICDATA","CHECKERSPARMS", "PACKAGERSPARMS", |
|
360 "DOCUMENTATIONPARMS", "OTHERTOOLSPARMS"]: |
|
361 return False |
|
362 |
|
363 # test for changes of data and save them in the project |
|
364 # 1. there were none, now there are |
|
365 if key not in self.pdata[category] and len(data) > 0: |
|
366 self.pdata[category][key] = copy.deepcopy(data) |
|
367 self.setDirty(True) |
|
368 # 2. there were some, now there aren't |
|
369 elif key in self.pdata[category] and len(data) == 0: |
|
370 del self.pdata[category][key] |
|
371 self.setDirty(True) |
|
372 # 3. there were some and still are |
|
373 elif key in self.pdata[category] and len(data) > 0: |
|
374 if data != self.pdata[category][key]: |
|
375 self.pdata[category][key] = copy.deepcopy(data) |
|
376 self.setDirty(True) |
|
377 # 4. there were none and none are given |
|
378 else: |
|
379 return False |
|
380 return True |
|
381 |
|
382 def initFileTypes(self): |
|
383 """ |
|
384 Public method to initialize the filetype associations with default values. |
|
385 """ |
|
386 self.pdata["FILETYPES"] = {} |
|
387 if self.pdata["MIXEDLANGUAGE"][0]: |
|
388 sourceKey = "Mixed" |
|
389 else: |
|
390 sourceKey = self.pdata["PROGLANGUAGE"][0] |
|
391 for ext in self.sourceExtensions[sourceKey]: |
|
392 self.pdata["FILETYPES"]["*%s" % ext] = "SOURCES" |
|
393 self.pdata["FILETYPES"]["*.idl"] = "INTERFACES" |
|
394 if self.pdata["PROJECTTYPE"][0] in ["Qt4", "E4Plugin", "PySide"]: |
|
395 self.pdata["FILETYPES"]["*.ui"] = "FORMS" |
|
396 self.pdata["FILETYPES"]["*.ui.h"] = "FORMS" |
|
397 if self.pdata["PROJECTTYPE"][0] in ["Qt4", "Qt4C", "E4Plugin", |
|
398 "PySide", "PySideC"]: |
|
399 self.pdata["FILETYPES"]["*.qrc"] = "RESOURCES" |
|
400 if self.pdata["PROJECTTYPE"][0] in ["Qt4", "Qt4C", "E4Plugin", |
|
401 "PySide", "PySideC"]: |
|
402 self.pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS" |
|
403 self.pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS" |
|
404 try: |
|
405 if self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]] is not None: |
|
406 ftypes = self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]]() |
|
407 self.pdata["FILETYPES"].update(ftypes) |
|
408 except KeyError: |
|
409 pass |
|
410 self.setDirty(True) |
|
411 |
|
412 def updateFileTypes(self): |
|
413 """ |
|
414 Public method to update the filetype associations with new default values. |
|
415 """ |
|
416 if self.pdata["PROJECTTYPE"][0] in ["Qt4", "Qt4C", "E4Plugin", |
|
417 "PySide", "PySideC"]: |
|
418 if "*.ts" not in self.pdata["FILETYPES"]: |
|
419 self.pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS" |
|
420 if "*.qm" not in self.pdata["FILETYPES"]: |
|
421 self.pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS" |
|
422 try: |
|
423 if self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]] is not None: |
|
424 ftypes = self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]]() |
|
425 for pattern, ftype in ftypes.items(): |
|
426 if pattern not in self.pdata["FILETYPES"]: |
|
427 self.pdata["FILETYPES"][pattern] = ftype |
|
428 self.setDirty(True) |
|
429 except KeyError: |
|
430 pass |
|
431 |
|
432 def __loadRecent(self): |
|
433 """ |
|
434 Private method to load the recently opened project filenames. |
|
435 """ |
|
436 self.recent = [] |
|
437 Preferences.Prefs.rsettings.sync() |
|
438 rp = Preferences.Prefs.rsettings.value(recentNameProject) |
|
439 if rp.isValid(): |
|
440 for f in rp.toStringList(): |
|
441 if QFileInfo(f).exists(): |
|
442 self.recent.append(f) |
|
443 |
|
444 def __saveRecent(self): |
|
445 """ |
|
446 Private method to save the list of recently opened filenames. |
|
447 """ |
|
448 Preferences.Prefs.rsettings.setValue(recentNameProject, QVariant(self.recent)) |
|
449 Preferences.Prefs.rsettings.sync() |
|
450 |
|
451 def getMostRecent(self): |
|
452 """ |
|
453 Public method to get the most recently opened project. |
|
454 |
|
455 @return path of the most recently opened project (string) |
|
456 """ |
|
457 if len(self.recent): |
|
458 return self.recent[0] |
|
459 else: |
|
460 return None |
|
461 |
|
462 def getModel(self): |
|
463 """ |
|
464 Public method to get a reference to the project browser model. |
|
465 |
|
466 @return reference to the project browser model (ProjectBrowserModel) |
|
467 """ |
|
468 return self.__model |
|
469 |
|
470 def getVcs(self): |
|
471 """ |
|
472 Public method to get a reference to the VCS object. |
|
473 |
|
474 @return reference to the VCS object |
|
475 """ |
|
476 return self.vcs |
|
477 |
|
478 def handlePreferencesChanged(self): |
|
479 """ |
|
480 Public slot used to handle the preferencesChanged signal. |
|
481 """ |
|
482 if self.pudata["VCSSTATUSMONITORINTERVAL"]: |
|
483 self.setStatusMonitorInterval(\ |
|
484 self.pudata["VCSSTATUSMONITORINTERVAL"][0]) |
|
485 else: |
|
486 self.setStatusMonitorInterval(\ |
|
487 Preferences.getVCS("StatusMonitorInterval")) |
|
488 |
|
489 self.__model.preferencesChanged() |
|
490 |
|
491 def setDirty(self, b): |
|
492 """ |
|
493 Public method to set the dirty state. |
|
494 |
|
495 It emits the signal dirty(int). |
|
496 |
|
497 @param b dirty state (boolean) |
|
498 """ |
|
499 self.dirty = b |
|
500 self.saveAct.setEnabled(b) |
|
501 self.emit(SIGNAL("dirty"), bool(b)) |
|
502 |
|
503 def isDirty(self): |
|
504 """ |
|
505 Public method to return the dirty state. |
|
506 |
|
507 @return dirty state (boolean) |
|
508 """ |
|
509 return self.dirty |
|
510 |
|
511 def isOpen(self): |
|
512 """ |
|
513 Public method to return the opened state. |
|
514 |
|
515 @return open state (boolean) |
|
516 """ |
|
517 return self.opened |
|
518 |
|
519 def __checkFilesExist(self, index): |
|
520 """ |
|
521 Private method to check, if the files in a list exist. |
|
522 |
|
523 The files in the indicated list are checked for existance in the |
|
524 filesystem. Non existant files are removed from the list and the |
|
525 dirty state of the project is changed accordingly. |
|
526 |
|
527 @param index key of the list to be checked (string) |
|
528 """ |
|
529 removed = False |
|
530 removelist = [] |
|
531 for file in self.pdata[index]: |
|
532 if not os.path.exists(os.path.join(self.ppath, file)): |
|
533 removelist.append(file) |
|
534 removed = True |
|
535 |
|
536 if removed: |
|
537 for file in removelist: |
|
538 self.pdata[index].remove(file) |
|
539 self.setDirty(True) |
|
540 |
|
541 def __readProject(self, fn): |
|
542 """ |
|
543 Private method to read in a project (.e4p, .e4pz, .e3p, .e3pz) file. |
|
544 |
|
545 @param fn filename of the project file to be read (string) |
|
546 @return flag indicating success |
|
547 """ |
|
548 try: |
|
549 if fn.lower().endswith("e4pz") or fn.lower().endswith("e3pz"): |
|
550 try: |
|
551 import gzip |
|
552 except ImportError: |
|
553 QApplication.restoreOverrideCursor() |
|
554 QMessageBox.critical(None, |
|
555 self.trUtf8("Read project file"), |
|
556 self.trUtf8("""Compressed project files not supported.""" |
|
557 """ The compression library is missing.""")) |
|
558 return False |
|
559 f = gzip.open(fn, "rb") |
|
560 else: |
|
561 f = open(fn, "rb") |
|
562 line = f.readline() |
|
563 dtdLine = f.readline() |
|
564 f.close() |
|
565 except EnvironmentError: |
|
566 QApplication.restoreOverrideCursor() |
|
567 QMessageBox.critical(None, |
|
568 self.trUtf8("Read project file"), |
|
569 self.trUtf8("<p>The project file <b>{0}</b> could not be read.</p>")\ |
|
570 .format(fn)) |
|
571 return False |
|
572 |
|
573 self.pfile = os.path.abspath(fn) |
|
574 self.ppath = os.path.abspath(os.path.dirname(fn)) |
|
575 |
|
576 # insert filename into list of recently opened projects |
|
577 self.__syncRecent() |
|
578 |
|
579 # now read the file |
|
580 if not line.startswith('<?xml'): |
|
581 QApplication.restoreOverrideCursor() |
|
582 QMessageBox.critical(None, |
|
583 self.trUtf8("Read project file"), |
|
584 self.trUtf8("<p>The project file <b>{0}</b> has an unsupported" |
|
585 " format.</p>").format(fn)) |
|
586 return False |
|
587 |
|
588 res = self.__readXMLProject(fn, dtdLine.startswith("<!DOCTYPE")) |
|
589 if res: |
|
590 if len(self.pdata["TRANSLATIONPATTERN"]) == 1: |
|
591 self.translationsRoot = \ |
|
592 self.pdata["TRANSLATIONPATTERN"][0].split("%language%")[0] |
|
593 elif len(self.pdata["MAINSCRIPT"]) == 1: |
|
594 self.translationsRoot = os.path.splitext(self.pdata["MAINSCRIPT"][0])[0] |
|
595 if os.path.isdir(os.path.join(self.ppath, self.translationsRoot)): |
|
596 dn = self.translationsRoot |
|
597 else: |
|
598 dn = os.path.dirname(self.translationsRoot) |
|
599 if dn not in self.subdirs: |
|
600 self.subdirs.append(dn) |
|
601 |
|
602 self.name = os.path.splitext(os.path.basename(fn))[0] |
|
603 |
|
604 # check, if the files of the project still exist in the project directory |
|
605 self.__checkFilesExist("SOURCES") |
|
606 self.__checkFilesExist("FORMS") |
|
607 self.__checkFilesExist("INTERFACES") |
|
608 self.__checkFilesExist("TRANSLATIONS") |
|
609 self.__checkFilesExist("RESOURCES") |
|
610 self.__checkFilesExist("OTHERS") |
|
611 |
|
612 # get the names of subdirectories the files are stored in |
|
613 for fn in self.pdata["SOURCES"] + \ |
|
614 self.pdata["FORMS"] + \ |
|
615 self.pdata["INTERFACES"] + \ |
|
616 self.pdata["RESOURCES"] + \ |
|
617 self.pdata["TRANSLATIONS"]: |
|
618 dn = os.path.dirname(fn) |
|
619 if dn not in self.subdirs: |
|
620 self.subdirs.append(dn) |
|
621 |
|
622 # get the names of other subdirectories |
|
623 for fn in self.pdata["OTHERS"]: |
|
624 dn = os.path.dirname(fn) |
|
625 if dn not in self.otherssubdirs: |
|
626 self.otherssubdirs.append(dn) |
|
627 |
|
628 if self.pfile.endswith('e3p') or self.pfile.endswith('e3pz'): |
|
629 # needs conversion to new format |
|
630 self.setDirty(True) |
|
631 |
|
632 return res |
|
633 |
|
634 def __readXMLProject(self, fn, validating): |
|
635 """ |
|
636 Private method to read the project data from an XML file. |
|
637 |
|
638 @param fn filename of the project file to be read (string) |
|
639 @param validating flag indicating a validation of the XML file is |
|
640 requested (boolean) |
|
641 @return flag indicating success (boolean) |
|
642 """ |
|
643 if fn.lower().endswith("e4pz") or fn.lower().endswith("e3pz"): |
|
644 # work around for a bug in xmlproc |
|
645 validating = False |
|
646 |
|
647 parser = make_parser(validating) |
|
648 handler = ProjectHandler(self) |
|
649 er = XMLEntityResolver() |
|
650 eh = XMLErrorHandler() |
|
651 |
|
652 parser.setContentHandler(handler) |
|
653 parser.setEntityResolver(er) |
|
654 parser.setErrorHandler(eh) |
|
655 |
|
656 try: |
|
657 if fn.lower().endswith("e4pz") or fn.lower().endswith("e3pz"): |
|
658 try: |
|
659 import gzip |
|
660 except ImportError: |
|
661 QApplication.restoreOverrideCursor() |
|
662 QMessageBox.critical(None, |
|
663 self.trUtf8("Read project file"), |
|
664 self.trUtf8("""Compressed project files not supported.""" |
|
665 """ The compression library is missing.""")) |
|
666 return False |
|
667 f = gzip.open(fn, "rb") |
|
668 else: |
|
669 f = open(fn, "rb") |
|
670 try: |
|
671 try: |
|
672 parser.parse(f) |
|
673 except UnicodeEncodeError: |
|
674 f.seek(0) |
|
675 buf = cStringIO.StringIO(f.read()) |
|
676 parser.parse(buf) |
|
677 finally: |
|
678 f.close() |
|
679 except IOError: |
|
680 QApplication.restoreOverrideCursor() |
|
681 QMessageBox.critical(None, |
|
682 self.trUtf8("Read project file"), |
|
683 self.trUtf8("<p>The project file <b>{0}</b> could not be read.</p>")\ |
|
684 .format(fn)) |
|
685 return False |
|
686 except XMLFatalParseError: |
|
687 QApplication.restoreOverrideCursor() |
|
688 QMessageBox.critical(None, |
|
689 self.trUtf8("Read project file"), |
|
690 self.trUtf8("<p>The project file <b>{0}</b> has invalid contents.</p>")\ |
|
691 .format(fn)) |
|
692 eh.showParseMessages() |
|
693 return False |
|
694 |
|
695 QApplication.restoreOverrideCursor() |
|
696 eh.showParseMessages() |
|
697 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
|
698 QApplication.processEvents() |
|
699 return True |
|
700 |
|
701 def __writeProject(self, fn = None): |
|
702 """ |
|
703 Private method to save the project infos to a project file. |
|
704 |
|
705 @param fn optional filename of the project file to be written (string). |
|
706 If fn is None, the filename stored in the project object |
|
707 is used. This is the 'save' action. If fn is given, this filename |
|
708 is used instead of the one in the project object. This is the |
|
709 'save as' action. |
|
710 @return flag indicating success |
|
711 """ |
|
712 if self.vcs is not None: |
|
713 self.pdata["VCSOPTIONS"] = [copy.deepcopy(self.vcs.vcsGetOptions())] |
|
714 self.pdata["VCSOTHERDATA"] = [copy.deepcopy(self.vcs.vcsGetOtherData())] |
|
715 |
|
716 if fn is None: |
|
717 fn = self.pfile |
|
718 |
|
719 res = self.__writeXMLProject(fn) |
|
720 |
|
721 if res: |
|
722 self.pfile = os.path.abspath(fn) |
|
723 self.ppath = os.path.abspath(os.path.dirname(fn)) |
|
724 self.name = os.path.splitext(os.path.basename(fn))[0] |
|
725 self.setDirty(False) |
|
726 |
|
727 # insert filename into list of recently opened projects |
|
728 self.__syncRecent() |
|
729 |
|
730 return res |
|
731 |
|
732 def __writeXMLProject(self, fn = None): |
|
733 """ |
|
734 Private method to write the project data to an XML file. |
|
735 |
|
736 @param fn the filename of the project file (string) |
|
737 @return flag indicating success (boolean) |
|
738 """ |
|
739 try: |
|
740 if fn.lower().endswith("e4pz"): |
|
741 try: |
|
742 import gzip |
|
743 except ImportError: |
|
744 QMessageBox.critical(None, |
|
745 self.trUtf8("Save project file"), |
|
746 self.trUtf8("""Compressed project files not supported.""" |
|
747 """ The compression library is missing.""")) |
|
748 return False |
|
749 f = gzip.open(fn, "wb") |
|
750 else: |
|
751 f = open(fn, "wb") |
|
752 |
|
753 ProjectWriter(f, os.path.splitext(os.path.basename(fn))[0]).writeXML() |
|
754 |
|
755 f.close() |
|
756 |
|
757 except IOError: |
|
758 QMessageBox.critical(None, |
|
759 self.trUtf8("Save project file"), |
|
760 self.trUtf8("<p>The project file <b>{0}</b> could not be written.</p>")\ |
|
761 .format(fn)) |
|
762 return False |
|
763 |
|
764 return True |
|
765 |
|
766 def __readUserProperties(self): |
|
767 """ |
|
768 Private method to read in the user specific project file (.e4q) |
|
769 """ |
|
770 if self.pfile is None: |
|
771 return |
|
772 |
|
773 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
774 |
|
775 fn = os.path.join(self.getProjectManagementDir(), '%s.e4q' % fn) |
|
776 if os.path.exists(fn): |
|
777 try: |
|
778 f = open(fn, "rb") |
|
779 |
|
780 parser = make_parser(True) |
|
781 handler = UserProjectHandler(self) |
|
782 er = XMLEntityResolver() |
|
783 eh = XMLErrorHandler() |
|
784 |
|
785 parser.setContentHandler(handler) |
|
786 parser.setEntityResolver(er) |
|
787 parser.setErrorHandler(eh) |
|
788 |
|
789 try: |
|
790 try: |
|
791 parser.parse(f) |
|
792 except UnicodeEncodeError: |
|
793 f.seek(0) |
|
794 buf = cStringIO.StringIO(f.read()) |
|
795 parser.parse(buf) |
|
796 finally: |
|
797 f.close() |
|
798 except IOError: |
|
799 QMessageBox.critical(None, |
|
800 self.trUtf8("Read user project properties"), |
|
801 self.trUtf8("<p>The user specific project properties file <b>{0}</b>" |
|
802 " could not be read.</p>").format(fn)) |
|
803 return |
|
804 except XMLFatalParseError: |
|
805 pass |
|
806 |
|
807 eh.showParseMessages() |
|
808 |
|
809 def __writeUserProperties(self): |
|
810 """ |
|
811 Private method to write the project data to an XML file. |
|
812 """ |
|
813 if self.pfile is None or \ |
|
814 self.pfile.endswith('e3p') or \ |
|
815 self.pfile.endswith('e3pz'): |
|
816 return |
|
817 |
|
818 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
819 |
|
820 fn = os.path.join(self.getProjectManagementDir(), '%s.e4q' % fn) |
|
821 |
|
822 try: |
|
823 f = open(fn, "wb") |
|
824 |
|
825 UserProjectWriter(f, os.path.splitext(os.path.basename(fn))[0]).writeXML() |
|
826 |
|
827 f.close() |
|
828 except IOError: |
|
829 QMessageBox.critical(None, |
|
830 self.trUtf8("Save user project properties"), |
|
831 self.trUtf8("<p>The user specific project properties file <b>{0}</b>" |
|
832 " could not be written.</p>").format(fn)) |
|
833 |
|
834 def __readSession(self, quiet = False, indicator = ""): |
|
835 """ |
|
836 Private method to read in the project session file (.e4s, .e3s) |
|
837 |
|
838 @param quiet flag indicating quiet operations. |
|
839 If this flag is true, no errors are reported. |
|
840 @keyparam indicator indicator string (string) |
|
841 """ |
|
842 if self.pfile is None: |
|
843 if not quiet: |
|
844 QMessageBox.critical(None, |
|
845 self.trUtf8("Read project session"), |
|
846 self.trUtf8("Please save the project first.")) |
|
847 return |
|
848 |
|
849 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
850 |
|
851 try: |
|
852 if ext.lower() in [".e4pz", ".e3pz"]: |
|
853 if ext.lower() == ".e3pz": |
|
854 fn = os.path.join(self.ppath, '%s.e3sz' % fn) |
|
855 else: |
|
856 fn = os.path.join(self.getProjectManagementDir(), |
|
857 '%s%s.e4sz' % (fn, indicator)) |
|
858 try: |
|
859 import gzip |
|
860 except ImportError: |
|
861 if not quiet: |
|
862 QMessageBox.critical(None, |
|
863 self.trUtf8("Read project session"), |
|
864 self.trUtf8("""Compressed project session files not""" |
|
865 """ supported. The compression library is missing.""")) |
|
866 return |
|
867 f = gzip.open(fn, "rb") |
|
868 else: |
|
869 if ext.lower() == ".e3p": |
|
870 fn = os.path.join(self.ppath, '%s.e3s' % fn) |
|
871 else: |
|
872 fn = os.path.join(self.getProjectManagementDir(), |
|
873 '%s%s.e4s' % (fn, indicator)) |
|
874 f = open(fn, "rb") |
|
875 line = f.readline() |
|
876 dtdLine = f.readline() |
|
877 f.close() |
|
878 except EnvironmentError: |
|
879 if not quiet: |
|
880 QMessageBox.critical(None, |
|
881 self.trUtf8("Read project session"), |
|
882 self.trUtf8("<p>The project session <b>{0}</b>" |
|
883 " could not be read.</p>")\ |
|
884 .format(fn)) |
|
885 return |
|
886 |
|
887 # now read the file |
|
888 if line.startswith('<?xml'): |
|
889 res = self.__readXMLSession(fn, dtdLine.startswith("<!DOCTYPE"), quiet) |
|
890 else: |
|
891 if not quiet: |
|
892 QMessageBox.critical(None, |
|
893 self.trUtf8("Read project session"), |
|
894 self.trUtf8("<p>The project session <b>{0}</b> has an unsupported" |
|
895 " format.</p>").format(fn)) |
|
896 |
|
897 def __readXMLSession(self, fn, validating, quiet): |
|
898 """ |
|
899 Private method to read the session data from an XML file. |
|
900 |
|
901 The data read is: |
|
902 <ul> |
|
903 <li>all open source filenames</li> |
|
904 <li>the active window</li> |
|
905 <li>all breakpoints</li> |
|
906 <li>the commandline</li> |
|
907 <li>the working directory</li> |
|
908 <li>the exception reporting flag</li> |
|
909 <li>the list of exception types to be highlighted</li> |
|
910 <li>all bookmarks</li> |
|
911 </ul> |
|
912 |
|
913 @param fn filename of the project session file to be read (string) |
|
914 @param validating flag indicating a validation of the XML file is |
|
915 requested (boolean) |
|
916 @param quiet flag indicating quiet operations. |
|
917 If this flag is true, no errors are reported. |
|
918 """ |
|
919 if fn.lower().endswith("e4sz") or fn.lower().endswith("e3sz"): |
|
920 # work around for a bug in xmlproc |
|
921 validating = False |
|
922 |
|
923 parser = make_parser(validating) |
|
924 handler = SessionHandler(self) |
|
925 er = XMLEntityResolver() |
|
926 eh = XMLErrorHandler() |
|
927 |
|
928 parser.setContentHandler(handler) |
|
929 parser.setEntityResolver(er) |
|
930 parser.setErrorHandler(eh) |
|
931 |
|
932 try: |
|
933 if fn.lower().endswith("e4sz") or fn.lower().endswith("e3sz"): |
|
934 try: |
|
935 import gzip |
|
936 except ImportError: |
|
937 if not quiet: |
|
938 QMessageBox.critical(None, |
|
939 self.trUtf8("Read project session"), |
|
940 self.trUtf8("<p>The project session <b>{0}</b> could not" |
|
941 " be read.</p>").format(fn)) |
|
942 return |
|
943 f = gzip.open(fn, "rb") |
|
944 else: |
|
945 f = open(fn, "rb") |
|
946 try: |
|
947 try: |
|
948 parser.parse(f) |
|
949 except UnicodeEncodeError: |
|
950 f.seek(0) |
|
951 buf = cStringIO.StringIO(f.read()) |
|
952 parser.parse(buf) |
|
953 finally: |
|
954 f.close() |
|
955 except IOError: |
|
956 if not quiet: |
|
957 QMessageBox.critical(None, |
|
958 self.trUtf8("Read project session"), |
|
959 self.trUtf8("<p>The project session file <b>{0}</b> could not be" |
|
960 " read.</p>").format(fn)) |
|
961 return |
|
962 except XMLFatalParseError: |
|
963 pass |
|
964 |
|
965 if not quiet: |
|
966 eh.showParseMessages() |
|
967 |
|
968 def __writeSession(self, quiet = False, indicator = ""): |
|
969 """ |
|
970 Private method to write the session data to an XML file (.e4s). |
|
971 |
|
972 The data saved is: |
|
973 <ul> |
|
974 <li>all open source filenames belonging to the project</li> |
|
975 <li>the active window, if it belongs to the project</li> |
|
976 <li>all breakpoints</li> |
|
977 <li>the commandline</li> |
|
978 <li>the working directory</li> |
|
979 <li>the exception reporting flag</li> |
|
980 <li>the list of exception types to be highlighted</li> |
|
981 <li>all bookmarks of files belonging to the project</li> |
|
982 </ul> |
|
983 |
|
984 @param quiet flag indicating quiet operations. |
|
985 If this flag is true, no errors are reported. |
|
986 @keyparam indicator indicator string (string) |
|
987 """ |
|
988 if self.pfile is None or \ |
|
989 self.pfile.endswith('e3p') or \ |
|
990 self.pfile.endswith('e3pz'): |
|
991 if not quiet: |
|
992 QMessageBox.critical(None, |
|
993 self.trUtf8("Save project session"), |
|
994 self.trUtf8("Please save the project first.")) |
|
995 return |
|
996 |
|
997 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
998 |
|
999 try: |
|
1000 if ext.lower() == ".e4pz": |
|
1001 fn = os.path.join(self.getProjectManagementDir(), |
|
1002 '%s%s.e4sz' % (fn, indicator)) |
|
1003 try: |
|
1004 import gzip |
|
1005 except ImportError: |
|
1006 if not quiet: |
|
1007 QMessageBox.critical(None, |
|
1008 self.trUtf8("Save project session"), |
|
1009 self.trUtf8("""Compressed project session files not""" |
|
1010 """ supported. The compression library is missing.""")) |
|
1011 return |
|
1012 f = gzip.open(fn, "wb") |
|
1013 else: |
|
1014 fn = os.path.join(self.getProjectManagementDir(), |
|
1015 '%s%s.e4s' % (fn, indicator)) |
|
1016 f = open(fn, "wb") |
|
1017 |
|
1018 SessionWriter(f, os.path.splitext(os.path.basename(fn))[0]).writeXML() |
|
1019 |
|
1020 f.close() |
|
1021 |
|
1022 except IOError: |
|
1023 if not quiet: |
|
1024 QMessageBox.critical(None, |
|
1025 self.trUtf8("Save project session"), |
|
1026 self.trUtf8("<p>The project session file <b>{0}</b> could not be" |
|
1027 " written.</p>").format(fn)) |
|
1028 |
|
1029 def __deleteSession(self): |
|
1030 """ |
|
1031 Private method to delete the session file. |
|
1032 """ |
|
1033 if self.pfile is None: |
|
1034 if not quiet: |
|
1035 QMessageBox.critical(None, |
|
1036 self.trUtf8("Delete project session"), |
|
1037 self.trUtf8("Please save the project first.")) |
|
1038 return |
|
1039 |
|
1040 fname, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1041 |
|
1042 for fn in [os.path.join(self.getProjectManagementDir(), "%s.e4sz" % fname), |
|
1043 os.path.join(self.getProjectManagementDir(), "%s.e4s" % fname), |
|
1044 os.path.join(self.ppath, "%s.e3sz" % fname), |
|
1045 os.path.join(self.ppath, "%s.e3s" % fname)]: |
|
1046 if os.path.exists(fn): |
|
1047 try: |
|
1048 os.remove(fn) |
|
1049 except OSError: |
|
1050 QMessageBox.critical(None, |
|
1051 self.trUtf8("Delete project session"), |
|
1052 self.trUtf8("<p>The project session file <b>{0}</b> could not be" |
|
1053 " deleted.</p>").format(fn)) |
|
1054 |
|
1055 def __readTasks(self): |
|
1056 """ |
|
1057 Private method to read in the project tasks file (.e4t, .e3t) |
|
1058 """ |
|
1059 if self.pfile is None: |
|
1060 QMessageBox.critical(None, |
|
1061 self.trUtf8("Read tasks"), |
|
1062 self.trUtf8("Please save the project first.")) |
|
1063 return |
|
1064 |
|
1065 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1066 |
|
1067 try: |
|
1068 if ext.lower() in [".e4pz", "*.e3pz"]: |
|
1069 if ext.lower() == ".e3pz": |
|
1070 fn = os.path.join(self.ppath, '%s.e3tz' % fn) |
|
1071 else: |
|
1072 fn = os.path.join(self.getProjectManagementDir(), '%s.e4tz' % fn) |
|
1073 if not os.path.exists(fn): |
|
1074 return |
|
1075 try: |
|
1076 import gzip |
|
1077 except ImportError: |
|
1078 QMessageBox.critical(None, |
|
1079 self.trUtf8("Read tasks"), |
|
1080 self.trUtf8("""Compressed tasks files not supported.""" |
|
1081 """ The compression library is missing.""")) |
|
1082 return |
|
1083 f = gzip.open(fn, "rb") |
|
1084 else: |
|
1085 if ext.lower() == ".e3p": |
|
1086 fn = os.path.join(self.ppath, '%s.e3t' % fn) |
|
1087 else: |
|
1088 fn = os.path.join(self.getProjectManagementDir(), '%s.e4t' % fn) |
|
1089 if not os.path.exists(fn): |
|
1090 return |
|
1091 f = open(fn, "rb") |
|
1092 line = f.readline() |
|
1093 dtdLine = f.readline() |
|
1094 f.close() |
|
1095 except EnvironmentError: |
|
1096 QMessageBox.critical(None, |
|
1097 self.trUtf8("Read tasks"), |
|
1098 self.trUtf8("<p>The tasks file <b>{0}</b> could not be read.</p>")\ |
|
1099 .format(fn)) |
|
1100 return |
|
1101 |
|
1102 # now read the file |
|
1103 if line.startswith('<?xml'): |
|
1104 res = self.__readXMLTasks(fn, dtdLine.startswith("<!DOCTYPE")) |
|
1105 else: |
|
1106 QMessageBox.critical(None, |
|
1107 self.trUtf8("Read project session"), |
|
1108 self.trUtf8("<p>The tasks file <b>{0}</b> has an unsupported" |
|
1109 " format.</p>")\ |
|
1110 .format(fn)) |
|
1111 |
|
1112 def __readXMLTasks(self, fn, validating): |
|
1113 """ |
|
1114 Private method to read the project tasks data from an XML file. |
|
1115 |
|
1116 @param fn filename of the project tasks file to be read (string) |
|
1117 @param validating flag indicating a validation of the XML file is |
|
1118 requested (boolean) |
|
1119 """ |
|
1120 if fn.lower().endswith("e4tz") or fn.lower().endswith("e3tz"): |
|
1121 # work around for a bug in xmlproc |
|
1122 validating = False |
|
1123 |
|
1124 parser = make_parser(validating) |
|
1125 handler = TasksHandler(1) |
|
1126 er = XMLEntityResolver() |
|
1127 eh = XMLErrorHandler() |
|
1128 |
|
1129 parser.setContentHandler(handler) |
|
1130 parser.setEntityResolver(er) |
|
1131 parser.setErrorHandler(eh) |
|
1132 |
|
1133 try: |
|
1134 if fn.lower().endswith("e4tz") or fn.lower().endswith("e3tz"): |
|
1135 try: |
|
1136 import gzip |
|
1137 except ImportError: |
|
1138 QMessageBox.critical(None, |
|
1139 self.trUtf8("Read tasks"), |
|
1140 self.trUtf8("""Compressed tasks files not supported.""" |
|
1141 """ The compression library is missing.""")) |
|
1142 return |
|
1143 f = gzip.open(fn, "rb") |
|
1144 else: |
|
1145 f = open(fn, "rb") |
|
1146 try: |
|
1147 try: |
|
1148 parser.parse(f) |
|
1149 except UnicodeEncodeError: |
|
1150 f.seek(0) |
|
1151 buf = cStringIO.StringIO(f.read()) |
|
1152 parser.parse(buf) |
|
1153 finally: |
|
1154 f.close() |
|
1155 except IOError: |
|
1156 QMessageBox.critical(None, |
|
1157 self.trUtf8("Read tasks"), |
|
1158 self.trUtf8("<p>The tasks file <b>{0}</b> could not be read.</p>")\ |
|
1159 .format(fn)) |
|
1160 return |
|
1161 except XMLFatalParseError: |
|
1162 pass |
|
1163 |
|
1164 eh.showParseMessages() |
|
1165 |
|
1166 def __writeTasks(self): |
|
1167 """ |
|
1168 Private method to write the tasks data to an XML file (.e4t). |
|
1169 """ |
|
1170 if self.pfile is None: |
|
1171 return |
|
1172 |
|
1173 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1174 |
|
1175 try: |
|
1176 if ext.lower() == ".e4pz": |
|
1177 fn = os.path.join(self.getProjectManagementDir(), '%s.e4tz' % fn) |
|
1178 try: |
|
1179 import gzip |
|
1180 except ImportError: |
|
1181 QMessageBox.critical(None, |
|
1182 self.trUtf8("Save tasks"), |
|
1183 self.trUtf8("""Compressed tasks files not supported.""" |
|
1184 """ The compression library is missing.""")) |
|
1185 return |
|
1186 f = gzip.open(fn, "wb") |
|
1187 else: |
|
1188 fn = os.path.join(self.getProjectManagementDir(), '%s.e4t' % fn) |
|
1189 f = open(fn, "wb") |
|
1190 |
|
1191 TasksWriter(f, True, os.path.splitext(os.path.basename(fn))[0]).writeXML() |
|
1192 |
|
1193 f.close() |
|
1194 |
|
1195 except IOError: |
|
1196 QMessageBox.critical(None, |
|
1197 self.trUtf8("Save tasks"), |
|
1198 self.trUtf8("<p>The tasks file <b>{0}</b> could not be written.</p>") |
|
1199 .format(fn)) |
|
1200 |
|
1201 def __readDebugProperties(self, quiet = False): |
|
1202 """ |
|
1203 Private method to read in the project debugger properties file (.e4d, .e3d) |
|
1204 |
|
1205 @param quiet flag indicating quiet operations. |
|
1206 If this flag is true, no errors are reported. |
|
1207 """ |
|
1208 if self.pfile is None: |
|
1209 if not quiet: |
|
1210 QMessageBox.critical(None, |
|
1211 self.trUtf8("Read debugger properties"), |
|
1212 self.trUtf8("Please save the project first.")) |
|
1213 return |
|
1214 |
|
1215 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1216 |
|
1217 try: |
|
1218 if ext.lower() in [".e4pz", "*.e3pz"]: |
|
1219 if ext.lower() == ".e3pz": |
|
1220 fn = os.path.join(self.ppath, '%s.e3dz' % fn) |
|
1221 else: |
|
1222 fn = os.path.join(self.getProjectManagementDir(), '%s.e4dz' % fn) |
|
1223 try: |
|
1224 import gzip |
|
1225 except ImportError: |
|
1226 if not quiet: |
|
1227 QMessageBox.critical(None, |
|
1228 self.trUtf8("Read debugger properties"), |
|
1229 self.trUtf8("""Compressed project session files not""" |
|
1230 """ supported. The compression library is""" |
|
1231 """ missing.""")) |
|
1232 return |
|
1233 f = gzip.open(fn, "rb") |
|
1234 else: |
|
1235 if ext.lower() == ".e3p": |
|
1236 fn = os.path.join(self.ppath, '%s.e3d' % fn) |
|
1237 else: |
|
1238 fn = os.path.join(self.getProjectManagementDir(), '%s.e4d' % fn) |
|
1239 f = open(fn, "rb") |
|
1240 line = f.readline() |
|
1241 dtdLine = f.readline() |
|
1242 f.close() |
|
1243 except EnvironmentError: |
|
1244 if not quiet: |
|
1245 QMessageBox.critical(None, |
|
1246 self.trUtf8("Read debugger properties"), |
|
1247 self.trUtf8("<p>The project debugger properties file <b>{0}</b> could" |
|
1248 " not be read.</p>").format(fn)) |
|
1249 return |
|
1250 |
|
1251 # now read the file |
|
1252 if line.startswith('<?xml'): |
|
1253 self.__readXMLDebugProperties(fn, dtdLine.startswith("<!DOCTYPE"), quiet) |
|
1254 else: |
|
1255 if not quiet: |
|
1256 QMessageBox.critical(None, |
|
1257 self.trUtf8("Read debugger properties"), |
|
1258 self.trUtf8("<p>The project debugger properties file <b>{0}</b> has" |
|
1259 " an unsupported format.</p>").format(fn)) |
|
1260 |
|
1261 def __readXMLDebugProperties(self, fn, validating, quiet): |
|
1262 """ |
|
1263 Public method to read the debugger properties from an XML file. |
|
1264 |
|
1265 @param fn filename of the project debugger properties file to be read |
|
1266 (string) |
|
1267 @param validating flag indicating a validation of the XML file is |
|
1268 requested (boolean) |
|
1269 @param quiet flag indicating quiet operations. |
|
1270 If this flag is true, no errors are reported. |
|
1271 """ |
|
1272 if fn.lower().endswith("e4dz") or fn.lower().endswith("e3dz"): |
|
1273 # work around for a bug in xmlproc |
|
1274 validating = False |
|
1275 |
|
1276 parser = make_parser(validating) |
|
1277 handler = DebuggerPropertiesHandler(self) |
|
1278 er = XMLEntityResolver() |
|
1279 eh = XMLErrorHandler() |
|
1280 |
|
1281 parser.setContentHandler(handler) |
|
1282 parser.setEntityResolver(er) |
|
1283 parser.setErrorHandler(eh) |
|
1284 |
|
1285 try: |
|
1286 if fn.lower().endswith("e4dz") or fn.lower().endswith("e3dz"): |
|
1287 try: |
|
1288 import gzip |
|
1289 except ImportError: |
|
1290 if not quiet: |
|
1291 QMessageBox.critical(None, |
|
1292 self.trUtf8("Read debugger properties"), |
|
1293 self.trUtf8("<p>The project debugger properties file" |
|
1294 " <b>{0}</b> could not be read.</p>").format(fn)) |
|
1295 return |
|
1296 f = gzip.open(fn, "rb") |
|
1297 else: |
|
1298 f = open(fn, "rb") |
|
1299 try: |
|
1300 try: |
|
1301 parser.parse(f) |
|
1302 except UnicodeEncodeError: |
|
1303 f.seek(0) |
|
1304 buf = cStringIO.StringIO(f.read()) |
|
1305 parser.parse(buf) |
|
1306 if self.debugProperties["INTERPRETER"]: |
|
1307 self.debugPropertiesLoaded = True |
|
1308 else: |
|
1309 self.debugPropertiesLoaded = False |
|
1310 finally: |
|
1311 f.close() |
|
1312 except IOError: |
|
1313 if not quiet: |
|
1314 QMessageBox.critical(None, |
|
1315 self.trUtf8("Read debugger properties"), |
|
1316 self.trUtf8("<p>The project debugger properties file <b>{0}</b> could" |
|
1317 " not be read.</p>") |
|
1318 .format(fn)) |
|
1319 return |
|
1320 except XMLFatalParseError: |
|
1321 pass |
|
1322 |
|
1323 if not quiet: |
|
1324 eh.showParseMessages() |
|
1325 |
|
1326 def __writeDebugProperties(self, quiet=0): |
|
1327 """ |
|
1328 Private method to write the project debugger properties file (.e4d) |
|
1329 |
|
1330 @param quiet flag indicating quiet operations. |
|
1331 If this flag is true, no errors are reported. |
|
1332 """ |
|
1333 if self.pfile is None or \ |
|
1334 self.pfile.endswith('e3p') or \ |
|
1335 self.pfile.endswith('e3pz'): |
|
1336 if not quiet: |
|
1337 QMessageBox.critical(None, |
|
1338 self.trUtf8("Save debugger properties"), |
|
1339 self.trUtf8("Please save the project first.")) |
|
1340 return |
|
1341 |
|
1342 fn, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1343 |
|
1344 try: |
|
1345 if ext.lower() == ".e4pz": |
|
1346 fn = os.path.join(self.getProjectManagementDir(), '%s.e4dz' % fn) |
|
1347 try: |
|
1348 import gzip |
|
1349 except ImportError: |
|
1350 if not quiet: |
|
1351 QMessageBox.critical(None, |
|
1352 self.trUtf8("Save debugger properties"), |
|
1353 self.trUtf8("""Compressed project debugger properties files""" |
|
1354 """ not supported. The compression library is""" |
|
1355 """ missing.""")) |
|
1356 return |
|
1357 f = gzip.open(fn, "wb") |
|
1358 else: |
|
1359 fn = os.path.join(self.getProjectManagementDir(), '%s.e4d' % fn) |
|
1360 f = open(fn, "wb") |
|
1361 |
|
1362 DebuggerPropertiesWriter(f, os.path.splitext(os.path.basename(fn))[0])\ |
|
1363 .writeXML() |
|
1364 |
|
1365 f.close() |
|
1366 |
|
1367 except IOError: |
|
1368 if not quiet: |
|
1369 QMessageBox.critical(None, |
|
1370 self.trUtf8("Save debugger properties"), |
|
1371 self.trUtf8("<p>The project debugger properties file <b>{0}</b> could" |
|
1372 " not be written.</p>") |
|
1373 .format(fn)) |
|
1374 |
|
1375 def __deleteDebugProperties(self): |
|
1376 """ |
|
1377 Private method to delete the project debugger properties file (.e3d) |
|
1378 """ |
|
1379 if self.pfile is None: |
|
1380 if not quiet: |
|
1381 QMessageBox.critical(None, |
|
1382 self.trUtf8("Delete debugger properties"), |
|
1383 self.trUtf8("Please save the project first.")) |
|
1384 return |
|
1385 |
|
1386 fname, ext = os.path.splitext(os.path.basename(self.pfile)) |
|
1387 |
|
1388 for fn in [os.path.join(self.getProjectManagementDir(), "%s.e4dz" % fname), |
|
1389 os.path.join(self.getProjectManagementDir(), "%s.e4d" % fname), |
|
1390 os.path.join(self.ppath, "%s.e3dz" % fname), |
|
1391 os.path.join(self.ppath, "%s.e3d" % fname)]: |
|
1392 if os.path.exists(fn): |
|
1393 try: |
|
1394 os.remove(fn) |
|
1395 except OSError: |
|
1396 QMessageBox.critical(None, |
|
1397 self.trUtf8("Delete debugger properties"), |
|
1398 self.trUtf8("<p>The project debugger properties file <b>{0}</b>" |
|
1399 " could not be deleted.</p>") |
|
1400 .format(fn)) |
|
1401 |
|
1402 def __initDebugProperties(self): |
|
1403 """ |
|
1404 Private method to initialize the debug properties. |
|
1405 """ |
|
1406 self.debugPropertiesLoaded = False |
|
1407 self.debugProperties = {} |
|
1408 for key in self.__class__.dbgKeynames: |
|
1409 self.debugProperties[key] = "" |
|
1410 self.debugProperties["ENVIRONMENTOVERRIDE"] = False |
|
1411 self.debugProperties["REMOTEDEBUGGER"] = False |
|
1412 self.debugProperties["PATHTRANSLATION"] = False |
|
1413 self.debugProperties["CONSOLEDEBUGGER"] = False |
|
1414 self.debugProperties["REDIRECT"] = True |
|
1415 self.debugProperties["NOENCODING"] = False |
|
1416 |
|
1417 def isDebugPropertiesLoaded(self): |
|
1418 """ |
|
1419 Public method to return the status of the debug properties. |
|
1420 |
|
1421 @return load status of debug properties (boolean) |
|
1422 """ |
|
1423 return self.debugPropertiesLoaded |
|
1424 |
|
1425 def __showDebugProperties(self): |
|
1426 """ |
|
1427 Private slot to display the debugger properties dialog. |
|
1428 """ |
|
1429 dlg = DebuggerPropertiesDialog(self) |
|
1430 if dlg.exec_() == QDialog.Accepted: |
|
1431 dlg.storeData() |
|
1432 |
|
1433 def getDebugProperty(self, key): |
|
1434 """ |
|
1435 Public method to retrieve a debugger property. |
|
1436 |
|
1437 @param key key of the property (string) |
|
1438 @return value of the property |
|
1439 """ |
|
1440 return self.debugProperties[key] |
|
1441 |
|
1442 def setDbgInfo(self, argv, wd, env, excReporting, excList, excIgnoreList, |
|
1443 autoClearShell, tracePython = None, autoContinue = None): |
|
1444 """ |
|
1445 Public method to set the debugging information. |
|
1446 |
|
1447 @param argv command line arguments to be used (string) |
|
1448 @param wd working directory (string) |
|
1449 @param env environment setting (string) |
|
1450 @param excReporting flag indicating the highlighting of exceptions |
|
1451 @param excList list of exceptions to be highlighted (list of strings) |
|
1452 @param excIgnoreList list of exceptions to be ignored (list of strings) |
|
1453 @param autoClearShell flag indicating, that the interpreter window |
|
1454 should be cleared (boolean) |
|
1455 @keyparam tracePython flag to indicate if the Python library should be |
|
1456 traced as well (boolean) |
|
1457 @keyparam autoContinue flag indicating, that the debugger should not stop |
|
1458 at the first executable line (boolean) |
|
1459 """ |
|
1460 self.dbgCmdline = argv |
|
1461 self.dbgWd = wd |
|
1462 self.dbgEnv = env |
|
1463 self.dbgReportExceptions = excReporting |
|
1464 self.dbgExcList = excList[:] # keep a copy of the list |
|
1465 self.dbgExcIgnoreList = excIgnoreList[:] # keep a copy of the list |
|
1466 self.dbgAutoClearShell = autoClearShell |
|
1467 if tracePython is not None: |
|
1468 self.dbgTracePython = tracePython |
|
1469 if autoContinue is not None: |
|
1470 self.dbgAutoContinue = autoContinue |
|
1471 |
|
1472 def addLanguage(self): |
|
1473 """ |
|
1474 Public slot used to add a language to the project. |
|
1475 """ |
|
1476 if self.pdata["PROJECTTYPE"][0] in \ |
|
1477 ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"] and \ |
|
1478 (len(self.pdata["TRANSLATIONPATTERN"]) == 0 or \ |
|
1479 self.pdata["TRANSLATIONPATTERN"][0] == ''): |
|
1480 QMessageBox.critical(None, |
|
1481 self.trUtf8("Add Language"), |
|
1482 self.trUtf8("You have to specify a translation pattern first.")) |
|
1483 return |
|
1484 |
|
1485 dlg = AddLanguageDialog(self.parent()) |
|
1486 if dlg.exec_() == QDialog.Accepted: |
|
1487 lang = dlg.getSelectedLanguage() |
|
1488 if self.pdata["PROJECTTYPE"][0] in \ |
|
1489 ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"]: |
|
1490 langFile = self.pdata["TRANSLATIONPATTERN"][0].replace("%language%", lang) |
|
1491 self.appendFile(langFile) |
|
1492 self.emit(SIGNAL("projectLanguageAddedByCode"), lang) |
|
1493 |
|
1494 def __binaryTranslationFile(self, langFile): |
|
1495 """ |
|
1496 Private method to calculate the filename of the binary translations file |
|
1497 given the name of the raw translations file. |
|
1498 |
|
1499 @param langFile name of the raw translations file (string) |
|
1500 @return name of the binary translations file (string) |
|
1501 """ |
|
1502 qmFile = "" |
|
1503 try: |
|
1504 if self.__binaryTranslationsCallbacks[self.pdata["PROJECTTYPE"][0]] \ |
|
1505 is not None: |
|
1506 qmFile = self.__binaryTranslationsCallbacks\ |
|
1507 [self.pdata["PROJECTTYPE"][0]](langFile) |
|
1508 except KeyError: |
|
1509 qmFile = langFile.replace('.ts', '.qm') |
|
1510 if qmFile == langFile: |
|
1511 qmFile = "" |
|
1512 return qmFile |
|
1513 |
|
1514 def checkLanguageFiles(self): |
|
1515 """ |
|
1516 Public slot to check the language files after a release process. |
|
1517 """ |
|
1518 tbPath = self.pdata["TRANSLATIONSBINPATH"] and \ |
|
1519 self.pdata["TRANSLATIONSBINPATH"][0] or "" |
|
1520 for langFile in self.pdata["TRANSLATIONS"][:]: |
|
1521 qmFile = self.__binaryTranslationFile(langFile) |
|
1522 if qmFile: |
|
1523 if qmFile not in self.pdata["TRANSLATIONS"] and \ |
|
1524 os.path.exists(os.path.join(self.ppath, qmFile)): |
|
1525 self.appendFile(qmFile) |
|
1526 if tbPath: |
|
1527 qmFile = os.path.join(tbPath, os.path.basename(qmFile)) |
|
1528 if qmFile not in self.pdata["TRANSLATIONS"] and \ |
|
1529 os.path.exists(os.path.join(self.ppath, qmFile)): |
|
1530 self.appendFile(qmFile) |
|
1531 |
|
1532 def removeLanguageFile(self, langFile): |
|
1533 """ |
|
1534 Public slot to remove a translation from the project. |
|
1535 |
|
1536 The translation file is not deleted from the project directory. |
|
1537 |
|
1538 @param langFile the translation file to be removed (string) |
|
1539 """ |
|
1540 langFile = langFile.replace(self.ppath + os.sep, '') |
|
1541 qmFile = self.__binaryTranslationFile(langFile) |
|
1542 self.pdata["TRANSLATIONS"].remove(langFile) |
|
1543 self.__model.removeItem(langFile) |
|
1544 if qmFile: |
|
1545 try: |
|
1546 if self.pdata["TRANSLATIONSBINPATH"]: |
|
1547 qmFile = os.path.join(self.pdata["TRANSLATIONSBINPATH"][0], |
|
1548 os.path.basename(qmFile)).replace(self.ppath + os.sep, '') |
|
1549 self.pdata["TRANSLATIONS"].remove(qmFile) |
|
1550 self.__model.removeItem(qmFile) |
|
1551 except ValueError: |
|
1552 pass |
|
1553 self.setDirty(True) |
|
1554 |
|
1555 def deleteLanguageFile(self, langFile): |
|
1556 """ |
|
1557 Public slot to delete a translation from the project directory. |
|
1558 |
|
1559 @param langFile the translation file to be removed (string) |
|
1560 """ |
|
1561 langFile = langFile.replace(self.ppath + os.sep, '') |
|
1562 qmFile = self.__binaryTranslationFile(langFile) |
|
1563 |
|
1564 try: |
|
1565 fn = os.path.join(self.ppath, langFile) |
|
1566 if os.path.exists(fn): |
|
1567 os.remove(fn) |
|
1568 except IOError: |
|
1569 QMessageBox.critical(None, |
|
1570 self.trUtf8("Delete translation"), |
|
1571 self.trUtf8("<p>The selected translation file <b>{0}</b> could not be" |
|
1572 " deleted.</p>").format(langFile)) |
|
1573 return |
|
1574 |
|
1575 self.removeLanguageFile(langFile) |
|
1576 |
|
1577 # now get rid of the .qm file |
|
1578 if qmFile: |
|
1579 try: |
|
1580 if self.pdata["TRANSLATIONSBINPATH"]: |
|
1581 qmFile = os.path.join(self.pdata["TRANSLATIONSBINPATH"][0], |
|
1582 os.path.basename(qmFile)).replace(self.ppath + os.sep, '') |
|
1583 fn = os.path.join(self.ppath, qmFile) |
|
1584 if os.path.exists(fn): |
|
1585 os.remove(fn) |
|
1586 except IOError: |
|
1587 QMessageBox.critical(None, |
|
1588 self.trUtf8("Delete translation"), |
|
1589 self.trUtf8("<p>The selected translation file <b>{0}</b> could not be" |
|
1590 " deleted.</p>").format(qmFile)) |
|
1591 return |
|
1592 |
|
1593 def appendFile(self, fn, isSourceFile = False, updateModel = True): |
|
1594 """ |
|
1595 Public method to append a file to the project. |
|
1596 |
|
1597 @param fn filename to be added to the project (string) |
|
1598 @param isSourceFile flag indicating that this is a source file |
|
1599 even if it doesn't have the source extension (boolean) |
|
1600 @param updateModel flag indicating an update of the model is requested (boolean) |
|
1601 """ |
|
1602 dirty = False |
|
1603 |
|
1604 if os.path.isabs(fn): |
|
1605 # make it relative to the project root, if it starts with that path |
|
1606 newfn = fn.replace(self.ppath + os.sep, '') |
|
1607 else: |
|
1608 # assume relative paths are relative to the project root |
|
1609 newfn = fn |
|
1610 newdir = os.path.dirname(newfn) |
|
1611 |
|
1612 if isSourceFile: |
|
1613 filetype = "SOURCES" |
|
1614 else: |
|
1615 filetype = "OTHERS" |
|
1616 bfn = os.path.basename(newfn) |
|
1617 if fnmatch.fnmatch(bfn, '*.ts') or fnmatch.fnmatch(bfn, '*.qm'): |
|
1618 filetype = "TRANSLATIONS" |
|
1619 else: |
|
1620 for pattern in reversed(sorted(self.pdata["FILETYPES"].keys())): |
|
1621 if fnmatch.fnmatch(bfn, pattern): |
|
1622 filetype = self.pdata["FILETYPES"][pattern] |
|
1623 break |
|
1624 |
|
1625 if filetype == "__IGNORE__": |
|
1626 return |
|
1627 |
|
1628 if filetype in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES"]: |
|
1629 if filetype == "SOURCES": |
|
1630 if newfn not in self.pdata["SOURCES"]: |
|
1631 self.pdata["SOURCES"].append(newfn) |
|
1632 self.emit(SIGNAL('projectSourceAdded'), newfn) |
|
1633 updateModel and self.__model.addNewItem("SOURCES", newfn) |
|
1634 dirty = True |
|
1635 else: |
|
1636 updateModel and self.repopulateItem(newfn) |
|
1637 elif filetype == "FORMS": |
|
1638 if newfn not in self.pdata["FORMS"]: |
|
1639 self.pdata["FORMS"].append(newfn) |
|
1640 self.emit(SIGNAL('projectFormAdded'), newfn) |
|
1641 updateModel and self.__model.addNewItem("FORMS", newfn) |
|
1642 dirty = True |
|
1643 else: |
|
1644 updateModel and self.repopulateItem(newfn) |
|
1645 elif filetype == "INTERFACES": |
|
1646 if newfn not in self.pdata["INTERFACES"]: |
|
1647 self.pdata["INTERFACES"].append(newfn) |
|
1648 self.emit(SIGNAL('projectInterfaceAdded'), newfn) |
|
1649 updateModel and self.__model.addNewItem("INTERFACES", newfn) |
|
1650 dirty = True |
|
1651 else: |
|
1652 updateModel and self.repopulateItem(newfn) |
|
1653 elif filetype == "RESOURCES": |
|
1654 if newfn not in self.pdata["RESOURCES"]: |
|
1655 self.pdata["RESOURCES"].append(newfn) |
|
1656 self.emit(SIGNAL('projectResourceAdded'), newfn) |
|
1657 updateModel and self.__model.addNewItem("RESOURCES", newfn) |
|
1658 dirty = True |
|
1659 else: |
|
1660 updateModel and self.repopulateItem(newfn) |
|
1661 if newdir not in self.subdirs: |
|
1662 self.subdirs.append(newdir) |
|
1663 elif filetype == "TRANSLATIONS": |
|
1664 if newfn not in self.pdata["TRANSLATIONS"]: |
|
1665 self.pdata["TRANSLATIONS"].append(newfn) |
|
1666 updateModel and self.__model.addNewItem("TRANSLATIONS", newfn) |
|
1667 self.emit(SIGNAL('projectLanguageAdded'), newfn) |
|
1668 dirty = True |
|
1669 else: |
|
1670 updateModel and self.repopulateItem(newfn) |
|
1671 else: # filetype == "OTHERS" |
|
1672 if newfn not in self.pdata["OTHERS"]: |
|
1673 self.pdata['OTHERS'].append(newfn) |
|
1674 self.othersAdded(newfn, updateModel) |
|
1675 dirty = True |
|
1676 else: |
|
1677 updateModel and self.repopulateItem(newfn) |
|
1678 if newdir not in self.otherssubdirs: |
|
1679 self.otherssubdirs.append(newdir) |
|
1680 |
|
1681 if dirty: |
|
1682 self.setDirty(True) |
|
1683 |
|
1684 def addFiles(self, filter = None, startdir = None): |
|
1685 """ |
|
1686 Public slot used to add files to the project. |
|
1687 |
|
1688 @param filter filter to be used by the add file dialog |
|
1689 (string out of source, form, resource, interface, others) |
|
1690 @param startdir start directory for the selection dialog |
|
1691 """ |
|
1692 if startdir is None: |
|
1693 startdir = self.ppath |
|
1694 dlg = AddFileDialog(self, self.parent(), filter, startdir = startdir) |
|
1695 if dlg.exec_() == QDialog.Accepted: |
|
1696 fnames, target, isSource = dlg.getData() |
|
1697 if target != '': |
|
1698 for fn in fnames: |
|
1699 ext = os.path.splitext(fn)[1] |
|
1700 targetfile = os.path.join(target, os.path.basename(fn)) |
|
1701 if not Utilities.samepath(os.path.dirname(fn), target): |
|
1702 try: |
|
1703 if not os.path.isdir(target): |
|
1704 os.makedirs(target) |
|
1705 |
|
1706 if os.path.exists(targetfile): |
|
1707 res = QMessageBox.warning(None, |
|
1708 self.trUtf8("Add file"), |
|
1709 self.trUtf8("<p>The file <b>{0}</b> already" |
|
1710 " exists.</p><p>Overwrite it?</p>") |
|
1711 .format(targetfile), |
|
1712 QMessageBox.StandardButtons(\ |
|
1713 QMessageBox.No | \ |
|
1714 QMessageBox.Yes), |
|
1715 QMessageBox.No) |
|
1716 if res != QMessageBox.Yes: |
|
1717 return # don't overwrite |
|
1718 |
|
1719 shutil.copy(fn, target) |
|
1720 except IOError, why: |
|
1721 QMessageBox.critical(None, |
|
1722 self.trUtf8("Add file"), |
|
1723 self.trUtf8("<p>The selected file <b>{0}</b> could not be" |
|
1724 " added to <b>{1}</b>.</p>") |
|
1725 .format(fn, target), |
|
1726 QMessageBox.StandardButtons(\ |
|
1727 QMessageBox.Abort)) |
|
1728 return |
|
1729 |
|
1730 self.appendFile(targetfile, isSource or filter == 'source') |
|
1731 else: |
|
1732 QMessageBox.critical(None, |
|
1733 self.trUtf8("Add file"), |
|
1734 self.trUtf8("The target directory must not be empty."), |
|
1735 QMessageBox.StandardButtons(\ |
|
1736 QMessageBox.Abort)) |
|
1737 |
|
1738 def __addSingleDirectory(self, filetype, source, target, quiet = False): |
|
1739 """ |
|
1740 Private method used to add all files of a single directory to the project. |
|
1741 |
|
1742 @param filetype type of files to add (string) |
|
1743 @param source source directory (string) |
|
1744 @param target target directory (string) |
|
1745 @param quiet flag indicating quiet operations (boolean) |
|
1746 """ |
|
1747 # get all relevant filename patterns |
|
1748 patterns = [] |
|
1749 ignorePatterns = [] |
|
1750 for pattern, patterntype in self.pdata["FILETYPES"].items(): |
|
1751 if patterntype == filetype: |
|
1752 patterns.append(pattern) |
|
1753 elif patterntype == "__IGNORE__": |
|
1754 ignorePatterns.append(pattern) |
|
1755 |
|
1756 files = [] |
|
1757 for pattern in patterns: |
|
1758 sstring = "%s%s%s" % (source, os.sep, pattern) |
|
1759 files.extend(glob.glob(sstring)) |
|
1760 |
|
1761 if len(files) == 0: |
|
1762 if not quiet: |
|
1763 QMessageBox.information(None, |
|
1764 self.trUtf8("Add directory"), |
|
1765 self.trUtf8("<p>The source directory doesn't contain" |
|
1766 " any files belonging to the selected category.</p>")) |
|
1767 return |
|
1768 |
|
1769 if not Utilities.samepath(target, source) and not os.path.isdir(target): |
|
1770 try: |
|
1771 os.makedirs(target) |
|
1772 except IOError, why: |
|
1773 QMessageBox.critical(None, |
|
1774 self.trUtf8("Add directory"), |
|
1775 self.trUtf8("<p>The target directory <b>{0}</b> could not be" |
|
1776 " created.</p>") |
|
1777 .format(target), |
|
1778 QMessageBox.StandardButtons(\ |
|
1779 QMessageBox.Abort)) |
|
1780 return |
|
1781 |
|
1782 for file in files: |
|
1783 for pattern in ignorePatterns: |
|
1784 if fnmatch.fnmatch(file, pattern): |
|
1785 continue |
|
1786 |
|
1787 targetfile = os.path.join(target, os.path.basename(file)) |
|
1788 if not Utilities.samepath(target, source): |
|
1789 try: |
|
1790 if os.path.exists(targetfile): |
|
1791 res = QMessageBox.warning(None, |
|
1792 self.trUtf8("Add directory"), |
|
1793 self.trUtf8("<p>The file <b>{0}</b> already exists.</p>" |
|
1794 "<p>Overwrite it?</p>") |
|
1795 .format(targetfile), |
|
1796 QMessageBox.StandardButtons(\ |
|
1797 QMessageBox.No | \ |
|
1798 QMessageBox.Yes), |
|
1799 QMessageBox.No) |
|
1800 if res != QMessageBox.Yes: |
|
1801 continue # don't overwrite, carry on with next file |
|
1802 |
|
1803 shutil.copy(file, target) |
|
1804 except EnvironmentError: |
|
1805 continue |
|
1806 self.appendFile(targetfile) |
|
1807 |
|
1808 def __addRecursiveDirectory(self, filetype, source, target): |
|
1809 """ |
|
1810 Private method used to add all files of a directory tree. |
|
1811 |
|
1812 The tree is rooted at source to another one rooted at target. This |
|
1813 method decents down to the lowest subdirectory. |
|
1814 |
|
1815 @param filetype type of files to add (string) |
|
1816 @param source source directory (string) |
|
1817 @param target target directory (string) |
|
1818 """ |
|
1819 # first perform the addition of source |
|
1820 self.__addSingleDirectory(filetype, source, target, True) |
|
1821 |
|
1822 # now recurse into subdirectories |
|
1823 for name in os.listdir(source): |
|
1824 ns = os.path.join(source, name) |
|
1825 if os.path.isdir(ns): |
|
1826 nt = os.path.join(target, name) |
|
1827 self.__addRecursiveDirectory(filetype, ns, nt) |
|
1828 |
|
1829 def addDirectory(self, filter = None, startdir = None): |
|
1830 """ |
|
1831 Public method used to add all files of a directory to the project. |
|
1832 |
|
1833 @param filter filter to be used by the add directory dialog |
|
1834 (string out of source, form, resource, interface, others) |
|
1835 @param startdir start directory for the selection dialog (string) |
|
1836 """ |
|
1837 if startdir is None: |
|
1838 startdir = self.ppath |
|
1839 dlg = AddDirectoryDialog(self, filter, self.parent(), startdir = startdir) |
|
1840 if dlg.exec_() == QDialog.Accepted: |
|
1841 filetype, source, target, recursive = dlg.getData() |
|
1842 if target == '': |
|
1843 QMessageBox.critical(None, |
|
1844 self.trUtf8("Add directory"), |
|
1845 self.trUtf8("The target directory must not be empty."), |
|
1846 QMessageBox.StandardButtons(\ |
|
1847 QMessageBox.Abort)) |
|
1848 return |
|
1849 |
|
1850 if filetype == 'OTHERS': |
|
1851 self.__addToOthers(source) |
|
1852 return |
|
1853 |
|
1854 if source == '': |
|
1855 QMessageBox.critical(None, |
|
1856 self.trUtf8("Add directory"), |
|
1857 self.trUtf8("The source directory must not be empty."), |
|
1858 QMessageBox.StandardButtons(\ |
|
1859 QMessageBox.Abort)) |
|
1860 return |
|
1861 |
|
1862 if recursive: |
|
1863 self.__addRecursiveDirectory(filetype, source, target) |
|
1864 else: |
|
1865 self.__addSingleDirectory(filetype, source, target) |
|
1866 |
|
1867 def __addToOthers(self, fn): |
|
1868 """ |
|
1869 Private method to add file/directory to the OTHERS project data. |
|
1870 |
|
1871 @param fn filename or directoryname to add |
|
1872 """ |
|
1873 if fn: |
|
1874 # if it is below the project directory, make it relative to that |
|
1875 fn = fn.replace(self.ppath + os.sep, '') |
|
1876 |
|
1877 # if it ends with the directory separator character, remove it |
|
1878 if fn.endswith(os.sep): |
|
1879 fn = fn[:-1] |
|
1880 |
|
1881 if fn not in self.pdata["OTHERS"]: |
|
1882 self.pdata['OTHERS'].append(fn) |
|
1883 self.othersAdded(fn) |
|
1884 self.setDirty(True) |
|
1885 |
|
1886 if os.path.isdir(fn) and fn not in self.otherssubdirs: |
|
1887 self.otherssubdirs.append(fn) |
|
1888 |
|
1889 def addSourceFiles(self): |
|
1890 """ |
|
1891 Public slot to add source files to the current project. |
|
1892 """ |
|
1893 self.addFiles('source') |
|
1894 |
|
1895 def addUiFiles(self): |
|
1896 """ |
|
1897 Public slot to add forms to the current project. |
|
1898 """ |
|
1899 self.addFiles('form') |
|
1900 |
|
1901 def addIdlFiles(self): |
|
1902 """ |
|
1903 Public slot to add IDL interfaces to the current project. |
|
1904 """ |
|
1905 self.addFiles('interface') |
|
1906 |
|
1907 def addResourceFiles(self): |
|
1908 """ |
|
1909 Public slot to add Qt resources to the current project. |
|
1910 """ |
|
1911 self.addFiles('resource') |
|
1912 |
|
1913 def addOthersFiles(self): |
|
1914 """ |
|
1915 Private slot to add files to the OTHERS project data. |
|
1916 """ |
|
1917 self.addFiles('others') |
|
1918 |
|
1919 def addSourceDir(self): |
|
1920 """ |
|
1921 Public slot to add all source files of a directory to the current project. |
|
1922 """ |
|
1923 self.addDirectory('source') |
|
1924 |
|
1925 def addUiDir(self): |
|
1926 """ |
|
1927 Public slot to add all forms of a directory to the current project. |
|
1928 """ |
|
1929 self.addDirectory('form') |
|
1930 |
|
1931 def addIdlDir(self): |
|
1932 """ |
|
1933 Public slot to add all IDL interfaces of a directory to the current project. |
|
1934 """ |
|
1935 self.addDirectory('interface') |
|
1936 |
|
1937 def addResourceDir(self): |
|
1938 """ |
|
1939 Public slot to add all Qt resource files of a directory to the current project. |
|
1940 """ |
|
1941 self.addDirectory('resource') |
|
1942 |
|
1943 def addOthersDir(self): |
|
1944 """ |
|
1945 Private slot to add a directory to the OTHERS project data. |
|
1946 """ |
|
1947 self.addDirectory('others') |
|
1948 |
|
1949 def renameMainScript(self, oldfn, newfn): |
|
1950 """ |
|
1951 Public method to rename the main script. |
|
1952 |
|
1953 @param oldfn old filename (string) |
|
1954 @param newfn new filename of the main script (string) |
|
1955 """ |
|
1956 if self.pdata["MAINSCRIPT"]: |
|
1957 ofn = oldfn.replace(self.ppath + os.sep, '') |
|
1958 if ofn != self.pdata["MAINSCRIPT"][0]: |
|
1959 return |
|
1960 |
|
1961 fn = newfn.replace(self.ppath + os.sep, '') |
|
1962 self.pdata["MAINSCRIPT"] = [fn] |
|
1963 self.setDirty(True) |
|
1964 |
|
1965 def renameFile(self, oldfn, newfn = None): |
|
1966 """ |
|
1967 Public slot to rename a file of the project. |
|
1968 |
|
1969 @param oldfn old filename of the file (string) |
|
1970 @param newfn new filename of the file (string) |
|
1971 @return flag indicating success |
|
1972 """ |
|
1973 fn = oldfn.replace(self.ppath + os.sep, '') |
|
1974 isSourceFile = fn in self.pdata["SOURCES"] |
|
1975 |
|
1976 if newfn is None: |
|
1977 newfn = QFileDialog.getSaveFileName(\ |
|
1978 None, |
|
1979 self.trUtf8("Rename file"), |
|
1980 os.path.dirname(oldfn), |
|
1981 "", |
|
1982 QFileDialog.Options(QFileDialog.DontConfirmOverwrite)) |
|
1983 if not newfn: |
|
1984 return False |
|
1985 |
|
1986 if os.path.exists(newfn): |
|
1987 canceled = QMessageBox.warning(None, |
|
1988 self.trUtf8("Rename File"), |
|
1989 self.trUtf8("""<p>The file <b>{0}</b> already exists.""" |
|
1990 """ Overwrite it?</p>""") |
|
1991 .format(newfn), |
|
1992 QMessageBox.StandardButtons(\ |
|
1993 QMessageBox.No | \ |
|
1994 QMessageBox.Yes), |
|
1995 QMessageBox.No) |
|
1996 if canceled != QMessageBox.Yes: |
|
1997 return False |
|
1998 |
|
1999 try: |
|
2000 os.rename(oldfn, newfn) |
|
2001 except OSError, msg: |
|
2002 QMessageBox.critical(None, |
|
2003 self.trUtf8("Rename File"), |
|
2004 self.trUtf8("""<p>The file <b>{0}</b> could not be renamed.<br />""" |
|
2005 """Reason: {1}</p>""").format(oldfn, unicode(msg))) |
|
2006 return False |
|
2007 |
|
2008 if fn in self.pdata["SOURCES"] or \ |
|
2009 fn in self.pdata["FORMS"] or \ |
|
2010 fn in self.pdata["TRANSLATIONS"] or \ |
|
2011 fn in self.pdata["INTERFACES"] or \ |
|
2012 fn in self.pdata["RESOURCES"] or \ |
|
2013 fn in self.pdata["OTHERS"]: |
|
2014 self.renameFileInPdata(oldfn, newfn, isSourceFile) |
|
2015 |
|
2016 return True |
|
2017 |
|
2018 def renameFileInPdata(self, oldname, newname, isSourceFile = False): |
|
2019 """ |
|
2020 Public method to rename a file in the pdata structure. |
|
2021 |
|
2022 @param oldname old filename (string) |
|
2023 @param newname new filename (string) |
|
2024 @param isSourceFile flag indicating that this is a source file |
|
2025 even if it doesn't have the source extension (boolean) |
|
2026 """ |
|
2027 fn = oldname.replace(self.ppath + os.sep, '') |
|
2028 if os.path.dirname(oldname) == os.path.dirname(newname): |
|
2029 self.removeFile(oldname, False) |
|
2030 self.appendFile(newname, isSourceFile, False) |
|
2031 self.__model.renameItem(fn, newname) |
|
2032 else: |
|
2033 self.removeFile(oldname) |
|
2034 self.appendFile(newname, isSourceFile) |
|
2035 self.emit(SIGNAL('projectFileRenamed'), oldname, newname) |
|
2036 |
|
2037 self.renameMainScript(fn, newname) |
|
2038 |
|
2039 def getFiles(self, start): |
|
2040 """ |
|
2041 Public method to get all files starting with a common prefix. |
|
2042 |
|
2043 @param start prefix (string) |
|
2044 """ |
|
2045 filelist = [] |
|
2046 start = start.replace(self.ppath + os.sep, '') |
|
2047 for key in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES", "OTHERS"]: |
|
2048 for entry in self.pdata[key][:]: |
|
2049 if entry.startswith(start): |
|
2050 filelist.append(os.path.join(self.ppath, entry)) |
|
2051 return filelist |
|
2052 |
|
2053 def copyDirectory(self, olddn, newdn): |
|
2054 """ |
|
2055 Public slot to copy a directory. |
|
2056 |
|
2057 @param olddn original directory name (string) |
|
2058 @param newdn new directory name (string) |
|
2059 """ |
|
2060 olddn = olddn.replace(self.ppath + os.sep, '') |
|
2061 newdn = newdn.replace(self.ppath + os.sep, '') |
|
2062 for key in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES", "OTHERS"]: |
|
2063 for entry in self.pdata[key][:]: |
|
2064 if entry.startswith(olddn): |
|
2065 entry = entry.replace(olddn, newdn) |
|
2066 self.appendFile(os.path.join(self.ppath, entry), key == "SOURCES") |
|
2067 self.setDirty(True) |
|
2068 |
|
2069 def moveDirectory(self, olddn, newdn): |
|
2070 """ |
|
2071 Public slot to move a directory. |
|
2072 |
|
2073 @param olddn old directory name (string) |
|
2074 @param newdn new directory name (string) |
|
2075 """ |
|
2076 olddn = olddn.replace(self.ppath + os.sep, '') |
|
2077 newdn = newdn.replace(self.ppath + os.sep, '') |
|
2078 typeStrings = [] |
|
2079 for key in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES", "OTHERS"]: |
|
2080 for entry in self.pdata[key][:]: |
|
2081 if entry.startswith(olddn): |
|
2082 if key not in typeStrings: |
|
2083 typeStrings.append(key) |
|
2084 self.pdata[key].remove(entry) |
|
2085 entry = entry.replace(olddn, newdn) |
|
2086 self.pdata[key].append(entry) |
|
2087 if key == "OTHERS": |
|
2088 if newdn not in self.otherssubdirs: |
|
2089 self.otherssubdirs.append(newdn) |
|
2090 else: |
|
2091 if newdn not in self.subdirs: |
|
2092 self.subdirs.append(newdn) |
|
2093 self.setDirty(True) |
|
2094 typeString = typeStrings[0] |
|
2095 del typeStrings[0] |
|
2096 self.__model.removeItem(olddn) |
|
2097 self.__model.addNewItem(typeString, newdn, typeStrings) |
|
2098 self.emit(SIGNAL('directoryRemoved'), olddn) |
|
2099 |
|
2100 def removeFile(self, fn, updateModel = True): |
|
2101 """ |
|
2102 Public slot to remove a file from the project. |
|
2103 |
|
2104 The file is not deleted from the project directory. |
|
2105 |
|
2106 @param fn filename to be removed from the project |
|
2107 @param updateModel flag indicating an update of the model is requested (boolean) |
|
2108 """ |
|
2109 fn = fn.replace(self.ppath + os.sep, '') |
|
2110 dirty = True |
|
2111 if fn in self.pdata["SOURCES"]: |
|
2112 self.pdata["SOURCES"].remove(fn) |
|
2113 elif fn in self.pdata["FORMS"]: |
|
2114 self.pdata["FORMS"].remove(fn) |
|
2115 elif fn in self.pdata["INTERFACES"]: |
|
2116 self.pdata["INTERFACES"].remove(fn) |
|
2117 elif fn in self.pdata["RESOURCES"]: |
|
2118 self.pdata["RESOURCES"].remove(fn) |
|
2119 elif fn in self.pdata["OTHERS"]: |
|
2120 self.pdata["OTHERS"].remove(fn) |
|
2121 elif fn in self.pdata["TRANSLATIONS"]: |
|
2122 self.pdata["TRANSLATIONS"].remove(fn) |
|
2123 else: |
|
2124 dirty = False |
|
2125 updateModel and self.__model.removeItem(fn) |
|
2126 if dirty: |
|
2127 self.setDirty(True) |
|
2128 |
|
2129 def removeDirectory(self, dn): |
|
2130 """ |
|
2131 Public slot to remove a directory from the project. |
|
2132 |
|
2133 The directory is not deleted from the project directory. |
|
2134 |
|
2135 @param dn directory name to be removed from the project |
|
2136 """ |
|
2137 dirty = False |
|
2138 dn = dn.replace(self.ppath + os.sep, '') |
|
2139 for entry in self.pdata["OTHERS"][:]: |
|
2140 if entry.startswith(dn): |
|
2141 self.pdata["OTHERS"].remove(entry) |
|
2142 dirty = True |
|
2143 if not dn.endswith(os.sep): |
|
2144 dn2 = dn + os.sep |
|
2145 else: |
|
2146 dn2 = dn |
|
2147 for key in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES", ]: |
|
2148 for entry in self.pdata[key][:]: |
|
2149 if entry.startswith(dn2): |
|
2150 self.pdata[key].remove(entry) |
|
2151 dirty = True |
|
2152 self.__model.removeItem(dn) |
|
2153 if dirty: |
|
2154 self.setDirty(True) |
|
2155 self.emit(SIGNAL('directoryRemoved'), dn) |
|
2156 |
|
2157 def deleteFile(self, fn): |
|
2158 """ |
|
2159 Public slot to delete a file from the project directory. |
|
2160 |
|
2161 @param fn filename to be deleted from the project |
|
2162 @return flag indicating success (boolean) |
|
2163 """ |
|
2164 try: |
|
2165 os.remove(os.path.join(self.ppath, fn)) |
|
2166 dummy, ext = os.path.splitext(fn) |
|
2167 if ext == '.ui': |
|
2168 fn2 = os.path.join(self.ppath, '%s.h' % fn) |
|
2169 if os.path.isfile(fn2): |
|
2170 os.remove(fn2) |
|
2171 except EnvironmentError: |
|
2172 QMessageBox.critical(None, |
|
2173 self.trUtf8("Delete file"), |
|
2174 self.trUtf8("<p>The selected file <b>{0}</b> could not be deleted.</p>") |
|
2175 .format(fn)) |
|
2176 return False |
|
2177 |
|
2178 self.removeFile(fn) |
|
2179 if ext == '.ui': |
|
2180 self.removeFile(fn + '.h') |
|
2181 return True |
|
2182 |
|
2183 def deleteDirectory(self, dn): |
|
2184 """ |
|
2185 Public slot to delete a directory from the project directory. |
|
2186 |
|
2187 @param dn directory name to be removed from the project |
|
2188 @return flag indicating success (boolean) |
|
2189 """ |
|
2190 if not os.path.isabs(dn): |
|
2191 dn = os.path.join(self.ppath, dn) |
|
2192 try: |
|
2193 shutil.rmtree(dn, True) |
|
2194 except EnvironmentError: |
|
2195 QMessageBox.critical(None, |
|
2196 self.trUtf8("Delete directory"), |
|
2197 self.trUtf8("<p>The selected directory <b>{0}</b> could not be" |
|
2198 " deleted.</p>").format(fn)) |
|
2199 return False |
|
2200 |
|
2201 self.removeDirectory(dn) |
|
2202 return True |
|
2203 |
|
2204 def hasEntry(self, fn): |
|
2205 """ |
|
2206 Public method to check the project for a file. |
|
2207 |
|
2208 @param fn filename to be checked (string) |
|
2209 @return flag indicating, if the project contains the file (boolean) |
|
2210 """ |
|
2211 fn = fn.replace(self.ppath + os.sep, '') |
|
2212 if fn in self.pdata["SOURCES"] or \ |
|
2213 fn in self.pdata["FORMS"] or \ |
|
2214 fn in self.pdata["INTERFACES"] or \ |
|
2215 fn in self.pdata["RESOURCES"] or \ |
|
2216 fn in self.pdata["OTHERS"]: |
|
2217 return True |
|
2218 else: |
|
2219 return False |
|
2220 |
|
2221 def newProject(self): |
|
2222 """ |
|
2223 Public slot to built a new project. |
|
2224 |
|
2225 This method displays the new project dialog and initializes |
|
2226 the project object with the data entered. |
|
2227 """ |
|
2228 if not self.checkDirty(): |
|
2229 return |
|
2230 |
|
2231 dlg = PropertiesDialog(self, True) |
|
2232 if dlg.exec_() == QDialog.Accepted: |
|
2233 self.closeProject() |
|
2234 dlg.storeData() |
|
2235 self.pdata["VCS"] = ['None'] |
|
2236 self.opened = True |
|
2237 if not self.pdata["FILETYPES"]: |
|
2238 self.initFileTypes() |
|
2239 self.setDirty(True) |
|
2240 self.closeAct.setEnabled(True) |
|
2241 self.saveasAct.setEnabled(True) |
|
2242 self.actGrp2.setEnabled(True) |
|
2243 self.propsAct.setEnabled(True) |
|
2244 self.userPropsAct.setEnabled(True) |
|
2245 self.filetypesAct.setEnabled(True) |
|
2246 self.lexersAct.setEnabled(True) |
|
2247 self.sessActGrp.setEnabled(False) |
|
2248 self.dbgActGrp.setEnabled(True) |
|
2249 self.menuDebuggerAct.setEnabled(True) |
|
2250 self.menuSessionAct.setEnabled(False) |
|
2251 self.menuCheckAct.setEnabled(True) |
|
2252 self.menuShowAct.setEnabled(True) |
|
2253 self.menuDiagramAct.setEnabled(True) |
|
2254 self.menuApidocAct.setEnabled(True) |
|
2255 self.menuPackagersAct.setEnabled(True) |
|
2256 self.pluginGrp.setEnabled(self.pdata["PROJECTTYPE"][0] == "E4Plugin") |
|
2257 |
|
2258 self.emit(SIGNAL("projectAboutToBeCreated")) |
|
2259 |
|
2260 # create the project directory if it doesn't exist already |
|
2261 if not os.path.isdir(self.ppath): |
|
2262 try: |
|
2263 os.makedirs(self.ppath) |
|
2264 except EnvironmentError: |
|
2265 QMessageBox.critical(None, |
|
2266 self.trUtf8("Create project directory"), |
|
2267 self.trUtf8("<p>The project directory <b>{0}</b> could not" |
|
2268 " be created.</p>") |
|
2269 .format(self.ppath)) |
|
2270 self.vcs = self.initVCS() |
|
2271 return |
|
2272 # create an empty __init__.py file to make it a Python package |
|
2273 # (only for Python and Python3) |
|
2274 if self.pdata["PROGLANGUAGE"][0] in ["Python", "Python3"]: |
|
2275 fn = os.path.join(self.ppath, "__init__.py") |
|
2276 f = open(fn, "w") |
|
2277 f.close() |
|
2278 self.appendFile(fn, True) |
|
2279 tpd = os.path.join(self.ppath, self.translationsRoot) |
|
2280 if not self.translationsRoot.endswith(os.sep): |
|
2281 tpd = os.path.dirname(tpd) |
|
2282 if not os.path.isdir(tpd): |
|
2283 os.makedirs(tpd) |
|
2284 if self.pdata["TRANSLATIONSBINPATH"]: |
|
2285 tpd = os.path.join(self.ppath, self.pdata["TRANSLATIONSBINPATH"][0]) |
|
2286 if not os.path.isdir(tpd): |
|
2287 os.makedirs(tpd) |
|
2288 |
|
2289 # create management directory if not present |
|
2290 mgmtDir = self.getProjectManagementDir() |
|
2291 if not os.path.exists(mgmtDir): |
|
2292 os.makedirs(mgmtDir) |
|
2293 |
|
2294 self.saveProject() |
|
2295 else: |
|
2296 # create management directory if not present |
|
2297 mgmtDir = self.getProjectManagementDir() |
|
2298 if not os.path.exists(mgmtDir): |
|
2299 os.makedirs(mgmtDir) |
|
2300 |
|
2301 try: |
|
2302 ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0]) |
|
2303 if os.path.exists(ms): |
|
2304 self.appendFile(ms) |
|
2305 except IndexError: |
|
2306 ms = "" |
|
2307 |
|
2308 # add existing files to the project |
|
2309 res = QMessageBox.question(None, |
|
2310 self.trUtf8("New Project"), |
|
2311 self.trUtf8("""Add existing files to the project?"""), |
|
2312 QMessageBox.StandardButtons(\ |
|
2313 QMessageBox.No | \ |
|
2314 QMessageBox.Yes), |
|
2315 QMessageBox.Yes) |
|
2316 if res == QMessageBox.Yes: |
|
2317 self.newProjectAddFiles(ms) |
|
2318 # create an empty __init__.py file to make it a Python package |
|
2319 # if none exists (only for Python and Python3) |
|
2320 if self.pdata["PROGLANGUAGE"][0] in ["Python", "Python3"]: |
|
2321 fn = os.path.join(self.ppath, "__init__.py") |
|
2322 if not os.path.exists(fn): |
|
2323 f = open(fn, "w") |
|
2324 f.close() |
|
2325 self.appendFile(fn, True) |
|
2326 self.saveProject() |
|
2327 |
|
2328 # check, if the existing project directory is already under |
|
2329 # VCS control |
|
2330 pluginManager = e4App().getObject("PluginManager") |
|
2331 for indicator, vcsData in pluginManager.getVcsSystemIndicators().items(): |
|
2332 if os.path.exists(os.path.join(self.ppath, indicator)): |
|
2333 if len(vcsData) > 1: |
|
2334 vcsList = [] |
|
2335 for vcsSystemStr, vcsSystemDisplay in vcsData: |
|
2336 vcsList.append(vcsSystemDisplay) |
|
2337 res, vcs_ok = QInputDialog.getItem(\ |
|
2338 None, |
|
2339 self.trUtf8("New Project"), |
|
2340 self.trUtf8("Select Version Control System"), |
|
2341 vcsList, |
|
2342 0, False) |
|
2343 if vcs_ok: |
|
2344 for vcsSystemStr, vcsSystemDisplay in vcsData: |
|
2345 if res == vcsSystemDisplay: |
|
2346 vcsSystem = vcsSystemStr |
|
2347 break |
|
2348 else: |
|
2349 vcsSystem = "None" |
|
2350 else: |
|
2351 vcsSystem = "None" |
|
2352 else: |
|
2353 vcsSystem = vcsData[0][1] |
|
2354 self.pdata["VCS"] = [vcsSystem] |
|
2355 self.vcs = self.initVCS() |
|
2356 self.setDirty(True) |
|
2357 if self.vcs is not None: |
|
2358 # edit VCS command options |
|
2359 vcores = QMessageBox.question(None, |
|
2360 self.trUtf8("New Project"), |
|
2361 self.trUtf8("""Would you like to edit the VCS""" |
|
2362 """ command options?"""), |
|
2363 QMessageBox.StandardButtons(\ |
|
2364 QMessageBox.No | \ |
|
2365 QMessageBox.Yes), |
|
2366 QMessageBox.No) |
|
2367 if vcores == QMessageBox.Yes: |
|
2368 codlg = vcsCommandOptionsDialog(self.vcs) |
|
2369 if codlg.exec_() == QDialog.Accepted: |
|
2370 self.vcs.vcsSetOptions(codlg.getOptions()) |
|
2371 # add project file to repository |
|
2372 if res == 0: |
|
2373 apres = QMessageBox.question(None, |
|
2374 self.trUtf8("New project"), |
|
2375 self.trUtf8("Shall the project file be added" |
|
2376 " to the repository?"), |
|
2377 QMessageBox.StandardButtons(\ |
|
2378 QMessageBox.No | \ |
|
2379 QMessageBox.Yes), |
|
2380 QMessageBox.Yes) |
|
2381 if apres == QMessageBox.Yes: |
|
2382 self.saveProject() |
|
2383 self.vcs.vcsAdd(self.pfile) |
|
2384 else: |
|
2385 self.pdata["VCS"] = ['None'] |
|
2386 self.saveProject() |
|
2387 break |
|
2388 |
|
2389 # put the project under VCS control |
|
2390 if self.vcs is None: |
|
2391 vcsSystemsDict = e4App().getObject("PluginManager")\ |
|
2392 .getPluginDisplayStrings("version_control") |
|
2393 vcsSystemsDisplay = [self.trUtf8("None")] |
|
2394 keys = sorted(vcsSystemsDict.keys()) |
|
2395 for key in keys: |
|
2396 vcsSystemsDisplay.append(vcsSystemsDict[key]) |
|
2397 vcsSelected, ok = QInputDialog.getItem(\ |
|
2398 None, |
|
2399 self.trUtf8("New Project"), |
|
2400 self.trUtf8("Select version control system for the project"), |
|
2401 vcsSystemsDisplay, |
|
2402 0, False) |
|
2403 if ok and vcsSelected != self.trUtf8("None"): |
|
2404 for vcsSystem, vcsSystemDisplay in vcsSystemsDict.items(): |
|
2405 if vcsSystemDisplay == vcsSelected: |
|
2406 break |
|
2407 else: |
|
2408 vcsSystem = "None" |
|
2409 self.pdata["VCS"] = [vcsSystem] |
|
2410 else: |
|
2411 self.pdata["VCS"] = ['None'] |
|
2412 self.vcs = self.initVCS() |
|
2413 if self.vcs is not None: |
|
2414 vcsdlg = self.vcs.vcsOptionsDialog(self, self.name) |
|
2415 if vcsdlg.exec_() == QDialog.Accepted: |
|
2416 vcsDataDict = vcsdlg.getData() |
|
2417 else: |
|
2418 self.pdata["VCS"] = ['None'] |
|
2419 self.vcs = self.initVCS() |
|
2420 self.setDirty(True) |
|
2421 if self.vcs is not None: |
|
2422 # edit VCS command options |
|
2423 vcores = QMessageBox.question(None, |
|
2424 self.trUtf8("New Project"), |
|
2425 self.trUtf8("""Would you like to edit the VCS command""" |
|
2426 """ options?"""), |
|
2427 QMessageBox.StandardButtons(\ |
|
2428 QMessageBox.No | \ |
|
2429 QMessageBox.Yes), |
|
2430 QMessageBox.No) |
|
2431 if vcores == QMessageBox.Yes: |
|
2432 codlg = vcsCommandOptionsDialog(self.vcs) |
|
2433 if codlg.exec_() == QDialog.Accepted: |
|
2434 self.vcs.vcsSetOptions(codlg.getOptions()) |
|
2435 |
|
2436 # create the project in the VCS |
|
2437 self.vcs.vcsSetDataFromDict(vcsDataDict) |
|
2438 self.saveProject() |
|
2439 self.vcs.vcsConvertProject(vcsDataDict, self) |
|
2440 else: |
|
2441 self.emit(SIGNAL('newProjectHooks')) |
|
2442 self.emit(SIGNAL('newProject')) |
|
2443 |
|
2444 else: |
|
2445 self.emit(SIGNAL('newProjectHooks')) |
|
2446 self.emit(SIGNAL('newProject')) |
|
2447 |
|
2448 |
|
2449 def newProjectAddFiles(self, mainscript): |
|
2450 """ |
|
2451 Public method to add files to a new project. |
|
2452 |
|
2453 @param mainscript name of the mainscript (string) |
|
2454 """ |
|
2455 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
|
2456 QApplication.processEvents() |
|
2457 |
|
2458 # search the project directory for files with known extensions |
|
2459 filespecs = self.pdata["FILETYPES"].keys() |
|
2460 for filespec in filespecs: |
|
2461 files = Utilities.direntries(self.ppath, True, filespec) |
|
2462 for file in files: |
|
2463 self.appendFile(file) |
|
2464 |
|
2465 # special handling for translation files |
|
2466 if self.translationsRoot: |
|
2467 tpd = os.path.join(self.ppath, self.translationsRoot) |
|
2468 if not self.translationsRoot.endswith(os.sep): |
|
2469 tpd = os.path.dirname(tpd) |
|
2470 else: |
|
2471 tpd = self.ppath |
|
2472 tslist = [] |
|
2473 if self.pdata["TRANSLATIONPATTERN"]: |
|
2474 pattern = os.path.basename(self.pdata["TRANSLATIONPATTERN"][0]) |
|
2475 if "%language%" in pattern: |
|
2476 pattern = pattern.replace("%language%", "*") |
|
2477 else: |
|
2478 tpd = self.pdata["TRANSLATIONPATTERN"][0].split("%language%")[0] |
|
2479 else: |
|
2480 pattern = "*.ts" |
|
2481 tslist.extend(Utilities.direntries(tpd, True, pattern)) |
|
2482 pattern = self.__binaryTranslationFile(pattern) |
|
2483 if pattern: |
|
2484 tslist.extend(Utilities.direntries(tpd, True, pattern)) |
|
2485 if tslist: |
|
2486 if '_' in os.path.basename(tslist[0]): |
|
2487 # the first entry determines the mainscript name |
|
2488 mainscriptname = os.path.splitext(mainscript)[0] or \ |
|
2489 os.path.basename(tslist[0]).split('_')[0] |
|
2490 self.pdata["TRANSLATIONPATTERN"] = \ |
|
2491 [os.path.join(os.path.dirname(tslist[0]), |
|
2492 "%s_%%language%%%s" % (os.path.basename(tslist[0]).split('_')[0], |
|
2493 os.path.splitext(tslist[0])[1]))] |
|
2494 else: |
|
2495 pattern, ok = QInputDialog.getText(\ |
|
2496 None, |
|
2497 self.trUtf8("Translation Pattern"), |
|
2498 self.trUtf8("Enter the path pattern for translation files " |
|
2499 "(use '%language%' in place of the language code):"), |
|
2500 QLineEdit.Normal, |
|
2501 tslist[0]) |
|
2502 if not pattern.isEmpty: |
|
2503 self.pdata["TRANSLATIONPATTERN"] = [pattern] |
|
2504 self.pdata["TRANSLATIONPATTERN"][0] = \ |
|
2505 self.pdata["TRANSLATIONPATTERN"][0].replace(self.ppath + os.sep, "") |
|
2506 pattern = self.pdata["TRANSLATIONPATTERN"][0].replace("%language%", "*") |
|
2507 for ts in tslist: |
|
2508 if fnmatch.fnmatch(ts, pattern): |
|
2509 self.pdata["TRANSLATIONS"].append(ts) |
|
2510 self.emit(SIGNAL('projectLanguageAdded'), ts) |
|
2511 if self.pdata["PROGLANGUAGE"][0] in ["Python", "Python3"]: |
|
2512 self.pdata["MAINSCRIPT"] = ['%s.py' % mainscriptname] |
|
2513 elif self.pdata["PROGLANGUAGE"][0] == "Ruby": |
|
2514 self.pdata["MAINSCRIPT"] = ['%s.rb' % mainscriptname] |
|
2515 if self.pdata["TRANSLATIONSBINPATH"]: |
|
2516 tpd = os.path.join(self.ppath, |
|
2517 self.pdata["TRANSLATIONSBINPATH"][0]) |
|
2518 pattern = os.path.splitext( |
|
2519 os.path.basename(self.pdata["TRANSLATIONPATTERN"][0])) |
|
2520 pattern = self.__binaryTranslationFile(pattern) |
|
2521 qmlist = Utilities.direntries(tpd, True, pattern) |
|
2522 for qm in qmlist: |
|
2523 self.pdata["TRANSLATIONS"].append(qm) |
|
2524 self.emit(SIGNAL('projectLanguageAdded'), qm) |
|
2525 self.setDirty(True) |
|
2526 QApplication.restoreOverrideCursor() |
|
2527 |
|
2528 def __showProperties(self): |
|
2529 """ |
|
2530 Private slot to display the properties dialog. |
|
2531 """ |
|
2532 dlg = PropertiesDialog(self, False) |
|
2533 if dlg.exec_() == QDialog.Accepted: |
|
2534 projectType = self.pdata["PROJECTTYPE"][0] |
|
2535 dlg.storeData() |
|
2536 self.setDirty(True) |
|
2537 try: |
|
2538 ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0]) |
|
2539 if os.path.exists(ms): |
|
2540 self.appendFile(ms) |
|
2541 except IndexError: |
|
2542 pass |
|
2543 |
|
2544 if self.pdata["PROJECTTYPE"][0] != projectType: |
|
2545 # reinitialize filetype associations |
|
2546 self.initFileTypes() |
|
2547 |
|
2548 if self.translationsRoot: |
|
2549 tp = os.path.join(self.ppath, self.translationsRoot) |
|
2550 if not self.translationsRoot.endswith(os.sep): |
|
2551 tp = os.path.dirname(tp) |
|
2552 else: |
|
2553 tp = self.ppath |
|
2554 if not os.path.isdir(tp): |
|
2555 os.makedirs(tp) |
|
2556 if tp != self.ppath and tp not in self.subdirs: |
|
2557 self.subdirs.append(tp) |
|
2558 |
|
2559 if self.pdata["TRANSLATIONSBINPATH"]: |
|
2560 tp = os.path.join(self.ppath, self.pdata["TRANSLATIONSBINPATH"][0]) |
|
2561 if not os.path.isdir(tp): |
|
2562 os.makedirs(tp) |
|
2563 if tp != self.ppath and tp not in self.subdirs: |
|
2564 self.subdirs.append(tp) |
|
2565 |
|
2566 self.pluginGrp.setEnabled(self.pdata["PROJECTTYPE"][0] == "E4Plugin") |
|
2567 |
|
2568 self.__model.projectPropertiesChanged() |
|
2569 self.emit(SIGNAL('projectPropertiesChanged')) |
|
2570 |
|
2571 def __showUserProperties(self): |
|
2572 """ |
|
2573 Private slot to display the user specific properties dialog. |
|
2574 """ |
|
2575 vcsSystem = self.pdata["VCS"] and self.pdata["VCS"][0] or None |
|
2576 vcsSystemOverride = \ |
|
2577 self.pudata["VCSOVERRIDE"] and self.pudata["VCSOVERRIDE"][0] or None |
|
2578 |
|
2579 dlg = UserPropertiesDialog(self) |
|
2580 if dlg.exec_() == QDialog.Accepted: |
|
2581 dlg.storeData() |
|
2582 |
|
2583 if (self.pdata["VCS"] and \ |
|
2584 self.pdata["VCS"][0] != vcsSystem) or \ |
|
2585 (self.pudata["VCSOVERRIDE"] and \ |
|
2586 self.pudata["VCSOVERRIDE"][0] != vcsSystemOverride) or \ |
|
2587 (vcsSystemOverride is not None and \ |
|
2588 len(self.pudata["VCSOVERRIDE"]) == 0): |
|
2589 # stop the VCS monitor thread and shutdown VCS |
|
2590 if self.vcs is not None: |
|
2591 self.vcs.stopStatusMonitor() |
|
2592 self.disconnect(self.vcs, |
|
2593 SIGNAL("vcsStatusMonitorData(QStringList)"), |
|
2594 self.__model.changeVCSStates) |
|
2595 self.disconnect(self.vcs, |
|
2596 SIGNAL("vcsStatusMonitorStatus(QString, QString)"), |
|
2597 self.__statusMonitorStatus) |
|
2598 self.vcs.vcsShutdown() |
|
2599 self.vcs = None |
|
2600 e4App().getObject("PluginManager").deactivateVcsPlugins() |
|
2601 # reinit VCS |
|
2602 self.vcs = self.initVCS() |
|
2603 # start the VCS monitor thread |
|
2604 if self.vcs is not None: |
|
2605 self.vcs.startStatusMonitor(self) |
|
2606 self.connect(self.vcs, |
|
2607 SIGNAL("vcsStatusMonitorData(QStringList)"), |
|
2608 self.__model.changeVCSStates) |
|
2609 self.connect(self.vcs, |
|
2610 SIGNAL("vcsStatusMonitorStatus(QString, QString)"), |
|
2611 self.__statusMonitorStatus) |
|
2612 self.emit(SIGNAL("reinitVCS")) |
|
2613 |
|
2614 if self.pudata["VCSSTATUSMONITORINTERVAL"]: |
|
2615 self.setStatusMonitorInterval(\ |
|
2616 self.pudata["VCSSTATUSMONITORINTERVAL"][0]) |
|
2617 else: |
|
2618 self.setStatusMonitorInterval(\ |
|
2619 Preferences.getVCS("StatusMonitorInterval")) |
|
2620 |
|
2621 def __showFiletypeAssociations(self): |
|
2622 """ |
|
2623 Public slot to display the filetype association dialog. |
|
2624 """ |
|
2625 dlg = FiletypeAssociationDialog(self) |
|
2626 if dlg.exec_() == QDialog.Accepted: |
|
2627 dlg.transferData() |
|
2628 self.setDirty(True) |
|
2629 |
|
2630 def __showLexerAssociations(self): |
|
2631 """ |
|
2632 Public slot to display the lexer association dialog. |
|
2633 """ |
|
2634 dlg = LexerAssociationDialog(self) |
|
2635 if dlg.exec_() == QDialog.Accepted: |
|
2636 dlg.transferData() |
|
2637 self.setDirty(True) |
|
2638 self.emit(SIGNAL("lexerAssociationsChanged")) |
|
2639 |
|
2640 def getEditorLexerAssoc(self, filename): |
|
2641 """ |
|
2642 Public method to retrieve a lexer association. |
|
2643 |
|
2644 @param filename filename used to determine the associated lexer language (string) |
|
2645 @return the requested lexer language (string) |
|
2646 """ |
|
2647 # try user settings first |
|
2648 for pattern, language in self.pdata["LEXERASSOCS"].items(): |
|
2649 if fnmatch.fnmatch(filename, pattern): |
|
2650 return language |
|
2651 |
|
2652 # try project type specific defaults next |
|
2653 projectType = self.pdata["PROJECTTYPE"][0] |
|
2654 try: |
|
2655 if self.__lexerAssociationCallbacks[projectType] is not None: |
|
2656 return self.__lexerAssociationCallbacks[projectType](filename) |
|
2657 except KeyError: |
|
2658 pass |
|
2659 |
|
2660 # return empty string to signal to use the global setting |
|
2661 return "" |
|
2662 |
|
2663 def openProject(self, fn = None, restoreSession = True, reopen = False): |
|
2664 """ |
|
2665 Public slot to open a project. |
|
2666 |
|
2667 @param fn optional filename of the project file to be read |
|
2668 @param restoreSession flag indicating to restore the project |
|
2669 session (boolean) |
|
2670 @keyparam reopen flag indicating a reopening of the project (boolean) |
|
2671 """ |
|
2672 if not self.checkDirty(): |
|
2673 return |
|
2674 |
|
2675 if fn is None: |
|
2676 fn = QFileDialog.getOpenFileName(\ |
|
2677 self.parent(), |
|
2678 self.trUtf8("Open project"), |
|
2679 "", |
|
2680 self.trUtf8("Project Files (*.e4p *.e4pz *.e3p *.e3pz)")) |
|
2681 |
|
2682 QApplication.processEvents() |
|
2683 |
|
2684 if fn: |
|
2685 if self.closeProject(): |
|
2686 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
|
2687 QApplication.processEvents() |
|
2688 if self.__readProject(fn): |
|
2689 self.opened = True |
|
2690 if not self.pdata["FILETYPES"]: |
|
2691 self.initFileTypes() |
|
2692 else: |
|
2693 self.updateFileTypes() |
|
2694 |
|
2695 QApplication.restoreOverrideCursor() |
|
2696 QApplication.processEvents() |
|
2697 |
|
2698 # read a user specific project file |
|
2699 self.__readUserProperties() |
|
2700 |
|
2701 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
|
2702 QApplication.processEvents() |
|
2703 |
|
2704 self.vcs = self.initVCS() |
|
2705 if self.vcs is None: |
|
2706 # check, if project is version controlled |
|
2707 pluginManager = e4App().getObject("PluginManager") |
|
2708 for indicator, vcsData in \ |
|
2709 pluginManager.getVcsSystemIndicators().items(): |
|
2710 if os.path.exists(os.path.join(self.ppath, indicator)): |
|
2711 if len(vcsData) > 1: |
|
2712 vcsList = [] |
|
2713 for vcsSystemStr, vcsSystemDisplay in vcsData: |
|
2714 vcsList.append(vcsSystemDisplay) |
|
2715 QApplication.restoreOverrideCursor() |
|
2716 res, vcs_ok = QInputDialog.getItem(\ |
|
2717 None, |
|
2718 self.trUtf8("New Project"), |
|
2719 self.trUtf8("Select Version Control System"), |
|
2720 vcsList, |
|
2721 0, False) |
|
2722 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
|
2723 QApplication.processEvents() |
|
2724 if vcs_ok: |
|
2725 for vcsSystemStr, vcsSystemDisplay in vcsData: |
|
2726 if res == vcsSystemDisplay: |
|
2727 vcsSystem = vcsSystemStr |
|
2728 break |
|
2729 else: |
|
2730 vcsSystem = "None" |
|
2731 else: |
|
2732 vcsSystem = "None" |
|
2733 else: |
|
2734 vcsSystem = vcsData[0][0] |
|
2735 self.pdata["VCS"] = [vcsSystem] |
|
2736 self.vcs = self.initVCS() |
|
2737 self.setDirty(True) |
|
2738 if self.vcs is not None and \ |
|
2739 self.vcs.vcsRegisteredState(self.ppath) != self.vcs.canBeCommitted: |
|
2740 self.pdata["VCS"] = ['None'] |
|
2741 self.vcs = self.initVCS() |
|
2742 self.closeAct.setEnabled(True) |
|
2743 self.saveasAct.setEnabled(True) |
|
2744 self.actGrp2.setEnabled(True) |
|
2745 self.propsAct.setEnabled(True) |
|
2746 self.userPropsAct.setEnabled(True) |
|
2747 self.filetypesAct.setEnabled(True) |
|
2748 self.lexersAct.setEnabled(True) |
|
2749 self.sessActGrp.setEnabled(True) |
|
2750 self.dbgActGrp.setEnabled(True) |
|
2751 self.menuDebuggerAct.setEnabled(True) |
|
2752 self.menuSessionAct.setEnabled(True) |
|
2753 self.menuCheckAct.setEnabled(True) |
|
2754 self.menuShowAct.setEnabled(True) |
|
2755 self.menuDiagramAct.setEnabled(True) |
|
2756 self.menuApidocAct.setEnabled(True) |
|
2757 self.menuPackagersAct.setEnabled(True) |
|
2758 self.pluginGrp.setEnabled(self.pdata["PROJECTTYPE"][0] == "E4Plugin") |
|
2759 |
|
2760 self.__model.projectOpened() |
|
2761 self.emit(SIGNAL('projectOpenedHooks')) |
|
2762 self.emit(SIGNAL('projectOpened')) |
|
2763 |
|
2764 QApplication.restoreOverrideCursor() |
|
2765 |
|
2766 if Preferences.getProject("SearchNewFiles"): |
|
2767 self.__doSearchNewFiles() |
|
2768 |
|
2769 # read a project tasks file |
|
2770 self.__readTasks() |
|
2771 self.ui.taskViewer.setProjectOpen(True) |
|
2772 |
|
2773 if restoreSession: |
|
2774 # open the main script |
|
2775 if len(self.pdata["MAINSCRIPT"]) == 1: |
|
2776 self.emit(SIGNAL('sourceFile'), |
|
2777 os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0])) |
|
2778 |
|
2779 # open a project session file being quiet about errors |
|
2780 if reopen: |
|
2781 self.__readSession(quiet = True, indicator = "_tmp") |
|
2782 elif Preferences.getProject("AutoLoadSession"): |
|
2783 self.__readSession(quiet = True) |
|
2784 |
|
2785 # open a project debugger properties file being quiet about errors |
|
2786 if Preferences.getProject("AutoLoadDbgProperties"): |
|
2787 self.__readDebugProperties(True) |
|
2788 |
|
2789 # start the VCS monitor thread |
|
2790 if self.vcs is not None: |
|
2791 self.vcs.startStatusMonitor(self) |
|
2792 self.connect(self.vcs, |
|
2793 SIGNAL("vcsStatusMonitorData(QStringList)"), |
|
2794 self.__model.changeVCSStates) |
|
2795 self.connect(self.vcs, |
|
2796 SIGNAL("vcsStatusMonitorStatus(QString, QString)"), |
|
2797 self.__statusMonitorStatus) |
|
2798 else: |
|
2799 QApplication.restoreOverrideCursor() |
|
2800 |
|
2801 def reopenProject(self): |
|
2802 """ |
|
2803 Public slot to reopen the current project. |
|
2804 """ |
|
2805 projectFile = self.pfile |
|
2806 res = self.closeProject(reopen = True) |
|
2807 if res: |
|
2808 self.openProject(projectFile, reopen = True) |
|
2809 |
|
2810 def saveProject(self): |
|
2811 """ |
|
2812 Public slot to save the current project. |
|
2813 |
|
2814 @return flag indicating success |
|
2815 """ |
|
2816 if self.isDirty(): |
|
2817 if len(self.pfile) > 0: |
|
2818 if self.pfile.endswith("e3pz") or self.pfile.endswith("e3p"): |
|
2819 ok = self.saveProjectAs() |
|
2820 else: |
|
2821 ok = self.__writeProject() |
|
2822 else: |
|
2823 ok = self.saveProjectAs() |
|
2824 else: |
|
2825 ok = True |
|
2826 self.sessActGrp.setEnabled(ok) |
|
2827 self.menuSessionAct.setEnabled(ok) |
|
2828 return ok |
|
2829 |
|
2830 def saveProjectAs(self): |
|
2831 """ |
|
2832 Public slot to save the current project to a different file. |
|
2833 |
|
2834 @return flag indicating success (boolean) |
|
2835 """ |
|
2836 if Preferences.getProject("CompressedProjectFiles"): |
|
2837 defaultFilter = self.trUtf8("Compressed Project Files (*.e4pz)") |
|
2838 else: |
|
2839 defaultFilter = self.trUtf8("Project Files (*.e4p)") |
|
2840 fn, selectedFilter = QFileDialog.getSaveFileNameAndFilter(\ |
|
2841 self.parent(), |
|
2842 self.trUtf8("Save project as"), |
|
2843 self.ppath, |
|
2844 self.trUtf8("Project Files (*.e4p);;" |
|
2845 "Compressed Project Files (*.e4pz)"), |
|
2846 defaultFilter, |
|
2847 QFileDialog.Options(QFileDialog.DontConfirmOverwrite)) |
|
2848 |
|
2849 if fn: |
|
2850 ext = QFileInfo(fn).suffix() |
|
2851 if not ext: |
|
2852 ex = selectedFilter.split("(*")[1].split(")")[0] |
|
2853 if ex: |
|
2854 fn += ex |
|
2855 if QFileInfo(fn).exists(): |
|
2856 res = QMessageBox.warning(None, |
|
2857 self.trUtf8("Save File"), |
|
2858 self.trUtf8("""<p>The file <b>{0}</b> already exists.</p>""") |
|
2859 .format(fn), |
|
2860 QMessageBox.StandardButtons(\ |
|
2861 QMessageBox.Abort | \ |
|
2862 QMessageBox.Save), |
|
2863 QMessageBox.Abort) |
|
2864 if res != QMessageBox.Save: |
|
2865 return False |
|
2866 |
|
2867 self.name = QFileInfo(fn).baseName() |
|
2868 ok = self.__writeProject(fn) |
|
2869 |
|
2870 if ok: |
|
2871 # now save the tasks |
|
2872 self.__writeTasks() |
|
2873 |
|
2874 self.sessActGrp.setEnabled(ok) |
|
2875 self.menuSessionAct.setEnabled(ok) |
|
2876 self.emit(SIGNAL('projectClosedHooks')) |
|
2877 self.emit(SIGNAL('projectClosed')) |
|
2878 self.emit(SIGNAL('projectOpenedHooks')) |
|
2879 self.emit(SIGNAL('projectOpened')) |
|
2880 return True |
|
2881 else: |
|
2882 return False |
|
2883 |
|
2884 def checkDirty(self): |
|
2885 """ |
|
2886 Public method to check dirty status and open a message window. |
|
2887 |
|
2888 @return flag indicating whether this operation was successful (boolean) |
|
2889 """ |
|
2890 if self.isDirty(): |
|
2891 res = QMessageBox.warning(self.parent(), |
|
2892 self.trUtf8("Close Project"), |
|
2893 self.trUtf8("The current project has unsaved changes."), |
|
2894 QMessageBox.StandardButtons(\ |
|
2895 QMessageBox.Abort | \ |
|
2896 QMessageBox.Discard | \ |
|
2897 QMessageBox.Save), |
|
2898 QMessageBox.Save) |
|
2899 if res == QMessageBox.Save: |
|
2900 return self.saveProject() |
|
2901 elif res == QMessageBox.Discard: |
|
2902 self.setDirty(False) |
|
2903 return True |
|
2904 elif res == QMessageBox.Abort: |
|
2905 return False |
|
2906 |
|
2907 return True |
|
2908 |
|
2909 def __closeAllWindows(self): |
|
2910 """ |
|
2911 Private method to close all project related windows. |
|
2912 """ |
|
2913 self.codemetrics and self.codemetrics.close() |
|
2914 self.codecoverage and self.codecoverage.close() |
|
2915 self.profiledata and self.profiledata.close() |
|
2916 self.applicationDiagram and self.applicationDiagram.close() |
|
2917 |
|
2918 def closeProject(self, reopen = False): |
|
2919 """ |
|
2920 Public slot to close the current project. |
|
2921 |
|
2922 @keyparam reopen flag indicating a reopening of the project (boolean) |
|
2923 @return flag indicating success (boolean) |
|
2924 """ |
|
2925 # save the list of recently opened projects |
|
2926 self.__saveRecent() |
|
2927 |
|
2928 if not self.isOpen(): |
|
2929 return True |
|
2930 |
|
2931 if not self.checkDirty(): |
|
2932 return False |
|
2933 |
|
2934 # save the user project properties |
|
2935 self.__writeUserProperties() |
|
2936 |
|
2937 # save the project session file being quiet about error |
|
2938 if reopen: |
|
2939 self.__writeSession(quiet = True, indicator = "_tmp") |
|
2940 elif Preferences.getProject("AutoSaveSession"): |
|
2941 self.__writeSession(quiet = True) |
|
2942 |
|
2943 # save the project debugger properties file being quiet about error |
|
2944 if Preferences.getProject("AutoSaveDbgProperties") and \ |
|
2945 self.isDebugPropertiesLoaded(): |
|
2946 self.__writeDebugProperties(True) |
|
2947 |
|
2948 # now save all open modified files of the project |
|
2949 vm = e4App().getObject("ViewManager") |
|
2950 success = True |
|
2951 for fn in vm.getOpenFilenames(): |
|
2952 if self.isProjectFile(fn): |
|
2953 success &= vm.closeWindow(fn) |
|
2954 |
|
2955 if not success: |
|
2956 return False |
|
2957 |
|
2958 # stop the VCS monitor thread |
|
2959 if self.vcs is not None: |
|
2960 self.vcs.stopStatusMonitor() |
|
2961 self.disconnect(self.vcs, |
|
2962 SIGNAL("vcsStatusMonitorData(QStringList)"), |
|
2963 self.__model.changeVCSStates) |
|
2964 self.disconnect(self.vcs, |
|
2965 SIGNAL("vcsStatusMonitorStatus(QString, QString)"), |
|
2966 self.__statusMonitorStatus) |
|
2967 |
|
2968 # now save the tasks |
|
2969 self.__writeTasks() |
|
2970 self.ui.taskViewer.clearProjectTasks() |
|
2971 self.ui.taskViewer.setProjectOpen(False) |
|
2972 |
|
2973 # now shutdown the vcs interface |
|
2974 if self.vcs: |
|
2975 self.vcs.vcsShutdown() |
|
2976 self.vcs = None |
|
2977 e4App().getObject("PluginManager").deactivateVcsPlugins() |
|
2978 |
|
2979 # now close all project related windows |
|
2980 self.__closeAllWindows() |
|
2981 |
|
2982 self.__initData() |
|
2983 self.closeAct.setEnabled(False) |
|
2984 self.saveasAct.setEnabled(False) |
|
2985 self.saveAct.setEnabled(False) |
|
2986 self.actGrp2.setEnabled(False) |
|
2987 self.propsAct.setEnabled(False) |
|
2988 self.userPropsAct.setEnabled(False) |
|
2989 self.filetypesAct.setEnabled(False) |
|
2990 self.lexersAct.setEnabled(False) |
|
2991 self.sessActGrp.setEnabled(False) |
|
2992 self.dbgActGrp.setEnabled(False) |
|
2993 self.menuDebuggerAct.setEnabled(False) |
|
2994 self.menuSessionAct.setEnabled(False) |
|
2995 self.menuCheckAct.setEnabled(False) |
|
2996 self.menuShowAct.setEnabled(False) |
|
2997 self.menuDiagramAct.setEnabled(False) |
|
2998 self.menuApidocAct.setEnabled(False) |
|
2999 self.menuPackagersAct.setEnabled(False) |
|
3000 self.pluginGrp.setEnabled(False) |
|
3001 |
|
3002 self.__model.projectClosed() |
|
3003 self.emit(SIGNAL('projectClosedHooks')) |
|
3004 self.emit(SIGNAL('projectClosed')) |
|
3005 |
|
3006 return True |
|
3007 |
|
3008 def saveAllScripts(self, reportSyntaxErrors = False): |
|
3009 """ |
|
3010 Public method to save all scripts belonging to the project. |
|
3011 |
|
3012 @keyparam reportSyntaxErrors flag indicating special reporting |
|
3013 for syntax errors (boolean) |
|
3014 @return flag indicating success (boolean) |
|
3015 """ |
|
3016 vm = e4App().getObject("ViewManager") |
|
3017 success = True |
|
3018 filesWithSyntaxErrors = 0 |
|
3019 for fn in vm.getOpenFilenames(): |
|
3020 rfn = fn.replace(self.ppath + os.sep, '') # make relativ to project |
|
3021 if rfn in self.pdata["SOURCES"] or rfn in self.pdata["OTHERS"]: |
|
3022 editor = vm.getOpenEditor(fn) |
|
3023 success &= vm.saveEditorEd(editor) |
|
3024 if reportSyntaxErrors and editor.hasSyntaxErrors(): |
|
3025 filesWithSyntaxErrors += 1 |
|
3026 |
|
3027 if reportSyntaxErrors and filesWithSyntaxErrors > 0: |
|
3028 QMessageBox.critical(None, |
|
3029 self.trUtf8("Syntax errors detected"), |
|
3030 self.trUtf8("""The project contains %n file(s) with syntax errors.""", |
|
3031 "", filesWithSyntaxErrors) |
|
3032 ) |
|
3033 return False |
|
3034 else: |
|
3035 return success |
|
3036 |
|
3037 def getMainScript(self, normalized = False): |
|
3038 """ |
|
3039 Public method to return the main script filename. |
|
3040 |
|
3041 @param normalized flag indicating a normalized filename is wanted (boolean) |
|
3042 @return filename of the projects main script (string) |
|
3043 """ |
|
3044 if len(self.pdata["MAINSCRIPT"]): |
|
3045 if normalized: |
|
3046 return os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0]) |
|
3047 else: |
|
3048 return self.pdata["MAINSCRIPT"] |
|
3049 else: |
|
3050 return None |
|
3051 |
|
3052 def getSources(self, normalized = False): |
|
3053 """ |
|
3054 Public method to return the source script files. |
|
3055 |
|
3056 @param normalized flag indicating a normalized filename is wanted (boolean) |
|
3057 @return list of the projects scripts (list of string) |
|
3058 """ |
|
3059 if normalized: |
|
3060 return [os.path.join(self.ppath, fn) for fn in self.pdata["SOURCES"]] |
|
3061 else: |
|
3062 return self.pdata["SOURCES"] |
|
3063 |
|
3064 def getProjectType(self): |
|
3065 """ |
|
3066 Public method to get the type of the project. |
|
3067 |
|
3068 @return UI type of the project (string) |
|
3069 """ |
|
3070 return self.pdata["PROJECTTYPE"][0] |
|
3071 |
|
3072 def getProjectLanguage(self): |
|
3073 """ |
|
3074 Public method to get the project's programming language. |
|
3075 |
|
3076 @return programming language (string) |
|
3077 """ |
|
3078 return self.pdata["PROGLANGUAGE"][0] |
|
3079 |
|
3080 def getProjectSpellLanguage(self): |
|
3081 """ |
|
3082 Public method to get the project's programming language. |
|
3083 |
|
3084 @return programming language (string) |
|
3085 """ |
|
3086 return self.pdata["SPELLLANGUAGE"][0] |
|
3087 |
|
3088 def getProjectDictionaries(self): |
|
3089 """ |
|
3090 Public method to get the names of the project specific dictionaries. |
|
3091 |
|
3092 @return tuple of two strings giving the absolute path names of the |
|
3093 project specific word and exclude list |
|
3094 """ |
|
3095 pwl = "" |
|
3096 if len(self.pdata["SPELLWORDS"][0]) > 0: |
|
3097 pwl = os.path.join(self.ppath, self.pdata["SPELLWORDS"][0]) |
|
3098 |
|
3099 pel = "" |
|
3100 if len(self.pdata["SPELLEXCLUDES"][0]) > 0: |
|
3101 pel = os.path.join(self.ppath, self.pdata["SPELLEXCLUDES"][0]) |
|
3102 |
|
3103 return (pwl, pel) |
|
3104 |
|
3105 def getDefaultSourceExtension(self): |
|
3106 """ |
|
3107 Public method to get the default extension for the project's |
|
3108 programming language. |
|
3109 |
|
3110 @return default extension (including the dot) (string) |
|
3111 """ |
|
3112 if self.pdata["PROGLANGUAGE"]: |
|
3113 return self.sourceExtensions[self.pdata["PROGLANGUAGE"][0]][0] |
|
3114 else: |
|
3115 return "" |
|
3116 |
|
3117 def getProjectPath(self): |
|
3118 """ |
|
3119 Public method to get the project path. |
|
3120 |
|
3121 @return project path (string) |
|
3122 """ |
|
3123 return self.ppath |
|
3124 |
|
3125 def getProjectFile(self): |
|
3126 """ |
|
3127 Public method to get the path of the project file. |
|
3128 |
|
3129 @return path of the project file (string) |
|
3130 """ |
|
3131 return self.pfile |
|
3132 |
|
3133 def getProjectManagementDir(self): |
|
3134 """ |
|
3135 Public method to get the path of the management directory. |
|
3136 |
|
3137 @return path of the management directory (string) |
|
3138 """ |
|
3139 if Utilities.isWindowsPlatform(): |
|
3140 return os.path.join(self.ppath, "_eric4project") |
|
3141 else: |
|
3142 return os.path.join(self.ppath, ".eric4project") |
|
3143 |
|
3144 def isProjectFile(self, fn): |
|
3145 """ |
|
3146 Public method used to check, if the passed in filename belongs to the project. |
|
3147 |
|
3148 @param fn filename to be checked (string) |
|
3149 @return flag indicating membership (boolean) |
|
3150 """ |
|
3151 newfn = os.path.abspath(fn) |
|
3152 newfn = newfn.replace(self.ppath + os.sep, '') |
|
3153 if newfn in self.pdata["SOURCES"] or \ |
|
3154 newfn in self.pdata["FORMS"] or \ |
|
3155 newfn in self.pdata["INTERFACES"] or \ |
|
3156 newfn in self.pdata["RESOURCES"] or \ |
|
3157 newfn in self.pdata["TRANSLATIONS"] or \ |
|
3158 newfn in self.pdata["OTHERS"]: |
|
3159 return True |
|
3160 else: |
|
3161 for entry in self.pdata["OTHERS"]: |
|
3162 if newfn.startswith(entry): |
|
3163 return True |
|
3164 return False |
|
3165 |
|
3166 def isProjectSource(self, fn): |
|
3167 """ |
|
3168 Public method used to check, if the passed in filename belongs to the project |
|
3169 sources. |
|
3170 |
|
3171 @param fn filename to be checked (string) |
|
3172 @return flag indicating membership (boolean) |
|
3173 """ |
|
3174 newfn = os.path.abspath(fn) |
|
3175 newfn = newfn.replace(self.ppath + os.sep, '') |
|
3176 return newfn in self.pdata["SOURCES"] |
|
3177 |
|
3178 def isProjectForm(self, fn): |
|
3179 """ |
|
3180 Public method used to check, if the passed in filename belongs to the project |
|
3181 forms. |
|
3182 |
|
3183 @param fn filename to be checked (string) |
|
3184 @return flag indicating membership (boolean) |
|
3185 """ |
|
3186 newfn = os.path.abspath(fn) |
|
3187 newfn = newfn.replace(self.ppath + os.sep, '') |
|
3188 return newfn in self.pdata["FORMS"] |
|
3189 |
|
3190 def isProjectInterface(self, fn): |
|
3191 """ |
|
3192 Public method used to check, if the passed in filename belongs to the project |
|
3193 interfaces. |
|
3194 |
|
3195 @param fn filename to be checked (string) |
|
3196 @return flag indicating membership (boolean) |
|
3197 """ |
|
3198 newfn = os.path.abspath(fn) |
|
3199 newfn = newfn.replace(self.ppath + os.sep, '') |
|
3200 return newfn in self.pdata["INTERFACES"] |
|
3201 |
|
3202 def isProjectResource(self, fn): |
|
3203 """ |
|
3204 Public method used to check, if the passed in filename belongs to the project |
|
3205 resources. |
|
3206 |
|
3207 @param fn filename to be checked (string) |
|
3208 @return flag indicating membership (boolean) |
|
3209 """ |
|
3210 newfn = os.path.abspath(fn) |
|
3211 newfn = newfn.replace(self.ppath + os.sep, '') |
|
3212 return newfn in self.pdata["RESOURCES"] |
|
3213 |
|
3214 def initActions(self): |
|
3215 """ |
|
3216 Public slot to initialize the project related actions. |
|
3217 """ |
|
3218 self.actions = [] |
|
3219 |
|
3220 self.actGrp1 = createActionGroup(self) |
|
3221 |
|
3222 act = E4Action(self.trUtf8('New project'), |
|
3223 UI.PixmapCache.getIcon("projectNew.png"), |
|
3224 self.trUtf8('&New...'), 0, 0, |
|
3225 self.actGrp1,'project_new') |
|
3226 act.setStatusTip(self.trUtf8('Generate a new project')) |
|
3227 act.setWhatsThis(self.trUtf8( |
|
3228 """<b>New...</b>""" |
|
3229 """<p>This opens a dialog for entering the info for a""" |
|
3230 """ new project.</p>""" |
|
3231 )) |
|
3232 self.connect(act, SIGNAL('triggered()'), self.newProject) |
|
3233 self.actions.append(act) |
|
3234 |
|
3235 act = E4Action(self.trUtf8('Open project'), |
|
3236 UI.PixmapCache.getIcon("projectOpen.png"), |
|
3237 self.trUtf8('&Open...'), 0, 0, |
|
3238 self.actGrp1,'project_open') |
|
3239 act.setStatusTip(self.trUtf8('Open an existing project')) |
|
3240 act.setWhatsThis(self.trUtf8( |
|
3241 """<b>Open...</b>""" |
|
3242 """<p>This opens an existing project.</p>""" |
|
3243 )) |
|
3244 self.connect(act, SIGNAL('triggered()'), self.openProject) |
|
3245 self.actions.append(act) |
|
3246 |
|
3247 self.closeAct = E4Action(self.trUtf8('Close project'), |
|
3248 UI.PixmapCache.getIcon("projectClose.png"), |
|
3249 self.trUtf8('&Close'), 0, 0, self, 'project_close') |
|
3250 self.closeAct.setStatusTip(self.trUtf8('Close the current project')) |
|
3251 self.closeAct.setWhatsThis(self.trUtf8( |
|
3252 """<b>Close</b>""" |
|
3253 """<p>This closes the current project.</p>""" |
|
3254 )) |
|
3255 self.connect(self.closeAct, SIGNAL('triggered()'), self.closeProject) |
|
3256 self.actions.append(self.closeAct) |
|
3257 |
|
3258 self.saveAct = E4Action(self.trUtf8('Save project'), |
|
3259 UI.PixmapCache.getIcon("projectSave.png"), |
|
3260 self.trUtf8('&Save'), 0, 0, self, 'project_save') |
|
3261 self.saveAct.setStatusTip(self.trUtf8('Save the current project')) |
|
3262 self.saveAct.setWhatsThis(self.trUtf8( |
|
3263 """<b>Save</b>""" |
|
3264 """<p>This saves the current project.</p>""" |
|
3265 )) |
|
3266 self.connect(self.saveAct, SIGNAL('triggered()'), self.saveProject) |
|
3267 self.actions.append(self.saveAct) |
|
3268 |
|
3269 self.saveasAct = E4Action(self.trUtf8('Save project as'), |
|
3270 UI.PixmapCache.getIcon("projectSaveAs.png"), |
|
3271 self.trUtf8('Save &as...'), 0, 0, self, 'project_save_as') |
|
3272 self.saveasAct.setStatusTip(self.trUtf8('Save the current project to a new file')) |
|
3273 self.saveasAct.setWhatsThis(self.trUtf8( |
|
3274 """<b>Save as</b>""" |
|
3275 """<p>This saves the current project to a new file.</p>""" |
|
3276 )) |
|
3277 self.connect(self.saveasAct, SIGNAL('triggered()'), self.saveProjectAs) |
|
3278 self.actions.append(self.saveasAct) |
|
3279 |
|
3280 self.actGrp2 = createActionGroup(self) |
|
3281 |
|
3282 self.addFilesAct = E4Action(self.trUtf8('Add files to project'), |
|
3283 UI.PixmapCache.getIcon("fileMisc.png"), |
|
3284 self.trUtf8('Add &files...'), 0, 0, |
|
3285 self.actGrp2,'project_add_file') |
|
3286 self.addFilesAct.setStatusTip(self.trUtf8('Add files to the current project')) |
|
3287 self.addFilesAct.setWhatsThis(self.trUtf8( |
|
3288 """<b>Add files...</b>""" |
|
3289 """<p>This opens a dialog for adding files""" |
|
3290 """ to the current project. The place to add is""" |
|
3291 """ determined by the file extension.</p>""" |
|
3292 )) |
|
3293 self.connect(self.addFilesAct, SIGNAL('triggered()'), self.addFiles) |
|
3294 self.actions.append(self.addFilesAct) |
|
3295 |
|
3296 self.addDirectoryAct = E4Action(self.trUtf8('Add directory to project'), |
|
3297 UI.PixmapCache.getIcon("dirOpen.png"), |
|
3298 self.trUtf8('Add directory...'), 0, 0, |
|
3299 self.actGrp2,'project_add_directory') |
|
3300 self.addDirectoryAct.setStatusTip( |
|
3301 self.trUtf8('Add a directory to the current project')) |
|
3302 self.addDirectoryAct.setWhatsThis(self.trUtf8( |
|
3303 """<b>Add directory...</b>""" |
|
3304 """<p>This opens a dialog for adding a directory""" |
|
3305 """ to the current project.</p>""" |
|
3306 )) |
|
3307 self.connect(self.addDirectoryAct, SIGNAL('triggered()'), self.addDirectory) |
|
3308 self.actions.append(self.addDirectoryAct) |
|
3309 |
|
3310 self.addLanguageAct = E4Action(self.trUtf8('Add translation to project'), |
|
3311 UI.PixmapCache.getIcon("linguist4.png"), |
|
3312 self.trUtf8('Add &translation...'), 0, 0, |
|
3313 self.actGrp2,'project_add_translation') |
|
3314 self.addLanguageAct.setStatusTip( |
|
3315 self.trUtf8('Add a translation to the current project')) |
|
3316 self.addLanguageAct.setWhatsThis(self.trUtf8( |
|
3317 """<b>Add translation...</b>""" |
|
3318 """<p>This opens a dialog for add a translation""" |
|
3319 """ to the current project.</p>""" |
|
3320 )) |
|
3321 self.connect(self.addLanguageAct, SIGNAL('triggered()'), self.addLanguage) |
|
3322 self.actions.append(self.addLanguageAct) |
|
3323 |
|
3324 act = E4Action(self.trUtf8('Search new files'), |
|
3325 self.trUtf8('Searc&h new files...'), 0, 0, |
|
3326 self.actGrp2,'project_search_new_files') |
|
3327 act.setStatusTip(self.trUtf8('Search new files in the project directory.')) |
|
3328 act.setWhatsThis(self.trUtf8( |
|
3329 """<b>Search new files...</b>""" |
|
3330 """<p>This searches for new files (sources, *.ui, *.idl) in the project""" |
|
3331 """ directory and registered subdirectories.</p>""" |
|
3332 )) |
|
3333 self.connect(act, SIGNAL('triggered()'), self.__searchNewFiles) |
|
3334 self.actions.append(act) |
|
3335 |
|
3336 self.propsAct = E4Action(self.trUtf8('Project properties'), |
|
3337 UI.PixmapCache.getIcon("projectProps.png"), |
|
3338 self.trUtf8('&Properties...'), 0, 0, self, 'project_properties') |
|
3339 self.propsAct.setStatusTip(self.trUtf8('Show the project properties')) |
|
3340 self.propsAct.setWhatsThis(self.trUtf8( |
|
3341 """<b>Properties...</b>""" |
|
3342 """<p>This shows a dialog to edit the project properties.</p>""" |
|
3343 )) |
|
3344 self.connect(self.propsAct, SIGNAL('triggered()'), self.__showProperties) |
|
3345 self.actions.append(self.propsAct) |
|
3346 |
|
3347 self.userPropsAct = E4Action(self.trUtf8('User project properties'), |
|
3348 UI.PixmapCache.getIcon("projectUserProps.png"), |
|
3349 self.trUtf8('&User Properties...'), 0, 0, self, 'project_user_properties') |
|
3350 self.userPropsAct.setStatusTip(self.trUtf8( |
|
3351 'Show the user specific project properties')) |
|
3352 self.userPropsAct.setWhatsThis(self.trUtf8( |
|
3353 """<b>User Properties...</b>""" |
|
3354 """<p>This shows a dialog to edit the user specific project properties.</p>""" |
|
3355 )) |
|
3356 self.connect(self.userPropsAct, SIGNAL('triggered()'), self.__showUserProperties) |
|
3357 self.actions.append(self.userPropsAct) |
|
3358 |
|
3359 self.filetypesAct = E4Action(self.trUtf8('Filetype Associations'), |
|
3360 self.trUtf8('Filetype Associations...'), 0, 0, |
|
3361 self, 'project_filetype_associatios') |
|
3362 self.filetypesAct.setStatusTip(\ |
|
3363 self.trUtf8('Show the project filetype associations')) |
|
3364 self.filetypesAct.setWhatsThis(self.trUtf8( |
|
3365 """<b>Filetype Associations...</b>""" |
|
3366 """<p>This shows a dialog to edit the filetype associations of the project.""" |
|
3367 """ These associations determine the type (source, form, interface""" |
|
3368 """ or others) with a filename pattern. They are used when adding a file""" |
|
3369 """ to the project and when performing a search for new files.</p>""" |
|
3370 )) |
|
3371 self.connect(self.filetypesAct, SIGNAL('triggered()'), |
|
3372 self.__showFiletypeAssociations) |
|
3373 self.actions.append(self.filetypesAct) |
|
3374 |
|
3375 self.lexersAct = E4Action(self.trUtf8('Lexer Associations'), |
|
3376 self.trUtf8('Lexer Associations...'), 0, 0, |
|
3377 self, 'project_lexer_associatios') |
|
3378 self.lexersAct.setStatusTip(\ |
|
3379 self.trUtf8('Show the project lexer associations (overriding defaults)')) |
|
3380 self.lexersAct.setWhatsThis(self.trUtf8( |
|
3381 """<b>Lexer Associations...</b>""" |
|
3382 """<p>This shows a dialog to edit the lexer associations of the project.""" |
|
3383 """ These associations override the global lexer associations. Lexers""" |
|
3384 """ are used to highlight the editor text.</p>""" |
|
3385 )) |
|
3386 self.connect(self.lexersAct, SIGNAL('triggered()'), |
|
3387 self.__showLexerAssociations) |
|
3388 self.actions.append(self.lexersAct) |
|
3389 |
|
3390 self.dbgActGrp = createActionGroup(self) |
|
3391 |
|
3392 act = E4Action(self.trUtf8('Debugger Properties'), |
|
3393 self.trUtf8('Debugger &Properties...'), 0, 0, |
|
3394 self.dbgActGrp, 'project_debugger_properties') |
|
3395 act.setStatusTip(self.trUtf8('Show the debugger properties')) |
|
3396 act.setWhatsThis(self.trUtf8( |
|
3397 """<b>Debugger Properties...</b>""" |
|
3398 """<p>This shows a dialog to edit project specific debugger settings.</p>""" |
|
3399 )) |
|
3400 self.connect(act, SIGNAL('triggered()'), self.__showDebugProperties) |
|
3401 self.actions.append(act) |
|
3402 |
|
3403 act = E4Action(self.trUtf8('Load'), |
|
3404 self.trUtf8('&Load'), 0, 0, |
|
3405 self.dbgActGrp, 'project_debugger_properties_load') |
|
3406 act.setStatusTip(self.trUtf8('Load the debugger properties')) |
|
3407 act.setWhatsThis(self.trUtf8( |
|
3408 """<b>Load Debugger Properties</b>""" |
|
3409 """<p>This loads the project specific debugger settings.</p>""" |
|
3410 )) |
|
3411 self.connect(act, SIGNAL('triggered()'), self.__readDebugProperties) |
|
3412 self.actions.append(act) |
|
3413 |
|
3414 act = E4Action(self.trUtf8('Save'), |
|
3415 self.trUtf8('&Save'), 0, 0, |
|
3416 self.dbgActGrp, 'project_debugger_properties_save') |
|
3417 act.setStatusTip(self.trUtf8('Save the debugger properties')) |
|
3418 act.setWhatsThis(self.trUtf8( |
|
3419 """<b>Save Debugger Properties</b>""" |
|
3420 """<p>This saves the project specific debugger settings.</p>""" |
|
3421 )) |
|
3422 self.connect(act, SIGNAL('triggered()'), self.__writeDebugProperties) |
|
3423 self.actions.append(act) |
|
3424 |
|
3425 act = E4Action(self.trUtf8('Delete'), |
|
3426 self.trUtf8('&Delete'), 0, 0, |
|
3427 self.dbgActGrp, 'project_debugger_properties_delete') |
|
3428 act.setStatusTip(self.trUtf8('Delete the debugger properties')) |
|
3429 act.setWhatsThis(self.trUtf8( |
|
3430 """<b>Delete Debugger Properties</b>""" |
|
3431 """<p>This deletes the file containing the project specific""" |
|
3432 """ debugger settings.</p>""" |
|
3433 )) |
|
3434 self.connect(act, SIGNAL('triggered()'), self.__deleteDebugProperties) |
|
3435 self.actions.append(act) |
|
3436 |
|
3437 act = E4Action(self.trUtf8('Reset'), |
|
3438 self.trUtf8('&Reset'), 0, 0, |
|
3439 self.dbgActGrp, 'project_debugger_properties_resets') |
|
3440 act.setStatusTip(self.trUtf8('Reset the debugger properties')) |
|
3441 act.setWhatsThis(self.trUtf8( |
|
3442 """<b>Reset Debugger Properties</b>""" |
|
3443 """<p>This resets the project specific debugger settings.</p>""" |
|
3444 )) |
|
3445 self.connect(act, SIGNAL('triggered()'), self.__initDebugProperties) |
|
3446 self.actions.append(act) |
|
3447 |
|
3448 self.sessActGrp = createActionGroup(self) |
|
3449 |
|
3450 act = E4Action(self.trUtf8('Load session'), |
|
3451 self.trUtf8('Load session'), 0, 0, |
|
3452 self.sessActGrp, 'project_load_session') |
|
3453 act.setStatusTip(self.trUtf8('Load the projects session file.')) |
|
3454 act.setWhatsThis(self.trUtf8( |
|
3455 """<b>Load session</b>""" |
|
3456 """<p>This loads the projects session file. The session consists""" |
|
3457 """ of the following data.<br>""" |
|
3458 """- all open source files<br>""" |
|
3459 """- all breakpoint<br>""" |
|
3460 """- the commandline arguments<br>""" |
|
3461 """- the working directory<br>""" |
|
3462 """- the exception reporting flag</p>""" |
|
3463 )) |
|
3464 self.connect(act, SIGNAL('triggered()'), self.__readSession) |
|
3465 self.actions.append(act) |
|
3466 |
|
3467 act = E4Action(self.trUtf8('Save session'), |
|
3468 self.trUtf8('Save session'), 0, 0, |
|
3469 self.sessActGrp, 'project_save_session') |
|
3470 act.setStatusTip(self.trUtf8('Save the projects session file.')) |
|
3471 act.setWhatsThis(self.trUtf8( |
|
3472 """<b>Save session</b>""" |
|
3473 """<p>This saves the projects session file. The session consists""" |
|
3474 """ of the following data.<br>""" |
|
3475 """- all open source files<br>""" |
|
3476 """- all breakpoint<br>""" |
|
3477 """- the commandline arguments<br>""" |
|
3478 """- the working directory<br>""" |
|
3479 """- the exception reporting flag</p>""" |
|
3480 )) |
|
3481 self.connect(act, SIGNAL('triggered()'), self.__writeSession) |
|
3482 self.actions.append(act) |
|
3483 |
|
3484 act = E4Action(self.trUtf8('Delete session'), |
|
3485 self.trUtf8('Delete session'), 0, 0, |
|
3486 self.sessActGrp, 'project_delete_session') |
|
3487 act.setStatusTip(self.trUtf8('Delete the projects session file.')) |
|
3488 act.setWhatsThis(self.trUtf8( |
|
3489 """<b>Delete session</b>""" |
|
3490 """<p>This deletes the projects session file</p>""" |
|
3491 )) |
|
3492 self.connect(act, SIGNAL('triggered()'), self.__deleteSession) |
|
3493 self.actions.append(act) |
|
3494 |
|
3495 self.chkGrp = createActionGroup(self) |
|
3496 |
|
3497 self.codeMetricsAct = E4Action(self.trUtf8('Code Metrics'), |
|
3498 self.trUtf8('&Code Metrics...'), 0, 0, |
|
3499 self.chkGrp,'project_code_metrics') |
|
3500 self.codeMetricsAct.setStatusTip(\ |
|
3501 self.trUtf8('Show some code metrics for the project.')) |
|
3502 self.codeMetricsAct.setWhatsThis(self.trUtf8( |
|
3503 """<b>Code Metrics...</b>""" |
|
3504 """<p>This shows some code metrics for all Python files in the project.</p>""" |
|
3505 )) |
|
3506 self.connect(self.codeMetricsAct, SIGNAL('triggered()'), self.__showCodeMetrics) |
|
3507 self.actions.append(self.codeMetricsAct) |
|
3508 |
|
3509 self.codeCoverageAct = E4Action(self.trUtf8('Python Code Coverage'), |
|
3510 self.trUtf8('Code Co&verage...'), 0, 0, |
|
3511 self.chkGrp,'project_code_coverage') |
|
3512 self.codeCoverageAct.setStatusTip(\ |
|
3513 self.trUtf8('Show code coverage information for the project.')) |
|
3514 self.codeCoverageAct.setWhatsThis(self.trUtf8( |
|
3515 """<b>Code Coverage...</b>""" |
|
3516 """<p>This shows the code coverage information for all Python files""" |
|
3517 """ in the project.</p>""" |
|
3518 )) |
|
3519 self.connect(self.codeCoverageAct, SIGNAL('triggered()'), self.__showCodeCoverage) |
|
3520 self.actions.append(self.codeCoverageAct) |
|
3521 |
|
3522 self.codeProfileAct = E4Action(self.trUtf8('Profile Data'), |
|
3523 self.trUtf8('&Profile Data...'), 0, 0, |
|
3524 self.chkGrp,'project_profile_data') |
|
3525 self.codeProfileAct.setStatusTip(\ |
|
3526 self.trUtf8('Show profiling data for the project.')) |
|
3527 self.codeProfileAct.setWhatsThis(self.trUtf8( |
|
3528 """<b>Profile Data...</b>""" |
|
3529 """<p>This shows the profiling data for the project.</p>""" |
|
3530 )) |
|
3531 self.connect(self.codeProfileAct, SIGNAL('triggered()'), self.__showProfileData) |
|
3532 self.actions.append(self.codeProfileAct) |
|
3533 |
|
3534 self.applicationDiagramAct = E4Action(self.trUtf8('Application Diagram'), |
|
3535 self.trUtf8('&Application Diagram...'), 0, 0, |
|
3536 self.chkGrp,'project_application_diagram') |
|
3537 self.applicationDiagramAct.setStatusTip(\ |
|
3538 self.trUtf8('Show a diagram of the project.')) |
|
3539 self.applicationDiagramAct.setWhatsThis(self.trUtf8( |
|
3540 """<b>Application Diagram...</b>""" |
|
3541 """<p>This shows a diagram of the project.</p>""" |
|
3542 )) |
|
3543 self.connect(self.applicationDiagramAct, |
|
3544 SIGNAL('triggered()'), self.handleApplicationDiagram) |
|
3545 self.actions.append(self.applicationDiagramAct) |
|
3546 |
|
3547 self.pluginGrp = createActionGroup(self) |
|
3548 |
|
3549 self.pluginPkgListAct = E4Action(self.trUtf8('Create Package List'), |
|
3550 UI.PixmapCache.getIcon("pluginArchiveList.png"), |
|
3551 self.trUtf8('Create &Package List'), 0, 0, |
|
3552 self.pluginGrp,'project_plugin_pkglist') |
|
3553 self.pluginPkgListAct.setStatusTip(\ |
|
3554 self.trUtf8('Create an initial PKGLIST file for an eric4 plugin.')) |
|
3555 self.pluginPkgListAct.setWhatsThis(self.trUtf8( |
|
3556 """<b>Create Package List</b>""" |
|
3557 """<p>This creates an initial list of files to include in an eric4 """ |
|
3558 """plugin archive. The list is created from the project file.</p>""" |
|
3559 )) |
|
3560 self.connect(self.pluginPkgListAct, SIGNAL('triggered()'), |
|
3561 self.__pluginCreatePkgList) |
|
3562 self.actions.append(self.pluginPkgListAct) |
|
3563 |
|
3564 self.pluginArchiveAct = E4Action(self.trUtf8('Create Plugin Archive'), |
|
3565 UI.PixmapCache.getIcon("pluginArchive.png"), |
|
3566 self.trUtf8('Create Plugin &Archive'), 0, 0, |
|
3567 self.pluginGrp,'project_plugin_archive') |
|
3568 self.pluginArchiveAct.setStatusTip(\ |
|
3569 self.trUtf8('Create an eric4 plugin archive file.')) |
|
3570 self.pluginArchiveAct.setWhatsThis(self.trUtf8( |
|
3571 """<b>Create Plugin Archive</b>""" |
|
3572 """<p>This creates an eric4 plugin archive file using the list of files """ |
|
3573 """given in the PKGLIST file. The archive name is built from the main """ |
|
3574 """script name.</p>""" |
|
3575 )) |
|
3576 self.connect(self.pluginArchiveAct, SIGNAL('triggered()'), |
|
3577 self.__pluginCreateArchive) |
|
3578 self.actions.append(self.pluginArchiveAct) |
|
3579 |
|
3580 self.pluginSArchiveAct = E4Action(self.trUtf8('Create Plugin Archive (Snapshot)'), |
|
3581 UI.PixmapCache.getIcon("pluginArchiveSnapshot.png"), |
|
3582 self.trUtf8('Create Plugin Archive (&Snapshot)'), 0, 0, |
|
3583 self.pluginGrp,'project_plugin_sarchive') |
|
3584 self.pluginSArchiveAct.setStatusTip(\ |
|
3585 self.trUtf8('Create an eric4 plugin archive file (snapshot release).')) |
|
3586 self.pluginSArchiveAct.setWhatsThis(self.trUtf8( |
|
3587 """<b>Create Plugin Archive (Snapshot)</b>""" |
|
3588 """<p>This creates an eric4 plugin archive file using the list of files """ |
|
3589 """given in the PKGLIST file. The archive name is built from the main """ |
|
3590 """script name. The version entry of the main script is modified to """ |
|
3591 """reflect a snapshot release.</p>""" |
|
3592 )) |
|
3593 self.connect(self.pluginSArchiveAct, SIGNAL('triggered()'), |
|
3594 self.__pluginCreateSnapshotArchive) |
|
3595 self.actions.append(self.pluginSArchiveAct) |
|
3596 |
|
3597 self.closeAct.setEnabled(False) |
|
3598 self.saveAct.setEnabled(False) |
|
3599 self.saveasAct.setEnabled(False) |
|
3600 self.actGrp2.setEnabled(False) |
|
3601 self.propsAct.setEnabled(False) |
|
3602 self.userPropsAct.setEnabled(False) |
|
3603 self.filetypesAct.setEnabled(False) |
|
3604 self.lexersAct.setEnabled(False) |
|
3605 self.sessActGrp.setEnabled(False) |
|
3606 self.dbgActGrp.setEnabled(False) |
|
3607 self.pluginGrp.setEnabled(False) |
|
3608 |
|
3609 def initMenu(self): |
|
3610 """ |
|
3611 Public slot to initialize the project menu. |
|
3612 |
|
3613 @return the menu generated (QMenu) |
|
3614 """ |
|
3615 menu = QMenu(self.trUtf8('&Project'), self.parent()) |
|
3616 self.recentMenu = QMenu(self.trUtf8('Open &Recent Projects'), menu) |
|
3617 self.vcsMenu = QMenu(self.trUtf8('&Version Control'), menu) |
|
3618 self.vcsMenu.setTearOffEnabled(True) |
|
3619 self.vcsProjectHelper.initMenu(self.vcsMenu) |
|
3620 self.checksMenu = QMenu(self.trUtf8('Chec&k'), menu) |
|
3621 self.checksMenu.setTearOffEnabled(True) |
|
3622 self.showMenu = QMenu(self.trUtf8('Sho&w'), menu) |
|
3623 self.graphicsMenu = QMenu(self.trUtf8('&Diagrams'), menu) |
|
3624 self.sessionMenu = QMenu(self.trUtf8('Session'), menu) |
|
3625 self.apidocMenu = QMenu(self.trUtf8('Source &Documentation'), menu) |
|
3626 self.apidocMenu.setTearOffEnabled(True) |
|
3627 self.debuggerMenu = QMenu(self.trUtf8('Debugger'), menu) |
|
3628 self.packagersMenu = QMenu(self.trUtf8('Pac&kagers'), menu) |
|
3629 self.packagersMenu.setTearOffEnabled(True) |
|
3630 |
|
3631 self.__menus = { |
|
3632 "Main" : menu, |
|
3633 "Recent" : self.recentMenu, |
|
3634 "VCS" : self.vcsMenu, |
|
3635 "Checks" : self.checksMenu, |
|
3636 "Show" : self.showMenu, |
|
3637 "Graphics" : self.graphicsMenu, |
|
3638 "Session" : self.sessionMenu, |
|
3639 "Apidoc" : self.apidocMenu, |
|
3640 "Debugger" : self.debuggerMenu, |
|
3641 "Packagers" : self.packagersMenu, |
|
3642 } |
|
3643 |
|
3644 # connect the aboutToShow signals |
|
3645 self.connect(self.recentMenu, SIGNAL('aboutToShow()'), |
|
3646 self.__showContextMenuRecent) |
|
3647 self.connect(self.recentMenu, SIGNAL('triggered(QAction *)'), |
|
3648 self.__openRecent) |
|
3649 self.connect(self.vcsMenu, SIGNAL('aboutToShow()'), self.__showContextMenuVCS) |
|
3650 self.connect(self.checksMenu, SIGNAL('aboutToShow()'), |
|
3651 self.__showContextMenuChecks) |
|
3652 self.connect(self.showMenu, SIGNAL('aboutToShow()'), self.__showContextMenuShow) |
|
3653 self.connect(self.graphicsMenu, SIGNAL('aboutToShow()'), |
|
3654 self.__showContextMenuGraphics) |
|
3655 self.connect(self.apidocMenu, SIGNAL('aboutToShow()'), |
|
3656 self.__showContextMenuApiDoc) |
|
3657 self.connect(self.packagersMenu, SIGNAL('aboutToShow()'), |
|
3658 self.__showContextMenuPackagers) |
|
3659 self.connect(menu, SIGNAL('aboutToShow()'), self.__showMenu) |
|
3660 |
|
3661 # build the show menu |
|
3662 self.showMenu.setTearOffEnabled(True) |
|
3663 self.showMenu.addAction(self.codeMetricsAct) |
|
3664 self.showMenu.addAction(self.codeCoverageAct) |
|
3665 self.showMenu.addAction(self.codeProfileAct) |
|
3666 |
|
3667 # build the diagrams menu |
|
3668 self.graphicsMenu.setTearOffEnabled(True) |
|
3669 self.graphicsMenu.addAction(self.applicationDiagramAct) |
|
3670 |
|
3671 # build the session menu |
|
3672 self.sessionMenu.setTearOffEnabled(True) |
|
3673 self.sessionMenu.addActions(self.sessActGrp.actions()) |
|
3674 |
|
3675 # build the debugger menu |
|
3676 self.debuggerMenu.setTearOffEnabled(True) |
|
3677 self.debuggerMenu.addActions(self.dbgActGrp.actions()) |
|
3678 |
|
3679 # build the packagers menu |
|
3680 self.packagersMenu.addActions(self.pluginGrp.actions()) |
|
3681 self.packagersMenu.addSeparator() |
|
3682 |
|
3683 # build the main menu |
|
3684 menu.setTearOffEnabled(True) |
|
3685 menu.addActions(self.actGrp1.actions()) |
|
3686 self.menuRecentAct = menu.addMenu(self.recentMenu) |
|
3687 menu.addSeparator() |
|
3688 menu.addAction(self.closeAct) |
|
3689 menu.addSeparator() |
|
3690 menu.addAction(self.saveAct) |
|
3691 menu.addAction(self.saveasAct) |
|
3692 menu.addSeparator() |
|
3693 self.menuDebuggerAct = menu.addMenu(self.debuggerMenu) |
|
3694 self.menuSessionAct = menu.addMenu(self.sessionMenu) |
|
3695 menu.addSeparator() |
|
3696 menu.addActions(self.actGrp2.actions()) |
|
3697 menu.addSeparator() |
|
3698 self.menuDiagramAct = menu.addMenu(self.graphicsMenu) |
|
3699 menu.addSeparator() |
|
3700 self.menuCheckAct = menu.addMenu(self.checksMenu) |
|
3701 menu.addSeparator() |
|
3702 menu.addMenu(self.vcsMenu) |
|
3703 menu.addSeparator() |
|
3704 self.menuShowAct = menu.addMenu(self.showMenu) |
|
3705 menu.addSeparator() |
|
3706 self.menuApidocAct = menu.addMenu(self.apidocMenu) |
|
3707 menu.addSeparator() |
|
3708 self.menuPackagersAct = menu.addMenu(self.packagersMenu) |
|
3709 menu.addSeparator() |
|
3710 menu.addAction(self.propsAct) |
|
3711 menu.addAction(self.userPropsAct) |
|
3712 menu.addAction(self.filetypesAct) |
|
3713 menu.addAction(self.lexersAct) |
|
3714 |
|
3715 self.menuCheckAct.setEnabled(False) |
|
3716 self.menuShowAct.setEnabled(False) |
|
3717 self.menuDiagramAct.setEnabled(False) |
|
3718 self.menuSessionAct.setEnabled(False) |
|
3719 self.menuDebuggerAct.setEnabled(False) |
|
3720 self.menuApidocAct.setEnabled(False) |
|
3721 self.menuPackagersAct.setEnabled(False) |
|
3722 |
|
3723 self.menu = menu |
|
3724 return menu |
|
3725 |
|
3726 def initToolbar(self, toolbarManager): |
|
3727 """ |
|
3728 Public slot to initialize the project toolbar. |
|
3729 |
|
3730 @param toolbarManager reference to a toolbar manager object (E4ToolBarManager) |
|
3731 @return the toolbar generated (QToolBar) |
|
3732 """ |
|
3733 tb = QToolBar(self.trUtf8("Project"), self.parent()) |
|
3734 tb.setIconSize(UI.Config.ToolBarIconSize) |
|
3735 tb.setObjectName("ProjectToolbar") |
|
3736 tb.setToolTip(self.trUtf8('Project')) |
|
3737 |
|
3738 tb.addActions(self.actGrp1.actions()) |
|
3739 tb.addAction(self.closeAct) |
|
3740 tb.addSeparator() |
|
3741 tb.addAction(self.saveAct) |
|
3742 tb.addAction(self.saveasAct) |
|
3743 |
|
3744 toolbarManager.addToolBar(tb, tb.windowTitle()) |
|
3745 toolbarManager.addAction(self.addFilesAct, tb.windowTitle()) |
|
3746 toolbarManager.addAction(self.addDirectoryAct, tb.windowTitle()) |
|
3747 toolbarManager.addAction(self.addLanguageAct, tb.windowTitle()) |
|
3748 toolbarManager.addAction(self.propsAct, tb.windowTitle()) |
|
3749 toolbarManager.addAction(self.userPropsAct, tb.windowTitle()) |
|
3750 |
|
3751 return tb |
|
3752 |
|
3753 def __showMenu(self): |
|
3754 """ |
|
3755 Private method to set up the project menu. |
|
3756 """ |
|
3757 self.menuRecentAct.setEnabled(len(self.recent) > 0) |
|
3758 |
|
3759 self.emit(SIGNAL("showMenu"), "Main", self.__menus["Main"]) |
|
3760 |
|
3761 def __syncRecent(self): |
|
3762 """ |
|
3763 Private method to synchronize the list of recently opened projects |
|
3764 with the central store. |
|
3765 """ |
|
3766 if self.pfile in self.recent: |
|
3767 self.recent.remove(self.pfile) |
|
3768 self.recent.insert(0, self.pfile) |
|
3769 maxRecent = Preferences.getProject("RecentNumber") |
|
3770 if len(self.recent) > maxRecent: |
|
3771 self.recent = self.recent[:maxRecent] |
|
3772 self.__saveRecent() |
|
3773 |
|
3774 def __showContextMenuRecent(self): |
|
3775 """ |
|
3776 Private method to set up the recent projects menu. |
|
3777 """ |
|
3778 self.__loadRecent() |
|
3779 |
|
3780 self.recentMenu.clear() |
|
3781 |
|
3782 idx = 1 |
|
3783 for rp in self.recent: |
|
3784 if idx < 10: |
|
3785 formatStr = '&%d. %s' |
|
3786 else: |
|
3787 formatStr = '%d. %s' |
|
3788 act = self.recentMenu.addAction(\ |
|
3789 formatStr % (idx, |
|
3790 Utilities.compactPath(rp, self.ui.maxMenuFilePathLen))) |
|
3791 act.setData(QVariant(rp)) |
|
3792 act.setEnabled(QFileInfo(rp).exists()) |
|
3793 idx += 1 |
|
3794 |
|
3795 self.recentMenu.addSeparator() |
|
3796 self.recentMenu.addAction(self.trUtf8('&Clear'), self.__clearRecent) |
|
3797 |
|
3798 def __openRecent(self, act): |
|
3799 """ |
|
3800 Private method to open a project from the list of rencently opened projects. |
|
3801 |
|
3802 @param act reference to the action that triggered (QAction) |
|
3803 """ |
|
3804 file = act.data().toString() |
|
3805 if file: |
|
3806 self.openProject(file) |
|
3807 |
|
3808 def __clearRecent(self): |
|
3809 """ |
|
3810 Private method to clear the recent projects menu. |
|
3811 """ |
|
3812 self.recent.clear() |
|
3813 |
|
3814 def __searchNewFiles(self): |
|
3815 """ |
|
3816 Private slot used to handle the search new files action. |
|
3817 """ |
|
3818 self.__doSearchNewFiles(False, True) |
|
3819 |
|
3820 def __doSearchNewFiles(self, AI = True, onUserDemand = False): |
|
3821 """ |
|
3822 Private method to search for new files in the project directory. |
|
3823 |
|
3824 If new files were found, it shows a dialog listing these files and |
|
3825 gives the user the opportunity to select the ones he wants to |
|
3826 include. If 'Automatic Inclusion' is enabled, the new files are |
|
3827 automatically added to the project. |
|
3828 |
|
3829 @param AI flag indicating whether the automatic inclusion should |
|
3830 be honoured (boolean) |
|
3831 @param onUserDemand flag indicating whether this method was |
|
3832 requested by the user via a menu action (boolean) |
|
3833 """ |
|
3834 autoInclude = Preferences.getProject("AutoIncludeNewFiles") |
|
3835 recursiveSearch = Preferences.getProject("SearchNewFilesRecursively") |
|
3836 newFiles = [] |
|
3837 |
|
3838 dirs = self.subdirs[:] |
|
3839 for dir in dirs: |
|
3840 curpath = os.path.join(self.ppath, dir) |
|
3841 try: |
|
3842 newSources = os.listdir(curpath) |
|
3843 except OSError: |
|
3844 newSources = [] |
|
3845 if self.pdata["TRANSLATIONPATTERN"]: |
|
3846 pattern = self.pdata["TRANSLATIONPATTERN"][0].replace("%language%", "*") |
|
3847 else: |
|
3848 pattern = "*.ts" |
|
3849 binpattern = self.__binaryTranslationFile(pattern) |
|
3850 for ns in newSources: |
|
3851 # ignore hidden files and directories |
|
3852 if ns.startswith('.'): |
|
3853 continue |
|
3854 if Utilities.isWindowsPlatform() and \ |
|
3855 os.path.isdir(os.path.join(curpath, ns)) and \ |
|
3856 ns.startswith('_'): |
|
3857 # dot net hack |
|
3858 continue |
|
3859 |
|
3860 # set fn to project relative name |
|
3861 # then reset ns to fully qualified name for insertion, possibly. |
|
3862 if dir == "": |
|
3863 fn = ns |
|
3864 else: |
|
3865 fn = os.path.join(dir, ns) |
|
3866 ns = os.path.abspath(os.path.join(curpath, ns)) |
|
3867 |
|
3868 # do not bother with dirs here... |
|
3869 if os.path.isdir(ns): |
|
3870 if recursiveSearch: |
|
3871 d = ns.replace(self.ppath + os.sep, '') |
|
3872 if d not in dirs: |
|
3873 dirs.append(d) |
|
3874 continue |
|
3875 |
|
3876 filetype = "" |
|
3877 bfn = os.path.basename(fn) |
|
3878 for pattern in reversed(sorted(self.pdata["FILETYPES"].keys())): |
|
3879 if fnmatch.fnmatch(bfn, pattern): |
|
3880 filetype = self.pdata["FILETYPES"][pattern] |
|
3881 break |
|
3882 |
|
3883 if (filetype == "SOURCES" and fn not in self.pdata["SOURCES"]) or \ |
|
3884 (filetype == "FORMS" and fn not in self.pdata["FORMS"]) or \ |
|
3885 (filetype == "INTERFACES" and fn not in self.pdata["INTERFACES"]) or \ |
|
3886 (filetype == "RESOURCES" and fn not in self.pdata["RESOURCES"]) or \ |
|
3887 (filetype == "OTHERS" and fn not in self.pdata["OTHERS"]): |
|
3888 if autoInclude and AI: |
|
3889 self.appendFile(ns) |
|
3890 else: |
|
3891 newFiles.append(ns) |
|
3892 elif filetype == "TRANSLATIONS" and fn not in self.pdata["TRANSLATIONS"]: |
|
3893 if fnmatch.fnmatch(ns, pattern) or fnmatch.fnmatch(ns, binpattern): |
|
3894 if autoInclude and AI: |
|
3895 self.appendFile(ns) |
|
3896 else: |
|
3897 newFiles.append(ns) |
|
3898 |
|
3899 # if autoInclude is set there is no more work left |
|
3900 if (autoInclude and AI): |
|
3901 return |
|
3902 |
|
3903 # if newfiles is empty, put up message box informing user nothing found |
|
3904 if not newFiles: |
|
3905 if onUserDemand: |
|
3906 QMessageBox.information(None, |
|
3907 self.trUtf8("Search New Files"), |
|
3908 self.trUtf8("There were no new files found to be added.")) |
|
3909 return |
|
3910 |
|
3911 # autoInclude is not set, show a dialog |
|
3912 dlg = AddFoundFilesDialog(newFiles, self.parent(), None) |
|
3913 res = dlg.exec_() |
|
3914 |
|
3915 # the 'Add All' button was pressed |
|
3916 if res == 1: |
|
3917 for file in newFiles: |
|
3918 self.appendFile(file) |
|
3919 |
|
3920 # the 'Add Selected' button was pressed |
|
3921 elif res == 2: |
|
3922 files = dlg.getSelection() |
|
3923 for file in files: |
|
3924 self.appendFile(file) |
|
3925 |
|
3926 def othersAdded(self, fn, updateModel = True): |
|
3927 """ |
|
3928 Public slot to be called, if something was added to the OTHERS project data area. |
|
3929 |
|
3930 @param fn filename or directory name added (string) |
|
3931 @param updateModel flag indicating an update of the model is requested (boolean) |
|
3932 """ |
|
3933 self.emit(SIGNAL('projectOthersAdded'), fn) |
|
3934 updateModel and self.__model.addNewItem("OTHERS", fn) |
|
3935 |
|
3936 def getActions(self): |
|
3937 """ |
|
3938 Public method to get a list of all actions. |
|
3939 |
|
3940 @return list of all actions (list of E4Action) |
|
3941 """ |
|
3942 return self.actions[:] |
|
3943 |
|
3944 def addE4Actions(self, actions): |
|
3945 """ |
|
3946 Public method to add actions to the list of actions. |
|
3947 |
|
3948 @param actions list of actions (list of E4Action) |
|
3949 """ |
|
3950 self.actions.extend(actions) |
|
3951 |
|
3952 def removeE4Actions(self, actions): |
|
3953 """ |
|
3954 Public method to remove actions from the list of actions. |
|
3955 |
|
3956 @param actions list of actions (list of E4Action) |
|
3957 """ |
|
3958 for act in actions: |
|
3959 try: |
|
3960 self.actions.remove(act) |
|
3961 except ValueError: |
|
3962 pass |
|
3963 |
|
3964 def getMenu(self, menuName): |
|
3965 """ |
|
3966 Public method to get a reference to the main menu or a submenu. |
|
3967 |
|
3968 @param menuName name of the menu (string) |
|
3969 @return reference to the requested menu (QMenu) or None |
|
3970 """ |
|
3971 try: |
|
3972 return self.__menus[menuName] |
|
3973 except KeyError: |
|
3974 return None |
|
3975 |
|
3976 def repopulateItem(self, fullname): |
|
3977 """ |
|
3978 Public slot to repopulate a named item. |
|
3979 |
|
3980 @param fullname full name of the item to repopulate (string) |
|
3981 """ |
|
3982 if not self.isOpen(): |
|
3983 return |
|
3984 |
|
3985 name = fullname.replace(self.ppath + os.sep, "") |
|
3986 self.emit(SIGNAL("prepareRepopulateItem"), name) |
|
3987 self.__model.repopulateItem(name) |
|
3988 self.emit(SIGNAL("completeRepopulateItem"), name) |
|
3989 |
|
3990 ############################################################## |
|
3991 ## Below is the VCS interface |
|
3992 ############################################################## |
|
3993 |
|
3994 def initVCS(self, vcsSystem = None, nooverride = False): |
|
3995 """ |
|
3996 Public method used to instantiate a vcs system. |
|
3997 |
|
3998 @param vcsSystem type of VCS to be used (string) |
|
3999 @param nooverride flag indicating to ignore an override request (boolean) |
|
4000 @return a reference to the vcs object |
|
4001 """ |
|
4002 vcs = None |
|
4003 forProject = True |
|
4004 override = False |
|
4005 |
|
4006 if vcsSystem is None: |
|
4007 if len(self.pdata["VCS"]): |
|
4008 if self.pdata["VCS"][0] != 'None': |
|
4009 vcsSystem = self.pdata["VCS"][0] |
|
4010 else: |
|
4011 forProject = False |
|
4012 |
|
4013 if self.pdata["VCS"] and self.pdata["VCS"][0] != 'None': |
|
4014 if self.pudata["VCSOVERRIDE"] and \ |
|
4015 self.pudata["VCSOVERRIDE"][0] is not None and \ |
|
4016 not nooverride: |
|
4017 vcsSystem = self.pudata["VCSOVERRIDE"][0] |
|
4018 override = True |
|
4019 |
|
4020 if vcsSystem is not None: |
|
4021 try: |
|
4022 vcs = VCS.factory(vcsSystem) |
|
4023 except ImportError: |
|
4024 if override: |
|
4025 # override failed, revert to original |
|
4026 self.pudata["VCSOVERRIDE"] = [] |
|
4027 return self.initVCS(nooverride = True) |
|
4028 |
|
4029 if vcs: |
|
4030 vcsExists, msg = vcs.vcsExists() |
|
4031 if not vcsExists: |
|
4032 if override: |
|
4033 # override failed, revert to original |
|
4034 QApplication.restoreOverrideCursor() |
|
4035 QMessageBox.critical(None, |
|
4036 self.trUtf8("Version Control System"), |
|
4037 self.trUtf8("<p>The selected VCS <b>{0}</b> could not be found." |
|
4038 "<br/>Reverting override.</p><p>{1}</p>")\ |
|
4039 .format(vcsSystem, msg)) |
|
4040 self.pudata["VCSOVERRIDE"] = [] |
|
4041 return self.initVCS(nooverride = True) |
|
4042 |
|
4043 QApplication.restoreOverrideCursor() |
|
4044 QMessageBox.critical(None, |
|
4045 self.trUtf8("Version Control System"), |
|
4046 self.trUtf8("<p>The selected VCS <b>{0}</b> could not be found.<br/>" |
|
4047 "Disabling version control.</p><p>{1}</p>")\ |
|
4048 .format(vcsSystem, msg)) |
|
4049 vcs = None |
|
4050 if forProject: |
|
4051 self.pdata["VCS"][0] = 'None' |
|
4052 self.setDirty(True) |
|
4053 |
|
4054 if vcs and forProject: |
|
4055 # set the vcs options |
|
4056 try: |
|
4057 vcsopt = copy.deepcopy(self.pdata["VCSOPTIONS"][0]) |
|
4058 vcs.vcsSetOptions(vcsopt) |
|
4059 except LookupError: |
|
4060 pass |
|
4061 # set vcs specific data |
|
4062 try: |
|
4063 vcsother = copy.deepcopy(self.pdata["VCSOTHERDATA"][0]) |
|
4064 vcs.vcsSetOtherData(vcsother) |
|
4065 except LookupError: |
|
4066 pass |
|
4067 |
|
4068 if vcs is None: |
|
4069 self.vcsProjectHelper = VcsProjectHelper(None, self) |
|
4070 self.vcsBasicHelper = True |
|
4071 else: |
|
4072 self.vcsProjectHelper = vcs.vcsGetProjectHelper(self) |
|
4073 self.vcsBasicHelper = False |
|
4074 if self.vcsMenu is not None: |
|
4075 self.vcsProjectHelper.initMenu(self.vcsMenu) |
|
4076 return vcs |
|
4077 |
|
4078 def __showContextMenuVCS(self): |
|
4079 """ |
|
4080 Private slot called before the vcs menu is shown. |
|
4081 """ |
|
4082 self.vcsProjectHelper.showMenu() |
|
4083 if self.vcsBasicHelper: |
|
4084 self.emit(SIGNAL("showMenu"), "VCS", self.vcsMenu) |
|
4085 |
|
4086 ######################################################################### |
|
4087 ## Below is the interface to the checker tools |
|
4088 ######################################################################### |
|
4089 |
|
4090 def __showContextMenuChecks(self): |
|
4091 """ |
|
4092 Private slot called before the checks menu is shown. |
|
4093 """ |
|
4094 self.emit(SIGNAL("showMenu"), "Checks", self.checksMenu) |
|
4095 |
|
4096 ######################################################################### |
|
4097 ## Below is the interface to the packagers tools |
|
4098 ######################################################################### |
|
4099 |
|
4100 def __showContextMenuPackagers(self): |
|
4101 """ |
|
4102 Private slot called before the packagers menu is shown. |
|
4103 """ |
|
4104 self.emit(SIGNAL("showMenu"), "Packagers", self.packagersMenu) |
|
4105 |
|
4106 ######################################################################### |
|
4107 ## Below is the interface to the apidoc tools |
|
4108 ######################################################################### |
|
4109 |
|
4110 def __showContextMenuApiDoc(self): |
|
4111 """ |
|
4112 Private slot called before the apidoc menu is shown. |
|
4113 """ |
|
4114 self.emit(SIGNAL("showMenu"), "Apidoc", self.apidocMenu) |
|
4115 |
|
4116 ######################################################################### |
|
4117 ## Below is the interface to the show tools |
|
4118 ######################################################################### |
|
4119 |
|
4120 def __showCodeMetrics(self): |
|
4121 """ |
|
4122 Private slot used to calculate some code metrics for the project files. |
|
4123 """ |
|
4124 files = [os.path.join(self.ppath, file) \ |
|
4125 for file in self.pdata["SOURCES"] if file.endswith(".py")] |
|
4126 self.codemetrics = CodeMetricsDialog() |
|
4127 self.codemetrics.show() |
|
4128 self.codemetrics.start(files) |
|
4129 |
|
4130 def __showCodeCoverage(self): |
|
4131 """ |
|
4132 Private slot used to show the code coverage information for the project files. |
|
4133 """ |
|
4134 fn = self.getMainScript(True) |
|
4135 if fn is None: |
|
4136 QMessageBox.critical(self.ui, |
|
4137 self.trUtf8("Coverage Data"), |
|
4138 self.trUtf8("There is no main script defined for the" |
|
4139 " current project. Aborting")) |
|
4140 return |
|
4141 |
|
4142 tfn = Utilities.getTestFileName(fn) |
|
4143 basename = os.path.splitext(fn)[0] |
|
4144 tbasename = os.path.splitext(tfn)[0] |
|
4145 |
|
4146 # determine name of coverage file to be used |
|
4147 files = [] |
|
4148 f = "%s.coverage" % basename |
|
4149 tf = "%s.coverage" % tbasename |
|
4150 if os.path.isfile(f): |
|
4151 files.append(f) |
|
4152 if os.path.isfile(tf): |
|
4153 files.append(tf) |
|
4154 |
|
4155 if files: |
|
4156 if len(files) > 1: |
|
4157 fn, ok = QInputDialog.getItem(\ |
|
4158 None, |
|
4159 self.trUtf8("Code Coverage"), |
|
4160 self.trUtf8("Please select a coverage file"), |
|
4161 files, |
|
4162 0, False) |
|
4163 if not ok: |
|
4164 return |
|
4165 else: |
|
4166 fn = files[0] |
|
4167 else: |
|
4168 return |
|
4169 |
|
4170 files = [os.path.join(self.ppath, file) \ |
|
4171 for file in self.pdata["SOURCES"] if file.endswith(".py")] |
|
4172 self.codecoverage = PyCoverageDialog() |
|
4173 self.codecoverage.show() |
|
4174 self.codecoverage.start(fn, files) |
|
4175 |
|
4176 def __showProfileData(self): |
|
4177 """ |
|
4178 Private slot used to show the profiling information for the project. |
|
4179 """ |
|
4180 fn = self.getMainScript(True) |
|
4181 if fn is None: |
|
4182 QMessageBox.critical(self.ui, |
|
4183 self.trUtf8("Profile Data"), |
|
4184 self.trUtf8("There is no main script defined for the" |
|
4185 " current project. Aborting")) |
|
4186 return |
|
4187 |
|
4188 tfn = Utilities.getTestFileName(fn) |
|
4189 basename = os.path.splitext(fn)[0] |
|
4190 tbasename = os.path.splitext(tfn)[0] |
|
4191 |
|
4192 # determine name of profile file to be used |
|
4193 files = [] |
|
4194 f = "%s.profile" % basename |
|
4195 tf = "%s.profile" % tbasename |
|
4196 if os.path.isfile(f): |
|
4197 files.append(f) |
|
4198 if os.path.isfile(tf): |
|
4199 files.append(tf) |
|
4200 |
|
4201 if files: |
|
4202 if len(files) > 1: |
|
4203 fn, ok = QInputDialog.getItem(\ |
|
4204 None, |
|
4205 self.trUtf8("Profile Data"), |
|
4206 self.trUtf8("Please select a profile file"), |
|
4207 files, |
|
4208 0, False) |
|
4209 if not ok: |
|
4210 return |
|
4211 else: |
|
4212 fn = files[0] |
|
4213 else: |
|
4214 return |
|
4215 |
|
4216 self.profiledata = PyProfileDialog() |
|
4217 self.profiledata.show() |
|
4218 self.profiledata.start(fn) |
|
4219 |
|
4220 def __showContextMenuShow(self): |
|
4221 """ |
|
4222 Private slot called before the show menu is shown. |
|
4223 """ |
|
4224 fn = self.getMainScript(True) |
|
4225 if fn is not None: |
|
4226 tfn = Utilities.getTestFileName(fn) |
|
4227 basename = os.path.splitext(fn)[0] |
|
4228 tbasename = os.path.splitext(tfn)[0] |
|
4229 self.codeProfileAct.setEnabled(\ |
|
4230 os.path.isfile("%s.profile" % basename) or \ |
|
4231 os.path.isfile("%s.profile" % tbasename)) |
|
4232 self.codeCoverageAct.setEnabled(\ |
|
4233 os.path.isfile("%s.coverage" % basename) or \ |
|
4234 os.path.isfile("%s.coverage" % tbasename)) |
|
4235 else: |
|
4236 self.codeProfileAct.setEnabled(False) |
|
4237 self.codeCoverageAct.setEnabled(False) |
|
4238 |
|
4239 self.emit(SIGNAL("showMenu"), "Show", self.showMenu) |
|
4240 |
|
4241 ######################################################################### |
|
4242 ## Below is the interface to the diagrams |
|
4243 ######################################################################### |
|
4244 |
|
4245 def __showContextMenuGraphics(self): |
|
4246 """ |
|
4247 Private slot called before the graphics menu is shown. |
|
4248 """ |
|
4249 self.emit(SIGNAL("showMenu"), "Graphics", self.graphicsMenu) |
|
4250 |
|
4251 def handleApplicationDiagram(self): |
|
4252 """ |
|
4253 Private method to handle the application diagram context menu action. |
|
4254 """ |
|
4255 res = QMessageBox.question(None, |
|
4256 self.trUtf8("Application Diagram"), |
|
4257 self.trUtf8("""Include module names?"""), |
|
4258 QMessageBox.StandardButtons(\ |
|
4259 QMessageBox.No | \ |
|
4260 QMessageBox.Yes), |
|
4261 QMessageBox.Yes) |
|
4262 |
|
4263 self.applicationDiagram = ApplicationDiagram(self, self.parent(), |
|
4264 noModules = (res != QMessageBox.Yes)) |
|
4265 self.applicationDiagram.show() |
|
4266 |
|
4267 ######################################################################### |
|
4268 ## Below is the interface to the VCS monitor thread |
|
4269 ######################################################################### |
|
4270 |
|
4271 def __statusMonitorStatus(self, status, statusMsg): |
|
4272 """ |
|
4273 Private method to receive the status monitor status. |
|
4274 |
|
4275 It simply reemits the received status. |
|
4276 |
|
4277 @param status status of the monitoring thread (string, ok, nok or off) |
|
4278 @param statusMsg explanotory text for the signaled status (string) |
|
4279 """ |
|
4280 self.emit(SIGNAL("vcsStatusMonitorStatus(QString, QString)"), status, statusMsg) |
|
4281 |
|
4282 def setStatusMonitorInterval(self, interval): |
|
4283 """ |
|
4284 Public method to se the interval of the VCS status monitor thread. |
|
4285 |
|
4286 @param interval status monitor interval in seconds (integer) |
|
4287 """ |
|
4288 if self.vcs is not None: |
|
4289 self.vcs.setStatusMonitorInterval(interval, self) |
|
4290 |
|
4291 def getStatusMonitorInterval(self): |
|
4292 """ |
|
4293 Public method to get the monitor interval. |
|
4294 |
|
4295 @return interval in seconds (integer) |
|
4296 """ |
|
4297 if self.vcs is not None: |
|
4298 return self.vcs.getStatusMonitorInterval() |
|
4299 else: |
|
4300 return 0 |
|
4301 |
|
4302 def setStatusMonitorAutoUpdate(self, auto): |
|
4303 """ |
|
4304 Public method to enable the auto update function. |
|
4305 |
|
4306 @param auto status of the auto update function (boolean) |
|
4307 """ |
|
4308 if self.vcs is not None: |
|
4309 self.vcs.setStatusMonitorAutoUpdate(auto) |
|
4310 |
|
4311 def getStatusMonitorAutoUpdate(self): |
|
4312 """ |
|
4313 Public method to retrieve the status of the auto update function. |
|
4314 |
|
4315 @return status of the auto update function (boolean) |
|
4316 """ |
|
4317 if self.vcs is not None: |
|
4318 return self.vcs.getStatusMonitorAutoUpdate() |
|
4319 else: |
|
4320 return False |
|
4321 |
|
4322 def checkVCSStatus(self): |
|
4323 """ |
|
4324 Public method to wake up the VCS status monitor thread. |
|
4325 """ |
|
4326 if self.vcs is not None: |
|
4327 self.vcs.checkVCSStatus() |
|
4328 |
|
4329 def clearStatusMonitorCachedState(self, name): |
|
4330 """ |
|
4331 Public method to clear the cached VCS state of a file/directory. |
|
4332 |
|
4333 @param name name of the entry to be cleared (string) |
|
4334 """ |
|
4335 if self.vcs is not None: |
|
4336 self.vcs.clearStatusMonitorCachedState(name) |
|
4337 |
|
4338 def startStatusMonitor(self): |
|
4339 """ |
|
4340 Public method to start the VCS status monitor thread. |
|
4341 """ |
|
4342 if self.vcs is not None: |
|
4343 self.vcs.startStatusMonitor(self) |
|
4344 |
|
4345 def stopStatusMonitor(self): |
|
4346 """ |
|
4347 Public method to stop the VCS status monitor thread. |
|
4348 """ |
|
4349 if self.vcs is not None: |
|
4350 self.vcs.stopStatusMonitor() |
|
4351 |
|
4352 ######################################################################### |
|
4353 ## Below are the plugin development related methods |
|
4354 ######################################################################### |
|
4355 |
|
4356 def __pluginCreatePkgList(self): |
|
4357 """ |
|
4358 Private slot to create a PKGLIST file needed for archive file creation. |
|
4359 """ |
|
4360 pkglist = os.path.join(self.ppath, "PKGLIST") |
|
4361 if os.path.exists(pkglist): |
|
4362 res = QMessageBox.warning(None, |
|
4363 self.trUtf8("Create Package List"), |
|
4364 self.trUtf8("<p>The file <b>PKGLIST</b> already" |
|
4365 " exists.</p><p>Overwrite it?</p>"), |
|
4366 QMessageBox.StandardButtons(\ |
|
4367 QMessageBox.No | \ |
|
4368 QMessageBox.Yes), |
|
4369 QMessageBox.No) |
|
4370 if res != QMessageBox.Yes: |
|
4371 return # don't overwrite |
|
4372 |
|
4373 # build the list of entries |
|
4374 lst = [] |
|
4375 for key in \ |
|
4376 ["SOURCES", "FORMS", "RESOURCES", "TRANSLATIONS", "INTERFACES", "OTHERS"]: |
|
4377 lst.extend(self.pdata[key]) |
|
4378 lst.sort() |
|
4379 if "PKGLIST" in lst: |
|
4380 lst.remove("PKGLIST") |
|
4381 |
|
4382 # write the file |
|
4383 try: |
|
4384 pkglistFile = open(pkglist, "wb") |
|
4385 pkglistFile.write("\n".join(lst)) |
|
4386 pkglistFile.close() |
|
4387 except IOError, why: |
|
4388 QMessageBox.critical(None, |
|
4389 self.trUtf8("Create Package List"), |
|
4390 self.trUtf8("""<p>The file <b>PKGLIST</b> could not be created.</p>""" |
|
4391 """<p>Reason: {0}</p>""").format(unicode(why)), |
|
4392 QMessageBox.StandardButtons(\ |
|
4393 QMessageBox.Ok)) |
|
4394 return |
|
4395 |
|
4396 if not "PKGLIST" in self.pdata["OTHERS"]: |
|
4397 self.appendFile("PKGLIST") |
|
4398 |
|
4399 def __pluginCreateArchive(self, snapshot = False): |
|
4400 """ |
|
4401 Private slot to create an eric4 plugin archive. |
|
4402 |
|
4403 @param snapshot flag indicating a snapshot archive (boolean) |
|
4404 """ |
|
4405 pkglist = os.path.join(self.ppath, "PKGLIST") |
|
4406 if not os.path.exists(pkglist): |
|
4407 QMessageBox.critical(None, |
|
4408 self.trUtf8("Create Plugin Archive"), |
|
4409 self.trUtf8("""<p>The file <b>PKGLIST</b> does not exist. """ |
|
4410 """Aborting...</p>"""), |
|
4411 QMessageBox.StandardButtons(\ |
|
4412 QMessageBox.Ok)) |
|
4413 return |
|
4414 |
|
4415 if len(self.pdata["MAINSCRIPT"]) == 0 or \ |
|
4416 len(self.pdata["MAINSCRIPT"][0]) == 0: |
|
4417 QMessageBox.critical(None, |
|
4418 self.trUtf8("Create Plugin Archive"), |
|
4419 self.trUtf8("""The project does not have a main script defined. """ |
|
4420 """Aborting..."""), |
|
4421 QMessageBox.StandardButtons(\ |
|
4422 QMessageBox.Ok)) |
|
4423 return |
|
4424 |
|
4425 try: |
|
4426 pkglistFile = open(pkglist, "rb") |
|
4427 names = pkglistFile.read() |
|
4428 pkglistFile.close() |
|
4429 names = names.splitlines() |
|
4430 names.sort() |
|
4431 except IOError, why: |
|
4432 QMessageBox.critical(None, |
|
4433 self.trUtf8("Create Plugin Archive"), |
|
4434 self.trUtf8("""<p>The file <b>PKGLIST</b> could not be read.</p>""" |
|
4435 """<p>Reason: {0}</p>""").format(unicode(why)), |
|
4436 QMessageBox.StandardButtons(\ |
|
4437 QMessageBox.Ok)) |
|
4438 return |
|
4439 |
|
4440 archive = \ |
|
4441 os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0].replace(".py", ".zip")) |
|
4442 try: |
|
4443 try: |
|
4444 archiveFile = zipfile.ZipFile(archive, "w", zipfile.ZIP_DEFLATED) |
|
4445 except RuntimeError: |
|
4446 archiveFile = zipfile.ZipFile(archive, "w") |
|
4447 except IOError, why: |
|
4448 QMessageBox.critical(None, |
|
4449 self.trUtf8("Create Plugin Archive"), |
|
4450 self.trUtf8("""<p>The eric4 plugin archive file <b>{0}</b> could """ |
|
4451 """not be created.</p>""" |
|
4452 """<p>Reason: {1}</p>""").format(archive, unicode(why)), |
|
4453 QMessageBox.StandardButtons(\ |
|
4454 QMessageBox.Ok)) |
|
4455 return |
|
4456 |
|
4457 for name in names: |
|
4458 try: |
|
4459 self.__createZipDirEntries(os.path.split(name)[0], archiveFile) |
|
4460 if snapshot and name == self.pdata["MAINSCRIPT"][0]: |
|
4461 snapshotSource, version = self.__createSnapshotSource(\ |
|
4462 os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0])) |
|
4463 archiveFile.writestr(name, snapshotSource) |
|
4464 else: |
|
4465 archiveFile.write(os.path.join(self.ppath, name), name) |
|
4466 if name == self.pdata["MAINSCRIPT"][0]: |
|
4467 version = self.__pluginExtractVersion(\ |
|
4468 os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0])) |
|
4469 except OSError, why: |
|
4470 QMessageBox.critical(None, |
|
4471 self.trUtf8("Create Plugin Archive"), |
|
4472 self.trUtf8("""<p>The file <b>{0}</b> could not be stored """ |
|
4473 """in the archive. Ignoring it.</p>""" |
|
4474 """<p>Reason: {1}</p>""")\ |
|
4475 .format(os.path.join(self.ppath, name), unicode(why)), |
|
4476 QMessageBox.StandardButtons(\ |
|
4477 QMessageBox.Ok)) |
|
4478 archiveFile.writestr("VERSION", version) |
|
4479 archiveFile.close() |
|
4480 |
|
4481 if not archive in self.pdata["OTHERS"]: |
|
4482 self.appendFile(archive) |
|
4483 |
|
4484 QMessageBox.information(None, |
|
4485 self.trUtf8("Create Plugin Archive"), |
|
4486 self.trUtf8("""<p>The eric4 plugin archive file <b>{0}</b> was """ |
|
4487 """created successfully.</p>""").format(archive), |
|
4488 QMessageBox.StandardButtons(\ |
|
4489 QMessageBox.Ok)) |
|
4490 |
|
4491 def __pluginCreateSnapshotArchive(self): |
|
4492 """ |
|
4493 Private slot to create an eric4 plugin archive snapshot release. |
|
4494 """ |
|
4495 self.__pluginCreateArchive(True) |
|
4496 |
|
4497 def __createZipDirEntries(self, path, zipFile): |
|
4498 """ |
|
4499 Private method to create dir entries in the zip file. |
|
4500 |
|
4501 @param path name of the directory entry to create (string) |
|
4502 @param zipFile open ZipFile object (zipfile.ZipFile) |
|
4503 """ |
|
4504 if path == "" or path == "/" or path == "\\": |
|
4505 return |
|
4506 |
|
4507 if not path.endswith("/") and not path.endswith("\\"): |
|
4508 path = "%s/" % path |
|
4509 |
|
4510 if not path in zipFile.namelist(): |
|
4511 self.__createZipDirEntries(os.path.split(path[:-1])[0], zipFile) |
|
4512 zipFile.writestr(path, "") |
|
4513 |
|
4514 def __createSnapshotSource(self, filename): |
|
4515 """ |
|
4516 Private method to create a snapshot plugin version. |
|
4517 |
|
4518 The version entry in the plugin module is modified to signify |
|
4519 a snapshot version. This method appends the string "-snapshot-" |
|
4520 and date indicator to the version string. |
|
4521 |
|
4522 @param filename name of the plugin file to modify (string) |
|
4523 @return modified source (string), snapshot version string (string) |
|
4524 """ |
|
4525 try: |
|
4526 f = open(filename, "r") |
|
4527 sourcelines = f.readlines() |
|
4528 f.close() |
|
4529 except IOError, why: |
|
4530 QMessageBox.critical(None, |
|
4531 self.trUtf8("Create Plugin Archive"), |
|
4532 self.trUtf8("""<p>The plugin file <b>{0}</b> could """ |
|
4533 """not be read.</p>""" |
|
4534 """<p>Reason: {1}</p>""").format(archive, unicode(why)), |
|
4535 QMessageBox.StandardButtons(\ |
|
4536 QMessageBox.Ok)) |
|
4537 return "" |
|
4538 |
|
4539 lineno = 0 |
|
4540 while lineno < len(sourcelines): |
|
4541 if sourcelines[lineno].startswith("version = "): |
|
4542 # found the line to modify |
|
4543 datestr = time.strftime("%Y%m%d") |
|
4544 lineend = sourcelines[lineno].replace(sourcelines[lineno].rstrip(), "") |
|
4545 sversion = "%s-snapshot-%s" % \ |
|
4546 (sourcelines[lineno].replace("version = ", "").strip()[1:-1], |
|
4547 datestr) |
|
4548 sourcelines[lineno] = '%s + "-snapshot-%s"%s' % \ |
|
4549 (sourcelines[lineno].rstrip(), datestr, lineend) |
|
4550 break |
|
4551 |
|
4552 lineno += 1 |
|
4553 |
|
4554 return "".join(sourcelines), sversion |
|
4555 |
|
4556 def __pluginExtractVersion(self, filename): |
|
4557 """ |
|
4558 Private method to extract the version number entry. |
|
4559 |
|
4560 @param filename name of the plugin file to modify (string) |
|
4561 @return version string (string) |
|
4562 """ |
|
4563 version = "0.0.0" |
|
4564 try: |
|
4565 f = open(filename, "r") |
|
4566 sourcelines = f.readlines() |
|
4567 f.close() |
|
4568 except IOError, why: |
|
4569 QMessageBox.critical(None, |
|
4570 self.trUtf8("Create Plugin Archive"), |
|
4571 self.trUtf8("""<p>The plugin file <b>{0}</b> could """ |
|
4572 """not be read.</p>""" |
|
4573 """<p>Reason: {1}</p>""").format(archive, unicode(why)), |
|
4574 QMessageBox.StandardButtons(\ |
|
4575 QMessageBox.Ok)) |
|
4576 return "" |
|
4577 |
|
4578 lineno = 0 |
|
4579 while lineno < len(sourcelines): |
|
4580 if sourcelines[lineno].startswith("version = "): |
|
4581 version = sourcelines[lineno].replace("version = ", "").strip()[1:-1] |
|
4582 break |
|
4583 |
|
4584 lineno += 1 |
|
4585 |
|
4586 return version |