Project/Project.py

changeset 2995
63d874899b8b
parent 2964
84b65fb9e780
child 3010
befeff46ec0f
equal deleted inserted replaced
2994:5ae1349b8fb4 2995:63d874899b8b
13 import glob 13 import glob
14 import fnmatch 14 import fnmatch
15 import copy 15 import copy
16 import zipfile 16 import zipfile
17 17
18 from PyQt4.QtCore import QFile, QFileInfo, pyqtSignal, QCryptographicHash, QIODevice, \ 18 from PyQt4.QtCore import QFile, QFileInfo, pyqtSignal, QCryptographicHash, \
19 QByteArray, QObject, Qt 19 QIODevice, QByteArray, QObject, Qt
20 from PyQt4.QtGui import QCursor, QLineEdit, QToolBar, QDialog, QInputDialog, \ 20 from PyQt4.QtGui import QCursor, QLineEdit, QToolBar, QDialog, QInputDialog, \
21 QApplication, QMenu, QAction 21 QApplication, QMenu, QAction
22 from PyQt4.Qsci import QsciScintilla 22 from PyQt4.Qsci import QsciScintilla
23 23
24 from E5Gui.E5Application import e5App 24 from E5Gui.E5Application import e5App
38 """ 38 """
39 Class implementing the project management functionality. 39 Class implementing the project management functionality.
40 40
41 @signal dirty(int) emitted when the dirty state changes 41 @signal dirty(int) emitted when the dirty state changes
42 @signal projectLanguageAdded(str) emitted after a new language was added 42 @signal projectLanguageAdded(str) emitted after a new language was added
43 @signal projectLanguageAddedByCode(str) emitted after a new language was added. 43 @signal projectLanguageAddedByCode(str) emitted after a new language was
44 The language code is sent by this signal. 44 added. The language code is sent by this signal.
45 @signal projectLanguageRemoved(str) emitted after a language was removed 45 @signal projectLanguageRemoved(str) emitted after a language was removed
46 @signal projectFormAdded(str) emitted after a new form was added 46 @signal projectFormAdded(str) emitted after a new form was added
47 @signal projectFormRemoved(str) emitted after a form was removed 47 @signal projectFormRemoved(str) emitted after a form was removed
48 @signal projectFormCompiled(str) emitted after a form was compiled 48 @signal projectFormCompiled(str) emitted after a form was compiled
49 @signal projectSourceAdded(str) emitted after a new source file was added 49 @signal projectSourceAdded(str) emitted after a new source file was added
50 @signal projectSourceRemoved(str) emitted after a source was removed 50 @signal projectSourceRemoved(str) emitted after a source was removed
51 @signal projectInterfaceAdded(str) emitted after a new IDL file was added 51 @signal projectInterfaceAdded(str) emitted after a new IDL file was added
52 @signal projectInterfaceRemoved(str) emitted after a IDL file was removed 52 @signal projectInterfaceRemoved(str) emitted after a IDL file was removed
53 @signal projectResourceAdded(str) emitted after a new resource file was added 53 @signal projectResourceAdded(str) emitted after a new resource file was
54 added
54 @signal projectResourceRemoved(str) emitted after a resource was removed 55 @signal projectResourceRemoved(str) emitted after a resource was removed
55 @signal projectOthersAdded(str) emitted after a file or directory was added 56 @signal projectOthersAdded(str) emitted after a file or directory was added
56 to the OTHERS project data area 57 to the OTHERS project data area
57 @signal projectOthersRemoved(str) emitted after a file was removed from the 58 @signal projectOthersRemoved(str) emitted after a file was removed from the
58 OTHERS project data area 59 OTHERS project data area
59 @signal projectAboutToBeCreated() emitted just before the project will be created 60 @signal projectAboutToBeCreated() emitted just before the project will be
60 @signal newProjectHooks() emitted after a new project was generated but before 61 created
61 the newProject() signal is sent 62 @signal newProjectHooks() emitted after a new project was generated but
63 before the newProject() signal is sent
62 @signal newProject() emitted after a new project was generated 64 @signal newProject() emitted after a new project was generated
63 @signal sourceFile(str) emitted after a project file was read to 65 @signal sourceFile(str) emitted after a project file was read to
64 open the main script 66 open the main script
65 @signal projectOpenedHooks() emitted after a project file was read but before the 67 @signal projectOpenedHooks() emitted after a project file was read but
66 projectOpened() signal is sent 68 before the projectOpened() signal is sent
67 @signal projectOpened() emitted after a project file was read 69 @signal projectOpened() emitted after a project file was read
68 @signal projectClosedHooks() emitted after a project file was closed but before the 70 @signal projectClosedHooks() emitted after a project file was closed but
69 projectClosed() signal is sent 71 before the projectClosed() signal is sent
70 @signal projectClosed() emitted after a project was closed 72 @signal projectClosed() emitted after a project was closed
71 @signal projectFileRenamed(str, str) emitted after a file of the project 73 @signal projectFileRenamed(str, str) emitted after a file of the project
72 has been renamed 74 has been renamed
73 @signal projectPropertiesChanged() emitted after the project properties were changed 75 @signal projectPropertiesChanged() emitted after the project properties
74 @signal directoryRemoved(str) emitted after a directory has been removed from 76 were changed
75 the project 77 @signal directoryRemoved(str) emitted after a directory has been removed
78 from the project
76 @signal prepareRepopulateItem(str) emitted before an item of the model is 79 @signal prepareRepopulateItem(str) emitted before an item of the model is
77 repopulated 80 repopulated
78 @signal completeRepopulateItem(str) emitted after an item of the model was 81 @signal completeRepopulateItem(str) emitted after an item of the model was
79 repopulated 82 repopulated
80 @signal vcsStatusMonitorStatus(str, str) emitted to signal the status of the 83 @signal vcsStatusMonitorStatus(str, str) emitted to signal the status of
81 monitoring thread (ok, nok, op, off) and a status message 84 the monitoring thread (ok, nok, op, off) and a status message
82 @signal reinitVCS() emitted after the VCS has been reinitialized 85 @signal reinitVCS() emitted after the VCS has been reinitialized
83 @signal showMenu(str, QMenu) emitted when a menu is about to be shown. The name 86 @signal showMenu(str, QMenu) emitted when a menu is about to be shown. The
84 of the menu and a reference to the menu are given. 87 name of the menu and a reference to the menu are given.
85 @signal lexerAssociationsChanged() emitted after the lexer associations have been 88 @signal lexerAssociationsChanged() emitted after the lexer associations
86 changed 89 have been changed
87 @signal projectChanged() emitted to signal a change of the project 90 @signal projectChanged() emitted to signal a change of the project
88 """ 91 """
89 dirty = pyqtSignal(int) 92 dirty = pyqtSignal(int)
90 projectLanguageAdded = pyqtSignal(str) 93 projectLanguageAdded = pyqtSignal(str)
91 projectLanguageAddedByCode = pyqtSignal(str) 94 projectLanguageAddedByCode = pyqtSignal(str)
235 self.__projectTypes["E4Plugin"] = self.trUtf8("Eric Plugin") 238 self.__projectTypes["E4Plugin"] = self.trUtf8("Eric Plugin")
236 self.__projectTypes["Console"] = self.trUtf8("Console") 239 self.__projectTypes["Console"] = self.trUtf8("Console")
237 self.__projectTypes["Other"] = self.trUtf8("Other") 240 self.__projectTypes["Other"] = self.trUtf8("Other")
238 241
239 self.__projectProgLanguages = { 242 self.__projectProgLanguages = {
240 "Python2": ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E4Plugin", "Console", "Other"], 243 "Python2": ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E4Plugin",
241 "Python3": ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E4Plugin", "Console", "Other"], 244 "Console", "Other"],
245 "Python3": ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E4Plugin",
246 "Console", "Other"],
242 "Ruby": ["Qt4", "Qt4C", "Console", "Other"], 247 "Ruby": ["Qt4", "Qt4C", "Console", "Other"],
243 } 248 }
244 249
245 pyside2, pyside3 = Utilities.checkPyside() 250 pyside2, pyside3 = Utilities.checkPyside()
246 if pyside2 or pyside3: 251 if pyside2 or pyside3:
247 self.__projectTypes["PySide"] = self.trUtf8("PySide GUI") 252 self.__projectTypes["PySide"] = self.trUtf8("PySide GUI")
248 self.__projectTypes["PySideC"] = self.trUtf8("PySide Console") 253 self.__projectTypes["PySideC"] = self.trUtf8("PySide Console")
249 if pyside2: 254 if pyside2:
250 self.__projectProgLanguages["Python2"].extend(["PySide", "PySideC"]) 255 self.__projectProgLanguages["Python2"].extend(
256 ["PySide", "PySideC"])
251 if pyside3: 257 if pyside3:
252 self.__projectProgLanguages["Python3"].extend(["PySide", "PySideC"]) 258 self.__projectProgLanguages["Python3"].extend(
259 ["PySide", "PySideC"])
253 260
254 def getProjectTypes(self, progLanguage=""): 261 def getProjectTypes(self, progLanguage=""):
255 """ 262 """
256 Public method to get the list of supported project types. 263 Public method to get the list of supported project types.
257 264
258 @param progLanguage programming language to get project types for (string) 265 @param progLanguage programming language to get project types for
266 (string)
259 @return reference to the dictionary of project types. 267 @return reference to the dictionary of project types.
260 """ 268 """
261 if progLanguage and progLanguage in self.__projectProgLanguages: 269 if progLanguage and progLanguage in self.__projectProgLanguages:
262 ptypes = {} 270 ptypes = {}
263 for ptype in self.__projectProgLanguages[progLanguage]: 271 for ptype in self.__projectProgLanguages[progLanguage]:
288 296
289 @param type_ internal type designator to be registered (string) 297 @param type_ internal type designator to be registered (string)
290 @param description more verbose type name (display string) (string) 298 @param description more verbose type name (display string) (string)
291 @keyparam fileTypeCallback reference to a method returning a dictionary 299 @keyparam fileTypeCallback reference to a method returning a dictionary
292 of filetype associations. 300 of filetype associations.
293 @keyparam binaryTranslationsCallback reference to a method returning the 301 @keyparam binaryTranslationsCallback reference to a method returning
294 name of the binary translation file given the name of the raw 302 the name of the binary translation file given the name of the raw
295 translation file 303 translation file
296 @keyparam lexerAssociationCallback reference to a method returning the 304 @keyparam lexerAssociationCallback reference to a method returning the
297 lexer type to be used for syntax highlighting given the name of 305 lexer type to be used for syntax highlighting given the name of
298 a file 306 a file
299 @keyparam progLanguages programming languages supported by the 307 @keyparam progLanguages programming languages supported by the
302 if progLanguages: 310 if progLanguages:
303 for progLanguage in progLanguages: 311 for progLanguage in progLanguages:
304 if progLanguage not in self.__projectProgLanguages: 312 if progLanguage not in self.__projectProgLanguages:
305 E5MessageBox.critical(self.ui, 313 E5MessageBox.critical(self.ui,
306 self.trUtf8("Registering Project Type"), 314 self.trUtf8("Registering Project Type"),
307 self.trUtf8("""<p>The Programming Language <b>{0}</b> is not""" 315 self.trUtf8(
308 """ supported.</p>""")\ 316 """<p>The Programming Language <b>{0}</b> is not"""
317 """ supported.</p>""")\
309 .format(progLanguage) 318 .format(progLanguage)
310 ) 319 )
311 return 320 return
312 321
313 if type_ in self.__projectProgLanguages[progLanguage]: 322 if type_ in self.__projectProgLanguages[progLanguage]:
314 E5MessageBox.critical(self.ui, 323 E5MessageBox.critical(self.ui,
315 self.trUtf8("Registering Project Type"), 324 self.trUtf8("Registering Project Type"),
316 self.trUtf8("""<p>The Project type <b>{0}</b> is already""" 325 self.trUtf8(
317 """ registered with Programming Language""" 326 """<p>The Project type <b>{0}</b> is already"""
318 """ <b>{1}</b>.</p>""")\ 327 """ registered with Programming Language"""
328 """ <b>{1}</b>.</p>""")\
319 .format(type_, progLanguage) 329 .format(type_, progLanguage)
320 ) 330 )
321 return 331 return
322 332
323 if type_ in self.__projectTypes: 333 if type_ in self.__projectTypes:
328 ) 338 )
329 else: 339 else:
330 self.__projectTypes[type_] = description 340 self.__projectTypes[type_] = description
331 self.__fileTypeCallbacks[type_] = fileTypeCallback 341 self.__fileTypeCallbacks[type_] = fileTypeCallback
332 self.__lexerAssociationCallbacks[type_] = lexerAssociationCallback 342 self.__lexerAssociationCallbacks[type_] = lexerAssociationCallback
333 self.__binaryTranslationsCallbacks[type_] = binaryTranslationsCallback 343 self.__binaryTranslationsCallbacks[type_] = \
344 binaryTranslationsCallback
334 if progLanguages: 345 if progLanguages:
335 for progLanguage in progLanguages: 346 for progLanguage in progLanguages:
336 self.__projectProgLanguages[progLanguage].append(type_) 347 self.__projectProgLanguages[progLanguage].append(type_)
337 else: 348 else:
338 # no specific programming languages given -> add to all 349 # no specific programming languages given -> add to all
366 self.pfile = "" # name of the project file 377 self.pfile = "" # name of the project file
367 self.ppath = "" # name of the project directory 378 self.ppath = "" # name of the project directory
368 self.translationsRoot = "" # the translations prefix 379 self.translationsRoot = "" # the translations prefix
369 self.name = "" 380 self.name = ""
370 self.opened = False 381 self.opened = False
371 self.subdirs = [""] # record the project dir as a relative path (i.e. empty path) 382 self.subdirs = [""] # record the project dir as a relative path
383 # (i.e. empty path)
372 self.otherssubdirs = [] 384 self.otherssubdirs = []
373 self.vcs = None 385 self.vcs = None
374 self.vcsRequested = False 386 self.vcsRequested = False
375 self.dbgCmdline = '' 387 self.dbgCmdline = ''
376 self.dbgWd = '' 388 self.dbgWd = ''
415 def getData(self, category, key): 427 def getData(self, category, key):
416 """ 428 """
417 Public method to get data out of the project data store. 429 Public method to get data out of the project data store.
418 430
419 @param category category of the data to get (string, one of 431 @param category category of the data to get (string, one of
420 PROJECTTYPESPECIFICDATA, CHECKERSPARMS, PACKAGERSPARMS, DOCUMENTATIONPARMS 432 PROJECTTYPESPECIFICDATA, CHECKERSPARMS, PACKAGERSPARMS,
421 or OTHERTOOLSPARMS) 433 DOCUMENTATIONPARMS or OTHERTOOLSPARMS)
422 @param key key of the data entry to get (string). 434 @param key key of the data entry to get (string).
423 @return a copy of the requested data or None 435 @return a copy of the requested data or None
424 """ 436 """
425 if category in ["PROJECTTYPESPECIFICDATA", "CHECKERSPARMS", "PACKAGERSPARMS", 437 if category in ["PROJECTTYPESPECIFICDATA", "CHECKERSPARMS",
426 "DOCUMENTATIONPARMS", "OTHERTOOLSPARMS"] and \ 438 "PACKAGERSPARMS", "DOCUMENTATIONPARMS",
439 "OTHERTOOLSPARMS"] and \
427 key in self.pdata[category]: 440 key in self.pdata[category]:
428 return copy.deepcopy(self.pdata[category][key]) 441 return copy.deepcopy(self.pdata[category][key])
429 else: 442 else:
430 return None 443 return None
431 444
432 def setData(self, category, key, data): 445 def setData(self, category, key, data):
433 """ 446 """
434 Public method to store data in the project data store. 447 Public method to store data in the project data store.
435 448
436 @param category category of the data to get (string, one of 449 @param category category of the data to get (string, one of
437 PROJECTTYPESPECIFICDATA, CHECKERSPARMS, PACKAGERSPARMS, DOCUMENTATIONPARMS 450 PROJECTTYPESPECIFICDATA, CHECKERSPARMS, PACKAGERSPARMS,
438 or OTHERTOOLSPARMS) 451 DOCUMENTATIONPARMS or OTHERTOOLSPARMS)
439 @param key key of the data entry to get (string). 452 @param key key of the data entry to get (string).
440 @param data data to be stored 453 @param data data to be stored
441 @return flag indicating success (boolean) 454 @return flag indicating success (boolean)
442 """ 455 """
443 if category not in ["PROJECTTYPESPECIFICDATA", "CHECKERSPARMS", "PACKAGERSPARMS", 456 if category not in ["PROJECTTYPESPECIFICDATA", "CHECKERSPARMS",
444 "DOCUMENTATIONPARMS", "OTHERTOOLSPARMS"]: 457 "PACKAGERSPARMS", "DOCUMENTATIONPARMS",
458 "OTHERTOOLSPARMS"]:
445 return False 459 return False
446 460
447 # test for changes of data and save them in the project 461 # test for changes of data and save them in the project
448 # 1. there were none, now there are 462 # 1. there were none, now there are
449 if key not in self.pdata[category] and len(data) > 0: 463 if key not in self.pdata[category] and len(data) > 0:
463 return False 477 return False
464 return True 478 return True
465 479
466 def initFileTypes(self): 480 def initFileTypes(self):
467 """ 481 """
468 Public method to initialize the filetype associations with default values. 482 Public method to initialize the filetype associations with default
483 values.
469 """ 484 """
470 self.pdata["FILETYPES"] = {} 485 self.pdata["FILETYPES"] = {}
471 if self.pdata["MIXEDLANGUAGE"][0]: 486 if self.pdata["MIXEDLANGUAGE"][0]:
472 sourceKey = "Mixed" 487 sourceKey = "Mixed"
473 else: 488 else:
474 sourceKey = self.pdata["PROGLANGUAGE"][0] 489 sourceKey = self.pdata["PROGLANGUAGE"][0]
475 for ext in self.sourceExtensions[sourceKey]: 490 for ext in self.sourceExtensions[sourceKey]:
476 self.pdata["FILETYPES"]["*{0}".format(ext)] = "SOURCES" 491 self.pdata["FILETYPES"]["*{0}".format(ext)] = "SOURCES"
477 self.pdata["FILETYPES"]["*.idl"] = "INTERFACES" 492 self.pdata["FILETYPES"]["*.idl"] = "INTERFACES"
478 if self.pdata["PROJECTTYPE"][0] in ["Qt4", "PyQt5", "E4Plugin", "PySide"]: 493 if self.pdata["PROJECTTYPE"][0] in ["Qt4", "PyQt5", "E4Plugin",
494 "PySide"]:
479 self.pdata["FILETYPES"]["*.ui"] = "FORMS" 495 self.pdata["FILETYPES"]["*.ui"] = "FORMS"
480 self.pdata["FILETYPES"]["*.ui.h"] = "FORMS" 496 self.pdata["FILETYPES"]["*.ui.h"] = "FORMS"
481 if self.pdata["PROJECTTYPE"][0] in ["Qt4", "Qt4C", "E4Plugin", 497 if self.pdata["PROJECTTYPE"][0] in ["Qt4", "Qt4C", "E4Plugin",
482 "PyQt5", "PyQt5C", 498 "PyQt5", "PyQt5C",
483 "PySide", "PySideC"]: 499 "PySide", "PySideC"]:
486 "PyQt5", "PyQt5C", 502 "PyQt5", "PyQt5C",
487 "PySide", "PySideC"]: 503 "PySide", "PySideC"]:
488 self.pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS" 504 self.pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS"
489 self.pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS" 505 self.pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS"
490 try: 506 try:
491 if self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]] is not None: 507 if self.__fileTypeCallbacks[
492 ftypes = self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]]() 508 self.pdata["PROJECTTYPE"][0]] is not None:
509 ftypes = \
510 self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]]()
493 self.pdata["FILETYPES"].update(ftypes) 511 self.pdata["FILETYPES"].update(ftypes)
494 except KeyError: 512 except KeyError:
495 pass 513 pass
496 self.setDirty(True) 514 self.setDirty(True)
497 515
498 def updateFileTypes(self): 516 def updateFileTypes(self):
499 """ 517 """
500 Public method to update the filetype associations with new default values. 518 Public method to update the filetype associations with new default
519 values.
501 """ 520 """
502 if self.pdata["PROJECTTYPE"][0] in ["Qt4", "Qt4C", "E4Plugin", 521 if self.pdata["PROJECTTYPE"][0] in ["Qt4", "Qt4C", "E4Plugin",
503 "PyQt5", "PyQt5C", 522 "PyQt5", "PyQt5C",
504 "PySide", "PySideC"]: 523 "PySide", "PySideC"]:
505 if "*.ts" not in self.pdata["FILETYPES"]: 524 if "*.ts" not in self.pdata["FILETYPES"]:
506 self.pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS" 525 self.pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS"
507 if "*.qm" not in self.pdata["FILETYPES"]: 526 if "*.qm" not in self.pdata["FILETYPES"]:
508 self.pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS" 527 self.pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS"
509 try: 528 try:
510 if self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]] is not None: 529 if self.__fileTypeCallbacks[
511 ftypes = self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]]() 530 self.pdata["PROJECTTYPE"][0]] is not None:
531 ftypes = \
532 self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]]()
512 for pattern, ftype in list(ftypes.items()): 533 for pattern, ftype in list(ftypes.items()):
513 if pattern not in self.pdata["FILETYPES"]: 534 if pattern not in self.pdata["FILETYPES"]:
514 self.pdata["FILETYPES"][pattern] = ftype 535 self.pdata["FILETYPES"][pattern] = ftype
515 self.setDirty(True) 536 self.setDirty(True)
516 except KeyError: 537 except KeyError:
643 f.close() 664 f.close()
644 else: 665 else:
645 QApplication.restoreOverrideCursor() 666 QApplication.restoreOverrideCursor()
646 E5MessageBox.critical(self.ui, 667 E5MessageBox.critical(self.ui,
647 self.trUtf8("Read project file"), 668 self.trUtf8("Read project file"),
648 self.trUtf8("<p>The project file <b>{0}</b> could not be read.</p>")\ 669 self.trUtf8(
670 "<p>The project file <b>{0}</b> could not be read.</p>")\
649 .format(fn)) 671 .format(fn))
650 return False 672 return False
651 673
652 self.pfile = os.path.abspath(fn) 674 self.pfile = os.path.abspath(fn)
653 self.ppath = os.path.abspath(os.path.dirname(fn)) 675 self.ppath = os.path.abspath(os.path.dirname(fn))
658 if res: 680 if res:
659 if len(self.pdata["TRANSLATIONPATTERN"]) == 1: 681 if len(self.pdata["TRANSLATIONPATTERN"]) == 1:
660 self.translationsRoot = \ 682 self.translationsRoot = \
661 self.pdata["TRANSLATIONPATTERN"][0].split("%language%")[0] 683 self.pdata["TRANSLATIONPATTERN"][0].split("%language%")[0]
662 elif len(self.pdata["MAINSCRIPT"]) == 1: 684 elif len(self.pdata["MAINSCRIPT"]) == 1:
663 self.translationsRoot = os.path.splitext(self.pdata["MAINSCRIPT"][0])[0] 685 self.translationsRoot = os.path.splitext(
686 self.pdata["MAINSCRIPT"][0])[0]
664 if os.path.isdir(os.path.join(self.ppath, self.translationsRoot)): 687 if os.path.isdir(os.path.join(self.ppath, self.translationsRoot)):
665 dn = self.translationsRoot 688 dn = self.translationsRoot
666 else: 689 else:
667 dn = os.path.dirname(self.translationsRoot) 690 dn = os.path.dirname(self.translationsRoot)
668 if dn not in self.subdirs: 691 if dn not in self.subdirs:
669 self.subdirs.append(dn) 692 self.subdirs.append(dn)
670 693
671 self.name = os.path.splitext(os.path.basename(fn))[0] 694 self.name = os.path.splitext(os.path.basename(fn))[0]
672 695
673 # check, if the files of the project still exist in the project directory 696 # check, if the files of the project still exist in the
697 # project directory
674 self.__checkFilesExist("SOURCES") 698 self.__checkFilesExist("SOURCES")
675 self.__checkFilesExist("FORMS") 699 self.__checkFilesExist("FORMS")
676 self.__checkFilesExist("INTERFACES") 700 self.__checkFilesExist("INTERFACES")
677 self.__checkFilesExist("TRANSLATIONS") 701 self.__checkFilesExist("TRANSLATIONS")
678 self.__checkFilesExist("RESOURCES") 702 self.__checkFilesExist("RESOURCES")
708 def __writeProject(self, fn=None): 732 def __writeProject(self, fn=None):
709 """ 733 """
710 Private method to save the project infos to a project file. 734 Private method to save the project infos to a project file.
711 735
712 @param fn optional filename of the project file to be written (string). 736 @param fn optional filename of the project file to be written (string).
713 If fn is None, the filename stored in the project object 737 If fn is None, the filename stored in the project object
714 is used. This is the 'save' action. If fn is given, this filename 738 is used. This is the 'save' action. If fn is given, this filename
715 is used instead of the one in the project object. This is the 739 is used instead of the one in the project object. This is the
716 'save as' action. 740 'save as' action.
717 @return flag indicating success 741 @return flag indicating success
718 """ 742 """
719 if self.vcs is not None: 743 if self.vcs is not None:
720 self.pdata["VCSOPTIONS"] = [copy.deepcopy(self.vcs.vcsGetOptions())] 744 self.pdata["VCSOPTIONS"] = [
721 self.pdata["VCSOTHERDATA"] = [copy.deepcopy(self.vcs.vcsGetOtherData())] 745 copy.deepcopy(self.vcs.vcsGetOptions())]
746 self.pdata["VCSOTHERDATA"] = [
747 copy.deepcopy(self.vcs.vcsGetOtherData())]
722 748
723 if not self.pdata["HASH"][0]: 749 if not self.pdata["HASH"][0]:
724 hash = str(QCryptographicHash.hash( 750 hash = str(QCryptographicHash.hash(
725 QByteArray(self.ppath.encode("utf-8")), 751 QByteArray(self.ppath.encode("utf-8")),
726 QCryptographicHash.Sha1).toHex(), 752 QCryptographicHash.Sha1).toHex(),
731 fn = self.pfile 757 fn = self.pfile
732 758
733 f = QFile(fn) 759 f = QFile(fn)
734 if f.open(QIODevice.WriteOnly): 760 if f.open(QIODevice.WriteOnly):
735 from E5XML.ProjectWriter import ProjectWriter 761 from E5XML.ProjectWriter import ProjectWriter
736 ProjectWriter(f, os.path.splitext(os.path.basename(fn))[0]).writeXML() 762 ProjectWriter(f, os.path.splitext(
763 os.path.basename(fn))[0]).writeXML()
737 res = True 764 res = True
738 else: 765 else:
739 E5MessageBox.critical(self.ui, 766 E5MessageBox.critical(self.ui,
740 self.trUtf8("Save project file"), 767 self.trUtf8("Save project file"),
741 self.trUtf8("<p>The project file <b>{0}</b> could not be written.</p>")\ 768 self.trUtf8(
742 .format(fn)) 769 "<p>The project file <b>{0}</b> could not be"
770 " written.</p>").format(fn))
743 res = False 771 res = False
744 772
745 if res: 773 if res:
746 self.pfile = os.path.abspath(fn) 774 self.pfile = os.path.abspath(fn)
747 self.ppath = os.path.abspath(os.path.dirname(fn)) 775 self.ppath = os.path.abspath(os.path.dirname(fn))
770 reader.readXML() 798 reader.readXML()
771 f.close() 799 f.close()
772 else: 800 else:
773 E5MessageBox.critical(self.ui, 801 E5MessageBox.critical(self.ui,
774 self.trUtf8("Read user project properties"), 802 self.trUtf8("Read user project properties"),
775 self.trUtf8("<p>The user specific project properties file <b>{0}</b>" 803 self.trUtf8(
776 " could not be read.</p>").format(fn)) 804 "<p>The user specific project properties file"
805 " <b>{0}</b> could not be read.</p>").format(fn))
777 806
778 def __writeUserProperties(self): 807 def __writeUserProperties(self):
779 """ 808 """
780 Private method to write the project data to an XML file. 809 Private method to write the project data to an XML file.
781 """ 810 """
786 fn = os.path.join(self.getProjectManagementDir(), '{0}.e4q'.format(fn)) 815 fn = os.path.join(self.getProjectManagementDir(), '{0}.e4q'.format(fn))
787 816
788 f = QFile(fn) 817 f = QFile(fn)
789 if f.open(QIODevice.WriteOnly): 818 if f.open(QIODevice.WriteOnly):
790 from E5XML.UserProjectWriter import UserProjectWriter 819 from E5XML.UserProjectWriter import UserProjectWriter
791 UserProjectWriter(f, os.path.splitext(os.path.basename(fn))[0]).writeXML() 820 UserProjectWriter(
821 f, os.path.splitext(os.path.basename(fn))[0]).writeXML()
792 f.close() 822 f.close()
793 else: 823 else:
794 E5MessageBox.critical(self.ui, 824 E5MessageBox.critical(self.ui,
795 self.trUtf8("Save user project properties"), 825 self.trUtf8("Save user project properties"),
796 self.trUtf8("<p>The user specific project properties file <b>{0}</b>" 826 self.trUtf8(
827 "<p>The user specific project properties file <b>{0}</b>"
797 " could not be written.</p>").format(fn)) 828 " could not be written.</p>").format(fn))
798 829
799 def __showContextMenuSession(self): 830 def __showContextMenuSession(self):
800 """ 831 """
801 Private slot called before the Session menu is shown. 832 Private slot called before the Session menu is shown.
806 else: 837 else:
807 fn, ext = os.path.splitext(os.path.basename(self.pfile)) 838 fn, ext = os.path.splitext(os.path.basename(self.pfile))
808 fn = os.path.join(self.getProjectManagementDir(), 839 fn = os.path.join(self.getProjectManagementDir(),
809 '{0}.e4s'.format(fn)) 840 '{0}.e4s'.format(fn))
810 enable = os.path.exists(fn) 841 enable = os.path.exists(fn)
811 self.sessActGrp.findChild(QAction, "project_load_session").setEnabled(enable) 842 self.sessActGrp.findChild(
812 self.sessActGrp.findChild(QAction, "project_delete_session").setEnabled(enable) 843 QAction, "project_load_session").setEnabled(enable)
844 self.sessActGrp.findChild(
845 QAction, "project_delete_session").setEnabled(enable)
813 846
814 def __readSession(self, quiet=False, indicator=""): 847 def __readSession(self, quiet=False, indicator=""):
815 """ 848 """
816 Private method to read in the project session file (.e4s). 849 Private method to read in the project session file (.e4s).
817 850
838 f.close() 871 f.close()
839 else: 872 else:
840 if not quiet: 873 if not quiet:
841 E5MessageBox.critical(self.ui, 874 E5MessageBox.critical(self.ui,
842 self.trUtf8("Read project session"), 875 self.trUtf8("Read project session"),
843 self.trUtf8("<p>The project session file <b>{0}</b> could not be" 876 self.trUtf8(
877 "<p>The project session file <b>{0}</b> could not be"
844 " read.</p>").format(fn)) 878 " read.</p>").format(fn))
845 879
846 def __writeSession(self, quiet=False, indicator=""): 880 def __writeSession(self, quiet=False, indicator=""):
847 """ 881 """
848 Private method to write the session data to an XML file (.e4s). 882 Private method to write the session data to an XML file (.e4s).
863 '{0}{1}.e4s'.format(fn, indicator)) 897 '{0}{1}.e4s'.format(fn, indicator))
864 898
865 f = QFile(fn) 899 f = QFile(fn)
866 if f.open(QIODevice.WriteOnly): 900 if f.open(QIODevice.WriteOnly):
867 from E5XML.SessionWriter import SessionWriter 901 from E5XML.SessionWriter import SessionWriter
868 SessionWriter(f, os.path.splitext(os.path.basename(fn))[0]).writeXML() 902 SessionWriter(
903 f, os.path.splitext(os.path.basename(fn))[0]).writeXML()
869 f.close() 904 f.close()
870 else: 905 else:
871 if not quiet: 906 if not quiet:
872 E5MessageBox.critical(self.ui, 907 E5MessageBox.critical(self.ui,
873 self.trUtf8("Save project session"), 908 self.trUtf8("Save project session"),
874 self.trUtf8("<p>The project session file <b>{0}</b> could not be" 909 self.trUtf8(
910 "<p>The project session file <b>{0}</b> could not be"
875 " written.</p>").format(fn)) 911 " written.</p>").format(fn))
876 912
877 def __deleteSession(self): 913 def __deleteSession(self):
878 """ 914 """
879 Private method to delete the session file. 915 Private method to delete the session file.
884 self.trUtf8("Please save the project first.")) 920 self.trUtf8("Please save the project first."))
885 return 921 return
886 922
887 fname, ext = os.path.splitext(os.path.basename(self.pfile)) 923 fname, ext = os.path.splitext(os.path.basename(self.pfile))
888 924
889 for fn in [os.path.join(self.getProjectManagementDir(), "{0}.e4s".format(fname))]: 925 for fn in [os.path.join(
926 self.getProjectManagementDir(), "{0}.e4s".format(fname))]:
890 if os.path.exists(fn): 927 if os.path.exists(fn):
891 try: 928 try:
892 os.remove(fn) 929 os.remove(fn)
893 except OSError: 930 except OSError:
894 E5MessageBox.critical(self.ui, 931 E5MessageBox.critical(self.ui,
895 self.trUtf8("Delete project session"), 932 self.trUtf8("Delete project session"),
896 self.trUtf8("<p>The project session file <b>{0}</b> could not be" 933 self.trUtf8(
897 " deleted.</p>").format(fn)) 934 "<p>The project session file <b>{0}</b> could"
935 " not be deleted.</p>").format(fn))
898 936
899 def __readTasks(self): 937 def __readTasks(self):
900 """ 938 """
901 Private method to read in the project tasks file (.e4t). 939 Private method to read in the project tasks file (.e4t).
902 """ 940 """
917 reader.readXML() 955 reader.readXML()
918 f.close() 956 f.close()
919 else: 957 else:
920 E5MessageBox.critical(self.ui, 958 E5MessageBox.critical(self.ui,
921 self.trUtf8("Read tasks"), 959 self.trUtf8("Read tasks"),
922 self.trUtf8("<p>The tasks file <b>{0}</b> could not be read.</p>")\ 960 self.trUtf8(
961 "<p>The tasks file <b>{0}</b> could not be read.</p>")\
923 .format(fn)) 962 .format(fn))
924 963
925 def writeTasks(self): 964 def writeTasks(self):
926 """ 965 """
927 Public method to write the tasks data to an XML file (.e4t). 966 Public method to write the tasks data to an XML file (.e4t).
935 f = QFile(fn) 974 f = QFile(fn)
936 ok = f.open(QIODevice.WriteOnly) 975 ok = f.open(QIODevice.WriteOnly)
937 if not ok: 976 if not ok:
938 E5MessageBox.critical(self.ui, 977 E5MessageBox.critical(self.ui,
939 self.trUtf8("Save tasks"), 978 self.trUtf8("Save tasks"),
940 self.trUtf8("<p>The tasks file <b>{0}</b> could not be written.</p>") 979 self.trUtf8(
980 "<p>The tasks file <b>{0}</b> could not be written.</p>")
941 .format(fn)) 981 .format(fn))
942 return 982 return
943 983
944 from E5XML.TasksWriter import TasksWriter 984 from E5XML.TasksWriter import TasksWriter
945 TasksWriter(f, True, os.path.splitext(os.path.basename(fn))[0]).writeXML() 985 TasksWriter(
986 f, True, os.path.splitext(os.path.basename(fn))[0]).writeXML()
946 f.close() 987 f.close()
947 988
948 def __showContextMenuDebugger(self): 989 def __showContextMenuDebugger(self):
949 """ 990 """
950 Private slot called before the Debugger menu is shown. 991 Private slot called before the Debugger menu is shown.
955 else: 996 else:
956 fn, ext = os.path.splitext(os.path.basename(self.pfile)) 997 fn, ext = os.path.splitext(os.path.basename(self.pfile))
957 fn = os.path.join(self.getProjectManagementDir(), 998 fn = os.path.join(self.getProjectManagementDir(),
958 '{0}.e4d'.format(fn)) 999 '{0}.e4d'.format(fn))
959 enable = os.path.exists(fn) 1000 enable = os.path.exists(fn)
960 self.dbgActGrp.findChild(QAction, "project_debugger_properties_load")\ 1001 self.dbgActGrp.findChild(
961 .setEnabled(enable) 1002 QAction, "project_debugger_properties_load").setEnabled(enable)
962 self.dbgActGrp.findChild(QAction, "project_debugger_properties_delete")\ 1003 self.dbgActGrp.findChild(
963 .setEnabled(enable) 1004 QAction, "project_debugger_properties_delete").setEnabled(enable)
964 1005
965 def __readDebugProperties(self, quiet=False): 1006 def __readDebugProperties(self, quiet=False):
966 """ 1007 """
967 Private method to read in the project debugger properties file (.e4d). 1008 Private method to read in the project debugger properties file (.e4d).
968 1009
987 f.close() 1028 f.close()
988 else: 1029 else:
989 if not quiet: 1030 if not quiet:
990 E5MessageBox.critical(self.ui, 1031 E5MessageBox.critical(self.ui,
991 self.trUtf8("Read debugger properties"), 1032 self.trUtf8("Read debugger properties"),
992 self.trUtf8("<p>The project debugger properties file <b>{0}</b> could" 1033 self.trUtf8(
993 " not be read.</p>").format(fn)) 1034 "<p>The project debugger properties file <b>{0}</b>"
1035 " could not be read.</p>").format(fn))
994 1036
995 def __writeDebugProperties(self, quiet=False): 1037 def __writeDebugProperties(self, quiet=False):
996 """ 1038 """
997 Private method to write the project debugger properties file (.e4d). 1039 Private method to write the project debugger properties file (.e4d).
998 1040
1010 fn = os.path.join(self.getProjectManagementDir(), '{0}.e4d'.format(fn)) 1052 fn = os.path.join(self.getProjectManagementDir(), '{0}.e4d'.format(fn))
1011 1053
1012 f = QFile(fn) 1054 f = QFile(fn)
1013 if f.open(QIODevice.WriteOnly): 1055 if f.open(QIODevice.WriteOnly):
1014 from E5XML.DebuggerPropertiesWriter import DebuggerPropertiesWriter 1056 from E5XML.DebuggerPropertiesWriter import DebuggerPropertiesWriter
1015 DebuggerPropertiesWriter(f, os.path.splitext(os.path.basename(fn))[0])\ 1057 DebuggerPropertiesWriter(
1016 .writeXML() 1058 f, os.path.splitext(os.path.basename(fn))[0]).writeXML()
1017 f.close() 1059 f.close()
1018 else: 1060 else:
1019 if not quiet: 1061 if not quiet:
1020 E5MessageBox.critical(self.ui, 1062 E5MessageBox.critical(self.ui,
1021 self.trUtf8("Save debugger properties"), 1063 self.trUtf8("Save debugger properties"),
1022 self.trUtf8("<p>The project debugger properties file <b>{0}</b> could" 1064 self.trUtf8(
1023 " not be written.</p>") 1065 "<p>The project debugger properties file <b>{0}</b>"
1024 .format(fn)) 1066 " could not be written.</p>").format(fn))
1025 1067
1026 def __deleteDebugProperties(self): 1068 def __deleteDebugProperties(self):
1027 """ 1069 """
1028 Private method to delete the project debugger properties file (.e4d). 1070 Private method to delete the project debugger properties file (.e4d).
1029 """ 1071 """
1033 self.trUtf8("Please save the project first.")) 1075 self.trUtf8("Please save the project first."))
1034 return 1076 return
1035 1077
1036 fname, ext = os.path.splitext(os.path.basename(self.pfile)) 1078 fname, ext = os.path.splitext(os.path.basename(self.pfile))
1037 1079
1038 for fn in [os.path.join(self.getProjectManagementDir(), "{0}.e4d".format(fname))]: 1080 for fn in [os.path.join(self.getProjectManagementDir(),
1081 "{0}.e4d".format(fname))]:
1039 if os.path.exists(fn): 1082 if os.path.exists(fn):
1040 try: 1083 try:
1041 os.remove(fn) 1084 os.remove(fn)
1042 except OSError: 1085 except OSError:
1043 E5MessageBox.critical(self.ui, 1086 E5MessageBox.critical(self.ui,
1044 self.trUtf8("Delete debugger properties"), 1087 self.trUtf8("Delete debugger properties"),
1045 self.trUtf8("<p>The project debugger properties file <b>{0}</b>" 1088 self.trUtf8(
1046 " could not be deleted.</p>") 1089 "<p>The project debugger properties file"
1090 " <b>{0}</b> could not be deleted.</p>")
1047 .format(fn)) 1091 .format(fn))
1048 1092
1049 def __initDebugProperties(self): 1093 def __initDebugProperties(self):
1050 """ 1094 """
1051 Private method to initialize the debug properties. 1095 Private method to initialize the debug properties.
1100 @param excIgnoreList list of exceptions to be ignored (list of strings) 1144 @param excIgnoreList list of exceptions to be ignored (list of strings)
1101 @param autoClearShell flag indicating, that the interpreter window 1145 @param autoClearShell flag indicating, that the interpreter window
1102 should be cleared (boolean) 1146 should be cleared (boolean)
1103 @keyparam tracePython flag to indicate if the Python library should be 1147 @keyparam tracePython flag to indicate if the Python library should be
1104 traced as well (boolean) 1148 traced as well (boolean)
1105 @keyparam autoContinue flag indicating, that the debugger should not stop 1149 @keyparam autoContinue flag indicating, that the debugger should not
1106 at the first executable line (boolean) 1150 stop at the first executable line (boolean)
1107 """ 1151 """
1108 self.dbgCmdline = argv 1152 self.dbgCmdline = argv
1109 self.dbgWd = wd 1153 self.dbgWd = wd
1110 self.dbgEnv = env 1154 self.dbgEnv = env
1111 self.dbgReportExceptions = excReporting 1155 self.dbgReportExceptions = excReporting
1134 """ 1178 """
1135 if len(self.pdata["TRANSLATIONPATTERN"]) == 0 or \ 1179 if len(self.pdata["TRANSLATIONPATTERN"]) == 0 or \
1136 self.pdata["TRANSLATIONPATTERN"][0] == '': 1180 self.pdata["TRANSLATIONPATTERN"][0] == '':
1137 E5MessageBox.critical(self.ui, 1181 E5MessageBox.critical(self.ui,
1138 self.trUtf8("Add Language"), 1182 self.trUtf8("Add Language"),
1139 self.trUtf8("You have to specify a translation pattern first.")) 1183 self.trUtf8(
1184 "You have to specify a translation pattern first."))
1140 return 1185 return
1141 1186
1142 from .AddLanguageDialog import AddLanguageDialog 1187 from .AddLanguageDialog import AddLanguageDialog
1143 dlg = AddLanguageDialog(self.parent()) 1188 dlg = AddLanguageDialog(self.parent())
1144 if dlg.exec_() == QDialog.Accepted: 1189 if dlg.exec_() == QDialog.Accepted:
1145 lang = dlg.getSelectedLanguage() 1190 lang = dlg.getSelectedLanguage()
1146 if self.pdata["PROJECTTYPE"][0] in \ 1191 if self.pdata["PROJECTTYPE"][0] in \
1147 ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E4Plugin", "PySide", "PySideC"]: 1192 ["Qt4", "Qt4C", "PyQt5", "PyQt5C", "E4Plugin", "PySide",
1148 langFile = self.pdata["TRANSLATIONPATTERN"][0].replace("%language%", lang) 1193 "PySideC"]:
1194 langFile = self.pdata["TRANSLATIONPATTERN"][0]\
1195 .replace("%language%", lang)
1149 self.appendFile(langFile) 1196 self.appendFile(langFile)
1150 self.projectLanguageAddedByCode.emit(lang) 1197 self.projectLanguageAddedByCode.emit(lang)
1151 1198
1152 def __binaryTranslationFile(self, langFile): 1199 def __binaryTranslationFile(self, langFile):
1153 """ 1200 """
1154 Private method to calculate the filename of the binary translations file 1201 Private method to calculate the filename of the binary translations
1155 given the name of the raw translations file. 1202 file given the name of the raw translations file.
1156 1203
1157 @param langFile name of the raw translations file (string) 1204 @param langFile name of the raw translations file (string)
1158 @return name of the binary translations file (string) 1205 @return name of the binary translations file (string)
1159 """ 1206 """
1160 qmFile = "" 1207 qmFile = ""
1161 try: 1208 try:
1162 if self.__binaryTranslationsCallbacks[self.pdata["PROJECTTYPE"][0]] \ 1209 if self.__binaryTranslationsCallbacks[
1163 is not None: 1210 self.pdata["PROJECTTYPE"][0]] is not None:
1164 qmFile = self.__binaryTranslationsCallbacks[ 1211 qmFile = self.__binaryTranslationsCallbacks[
1165 self.pdata["PROJECTTYPE"][0]](langFile) 1212 self.pdata["PROJECTTYPE"][0]](langFile)
1166 except KeyError: 1213 except KeyError:
1167 qmFile = langFile.replace('.ts', '.qm') 1214 qmFile = langFile.replace('.ts', '.qm')
1168 if qmFile == langFile: 1215 if qmFile == langFile:
1225 if os.path.exists(fn): 1272 if os.path.exists(fn):
1226 os.remove(fn) 1273 os.remove(fn)
1227 except IOError: 1274 except IOError:
1228 E5MessageBox.critical(self.ui, 1275 E5MessageBox.critical(self.ui,
1229 self.trUtf8("Delete translation"), 1276 self.trUtf8("Delete translation"),
1230 self.trUtf8("<p>The selected translation file <b>{0}</b> could not be" 1277 self.trUtf8(
1278 "<p>The selected translation file <b>{0}</b> could not be"
1231 " deleted.</p>").format(langFile)) 1279 " deleted.</p>").format(langFile))
1232 return 1280 return
1233 1281
1234 self.removeLanguageFile(langFile) 1282 self.removeLanguageFile(langFile)
1235 1283
1244 if os.path.exists(fn): 1292 if os.path.exists(fn):
1245 os.remove(fn) 1293 os.remove(fn)
1246 except IOError: 1294 except IOError:
1247 E5MessageBox.critical(self.ui, 1295 E5MessageBox.critical(self.ui,
1248 self.trUtf8("Delete translation"), 1296 self.trUtf8("Delete translation"),
1249 self.trUtf8("<p>The selected translation file <b>{0}</b> could not be" 1297 self.trUtf8(
1250 " deleted.</p>").format(qmFile)) 1298 "<p>The selected translation file <b>{0}</b> could"
1299 " not be deleted.</p>").format(qmFile))
1251 return 1300 return
1252 1301
1253 def appendFile(self, fn, isSourceFile=False, updateModel=True): 1302 def appendFile(self, fn, isSourceFile=False, updateModel=True):
1254 """ 1303 """
1255 Public method to append a file to the project. 1304 Public method to append a file to the project.
1256 1305
1257 @param fn filename to be added to the project (string) 1306 @param fn filename to be added to the project (string)
1258 @param isSourceFile flag indicating that this is a source file 1307 @param isSourceFile flag indicating that this is a source file
1259 even if it doesn't have the source extension (boolean) 1308 even if it doesn't have the source extension (boolean)
1260 @param updateModel flag indicating an update of the model is requested (boolean) 1309 @param updateModel flag indicating an update of the model is
1310 requested (boolean)
1261 """ 1311 """
1262 dirty = False 1312 dirty = False
1263 1313
1264 if os.path.isabs(fn): 1314 if os.path.isabs(fn):
1265 # make it relative to the project root, if it starts with that path 1315 # make it relative to the project root, if it starts with that path
1275 filetype = "OTHERS" 1325 filetype = "OTHERS"
1276 bfn = os.path.basename(newfn) 1326 bfn = os.path.basename(newfn)
1277 if fnmatch.fnmatch(bfn, '*.ts') or fnmatch.fnmatch(bfn, '*.qm'): 1327 if fnmatch.fnmatch(bfn, '*.ts') or fnmatch.fnmatch(bfn, '*.qm'):
1278 filetype = "TRANSLATIONS" 1328 filetype = "TRANSLATIONS"
1279 else: 1329 else:
1280 for pattern in reversed(sorted(self.pdata["FILETYPES"].keys())): 1330 for pattern in reversed(
1331 sorted(self.pdata["FILETYPES"].keys())):
1281 if fnmatch.fnmatch(bfn, pattern): 1332 if fnmatch.fnmatch(bfn, pattern):
1282 filetype = self.pdata["FILETYPES"][pattern] 1333 filetype = self.pdata["FILETYPES"][pattern]
1283 break 1334 break
1284 1335
1285 if filetype == "__IGNORE__": 1336 if filetype == "__IGNORE__":
1304 updateModel and self.repopulateItem(newfn) 1355 updateModel and self.repopulateItem(newfn)
1305 elif filetype == "INTERFACES": 1356 elif filetype == "INTERFACES":
1306 if newfn not in self.pdata["INTERFACES"]: 1357 if newfn not in self.pdata["INTERFACES"]:
1307 self.pdata["INTERFACES"].append(newfn) 1358 self.pdata["INTERFACES"].append(newfn)
1308 self.projectInterfaceAdded.emit(newfn) 1359 self.projectInterfaceAdded.emit(newfn)
1309 updateModel and self.__model.addNewItem("INTERFACES", newfn) 1360 updateModel and \
1361 self.__model.addNewItem("INTERFACES", newfn)
1310 dirty = True 1362 dirty = True
1311 else: 1363 else:
1312 updateModel and self.repopulateItem(newfn) 1364 updateModel and self.repopulateItem(newfn)
1313 elif filetype == "RESOURCES": 1365 elif filetype == "RESOURCES":
1314 if newfn not in self.pdata["RESOURCES"]: 1366 if newfn not in self.pdata["RESOURCES"]:
1364 os.makedirs(target) 1416 os.makedirs(target)
1365 1417
1366 if os.path.exists(targetfile): 1418 if os.path.exists(targetfile):
1367 res = E5MessageBox.yesNo(self.ui, 1419 res = E5MessageBox.yesNo(self.ui,
1368 self.trUtf8("Add file"), 1420 self.trUtf8("Add file"),
1369 self.trUtf8("<p>The file <b>{0}</b> already" 1421 self.trUtf8(
1422 "<p>The file <b>{0}</b> already"
1370 " exists.</p><p>Overwrite it?</p>") 1423 " exists.</p><p>Overwrite it?</p>")
1371 .format(targetfile), 1424 .format(targetfile),
1372 icon=E5MessageBox.Warning) 1425 icon=E5MessageBox.Warning)
1373 if not res: 1426 if not res:
1374 return # don't overwrite 1427 return # don't overwrite
1375 1428
1376 shutil.copy(fn, target) 1429 shutil.copy(fn, target)
1377 except IOError as why: 1430 except IOError as why:
1378 E5MessageBox.critical(self.ui, 1431 E5MessageBox.critical(self.ui,
1379 self.trUtf8("Add file"), 1432 self.trUtf8("Add file"),
1380 self.trUtf8("<p>The selected file <b>{0}</b> could not be" 1433 self.trUtf8(
1381 " added to <b>{1}</b>.</p><p>Reason: {2}</p>") 1434 "<p>The selected file <b>{0}</b> could"
1435 " not be added to <b>{1}</b>.</p>"
1436 "<p>Reason: {2}</p>")
1382 .format(fn, target, str(why))) 1437 .format(fn, target, str(why)))
1383 continue 1438 continue
1384 1439
1385 self.appendFile(targetfile, isSource or filter == 'source') 1440 self.appendFile(targetfile, isSource or filter == 'source')
1386 else: 1441 else:
1388 self.trUtf8("Add file"), 1443 self.trUtf8("Add file"),
1389 self.trUtf8("The target directory must not be empty.")) 1444 self.trUtf8("The target directory must not be empty."))
1390 1445
1391 def __addSingleDirectory(self, filetype, source, target, quiet=False): 1446 def __addSingleDirectory(self, filetype, source, target, quiet=False):
1392 """ 1447 """
1393 Private method used to add all files of a single directory to the project. 1448 Private method used to add all files of a single directory to the
1449 project.
1394 1450
1395 @param filetype type of files to add (string) 1451 @param filetype type of files to add (string)
1396 @param source source directory (string) 1452 @param source source directory (string)
1397 @param target target directory (string) 1453 @param target target directory (string)
1398 @param quiet flag indicating quiet operations (boolean) 1454 @param quiet flag indicating quiet operations (boolean)
1417 self.trUtf8("Add directory"), 1473 self.trUtf8("Add directory"),
1418 self.trUtf8("<p>The source directory doesn't contain" 1474 self.trUtf8("<p>The source directory doesn't contain"
1419 " any files belonging to the selected category.</p>")) 1475 " any files belonging to the selected category.</p>"))
1420 return 1476 return
1421 1477
1422 if not Utilities.samepath(target, source) and not os.path.isdir(target): 1478 if not Utilities.samepath(target, source) and \
1479 not os.path.isdir(target):
1423 try: 1480 try:
1424 os.makedirs(target) 1481 os.makedirs(target)
1425 except IOError as why: 1482 except IOError as why:
1426 E5MessageBox.critical(self.ui, 1483 E5MessageBox.critical(self.ui,
1427 self.trUtf8("Add directory"), 1484 self.trUtf8("Add directory"),
1428 self.trUtf8("<p>The target directory <b>{0}</b> could not be" 1485 self.trUtf8(
1486 "<p>The target directory <b>{0}</b> could not be"
1429 " created.</p><p>Reason: {1}</p>") 1487 " created.</p><p>Reason: {1}</p>")
1430 .format(target, str(why))) 1488 .format(target, str(why)))
1431 return 1489 return
1432 1490
1433 for file in files: 1491 for file in files:
1439 if not Utilities.samepath(target, source): 1497 if not Utilities.samepath(target, source):
1440 try: 1498 try:
1441 if os.path.exists(targetfile): 1499 if os.path.exists(targetfile):
1442 res = E5MessageBox.yesNo(self.ui, 1500 res = E5MessageBox.yesNo(self.ui,
1443 self.trUtf8("Add directory"), 1501 self.trUtf8("Add directory"),
1444 self.trUtf8("<p>The file <b>{0}</b> already exists.</p>" 1502 self.trUtf8(
1445 "<p>Overwrite it?</p>") 1503 "<p>The file <b>{0}</b> already exists.</p>"
1504 "<p>Overwrite it?</p>")
1446 .format(targetfile), 1505 .format(targetfile),
1447 icon=E5MessageBox.Warning) 1506 icon=E5MessageBox.Warning)
1448 if not res: 1507 if not res:
1449 continue # don't overwrite, carry on with next file 1508 continue # don't overwrite, carry on
1509 # with next file
1450 1510
1451 shutil.copy(file, target) 1511 shutil.copy(file, target)
1452 except EnvironmentError: 1512 except EnvironmentError:
1453 continue 1513 continue
1454 self.appendFile(targetfile) 1514 self.appendFile(targetfile)
1483 @param startdir start directory for the selection dialog (string) 1543 @param startdir start directory for the selection dialog (string)
1484 """ 1544 """
1485 if startdir is None: 1545 if startdir is None:
1486 startdir = self.ppath 1546 startdir = self.ppath
1487 from .AddDirectoryDialog import AddDirectoryDialog 1547 from .AddDirectoryDialog import AddDirectoryDialog
1488 dlg = AddDirectoryDialog(self, filter, self.parent(), startdir=startdir) 1548 dlg = AddDirectoryDialog(
1549 self, filter, self.parent(), startdir=startdir)
1489 if dlg.exec_() == QDialog.Accepted: 1550 if dlg.exec_() == QDialog.Accepted:
1490 filetype, source, target, recursive = dlg.getData() 1551 filetype, source, target, recursive = dlg.getData()
1491 if target == '': 1552 if target == '':
1492 E5MessageBox.critical(self.ui, 1553 E5MessageBox.critical(self.ui,
1493 self.trUtf8("Add directory"), 1554 self.trUtf8("Add directory"),
1561 """ 1622 """
1562 self.addFiles('others') 1623 self.addFiles('others')
1563 1624
1564 def addSourceDir(self): 1625 def addSourceDir(self):
1565 """ 1626 """
1566 Public slot to add all source files of a directory to the current project. 1627 Public slot to add all source files of a directory to the current
1628 project.
1567 """ 1629 """
1568 self.addDirectory('source') 1630 self.addDirectory('source')
1569 1631
1570 def addUiDir(self): 1632 def addUiDir(self):
1571 """ 1633 """
1573 """ 1635 """
1574 self.addDirectory('form') 1636 self.addDirectory('form')
1575 1637
1576 def addIdlDir(self): 1638 def addIdlDir(self):
1577 """ 1639 """
1578 Public slot to add all IDL interfaces of a directory to the current project. 1640 Public slot to add all IDL interfaces of a directory to the current
1641 project.
1579 """ 1642 """
1580 self.addDirectory('interface') 1643 self.addDirectory('interface')
1581 1644
1582 def addResourceDir(self): 1645 def addResourceDir(self):
1583 """ 1646 """
1584 Public slot to add all Qt resource files of a directory to the current project. 1647 Public slot to add all Qt resource files of a directory to the current
1648 project.
1585 """ 1649 """
1586 self.addDirectory('resource') 1650 self.addDirectory('resource')
1587 1651
1588 def addOthersDir(self): 1652 def addOthersDir(self):
1589 """ 1653 """
1642 try: 1706 try:
1643 os.rename(oldfn, newfn) 1707 os.rename(oldfn, newfn)
1644 except OSError as msg: 1708 except OSError as msg:
1645 E5MessageBox.critical(self.ui, 1709 E5MessageBox.critical(self.ui,
1646 self.trUtf8("Rename File"), 1710 self.trUtf8("Rename File"),
1647 self.trUtf8("""<p>The file <b>{0}</b> could not be renamed.<br />""" 1711 self.trUtf8(
1712 """<p>The file <b>{0}</b> could not be renamed.<br />"""
1648 """Reason: {1}</p>""").format(oldfn, str(msg))) 1713 """Reason: {1}</p>""").format(oldfn, str(msg)))
1649 return False 1714 return False
1650 1715
1651 if fn in self.pdata["SOURCES"] or \ 1716 if fn in self.pdata["SOURCES"] or \
1652 fn in self.pdata["FORMS"] or \ 1717 fn in self.pdata["FORMS"] or \
1705 newdn = self.getRelativePath(newdn) 1770 newdn = self.getRelativePath(newdn)
1706 for key in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES", "OTHERS"]: 1771 for key in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES", "OTHERS"]:
1707 for entry in self.pdata[key][:]: 1772 for entry in self.pdata[key][:]:
1708 if entry.startswith(olddn): 1773 if entry.startswith(olddn):
1709 entry = entry.replace(olddn, newdn) 1774 entry = entry.replace(olddn, newdn)
1710 self.appendFile(os.path.join(self.ppath, entry), key == "SOURCES") 1775 self.appendFile(os.path.join(self.ppath, entry),
1776 key == "SOURCES")
1711 self.setDirty(True) 1777 self.setDirty(True)
1712 1778
1713 def moveDirectory(self, olddn, newdn): 1779 def moveDirectory(self, olddn, newdn):
1714 """ 1780 """
1715 Public slot to move a directory. 1781 Public slot to move a directory.
1746 Public slot to remove a file from the project. 1812 Public slot to remove a file from the project.
1747 1813
1748 The file is not deleted from the project directory. 1814 The file is not deleted from the project directory.
1749 1815
1750 @param fn filename to be removed from the project 1816 @param fn filename to be removed from the project
1751 @param updateModel flag indicating an update of the model is requested (boolean) 1817 @param updateModel flag indicating an update of the model is
1818 requested (boolean)
1752 """ 1819 """
1753 fn = self.getRelativePath(fn) 1820 fn = self.getRelativePath(fn)
1754 dirty = True 1821 dirty = True
1755 if fn in self.pdata["SOURCES"]: 1822 if fn in self.pdata["SOURCES"]:
1756 self.pdata["SOURCES"].remove(fn) 1823 self.pdata["SOURCES"].remove(fn)
1792 dirty = True 1859 dirty = True
1793 if not dn.endswith(os.sep): 1860 if not dn.endswith(os.sep):
1794 dn2 = dn + os.sep 1861 dn2 = dn + os.sep
1795 else: 1862 else:
1796 dn2 = dn 1863 dn2 = dn
1797 for key in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES", "TRANSLATIONS", ]: 1864 for key in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES",
1865 "TRANSLATIONS", ]:
1798 for entry in self.pdata[key][:]: 1866 for entry in self.pdata[key][:]:
1799 if entry.startswith(dn2): 1867 if entry.startswith(dn2):
1800 self.pdata[key].remove(entry) 1868 self.pdata[key].remove(entry)
1801 dirty = True 1869 dirty = True
1802 self.__model.removeItem(dn) 1870 self.__model.removeItem(dn)
1822 for ext in ['.pyc', '.pyo']: 1890 for ext in ['.pyc', '.pyo']:
1823 fn2 = os.path.join(self.ppath, path + ext) 1891 fn2 = os.path.join(self.ppath, path + ext)
1824 if os.path.isfile(fn2): 1892 if os.path.isfile(fn2):
1825 os.remove(fn2) 1893 os.remove(fn2)
1826 pat = os.path.join( 1894 pat = os.path.join(
1827 self.ppath, head, "__pycache__", "{0}.*{1}".format(tail, ext)) 1895 self.ppath, head,
1896 "__pycache__", "{0}.*{1}".format(tail, ext))
1828 for f in glob.glob(pat): 1897 for f in glob.glob(pat):
1829 os.remove(f) 1898 os.remove(f)
1830 except EnvironmentError: 1899 except EnvironmentError:
1831 E5MessageBox.critical(self.ui, 1900 E5MessageBox.critical(self.ui,
1832 self.trUtf8("Delete file"), 1901 self.trUtf8("Delete file"),
1833 self.trUtf8("<p>The selected file <b>{0}</b> could not be deleted.</p>") 1902 self.trUtf8(
1834 .format(fn)) 1903 "<p>The selected file <b>{0}</b> could not be"
1904 " deleted.</p>").format(fn))
1835 return False 1905 return False
1836 1906
1837 self.removeFile(fn) 1907 self.removeFile(fn)
1838 if ext == '.ui': 1908 if ext == '.ui':
1839 self.removeFile(fn + '.h') 1909 self.removeFile(fn + '.h')
1911 self.menuCheckAct.setEnabled(True) 1981 self.menuCheckAct.setEnabled(True)
1912 self.menuShowAct.setEnabled(True) 1982 self.menuShowAct.setEnabled(True)
1913 self.menuDiagramAct.setEnabled(True) 1983 self.menuDiagramAct.setEnabled(True)
1914 self.menuApidocAct.setEnabled(True) 1984 self.menuApidocAct.setEnabled(True)
1915 self.menuPackagersAct.setEnabled(True) 1985 self.menuPackagersAct.setEnabled(True)
1916 self.pluginGrp.setEnabled(self.pdata["PROJECTTYPE"][0] == "E4Plugin") 1986 self.pluginGrp.setEnabled(
1987 self.pdata["PROJECTTYPE"][0] == "E4Plugin")
1917 self.addLanguageAct.setEnabled( 1988 self.addLanguageAct.setEnabled(
1918 len(self.pdata["TRANSLATIONPATTERN"]) > 0 and \ 1989 len(self.pdata["TRANSLATIONPATTERN"]) > 0 and \
1919 self.pdata["TRANSLATIONPATTERN"][0] != '') 1990 self.pdata["TRANSLATIONPATTERN"][0] != '')
1920 1991
1921 self.projectAboutToBeCreated.emit() 1992 self.projectAboutToBeCreated.emit()
1922 1993
1923 hash = str(QCryptographicHash.hash( 1994 hash = str(QCryptographicHash.hash(
1924 QByteArray(self.ppath.encode("utf-8")), QCryptographicHash.Sha1).toHex(), 1995 QByteArray(self.ppath.encode("utf-8")),
1996 QCryptographicHash.Sha1).toHex(),
1925 encoding="utf-8") 1997 encoding="utf-8")
1926 self.pdata["HASH"] = [hash] 1998 self.pdata["HASH"] = [hash]
1927 1999
1928 # create the project directory if it doesn't exist already 2000 # create the project directory if it doesn't exist already
1929 if not os.path.isdir(self.ppath): 2001 if not os.path.isdir(self.ppath):
1930 try: 2002 try:
1931 os.makedirs(self.ppath) 2003 os.makedirs(self.ppath)
1932 except EnvironmentError: 2004 except EnvironmentError:
1933 E5MessageBox.critical(self.ui, 2005 E5MessageBox.critical(self.ui,
1934 self.trUtf8("Create project directory"), 2006 self.trUtf8("Create project directory"),
1935 self.trUtf8("<p>The project directory <b>{0}</b> could not" 2007 self.trUtf8(
2008 "<p>The project directory <b>{0}</b> could not"
1936 " be created.</p>") 2009 " be created.</p>")
1937 .format(self.ppath)) 2010 .format(self.ppath))
1938 self.vcs = self.initVCS() 2011 self.vcs = self.initVCS()
1939 return 2012 return
1940 # create an empty __init__.py file to make it a Python package 2013 # create an empty __init__.py file to make it a Python package
1941 # (only for Python and Python3) 2014 # (only for Python and Python3)
1942 if self.pdata["PROGLANGUAGE"][0] in ["Python", "Python2", "Python3"]: 2015 if self.pdata["PROGLANGUAGE"][0] in \
2016 ["Python", "Python2", "Python3"]:
1943 fn = os.path.join(self.ppath, "__init__.py") 2017 fn = os.path.join(self.ppath, "__init__.py")
1944 f = open(fn, "w", encoding="utf-8") 2018 f = open(fn, "w", encoding="utf-8")
1945 f.close() 2019 f.close()
1946 self.appendFile(fn, True) 2020 self.appendFile(fn, True)
1947 # create an empty main script file, if a name was given 2021 # create an empty main script file, if a name was given
1948 if len(self.pdata["MAINSCRIPT"]) and self.pdata["MAINSCRIPT"][0]: 2022 if len(self.pdata["MAINSCRIPT"]) and \
2023 self.pdata["MAINSCRIPT"][0]:
1949 if not os.path.isabs(self.pdata["MAINSCRIPT"][0]): 2024 if not os.path.isabs(self.pdata["MAINSCRIPT"][0]):
1950 ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0]) 2025 ms = os.path.join(
2026 self.ppath, self.pdata["MAINSCRIPT"][0])
1951 else: 2027 else:
1952 ms = self.pdata["MAINSCRIPT"][0] 2028 ms = self.pdata["MAINSCRIPT"][0]
1953 f = open(ms, "w") 2029 f = open(ms, "w")
1954 f.close() 2030 f.close()
1955 self.appendFile(ms, True) 2031 self.appendFile(ms, True)
1957 if not self.translationsRoot.endswith(os.sep): 2033 if not self.translationsRoot.endswith(os.sep):
1958 tpd = os.path.dirname(tpd) 2034 tpd = os.path.dirname(tpd)
1959 if not os.path.isdir(tpd): 2035 if not os.path.isdir(tpd):
1960 os.makedirs(tpd) 2036 os.makedirs(tpd)
1961 if self.pdata["TRANSLATIONSBINPATH"]: 2037 if self.pdata["TRANSLATIONSBINPATH"]:
1962 tpd = os.path.join(self.ppath, self.pdata["TRANSLATIONSBINPATH"][0]) 2038 tpd = os.path.join(
2039 self.ppath, self.pdata["TRANSLATIONSBINPATH"][0])
1963 if not os.path.isdir(tpd): 2040 if not os.path.isdir(tpd):
1964 os.makedirs(tpd) 2041 os.makedirs(tpd)
1965 2042
1966 # create management directory if not present 2043 # create management directory if not present
1967 mgmtDir = self.getProjectManagementDir() 2044 mgmtDir = self.getProjectManagementDir()
1982 f = open(ms, "w") 2059 f = open(ms, "w")
1983 f.close() 2060 f.close()
1984 except IOError as err: 2061 except IOError as err:
1985 E5MessageBox.critical(self.ui, 2062 E5MessageBox.critical(self.ui,
1986 self.trUtf8("Create main script"), 2063 self.trUtf8("Create main script"),
1987 self.trUtf8("<p>The mainscript <b>{0}</b> could not" 2064 self.trUtf8(
2065 "<p>The mainscript <b>{0}</b> could not"
1988 " be created.<br/>Reason: {1}</p>") 2066 " be created.<br/>Reason: {1}</p>")
1989 .format(self.ppath, str(err))) 2067 .format(self.ppath, str(err)))
1990 self.appendFile(ms) 2068 self.appendFile(ms)
1991 except IndexError: 2069 except IndexError:
1992 ms = "" 2070 ms = ""
1998 yesDefault=True) 2076 yesDefault=True)
1999 if res: 2077 if res:
2000 self.newProjectAddFiles(ms) 2078 self.newProjectAddFiles(ms)
2001 # create an empty __init__.py file to make it a Python package 2079 # create an empty __init__.py file to make it a Python package
2002 # if none exists (only for Python and Python3) 2080 # if none exists (only for Python and Python3)
2003 if self.pdata["PROGLANGUAGE"][0] in ["Python", "Python2", "Python3"]: 2081 if self.pdata["PROGLANGUAGE"][0] in \
2082 ["Python", "Python2", "Python3"]:
2004 fn = os.path.join(self.ppath, "__init__.py") 2083 fn = os.path.join(self.ppath, "__init__.py")
2005 if not os.path.exists(fn): 2084 if not os.path.exists(fn):
2006 f = open(fn, "w", encoding="utf-8") 2085 f = open(fn, "w", encoding="utf-8")
2007 f.close() 2086 f.close()
2008 self.appendFile(fn, True) 2087 self.appendFile(fn, True)
2040 self.setDirty(True) 2119 self.setDirty(True)
2041 if self.vcs is not None: 2120 if self.vcs is not None:
2042 # edit VCS command options 2121 # edit VCS command options
2043 vcores = E5MessageBox.yesNo(self.ui, 2122 vcores = E5MessageBox.yesNo(self.ui,
2044 self.trUtf8("New Project"), 2123 self.trUtf8("New Project"),
2045 self.trUtf8("""Would you like to edit the VCS""" 2124 self.trUtf8(
2125 """Would you like to edit the VCS"""
2046 """ command options?""")) 2126 """ command options?"""))
2047 if vcores: 2127 if vcores:
2048 from VCS.CommandOptionsDialog import \ 2128 from VCS.CommandOptionsDialog import \
2049 vcsCommandOptionsDialog 2129 vcsCommandOptionsDialog
2050 codlg = vcsCommandOptionsDialog(self.vcs) 2130 codlg = vcsCommandOptionsDialog(self.vcs)
2052 self.vcs.vcsSetOptions(codlg.getOptions()) 2132 self.vcs.vcsSetOptions(codlg.getOptions())
2053 # add project file to repository 2133 # add project file to repository
2054 if res == 0: 2134 if res == 0:
2055 apres = E5MessageBox.yesNo(self.ui, 2135 apres = E5MessageBox.yesNo(self.ui,
2056 self.trUtf8("New project"), 2136 self.trUtf8("New project"),
2057 self.trUtf8("Shall the project file be added" 2137 self.trUtf8(
2138 "Shall the project file be added"
2058 " to the repository?"), 2139 " to the repository?"),
2059 yesDefault=True) 2140 yesDefault=True)
2060 if apres: 2141 if apres:
2061 self.saveProject() 2142 self.saveProject()
2062 self.vcs.vcsAdd(self.pfile) 2143 self.vcs.vcsAdd(self.pfile)
2064 self.pdata["VCS"] = ['None'] 2145 self.pdata["VCS"] = ['None']
2065 self.saveProject() 2146 self.saveProject()
2066 break 2147 break
2067 2148
2068 # put the project under VCS control 2149 # put the project under VCS control
2069 if self.vcs is None and self.vcsSoftwareAvailable() and self.vcsRequested: 2150 if self.vcs is None and self.vcsSoftwareAvailable() and \
2151 self.vcsRequested:
2070 vcsSystemsDict = e5App().getObject("PluginManager")\ 2152 vcsSystemsDict = e5App().getObject("PluginManager")\
2071 .getPluginDisplayStrings("version_control") 2153 .getPluginDisplayStrings("version_control")
2072 vcsSystemsDisplay = [self.trUtf8("None")] 2154 vcsSystemsDisplay = [self.trUtf8("None")]
2073 keys = sorted(vcsSystemsDict.keys()) 2155 keys = sorted(vcsSystemsDict.keys())
2074 for key in keys: 2156 for key in keys:
2075 vcsSystemsDisplay.append(vcsSystemsDict[key]) 2157 vcsSystemsDisplay.append(vcsSystemsDict[key])
2076 vcsSelected, ok = QInputDialog.getItem( 2158 vcsSelected, ok = QInputDialog.getItem(
2077 None, 2159 None,
2078 self.trUtf8("New Project"), 2160 self.trUtf8("New Project"),
2079 self.trUtf8("Select version control system for the project"), 2161 self.trUtf8(
2162 "Select version control system for the project"),
2080 vcsSystemsDisplay, 2163 vcsSystemsDisplay,
2081 0, False) 2164 0, False)
2082 if ok and vcsSelected != self.trUtf8("None"): 2165 if ok and vcsSelected != self.trUtf8("None"):
2083 for vcsSystem, vcsSystemDisplay in list(vcsSystemsDict.items()): 2166 for vcsSystem, vcsSystemDisplay in vcsSystemsDict.items():
2084 if vcsSystemDisplay == vcsSelected: 2167 if vcsSystemDisplay == vcsSelected:
2085 break 2168 break
2086 else: 2169 else:
2087 vcsSystem = "None" 2170 vcsSystem = "None"
2088 self.pdata["VCS"] = [vcsSystem] 2171 self.pdata["VCS"] = [vcsSystem]
2099 self.setDirty(True) 2182 self.setDirty(True)
2100 if self.vcs is not None: 2183 if self.vcs is not None:
2101 # edit VCS command options 2184 # edit VCS command options
2102 vcores = E5MessageBox.yesNo(self.ui, 2185 vcores = E5MessageBox.yesNo(self.ui,
2103 self.trUtf8("New Project"), 2186 self.trUtf8("New Project"),
2104 self.trUtf8("""Would you like to edit the VCS command""" 2187 self.trUtf8(
2105 """ options?""")) 2188 """Would you like to edit the VCS command"""
2189 """ options?"""))
2106 if vcores: 2190 if vcores:
2107 codlg = vcsCommandOptionsDialog(self.vcs) 2191 codlg = vcsCommandOptionsDialog(self.vcs)
2108 if codlg.exec_() == QDialog.Accepted: 2192 if codlg.exec_() == QDialog.Accepted:
2109 self.vcs.vcsSetOptions(codlg.getOptions()) 2193 self.vcs.vcsSetOptions(codlg.getOptions())
2110 2194
2147 if self.pdata["TRANSLATIONPATTERN"]: 2231 if self.pdata["TRANSLATIONPATTERN"]:
2148 pattern = os.path.basename(self.pdata["TRANSLATIONPATTERN"][0]) 2232 pattern = os.path.basename(self.pdata["TRANSLATIONPATTERN"][0])
2149 if "%language%" in pattern: 2233 if "%language%" in pattern:
2150 pattern = pattern.replace("%language%", "*") 2234 pattern = pattern.replace("%language%", "*")
2151 else: 2235 else:
2152 tpd = self.pdata["TRANSLATIONPATTERN"][0].split("%language%")[0] 2236 tpd = self.pdata["TRANSLATIONPATTERN"][0].split(
2237 "%language%")[0]
2153 else: 2238 else:
2154 pattern = "*.ts" 2239 pattern = "*.ts"
2155 tslist.extend(Utilities.direntries(tpd, True, pattern)) 2240 tslist.extend(Utilities.direntries(tpd, True, pattern))
2156 pattern = self.__binaryTranslationFile(pattern) 2241 pattern = self.__binaryTranslationFile(pattern)
2157 if pattern: 2242 if pattern:
2161 # the first entry determines the mainscript name 2246 # the first entry determines the mainscript name
2162 mainscriptname = os.path.splitext(mainscript)[0] or \ 2247 mainscriptname = os.path.splitext(mainscript)[0] or \
2163 os.path.basename(tslist[0]).split('_')[0] 2248 os.path.basename(tslist[0]).split('_')[0]
2164 self.pdata["TRANSLATIONPATTERN"] = \ 2249 self.pdata["TRANSLATIONPATTERN"] = \
2165 [os.path.join(os.path.dirname(tslist[0]), 2250 [os.path.join(os.path.dirname(tslist[0]),
2166 "{0}_%language%{1}".format(os.path.basename(tslist[0]).split('_')[0], 2251 "{0}_%language%{1}".format(
2252 os.path.basename(tslist[0]).split('_')[0],
2167 os.path.splitext(tslist[0])[1]))] 2253 os.path.splitext(tslist[0])[1]))]
2168 else: 2254 else:
2169 pattern, ok = QInputDialog.getText( 2255 pattern, ok = QInputDialog.getText(
2170 None, 2256 None,
2171 self.trUtf8("Translation Pattern"), 2257 self.trUtf8("Translation Pattern"),
2172 self.trUtf8("Enter the path pattern for translation files " 2258 self.trUtf8(
2173 "(use '%language%' in place of the language code):"), 2259 "Enter the path pattern for translation files "
2260 "(use '%language%' in place of the language code):"),
2174 QLineEdit.Normal, 2261 QLineEdit.Normal,
2175 tslist[0]) 2262 tslist[0])
2176 if pattern: 2263 if pattern:
2177 self.pdata["TRANSLATIONPATTERN"] = [pattern] 2264 self.pdata["TRANSLATIONPATTERN"] = [pattern]
2178 if self.pdata["TRANSLATIONPATTERN"]: 2265 if self.pdata["TRANSLATIONPATTERN"]:
2179 self.pdata["TRANSLATIONPATTERN"][0] = \ 2266 self.pdata["TRANSLATIONPATTERN"][0] = \
2180 self.getRelativePath(self.pdata["TRANSLATIONPATTERN"][0]) 2267 self.getRelativePath(self.pdata["TRANSLATIONPATTERN"][0])
2181 pattern = self.pdata["TRANSLATIONPATTERN"][0].replace("%language%", "*") 2268 pattern = self.pdata["TRANSLATIONPATTERN"][0]\
2269 .replace("%language%", "*")
2182 for ts in tslist: 2270 for ts in tslist:
2183 if fnmatch.fnmatch(ts, pattern): 2271 if fnmatch.fnmatch(ts, pattern):
2184 self.pdata["TRANSLATIONS"].append(ts) 2272 self.pdata["TRANSLATIONS"].append(ts)
2185 self.projectLanguageAdded.emit(ts) 2273 self.projectLanguageAdded.emit(ts)
2186 if self.pdata["TRANSLATIONSBINPATH"]: 2274 if self.pdata["TRANSLATIONSBINPATH"]:
2187 tpd = os.path.join(self.ppath, 2275 tpd = os.path.join(self.ppath,
2188 self.pdata["TRANSLATIONSBINPATH"][0]) 2276 self.pdata["TRANSLATIONSBINPATH"][0])
2189 pattern = os.path.basename(self.pdata["TRANSLATIONPATTERN"][0])\ 2277 pattern = os.path.basename(
2278 self.pdata["TRANSLATIONPATTERN"][0])\
2190 .replace("%language%", "*") 2279 .replace("%language%", "*")
2191 pattern = self.__binaryTranslationFile(pattern) 2280 pattern = self.__binaryTranslationFile(pattern)
2192 qmlist = Utilities.direntries(tpd, True, pattern) 2281 qmlist = Utilities.direntries(tpd, True, pattern)
2193 for qm in qmlist: 2282 for qm in qmlist:
2194 self.pdata["TRANSLATIONS"].append(qm) 2283 self.pdata["TRANSLATIONS"].append(qm)
2195 self.projectLanguageAdded.emit(qm) 2284 self.projectLanguageAdded.emit(qm)
2196 if len(self.pdata["MAINSCRIPT"]) == 0 or \ 2285 if len(self.pdata["MAINSCRIPT"]) == 0 or \
2197 len(self.pdata["MAINSCRIPT"][0]) == 0: 2286 len(self.pdata["MAINSCRIPT"][0]) == 0:
2198 if self.pdata["PROGLANGUAGE"][0] in ["Python", "Python2", "Python3"]: 2287 if self.pdata["PROGLANGUAGE"][0] in \
2199 self.pdata["MAINSCRIPT"] = ['{0}.py'.format(mainscriptname)] 2288 ["Python", "Python2", "Python3"]:
2289 self.pdata["MAINSCRIPT"] = [
2290 '{0}.py'.format(mainscriptname)]
2200 elif self.pdata["PROGLANGUAGE"][0] == "Ruby": 2291 elif self.pdata["PROGLANGUAGE"][0] == "Ruby":
2201 self.pdata["MAINSCRIPT"] = ['{0}.rb'.format(mainscriptname)] 2292 self.pdata["MAINSCRIPT"] = [
2293 '{0}.rb'.format(mainscriptname)]
2202 self.setDirty(True) 2294 self.setDirty(True)
2203 QApplication.restoreOverrideCursor() 2295 QApplication.restoreOverrideCursor()
2204 2296
2205 def __showProperties(self): 2297 def __showProperties(self):
2206 """ 2298 """
2233 os.makedirs(tp) 2325 os.makedirs(tp)
2234 if tp != self.ppath and tp not in self.subdirs: 2326 if tp != self.ppath and tp not in self.subdirs:
2235 self.subdirs.append(tp) 2327 self.subdirs.append(tp)
2236 2328
2237 if self.pdata["TRANSLATIONSBINPATH"]: 2329 if self.pdata["TRANSLATIONSBINPATH"]:
2238 tp = os.path.join(self.ppath, self.pdata["TRANSLATIONSBINPATH"][0]) 2330 tp = os.path.join(
2331 self.ppath, self.pdata["TRANSLATIONSBINPATH"][0])
2239 if not os.path.isdir(tp): 2332 if not os.path.isdir(tp):
2240 os.makedirs(tp) 2333 os.makedirs(tp)
2241 if tp != self.ppath and tp not in self.subdirs: 2334 if tp != self.ppath and tp not in self.subdirs:
2242 self.subdirs.append(tp) 2335 self.subdirs.append(tp)
2243 2336
2244 self.pluginGrp.setEnabled(self.pdata["PROJECTTYPE"][0] == "E4Plugin") 2337 self.pluginGrp.setEnabled(
2338 self.pdata["PROJECTTYPE"][0] == "E4Plugin")
2245 2339
2246 self.__model.projectPropertiesChanged() 2340 self.__model.projectPropertiesChanged()
2247 self.projectPropertiesChanged.emit() 2341 self.projectPropertiesChanged.emit()
2248 2342
2249 def __showUserProperties(self): 2343 def __showUserProperties(self):
2250 """ 2344 """
2251 Private slot to display the user specific properties dialog. 2345 Private slot to display the user specific properties dialog.
2252 """ 2346 """
2253 vcsSystem = self.pdata["VCS"] and self.pdata["VCS"][0] or None 2347 vcsSystem = self.pdata["VCS"] and self.pdata["VCS"][0] or None
2254 vcsSystemOverride = \ 2348 vcsSystemOverride = self.pudata["VCSOVERRIDE"] and \
2255 self.pudata["VCSOVERRIDE"] and self.pudata["VCSOVERRIDE"][0] or None 2349 self.pudata["VCSOVERRIDE"][0] or None
2256 2350
2257 from .UserPropertiesDialog import UserPropertiesDialog 2351 from .UserPropertiesDialog import UserPropertiesDialog
2258 dlg = UserPropertiesDialog(self) 2352 dlg = UserPropertiesDialog(self)
2259 if dlg.exec_() == QDialog.Accepted: 2353 if dlg.exec_() == QDialog.Accepted:
2260 dlg.storeData() 2354 dlg.storeData()
2275 # reinit VCS 2369 # reinit VCS
2276 self.vcs = self.initVCS() 2370 self.vcs = self.initVCS()
2277 # start the VCS monitor thread 2371 # start the VCS monitor thread
2278 if self.vcs is not None: 2372 if self.vcs is not None:
2279 self.vcs.startStatusMonitor(self) 2373 self.vcs.startStatusMonitor(self)
2280 self.vcs.vcsStatusMonitorData.connect(self.__model.changeVCSStates) 2374 self.vcs.vcsStatusMonitorData.connect(
2281 self.vcs.vcsStatusMonitorStatus.connect(self.__statusMonitorStatus) 2375 self.__model.changeVCSStates)
2376 self.vcs.vcsStatusMonitorStatus.connect(
2377 self.__statusMonitorStatus)
2282 self.vcs.vcsStatusChanged.connect(self.__vcsStatusChanged) 2378 self.vcs.vcsStatusChanged.connect(self.__vcsStatusChanged)
2283 self.reinitVCS.emit() 2379 self.reinitVCS.emit()
2284 2380
2285 if self.pudata["VCSSTATUSMONITORINTERVAL"]: 2381 if self.pudata["VCSSTATUSMONITORINTERVAL"]:
2286 self.setStatusMonitorInterval( 2382 self.setStatusMonitorInterval(
2312 2408
2313 def getEditorLexerAssoc(self, filename): 2409 def getEditorLexerAssoc(self, filename):
2314 """ 2410 """
2315 Public method to retrieve a lexer association. 2411 Public method to retrieve a lexer association.
2316 2412
2317 @param filename filename used to determine the associated lexer language (string) 2413 @param filename filename used to determine the associated lexer
2414 language (string)
2318 @return the requested lexer language (string) 2415 @return the requested lexer language (string)
2319 """ 2416 """
2320 # try user settings first 2417 # try user settings first
2321 for pattern, language in list(self.pdata["LEXERASSOCS"].items()): 2418 for pattern, language in list(self.pdata["LEXERASSOCS"].items()):
2322 if fnmatch.fnmatch(filename, pattern): 2419 if fnmatch.fnmatch(filename, pattern):
2347 2444
2348 if fn is None: 2445 if fn is None:
2349 fn = E5FileDialog.getOpenFileName( 2446 fn = E5FileDialog.getOpenFileName(
2350 self.parent(), 2447 self.parent(),
2351 self.trUtf8("Open project"), 2448 self.trUtf8("Open project"),
2352 Preferences.getMultiProject("Workspace") or Utilities.getHomeDir(), 2449 Preferences.getMultiProject("Workspace") or \
2450 Utilities.getHomeDir(),
2353 self.trUtf8("Project Files (*.e4p)")) 2451 self.trUtf8("Project Files (*.e4p)"))
2354 2452
2355 QApplication.processEvents() 2453 QApplication.processEvents()
2356 2454
2357 if fn: 2455 if fn:
2383 self.vcs = self.initVCS() 2481 self.vcs = self.initVCS()
2384 if self.vcs is None and self.isDirty() == oldState: 2482 if self.vcs is None and self.isDirty() == oldState:
2385 # check, if project is version controlled 2483 # check, if project is version controlled
2386 pluginManager = e5App().getObject("PluginManager") 2484 pluginManager = e5App().getObject("PluginManager")
2387 for indicator, vcsData in \ 2485 for indicator, vcsData in \
2388 list(pluginManager.getVcsSystemIndicators().items()): 2486 pluginManager.getVcsSystemIndicators().items():
2389 if os.path.exists(os.path.join(self.ppath, indicator)): 2487 if os.path.exists(
2488 os.path.join(self.ppath, indicator)):
2390 if len(vcsData) > 1: 2489 if len(vcsData) > 1:
2391 vcsList = [] 2490 vcsList = []
2392 for vcsSystemStr, vcsSystemDisplay in vcsData: 2491 for vcsSystemStr, vcsSystemDisplay in \
2492 vcsData:
2393 vcsList.append(vcsSystemDisplay) 2493 vcsList.append(vcsSystemDisplay)
2394 QApplication.restoreOverrideCursor() 2494 QApplication.restoreOverrideCursor()
2395 res, vcs_ok = QInputDialog.getItem( 2495 res, vcs_ok = QInputDialog.getItem(
2396 None, 2496 None,
2397 self.trUtf8("New Project"), 2497 self.trUtf8("New Project"),
2398 self.trUtf8("Select Version Control System"), 2498 self.trUtf8(
2499 "Select Version Control System"),
2399 vcsList, 2500 vcsList,
2400 0, False) 2501 0, False)
2401 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) 2502 QApplication.setOverrideCursor(
2503 QCursor(Qt.WaitCursor))
2402 QApplication.processEvents() 2504 QApplication.processEvents()
2403 if vcs_ok: 2505 if vcs_ok:
2404 for vcsSystemStr, vcsSystemDisplay in vcsData: 2506 for vcsSystemStr, vcsSystemDisplay in \
2507 vcsData:
2405 if res == vcsSystemDisplay: 2508 if res == vcsSystemDisplay:
2406 vcsSystem = vcsSystemStr 2509 vcsSystem = vcsSystemStr
2407 break 2510 break
2408 else: 2511 else:
2409 vcsSystem = "None" 2512 vcsSystem = "None"
2413 vcsSystem = vcsData[0][0] 2516 vcsSystem = vcsData[0][0]
2414 self.pdata["VCS"] = [vcsSystem] 2517 self.pdata["VCS"] = [vcsSystem]
2415 self.vcs = self.initVCS() 2518 self.vcs = self.initVCS()
2416 self.setDirty(True) 2519 self.setDirty(True)
2417 if self.vcs is not None and \ 2520 if self.vcs is not None and \
2418 self.vcs.vcsRegisteredState(self.ppath) != self.vcs.canBeCommitted: 2521 (self.vcs.vcsRegisteredState(self.ppath) !=
2522 self.vcs.canBeCommitted):
2419 self.pdata["VCS"] = ['None'] 2523 self.pdata["VCS"] = ['None']
2420 self.vcs = self.initVCS() 2524 self.vcs = self.initVCS()
2421 self.closeAct.setEnabled(True) 2525 self.closeAct.setEnabled(True)
2422 self.saveasAct.setEnabled(True) 2526 self.saveasAct.setEnabled(True)
2423 self.actGrp2.setEnabled(True) 2527 self.actGrp2.setEnabled(True)
2432 self.menuCheckAct.setEnabled(True) 2536 self.menuCheckAct.setEnabled(True)
2433 self.menuShowAct.setEnabled(True) 2537 self.menuShowAct.setEnabled(True)
2434 self.menuDiagramAct.setEnabled(True) 2538 self.menuDiagramAct.setEnabled(True)
2435 self.menuApidocAct.setEnabled(True) 2539 self.menuApidocAct.setEnabled(True)
2436 self.menuPackagersAct.setEnabled(True) 2540 self.menuPackagersAct.setEnabled(True)
2437 self.pluginGrp.setEnabled(self.pdata["PROJECTTYPE"][0] == "E4Plugin") 2541 self.pluginGrp.setEnabled(
2542 self.pdata["PROJECTTYPE"][0] == "E4Plugin")
2438 self.addLanguageAct.setEnabled( 2543 self.addLanguageAct.setEnabled(
2439 len(self.pdata["TRANSLATIONPATTERN"]) > 0 and \ 2544 len(self.pdata["TRANSLATIONPATTERN"]) > 0 and \
2440 self.pdata["TRANSLATIONPATTERN"][0] != '') 2545 self.pdata["TRANSLATIONPATTERN"][0] != '')
2441 2546
2442 self.__model.projectOpened() 2547 self.__model.projectOpened()
2454 2559
2455 if restoreSession: 2560 if restoreSession:
2456 # open the main script 2561 # open the main script
2457 if len(self.pdata["MAINSCRIPT"]) == 1: 2562 if len(self.pdata["MAINSCRIPT"]) == 1:
2458 self.sourceFile.emit( 2563 self.sourceFile.emit(
2459 os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0])) 2564 os.path.join(
2565 self.ppath, self.pdata["MAINSCRIPT"][0]))
2460 2566
2461 # open a project session file being quiet about errors 2567 # open a project session file being quiet about errors
2462 if reopen: 2568 if reopen:
2463 self.__readSession(quiet=True, indicator="_tmp") 2569 self.__readSession(quiet=True, indicator="_tmp")
2464 elif Preferences.getProject("AutoLoadSession"): 2570 elif Preferences.getProject("AutoLoadSession"):
2465 self.__readSession(quiet=True) 2571 self.__readSession(quiet=True)
2466 2572
2467 # open a project debugger properties file being quiet about errors 2573 # open a project debugger properties file being quiet
2574 # about errors
2468 if Preferences.getProject("AutoLoadDbgProperties"): 2575 if Preferences.getProject("AutoLoadDbgProperties"):
2469 self.__readDebugProperties(True) 2576 self.__readDebugProperties(True)
2470 2577
2471 # start the VCS monitor thread 2578 # start the VCS monitor thread
2472 if self.vcs is not None: 2579 if self.vcs is not None:
2699 filesWithSyntaxErrors += 1 2806 filesWithSyntaxErrors += 1
2700 2807
2701 if reportSyntaxErrors and filesWithSyntaxErrors > 0: 2808 if reportSyntaxErrors and filesWithSyntaxErrors > 0:
2702 E5MessageBox.critical(self.ui, 2809 E5MessageBox.critical(self.ui,
2703 self.trUtf8("Syntax errors detected"), 2810 self.trUtf8("Syntax errors detected"),
2704 self.trUtf8("""The project contains %n file(s) with syntax errors.""", 2811 self.trUtf8(
2812 """The project contains %n file(s) with syntax errors.""",
2705 "", filesWithSyntaxErrors) 2813 "", filesWithSyntaxErrors)
2706 ) 2814 )
2707 return False 2815 return False
2708 else: 2816 else:
2709 return success 2817 return success
2729 filesWithSyntaxErrors += 1 2837 filesWithSyntaxErrors += 1
2730 2838
2731 if reportSyntaxErrors and filesWithSyntaxErrors > 0: 2839 if reportSyntaxErrors and filesWithSyntaxErrors > 0:
2732 E5MessageBox.critical(self.ui, 2840 E5MessageBox.critical(self.ui,
2733 self.trUtf8("Syntax errors detected"), 2841 self.trUtf8("Syntax errors detected"),
2734 self.trUtf8("""The project contains %n file(s) with syntax errors.""", 2842 self.trUtf8(
2843 """The project contains %n file(s) with syntax errors.""",
2735 "", filesWithSyntaxErrors) 2844 "", filesWithSyntaxErrors)
2736 ) 2845 )
2737 return False 2846 return False
2738 else: 2847 else:
2739 return success 2848 return success
2740 2849
2741 def getMainScript(self, normalized=False): 2850 def getMainScript(self, normalized=False):
2742 """ 2851 """
2743 Public method to return the main script filename. 2852 Public method to return the main script filename.
2744 2853
2745 @param normalized flag indicating a normalized filename is wanted (boolean) 2854 @param normalized flag indicating a normalized filename is wanted
2855 (boolean)
2746 @return filename of the projects main script (string) 2856 @return filename of the projects main script (string)
2747 """ 2857 """
2748 if len(self.pdata["MAINSCRIPT"]): 2858 if len(self.pdata["MAINSCRIPT"]):
2749 if normalized: 2859 if normalized:
2750 return os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0]) 2860 return os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0])
2755 2865
2756 def getSources(self, normalized=False): 2866 def getSources(self, normalized=False):
2757 """ 2867 """
2758 Public method to return the source script files. 2868 Public method to return the source script files.
2759 2869
2760 @param normalized flag indicating a normalized filename is wanted (boolean) 2870 @param normalized flag indicating a normalized filename is wanted
2871 (boolean)
2761 @return list of the projects scripts (list of string) 2872 @return list of the projects scripts (list of string)
2762 """ 2873 """
2763 if normalized: 2874 if normalized:
2764 return [os.path.join(self.ppath, fn) for fn in self.pdata["SOURCES"]] 2875 return [os.path.join(self.ppath, fn) for fn in
2876 self.pdata["SOURCES"]]
2765 else: 2877 else:
2766 return self.pdata["SOURCES"] 2878 return self.pdata["SOURCES"]
2767 2879
2768 def getProjectType(self): 2880 def getProjectType(self):
2769 """ 2881 """
2864 (boolean) 2976 (boolean)
2865 """ 2977 """
2866 if self.ppath: 2978 if self.ppath:
2867 if path == self.ppath: 2979 if path == self.ppath:
2868 return True 2980 return True
2869 elif Utilities.normcasepath(Utilities.toNativeSeparators(path)).startswith( 2981 elif Utilities.normcasepath(Utilities.toNativeSeparators(path))\
2870 Utilities.normcasepath(Utilities.toNativeSeparators(self.ppath + "/"))): 2982 .startswith(Utilities.normcasepath(
2983 Utilities.toNativeSeparators(self.ppath + "/"))):
2871 return True 2984 return True
2872 else: 2985 else:
2873 return False 2986 return False
2874 else: 2987 else:
2875 return False 2988 return False
2995 """ 3108 """
2996 return self.pdata["EOL"][0] == 0 3109 return self.pdata["EOL"][0] == 0
2997 3110
2998 def isProjectFile(self, fn): 3111 def isProjectFile(self, fn):
2999 """ 3112 """
3000 Public method used to check, if the passed in filename belongs to the project. 3113 Public method used to check, if the passed in filename belongs to the
3114 project.
3001 3115
3002 @param fn filename to be checked (string) 3116 @param fn filename to be checked (string)
3003 @return flag indicating membership (boolean) 3117 @return flag indicating membership (boolean)
3004 """ 3118 """
3005 for group in ["SOURCES", "FORMS", "INTERFACES", 3119 for group in ["SOURCES", "FORMS", "INTERFACES",
3009 3123
3010 return False 3124 return False
3011 3125
3012 def __checkProjectFileGroup(self, fn, group): 3126 def __checkProjectFileGroup(self, fn, group):
3013 """ 3127 """
3014 Private method to check, if a file is in a specific file group of the project. 3128 Private method to check, if a file is in a specific file group of the
3129 project.
3015 3130
3016 @param fn filename to be checked (string) 3131 @param fn filename to be checked (string)
3017 @param group group to check (string) 3132 @param group group to check (string)
3018 @return flag indicating membership (boolean) 3133 @return flag indicating membership (boolean)
3019 """ 3134 """
3039 3154
3040 return False 3155 return False
3041 3156
3042 def isProjectSource(self, fn): 3157 def isProjectSource(self, fn):
3043 """ 3158 """
3044 Public method used to check, if the passed in filename belongs to the project 3159 Public method used to check, if the passed in filename belongs to the
3045 sources. 3160 project sources.
3046 3161
3047 @param fn filename to be checked (string) 3162 @param fn filename to be checked (string)
3048 @return flag indicating membership (boolean) 3163 @return flag indicating membership (boolean)
3049 """ 3164 """
3050 return self.__checkProjectFileGroup(fn, "SOURCES") 3165 return self.__checkProjectFileGroup(fn, "SOURCES")
3051 3166
3052 def isProjectForm(self, fn): 3167 def isProjectForm(self, fn):
3053 """ 3168 """
3054 Public method used to check, if the passed in filename belongs to the project 3169 Public method used to check, if the passed in filename belongs to the
3055 forms. 3170 project forms.
3056 3171
3057 @param fn filename to be checked (string) 3172 @param fn filename to be checked (string)
3058 @return flag indicating membership (boolean) 3173 @return flag indicating membership (boolean)
3059 """ 3174 """
3060 return self.__checkProjectFileGroup(fn, "FORMS") 3175 return self.__checkProjectFileGroup(fn, "FORMS")
3061 3176
3062 def isProjectInterface(self, fn): 3177 def isProjectInterface(self, fn):
3063 """ 3178 """
3064 Public method used to check, if the passed in filename belongs to the project 3179 Public method used to check, if the passed in filename belongs to the
3065 interfaces. 3180 project interfaces.
3066 3181
3067 @param fn filename to be checked (string) 3182 @param fn filename to be checked (string)
3068 @return flag indicating membership (boolean) 3183 @return flag indicating membership (boolean)
3069 """ 3184 """
3070 return self.__checkProjectFileGroup(fn, "INTERFACES") 3185 return self.__checkProjectFileGroup(fn, "INTERFACES")
3071 3186
3072 def isProjectResource(self, fn): 3187 def isProjectResource(self, fn):
3073 """ 3188 """
3074 Public method used to check, if the passed in filename belongs to the project 3189 Public method used to check, if the passed in filename belongs to the
3075 resources. 3190 project resources.
3076 3191
3077 @param fn filename to be checked (string) 3192 @param fn filename to be checked (string)
3078 @return flag indicating membership (boolean) 3193 @return flag indicating membership (boolean)
3079 """ 3194 """
3080 return self.__checkProjectFileGroup(fn, "RESOURCES") 3195 return self.__checkProjectFileGroup(fn, "RESOURCES")
3135 self.actions.append(self.saveAct) 3250 self.actions.append(self.saveAct)
3136 3251
3137 self.saveasAct = E5Action(self.trUtf8('Save project as'), 3252 self.saveasAct = E5Action(self.trUtf8('Save project as'),
3138 UI.PixmapCache.getIcon("projectSaveAs.png"), 3253 UI.PixmapCache.getIcon("projectSaveAs.png"),
3139 self.trUtf8('Save &as...'), 0, 0, self, 'project_save_as') 3254 self.trUtf8('Save &as...'), 0, 0, self, 'project_save_as')
3140 self.saveasAct.setStatusTip(self.trUtf8('Save the current project to a new file')) 3255 self.saveasAct.setStatusTip(self.trUtf8(
3256 'Save the current project to a new file'))
3141 self.saveasAct.setWhatsThis(self.trUtf8( 3257 self.saveasAct.setWhatsThis(self.trUtf8(
3142 """<b>Save as</b>""" 3258 """<b>Save as</b>"""
3143 """<p>This saves the current project to a new file.</p>""" 3259 """<p>This saves the current project to a new file.</p>"""
3144 )) 3260 ))
3145 self.saveasAct.triggered[()].connect(self.saveProjectAs) 3261 self.saveasAct.triggered[()].connect(self.saveProjectAs)
3149 3265
3150 self.addFilesAct = E5Action(self.trUtf8('Add files to project'), 3266 self.addFilesAct = E5Action(self.trUtf8('Add files to project'),
3151 UI.PixmapCache.getIcon("fileMisc.png"), 3267 UI.PixmapCache.getIcon("fileMisc.png"),
3152 self.trUtf8('Add &files...'), 0, 0, 3268 self.trUtf8('Add &files...'), 0, 0,
3153 self.actGrp2, 'project_add_file') 3269 self.actGrp2, 'project_add_file')
3154 self.addFilesAct.setStatusTip(self.trUtf8('Add files to the current project')) 3270 self.addFilesAct.setStatusTip(self.trUtf8(
3271 'Add files to the current project'))
3155 self.addFilesAct.setWhatsThis(self.trUtf8( 3272 self.addFilesAct.setWhatsThis(self.trUtf8(
3156 """<b>Add files...</b>""" 3273 """<b>Add files...</b>"""
3157 """<p>This opens a dialog for adding files""" 3274 """<p>This opens a dialog for adding files"""
3158 """ to the current project. The place to add is""" 3275 """ to the current project. The place to add is"""
3159 """ determined by the file extension.</p>""" 3276 """ determined by the file extension.</p>"""
3160 )) 3277 ))
3161 self.addFilesAct.triggered[()].connect(self.addFiles) 3278 self.addFilesAct.triggered[()].connect(self.addFiles)
3162 self.actions.append(self.addFilesAct) 3279 self.actions.append(self.addFilesAct)
3163 3280
3164 self.addDirectoryAct = E5Action(self.trUtf8('Add directory to project'), 3281 self.addDirectoryAct = E5Action(
3165 UI.PixmapCache.getIcon("dirOpen.png"), 3282 self.trUtf8('Add directory to project'),
3166 self.trUtf8('Add directory...'), 0, 0, 3283 UI.PixmapCache.getIcon("dirOpen.png"),
3167 self.actGrp2, 'project_add_directory') 3284 self.trUtf8('Add directory...'), 0, 0,
3285 self.actGrp2, 'project_add_directory')
3168 self.addDirectoryAct.setStatusTip( 3286 self.addDirectoryAct.setStatusTip(
3169 self.trUtf8('Add a directory to the current project')) 3287 self.trUtf8('Add a directory to the current project'))
3170 self.addDirectoryAct.setWhatsThis(self.trUtf8( 3288 self.addDirectoryAct.setWhatsThis(self.trUtf8(
3171 """<b>Add directory...</b>""" 3289 """<b>Add directory...</b>"""
3172 """<p>This opens a dialog for adding a directory""" 3290 """<p>This opens a dialog for adding a directory"""
3173 """ to the current project.</p>""" 3291 """ to the current project.</p>"""
3174 )) 3292 ))
3175 self.addDirectoryAct.triggered[()].connect(self.addDirectory) 3293 self.addDirectoryAct.triggered[()].connect(self.addDirectory)
3176 self.actions.append(self.addDirectoryAct) 3294 self.actions.append(self.addDirectoryAct)
3177 3295
3178 self.addLanguageAct = E5Action(self.trUtf8('Add translation to project'), 3296 self.addLanguageAct = E5Action(
3179 UI.PixmapCache.getIcon("linguist4.png"), 3297 self.trUtf8('Add translation to project'),
3180 self.trUtf8('Add &translation...'), 0, 0, 3298 UI.PixmapCache.getIcon("linguist4.png"),
3181 self.actGrp2, 'project_add_translation') 3299 self.trUtf8('Add &translation...'), 0, 0,
3300 self.actGrp2, 'project_add_translation')
3182 self.addLanguageAct.setStatusTip( 3301 self.addLanguageAct.setStatusTip(
3183 self.trUtf8('Add a translation to the current project')) 3302 self.trUtf8('Add a translation to the current project'))
3184 self.addLanguageAct.setWhatsThis(self.trUtf8( 3303 self.addLanguageAct.setWhatsThis(self.trUtf8(
3185 """<b>Add translation...</b>""" 3304 """<b>Add translation...</b>"""
3186 """<p>This opens a dialog for add a translation""" 3305 """<p>This opens a dialog for add a translation"""
3190 self.actions.append(self.addLanguageAct) 3309 self.actions.append(self.addLanguageAct)
3191 3310
3192 act = E5Action(self.trUtf8('Search new files'), 3311 act = E5Action(self.trUtf8('Search new files'),
3193 self.trUtf8('Searc&h new files...'), 0, 0, 3312 self.trUtf8('Searc&h new files...'), 0, 0,
3194 self.actGrp2, 'project_search_new_files') 3313 self.actGrp2, 'project_search_new_files')
3195 act.setStatusTip(self.trUtf8('Search new files in the project directory.')) 3314 act.setStatusTip(self.trUtf8(
3315 'Search new files in the project directory.'))
3196 act.setWhatsThis(self.trUtf8( 3316 act.setWhatsThis(self.trUtf8(
3197 """<b>Search new files...</b>""" 3317 """<b>Search new files...</b>"""
3198 """<p>This searches for new files (sources, *.ui, *.idl) in the project""" 3318 """<p>This searches for new files (sources, *.ui, *.idl) in"""
3199 """ directory and registered subdirectories.</p>""" 3319 """ the project directory and registered subdirectories.</p>"""
3200 )) 3320 ))
3201 act.triggered[()].connect(self.__searchNewFiles) 3321 act.triggered[()].connect(self.__searchNewFiles)
3202 self.actions.append(act) 3322 self.actions.append(act)
3203 3323
3204 self.propsAct = E5Action(self.trUtf8('Project properties'), 3324 self.propsAct = E5Action(self.trUtf8('Project properties'),
3205 UI.PixmapCache.getIcon("projectProps.png"), 3325 UI.PixmapCache.getIcon("projectProps.png"),
3206 self.trUtf8('&Properties...'), 0, 0, self, 'project_properties') 3326 self.trUtf8('&Properties...'), 0, 0, self,
3327 'project_properties')
3207 self.propsAct.setStatusTip(self.trUtf8('Show the project properties')) 3328 self.propsAct.setStatusTip(self.trUtf8('Show the project properties'))
3208 self.propsAct.setWhatsThis(self.trUtf8( 3329 self.propsAct.setWhatsThis(self.trUtf8(
3209 """<b>Properties...</b>""" 3330 """<b>Properties...</b>"""
3210 """<p>This shows a dialog to edit the project properties.</p>""" 3331 """<p>This shows a dialog to edit the project properties.</p>"""
3211 )) 3332 ))
3212 self.propsAct.triggered[()].connect(self.__showProperties) 3333 self.propsAct.triggered[()].connect(self.__showProperties)
3213 self.actions.append(self.propsAct) 3334 self.actions.append(self.propsAct)
3214 3335
3215 self.userPropsAct = E5Action(self.trUtf8('User project properties'), 3336 self.userPropsAct = E5Action(self.trUtf8('User project properties'),
3216 UI.PixmapCache.getIcon("projectUserProps.png"), 3337 UI.PixmapCache.getIcon("projectUserProps.png"),
3217 self.trUtf8('&User Properties...'), 0, 0, self, 'project_user_properties') 3338 self.trUtf8('&User Properties...'), 0, 0, self,
3339 'project_user_properties')
3218 self.userPropsAct.setStatusTip(self.trUtf8( 3340 self.userPropsAct.setStatusTip(self.trUtf8(
3219 'Show the user specific project properties')) 3341 'Show the user specific project properties'))
3220 self.userPropsAct.setWhatsThis(self.trUtf8( 3342 self.userPropsAct.setWhatsThis(self.trUtf8(
3221 """<b>User Properties...</b>""" 3343 """<b>User Properties...</b>"""
3222 """<p>This shows a dialog to edit the user specific project properties.</p>""" 3344 """<p>This shows a dialog to edit the user specific project"""
3345 """ properties.</p>"""
3223 )) 3346 ))
3224 self.userPropsAct.triggered[()].connect(self.__showUserProperties) 3347 self.userPropsAct.triggered[()].connect(self.__showUserProperties)
3225 self.actions.append(self.userPropsAct) 3348 self.actions.append(self.userPropsAct)
3226 3349
3227 self.filetypesAct = E5Action(self.trUtf8('Filetype Associations'), 3350 self.filetypesAct = E5Action(self.trUtf8('Filetype Associations'),
3229 self, 'project_filetype_associatios') 3352 self, 'project_filetype_associatios')
3230 self.filetypesAct.setStatusTip( 3353 self.filetypesAct.setStatusTip(
3231 self.trUtf8('Show the project filetype associations')) 3354 self.trUtf8('Show the project filetype associations'))
3232 self.filetypesAct.setWhatsThis(self.trUtf8( 3355 self.filetypesAct.setWhatsThis(self.trUtf8(
3233 """<b>Filetype Associations...</b>""" 3356 """<b>Filetype Associations...</b>"""
3234 """<p>This shows a dialog to edit the filetype associations of the project.""" 3357 """<p>This shows a dialog to edit the filetype associations of"""
3235 """ These associations determine the type (source, form, interface""" 3358 """ the project. These associations determine the type"""
3236 """ or others) with a filename pattern. They are used when adding a file""" 3359 """ (source, form, interface or others) with a filename"""
3237 """ to the project and when performing a search for new files.</p>""" 3360 """ pattern. They are used when adding a file to the project"""
3361 """ and when performing a search for new files.</p>"""
3238 )) 3362 ))
3239 self.filetypesAct.triggered[()].connect(self.__showFiletypeAssociations) 3363 self.filetypesAct.triggered[()].connect(
3364 self.__showFiletypeAssociations)
3240 self.actions.append(self.filetypesAct) 3365 self.actions.append(self.filetypesAct)
3241 3366
3242 self.lexersAct = E5Action(self.trUtf8('Lexer Associations'), 3367 self.lexersAct = E5Action(self.trUtf8('Lexer Associations'),
3243 self.trUtf8('Lexer Associations...'), 0, 0, 3368 self.trUtf8('Lexer Associations...'), 0, 0,
3244 self, 'project_lexer_associatios') 3369 self, 'project_lexer_associatios')
3245 self.lexersAct.setStatusTip( 3370 self.lexersAct.setStatusTip(self.trUtf8(
3246 self.trUtf8('Show the project lexer associations (overriding defaults)')) 3371 'Show the project lexer associations (overriding defaults)'))
3247 self.lexersAct.setWhatsThis(self.trUtf8( 3372 self.lexersAct.setWhatsThis(self.trUtf8(
3248 """<b>Lexer Associations...</b>""" 3373 """<b>Lexer Associations...</b>"""
3249 """<p>This shows a dialog to edit the lexer associations of the project.""" 3374 """<p>This shows a dialog to edit the lexer associations of"""
3250 """ These associations override the global lexer associations. Lexers""" 3375 """ the project. These associations override the global lexer"""
3251 """ are used to highlight the editor text.</p>""" 3376 """ associations. Lexers are used to highlight the editor"""
3377 """ text.</p>"""
3252 )) 3378 ))
3253 self.lexersAct.triggered[()].connect(self.__showLexerAssociations) 3379 self.lexersAct.triggered[()].connect(self.__showLexerAssociations)
3254 self.actions.append(self.lexersAct) 3380 self.actions.append(self.lexersAct)
3255 3381
3256 self.dbgActGrp = createActionGroup(self) 3382 self.dbgActGrp = createActionGroup(self)
3259 self.trUtf8('Debugger &Properties...'), 0, 0, 3385 self.trUtf8('Debugger &Properties...'), 0, 0,
3260 self.dbgActGrp, 'project_debugger_properties') 3386 self.dbgActGrp, 'project_debugger_properties')
3261 act.setStatusTip(self.trUtf8('Show the debugger properties')) 3387 act.setStatusTip(self.trUtf8('Show the debugger properties'))
3262 act.setWhatsThis(self.trUtf8( 3388 act.setWhatsThis(self.trUtf8(
3263 """<b>Debugger Properties...</b>""" 3389 """<b>Debugger Properties...</b>"""
3264 """<p>This shows a dialog to edit project specific debugger settings.</p>""" 3390 """<p>This shows a dialog to edit project specific debugger"""
3391 """ settings.</p>"""
3265 )) 3392 ))
3266 act.triggered[()].connect(self.__showDebugProperties) 3393 act.triggered[()].connect(self.__showDebugProperties)
3267 self.actions.append(act) 3394 self.actions.append(act)
3268 3395
3269 act = E5Action(self.trUtf8('Load'), 3396 act = E5Action(self.trUtf8('Load'),
3365 self.chkGrp, 'project_code_metrics') 3492 self.chkGrp, 'project_code_metrics')
3366 self.codeMetricsAct.setStatusTip( 3493 self.codeMetricsAct.setStatusTip(
3367 self.trUtf8('Show some code metrics for the project.')) 3494 self.trUtf8('Show some code metrics for the project.'))
3368 self.codeMetricsAct.setWhatsThis(self.trUtf8( 3495 self.codeMetricsAct.setWhatsThis(self.trUtf8(
3369 """<b>Code Metrics...</b>""" 3496 """<b>Code Metrics...</b>"""
3370 """<p>This shows some code metrics for all Python files in the project.</p>""" 3497 """<p>This shows some code metrics for all Python files in"""
3498 """ the project.</p>"""
3371 )) 3499 ))
3372 self.codeMetricsAct.triggered[()].connect(self.__showCodeMetrics) 3500 self.codeMetricsAct.triggered[()].connect(self.__showCodeMetrics)
3373 self.actions.append(self.codeMetricsAct) 3501 self.actions.append(self.codeMetricsAct)
3374 3502
3375 self.codeCoverageAct = E5Action(self.trUtf8('Python Code Coverage'), 3503 self.codeCoverageAct = E5Action(self.trUtf8('Python Code Coverage'),
3377 self.chkGrp, 'project_code_coverage') 3505 self.chkGrp, 'project_code_coverage')
3378 self.codeCoverageAct.setStatusTip( 3506 self.codeCoverageAct.setStatusTip(
3379 self.trUtf8('Show code coverage information for the project.')) 3507 self.trUtf8('Show code coverage information for the project.'))
3380 self.codeCoverageAct.setWhatsThis(self.trUtf8( 3508 self.codeCoverageAct.setWhatsThis(self.trUtf8(
3381 """<b>Code Coverage...</b>""" 3509 """<b>Code Coverage...</b>"""
3382 """<p>This shows the code coverage information for all Python files""" 3510 """<p>This shows the code coverage information for all Python"""
3383 """ in the project.</p>""" 3511 """ files in the project.</p>"""
3384 )) 3512 ))
3385 self.codeCoverageAct.triggered[()].connect(self.__showCodeCoverage) 3513 self.codeCoverageAct.triggered[()].connect(self.__showCodeCoverage)
3386 self.actions.append(self.codeCoverageAct) 3514 self.actions.append(self.codeCoverageAct)
3387 3515
3388 self.codeProfileAct = E5Action(self.trUtf8('Profile Data'), 3516 self.codeProfileAct = E5Action(self.trUtf8('Profile Data'),
3397 self.codeProfileAct.triggered[()].connect(self.__showProfileData) 3525 self.codeProfileAct.triggered[()].connect(self.__showProfileData)
3398 self.actions.append(self.codeProfileAct) 3526 self.actions.append(self.codeProfileAct)
3399 3527
3400 self.graphicsGrp = createActionGroup(self) 3528 self.graphicsGrp = createActionGroup(self)
3401 3529
3402 self.applicationDiagramAct = E5Action(self.trUtf8('Application Diagram'), 3530 self.applicationDiagramAct = E5Action(
3403 self.trUtf8('&Application Diagram...'), 0, 0, 3531 self.trUtf8('Application Diagram'),
3404 self.graphicsGrp, 'project_application_diagram') 3532 self.trUtf8('&Application Diagram...'), 0, 0,
3533 self.graphicsGrp, 'project_application_diagram')
3405 self.applicationDiagramAct.setStatusTip( 3534 self.applicationDiagramAct.setStatusTip(
3406 self.trUtf8('Show a diagram of the project.')) 3535 self.trUtf8('Show a diagram of the project.'))
3407 self.applicationDiagramAct.setWhatsThis(self.trUtf8( 3536 self.applicationDiagramAct.setWhatsThis(self.trUtf8(
3408 """<b>Application Diagram...</b>""" 3537 """<b>Application Diagram...</b>"""
3409 """<p>This shows a diagram of the project.</p>""" 3538 """<p>This shows a diagram of the project.</p>"""
3410 )) 3539 ))
3411 self.applicationDiagramAct.triggered[()].connect(self.handleApplicationDiagram) 3540 self.applicationDiagramAct.triggered[()].connect(
3541 self.handleApplicationDiagram)
3412 self.actions.append(self.applicationDiagramAct) 3542 self.actions.append(self.applicationDiagramAct)
3413 3543
3414 self.loadDiagramAct = E5Action(self.trUtf8('Load Diagram'), 3544 self.loadDiagramAct = E5Action(self.trUtf8('Load Diagram'),
3415 self.trUtf8('&Load Diagram...'), 0, 0, 3545 self.trUtf8('&Load Diagram...'), 0, 0,
3416 self.graphicsGrp, 'project_load_diagram') 3546 self.graphicsGrp, 'project_load_diagram')
3431 self.pluginGrp, 'project_plugin_pkglist') 3561 self.pluginGrp, 'project_plugin_pkglist')
3432 self.pluginPkgListAct.setStatusTip( 3562 self.pluginPkgListAct.setStatusTip(
3433 self.trUtf8('Create an initial PKGLIST file for an eric5 plugin.')) 3563 self.trUtf8('Create an initial PKGLIST file for an eric5 plugin.'))
3434 self.pluginPkgListAct.setWhatsThis(self.trUtf8( 3564 self.pluginPkgListAct.setWhatsThis(self.trUtf8(
3435 """<b>Create Package List</b>""" 3565 """<b>Create Package List</b>"""
3436 """<p>This creates an initial list of files to include in an eric5 """ 3566 """<p>This creates an initial list of files to include in an"""
3437 """plugin archive. The list is created from the project file.</p>""" 3567 """ eric5 plugin archive. The list is created from the project"""
3568 """ file.</p>"""
3438 )) 3569 ))
3439 self.pluginPkgListAct.triggered[()].connect(self.__pluginCreatePkgList) 3570 self.pluginPkgListAct.triggered[()].connect(self.__pluginCreatePkgList)
3440 self.actions.append(self.pluginPkgListAct) 3571 self.actions.append(self.pluginPkgListAct)
3441 3572
3442 self.pluginArchiveAct = E5Action(self.trUtf8('Create Plugin Archive'), 3573 self.pluginArchiveAct = E5Action(self.trUtf8('Create Plugin Archive'),
3445 self.pluginGrp, 'project_plugin_archive') 3576 self.pluginGrp, 'project_plugin_archive')
3446 self.pluginArchiveAct.setStatusTip( 3577 self.pluginArchiveAct.setStatusTip(
3447 self.trUtf8('Create an eric5 plugin archive file.')) 3578 self.trUtf8('Create an eric5 plugin archive file.'))
3448 self.pluginArchiveAct.setWhatsThis(self.trUtf8( 3579 self.pluginArchiveAct.setWhatsThis(self.trUtf8(
3449 """<b>Create Plugin Archive</b>""" 3580 """<b>Create Plugin Archive</b>"""
3450 """<p>This creates an eric5 plugin archive file using the list of files """ 3581 """<p>This creates an eric5 plugin archive file using the list"""
3451 """given in the PKGLIST file. The archive name is built from the main """ 3582 """ of files given in the PKGLIST file. The archive name is"""
3452 """script name.</p>""" 3583 """ built from the main script name.</p>"""
3453 )) 3584 ))
3454 self.pluginArchiveAct.triggered[()].connect(self.__pluginCreateArchive) 3585 self.pluginArchiveAct.triggered[()].connect(self.__pluginCreateArchive)
3455 self.actions.append(self.pluginArchiveAct) 3586 self.actions.append(self.pluginArchiveAct)
3456 3587
3457 self.pluginSArchiveAct = E5Action(self.trUtf8('Create Plugin Archive (Snapshot)'), 3588 self.pluginSArchiveAct = E5Action(
3458 UI.PixmapCache.getIcon("pluginArchiveSnapshot.png"), 3589 self.trUtf8('Create Plugin Archive (Snapshot)'),
3459 self.trUtf8('Create Plugin Archive (&Snapshot)'), 0, 0, 3590 UI.PixmapCache.getIcon("pluginArchiveSnapshot.png"),
3460 self.pluginGrp, 'project_plugin_sarchive') 3591 self.trUtf8('Create Plugin Archive (&Snapshot)'), 0, 0,
3461 self.pluginSArchiveAct.setStatusTip( 3592 self.pluginGrp, 'project_plugin_sarchive')
3462 self.trUtf8('Create an eric5 plugin archive file (snapshot release).')) 3593 self.pluginSArchiveAct.setStatusTip(self.trUtf8(
3594 'Create an eric5 plugin archive file (snapshot release).'))
3463 self.pluginSArchiveAct.setWhatsThis(self.trUtf8( 3595 self.pluginSArchiveAct.setWhatsThis(self.trUtf8(
3464 """<b>Create Plugin Archive (Snapshot)</b>""" 3596 """<b>Create Plugin Archive (Snapshot)</b>"""
3465 """<p>This creates an eric5 plugin archive file using the list of files """ 3597 """<p>This creates an eric5 plugin archive file using the list"""
3466 """given in the PKGLIST file. The archive name is built from the main """ 3598 """ of files given in the PKGLIST file. The archive name is"""
3467 """script name. The version entry of the main script is modified to """ 3599 """ built from the main script name. The version entry of the"""
3468 """reflect a snapshot release.</p>""" 3600 """ main script is modified to reflect a snapshot release.</p>"""
3469 )) 3601 ))
3470 self.pluginSArchiveAct.triggered[()].connect(self.__pluginCreateSnapshotArchive) 3602 self.pluginSArchiveAct.triggered[()].connect(
3603 self.__pluginCreateSnapshotArchive)
3471 self.actions.append(self.pluginSArchiveAct) 3604 self.actions.append(self.pluginSArchiveAct)
3472 3605
3473 self.closeAct.setEnabled(False) 3606 self.closeAct.setEnabled(False)
3474 self.saveAct.setEnabled(False) 3607 self.saveAct.setEnabled(False)
3475 self.saveasAct.setEnabled(False) 3608 self.saveasAct.setEnabled(False)
3600 3733
3601 def initToolbar(self, toolbarManager): 3734 def initToolbar(self, toolbarManager):
3602 """ 3735 """
3603 Public slot to initialize the project toolbar. 3736 Public slot to initialize the project toolbar.
3604 3737
3605 @param toolbarManager reference to a toolbar manager object (E5ToolBarManager) 3738 @param toolbarManager reference to a toolbar manager object
3739 (E5ToolBarManager)
3606 @return the toolbar generated (QToolBar) 3740 @return the toolbar generated (QToolBar)
3607 """ 3741 """
3608 tb = QToolBar(self.trUtf8("Project"), self.ui) 3742 tb = QToolBar(self.trUtf8("Project"), self.ui)
3609 tb.setIconSize(UI.Config.ToolBarIconSize) 3743 tb.setIconSize(UI.Config.ToolBarIconSize)
3610 tb.setObjectName("ProjectToolbar") 3744 tb.setObjectName("ProjectToolbar")
3671 self.recentMenu.addSeparator() 3805 self.recentMenu.addSeparator()
3672 self.recentMenu.addAction(self.trUtf8('&Clear'), self.__clearRecent) 3806 self.recentMenu.addAction(self.trUtf8('&Clear'), self.__clearRecent)
3673 3807
3674 def __openRecent(self, act): 3808 def __openRecent(self, act):
3675 """ 3809 """
3676 Private method to open a project from the list of rencently opened projects. 3810 Private method to open a project from the list of rencently opened
3811 projects.
3677 3812
3678 @param act reference to the action that triggered (QAction) 3813 @param act reference to the action that triggered (QAction)
3679 """ 3814 """
3680 file = act.data() 3815 file = act.data()
3681 if file: 3816 if file:
3717 try: 3852 try:
3718 newSources = os.listdir(curpath) 3853 newSources = os.listdir(curpath)
3719 except OSError: 3854 except OSError:
3720 newSources = [] 3855 newSources = []
3721 if self.pdata["TRANSLATIONPATTERN"]: 3856 if self.pdata["TRANSLATIONPATTERN"]:
3722 pattern = self.pdata["TRANSLATIONPATTERN"][0].replace("%language%", "*") 3857 pattern = self.pdata["TRANSLATIONPATTERN"][0]\
3858 .replace("%language%", "*")
3723 else: 3859 else:
3724 pattern = "*.ts" 3860 pattern = "*.ts"
3725 binpattern = self.__binaryTranslationFile(pattern) 3861 binpattern = self.__binaryTranslationFile(pattern)
3726 for ns in newSources: 3862 for ns in newSources:
3727 # ignore hidden files and directories 3863 # ignore hidden files and directories
3754 for pattern in reversed(sorted(self.pdata["FILETYPES"].keys())): 3890 for pattern in reversed(sorted(self.pdata["FILETYPES"].keys())):
3755 if fnmatch.fnmatch(bfn, pattern): 3891 if fnmatch.fnmatch(bfn, pattern):
3756 filetype = self.pdata["FILETYPES"][pattern] 3892 filetype = self.pdata["FILETYPES"][pattern]
3757 break 3893 break
3758 3894
3759 if (filetype == "SOURCES" and fn not in self.pdata["SOURCES"]) or \ 3895 if (filetype == "SOURCES" and
3760 (filetype == "FORMS" and fn not in self.pdata["FORMS"]) or \ 3896 fn not in self.pdata["SOURCES"]) or \
3761 (filetype == "INTERFACES" and fn not in self.pdata["INTERFACES"]) or \ 3897 (filetype == "FORMS" and
3762 (filetype == "RESOURCES" and fn not in self.pdata["RESOURCES"]) or \ 3898 fn not in self.pdata["FORMS"]) or \
3899 (filetype == "INTERFACES" and
3900 fn not in self.pdata["INTERFACES"]) or \
3901 (filetype == "RESOURCES" and
3902 fn not in self.pdata["RESOURCES"]) or \
3763 (filetype == "OTHERS" and fn not in self.pdata["OTHERS"]): 3903 (filetype == "OTHERS" and fn not in self.pdata["OTHERS"]):
3764 if autoInclude and AI: 3904 if autoInclude and AI:
3765 self.appendFile(ns) 3905 self.appendFile(ns)
3766 else: 3906 else:
3767 newFiles.append(ns) 3907 newFiles.append(ns)
3768 elif filetype == "TRANSLATIONS" and fn not in self.pdata["TRANSLATIONS"]: 3908 elif filetype == "TRANSLATIONS" and \
3769 if fnmatch.fnmatch(ns, pattern) or fnmatch.fnmatch(ns, binpattern): 3909 fn not in self.pdata["TRANSLATIONS"]:
3910 if fnmatch.fnmatch(ns, pattern) or \
3911 fnmatch.fnmatch(ns, binpattern):
3770 if autoInclude and AI: 3912 if autoInclude and AI:
3771 self.appendFile(ns) 3913 self.appendFile(ns)
3772 else: 3914 else:
3773 newFiles.append(ns) 3915 newFiles.append(ns)
3774 3916
3800 for file in files: 3942 for file in files:
3801 self.appendFile(file) 3943 self.appendFile(file)
3802 3944
3803 def othersAdded(self, fn, updateModel=True): 3945 def othersAdded(self, fn, updateModel=True):
3804 """ 3946 """
3805 Public slot to be called, if something was added to the OTHERS project data area. 3947 Public slot to be called, if something was added to the OTHERS project
3948 data area.
3806 3949
3807 @param fn filename or directory name added (string) 3950 @param fn filename or directory name added (string)
3808 @param updateModel flag indicating an update of the model is requested (boolean) 3951 @param updateModel flag indicating an update of the model is requested
3952 (boolean)
3809 """ 3953 """
3810 self.projectOthersAdded.emit(fn) 3954 self.projectOthersAdded.emit(fn)
3811 updateModel and self.__model.addNewItem("OTHERS", fn) 3955 updateModel and self.__model.addNewItem("OTHERS", fn)
3812 3956
3813 def getActions(self): 3957 def getActions(self):
3871 def initVCS(self, vcsSystem=None, nooverride=False): 4015 def initVCS(self, vcsSystem=None, nooverride=False):
3872 """ 4016 """
3873 Public method used to instantiate a vcs system. 4017 Public method used to instantiate a vcs system.
3874 4018
3875 @param vcsSystem type of VCS to be used (string) 4019 @param vcsSystem type of VCS to be used (string)
3876 @param nooverride flag indicating to ignore an override request (boolean) 4020 @param nooverride flag indicating to ignore an override request
4021 (boolean)
3877 @return a reference to the vcs object 4022 @return a reference to the vcs object
3878 """ 4023 """
3879 vcs = None 4024 vcs = None
3880 forProject = True 4025 forProject = True
3881 override = False 4026 override = False
3910 if override: 4055 if override:
3911 # override failed, revert to original 4056 # override failed, revert to original
3912 QApplication.restoreOverrideCursor() 4057 QApplication.restoreOverrideCursor()
3913 E5MessageBox.critical(self.ui, 4058 E5MessageBox.critical(self.ui,
3914 self.trUtf8("Version Control System"), 4059 self.trUtf8("Version Control System"),
3915 self.trUtf8("<p>The selected VCS <b>{0}</b> could not be found." 4060 self.trUtf8(
3916 "<br/>Reverting override.</p><p>{1}</p>")\ 4061 "<p>The selected VCS <b>{0}</b> could not be found."
4062 "<br/>Reverting override.</p><p>{1}</p>")\
3917 .format(vcsSystem, msg)) 4063 .format(vcsSystem, msg))
3918 self.pudata["VCSOVERRIDE"] = [] 4064 self.pudata["VCSOVERRIDE"] = []
3919 return self.initVCS(nooverride=True) 4065 return self.initVCS(nooverride=True)
3920 4066
3921 QApplication.restoreOverrideCursor() 4067 QApplication.restoreOverrideCursor()
3922 E5MessageBox.critical(self.ui, 4068 E5MessageBox.critical(self.ui,
3923 self.trUtf8("Version Control System"), 4069 self.trUtf8("Version Control System"),
3924 self.trUtf8("<p>The selected VCS <b>{0}</b> could not be found.<br/>" 4070 self.trUtf8(
3925 "Disabling version control.</p><p>{1}</p>")\ 4071 "<p>The selected VCS <b>{0}</b> could not be"
3926 .format(vcsSystem, msg)) 4072 " found.<br/>Disabling version control.</p>"
4073 "<p>{1}</p>").format(vcsSystem, msg))
3927 vcs = None 4074 vcs = None
3928 if forProject: 4075 if forProject:
3929 self.pdata["VCS"][0] = 'None' 4076 self.pdata["VCS"][0] = 'None'
3930 self.setDirty(True) 4077 self.setDirty(True)
3931 else: 4078 else:
4027 self.codemetrics.show() 4174 self.codemetrics.show()
4028 self.codemetrics.prepare(files, self) 4175 self.codemetrics.prepare(files, self)
4029 4176
4030 def __showCodeCoverage(self): 4177 def __showCodeCoverage(self):
4031 """ 4178 """
4032 Private slot used to show the code coverage information for the project files. 4179 Private slot used to show the code coverage information for the
4180 project files.
4033 """ 4181 """
4034 fn = self.getMainScript(True) 4182 fn = self.getMainScript(True)
4035 if fn is None: 4183 if fn is None:
4036 E5MessageBox.critical(self.ui, 4184 E5MessageBox.critical(self.ui,
4037 self.trUtf8("Coverage Data"), 4185 self.trUtf8("Coverage Data"),
4169 """ 4317 """
4170 Private slot to load a diagram from file. 4318 Private slot to load a diagram from file.
4171 """ 4319 """
4172 from Graphics.UMLDialog import UMLDialog 4320 from Graphics.UMLDialog import UMLDialog
4173 self.loadedDiagram = None 4321 self.loadedDiagram = None
4174 loadedDiagram = UMLDialog(UMLDialog.NoDiagram, self, parent=self.parent()) 4322 loadedDiagram = UMLDialog(UMLDialog.NoDiagram,
4323 self, parent=self.parent())
4175 if loadedDiagram.load(): 4324 if loadedDiagram.load():
4176 self.loadedDiagram = loadedDiagram 4325 self.loadedDiagram = loadedDiagram
4177 self.loadedDiagram.show(fromFile=True) 4326 self.loadedDiagram.show(fromFile=True)
4178 4327
4179 ######################################################################### 4328 #########################################################################
4279 if not res: 4428 if not res:
4280 return # don't overwrite 4429 return # don't overwrite
4281 4430
4282 # build the list of entries 4431 # build the list of entries
4283 lst_ = [] 4432 lst_ = []
4284 for key in \ 4433 for key in ["SOURCES", "FORMS", "RESOURCES", "TRANSLATIONS",
4285 ["SOURCES", "FORMS", "RESOURCES", "TRANSLATIONS", "INTERFACES", "OTHERS"]: 4434 "INTERFACES", "OTHERS"]:
4286 lst_.extend(self.pdata[key]) 4435 lst_.extend(self.pdata[key])
4287 lst = [] 4436 lst = []
4288 for entry in lst_: 4437 for entry in lst_:
4289 if os.path.isdir(self.getAbsolutePath(entry)): 4438 if os.path.isdir(self.getAbsolutePath(entry)):
4290 lst.extend([self.getRelativePath(p) for p in 4439 lst.extend([self.getRelativePath(p) for p in
4301 if self.pdata["EOL"][0] == 0: 4450 if self.pdata["EOL"][0] == 0:
4302 newline = None 4451 newline = None
4303 else: 4452 else:
4304 newline = self.getEolString() 4453 newline = self.getEolString()
4305 pkglistFile = open(pkglist, "w", encoding="utf-8", newline=newline) 4454 pkglistFile = open(pkglist, "w", encoding="utf-8", newline=newline)
4306 pkglistFile.write("\n".join([Utilities.fromNativeSeparators(f) for f in lst])) 4455 pkglistFile.write(
4456 "\n".join([Utilities.fromNativeSeparators(f) for f in lst]))
4307 pkglistFile.write("\n") # ensure the file ends with an empty line 4457 pkglistFile.write("\n") # ensure the file ends with an empty line
4308 pkglistFile.close() 4458 pkglistFile.close()
4309 except IOError as why: 4459 except IOError as why:
4310 E5MessageBox.critical(self.ui, 4460 E5MessageBox.critical(self.ui,
4311 self.trUtf8("Create Package List"), 4461 self.trUtf8("Create Package List"),
4312 self.trUtf8("""<p>The file <b>PKGLIST</b> could not be created.</p>""" 4462 self.trUtf8(
4313 """<p>Reason: {0}</p>""").format(str(why))) 4463 """<p>The file <b>PKGLIST</b> could not be created.</p>"""
4464 """<p>Reason: {0}</p>""").format(str(why)))
4314 return 4465 return
4315 4466
4316 if not "PKGLIST" in self.pdata["OTHERS"]: 4467 if not "PKGLIST" in self.pdata["OTHERS"]:
4317 self.appendFile("PKGLIST") 4468 self.appendFile("PKGLIST")
4318 4469
4332 4483
4333 if len(self.pdata["MAINSCRIPT"]) == 0 or \ 4484 if len(self.pdata["MAINSCRIPT"]) == 0 or \
4334 len(self.pdata["MAINSCRIPT"][0]) == 0: 4485 len(self.pdata["MAINSCRIPT"][0]) == 0:
4335 E5MessageBox.critical(self.ui, 4486 E5MessageBox.critical(self.ui,
4336 self.trUtf8("Create Plugin Archive"), 4487 self.trUtf8("Create Plugin Archive"),
4337 self.trUtf8("""The project does not have a main script defined. """ 4488 self.trUtf8(
4338 """Aborting...""")) 4489 """The project does not have a main script defined. """
4490 """Aborting..."""))
4339 return 4491 return
4340 4492
4341 try: 4493 try:
4342 pkglistFile = open(pkglist, "r", encoding="utf-8") 4494 pkglistFile = open(pkglist, "r", encoding="utf-8")
4343 names = pkglistFile.read() 4495 names = pkglistFile.read()
4344 pkglistFile.close() 4496 pkglistFile.close()
4345 names = sorted(names.splitlines()) 4497 names = sorted(names.splitlines())
4346 except IOError as why: 4498 except IOError as why:
4347 E5MessageBox.critical(self.ui, 4499 E5MessageBox.critical(self.ui,
4348 self.trUtf8("Create Plugin Archive"), 4500 self.trUtf8("Create Plugin Archive"),
4349 self.trUtf8("""<p>The file <b>PKGLIST</b> could not be read.</p>""" 4501 self.trUtf8(
4350 """<p>Reason: {0}</p>""").format(str(why))) 4502 """<p>The file <b>PKGLIST</b> could not be read.</p>"""
4503 """<p>Reason: {0}</p>""").format(str(why)))
4351 return 4504 return
4352 4505
4353 archive = \ 4506 archive = os.path.join(
4354 os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0].replace(".py", ".zip")) 4507 self.ppath, self.pdata["MAINSCRIPT"][0].replace(".py", ".zip"))
4355 try: 4508 try:
4356 archiveFile = zipfile.ZipFile(archive, "w") 4509 archiveFile = zipfile.ZipFile(archive, "w")
4357 except IOError as why: 4510 except IOError as why:
4358 E5MessageBox.critical(self.ui, 4511 E5MessageBox.critical(self.ui,
4359 self.trUtf8("Create Plugin Archive"), 4512 self.trUtf8("Create Plugin Archive"),
4360 self.trUtf8("""<p>The eric5 plugin archive file <b>{0}</b> could """ 4513 self.trUtf8(
4361 """not be created.</p>""" 4514 """<p>The eric5 plugin archive file <b>{0}</b> could """
4362 """<p>Reason: {1}</p>""").format(archive, str(why))) 4515 """not be created.</p>"""
4516 """<p>Reason: {1}</p>""").format(archive, str(why)))
4363 return 4517 return
4364 4518
4365 for name in names: 4519 for name in names:
4366 if name: 4520 if name:
4367 try: 4521 try:
4368 self.__createZipDirEntries(os.path.split(name)[0], archiveFile) 4522 self.__createZipDirEntries(
4523 os.path.split(name)[0], archiveFile)
4369 if snapshot and name == self.pdata["MAINSCRIPT"][0]: 4524 if snapshot and name == self.pdata["MAINSCRIPT"][0]:
4370 snapshotSource, version = self.__createSnapshotSource( 4525 snapshotSource, version = self.__createSnapshotSource(
4371 os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0])) 4526 os.path.join(self.ppath,
4527 self.pdata["MAINSCRIPT"][0]))
4372 archiveFile.writestr(name, snapshotSource) 4528 archiveFile.writestr(name, snapshotSource)
4373 else: 4529 else:
4374 archiveFile.write(os.path.join(self.ppath, name), name) 4530 archiveFile.write(os.path.join(self.ppath, name), name)
4375 if name == self.pdata["MAINSCRIPT"][0]: 4531 if name == self.pdata["MAINSCRIPT"][0]:
4376 version = self.__pluginExtractVersion( 4532 version = self.__pluginExtractVersion(
4377 os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0])) 4533 os.path.join(self.ppath,
4534 self.pdata["MAINSCRIPT"][0]))
4378 except OSError as why: 4535 except OSError as why:
4379 E5MessageBox.critical(self.ui, 4536 E5MessageBox.critical(self.ui,
4380 self.trUtf8("Create Plugin Archive"), 4537 self.trUtf8("Create Plugin Archive"),
4381 self.trUtf8("""<p>The file <b>{0}</b> could not be stored """ 4538 self.trUtf8(
4382 """in the archive. Ignoring it.</p>""" 4539 """<p>The file <b>{0}</b> could not be stored """
4383 """<p>Reason: {1}</p>""")\ 4540 """in the archive. Ignoring it.</p>"""
4384 .format(os.path.join(self.ppath, name), str(why))) 4541 """<p>Reason: {1}</p>""")\
4542 .format(os.path.join(self.ppath, name), str(why)))
4385 archiveFile.writestr("VERSION", version.encode("utf-8")) 4543 archiveFile.writestr("VERSION", version.encode("utf-8"))
4386 archiveFile.close() 4544 archiveFile.close()
4387 4545
4388 if not archive in self.pdata["OTHERS"]: 4546 if not archive in self.pdata["OTHERS"]:
4389 self.appendFile(archive) 4547 self.appendFile(archive)
4390 4548
4391 if self.ui.notificationsEnabled(): 4549 if self.ui.notificationsEnabled():
4392 self.ui.showNotification(UI.PixmapCache.getPixmap("pluginArchive48.png"), 4550 self.ui.showNotification(
4551 UI.PixmapCache.getPixmap("pluginArchive48.png"),
4393 self.trUtf8("Create Plugin Archive"), 4552 self.trUtf8("Create Plugin Archive"),
4394 self.trUtf8("""<p>The eric5 plugin archive file <b>{0}</b> was """ 4553 self.trUtf8(
4395 """created successfully.</p>""")\ 4554 """<p>The eric5 plugin archive file <b>{0}</b> was """
4555 """created successfully.</p>""")\
4396 .format(os.path.basename(archive))) 4556 .format(os.path.basename(archive)))
4397 else: 4557 else:
4398 E5MessageBox.information(self.ui, 4558 E5MessageBox.information(self.ui,
4399 self.trUtf8("Create Plugin Archive"), 4559 self.trUtf8("Create Plugin Archive"),
4400 self.trUtf8("""<p>The eric5 plugin archive file <b>{0}</b> was """ 4560 self.trUtf8(
4401 """created successfully.</p>""").format(archive)) 4561 """<p>The eric5 plugin archive file <b>{0}</b> was """
4562 """created successfully.</p>""").format(archive))
4402 4563
4403 def __pluginCreateSnapshotArchive(self): 4564 def __pluginCreateSnapshotArchive(self):
4404 """ 4565 """
4405 Private slot to create an eric5 plugin archive snapshot release. 4566 Private slot to create an eric5 plugin archive snapshot release.
4406 """ 4567 """
4440 except (IOError, UnicodeError) as why: 4601 except (IOError, UnicodeError) as why:
4441 E5MessageBox.critical(self.ui, 4602 E5MessageBox.critical(self.ui,
4442 self.trUtf8("Create Plugin Archive"), 4603 self.trUtf8("Create Plugin Archive"),
4443 self.trUtf8("""<p>The plugin file <b>{0}</b> could """ 4604 self.trUtf8("""<p>The plugin file <b>{0}</b> could """
4444 """not be read.</p>""" 4605 """not be read.</p>"""
4445 """<p>Reason: {1}</p>""").format(filename, str(why))) 4606 """<p>Reason: {1}</p>""")
4607 .format(filename, str(why)))
4446 return b"", "" 4608 return b"", ""
4447 4609
4448 lineno = 0 4610 lineno = 0
4449 while lineno < len(sourcelines): 4611 while lineno < len(sourcelines):
4450 if sourcelines[lineno].startswith("version = "): 4612 if sourcelines[lineno].startswith("version = "):
4451 # found the line to modify 4613 # found the line to modify
4452 datestr = time.strftime("%Y%m%d") 4614 datestr = time.strftime("%Y%m%d")
4453 lineend = sourcelines[lineno].replace(sourcelines[lineno].rstrip(), "") 4615 lineend = sourcelines[lineno]\
4454 sversion = "{0}-snapshot-{1}".format( 4616 .replace(sourcelines[lineno].rstrip(), "")
4455 sourcelines[lineno].replace("version = ", "").strip()[1:-1], 4617 sversion = "{0}-snapshot-{1}".format(sourcelines[lineno]\
4618 .replace("version = ", "").strip()[1:-1],
4456 datestr) 4619 datestr)
4457 sourcelines[lineno] = '{0} + "-snapshot-{1}"{2}'.format( 4620 sourcelines[lineno] = '{0} + "-snapshot-{1}"{2}'.format(
4458 sourcelines[lineno].rstrip(), datestr, lineend) 4621 sourcelines[lineno].rstrip(), datestr, lineend)
4459 break 4622 break
4460 4623

eric ide

mercurial