Project/Project.py

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

eric ide

mercurial