Project/Project.py

changeset 0
de9c2efb9d02
child 4
ed9829a5fe55
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Project/Project.py	Mon Dec 28 16:03:33 2009 +0000
@@ -0,0 +1,4586 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the project management functionality.
+"""
+
+import os
+import sys
+import re
+import time
+import shutil
+import glob
+import fnmatch
+import copy
+import zipfile
+import cStringIO
+
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+from E4Gui.E4Application import e4App
+
+from Globals import recentNameProject
+
+from ProjectBrowserModel import ProjectBrowserModel
+
+from AddLanguageDialog import AddLanguageDialog
+from AddFileDialog import AddFileDialog
+from AddDirectoryDialog import AddDirectoryDialog
+from PropertiesDialog import PropertiesDialog
+from AddFoundFilesDialog import AddFoundFilesDialog
+from DebuggerPropertiesDialog import DebuggerPropertiesDialog
+from FiletypeAssociationDialog import FiletypeAssociationDialog
+from LexerAssociationDialog import LexerAssociationDialog
+from UserPropertiesDialog import UserPropertiesDialog
+
+from E4XML.XMLUtilities import make_parser
+from E4XML.XMLErrorHandler import XMLErrorHandler, XMLFatalParseError
+from E4XML.XMLEntityResolver import XMLEntityResolver
+
+from E4XML.ProjectHandler import ProjectHandler
+from E4XML.ProjectWriter import ProjectWriter
+from E4XML.UserProjectHandler import UserProjectHandler
+from E4XML.UserProjectWriter import UserProjectWriter
+from E4XML.SessionHandler import SessionHandler
+from E4XML.SessionWriter import SessionWriter
+from E4XML.TasksHandler import TasksHandler
+from E4XML.TasksWriter import TasksWriter
+from E4XML.DebuggerPropertiesHandler import DebuggerPropertiesHandler
+from E4XML.DebuggerPropertiesWriter import DebuggerPropertiesWriter
+
+import VCS
+from VCS.CommandOptionsDialog import vcsCommandOptionsDialog
+from VCS.ProjectHelper import VcsProjectHelper
+
+from Graphics.ApplicationDiagram import ApplicationDiagram
+
+from DataViews.CodeMetricsDialog import CodeMetricsDialog
+from DataViews.PyCoverageDialog import PyCoverageDialog
+from DataViews.PyProfileDialog import PyProfileDialog
+
+import UI.PixmapCache
+
+from E4Gui.E4Action import E4Action, createActionGroup
+
+import Preferences
+import Utilities
+
+from eric4config import getConfig
+
+class Project(QObject):
+    """
+    Class implementing the project management functionality.
+    
+    @signal dirty(int) emitted when the dirty state changes
+    @signal projectSessionLoaded() emitted after a project session file was loaded
+    @signal projectLanguageAdded(string) emitted after a new language was added
+    @signal projectLanguageAddedByCode(string) emitted after a new language was added.
+        The language code is sent by this signal.
+    @signal projectFormAdded(string) emitted after a new form was added
+    @signal projectSourceAdded(string) emitted after a new source file was added
+    @signal projectInterfaceAdded(string) emitted after a new IDL file was added
+    @signal projectResourceAdded(string) emitted after a new resource file was added
+    @signal projectAboutToBeCreated() emitted just before the project will be created
+    @signal newProjectHooks() emitted after a new project was generated but before
+            the newProject() signal is sent
+    @signal newProject() emitted after a new project was generated
+    @signal sourceFile(string) emitted after a project file was read to 
+            open the main script
+    @signal projectOpenedHooks() emitted after a project file was read but before the
+            projectOpened() signal is sent
+    @signal projectOpened() emitted after a project file was read
+    @signal projectClosedHooks() emitted after a project file was clsoed but before the
+            projectClosed() signal is sent
+    @signal projectClosed() emitted after a project was closed
+    @signal projectOthersAdded(string) emitted after a file or directory was added
+            to the OTHERS project data area
+    @signal projectFileRenamed(string, string) emitted after a file of the project
+            has been renamed
+    @signal projectPropertiesChanged() emitted after the project properties were changed
+    @signal directoryRemoved(string) emitted after a directory has been removed from
+            the project
+    @signal prepareRepopulateItem(string) emitted before an item of the model is
+            repopulated
+    @signal completeRepopulateItem(string) emitted after an item of the model was
+            repopulated
+    @signal vcsStatusMonitorStatus(QString, QString) emitted to signal the status of the
+            monitoring thread (ok, nok, op, off) and a status message
+    @signal reinitVCS() emitted after the VCS has been reinitialized
+    @signal showMenu(string, QMenu) emitted when a menu is about to be shown. The name
+            of the menu and a reference to the menu are given.
+    @signal lexerAssociationsChanged() emitted after the lexer associations have been
+            changed
+    """
+    keynames = [
+        "PROGLANGUAGE", "MIXEDLANGUAGE", "PROJECTTYPE",
+        "SPELLLANGUAGE", "SPELLWORDS", "SPELLEXCLUDES", 
+        "DESCRIPTION", "VERSION",
+        "AUTHOR", "EMAIL",
+        "SOURCES", "FORMS", "RESOURCES",
+        "TRANSLATIONS", "TRANSLATIONPATTERN", "TRANSLATIONSBINPATH", 
+        "TRANSLATIONEXCEPTIONS",
+        "MAINSCRIPT",
+        "VCS", "VCSOPTIONS", "VCSOTHERDATA",
+        "OTHERS", "INTERFACES", 
+        "FILETYPES", "LEXERASSOCS", 
+        "PROJECTTYPESPECIFICDATA", 
+        "DOCUMENTATIONPARMS", 
+        "PACKAGERSPARMS",
+        "CHECKERSPARMS",
+        "OTHERTOOLSPARMS", 
+    ]
+    
+    dbgKeynames = [
+        "INTERPRETER", "DEBUGCLIENT",
+        "ENVIRONMENTOVERRIDE", "ENVIRONMENTSTRING",
+        "REMOTEDEBUGGER", "REMOTEHOST", "REMOTECOMMAND",
+        "PATHTRANSLATION", "REMOTEPATH", "LOCALPATH",
+        "CONSOLEDEBUGGER", "CONSOLECOMMAND",
+        "REDIRECT", "NOENCODING",
+    ]
+    
+    userKeynames = [
+        "VCSOVERRIDE", "VCSSTATUSMONITORINTERVAL",
+    ]
+    
+    def __init__(self, parent = None, filename = None):
+        """
+        Constructor
+        
+        @param parent parent widget (usually the ui object) (QWidget)
+        @param filename optional filename of a project file to open (string)
+        """
+        QObject.__init__(self, parent)
+        
+        self.ui = parent
+        
+        self.sourceExtensions = {
+            "Python"  : ['.py', '.ptl', '.pyw'],
+            "Python3" : ['.py', '.pyw'],
+            "Ruby"    : ['.rb'],
+            "Mixed"   : ['.py', '.ptl', '.rb']
+        }
+        
+        self.dbgFilters = {
+            "Python"  : self.trUtf8(\
+                         "Python Files (*.py);;"
+                         "Python GUI Files (*.pyw);;"),
+            "Python3" : self.trUtf8(\
+                         "Python3 Files (*.py3);;"
+                         "Python3 GUI Files (*.pyw3);;"),
+            "Ruby"    : self.trUtf8("Ruby Files (*.rb);;"),
+        }
+        
+        self.vcsMenu = None
+        
+        self.__initProjectTypes()
+        
+        self.__initData()
+        
+        self.recent = []
+        self.__loadRecent()
+        
+        if filename is not None:
+            self.openProject(filename)
+        else:
+            self.vcs = self.initVCS()
+        
+        self.__model = ProjectBrowserModel(self)
+        
+        self.codemetrics        = None
+        self.codecoverage       = None
+        self.profiledata        = None
+        self.applicationDiagram = None
+        
+    def __initProjectTypes(self):
+        """
+        Private method to initialize the list of supported project types.
+        """
+        self.__projectTypes = {}
+        self.__fileTypeCallbacks = {}
+        self.__lexerAssociationCallbacks = {}
+        self.__binaryTranslationsCallbacks = {}
+        self.__projectTypes["Qt4"] = self.trUtf8("Qt4 GUI")
+        self.__projectTypes["Qt4C"] = self.trUtf8("Qt4 Console")
+        self.__projectTypes["E4Plugin"] = self.trUtf8("Eric4 Plugin")
+        self.__projectTypes["Console"] = self.trUtf8("Console")
+        self.__projectTypes["Other"] = self.trUtf8("Other")
+        try:
+            import PySide
+            self.__projectTypes["PySide"] = self.trUtf8("PySide GUI")
+            self.__projectTypes["PySideC"] = self.trUtf8("PySide Console")
+            del PySide
+        except ImportError:
+            pass
+        
+    def getProjectTypes(self):
+        """
+        Public method to get the list of supported project types.
+        
+        @return reference to the dictionary of project types.
+        """
+        return self.__projectTypes
+        
+    def hasProjectType(self, type_):
+        """
+        Public method to check, if a project type is already registered.
+        
+        @param type_ internal type designator to be unregistered (string)
+        """
+        return type_ in self.__projectTypes
+        
+    def registerProjectType(self, type_, description, fileTypeCallback = None, 
+        binaryTranslationsCallback = None, lexerAssociationCallback = None):
+        """
+        Public method to register a project type.
+        
+        @param type_ internal type designator to be registered (string)
+        @param description more verbose type name (display string) (string)
+        @keyparam fileTypeCallback reference to a method returning a dictionary
+            of filetype associations.
+        @keyparam binaryTranslationsCallback reference to a method returning the
+            name of the binary translation file given the name of the raw 
+            translation file
+        @keyparam lexerAssociationCallback reference to a method returning the
+            lexer type to be used for syntax highlighting given the name of
+            a file
+        """
+        if type_ in self.__projectTypes:
+            QMessageBox.critical(None,
+                self.trUtf8("Registering Project Type"),
+                self.trUtf8("""<p>The Project type <b>{0}</b> already exists.</p>""")\
+                    .format(type_)
+            )
+        else:
+            self.__projectTypes[type_] = description
+            self.__fileTypeCallbacks[type_] = fileTypeCallback
+            self.__lexerAssociationCallbacks[type_] = lexerAssociationCallback
+            self.__binaryTranslationsCallbacks[type_] = binaryTranslationsCallback
+        
+    def unregisterProjectType(self, type_):
+        """
+        Public method to unregister a project type.
+        
+        @param type_ internal type designator to be unregistered (string)
+        """
+        if type_ in self.__projectTypes:
+            del self.__projectTypes[type_]
+        if type_ in self.__fileTypeCallbacks:
+            del self.__fileTypeCallbacks[type_]
+        if type_ in self.__lexerAssociationCallbacks:
+            del self.__lexerAssociationCallbacks[type_]
+        if type_ in self.__binaryTranslationsCallbacks:
+            del self.__binaryTranslationsCallbacks[type_]
+        
+    def __initData(self):
+        """
+        Private method to initialize the project data part.
+        """
+        self.loaded = False     # flag for the loaded status
+        self.dirty = False      # dirty flag
+        self.pfile = ""         # name of the project file
+        self.ppath = ""         # name of the project directory
+        self.translationsRoot = ""  # the translations prefix
+        self.name = ""
+        self.opened = False
+        self.subdirs = [""] # record the project dir as a relative path (i.e. empty path)
+        self.otherssubdirs = []
+        self.vcs = None
+        self.dbgCmdline = ''
+        self.dbgWd = ''
+        self.dbgEnv = ''
+        self.dbgReportExceptions = True
+        self.dbgExcList = []
+        self.dbgExcIgnoreList = []
+        self.dbgAutoClearShell = True
+        self.dbgTracePython = False
+        self.dbgAutoContinue = True
+        
+        self.pdata = {}
+        for key in self.__class__.keynames:
+            self.pdata[key] = []
+        self.pdata["AUTHOR"] = ['']
+        self.pdata["EMAIL"] = ['']
+        self.pdata["PROGLANGUAGE"] = ["Python"]
+        self.pdata["MIXEDLANGUAGE"] = [False]
+        self.pdata["PROJECTTYPE"] = ["Qt4"]
+        self.pdata["SPELLLANGUAGE"] = \
+            [Preferences.getEditor("SpellCheckingDefaultLanguage")]
+        self.pdata["SPELLWORDS"] = ['']
+        self.pdata["SPELLEXCLUDES"] = ['']
+        self.pdata["FILETYPES"] = {}
+        self.pdata["LEXERASSOCS"] = {}
+        self.pdata["PROJECTTYPESPECIFICDATA"] = {}
+        self.pdata["CHECKERSPARMS"] = {}
+        self.pdata["PACKAGERSPARMS"] = {}
+        self.pdata["DOCUMENTATIONPARMS"] = {}
+        self.pdata["OTHERTOOLSPARMS"] = {}
+        
+        self.__initDebugProperties()
+        
+        self.pudata = {}
+        for key in self.__class__.userKeynames:
+            self.pudata[key] = []
+        
+        self.vcs = self.initVCS()
+        
+    def getData(self, category, key):
+        """
+        Public method to get data out of the project data store.
+        
+        @param category category of the data to get (string, one of
+            PROJECTTYPESPECIFICDATA, CHECKERSPARMS, PACKAGERSPARMS, DOCUMENTATIONPARMS
+            or OTHERTOOLSPARMS)
+        @param key key of the data entry to get (string).
+        @return a copy of the requested data or None
+        """
+        if category in ["PROJECTTYPESPECIFICDATA","CHECKERSPARMS", "PACKAGERSPARMS", 
+                        "DOCUMENTATIONPARMS", "OTHERTOOLSPARMS"] and \
+           key in self.pdata[category]:
+            return copy.deepcopy(self.pdata[category][key])
+        else:
+            return None
+        
+    def setData(self, category, key, data):
+        """
+        Public method to store data in the project data store.
+        
+        @param category category of the data to get (string, one of
+            PROJECTTYPESPECIFICDATA, CHECKERSPARMS, PACKAGERSPARMS, DOCUMENTATIONPARMS
+            or OTHERTOOLSPARMS)
+        @param key key of the data entry to get (string).
+        @param data data to be stored
+        @return flag indicating success (boolean)
+        """
+        if category not in ["PROJECTTYPESPECIFICDATA","CHECKERSPARMS", "PACKAGERSPARMS", 
+                            "DOCUMENTATIONPARMS", "OTHERTOOLSPARMS"]:
+            return False
+        
+        # test for changes of data and save them in the project
+        # 1. there were none, now there are
+        if key not in self.pdata[category] and len(data) > 0:
+            self.pdata[category][key] = copy.deepcopy(data)
+            self.setDirty(True)
+        # 2. there were some, now there aren't
+        elif key in self.pdata[category] and len(data) == 0:
+            del self.pdata[category][key]
+            self.setDirty(True)
+        # 3. there were some and still are
+        elif key in self.pdata[category] and len(data) > 0:
+            if data != self.pdata[category][key]:
+                self.pdata[category][key] = copy.deepcopy(data)
+                self.setDirty(True)
+        # 4. there were none and none are given
+        else:
+            return False
+        return True
+        
+    def initFileTypes(self):
+        """
+        Public method to initialize the filetype associations with default values.
+        """
+        self.pdata["FILETYPES"] = {}
+        if self.pdata["MIXEDLANGUAGE"][0]:
+            sourceKey = "Mixed"
+        else:
+            sourceKey = self.pdata["PROGLANGUAGE"][0]
+        for ext in self.sourceExtensions[sourceKey]:
+            self.pdata["FILETYPES"]["*%s" % ext] = "SOURCES"
+        self.pdata["FILETYPES"]["*.idl"] = "INTERFACES"
+        if self.pdata["PROJECTTYPE"][0] in ["Qt4", "E4Plugin", "PySide"]:
+            self.pdata["FILETYPES"]["*.ui"] = "FORMS"
+            self.pdata["FILETYPES"]["*.ui.h"] = "FORMS"
+        if self.pdata["PROJECTTYPE"][0] in ["Qt4", "Qt4C", "E4Plugin", 
+                                            "PySide", "PySideC"]:
+            self.pdata["FILETYPES"]["*.qrc"] = "RESOURCES"
+        if self.pdata["PROJECTTYPE"][0] in ["Qt4", "Qt4C", "E4Plugin", 
+                                            "PySide", "PySideC"]:
+            self.pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS"
+            self.pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS"
+        try:
+            if self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]] is not None:
+                ftypes = self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]]()
+                self.pdata["FILETYPES"].update(ftypes)
+        except KeyError:
+            pass
+        self.setDirty(True)
+        
+    def updateFileTypes(self):
+        """
+        Public method to update the filetype associations with new default values.
+        """
+        if self.pdata["PROJECTTYPE"][0] in ["Qt4", "Qt4C", "E4Plugin", 
+                                            "PySide", "PySideC"]:
+            if "*.ts" not in self.pdata["FILETYPES"]:
+                self.pdata["FILETYPES"]["*.ts"] = "TRANSLATIONS"
+            if "*.qm" not in self.pdata["FILETYPES"]:
+                self.pdata["FILETYPES"]["*.qm"] = "TRANSLATIONS"
+        try:
+            if self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]] is not None:
+                ftypes = self.__fileTypeCallbacks[self.pdata["PROJECTTYPE"][0]]()
+                for pattern, ftype in ftypes.items():
+                    if pattern not in self.pdata["FILETYPES"]:
+                        self.pdata["FILETYPES"][pattern] = ftype
+                        self.setDirty(True)
+        except KeyError:
+            pass
+        
+    def __loadRecent(self):
+        """
+        Private method to load the recently opened project filenames.
+        """
+        self.recent = []
+        Preferences.Prefs.rsettings.sync()
+        rp = Preferences.Prefs.rsettings.value(recentNameProject)
+        if rp.isValid():
+            for f in rp.toStringList():
+                if QFileInfo(f).exists():
+                    self.recent.append(f)
+    
+    def __saveRecent(self):
+        """
+        Private method to save the list of recently opened filenames.
+        """
+        Preferences.Prefs.rsettings.setValue(recentNameProject, QVariant(self.recent))
+        Preferences.Prefs.rsettings.sync()
+        
+    def getMostRecent(self):
+        """
+        Public method to get the most recently opened project.
+        
+        @return path of the most recently opened project (string)
+        """
+        if len(self.recent):
+            return self.recent[0]
+        else:
+            return None
+        
+    def getModel(self):
+        """
+        Public method to get a reference to the project browser model.
+        
+        @return reference to the project browser model (ProjectBrowserModel)
+        """
+        return self.__model
+        
+    def getVcs(self):
+        """
+        Public method to get a reference to the VCS object.
+        
+        @return reference to the VCS object
+        """
+        return self.vcs
+        
+    def handlePreferencesChanged(self):
+        """
+        Public slot used to handle the preferencesChanged signal.
+        """
+        if self.pudata["VCSSTATUSMONITORINTERVAL"]:
+            self.setStatusMonitorInterval(\
+                self.pudata["VCSSTATUSMONITORINTERVAL"][0])
+        else:
+            self.setStatusMonitorInterval(\
+                Preferences.getVCS("StatusMonitorInterval"))
+        
+        self.__model.preferencesChanged()
+        
+    def setDirty(self, b):
+        """
+        Public method to set the dirty state.
+        
+        It emits the signal dirty(int).
+        
+        @param b dirty state (boolean)
+        """
+        self.dirty = b
+        self.saveAct.setEnabled(b)
+        self.emit(SIGNAL("dirty"), bool(b))
+        
+    def isDirty(self):
+        """
+        Public method to return the dirty state.
+        
+        @return dirty state (boolean)
+        """
+        return self.dirty
+        
+    def isOpen(self):
+        """
+        Public method to return the opened state.
+        
+        @return open state (boolean)
+        """
+        return self.opened
+        
+    def __checkFilesExist(self, index):
+        """
+        Private method to check, if the files in a list exist. 
+        
+        The files in the indicated list are checked for existance in the
+        filesystem. Non existant files are removed from the list and the
+        dirty state of the project is changed accordingly.
+        
+        @param index key of the list to be checked (string)
+        """
+        removed = False
+        removelist = []
+        for file in self.pdata[index]:
+            if not os.path.exists(os.path.join(self.ppath, file)):
+                removelist.append(file)
+                removed = True
+                
+        if removed:
+            for file in removelist:
+                self.pdata[index].remove(file)
+            self.setDirty(True)
+        
+    def __readProject(self, fn):
+        """
+        Private method to read in a project (.e4p, .e4pz, .e3p, .e3pz) file.
+        
+        @param fn filename of the project file to be read (string)
+        @return flag indicating success
+        """
+        try:
+            if fn.lower().endswith("e4pz") or fn.lower().endswith("e3pz"):
+                try:
+                    import gzip
+                except ImportError:
+                    QApplication.restoreOverrideCursor()
+                    QMessageBox.critical(None,
+                        self.trUtf8("Read project file"),
+                        self.trUtf8("""Compressed project files not supported."""
+                                    """ The compression library is missing."""))
+                    return False
+                f = gzip.open(fn, "rb")
+            else:
+                f = open(fn, "rb")
+            line = f.readline()
+            dtdLine = f.readline()
+            f.close()
+        except EnvironmentError:
+            QApplication.restoreOverrideCursor()
+            QMessageBox.critical(None,
+                self.trUtf8("Read project file"),
+                self.trUtf8("<p>The project file <b>{0}</b> could not be read.</p>")\
+                    .format(fn))
+            return False
+            
+        self.pfile = os.path.abspath(fn)
+        self.ppath = os.path.abspath(os.path.dirname(fn))
+        
+        # insert filename into list of recently opened projects
+        self.__syncRecent()
+        
+        # now read the file
+        if not line.startswith('<?xml'):
+            QApplication.restoreOverrideCursor()
+            QMessageBox.critical(None,
+                self.trUtf8("Read project file"),
+                self.trUtf8("<p>The project file <b>{0}</b> has an unsupported"
+                            " format.</p>").format(fn))
+            return False
+            
+        res = self.__readXMLProject(fn, dtdLine.startswith("<!DOCTYPE"))
+        if res:
+            if len(self.pdata["TRANSLATIONPATTERN"]) == 1:
+                self.translationsRoot = \
+                    self.pdata["TRANSLATIONPATTERN"][0].split("%language%")[0]
+            elif len(self.pdata["MAINSCRIPT"]) == 1:
+                self.translationsRoot = os.path.splitext(self.pdata["MAINSCRIPT"][0])[0]
+            if os.path.isdir(os.path.join(self.ppath, self.translationsRoot)):
+                dn = self.translationsRoot
+            else:
+                dn = os.path.dirname(self.translationsRoot)
+            if dn not in self.subdirs:
+                self.subdirs.append(dn)
+                
+            self.name = os.path.splitext(os.path.basename(fn))[0]
+            
+            # check, if the files of the project still exist in the project directory
+            self.__checkFilesExist("SOURCES")
+            self.__checkFilesExist("FORMS")
+            self.__checkFilesExist("INTERFACES")
+            self.__checkFilesExist("TRANSLATIONS")
+            self.__checkFilesExist("RESOURCES")
+            self.__checkFilesExist("OTHERS")
+            
+            # get the names of subdirectories the files are stored in
+            for fn in self.pdata["SOURCES"] + \
+                      self.pdata["FORMS"] + \
+                      self.pdata["INTERFACES"] + \
+                      self.pdata["RESOURCES"] + \
+                      self.pdata["TRANSLATIONS"]:
+                dn = os.path.dirname(fn)
+                if dn not in self.subdirs:
+                    self.subdirs.append(dn)
+            
+            # get the names of other subdirectories
+            for fn in self.pdata["OTHERS"]:
+                dn = os.path.dirname(fn)
+                if dn not in self.otherssubdirs:
+                    self.otherssubdirs.append(dn)
+            
+            if self.pfile.endswith('e3p') or self.pfile.endswith('e3pz'):
+                # needs conversion to new format
+                self.setDirty(True)
+            
+        return res
+
+    def __readXMLProject(self, fn, validating):
+        """
+        Private method to read the project data from an XML file.
+        
+        @param fn filename of the project file to be read (string)
+        @param validating flag indicating a validation of the XML file is
+            requested (boolean)
+        @return flag indicating success (boolean)
+        """
+        if fn.lower().endswith("e4pz") or fn.lower().endswith("e3pz"):
+            # work around for a bug in xmlproc
+            validating = False
+        
+        parser = make_parser(validating)
+        handler = ProjectHandler(self)
+        er = XMLEntityResolver()
+        eh = XMLErrorHandler()
+        
+        parser.setContentHandler(handler)
+        parser.setEntityResolver(er)
+        parser.setErrorHandler(eh)
+        
+        try:
+            if fn.lower().endswith("e4pz") or fn.lower().endswith("e3pz"):
+                try:
+                    import gzip
+                except ImportError:
+                    QApplication.restoreOverrideCursor()
+                    QMessageBox.critical(None,
+                        self.trUtf8("Read project file"),
+                        self.trUtf8("""Compressed project files not supported."""
+                                    """ The compression library is missing."""))
+                    return False
+                f = gzip.open(fn, "rb")
+            else:
+                f = open(fn, "rb")
+            try:
+                try:
+                    parser.parse(f)
+                except UnicodeEncodeError:
+                    f.seek(0)
+                    buf = cStringIO.StringIO(f.read())
+                    parser.parse(buf)
+            finally:
+                f.close()
+        except IOError:
+            QApplication.restoreOverrideCursor()
+            QMessageBox.critical(None,
+                self.trUtf8("Read project file"),
+                self.trUtf8("<p>The project file <b>{0}</b> could not be read.</p>")\
+                    .format(fn))
+            return False
+        except XMLFatalParseError:
+            QApplication.restoreOverrideCursor()
+            QMessageBox.critical(None,
+                self.trUtf8("Read project file"),
+                self.trUtf8("<p>The project file <b>{0}</b> has invalid contents.</p>")\
+                    .format(fn))
+            eh.showParseMessages()
+            return False
+        
+        QApplication.restoreOverrideCursor()
+        eh.showParseMessages()
+        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
+        QApplication.processEvents()
+        return True
+        
+    def __writeProject(self, fn = None):
+        """
+        Private method to save the project infos to a project file.
+        
+        @param fn optional filename of the project file to be written (string).
+                If fn is None, the filename stored in the project object
+                is used. This is the 'save' action. If fn is given, this filename
+                is used instead of the one in the project object. This is the
+                'save as' action.
+        @return flag indicating success
+        """
+        if self.vcs is not None:
+            self.pdata["VCSOPTIONS"] = [copy.deepcopy(self.vcs.vcsGetOptions())]
+            self.pdata["VCSOTHERDATA"] = [copy.deepcopy(self.vcs.vcsGetOtherData())]
+        
+        if fn is None:
+            fn = self.pfile
+        
+        res = self.__writeXMLProject(fn)
+        
+        if res:
+            self.pfile = os.path.abspath(fn)
+            self.ppath = os.path.abspath(os.path.dirname(fn))
+            self.name = os.path.splitext(os.path.basename(fn))[0]
+            self.setDirty(False)
+            
+            # insert filename into list of recently opened projects
+            self.__syncRecent()
+        
+        return res
+        
+    def __writeXMLProject(self, fn = None):
+        """
+        Private method to write the project data to an XML file.
+        
+        @param fn the filename of the project file (string)
+        @return flag indicating success (boolean)
+        """
+        try:
+            if fn.lower().endswith("e4pz"):
+                try:
+                    import gzip
+                except ImportError:
+                    QMessageBox.critical(None,
+                        self.trUtf8("Save project file"),
+                        self.trUtf8("""Compressed project files not supported."""
+                                    """ The compression library is missing."""))
+                    return False
+                f = gzip.open(fn, "wb")
+            else:
+                f = open(fn, "wb")
+            
+            ProjectWriter(f, os.path.splitext(os.path.basename(fn))[0]).writeXML()
+            
+            f.close()
+            
+        except IOError:
+            QMessageBox.critical(None,
+                self.trUtf8("Save project file"),
+                self.trUtf8("<p>The project file <b>{0}</b> could not be written.</p>")\
+                    .format(fn))
+            return False
+        
+        return True
+        
+    def __readUserProperties(self):
+        """
+        Private method to read in the user specific project file (.e4q)
+        """
+        if self.pfile is None:
+            return
+        
+        fn, ext = os.path.splitext(os.path.basename(self.pfile))
+        
+        fn = os.path.join(self.getProjectManagementDir(), '%s.e4q' % fn)
+        if os.path.exists(fn):
+            try:
+                f = open(fn, "rb")
+                
+                parser = make_parser(True)
+                handler = UserProjectHandler(self)
+                er = XMLEntityResolver()
+                eh = XMLErrorHandler()
+                
+                parser.setContentHandler(handler)
+                parser.setEntityResolver(er)
+                parser.setErrorHandler(eh)
+                
+                try:
+                    try:
+                        parser.parse(f)
+                    except UnicodeEncodeError:
+                        f.seek(0)
+                        buf = cStringIO.StringIO(f.read())
+                        parser.parse(buf)
+                finally:
+                    f.close()
+            except IOError:
+                QMessageBox.critical(None,
+                    self.trUtf8("Read user project properties"),
+                    self.trUtf8("<p>The user specific project properties file <b>{0}</b>"
+                        " could not be read.</p>").format(fn))
+                return
+            except XMLFatalParseError:
+                pass
+            
+            eh.showParseMessages()
+        
+    def __writeUserProperties(self):
+        """
+        Private method to write the project data to an XML file.
+        """
+        if self.pfile is None or \
+           self.pfile.endswith('e3p') or \
+           self.pfile.endswith('e3pz'):
+            return
+        
+        fn, ext = os.path.splitext(os.path.basename(self.pfile))
+        
+        fn = os.path.join(self.getProjectManagementDir(), '%s.e4q' % fn)
+        
+        try:
+            f = open(fn, "wb")
+            
+            UserProjectWriter(f, os.path.splitext(os.path.basename(fn))[0]).writeXML()
+            
+            f.close()
+        except IOError:
+            QMessageBox.critical(None,
+                self.trUtf8("Save user project properties"),
+                self.trUtf8("<p>The user specific project properties file <b>{0}</b>"
+                    " could not be written.</p>").format(fn))
+        
+    def __readSession(self, quiet = False, indicator = ""):
+        """
+        Private method to read in the project session file (.e4s, .e3s)
+        
+        @param quiet flag indicating quiet operations.
+                If this flag is true, no errors are reported.
+        @keyparam indicator indicator string (string)
+        """
+        if self.pfile is None:
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Read project session"),
+                    self.trUtf8("Please save the project first."))
+            return
+            
+        fn, ext = os.path.splitext(os.path.basename(self.pfile))
+        
+        try:
+            if ext.lower() in [".e4pz", ".e3pz"]:
+                if ext.lower() == ".e3pz":
+                    fn = os.path.join(self.ppath, '%s.e3sz' % fn)
+                else:
+                    fn = os.path.join(self.getProjectManagementDir(), 
+                                      '%s%s.e4sz' % (fn, indicator))
+                try:
+                    import gzip
+                except ImportError:
+                    if not quiet:
+                        QMessageBox.critical(None,
+                            self.trUtf8("Read project session"),
+                            self.trUtf8("""Compressed project session files not"""
+                                """ supported. The compression library is missing."""))
+                    return
+                f = gzip.open(fn, "rb")
+            else:
+                if ext.lower() == ".e3p":
+                    fn = os.path.join(self.ppath, '%s.e3s' % fn)
+                else:
+                    fn = os.path.join(self.getProjectManagementDir(), 
+                                      '%s%s.e4s' % (fn, indicator))
+                f = open(fn, "rb")
+            line = f.readline()
+            dtdLine = f.readline()
+            f.close()
+        except EnvironmentError:
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Read project session"),
+                    self.trUtf8("<p>The project session <b>{0}</b>"
+                                " could not be read.</p>")\
+                        .format(fn))
+            return
+            
+        # now read the file
+        if line.startswith('<?xml'):
+            res = self.__readXMLSession(fn, dtdLine.startswith("<!DOCTYPE"), quiet)
+        else:
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Read project session"),
+                    self.trUtf8("<p>The project session <b>{0}</b> has an unsupported"
+                        " format.</p>").format(fn))
+    
+    def __readXMLSession(self, fn, validating, quiet):
+        """
+        Private method to read the session data from an XML file.
+        
+        The data read is:
+            <ul>
+            <li>all open source filenames</li>
+            <li>the active window</li>
+            <li>all breakpoints</li>
+            <li>the commandline</li>
+            <li>the working directory</li>
+            <li>the exception reporting flag</li>
+            <li>the list of exception types to be highlighted</li>
+            <li>all bookmarks</li>
+            </ul>
+            
+        @param fn filename of the project session file to be read (string)
+        @param validating flag indicating a validation of the XML file is
+            requested (boolean)
+        @param quiet flag indicating quiet operations.
+            If this flag is true, no errors are reported.
+        """
+        if fn.lower().endswith("e4sz") or fn.lower().endswith("e3sz"):
+            # work around for a bug in xmlproc
+            validating = False
+        
+        parser = make_parser(validating)
+        handler = SessionHandler(self)
+        er = XMLEntityResolver()
+        eh = XMLErrorHandler()
+        
+        parser.setContentHandler(handler)
+        parser.setEntityResolver(er)
+        parser.setErrorHandler(eh)
+        
+        try:
+            if fn.lower().endswith("e4sz") or fn.lower().endswith("e3sz"):
+                try:
+                    import gzip
+                except ImportError:
+                    if not quiet:
+                        QMessageBox.critical(None,
+                            self.trUtf8("Read project session"),
+                            self.trUtf8("<p>The project session <b>{0}</b> could not"
+                                " be read.</p>").format(fn))
+                    return
+                f = gzip.open(fn, "rb")
+            else:
+                f = open(fn, "rb")
+            try:
+                try:
+                    parser.parse(f)
+                except UnicodeEncodeError:
+                    f.seek(0)
+                    buf = cStringIO.StringIO(f.read())
+                    parser.parse(buf)
+            finally:
+                f.close()
+        except IOError:
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Read project session"),
+                    self.trUtf8("<p>The project session file <b>{0}</b> could not be"
+                        " read.</p>").format(fn))
+            return
+        except XMLFatalParseError:
+            pass
+        
+        if not quiet:
+            eh.showParseMessages()
+        
+    def __writeSession(self, quiet = False, indicator = ""):
+        """
+        Private method to write the session data to an XML file (.e4s).
+        
+        The data saved is:
+            <ul>
+            <li>all open source filenames belonging to the project</li>
+            <li>the active window, if it belongs to the project</li>
+            <li>all breakpoints</li>
+            <li>the commandline</li>
+            <li>the working directory</li>
+            <li>the exception reporting flag</li>
+            <li>the list of exception types to be highlighted</li>
+            <li>all bookmarks of files belonging to the project</li>
+            </ul>
+        
+        @param quiet flag indicating quiet operations.
+                If this flag is true, no errors are reported.
+        @keyparam indicator indicator string (string)
+        """
+        if self.pfile is None or \
+           self.pfile.endswith('e3p') or \
+           self.pfile.endswith('e3pz'):
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Save project session"),
+                    self.trUtf8("Please save the project first."))
+            return
+        
+        fn, ext = os.path.splitext(os.path.basename(self.pfile))
+        
+        try:
+            if ext.lower() == ".e4pz":
+                fn = os.path.join(self.getProjectManagementDir(), 
+                                  '%s%s.e4sz' % (fn, indicator))
+                try:
+                    import gzip
+                except ImportError:
+                    if not quiet:
+                        QMessageBox.critical(None,
+                            self.trUtf8("Save project session"),
+                            self.trUtf8("""Compressed project session files not"""
+                                """ supported. The compression library is missing."""))
+                    return
+                f = gzip.open(fn, "wb")
+            else:
+                fn = os.path.join(self.getProjectManagementDir(), 
+                                  '%s%s.e4s' % (fn, indicator))
+                f = open(fn, "wb")
+            
+            SessionWriter(f, os.path.splitext(os.path.basename(fn))[0]).writeXML()
+            
+            f.close()
+            
+        except IOError:
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Save project session"),
+                    self.trUtf8("<p>The project session file <b>{0}</b> could not be"
+                        " written.</p>").format(fn))
+        
+    def __deleteSession(self):
+        """
+        Private method to delete the session file.
+        """
+        if self.pfile is None:
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Delete project session"),
+                    self.trUtf8("Please save the project first."))
+            return
+            
+        fname, ext = os.path.splitext(os.path.basename(self.pfile))
+        
+        for fn in [os.path.join(self.getProjectManagementDir(), "%s.e4sz" % fname), 
+                   os.path.join(self.getProjectManagementDir(), "%s.e4s" % fname), 
+                   os.path.join(self.ppath, "%s.e3sz" % fname), 
+                   os.path.join(self.ppath, "%s.e3s" % fname)]:
+            if os.path.exists(fn):
+                try:
+                    os.remove(fn)
+                except OSError:
+                    QMessageBox.critical(None,
+                        self.trUtf8("Delete project session"),
+                        self.trUtf8("<p>The project session file <b>{0}</b> could not be"
+                            " deleted.</p>").format(fn))
+        
+    def __readTasks(self):
+        """
+        Private method to read in the project tasks file (.e4t, .e3t)
+        """
+        if self.pfile is None:
+            QMessageBox.critical(None,
+                self.trUtf8("Read tasks"),
+                self.trUtf8("Please save the project first."))
+            return
+            
+        fn, ext = os.path.splitext(os.path.basename(self.pfile))
+        
+        try:
+            if ext.lower() in [".e4pz", "*.e3pz"]:
+                if ext.lower() == ".e3pz":
+                    fn = os.path.join(self.ppath, '%s.e3tz' % fn)
+                else:
+                    fn = os.path.join(self.getProjectManagementDir(), '%s.e4tz' % fn)
+                if not os.path.exists(fn):
+                    return
+                try:
+                    import gzip
+                except ImportError:
+                    QMessageBox.critical(None,
+                        self.trUtf8("Read tasks"),
+                        self.trUtf8("""Compressed tasks files not supported."""
+                            """ The compression library is missing."""))
+                    return
+                f = gzip.open(fn, "rb")
+            else:
+                if ext.lower() == ".e3p":
+                    fn = os.path.join(self.ppath, '%s.e3t' % fn)
+                else:
+                    fn = os.path.join(self.getProjectManagementDir(), '%s.e4t' % fn)
+                if not os.path.exists(fn):
+                    return
+                f = open(fn, "rb")
+            line = f.readline()
+            dtdLine = f.readline()
+            f.close()
+        except EnvironmentError:
+            QMessageBox.critical(None,
+                self.trUtf8("Read tasks"),
+                self.trUtf8("<p>The tasks file <b>{0}</b> could not be read.</p>")\
+                    .format(fn))
+            return
+            
+        # now read the file
+        if line.startswith('<?xml'):
+            res = self.__readXMLTasks(fn, dtdLine.startswith("<!DOCTYPE"))
+        else:
+            QMessageBox.critical(None,
+                self.trUtf8("Read project session"),
+                self.trUtf8("<p>The tasks file <b>{0}</b> has an unsupported"
+                            " format.</p>")\
+                    .format(fn))
+    
+    def __readXMLTasks(self, fn, validating):
+        """
+        Private method to read the project tasks data from an XML file.
+        
+        @param fn filename of the project tasks file to be read (string)
+        @param validating flag indicating a validation of the XML file is
+            requested (boolean)
+        """
+        if fn.lower().endswith("e4tz") or fn.lower().endswith("e3tz"):
+            # work around for a bug in xmlproc
+            validating = False
+        
+        parser = make_parser(validating)
+        handler = TasksHandler(1)
+        er = XMLEntityResolver()
+        eh = XMLErrorHandler()
+        
+        parser.setContentHandler(handler)
+        parser.setEntityResolver(er)
+        parser.setErrorHandler(eh)
+        
+        try:
+            if fn.lower().endswith("e4tz") or fn.lower().endswith("e3tz"):
+                try:
+                    import gzip
+                except ImportError:
+                    QMessageBox.critical(None,
+                        self.trUtf8("Read tasks"),
+                        self.trUtf8("""Compressed tasks files not supported."""
+                            """ The compression library is missing."""))
+                    return
+                f = gzip.open(fn, "rb")
+            else:
+                f = open(fn, "rb")
+            try:
+                try:
+                    parser.parse(f)
+                except UnicodeEncodeError:
+                    f.seek(0)
+                    buf = cStringIO.StringIO(f.read())
+                    parser.parse(buf)
+            finally:
+                f.close()
+        except IOError:
+            QMessageBox.critical(None,
+                self.trUtf8("Read tasks"),
+                self.trUtf8("<p>The tasks file <b>{0}</b> could not be read.</p>")\
+                    .format(fn))
+            return
+        except XMLFatalParseError:
+            pass
+            
+        eh.showParseMessages()
+        
+    def __writeTasks(self):
+        """
+        Private method to write the tasks data to an XML file (.e4t).
+        """
+        if self.pfile is None:
+            return
+            
+        fn, ext = os.path.splitext(os.path.basename(self.pfile))
+        
+        try:
+            if ext.lower() == ".e4pz":
+                fn = os.path.join(self.getProjectManagementDir(), '%s.e4tz' % fn)
+                try:
+                    import gzip
+                except ImportError:
+                    QMessageBox.critical(None,
+                        self.trUtf8("Save tasks"),
+                        self.trUtf8("""Compressed tasks files not supported."""
+                            """ The compression library is missing."""))
+                    return
+                f = gzip.open(fn, "wb")
+            else:
+                fn = os.path.join(self.getProjectManagementDir(), '%s.e4t' % fn)
+                f = open(fn, "wb")
+            
+            TasksWriter(f, True, os.path.splitext(os.path.basename(fn))[0]).writeXML()
+            
+            f.close()
+            
+        except IOError:
+            QMessageBox.critical(None,
+                self.trUtf8("Save tasks"),
+                self.trUtf8("<p>The tasks file <b>{0}</b> could not be written.</p>")
+                    .format(fn))
+        
+    def __readDebugProperties(self, quiet = False):
+        """
+        Private method to read in the project debugger properties file (.e4d, .e3d)
+        
+        @param quiet flag indicating quiet operations.
+                If this flag is true, no errors are reported.
+        """
+        if self.pfile is None:
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Read debugger properties"),
+                    self.trUtf8("Please save the project first."))
+            return
+            
+        fn, ext = os.path.splitext(os.path.basename(self.pfile))
+        
+        try:
+            if ext.lower() in [".e4pz", "*.e3pz"]:
+                if ext.lower() == ".e3pz":
+                    fn = os.path.join(self.ppath, '%s.e3dz' % fn)
+                else:
+                    fn = os.path.join(self.getProjectManagementDir(), '%s.e4dz' % fn)
+                try:
+                    import gzip
+                except ImportError:
+                    if not quiet:
+                        QMessageBox.critical(None,
+                            self.trUtf8("Read debugger properties"),
+                            self.trUtf8("""Compressed project session files not"""
+                                        """ supported. The compression library is"""
+                                        """ missing."""))
+                    return
+                f = gzip.open(fn, "rb")
+            else:
+                if ext.lower() == ".e3p":
+                    fn = os.path.join(self.ppath, '%s.e3d' % fn)
+                else:
+                    fn = os.path.join(self.getProjectManagementDir(), '%s.e4d' % fn)
+                f = open(fn, "rb")
+            line = f.readline()
+            dtdLine = f.readline()
+            f.close()
+        except EnvironmentError:
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Read debugger properties"),
+                    self.trUtf8("<p>The project debugger properties file <b>{0}</b> could"
+                                " not be read.</p>").format(fn))
+            return
+            
+        # now read the file
+        if line.startswith('<?xml'):
+            self.__readXMLDebugProperties(fn, dtdLine.startswith("<!DOCTYPE"), quiet)
+        else:
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Read debugger properties"),
+                    self.trUtf8("<p>The project debugger properties file <b>{0}</b> has"
+                                " an unsupported format.</p>").format(fn))
+        
+    def __readXMLDebugProperties(self, fn, validating, quiet):
+        """
+        Public method to read the debugger properties from an XML file.
+        
+        @param fn filename of the project debugger properties file to be read
+            (string)
+        @param validating flag indicating a validation of the XML file is
+            requested (boolean)
+        @param quiet flag indicating quiet operations.
+            If this flag is true, no errors are reported.
+        """
+        if fn.lower().endswith("e4dz") or fn.lower().endswith("e3dz"):
+            # work around for a bug in xmlproc
+            validating = False
+        
+        parser = make_parser(validating)
+        handler = DebuggerPropertiesHandler(self)
+        er = XMLEntityResolver()
+        eh = XMLErrorHandler()
+        
+        parser.setContentHandler(handler)
+        parser.setEntityResolver(er)
+        parser.setErrorHandler(eh)
+        
+        try:
+            if fn.lower().endswith("e4dz") or fn.lower().endswith("e3dz"):
+                try:
+                    import gzip
+                except ImportError:
+                    if not quiet:
+                        QMessageBox.critical(None,
+                            self.trUtf8("Read debugger properties"),
+                            self.trUtf8("<p>The project debugger properties file"
+                                        " <b>{0}</b> could not be read.</p>").format(fn))
+                    return
+                f = gzip.open(fn, "rb")
+            else:
+                f = open(fn, "rb")
+            try:
+                try:
+                    parser.parse(f)
+                except UnicodeEncodeError:
+                    f.seek(0)
+                    buf = cStringIO.StringIO(f.read())
+                    parser.parse(buf)
+                if self.debugProperties["INTERPRETER"]:
+                    self.debugPropertiesLoaded = True
+                else:
+                    self.debugPropertiesLoaded = False
+            finally:
+                f.close()
+        except IOError:
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Read debugger properties"),
+                    self.trUtf8("<p>The project debugger properties file <b>{0}</b> could"
+                                " not be read.</p>")
+                        .format(fn))
+            return
+        except XMLFatalParseError:
+            pass
+            
+        if not quiet:
+            eh.showParseMessages()
+        
+    def __writeDebugProperties(self, quiet=0):
+        """
+        Private method to write the project debugger properties file (.e4d)
+        
+        @param quiet flag indicating quiet operations.
+                If this flag is true, no errors are reported.
+        """
+        if self.pfile is None or \
+           self.pfile.endswith('e3p') or \
+           self.pfile.endswith('e3pz'):
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Save debugger properties"),
+                    self.trUtf8("Please save the project first."))
+            return
+            
+        fn, ext = os.path.splitext(os.path.basename(self.pfile))
+        
+        try:
+            if ext.lower() == ".e4pz":
+                fn = os.path.join(self.getProjectManagementDir(), '%s.e4dz' % fn)
+                try:
+                    import gzip
+                except ImportError:
+                    if not quiet:
+                        QMessageBox.critical(None,
+                            self.trUtf8("Save debugger properties"),
+                            self.trUtf8("""Compressed project debugger properties files"""
+                                        """ not supported. The compression library is"""
+                                        """ missing."""))
+                    return
+                f = gzip.open(fn, "wb")
+            else:
+                fn = os.path.join(self.getProjectManagementDir(), '%s.e4d' % fn)
+                f = open(fn, "wb")
+            
+            DebuggerPropertiesWriter(f, os.path.splitext(os.path.basename(fn))[0])\
+                .writeXML()
+            
+            f.close()
+            
+        except IOError:
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Save debugger properties"),
+                    self.trUtf8("<p>The project debugger properties file <b>{0}</b> could"
+                                " not be written.</p>")
+                        .format(fn))
+        
+    def __deleteDebugProperties(self):
+        """
+        Private method to delete the project debugger properties file (.e3d)
+        """
+        if self.pfile is None:
+            if not quiet:
+                QMessageBox.critical(None,
+                    self.trUtf8("Delete debugger properties"),
+                    self.trUtf8("Please save the project first."))
+            return
+            
+        fname, ext = os.path.splitext(os.path.basename(self.pfile))
+        
+        for fn in [os.path.join(self.getProjectManagementDir(), "%s.e4dz" % fname), 
+                   os.path.join(self.getProjectManagementDir(), "%s.e4d" % fname), 
+                   os.path.join(self.ppath, "%s.e3dz" % fname), 
+                   os.path.join(self.ppath, "%s.e3d" % fname)]:
+            if os.path.exists(fn):
+                try:
+                    os.remove(fn)
+                except OSError:
+                    QMessageBox.critical(None,
+                        self.trUtf8("Delete debugger properties"),
+                        self.trUtf8("<p>The project debugger properties file <b>{0}</b>"
+                                    " could not be deleted.</p>")
+                            .format(fn))
+        
+    def __initDebugProperties(self):
+        """
+        Private method to initialize the debug properties.
+        """
+        self.debugPropertiesLoaded = False
+        self.debugProperties = {}
+        for key in self.__class__.dbgKeynames:
+            self.debugProperties[key] = ""
+        self.debugProperties["ENVIRONMENTOVERRIDE"] = False
+        self.debugProperties["REMOTEDEBUGGER"] = False
+        self.debugProperties["PATHTRANSLATION"] = False
+        self.debugProperties["CONSOLEDEBUGGER"] = False
+        self.debugProperties["REDIRECT"] = True
+        self.debugProperties["NOENCODING"] = False
+    
+    def isDebugPropertiesLoaded(self):
+        """
+        Public method to return the status of the debug properties.
+        
+        @return load status of debug properties (boolean)
+        """
+        return self.debugPropertiesLoaded
+        
+    def __showDebugProperties(self):
+        """
+        Private slot to display the debugger properties dialog.
+        """
+        dlg = DebuggerPropertiesDialog(self)
+        if dlg.exec_() == QDialog.Accepted:
+            dlg.storeData()
+        
+    def getDebugProperty(self, key):
+        """
+        Public method to retrieve a debugger property.
+        
+        @param key key of the property (string)
+        @return value of the property
+        """
+        return self.debugProperties[key]
+        
+    def setDbgInfo(self, argv, wd, env, excReporting, excList, excIgnoreList, 
+                   autoClearShell, tracePython = None, autoContinue = None):
+        """
+        Public method to set the debugging information.
+        
+        @param argv command line arguments to be used (string)
+        @param wd working directory (string)
+        @param env environment setting (string)
+        @param excReporting flag indicating the highlighting of exceptions
+        @param excList list of exceptions to be highlighted (list of strings)
+        @param excIgnoreList list of exceptions to be ignored (list of strings)
+        @param autoClearShell flag indicating, that the interpreter window
+            should be cleared (boolean)
+        @keyparam tracePython flag to indicate if the Python library should be
+            traced as well (boolean)
+        @keyparam autoContinue flag indicating, that the debugger should not stop
+            at the first executable line (boolean)
+        """
+        self.dbgCmdline = argv
+        self.dbgWd = wd
+        self.dbgEnv = env
+        self.dbgReportExceptions = excReporting
+        self.dbgExcList = excList[:]                # keep a copy of the list
+        self.dbgExcIgnoreList = excIgnoreList[:]    # keep a copy of the list
+        self.dbgAutoClearShell = autoClearShell
+        if tracePython is not None:
+            self.dbgTracePython = tracePython
+        if autoContinue is not None:
+            self.dbgAutoContinue = autoContinue
+    
+    def addLanguage(self):
+        """
+        Public slot used to add a language to the project.
+        """
+        if self.pdata["PROJECTTYPE"][0] in \
+                ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"] and \
+           (len(self.pdata["TRANSLATIONPATTERN"]) == 0 or \
+            self.pdata["TRANSLATIONPATTERN"][0] == ''):
+            QMessageBox.critical(None,
+                self.trUtf8("Add Language"),
+                self.trUtf8("You have to specify a translation pattern first."))
+            return
+        
+        dlg = AddLanguageDialog(self.parent())
+        if dlg.exec_() == QDialog.Accepted:
+            lang = dlg.getSelectedLanguage()
+            if self.pdata["PROJECTTYPE"][0] in \
+                    ["Qt4", "Qt4C", "E4Plugin", "PySide", "PySideC"]:
+                langFile = self.pdata["TRANSLATIONPATTERN"][0].replace("%language%", lang)
+                self.appendFile(langFile)
+            self.emit(SIGNAL("projectLanguageAddedByCode"), lang)
+        
+    def __binaryTranslationFile(self, langFile):
+        """
+        Private method to calculate the filename of the binary translations file
+        given the name of the raw translations file.
+        
+        @param langFile name of the raw translations file (string)
+        @return name of the binary translations file (string)
+        """
+        qmFile = ""
+        try:
+            if self.__binaryTranslationsCallbacks[self.pdata["PROJECTTYPE"][0]] \
+               is not None:
+                qmFile = self.__binaryTranslationsCallbacks\
+                         [self.pdata["PROJECTTYPE"][0]](langFile)
+        except KeyError:
+                qmFile = langFile.replace('.ts', '.qm')
+        if qmFile == langFile:
+            qmFile = ""
+        return qmFile
+        
+    def checkLanguageFiles(self):
+        """
+        Public slot to check the language files after a release process.
+        """
+        tbPath = self.pdata["TRANSLATIONSBINPATH"] and \
+                 self.pdata["TRANSLATIONSBINPATH"][0] or ""
+        for langFile in self.pdata["TRANSLATIONS"][:]:
+            qmFile = self.__binaryTranslationFile(langFile)
+            if qmFile:
+                if qmFile not in self.pdata["TRANSLATIONS"] and \
+                   os.path.exists(os.path.join(self.ppath, qmFile)):
+                    self.appendFile(qmFile)
+                if tbPath:
+                    qmFile = os.path.join(tbPath, os.path.basename(qmFile))
+                    if qmFile not in self.pdata["TRANSLATIONS"] and \
+                       os.path.exists(os.path.join(self.ppath, qmFile)):
+                        self.appendFile(qmFile)
+        
+    def removeLanguageFile(self, langFile):
+        """
+        Public slot to remove a translation from the project.
+        
+        The translation file is not deleted from the project directory.
+        
+        @param langFile the translation file to be removed (string)
+        """
+        langFile = langFile.replace(self.ppath + os.sep, '')
+        qmFile = self.__binaryTranslationFile(langFile)
+        self.pdata["TRANSLATIONS"].remove(langFile)
+        self.__model.removeItem(langFile)
+        if qmFile:
+            try:
+                if self.pdata["TRANSLATIONSBINPATH"]:
+                    qmFile = os.path.join(self.pdata["TRANSLATIONSBINPATH"][0],
+                        os.path.basename(qmFile)).replace(self.ppath + os.sep, '')
+                self.pdata["TRANSLATIONS"].remove(qmFile)
+                self.__model.removeItem(qmFile)
+            except ValueError:
+                pass
+        self.setDirty(True)
+        
+    def deleteLanguageFile(self, langFile):
+        """
+        Public slot to delete a translation from the project directory.
+        
+        @param langFile the translation file to be removed (string)
+        """
+        langFile = langFile.replace(self.ppath + os.sep, '')
+        qmFile = self.__binaryTranslationFile(langFile)
+        
+        try:
+            fn = os.path.join(self.ppath, langFile)
+            if os.path.exists(fn):
+                os.remove(fn)
+        except IOError:
+            QMessageBox.critical(None,
+                self.trUtf8("Delete translation"),
+                self.trUtf8("<p>The selected translation file <b>{0}</b> could not be"
+                    " deleted.</p>").format(langFile))
+            return
+        
+        self.removeLanguageFile(langFile)
+        
+        # now get rid of the .qm file
+        if qmFile:
+            try:
+                if self.pdata["TRANSLATIONSBINPATH"]:
+                    qmFile = os.path.join(self.pdata["TRANSLATIONSBINPATH"][0],
+                        os.path.basename(qmFile)).replace(self.ppath + os.sep, '')
+                fn = os.path.join(self.ppath, qmFile)
+                if os.path.exists(fn):
+                    os.remove(fn)
+            except IOError:
+                QMessageBox.critical(None,
+                    self.trUtf8("Delete translation"),
+                    self.trUtf8("<p>The selected translation file <b>{0}</b> could not be"
+                        " deleted.</p>").format(qmFile))
+                return
+        
+    def appendFile(self, fn, isSourceFile = False, updateModel = True):
+        """
+        Public method to append a file to the project.
+        
+        @param fn filename to be added to the project (string)
+        @param isSourceFile flag indicating that this is a source file
+                even if it doesn't have the source extension (boolean)
+        @param updateModel flag indicating an update of the model is requested (boolean)
+        """
+        dirty = False
+        
+        if os.path.isabs(fn):
+            # make it relative to the project root, if it starts with that path
+            newfn = fn.replace(self.ppath + os.sep, '')
+        else:
+            # assume relative paths are relative to the project root
+            newfn = fn
+        newdir = os.path.dirname(newfn)
+        
+        if isSourceFile:
+            filetype = "SOURCES"
+        else:
+            filetype = "OTHERS"
+            bfn = os.path.basename(newfn)
+            if fnmatch.fnmatch(bfn, '*.ts') or fnmatch.fnmatch(bfn, '*.qm'):
+                filetype = "TRANSLATIONS"
+            else:
+                for pattern in reversed(sorted(self.pdata["FILETYPES"].keys())):
+                    if fnmatch.fnmatch(bfn, pattern):
+                        filetype = self.pdata["FILETYPES"][pattern]
+                        break
+        
+        if filetype == "__IGNORE__":
+            return
+        
+        if filetype in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES"]:
+            if filetype == "SOURCES":
+                if newfn not in self.pdata["SOURCES"]:
+                    self.pdata["SOURCES"].append(newfn)
+                    self.emit(SIGNAL('projectSourceAdded'), newfn)
+                    updateModel and self.__model.addNewItem("SOURCES", newfn)
+                    dirty = True
+                else:
+                    updateModel and self.repopulateItem(newfn)
+            elif filetype == "FORMS":
+                if newfn not in self.pdata["FORMS"]:
+                    self.pdata["FORMS"].append(newfn)
+                    self.emit(SIGNAL('projectFormAdded'), newfn)
+                    updateModel and self.__model.addNewItem("FORMS", newfn)
+                    dirty = True
+                else:
+                    updateModel and self.repopulateItem(newfn)
+            elif filetype == "INTERFACES":
+                if newfn not in self.pdata["INTERFACES"]:
+                    self.pdata["INTERFACES"].append(newfn)
+                    self.emit(SIGNAL('projectInterfaceAdded'), newfn)
+                    updateModel and self.__model.addNewItem("INTERFACES", newfn)
+                    dirty = True
+                else:
+                    updateModel and self.repopulateItem(newfn)
+            elif filetype == "RESOURCES":
+                if newfn not in self.pdata["RESOURCES"]:
+                    self.pdata["RESOURCES"].append(newfn)
+                    self.emit(SIGNAL('projectResourceAdded'), newfn)
+                    updateModel and self.__model.addNewItem("RESOURCES", newfn)
+                    dirty = True
+                else:
+                    updateModel and self.repopulateItem(newfn)
+            if newdir not in self.subdirs:
+                self.subdirs.append(newdir)
+        elif filetype == "TRANSLATIONS":
+            if newfn not in self.pdata["TRANSLATIONS"]:
+                self.pdata["TRANSLATIONS"].append(newfn)
+                updateModel and self.__model.addNewItem("TRANSLATIONS", newfn)
+                self.emit(SIGNAL('projectLanguageAdded'), newfn)
+                dirty = True
+            else:
+                updateModel and self.repopulateItem(newfn)
+        else:   # filetype == "OTHERS"
+            if newfn not in self.pdata["OTHERS"]:
+                self.pdata['OTHERS'].append(newfn)
+                self.othersAdded(newfn, updateModel)
+                dirty = True
+            else:
+                updateModel and self.repopulateItem(newfn)
+            if newdir not in self.otherssubdirs:
+                self.otherssubdirs.append(newdir)
+        
+        if dirty:
+            self.setDirty(True)
+        
+    def addFiles(self, filter = None, startdir = None):
+        """
+        Public slot used to add files to the project.
+        
+        @param filter filter to be used by the add file dialog
+            (string out of source, form, resource, interface, others)
+        @param startdir start directory for the selection dialog
+        """
+        if startdir is None:
+            startdir = self.ppath
+        dlg = AddFileDialog(self, self.parent(), filter, startdir = startdir)
+        if dlg.exec_() == QDialog.Accepted:
+            fnames, target, isSource = dlg.getData()
+            if target != '':
+                for fn in fnames:
+                    ext = os.path.splitext(fn)[1]
+                    targetfile = os.path.join(target, os.path.basename(fn))
+                    if not Utilities.samepath(os.path.dirname(fn), target):
+                        try:
+                            if not os.path.isdir(target):
+                                os.makedirs(target)
+                                
+                            if os.path.exists(targetfile):
+                                res = QMessageBox.warning(None,
+                                    self.trUtf8("Add file"),
+                                    self.trUtf8("<p>The file <b>{0}</b> already"
+                                        " exists.</p><p>Overwrite it?</p>")
+                                        .format(targetfile),
+                                    QMessageBox.StandardButtons(\
+                                        QMessageBox.No | \
+                                        QMessageBox.Yes),
+                                    QMessageBox.No)
+                                if res != QMessageBox.Yes:
+                                    return  # don't overwrite
+                                    
+                            shutil.copy(fn, target)
+                        except IOError, why:
+                            QMessageBox.critical(None,
+                                self.trUtf8("Add file"),
+                                self.trUtf8("<p>The selected file <b>{0}</b> could not be"
+                                    " added to <b>{1}</b>.</p>")
+                                    .format(fn, target),
+                                QMessageBox.StandardButtons(\
+                                    QMessageBox.Abort))
+                            return
+                            
+                    self.appendFile(targetfile, isSource or filter == 'source')
+            else:
+                QMessageBox.critical(None,
+                    self.trUtf8("Add file"),
+                    self.trUtf8("The target directory must not be empty."),
+                    QMessageBox.StandardButtons(\
+                        QMessageBox.Abort))
+        
+    def __addSingleDirectory(self, filetype, source, target, quiet = False):
+        """
+        Private method used to add all files of a single directory to the project.
+        
+        @param filetype type of files to add (string)
+        @param source source directory (string)
+        @param target target directory (string)
+        @param quiet flag indicating quiet operations (boolean)
+        """
+        # get all relevant filename patterns
+        patterns = []
+        ignorePatterns = []
+        for pattern, patterntype in self.pdata["FILETYPES"].items():
+            if patterntype == filetype:
+                patterns.append(pattern)
+            elif patterntype == "__IGNORE__":
+                ignorePatterns.append(pattern)
+        
+        files = []
+        for pattern in patterns:
+            sstring = "%s%s%s" % (source, os.sep, pattern)
+            files.extend(glob.glob(sstring))
+        
+        if len(files) == 0:
+            if not quiet:
+                QMessageBox.information(None,
+                    self.trUtf8("Add directory"),
+                    self.trUtf8("<p>The source directory doesn't contain"
+                        " any files belonging to the selected category.</p>"))
+            return
+        
+        if not Utilities.samepath(target, source) and not os.path.isdir(target):
+            try:
+                os.makedirs(target)
+            except IOError, why:
+                QMessageBox.critical(None,
+                    self.trUtf8("Add directory"),
+                    self.trUtf8("<p>The target directory <b>{0}</b> could not be"
+                        " created.</p>")
+                        .format(target),
+                    QMessageBox.StandardButtons(\
+                        QMessageBox.Abort))
+                return
+        
+        for file in files:
+            for pattern in ignorePatterns:
+                if fnmatch.fnmatch(file, pattern):
+                    continue
+            
+            targetfile = os.path.join(target, os.path.basename(file))
+            if not Utilities.samepath(target, source):
+                try:
+                    if os.path.exists(targetfile):
+                        res = QMessageBox.warning(None,
+                            self.trUtf8("Add directory"),
+                            self.trUtf8("<p>The file <b>{0}</b> already exists.</p>"
+                                        "<p>Overwrite it?</p>")
+                                .format(targetfile),
+                            QMessageBox.StandardButtons(\
+                                QMessageBox.No | \
+                                QMessageBox.Yes),
+                            QMessageBox.No)
+                        if res != QMessageBox.Yes:
+                            continue  # don't overwrite, carry on with next file
+                            
+                    shutil.copy(file, target)
+                except EnvironmentError:
+                    continue
+            self.appendFile(targetfile)
+        
+    def __addRecursiveDirectory(self, filetype, source, target):
+        """
+        Private method used to add all files of a directory tree.
+        
+        The tree is rooted at source to another one rooted at target. This
+        method decents down to the lowest subdirectory.
+        
+        @param filetype type of files to add (string)
+        @param source source directory (string)
+        @param target target directory (string)
+        """
+        # first perform the addition of source
+        self.__addSingleDirectory(filetype, source, target, True)
+        
+        # now recurse into subdirectories
+        for name in os.listdir(source):
+            ns = os.path.join(source, name)
+            if os.path.isdir(ns):
+                nt = os.path.join(target, name)
+                self.__addRecursiveDirectory(filetype, ns, nt)
+        
+    def addDirectory(self, filter = None, startdir = None):
+        """
+        Public method used to add all files of a directory to the project.
+        
+        @param filter filter to be used by the add directory dialog
+            (string out of source, form, resource, interface, others)
+        @param startdir start directory for the selection dialog (string)
+        """
+        if startdir is None:
+            startdir = self.ppath
+        dlg = AddDirectoryDialog(self, filter, self.parent(), startdir = startdir)
+        if dlg.exec_() == QDialog.Accepted:
+            filetype, source, target, recursive = dlg.getData()
+            if target == '':
+                QMessageBox.critical(None,
+                    self.trUtf8("Add directory"),
+                    self.trUtf8("The target directory must not be empty."),
+                    QMessageBox.StandardButtons(\
+                        QMessageBox.Abort))
+                return
+            
+            if filetype == 'OTHERS':
+                self.__addToOthers(source)
+                return
+            
+            if source == '':
+                QMessageBox.critical(None,
+                    self.trUtf8("Add directory"),
+                    self.trUtf8("The source directory must not be empty."),
+                    QMessageBox.StandardButtons(\
+                        QMessageBox.Abort))
+                return
+            
+            if recursive:
+                self.__addRecursiveDirectory(filetype, source, target)
+            else:
+                self.__addSingleDirectory(filetype, source, target)
+        
+    def __addToOthers(self, fn):
+        """
+        Private method to add file/directory to the OTHERS project data.
+        
+        @param fn filename or directoryname to add
+        """
+        if fn:
+            # if it is below the project directory, make it relative to that
+            fn = fn.replace(self.ppath + os.sep, '')
+            
+            # if it ends with the directory separator character, remove it
+            if fn.endswith(os.sep):
+                fn = fn[:-1]
+            
+            if fn not in self.pdata["OTHERS"]:
+                self.pdata['OTHERS'].append(fn)
+                self.othersAdded(fn)
+                self.setDirty(True)
+            
+            if os.path.isdir(fn) and fn not in self.otherssubdirs:
+                self.otherssubdirs.append(fn)
+        
+    def addSourceFiles(self):
+        """
+        Public slot to add source files to the current project.
+        """
+        self.addFiles('source')
+        
+    def addUiFiles(self):
+        """
+        Public slot to add forms to the current project.
+        """
+        self.addFiles('form')
+        
+    def addIdlFiles(self):
+        """
+        Public slot to add IDL interfaces to the current project.
+        """
+        self.addFiles('interface')
+        
+    def addResourceFiles(self):
+        """
+        Public slot to add Qt resources to the current project.
+        """
+        self.addFiles('resource')
+        
+    def addOthersFiles(self):
+        """
+        Private slot to add files to the OTHERS project data.
+        """
+        self.addFiles('others')
+        
+    def addSourceDir(self):
+        """
+        Public slot to add all source files of a directory to the current project.
+        """
+        self.addDirectory('source')
+        
+    def addUiDir(self):
+        """
+        Public slot to add all forms of a directory to the current project.
+        """
+        self.addDirectory('form')
+        
+    def addIdlDir(self):
+        """
+        Public slot to add all IDL interfaces of a directory to the current project.
+        """
+        self.addDirectory('interface')
+        
+    def addResourceDir(self):
+        """
+        Public slot to add all Qt resource files of a directory to the current project.
+        """
+        self.addDirectory('resource')
+        
+    def addOthersDir(self):
+        """
+        Private slot to add a directory to the OTHERS project data.
+        """
+        self.addDirectory('others')
+        
+    def renameMainScript(self, oldfn, newfn):
+        """
+        Public method to rename the main script.
+        
+        @param oldfn old filename (string)
+        @param newfn new filename of the main script (string)
+        """
+        if self.pdata["MAINSCRIPT"]:
+            ofn = oldfn.replace(self.ppath + os.sep, '')
+            if ofn != self.pdata["MAINSCRIPT"][0]:
+                return
+            
+            fn = newfn.replace(self.ppath + os.sep, '')
+            self.pdata["MAINSCRIPT"] = [fn]
+            self.setDirty(True)
+        
+    def renameFile(self, oldfn, newfn = None):
+        """
+        Public slot to rename a file of the project.
+        
+        @param oldfn old filename of the file (string)
+        @param newfn new filename of the file (string)
+        @return flag indicating success
+        """
+        fn = oldfn.replace(self.ppath + os.sep, '')
+        isSourceFile = fn in self.pdata["SOURCES"]
+        
+        if newfn is None:
+            newfn = QFileDialog.getSaveFileName(\
+                None,
+                self.trUtf8("Rename file"),
+                os.path.dirname(oldfn),
+                "",
+                QFileDialog.Options(QFileDialog.DontConfirmOverwrite))
+            if not newfn:
+                return False
+        
+        if os.path.exists(newfn):
+            canceled = QMessageBox.warning(None,
+                self.trUtf8("Rename File"),
+                self.trUtf8("""<p>The file <b>{0}</b> already exists."""
+                            """ Overwrite it?</p>""")
+                    .format(newfn),
+                QMessageBox.StandardButtons(\
+                    QMessageBox.No | \
+                    QMessageBox.Yes),
+                QMessageBox.No)
+            if canceled != QMessageBox.Yes:
+                return False
+        
+        try:
+            os.rename(oldfn, newfn)
+        except OSError, msg:
+            QMessageBox.critical(None,
+                self.trUtf8("Rename File"),
+                self.trUtf8("""<p>The file <b>{0}</b> could not be renamed.<br />"""
+                    """Reason: {1}</p>""").format(oldfn, unicode(msg)))
+            return False
+
+        if fn in self.pdata["SOURCES"] or \
+           fn in self.pdata["FORMS"] or \
+           fn in self.pdata["TRANSLATIONS"] or \
+           fn in self.pdata["INTERFACES"] or \
+           fn in self.pdata["RESOURCES"] or \
+           fn in self.pdata["OTHERS"]:
+            self.renameFileInPdata(oldfn, newfn, isSourceFile)
+        
+        return True
+        
+    def renameFileInPdata(self, oldname, newname, isSourceFile = False):
+        """
+        Public method to rename a file in the pdata structure.
+        
+        @param oldname old filename (string)
+        @param newname new filename (string)
+        @param isSourceFile flag indicating that this is a source file
+                even if it doesn't have the source extension (boolean)
+        """
+        fn = oldname.replace(self.ppath + os.sep, '')
+        if os.path.dirname(oldname) == os.path.dirname(newname):
+            self.removeFile(oldname, False)
+            self.appendFile(newname, isSourceFile, False)
+            self.__model.renameItem(fn, newname)
+        else:
+            self.removeFile(oldname)
+            self.appendFile(newname, isSourceFile)
+        self.emit(SIGNAL('projectFileRenamed'), oldname, newname)
+        
+        self.renameMainScript(fn, newname)
+        
+    def getFiles(self, start):
+        """
+        Public method to get all files starting with a common prefix.
+        
+        @param start prefix (string)
+        """
+        filelist = []
+        start = start.replace(self.ppath + os.sep, '')
+        for key in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES", "OTHERS"]:
+            for entry in self.pdata[key][:]:
+                if entry.startswith(start):
+                    filelist.append(os.path.join(self.ppath, entry))
+        return filelist
+        
+    def copyDirectory(self, olddn, newdn):
+        """
+        Public slot to copy a directory.
+        
+        @param olddn original directory name (string)
+        @param newdn new directory name (string)
+        """
+        olddn = olddn.replace(self.ppath + os.sep, '')
+        newdn = newdn.replace(self.ppath + os.sep, '')
+        for key in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES", "OTHERS"]:
+            for entry in self.pdata[key][:]:
+                if entry.startswith(olddn):
+                    entry = entry.replace(olddn, newdn)
+                    self.appendFile(os.path.join(self.ppath, entry), key == "SOURCES")
+        self.setDirty(True)
+        
+    def moveDirectory(self, olddn, newdn):
+        """
+        Public slot to move a directory.
+        
+        @param olddn old directory name (string)
+        @param newdn new directory name (string)
+        """
+        olddn = olddn.replace(self.ppath + os.sep, '')
+        newdn = newdn.replace(self.ppath + os.sep, '')
+        typeStrings = []
+        for key in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES", "OTHERS"]:
+            for entry in self.pdata[key][:]:
+                if entry.startswith(olddn):
+                    if key not in typeStrings:
+                        typeStrings.append(key)
+                    self.pdata[key].remove(entry)
+                    entry = entry.replace(olddn, newdn)
+                    self.pdata[key].append(entry)
+            if key == "OTHERS":
+                if newdn not in self.otherssubdirs:
+                    self.otherssubdirs.append(newdn)
+            else:
+                if newdn not in self.subdirs:
+                    self.subdirs.append(newdn)
+        self.setDirty(True)
+        typeString = typeStrings[0]
+        del typeStrings[0]
+        self.__model.removeItem(olddn)
+        self.__model.addNewItem(typeString, newdn, typeStrings)
+        self.emit(SIGNAL('directoryRemoved'), olddn)
+        
+    def removeFile(self, fn, updateModel = True):
+        """
+        Public slot to remove a file from the project.
+        
+        The file is not deleted from the project directory.
+        
+        @param fn filename to be removed from the project
+        @param updateModel flag indicating an update of the model is requested (boolean)
+        """
+        fn = fn.replace(self.ppath + os.sep, '')
+        dirty = True
+        if fn in self.pdata["SOURCES"]:
+            self.pdata["SOURCES"].remove(fn)
+        elif fn in self.pdata["FORMS"]:
+            self.pdata["FORMS"].remove(fn)
+        elif fn in self.pdata["INTERFACES"]:
+            self.pdata["INTERFACES"].remove(fn)
+        elif fn in self.pdata["RESOURCES"]:
+            self.pdata["RESOURCES"].remove(fn)
+        elif fn in self.pdata["OTHERS"]:
+            self.pdata["OTHERS"].remove(fn)
+        elif fn in self.pdata["TRANSLATIONS"]:
+            self.pdata["TRANSLATIONS"].remove(fn)
+        else:
+            dirty = False
+        updateModel and self.__model.removeItem(fn)
+        if dirty:
+            self.setDirty(True)
+        
+    def removeDirectory(self, dn):
+        """
+        Public slot to remove a directory from the project.
+        
+        The directory is not deleted from the project directory.
+        
+        @param dn directory name to be removed from the project
+        """
+        dirty = False
+        dn = dn.replace(self.ppath + os.sep, '')
+        for entry in self.pdata["OTHERS"][:]:
+            if entry.startswith(dn):
+                self.pdata["OTHERS"].remove(entry)
+                dirty = True
+        if not dn.endswith(os.sep):
+            dn2 = dn + os.sep
+        else:
+            dn2 = dn
+        for key in ["SOURCES", "FORMS", "INTERFACES", "RESOURCES", ]:
+            for entry in self.pdata[key][:]:
+                if entry.startswith(dn2):
+                    self.pdata[key].remove(entry)
+                    dirty = True
+        self.__model.removeItem(dn)
+        if dirty:
+            self.setDirty(True)
+        self.emit(SIGNAL('directoryRemoved'), dn)
+        
+    def deleteFile(self, fn):
+        """
+        Public slot to delete a file from the project directory.
+        
+        @param fn filename to be deleted from the project
+        @return flag indicating success (boolean)
+        """
+        try:
+            os.remove(os.path.join(self.ppath, fn))
+            dummy, ext = os.path.splitext(fn)
+            if ext == '.ui':
+                fn2 = os.path.join(self.ppath, '%s.h' % fn)
+                if os.path.isfile(fn2):
+                    os.remove(fn2)
+        except EnvironmentError:
+            QMessageBox.critical(None,
+                self.trUtf8("Delete file"),
+                self.trUtf8("<p>The selected file <b>{0}</b> could not be deleted.</p>")
+                    .format(fn))
+            return False
+        
+        self.removeFile(fn)
+        if ext == '.ui':
+            self.removeFile(fn + '.h')
+        return True
+        
+    def deleteDirectory(self, dn):
+        """
+        Public slot to delete a directory from the project directory.
+        
+        @param dn directory name to be removed from the project
+        @return flag indicating success (boolean)
+        """
+        if not os.path.isabs(dn):
+            dn = os.path.join(self.ppath, dn)
+        try:
+            shutil.rmtree(dn, True)
+        except EnvironmentError:
+            QMessageBox.critical(None,
+                self.trUtf8("Delete directory"),
+                self.trUtf8("<p>The selected directory <b>{0}</b> could not be"
+                    " deleted.</p>").format(fn))
+            return False
+        
+        self.removeDirectory(dn)
+        return True
+    
+    def hasEntry(self, fn):
+        """
+        Public method to check the project for a file.
+        
+        @param fn filename to be checked (string)
+        @return flag indicating, if the project contains the file (boolean)
+        """
+        fn = fn.replace(self.ppath + os.sep, '')
+        if fn in self.pdata["SOURCES"] or \
+           fn in self.pdata["FORMS"] or \
+           fn in self.pdata["INTERFACES"] or \
+           fn in self.pdata["RESOURCES"] or \
+           fn in self.pdata["OTHERS"]:
+            return True
+        else:
+            return False
+        
+    def newProject(self):
+        """
+        Public slot to built a new project.
+        
+        This method displays the new project dialog and initializes
+        the project object with the data entered.
+        """
+        if not self.checkDirty():
+            return
+            
+        dlg = PropertiesDialog(self, True)
+        if dlg.exec_() == QDialog.Accepted:
+            self.closeProject()
+            dlg.storeData()
+            self.pdata["VCS"] = ['None']
+            self.opened = True
+            if not self.pdata["FILETYPES"]:
+                self.initFileTypes()
+            self.setDirty(True)
+            self.closeAct.setEnabled(True)
+            self.saveasAct.setEnabled(True)
+            self.actGrp2.setEnabled(True)
+            self.propsAct.setEnabled(True)
+            self.userPropsAct.setEnabled(True)
+            self.filetypesAct.setEnabled(True)
+            self.lexersAct.setEnabled(True)
+            self.sessActGrp.setEnabled(False)
+            self.dbgActGrp.setEnabled(True)
+            self.menuDebuggerAct.setEnabled(True)
+            self.menuSessionAct.setEnabled(False)
+            self.menuCheckAct.setEnabled(True)
+            self.menuShowAct.setEnabled(True)
+            self.menuDiagramAct.setEnabled(True)
+            self.menuApidocAct.setEnabled(True)
+            self.menuPackagersAct.setEnabled(True)
+            self.pluginGrp.setEnabled(self.pdata["PROJECTTYPE"][0] == "E4Plugin")
+            
+            self.emit(SIGNAL("projectAboutToBeCreated"))
+            
+            # create the project directory if it doesn't exist already
+            if not os.path.isdir(self.ppath):
+                try:
+                    os.makedirs(self.ppath)
+                except EnvironmentError:
+                    QMessageBox.critical(None,
+                        self.trUtf8("Create project directory"),
+                        self.trUtf8("<p>The project directory <b>{0}</b> could not"
+                            " be created.</p>")
+                            .format(self.ppath))
+                    self.vcs = self.initVCS()
+                    return
+                # create an empty __init__.py file to make it a Python package
+                # (only for Python and Python3)
+                if self.pdata["PROGLANGUAGE"][0] in ["Python", "Python3"]:
+                    fn = os.path.join(self.ppath, "__init__.py")
+                    f = open(fn, "w")
+                    f.close()
+                    self.appendFile(fn, True)
+                tpd = os.path.join(self.ppath, self.translationsRoot)
+                if not self.translationsRoot.endswith(os.sep):
+                    tpd = os.path.dirname(tpd)
+                if not os.path.isdir(tpd):
+                    os.makedirs(tpd)
+                if self.pdata["TRANSLATIONSBINPATH"]:
+                    tpd = os.path.join(self.ppath, self.pdata["TRANSLATIONSBINPATH"][0])
+                    if not os.path.isdir(tpd):
+                        os.makedirs(tpd)
+                
+                # create management directory if not present
+                mgmtDir = self.getProjectManagementDir()
+                if not os.path.exists(mgmtDir):
+                    os.makedirs(mgmtDir)
+                
+                self.saveProject()
+            else:
+                # create management directory if not present
+                mgmtDir = self.getProjectManagementDir()
+                if not os.path.exists(mgmtDir):
+                    os.makedirs(mgmtDir)
+                
+                try:
+                    ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0])
+                    if os.path.exists(ms):
+                        self.appendFile(ms)
+                except IndexError:
+                    ms = ""
+                
+                # add existing files to the project
+                res = QMessageBox.question(None,
+                    self.trUtf8("New Project"),
+                    self.trUtf8("""Add existing files to the project?"""),
+                    QMessageBox.StandardButtons(\
+                        QMessageBox.No | \
+                        QMessageBox.Yes),
+                    QMessageBox.Yes)
+                if res == QMessageBox.Yes:
+                    self.newProjectAddFiles(ms)
+                # create an empty __init__.py file to make it a Python package
+                # if none exists (only for Python and Python3)
+                if self.pdata["PROGLANGUAGE"][0] in ["Python", "Python3"]:
+                    fn = os.path.join(self.ppath, "__init__.py")
+                    if not os.path.exists(fn):
+                        f = open(fn, "w")
+                        f.close()
+                        self.appendFile(fn, True)
+                self.saveProject()
+                
+                # check, if the existing project directory is already under
+                # VCS control
+                pluginManager = e4App().getObject("PluginManager")
+                for indicator, vcsData in pluginManager.getVcsSystemIndicators().items():
+                    if os.path.exists(os.path.join(self.ppath, indicator)):
+                        if len(vcsData) > 1:
+                            vcsList = []
+                            for vcsSystemStr, vcsSystemDisplay in vcsData:
+                                vcsList.append(vcsSystemDisplay)
+                            res, vcs_ok = QInputDialog.getItem(\
+                                None,
+                                self.trUtf8("New Project"),
+                                self.trUtf8("Select Version Control System"),
+                                vcsList,
+                                0, False)
+                            if vcs_ok:
+                                for vcsSystemStr, vcsSystemDisplay in vcsData:
+                                    if res == vcsSystemDisplay:
+                                        vcsSystem = vcsSystemStr
+                                        break
+                                else:
+                                    vcsSystem = "None"
+                            else:
+                                vcsSystem = "None"
+                        else:
+                            vcsSystem = vcsData[0][1]
+                        self.pdata["VCS"] = [vcsSystem]
+                        self.vcs = self.initVCS()
+                        self.setDirty(True)
+                        if self.vcs is not None:
+                            # edit VCS command options
+                            vcores = QMessageBox.question(None,
+                                self.trUtf8("New Project"),
+                                self.trUtf8("""Would you like to edit the VCS"""
+                                    """ command options?"""),
+                                QMessageBox.StandardButtons(\
+                                    QMessageBox.No | \
+                                    QMessageBox.Yes),
+                                QMessageBox.No)
+                            if vcores == QMessageBox.Yes:
+                                codlg = vcsCommandOptionsDialog(self.vcs)
+                                if codlg.exec_() == QDialog.Accepted:
+                                    self.vcs.vcsSetOptions(codlg.getOptions())
+                            # add project file to repository
+                            if res == 0:
+                                apres = QMessageBox.question(None,
+                                    self.trUtf8("New project"),
+                                    self.trUtf8("Shall the project file be added"
+                                        " to the repository?"),
+                                    QMessageBox.StandardButtons(\
+                                        QMessageBox.No | \
+                                        QMessageBox.Yes),
+                                    QMessageBox.Yes)
+                                if apres == QMessageBox.Yes:
+                                    self.saveProject()
+                                    self.vcs.vcsAdd(self.pfile)
+                        else:
+                            self.pdata["VCS"] = ['None']
+                        self.saveProject()
+                        break
+            
+            # put the project under VCS control
+            if self.vcs is None:
+                vcsSystemsDict = e4App().getObject("PluginManager")\
+                    .getPluginDisplayStrings("version_control")
+                vcsSystemsDisplay = [self.trUtf8("None")]
+                keys = sorted(vcsSystemsDict.keys())
+                for key in keys:
+                    vcsSystemsDisplay.append(vcsSystemsDict[key])
+                vcsSelected, ok = QInputDialog.getItem(\
+                    None,
+                    self.trUtf8("New Project"),
+                    self.trUtf8("Select version control system for the project"),
+                    vcsSystemsDisplay,
+                    0, False)
+                if ok and vcsSelected != self.trUtf8("None"):
+                    for vcsSystem, vcsSystemDisplay in vcsSystemsDict.items():
+                        if vcsSystemDisplay == vcsSelected:
+                            break
+                    else:
+                        vcsSystem = "None"
+                    self.pdata["VCS"] = [vcsSystem]
+                else:
+                    self.pdata["VCS"] = ['None']
+                self.vcs = self.initVCS()
+                if self.vcs is not None:
+                    vcsdlg = self.vcs.vcsOptionsDialog(self, self.name)
+                    if vcsdlg.exec_() == QDialog.Accepted:
+                        vcsDataDict = vcsdlg.getData()
+                    else:
+                        self.pdata["VCS"] = ['None']
+                        self.vcs = self.initVCS()
+                self.setDirty(True)
+                if self.vcs is not None:
+                    # edit VCS command options
+                    vcores = QMessageBox.question(None,
+                        self.trUtf8("New Project"),
+                        self.trUtf8("""Would you like to edit the VCS command"""
+                                    """ options?"""),
+                        QMessageBox.StandardButtons(\
+                            QMessageBox.No | \
+                            QMessageBox.Yes),
+                        QMessageBox.No)
+                    if vcores == QMessageBox.Yes:
+                        codlg = vcsCommandOptionsDialog(self.vcs)
+                        if codlg.exec_() == QDialog.Accepted:
+                            self.vcs.vcsSetOptions(codlg.getOptions())
+                    
+                    # create the project in the VCS
+                    self.vcs.vcsSetDataFromDict(vcsDataDict)
+                    self.saveProject()
+                    self.vcs.vcsConvertProject(vcsDataDict, self)
+                else:
+                    self.emit(SIGNAL('newProjectHooks'))
+                    self.emit(SIGNAL('newProject'))
+            
+            else:
+                self.emit(SIGNAL('newProjectHooks'))
+                self.emit(SIGNAL('newProject'))
+            
+
+    def newProjectAddFiles(self, mainscript):
+        """
+        Public method to add files to a new project.
+        
+        @param mainscript name of the mainscript (string)
+        """
+        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
+        QApplication.processEvents()
+        
+        # search the project directory for files with known extensions
+        filespecs = self.pdata["FILETYPES"].keys()
+        for filespec in filespecs:
+            files = Utilities.direntries(self.ppath, True, filespec)
+            for file in files:
+                self.appendFile(file)
+        
+        # special handling for translation files
+        if self.translationsRoot:
+            tpd = os.path.join(self.ppath, self.translationsRoot)
+            if not self.translationsRoot.endswith(os.sep):
+                tpd = os.path.dirname(tpd)
+        else:
+            tpd = self.ppath
+        tslist = []
+        if self.pdata["TRANSLATIONPATTERN"]:
+            pattern = os.path.basename(self.pdata["TRANSLATIONPATTERN"][0])
+            if "%language%" in pattern:
+                pattern = pattern.replace("%language%", "*")
+            else:
+                tpd = self.pdata["TRANSLATIONPATTERN"][0].split("%language%")[0]
+        else:
+            pattern = "*.ts"
+        tslist.extend(Utilities.direntries(tpd, True, pattern))
+        pattern = self.__binaryTranslationFile(pattern)
+        if pattern:
+            tslist.extend(Utilities.direntries(tpd, True, pattern))
+        if tslist:
+            if '_' in os.path.basename(tslist[0]):
+                # the first entry determines the mainscript name
+                mainscriptname = os.path.splitext(mainscript)[0] or \
+                                 os.path.basename(tslist[0]).split('_')[0]
+                self.pdata["TRANSLATIONPATTERN"] = \
+                    [os.path.join(os.path.dirname(tslist[0]), 
+                     "%s_%%language%%%s" % (os.path.basename(tslist[0]).split('_')[0], 
+                        os.path.splitext(tslist[0])[1]))]
+            else:
+                pattern, ok = QInputDialog.getText(\
+                    None,
+                    self.trUtf8("Translation Pattern"),
+                    self.trUtf8("Enter the path pattern for translation files "
+                                "(use '%language%' in place of the language code):"),
+                    QLineEdit.Normal, 
+                    tslist[0])
+                if not pattern.isEmpty:
+                    self.pdata["TRANSLATIONPATTERN"] = [pattern]
+            self.pdata["TRANSLATIONPATTERN"][0] = \
+                self.pdata["TRANSLATIONPATTERN"][0].replace(self.ppath + os.sep, "")
+            pattern = self.pdata["TRANSLATIONPATTERN"][0].replace("%language%", "*")
+            for ts in tslist:
+                if fnmatch.fnmatch(ts, pattern):
+                    self.pdata["TRANSLATIONS"].append(ts)
+                    self.emit(SIGNAL('projectLanguageAdded'), ts)
+            if self.pdata["PROGLANGUAGE"][0] in ["Python", "Python3"]:
+                self.pdata["MAINSCRIPT"] = ['%s.py' % mainscriptname]
+            elif self.pdata["PROGLANGUAGE"][0] == "Ruby":
+                self.pdata["MAINSCRIPT"] = ['%s.rb' % mainscriptname]
+            if self.pdata["TRANSLATIONSBINPATH"]:
+                tpd = os.path.join(self.ppath, 
+                                   self.pdata["TRANSLATIONSBINPATH"][0])
+                pattern = os.path.splitext(
+                    os.path.basename(self.pdata["TRANSLATIONPATTERN"][0]))
+                pattern = self.__binaryTranslationFile(pattern)
+                qmlist = Utilities.direntries(tpd, True, pattern)
+                for qm in qmlist:
+                    self.pdata["TRANSLATIONS"].append(qm)
+                    self.emit(SIGNAL('projectLanguageAdded'), qm)
+        self.setDirty(True)
+        QApplication.restoreOverrideCursor()
+    
+    def __showProperties(self):
+        """
+        Private slot to display the properties dialog.
+        """
+        dlg = PropertiesDialog(self, False)
+        if dlg.exec_() == QDialog.Accepted:
+            projectType = self.pdata["PROJECTTYPE"][0]
+            dlg.storeData()
+            self.setDirty(True)
+            try:
+                ms = os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0])
+                if os.path.exists(ms):
+                    self.appendFile(ms)
+            except IndexError:
+                pass
+            
+            if self.pdata["PROJECTTYPE"][0] != projectType:
+                # reinitialize filetype associations
+                self.initFileTypes()
+            
+            if self.translationsRoot:
+                tp = os.path.join(self.ppath, self.translationsRoot)
+                if not self.translationsRoot.endswith(os.sep):
+                    tp = os.path.dirname(tp)
+            else:
+                tp = self.ppath
+            if not os.path.isdir(tp):
+                os.makedirs(tp)
+            if tp != self.ppath and tp not in self.subdirs:
+                self.subdirs.append(tp)
+            
+            if self.pdata["TRANSLATIONSBINPATH"]:
+                tp = os.path.join(self.ppath, self.pdata["TRANSLATIONSBINPATH"][0])
+                if not os.path.isdir(tp):
+                    os.makedirs(tp)
+                if tp != self.ppath and tp not in self.subdirs:
+                    self.subdirs.append(tp)
+            
+            self.pluginGrp.setEnabled(self.pdata["PROJECTTYPE"][0] == "E4Plugin")
+            
+            self.__model.projectPropertiesChanged()
+            self.emit(SIGNAL('projectPropertiesChanged'))
+        
+    def __showUserProperties(self):
+        """
+        Private slot to display the user specific properties dialog.
+        """
+        vcsSystem = self.pdata["VCS"] and self.pdata["VCS"][0] or None
+        vcsSystemOverride = \
+            self.pudata["VCSOVERRIDE"] and self.pudata["VCSOVERRIDE"][0] or None
+        
+        dlg = UserPropertiesDialog(self)
+        if dlg.exec_() == QDialog.Accepted:
+            dlg.storeData()
+            
+            if (self.pdata["VCS"] and \
+                self.pdata["VCS"][0] != vcsSystem) or \
+               (self.pudata["VCSOVERRIDE"] and \
+                self.pudata["VCSOVERRIDE"][0] != vcsSystemOverride) or \
+               (vcsSystemOverride is not None and \
+                len(self.pudata["VCSOVERRIDE"]) == 0):
+                # stop the VCS monitor thread and shutdown VCS
+                if self.vcs is not None:
+                    self.vcs.stopStatusMonitor()
+                    self.disconnect(self.vcs, 
+                        SIGNAL("vcsStatusMonitorData(QStringList)"),
+                        self.__model.changeVCSStates)
+                    self.disconnect(self.vcs, 
+                        SIGNAL("vcsStatusMonitorStatus(QString, QString)"),
+                        self.__statusMonitorStatus)
+                    self.vcs.vcsShutdown()
+                    self.vcs = None
+                    e4App().getObject("PluginManager").deactivateVcsPlugins()
+                # reinit VCS
+                self.vcs = self.initVCS()
+                # start the VCS monitor thread
+                if self.vcs is not None:
+                    self.vcs.startStatusMonitor(self)
+                    self.connect(self.vcs, 
+                         SIGNAL("vcsStatusMonitorData(QStringList)"),
+                         self.__model.changeVCSStates)
+                    self.connect(self.vcs, 
+                         SIGNAL("vcsStatusMonitorStatus(QString, QString)"),
+                         self.__statusMonitorStatus)
+                self.emit(SIGNAL("reinitVCS"))
+            
+            if self.pudata["VCSSTATUSMONITORINTERVAL"]:
+                self.setStatusMonitorInterval(\
+                    self.pudata["VCSSTATUSMONITORINTERVAL"][0])
+            else:
+                self.setStatusMonitorInterval(\
+                    Preferences.getVCS("StatusMonitorInterval"))
+        
+    def __showFiletypeAssociations(self):
+        """
+        Public slot to display the filetype association dialog.
+        """
+        dlg = FiletypeAssociationDialog(self)
+        if dlg.exec_() == QDialog.Accepted:
+            dlg.transferData()
+            self.setDirty(True)
+        
+    def __showLexerAssociations(self):
+        """
+        Public slot to display the lexer association dialog.
+        """
+        dlg = LexerAssociationDialog(self)
+        if dlg.exec_() == QDialog.Accepted:
+            dlg.transferData()
+            self.setDirty(True)
+            self.emit(SIGNAL("lexerAssociationsChanged"))
+        
+    def getEditorLexerAssoc(self, filename):
+        """
+        Public method to retrieve a lexer association.
+        
+        @param filename filename used to determine the associated lexer language (string)
+        @return the requested lexer language (string)
+        """
+        # try user settings first
+        for pattern, language in self.pdata["LEXERASSOCS"].items():
+            if fnmatch.fnmatch(filename, pattern):
+                return language
+        
+        # try project type specific defaults next
+        projectType = self.pdata["PROJECTTYPE"][0]
+        try:
+            if self.__lexerAssociationCallbacks[projectType] is not None:
+                return self.__lexerAssociationCallbacks[projectType](filename)
+        except KeyError:
+            pass
+        
+        # return empty string to signal to use the global setting
+        return ""
+        
+    def openProject(self, fn = None, restoreSession = True, reopen = False):
+        """
+        Public slot to open a project.
+        
+        @param fn optional filename of the project file to be read
+        @param restoreSession flag indicating to restore the project
+            session (boolean)
+        @keyparam reopen flag indicating a reopening of the project (boolean)
+        """
+        if not self.checkDirty():
+            return
+        
+        if fn is None:
+            fn = QFileDialog.getOpenFileName(\
+                self.parent(),
+                self.trUtf8("Open project"),
+                "",
+                self.trUtf8("Project Files (*.e4p *.e4pz *.e3p *.e3pz)"))
+        
+        QApplication.processEvents()
+        
+        if fn:
+            if self.closeProject():
+                QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
+                QApplication.processEvents()
+                if self.__readProject(fn):
+                    self.opened = True
+                    if not self.pdata["FILETYPES"]:
+                        self.initFileTypes()
+                    else:
+                        self.updateFileTypes()
+                    
+                    QApplication.restoreOverrideCursor()
+                    QApplication.processEvents()
+                    
+                    # read a user specific project file
+                    self.__readUserProperties()
+                    
+                    QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
+                    QApplication.processEvents()
+                    
+                    self.vcs = self.initVCS()
+                    if self.vcs is None:
+                        # check, if project is version controlled
+                        pluginManager = e4App().getObject("PluginManager")
+                        for indicator, vcsData in \
+                                pluginManager.getVcsSystemIndicators().items():
+                            if os.path.exists(os.path.join(self.ppath, indicator)):
+                                if len(vcsData) > 1:
+                                    vcsList = []
+                                    for vcsSystemStr, vcsSystemDisplay in vcsData:
+                                        vcsList.append(vcsSystemDisplay)
+                                    QApplication.restoreOverrideCursor()
+                                    res, vcs_ok = QInputDialog.getItem(\
+                                        None,
+                                        self.trUtf8("New Project"),
+                                        self.trUtf8("Select Version Control System"),
+                                        vcsList,
+                                        0, False)
+                                    QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
+                                    QApplication.processEvents()
+                                    if vcs_ok:
+                                        for vcsSystemStr, vcsSystemDisplay in vcsData:
+                                            if res == vcsSystemDisplay:
+                                                vcsSystem = vcsSystemStr
+                                                break
+                                        else:
+                                            vcsSystem = "None"
+                                    else:
+                                        vcsSystem = "None"
+                                else:
+                                    vcsSystem = vcsData[0][0]
+                                self.pdata["VCS"] = [vcsSystem]
+                                self.vcs = self.initVCS()
+                                self.setDirty(True)
+                    if self.vcs is not None and \
+                       self.vcs.vcsRegisteredState(self.ppath) != self.vcs.canBeCommitted:
+                        self.pdata["VCS"] = ['None']
+                        self.vcs = self.initVCS()
+                    self.closeAct.setEnabled(True)
+                    self.saveasAct.setEnabled(True)
+                    self.actGrp2.setEnabled(True)
+                    self.propsAct.setEnabled(True)
+                    self.userPropsAct.setEnabled(True)
+                    self.filetypesAct.setEnabled(True)
+                    self.lexersAct.setEnabled(True)
+                    self.sessActGrp.setEnabled(True)
+                    self.dbgActGrp.setEnabled(True)
+                    self.menuDebuggerAct.setEnabled(True)
+                    self.menuSessionAct.setEnabled(True)
+                    self.menuCheckAct.setEnabled(True)
+                    self.menuShowAct.setEnabled(True)
+                    self.menuDiagramAct.setEnabled(True)
+                    self.menuApidocAct.setEnabled(True)
+                    self.menuPackagersAct.setEnabled(True)
+                    self.pluginGrp.setEnabled(self.pdata["PROJECTTYPE"][0] == "E4Plugin")
+                    
+                    self.__model.projectOpened()
+                    self.emit(SIGNAL('projectOpenedHooks'))
+                    self.emit(SIGNAL('projectOpened'))
+                    
+                    QApplication.restoreOverrideCursor()
+                    
+                    if Preferences.getProject("SearchNewFiles"):
+                        self.__doSearchNewFiles()
+                    
+                    # read a project tasks file
+                    self.__readTasks()
+                    self.ui.taskViewer.setProjectOpen(True)
+                    
+                    if restoreSession:
+                        # open the main script
+                        if len(self.pdata["MAINSCRIPT"]) == 1:
+                            self.emit(SIGNAL('sourceFile'), 
+                                os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0]))
+                        
+                        # open a project session file being quiet about errors
+                        if reopen:
+                            self.__readSession(quiet = True, indicator = "_tmp")
+                        elif Preferences.getProject("AutoLoadSession"):
+                            self.__readSession(quiet = True)
+                    
+                    # open a project debugger properties file being quiet about errors
+                    if Preferences.getProject("AutoLoadDbgProperties"):
+                        self.__readDebugProperties(True)
+                    
+                    # start the VCS monitor thread
+                    if self.vcs is not None:
+                        self.vcs.startStatusMonitor(self)
+                        self.connect(self.vcs, 
+                             SIGNAL("vcsStatusMonitorData(QStringList)"),
+                             self.__model.changeVCSStates)
+                        self.connect(self.vcs, 
+                             SIGNAL("vcsStatusMonitorStatus(QString, QString)"),
+                             self.__statusMonitorStatus)
+                else:
+                    QApplication.restoreOverrideCursor()
+        
+    def reopenProject(self):
+        """
+        Public slot to reopen the current project.
+        """
+        projectFile = self.pfile
+        res = self.closeProject(reopen = True)
+        if res:
+            self.openProject(projectFile, reopen = True)
+        
+    def saveProject(self):
+        """
+        Public slot to save the current project.
+        
+        @return flag indicating success
+        """
+        if self.isDirty():
+            if len(self.pfile) > 0:
+                if self.pfile.endswith("e3pz") or self.pfile.endswith("e3p"):
+                    ok = self.saveProjectAs()
+                else:
+                    ok = self.__writeProject()
+            else:
+                ok = self.saveProjectAs()
+        else:
+            ok = True
+        self.sessActGrp.setEnabled(ok)
+        self.menuSessionAct.setEnabled(ok)
+        return ok
+        
+    def saveProjectAs(self):
+        """
+        Public slot to save the current project to a different file.
+        
+        @return flag indicating success (boolean)
+        """
+        if Preferences.getProject("CompressedProjectFiles"):
+            defaultFilter = self.trUtf8("Compressed Project Files (*.e4pz)")
+        else:
+            defaultFilter = self.trUtf8("Project Files (*.e4p)")
+        fn, selectedFilter = QFileDialog.getSaveFileNameAndFilter(\
+            self.parent(),
+            self.trUtf8("Save project as"),
+            self.ppath,
+            self.trUtf8("Project Files (*.e4p);;"
+                "Compressed Project Files (*.e4pz)"),
+            defaultFilter,
+            QFileDialog.Options(QFileDialog.DontConfirmOverwrite))
+        
+        if fn:
+            ext = QFileInfo(fn).suffix()
+            if not ext:
+                ex = selectedFilter.split("(*")[1].split(")")[0]
+                if ex:
+                    fn += ex
+            if QFileInfo(fn).exists():
+                res = QMessageBox.warning(None,
+                    self.trUtf8("Save File"),
+                    self.trUtf8("""<p>The file <b>{0}</b> already exists.</p>""")
+                        .format(fn),
+                    QMessageBox.StandardButtons(\
+                        QMessageBox.Abort | \
+                        QMessageBox.Save),
+                    QMessageBox.Abort)
+                if res != QMessageBox.Save:
+                    return False
+                
+            self.name = QFileInfo(fn).baseName()
+            ok = self.__writeProject(fn)
+            
+            if ok:
+                # now save the tasks
+                self.__writeTasks()
+            
+            self.sessActGrp.setEnabled(ok)
+            self.menuSessionAct.setEnabled(ok)
+            self.emit(SIGNAL('projectClosedHooks'))
+            self.emit(SIGNAL('projectClosed'))
+            self.emit(SIGNAL('projectOpenedHooks'))
+            self.emit(SIGNAL('projectOpened'))
+            return True
+        else:
+            return False
+    
+    def checkDirty(self):
+        """
+        Public method to check dirty status and open a message window.
+        
+        @return flag indicating whether this operation was successful (boolean)
+        """
+        if self.isDirty():
+            res = QMessageBox.warning(self.parent(), 
+                self.trUtf8("Close Project"),
+                self.trUtf8("The current project has unsaved changes."),
+                QMessageBox.StandardButtons(\
+                    QMessageBox.Abort | \
+                    QMessageBox.Discard | \
+                    QMessageBox.Save),
+                QMessageBox.Save)
+            if res == QMessageBox.Save:
+                return self.saveProject()
+            elif res == QMessageBox.Discard:
+                self.setDirty(False)
+                return True
+            elif res == QMessageBox.Abort:
+                return False
+            
+        return True
+        
+    def __closeAllWindows(self):
+        """
+        Private method to close all project related windows.
+        """
+        self.codemetrics        and self.codemetrics.close()
+        self.codecoverage       and self.codecoverage.close()
+        self.profiledata        and self.profiledata.close()
+        self.applicationDiagram and self.applicationDiagram.close()
+        
+    def closeProject(self, reopen = False):
+        """
+        Public slot to close the current project.
+        
+        @keyparam reopen flag indicating a reopening of the project (boolean)
+        @return flag indicating success (boolean)
+        """
+        # save the list of recently opened projects
+        self.__saveRecent()
+        
+        if not self.isOpen():
+            return True
+        
+        if not self.checkDirty():
+            return False
+        
+        # save the user project properties
+        self.__writeUserProperties()
+        
+        # save the project session file being quiet about error
+        if reopen:
+            self.__writeSession(quiet = True, indicator = "_tmp")
+        elif Preferences.getProject("AutoSaveSession"):
+            self.__writeSession(quiet = True)
+        
+        # save the project debugger properties file being quiet about error
+        if Preferences.getProject("AutoSaveDbgProperties") and \
+           self.isDebugPropertiesLoaded():
+            self.__writeDebugProperties(True)
+        
+        # now save all open modified files of the project
+        vm = e4App().getObject("ViewManager")
+        success = True
+        for fn in vm.getOpenFilenames():
+            if self.isProjectFile(fn):
+                success &= vm.closeWindow(fn)
+        
+        if not success:
+            return False
+        
+        # stop the VCS monitor thread
+        if self.vcs is not None:
+            self.vcs.stopStatusMonitor()
+            self.disconnect(self.vcs, 
+                SIGNAL("vcsStatusMonitorData(QStringList)"),
+                self.__model.changeVCSStates)
+            self.disconnect(self.vcs, 
+                SIGNAL("vcsStatusMonitorStatus(QString, QString)"),
+                self.__statusMonitorStatus)
+        
+        # now save the tasks
+        self.__writeTasks()
+        self.ui.taskViewer.clearProjectTasks()
+        self.ui.taskViewer.setProjectOpen(False)
+        
+        # now shutdown the vcs interface
+        if self.vcs:
+            self.vcs.vcsShutdown()
+            self.vcs = None
+            e4App().getObject("PluginManager").deactivateVcsPlugins()
+        
+        # now close all project related windows
+        self.__closeAllWindows()
+        
+        self.__initData()
+        self.closeAct.setEnabled(False)
+        self.saveasAct.setEnabled(False)
+        self.saveAct.setEnabled(False)
+        self.actGrp2.setEnabled(False)
+        self.propsAct.setEnabled(False)
+        self.userPropsAct.setEnabled(False)
+        self.filetypesAct.setEnabled(False)
+        self.lexersAct.setEnabled(False)
+        self.sessActGrp.setEnabled(False)
+        self.dbgActGrp.setEnabled(False)
+        self.menuDebuggerAct.setEnabled(False)
+        self.menuSessionAct.setEnabled(False)
+        self.menuCheckAct.setEnabled(False)
+        self.menuShowAct.setEnabled(False)
+        self.menuDiagramAct.setEnabled(False)
+        self.menuApidocAct.setEnabled(False)
+        self.menuPackagersAct.setEnabled(False)
+        self.pluginGrp.setEnabled(False)
+        
+        self.__model.projectClosed()
+        self.emit(SIGNAL('projectClosedHooks'))
+        self.emit(SIGNAL('projectClosed'))
+        
+        return True
+
+    def saveAllScripts(self, reportSyntaxErrors = False):
+        """
+        Public method to save all scripts belonging to the project.
+        
+        @keyparam reportSyntaxErrors flag indicating special reporting
+            for syntax errors (boolean)
+        @return flag indicating success (boolean)
+        """
+        vm = e4App().getObject("ViewManager")
+        success = True
+        filesWithSyntaxErrors = 0
+        for fn in vm.getOpenFilenames():
+            rfn = fn.replace(self.ppath + os.sep, '') # make relativ to project
+            if rfn in self.pdata["SOURCES"] or rfn in self.pdata["OTHERS"]:
+                editor = vm.getOpenEditor(fn)
+                success &= vm.saveEditorEd(editor)
+                if reportSyntaxErrors and editor.hasSyntaxErrors():
+                    filesWithSyntaxErrors += 1
+        
+        if reportSyntaxErrors and filesWithSyntaxErrors > 0:
+            QMessageBox.critical(None,
+                self.trUtf8("Syntax errors detected"),
+                self.trUtf8("""The project contains %n file(s) with syntax errors.""",
+                    "", filesWithSyntaxErrors)
+            )
+            return False
+        else:
+            return success
+        
+    def getMainScript(self, normalized = False):
+        """
+        Public method to return the main script filename.
+        
+        @param normalized flag indicating a normalized filename is wanted (boolean)
+        @return filename of the projects main script (string)
+        """
+        if len(self.pdata["MAINSCRIPT"]):
+            if normalized:
+                return os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0])
+            else:
+                return self.pdata["MAINSCRIPT"]
+        else:
+            return None
+        
+    def getSources(self, normalized = False):
+        """
+        Public method to return the source script files.
+        
+        @param normalized flag indicating a normalized filename is wanted (boolean)
+        @return list of the projects scripts (list of string)
+        """
+        if normalized:
+            return [os.path.join(self.ppath, fn) for fn in self.pdata["SOURCES"]]
+        else:
+            return self.pdata["SOURCES"]
+        
+    def getProjectType(self):
+        """
+        Public method to get the type of the project.
+        
+        @return UI type of the project (string)
+        """
+        return self.pdata["PROJECTTYPE"][0]
+        
+    def getProjectLanguage(self):
+        """
+        Public method to get the project's programming language.
+        
+        @return programming language (string)
+        """
+        return self.pdata["PROGLANGUAGE"][0]
+        
+    def getProjectSpellLanguage(self):
+        """
+        Public method to get the project's programming language.
+        
+        @return programming language (string)
+        """
+        return self.pdata["SPELLLANGUAGE"][0]
+        
+    def getProjectDictionaries(self):
+        """
+        Public method to get the names of the project specific dictionaries.
+        
+        @return tuple of two strings giving the absolute path names of the
+            project specific word and exclude list
+        """
+        pwl = ""
+        if len(self.pdata["SPELLWORDS"][0]) > 0:
+            pwl = os.path.join(self.ppath, self.pdata["SPELLWORDS"][0])
+        
+        pel = ""
+        if len(self.pdata["SPELLEXCLUDES"][0]) > 0:
+            pel = os.path.join(self.ppath, self.pdata["SPELLEXCLUDES"][0])
+        
+        return (pwl, pel)
+        
+    def getDefaultSourceExtension(self):
+        """
+        Public method to get the default extension for the project's
+        programming language.
+        
+        @return default extension (including the dot) (string)
+        """
+        if self.pdata["PROGLANGUAGE"]:
+            return self.sourceExtensions[self.pdata["PROGLANGUAGE"][0]][0]
+        else:
+            return ""
+        
+    def getProjectPath(self):
+        """
+        Public method to get the project path.
+        
+        @return project path (string)
+        """
+        return self.ppath
+        
+    def getProjectFile(self):
+        """
+        Public method to get the path of the project file.
+        
+        @return path of the project file (string)
+        """
+        return self.pfile
+        
+    def getProjectManagementDir(self):
+        """
+        Public method to get the path of the management directory.
+        
+        @return path of the management directory (string)
+        """
+        if Utilities.isWindowsPlatform():
+            return os.path.join(self.ppath, "_eric4project")
+        else:
+            return os.path.join(self.ppath, ".eric4project")
+        
+    def isProjectFile(self, fn):
+        """
+        Public method used to check, if the passed in filename belongs to the project.
+        
+        @param fn filename to be checked (string)
+        @return flag indicating membership (boolean)
+        """
+        newfn = os.path.abspath(fn)
+        newfn = newfn.replace(self.ppath + os.sep, '')
+        if newfn in self.pdata["SOURCES"] or \
+           newfn in self.pdata["FORMS"] or \
+           newfn in self.pdata["INTERFACES"] or \
+           newfn in self.pdata["RESOURCES"] or \
+           newfn in self.pdata["TRANSLATIONS"] or \
+           newfn in self.pdata["OTHERS"]:
+            return True
+        else:
+            for entry in self.pdata["OTHERS"]:
+                if newfn.startswith(entry):
+                    return True
+        return False
+        
+    def isProjectSource(self, fn):
+        """
+        Public method used to check, if the passed in filename belongs to the project
+        sources.
+        
+        @param fn filename to be checked (string)
+        @return flag indicating membership (boolean)
+        """
+        newfn = os.path.abspath(fn)
+        newfn = newfn.replace(self.ppath + os.sep, '')
+        return newfn in self.pdata["SOURCES"]
+        
+    def isProjectForm(self, fn):
+        """
+        Public method used to check, if the passed in filename belongs to the project
+        forms.
+        
+        @param fn filename to be checked (string)
+        @return flag indicating membership (boolean)
+        """
+        newfn = os.path.abspath(fn)
+        newfn = newfn.replace(self.ppath + os.sep, '')
+        return newfn in self.pdata["FORMS"]
+        
+    def isProjectInterface(self, fn):
+        """
+        Public method used to check, if the passed in filename belongs to the project
+        interfaces.
+        
+        @param fn filename to be checked (string)
+        @return flag indicating membership (boolean)
+        """
+        newfn = os.path.abspath(fn)
+        newfn = newfn.replace(self.ppath + os.sep, '')
+        return newfn in self.pdata["INTERFACES"]
+        
+    def isProjectResource(self, fn):
+        """
+        Public method used to check, if the passed in filename belongs to the project
+        resources.
+        
+        @param fn filename to be checked (string)
+        @return flag indicating membership (boolean)
+        """
+        newfn = os.path.abspath(fn)
+        newfn = newfn.replace(self.ppath + os.sep, '')
+        return newfn in self.pdata["RESOURCES"]
+        
+    def initActions(self):
+        """
+        Public slot to initialize the project related actions.
+        """
+        self.actions = []
+        
+        self.actGrp1 = createActionGroup(self)
+        
+        act = E4Action(self.trUtf8('New project'),
+                UI.PixmapCache.getIcon("projectNew.png"),
+                self.trUtf8('&New...'), 0, 0,
+                self.actGrp1,'project_new')
+        act.setStatusTip(self.trUtf8('Generate a new project'))
+        act.setWhatsThis(self.trUtf8(
+            """<b>New...</b>"""
+            """<p>This opens a dialog for entering the info for a"""
+            """ new project.</p>"""
+        ))
+        self.connect(act, SIGNAL('triggered()'), self.newProject)
+        self.actions.append(act)
+
+        act = E4Action(self.trUtf8('Open project'),
+                UI.PixmapCache.getIcon("projectOpen.png"),
+                self.trUtf8('&Open...'), 0, 0,
+                self.actGrp1,'project_open')
+        act.setStatusTip(self.trUtf8('Open an existing project'))
+        act.setWhatsThis(self.trUtf8(
+            """<b>Open...</b>"""
+            """<p>This opens an existing project.</p>"""
+        ))
+        self.connect(act, SIGNAL('triggered()'), self.openProject)
+        self.actions.append(act)
+
+        self.closeAct = E4Action(self.trUtf8('Close project'),
+                UI.PixmapCache.getIcon("projectClose.png"),
+                self.trUtf8('&Close'), 0, 0, self, 'project_close')
+        self.closeAct.setStatusTip(self.trUtf8('Close the current project'))
+        self.closeAct.setWhatsThis(self.trUtf8(
+            """<b>Close</b>"""
+            """<p>This closes the current project.</p>"""
+        ))
+        self.connect(self.closeAct, SIGNAL('triggered()'), self.closeProject)
+        self.actions.append(self.closeAct)
+
+        self.saveAct = E4Action(self.trUtf8('Save project'),
+                UI.PixmapCache.getIcon("projectSave.png"),
+                self.trUtf8('&Save'), 0, 0, self, 'project_save')
+        self.saveAct.setStatusTip(self.trUtf8('Save the current project'))
+        self.saveAct.setWhatsThis(self.trUtf8(
+            """<b>Save</b>"""
+            """<p>This saves the current project.</p>"""
+        ))
+        self.connect(self.saveAct, SIGNAL('triggered()'), self.saveProject)
+        self.actions.append(self.saveAct)
+
+        self.saveasAct = E4Action(self.trUtf8('Save project as'),
+                UI.PixmapCache.getIcon("projectSaveAs.png"),
+                self.trUtf8('Save &as...'), 0, 0, self, 'project_save_as')
+        self.saveasAct.setStatusTip(self.trUtf8('Save the current project to a new file'))
+        self.saveasAct.setWhatsThis(self.trUtf8(
+            """<b>Save as</b>"""
+            """<p>This saves the current project to a new file.</p>"""
+        ))
+        self.connect(self.saveasAct, SIGNAL('triggered()'), self.saveProjectAs)
+        self.actions.append(self.saveasAct)
+
+        self.actGrp2 = createActionGroup(self)
+        
+        self.addFilesAct = E4Action(self.trUtf8('Add files to project'),
+                UI.PixmapCache.getIcon("fileMisc.png"),
+                self.trUtf8('Add &files...'), 0, 0,
+                self.actGrp2,'project_add_file')
+        self.addFilesAct.setStatusTip(self.trUtf8('Add files to the current project'))
+        self.addFilesAct.setWhatsThis(self.trUtf8(
+            """<b>Add files...</b>"""
+            """<p>This opens a dialog for adding files"""
+            """ to the current project. The place to add is"""
+            """ determined by the file extension.</p>"""
+        ))
+        self.connect(self.addFilesAct, SIGNAL('triggered()'), self.addFiles)
+        self.actions.append(self.addFilesAct)
+
+        self.addDirectoryAct = E4Action(self.trUtf8('Add directory to project'),
+                UI.PixmapCache.getIcon("dirOpen.png"),
+                self.trUtf8('Add directory...'), 0, 0,
+                self.actGrp2,'project_add_directory')
+        self.addDirectoryAct.setStatusTip(
+            self.trUtf8('Add a directory to the current project'))
+        self.addDirectoryAct.setWhatsThis(self.trUtf8(
+            """<b>Add directory...</b>"""
+            """<p>This opens a dialog for adding a directory"""
+            """ to the current project.</p>"""
+        ))
+        self.connect(self.addDirectoryAct, SIGNAL('triggered()'), self.addDirectory)
+        self.actions.append(self.addDirectoryAct)
+
+        self.addLanguageAct = E4Action(self.trUtf8('Add translation to project'),
+                UI.PixmapCache.getIcon("linguist4.png"),
+                self.trUtf8('Add &translation...'), 0, 0,
+                self.actGrp2,'project_add_translation')
+        self.addLanguageAct.setStatusTip(
+            self.trUtf8('Add a translation to the current project'))
+        self.addLanguageAct.setWhatsThis(self.trUtf8(
+            """<b>Add translation...</b>"""
+            """<p>This opens a dialog for add a translation"""
+            """ to the current project.</p>"""
+        ))
+        self.connect(self.addLanguageAct, SIGNAL('triggered()'), self.addLanguage)
+        self.actions.append(self.addLanguageAct)
+
+        act = E4Action(self.trUtf8('Search new files'),
+                self.trUtf8('Searc&h new files...'), 0, 0,
+                self.actGrp2,'project_search_new_files')
+        act.setStatusTip(self.trUtf8('Search new files in the project directory.'))
+        act.setWhatsThis(self.trUtf8(
+            """<b>Search new files...</b>"""
+            """<p>This searches for new files (sources, *.ui, *.idl) in the project"""
+            """ directory and registered subdirectories.</p>"""
+        ))
+        self.connect(act, SIGNAL('triggered()'), self.__searchNewFiles)
+        self.actions.append(act)
+
+        self.propsAct = E4Action(self.trUtf8('Project properties'),
+                UI.PixmapCache.getIcon("projectProps.png"),
+                self.trUtf8('&Properties...'), 0, 0, self, 'project_properties')
+        self.propsAct.setStatusTip(self.trUtf8('Show the project properties'))
+        self.propsAct.setWhatsThis(self.trUtf8(
+            """<b>Properties...</b>"""
+            """<p>This shows a dialog to edit the project properties.</p>"""
+        ))
+        self.connect(self.propsAct, SIGNAL('triggered()'), self.__showProperties)
+        self.actions.append(self.propsAct)
+
+        self.userPropsAct = E4Action(self.trUtf8('User project properties'),
+                UI.PixmapCache.getIcon("projectUserProps.png"),
+                self.trUtf8('&User Properties...'), 0, 0, self, 'project_user_properties')
+        self.userPropsAct.setStatusTip(self.trUtf8(
+            'Show the user specific project properties'))
+        self.userPropsAct.setWhatsThis(self.trUtf8(
+            """<b>User Properties...</b>"""
+            """<p>This shows a dialog to edit the user specific project properties.</p>"""
+        ))
+        self.connect(self.userPropsAct, SIGNAL('triggered()'), self.__showUserProperties)
+        self.actions.append(self.userPropsAct)
+
+        self.filetypesAct = E4Action(self.trUtf8('Filetype Associations'),
+                self.trUtf8('Filetype Associations...'), 0, 0,
+                self, 'project_filetype_associatios')
+        self.filetypesAct.setStatusTip(\
+            self.trUtf8('Show the project filetype associations'))
+        self.filetypesAct.setWhatsThis(self.trUtf8(
+            """<b>Filetype Associations...</b>"""
+            """<p>This shows a dialog to edit the filetype associations of the project."""
+            """ These associations determine the type (source, form, interface"""
+            """ or others) with a filename pattern. They are used when adding a file"""
+            """ to the project and when performing a search for new files.</p>"""
+        ))
+        self.connect(self.filetypesAct, SIGNAL('triggered()'), 
+            self.__showFiletypeAssociations)
+        self.actions.append(self.filetypesAct)
+
+        self.lexersAct = E4Action(self.trUtf8('Lexer Associations'),
+                self.trUtf8('Lexer Associations...'), 0, 0,
+                self, 'project_lexer_associatios')
+        self.lexersAct.setStatusTip(\
+            self.trUtf8('Show the project lexer associations (overriding defaults)'))
+        self.lexersAct.setWhatsThis(self.trUtf8(
+            """<b>Lexer Associations...</b>"""
+            """<p>This shows a dialog to edit the lexer associations of the project."""
+            """ These associations override the global lexer associations. Lexers"""
+            """ are used to highlight the editor text.</p>"""
+        ))
+        self.connect(self.lexersAct, SIGNAL('triggered()'), 
+            self.__showLexerAssociations)
+        self.actions.append(self.lexersAct)
+
+        self.dbgActGrp = createActionGroup(self)
+        
+        act = E4Action(self.trUtf8('Debugger Properties'),
+                self.trUtf8('Debugger &Properties...'), 0, 0,
+                self.dbgActGrp, 'project_debugger_properties')
+        act.setStatusTip(self.trUtf8('Show the debugger properties'))
+        act.setWhatsThis(self.trUtf8(
+            """<b>Debugger Properties...</b>"""
+            """<p>This shows a dialog to edit project specific debugger settings.</p>"""
+        ))
+        self.connect(act, SIGNAL('triggered()'), self.__showDebugProperties)
+        self.actions.append(act)
+        
+        act = E4Action(self.trUtf8('Load'),
+                self.trUtf8('&Load'), 0, 0,
+                self.dbgActGrp, 'project_debugger_properties_load')
+        act.setStatusTip(self.trUtf8('Load the debugger properties'))
+        act.setWhatsThis(self.trUtf8(
+            """<b>Load Debugger Properties</b>"""
+            """<p>This loads the project specific debugger settings.</p>"""
+        ))
+        self.connect(act, SIGNAL('triggered()'), self.__readDebugProperties)
+        self.actions.append(act)
+        
+        act = E4Action(self.trUtf8('Save'),
+                self.trUtf8('&Save'), 0, 0,
+                self.dbgActGrp, 'project_debugger_properties_save')
+        act.setStatusTip(self.trUtf8('Save the debugger properties'))
+        act.setWhatsThis(self.trUtf8(
+            """<b>Save Debugger Properties</b>"""
+            """<p>This saves the project specific debugger settings.</p>"""
+        ))
+        self.connect(act, SIGNAL('triggered()'), self.__writeDebugProperties)
+        self.actions.append(act)
+        
+        act = E4Action(self.trUtf8('Delete'),
+                self.trUtf8('&Delete'), 0, 0,
+                self.dbgActGrp, 'project_debugger_properties_delete')
+        act.setStatusTip(self.trUtf8('Delete the debugger properties'))
+        act.setWhatsThis(self.trUtf8(
+            """<b>Delete Debugger Properties</b>"""
+            """<p>This deletes the file containing the project specific"""
+            """ debugger settings.</p>"""
+        ))
+        self.connect(act, SIGNAL('triggered()'), self.__deleteDebugProperties)
+        self.actions.append(act)
+        
+        act = E4Action(self.trUtf8('Reset'),
+                self.trUtf8('&Reset'), 0, 0,
+                self.dbgActGrp, 'project_debugger_properties_resets')
+        act.setStatusTip(self.trUtf8('Reset the debugger properties'))
+        act.setWhatsThis(self.trUtf8(
+            """<b>Reset Debugger Properties</b>"""
+            """<p>This resets the project specific debugger settings.</p>"""
+        ))
+        self.connect(act, SIGNAL('triggered()'), self.__initDebugProperties)
+        self.actions.append(act)
+        
+        self.sessActGrp = createActionGroup(self)
+
+        act = E4Action(self.trUtf8('Load session'),
+                self.trUtf8('Load session'), 0, 0,
+                self.sessActGrp, 'project_load_session')
+        act.setStatusTip(self.trUtf8('Load the projects session file.'))
+        act.setWhatsThis(self.trUtf8(
+            """<b>Load session</b>"""
+            """<p>This loads the projects session file. The session consists"""
+            """ of the following data.<br>"""
+            """- all open source files<br>"""
+            """- all breakpoint<br>"""
+            """- the commandline arguments<br>"""
+            """- the working directory<br>"""
+            """- the exception reporting flag</p>"""
+        ))
+        self.connect(act, SIGNAL('triggered()'), self.__readSession)
+        self.actions.append(act)
+
+        act = E4Action(self.trUtf8('Save session'),
+                self.trUtf8('Save session'), 0, 0,
+                self.sessActGrp, 'project_save_session')
+        act.setStatusTip(self.trUtf8('Save the projects session file.'))
+        act.setWhatsThis(self.trUtf8(
+            """<b>Save session</b>"""
+            """<p>This saves the projects session file. The session consists"""
+            """ of the following data.<br>"""
+            """- all open source files<br>"""
+            """- all breakpoint<br>"""
+            """- the commandline arguments<br>"""
+            """- the working directory<br>"""
+            """- the exception reporting flag</p>"""
+        ))
+        self.connect(act, SIGNAL('triggered()'), self.__writeSession)
+        self.actions.append(act)
+        
+        act = E4Action(self.trUtf8('Delete session'),
+                self.trUtf8('Delete session'), 0, 0,
+                self.sessActGrp, 'project_delete_session')
+        act.setStatusTip(self.trUtf8('Delete the projects session file.'))
+        act.setWhatsThis(self.trUtf8(
+            """<b>Delete session</b>"""
+            """<p>This deletes the projects session file</p>"""
+        ))
+        self.connect(act, SIGNAL('triggered()'), self.__deleteSession)
+        self.actions.append(act)
+        
+        self.chkGrp = createActionGroup(self)
+
+        self.codeMetricsAct = E4Action(self.trUtf8('Code Metrics'),
+                self.trUtf8('&Code Metrics...'), 0, 0,
+                self.chkGrp,'project_code_metrics')
+        self.codeMetricsAct.setStatusTip(\
+            self.trUtf8('Show some code metrics for the project.'))
+        self.codeMetricsAct.setWhatsThis(self.trUtf8(
+            """<b>Code Metrics...</b>"""
+            """<p>This shows some code metrics for all Python files in the project.</p>"""
+        ))
+        self.connect(self.codeMetricsAct, SIGNAL('triggered()'), self.__showCodeMetrics)
+        self.actions.append(self.codeMetricsAct)
+
+        self.codeCoverageAct = E4Action(self.trUtf8('Python Code Coverage'),
+                self.trUtf8('Code Co&verage...'), 0, 0,
+                self.chkGrp,'project_code_coverage')
+        self.codeCoverageAct.setStatusTip(\
+            self.trUtf8('Show code coverage information for the project.'))
+        self.codeCoverageAct.setWhatsThis(self.trUtf8(
+            """<b>Code Coverage...</b>"""
+            """<p>This shows the code coverage information for all Python files"""
+            """ in the project.</p>"""
+        ))
+        self.connect(self.codeCoverageAct, SIGNAL('triggered()'), self.__showCodeCoverage)
+        self.actions.append(self.codeCoverageAct)
+
+        self.codeProfileAct = E4Action(self.trUtf8('Profile Data'),
+                self.trUtf8('&Profile Data...'), 0, 0,
+                self.chkGrp,'project_profile_data')
+        self.codeProfileAct.setStatusTip(\
+            self.trUtf8('Show profiling data for the project.'))
+        self.codeProfileAct.setWhatsThis(self.trUtf8(
+            """<b>Profile Data...</b>"""
+            """<p>This shows the profiling data for the project.</p>"""
+        ))
+        self.connect(self.codeProfileAct, SIGNAL('triggered()'), self.__showProfileData)
+        self.actions.append(self.codeProfileAct)
+
+        self.applicationDiagramAct = E4Action(self.trUtf8('Application Diagram'),
+                self.trUtf8('&Application Diagram...'), 0, 0,
+                self.chkGrp,'project_application_diagram')
+        self.applicationDiagramAct.setStatusTip(\
+            self.trUtf8('Show a diagram of the project.'))
+        self.applicationDiagramAct.setWhatsThis(self.trUtf8(
+            """<b>Application Diagram...</b>"""
+            """<p>This shows a diagram of the project.</p>"""
+        ))
+        self.connect(self.applicationDiagramAct, 
+            SIGNAL('triggered()'), self.handleApplicationDiagram)
+        self.actions.append(self.applicationDiagramAct)
+
+        self.pluginGrp = createActionGroup(self)
+
+        self.pluginPkgListAct = E4Action(self.trUtf8('Create Package List'),
+                UI.PixmapCache.getIcon("pluginArchiveList.png"),
+                self.trUtf8('Create &Package List'), 0, 0,
+                self.pluginGrp,'project_plugin_pkglist')
+        self.pluginPkgListAct.setStatusTip(\
+            self.trUtf8('Create an initial PKGLIST file for an eric4 plugin.'))
+        self.pluginPkgListAct.setWhatsThis(self.trUtf8(
+            """<b>Create Package List</b>"""
+            """<p>This creates an initial list of files to include in an eric4 """
+            """plugin archive. The list is created from the project file.</p>"""
+        ))
+        self.connect(self.pluginPkgListAct, SIGNAL('triggered()'), 
+            self.__pluginCreatePkgList)
+        self.actions.append(self.pluginPkgListAct)
+
+        self.pluginArchiveAct = E4Action(self.trUtf8('Create Plugin Archive'),
+                UI.PixmapCache.getIcon("pluginArchive.png"),
+                self.trUtf8('Create Plugin &Archive'), 0, 0,
+                self.pluginGrp,'project_plugin_archive')
+        self.pluginArchiveAct.setStatusTip(\
+            self.trUtf8('Create an eric4 plugin archive file.'))
+        self.pluginArchiveAct.setWhatsThis(self.trUtf8(
+            """<b>Create Plugin Archive</b>"""
+            """<p>This creates an eric4 plugin archive file using the list of files """
+            """given in the PKGLIST file. The archive name is built from the main """
+            """script name.</p>"""
+        ))
+        self.connect(self.pluginArchiveAct, SIGNAL('triggered()'), 
+            self.__pluginCreateArchive)
+        self.actions.append(self.pluginArchiveAct)
+    
+        self.pluginSArchiveAct = E4Action(self.trUtf8('Create Plugin Archive (Snapshot)'),
+                UI.PixmapCache.getIcon("pluginArchiveSnapshot.png"),
+                self.trUtf8('Create Plugin Archive (&Snapshot)'), 0, 0,
+                self.pluginGrp,'project_plugin_sarchive')
+        self.pluginSArchiveAct.setStatusTip(\
+            self.trUtf8('Create an eric4 plugin archive file (snapshot release).'))
+        self.pluginSArchiveAct.setWhatsThis(self.trUtf8(
+            """<b>Create Plugin Archive (Snapshot)</b>"""
+            """<p>This creates an eric4 plugin archive file using the list of files """
+            """given in the PKGLIST file. The archive name is built from the main """
+            """script name. The version entry of the main script is modified to """
+            """reflect a snapshot release.</p>"""
+        ))
+        self.connect(self.pluginSArchiveAct, SIGNAL('triggered()'), 
+            self.__pluginCreateSnapshotArchive)
+        self.actions.append(self.pluginSArchiveAct)
+
+        self.closeAct.setEnabled(False)
+        self.saveAct.setEnabled(False)
+        self.saveasAct.setEnabled(False)
+        self.actGrp2.setEnabled(False)
+        self.propsAct.setEnabled(False)
+        self.userPropsAct.setEnabled(False)
+        self.filetypesAct.setEnabled(False)
+        self.lexersAct.setEnabled(False)
+        self.sessActGrp.setEnabled(False)
+        self.dbgActGrp.setEnabled(False)
+        self.pluginGrp.setEnabled(False)
+        
+    def initMenu(self):
+        """
+        Public slot to initialize the project menu.
+        
+        @return the menu generated (QMenu)
+        """
+        menu = QMenu(self.trUtf8('&Project'), self.parent())
+        self.recentMenu = QMenu(self.trUtf8('Open &Recent Projects'), menu)
+        self.vcsMenu = QMenu(self.trUtf8('&Version Control'), menu)
+        self.vcsMenu.setTearOffEnabled(True)
+        self.vcsProjectHelper.initMenu(self.vcsMenu)
+        self.checksMenu = QMenu(self.trUtf8('Chec&k'), menu)
+        self.checksMenu.setTearOffEnabled(True)
+        self.showMenu = QMenu(self.trUtf8('Sho&w'), menu)
+        self.graphicsMenu = QMenu(self.trUtf8('&Diagrams'), menu)
+        self.sessionMenu = QMenu(self.trUtf8('Session'), menu)
+        self.apidocMenu = QMenu(self.trUtf8('Source &Documentation'), menu)
+        self.apidocMenu.setTearOffEnabled(True)
+        self.debuggerMenu = QMenu(self.trUtf8('Debugger'), menu)
+        self.packagersMenu = QMenu(self.trUtf8('Pac&kagers'), menu)
+        self.packagersMenu.setTearOffEnabled(True)
+        
+        self.__menus = {
+            "Main"      : menu, 
+            "Recent"    : self.recentMenu, 
+            "VCS"       : self.vcsMenu, 
+            "Checks"    : self.checksMenu, 
+            "Show"      : self.showMenu, 
+            "Graphics"  : self.graphicsMenu, 
+            "Session"   : self.sessionMenu, 
+            "Apidoc"    : self.apidocMenu, 
+            "Debugger"  : self.debuggerMenu, 
+            "Packagers" : self.packagersMenu, 
+        }
+        
+        # connect the aboutToShow signals
+        self.connect(self.recentMenu, SIGNAL('aboutToShow()'), 
+            self.__showContextMenuRecent)
+        self.connect(self.recentMenu, SIGNAL('triggered(QAction *)'),
+                     self.__openRecent)
+        self.connect(self.vcsMenu, SIGNAL('aboutToShow()'), self.__showContextMenuVCS)
+        self.connect(self.checksMenu, SIGNAL('aboutToShow()'), 
+            self.__showContextMenuChecks)
+        self.connect(self.showMenu, SIGNAL('aboutToShow()'), self.__showContextMenuShow)
+        self.connect(self.graphicsMenu, SIGNAL('aboutToShow()'), 
+            self.__showContextMenuGraphics)
+        self.connect(self.apidocMenu, SIGNAL('aboutToShow()'), 
+            self.__showContextMenuApiDoc)
+        self.connect(self.packagersMenu, SIGNAL('aboutToShow()'), 
+            self.__showContextMenuPackagers)
+        self.connect(menu, SIGNAL('aboutToShow()'), self.__showMenu)
+        
+        # build the show menu
+        self.showMenu.setTearOffEnabled(True)
+        self.showMenu.addAction(self.codeMetricsAct)
+        self.showMenu.addAction(self.codeCoverageAct)
+        self.showMenu.addAction(self.codeProfileAct)
+        
+        # build the diagrams menu
+        self.graphicsMenu.setTearOffEnabled(True)
+        self.graphicsMenu.addAction(self.applicationDiagramAct)
+        
+        # build the session menu
+        self.sessionMenu.setTearOffEnabled(True)
+        self.sessionMenu.addActions(self.sessActGrp.actions())
+        
+        # build the debugger menu
+        self.debuggerMenu.setTearOffEnabled(True)
+        self.debuggerMenu.addActions(self.dbgActGrp.actions())
+        
+        # build the packagers menu
+        self.packagersMenu.addActions(self.pluginGrp.actions())
+        self.packagersMenu.addSeparator()
+        
+        # build the main menu
+        menu.setTearOffEnabled(True)
+        menu.addActions(self.actGrp1.actions())
+        self.menuRecentAct = menu.addMenu(self.recentMenu)
+        menu.addSeparator()
+        menu.addAction(self.closeAct)
+        menu.addSeparator()
+        menu.addAction(self.saveAct)
+        menu.addAction(self.saveasAct)
+        menu.addSeparator()
+        self.menuDebuggerAct = menu.addMenu(self.debuggerMenu)
+        self.menuSessionAct = menu.addMenu(self.sessionMenu)
+        menu.addSeparator()
+        menu.addActions(self.actGrp2.actions())
+        menu.addSeparator()
+        self.menuDiagramAct = menu.addMenu(self.graphicsMenu)
+        menu.addSeparator()
+        self.menuCheckAct = menu.addMenu(self.checksMenu)
+        menu.addSeparator()
+        menu.addMenu(self.vcsMenu)
+        menu.addSeparator()
+        self.menuShowAct = menu.addMenu(self.showMenu)
+        menu.addSeparator()
+        self.menuApidocAct = menu.addMenu(self.apidocMenu)
+        menu.addSeparator()
+        self.menuPackagersAct = menu.addMenu(self.packagersMenu)
+        menu.addSeparator()
+        menu.addAction(self.propsAct)
+        menu.addAction(self.userPropsAct)
+        menu.addAction(self.filetypesAct)
+        menu.addAction(self.lexersAct)
+        
+        self.menuCheckAct.setEnabled(False)
+        self.menuShowAct.setEnabled(False)
+        self.menuDiagramAct.setEnabled(False)
+        self.menuSessionAct.setEnabled(False)
+        self.menuDebuggerAct.setEnabled(False)
+        self.menuApidocAct.setEnabled(False)
+        self.menuPackagersAct.setEnabled(False)
+        
+        self.menu = menu
+        return menu
+        
+    def initToolbar(self, toolbarManager):
+        """
+        Public slot to initialize the project toolbar.
+        
+        @param toolbarManager reference to a toolbar manager object (E4ToolBarManager)
+        @return the toolbar generated (QToolBar)
+        """
+        tb = QToolBar(self.trUtf8("Project"), self.parent())
+        tb.setIconSize(UI.Config.ToolBarIconSize)
+        tb.setObjectName("ProjectToolbar")
+        tb.setToolTip(self.trUtf8('Project'))
+        
+        tb.addActions(self.actGrp1.actions())
+        tb.addAction(self.closeAct)
+        tb.addSeparator()
+        tb.addAction(self.saveAct)
+        tb.addAction(self.saveasAct)
+        
+        toolbarManager.addToolBar(tb, tb.windowTitle())
+        toolbarManager.addAction(self.addFilesAct, tb.windowTitle())
+        toolbarManager.addAction(self.addDirectoryAct, tb.windowTitle())
+        toolbarManager.addAction(self.addLanguageAct, tb.windowTitle())
+        toolbarManager.addAction(self.propsAct, tb.windowTitle())
+        toolbarManager.addAction(self.userPropsAct, tb.windowTitle())
+        
+        return tb
+        
+    def __showMenu(self):
+        """
+        Private method to set up the project menu.
+        """
+        self.menuRecentAct.setEnabled(len(self.recent) > 0)
+        
+        self.emit(SIGNAL("showMenu"), "Main", self.__menus["Main"])
+        
+    def __syncRecent(self):
+        """
+        Private method to synchronize the list of recently opened projects
+        with the central store.
+        """
+        if self.pfile in self.recent:
+            self.recent.remove(self.pfile)
+        self.recent.insert(0, self.pfile)
+        maxRecent = Preferences.getProject("RecentNumber")
+        if len(self.recent) > maxRecent:
+            self.recent = self.recent[:maxRecent]
+        self.__saveRecent()
+        
+    def __showContextMenuRecent(self):
+        """
+        Private method to set up the recent projects menu.
+        """
+        self.__loadRecent()
+        
+        self.recentMenu.clear()
+        
+        idx = 1
+        for rp in self.recent:
+            if idx < 10:
+                formatStr = '&%d. %s'
+            else:
+                formatStr = '%d. %s'
+            act = self.recentMenu.addAction(\
+                formatStr % (idx, 
+                    Utilities.compactPath(rp, self.ui.maxMenuFilePathLen)))
+            act.setData(QVariant(rp))
+            act.setEnabled(QFileInfo(rp).exists())
+            idx += 1
+        
+        self.recentMenu.addSeparator()
+        self.recentMenu.addAction(self.trUtf8('&Clear'), self.__clearRecent)
+        
+    def __openRecent(self, act):
+        """
+        Private method to open a project from the list of rencently opened projects.
+        
+        @param act reference to the action that triggered (QAction)
+        """
+        file = act.data().toString()
+        if file:
+            self.openProject(file)
+        
+    def __clearRecent(self):
+        """
+        Private method to clear the recent projects menu.
+        """
+        self.recent.clear()
+        
+    def __searchNewFiles(self):
+        """
+        Private slot used to handle the search new files action.
+        """
+        self.__doSearchNewFiles(False, True)
+        
+    def __doSearchNewFiles(self, AI = True, onUserDemand = False):
+        """
+        Private method to search for new files in the project directory.
+        
+        If new files were found, it shows a dialog listing these files and
+        gives the user the opportunity to select the ones he wants to
+        include. If 'Automatic Inclusion' is enabled, the new files are
+        automatically added to the project.
+        
+        @param AI flag indicating whether the automatic inclusion should
+                be honoured (boolean)
+        @param onUserDemand flag indicating whether this method was 
+                requested by the user via a menu action (boolean)
+        """
+        autoInclude = Preferences.getProject("AutoIncludeNewFiles")
+        recursiveSearch = Preferences.getProject("SearchNewFilesRecursively")
+        newFiles = []
+        
+        dirs = self.subdirs[:]
+        for dir in dirs:
+            curpath = os.path.join(self.ppath, dir)
+            try:
+                newSources = os.listdir(curpath)
+            except OSError:
+                newSources = []
+            if self.pdata["TRANSLATIONPATTERN"]:
+                pattern = self.pdata["TRANSLATIONPATTERN"][0].replace("%language%", "*")
+            else:
+                pattern = "*.ts"
+            binpattern = self.__binaryTranslationFile(pattern)
+            for ns in newSources:
+                # ignore hidden files and directories
+                if ns.startswith('.'):
+                    continue
+                if Utilities.isWindowsPlatform() and \
+                   os.path.isdir(os.path.join(curpath, ns)) and \
+                   ns.startswith('_'):
+                    # dot net hack
+                    continue
+                
+                # set fn to project relative name
+                # then reset ns to fully qualified name for insertion, possibly.
+                if dir == "":
+                    fn = ns
+                else:
+                    fn = os.path.join(dir, ns)
+                ns = os.path.abspath(os.path.join(curpath, ns))
+                
+                # do not bother with dirs here...
+                if os.path.isdir(ns):
+                    if recursiveSearch:
+                        d = ns.replace(self.ppath + os.sep, '')
+                        if d not in dirs:
+                            dirs.append(d)
+                    continue
+                
+                filetype = ""
+                bfn = os.path.basename(fn)
+                for pattern in reversed(sorted(self.pdata["FILETYPES"].keys())):
+                    if fnmatch.fnmatch(bfn, pattern):
+                        filetype = self.pdata["FILETYPES"][pattern]
+                        break
+                
+                if (filetype == "SOURCES" and fn not in self.pdata["SOURCES"]) or \
+                   (filetype == "FORMS" and fn not in self.pdata["FORMS"]) or \
+                   (filetype == "INTERFACES" and fn not in self.pdata["INTERFACES"]) or \
+                   (filetype == "RESOURCES" and fn not in self.pdata["RESOURCES"]) or \
+                   (filetype == "OTHERS" and fn not in self.pdata["OTHERS"]):
+                    if autoInclude and AI:
+                        self.appendFile(ns)
+                    else:
+                        newFiles.append(ns)
+                elif filetype == "TRANSLATIONS" and fn not in self.pdata["TRANSLATIONS"]:
+                    if fnmatch.fnmatch(ns, pattern) or fnmatch.fnmatch(ns, binpattern):
+                        if autoInclude and AI:
+                            self.appendFile(ns)
+                        else:
+                            newFiles.append(ns)
+        
+        # if autoInclude is set there is no more work left
+        if (autoInclude and AI):
+            return
+        
+        # if newfiles is empty, put up message box informing user nothing found
+        if not newFiles:
+            if onUserDemand:
+                QMessageBox.information(None,
+                    self.trUtf8("Search New Files"),
+                    self.trUtf8("There were no new files found to be added."))
+            return
+            
+        # autoInclude is not set, show a dialog
+        dlg = AddFoundFilesDialog(newFiles, self.parent(), None)
+        res = dlg.exec_()
+        
+        # the 'Add All' button was pressed
+        if res == 1:
+            for file in newFiles:
+                self.appendFile(file)
+            
+        # the 'Add Selected' button was pressed
+        elif res == 2:
+            files = dlg.getSelection()
+            for file in files:
+                self.appendFile(file)
+        
+    def othersAdded(self, fn, updateModel = True):
+        """
+        Public slot to be called, if something was added to the OTHERS project data area.
+        
+        @param fn filename or directory name added (string)
+        @param updateModel flag indicating an update of the model is requested (boolean)
+        """
+        self.emit(SIGNAL('projectOthersAdded'), fn)
+        updateModel and self.__model.addNewItem("OTHERS", fn)
+        
+    def getActions(self):
+        """
+        Public method to get a list of all actions.
+        
+        @return list of all actions (list of E4Action)
+        """
+        return self.actions[:]
+        
+    def addE4Actions(self, actions):
+        """
+        Public method to add actions to the list of actions.
+        
+        @param actions list of actions (list of E4Action)
+        """
+        self.actions.extend(actions)
+        
+    def removeE4Actions(self, actions):
+        """
+        Public method to remove actions from the list of actions.
+        
+        @param actions list of actions (list of E4Action)
+        """
+        for act in actions:
+            try:
+                self.actions.remove(act)
+            except ValueError:
+                pass
+        
+    def getMenu(self, menuName):
+        """
+        Public method to get a reference to the main menu or a submenu.
+        
+        @param menuName name of the menu (string)
+        @return reference to the requested menu (QMenu) or None
+        """
+        try:
+            return self.__menus[menuName]
+        except KeyError:
+            return None
+        
+    def repopulateItem(self, fullname):
+        """
+        Public slot to repopulate a named item.
+        
+        @param fullname full name of the item to repopulate (string)
+        """
+        if not self.isOpen():
+            return
+        
+        name = fullname.replace(self.ppath + os.sep, "")
+        self.emit(SIGNAL("prepareRepopulateItem"), name)
+        self.__model.repopulateItem(name)
+        self.emit(SIGNAL("completeRepopulateItem"), name)
+    
+    ##############################################################
+    ## Below is the VCS interface
+    ##############################################################
+    
+    def initVCS(self, vcsSystem = None, nooverride = False):
+        """
+        Public method used to instantiate a vcs system.
+        
+        @param vcsSystem type of VCS to be used (string)
+        @param nooverride flag indicating to ignore an override request (boolean)
+        @return a reference to the vcs object
+        """
+        vcs = None
+        forProject = True
+        override = False
+        
+        if vcsSystem is None:
+            if len(self.pdata["VCS"]):
+                if self.pdata["VCS"][0] != 'None':
+                    vcsSystem = self.pdata["VCS"][0]
+        else:
+            forProject = False
+        
+        if self.pdata["VCS"] and self.pdata["VCS"][0] != 'None':
+            if self.pudata["VCSOVERRIDE"] and \
+               self.pudata["VCSOVERRIDE"][0] is not None and \
+               not nooverride:
+                vcsSystem = self.pudata["VCSOVERRIDE"][0]
+                override = True
+        
+        if vcsSystem is not None:
+            try:
+                vcs = VCS.factory(vcsSystem)
+            except ImportError:
+                if override:
+                    # override failed, revert to original
+                    self.pudata["VCSOVERRIDE"] = []
+                    return self.initVCS(nooverride = True)
+        
+        if vcs:
+            vcsExists, msg = vcs.vcsExists()
+            if not vcsExists:
+                if override:
+                    # override failed, revert to original
+                    QApplication.restoreOverrideCursor()
+                    QMessageBox.critical(None,
+                        self.trUtf8("Version Control System"),
+                        self.trUtf8("<p>The selected VCS <b>{0}</b> could not be found."
+                                    "<br/>Reverting override.</p><p>{1}</p>")\
+                            .format(vcsSystem, msg))
+                    self.pudata["VCSOVERRIDE"] = []
+                    return self.initVCS(nooverride = True)
+                
+                QApplication.restoreOverrideCursor()
+                QMessageBox.critical(None,
+                    self.trUtf8("Version Control System"),
+                    self.trUtf8("<p>The selected VCS <b>{0}</b> could not be found.<br/>"
+                                "Disabling version control.</p><p>{1}</p>")\
+                        .format(vcsSystem, msg))
+                vcs = None
+                if forProject:
+                    self.pdata["VCS"][0] = 'None'
+                    self.setDirty(True)
+        
+        if vcs and forProject:
+            # set the vcs options
+            try:
+                vcsopt = copy.deepcopy(self.pdata["VCSOPTIONS"][0])
+                vcs.vcsSetOptions(vcsopt)
+            except LookupError:
+                pass
+            # set vcs specific data
+            try:
+                vcsother = copy.deepcopy(self.pdata["VCSOTHERDATA"][0])
+                vcs.vcsSetOtherData(vcsother)
+            except LookupError:
+                pass
+        
+        if vcs is None:
+            self.vcsProjectHelper = VcsProjectHelper(None, self)
+            self.vcsBasicHelper = True
+        else:
+            self.vcsProjectHelper = vcs.vcsGetProjectHelper(self)
+            self.vcsBasicHelper = False
+        if self.vcsMenu is not None:
+            self.vcsProjectHelper.initMenu(self.vcsMenu)
+        return vcs
+        
+    def __showContextMenuVCS(self):
+        """
+        Private slot called before the vcs menu is shown.
+        """
+        self.vcsProjectHelper.showMenu()
+        if self.vcsBasicHelper:
+            self.emit(SIGNAL("showMenu"), "VCS", self.vcsMenu)
+    
+    #########################################################################
+    ## Below is the interface to the checker tools
+    #########################################################################
+    
+    def __showContextMenuChecks(self):
+        """
+        Private slot called before the checks menu is shown.
+        """
+        self.emit(SIGNAL("showMenu"), "Checks", self.checksMenu)
+    
+    #########################################################################
+    ## Below is the interface to the packagers tools
+    #########################################################################
+    
+    def __showContextMenuPackagers(self):
+        """
+        Private slot called before the packagers menu is shown.
+        """
+        self.emit(SIGNAL("showMenu"), "Packagers", self.packagersMenu)
+    
+    #########################################################################
+    ## Below is the interface to the apidoc tools
+    #########################################################################
+    
+    def __showContextMenuApiDoc(self):
+        """
+        Private slot called before the apidoc menu is shown.
+        """
+        self.emit(SIGNAL("showMenu"), "Apidoc", self.apidocMenu)
+    
+    #########################################################################
+    ## Below is the interface to the show tools
+    #########################################################################
+    
+    def __showCodeMetrics(self):
+        """
+        Private slot used to calculate some code metrics for the project files.
+        """
+        files = [os.path.join(self.ppath, file) \
+            for file in self.pdata["SOURCES"] if file.endswith(".py")]
+        self.codemetrics = CodeMetricsDialog()
+        self.codemetrics.show()
+        self.codemetrics.start(files)
+
+    def __showCodeCoverage(self):
+        """
+        Private slot used to show the code coverage information for the project files.
+        """
+        fn = self.getMainScript(True)
+        if fn is None:
+            QMessageBox.critical(self.ui,
+                self.trUtf8("Coverage Data"),
+                self.trUtf8("There is no main script defined for the"
+                    " current project. Aborting"))
+            return
+        
+        tfn = Utilities.getTestFileName(fn)
+        basename = os.path.splitext(fn)[0]
+        tbasename = os.path.splitext(tfn)[0]
+        
+        # determine name of coverage file to be used
+        files = []
+        f = "%s.coverage" % basename
+        tf = "%s.coverage" % tbasename
+        if os.path.isfile(f):
+            files.append(f)
+        if os.path.isfile(tf):
+            files.append(tf)
+        
+        if files:
+            if len(files) > 1:
+                fn, ok = QInputDialog.getItem(\
+                    None,
+                    self.trUtf8("Code Coverage"),
+                    self.trUtf8("Please select a coverage file"),
+                    files,
+                    0, False)
+                if not ok:
+                    return
+            else:
+                fn = files[0]
+        else:
+            return
+        
+        files = [os.path.join(self.ppath, file) \
+            for file in self.pdata["SOURCES"] if file.endswith(".py")]
+        self.codecoverage = PyCoverageDialog()
+        self.codecoverage.show()
+        self.codecoverage.start(fn, files)
+
+    def __showProfileData(self):
+        """
+        Private slot used to show the profiling information for the project.
+        """
+        fn = self.getMainScript(True)
+        if fn is None:
+            QMessageBox.critical(self.ui,
+                self.trUtf8("Profile Data"),
+                self.trUtf8("There is no main script defined for the"
+                    " current project. Aborting"))
+            return
+        
+        tfn = Utilities.getTestFileName(fn)
+        basename = os.path.splitext(fn)[0]
+        tbasename = os.path.splitext(tfn)[0]
+        
+        # determine name of profile file to be used
+        files = []
+        f = "%s.profile" % basename
+        tf = "%s.profile" % tbasename
+        if os.path.isfile(f):
+            files.append(f)
+        if os.path.isfile(tf):
+            files.append(tf)
+        
+        if files:
+            if len(files) > 1:
+                fn, ok = QInputDialog.getItem(\
+                    None,
+                    self.trUtf8("Profile Data"),
+                    self.trUtf8("Please select a profile file"),
+                    files,
+                    0, False)
+                if not ok:
+                    return
+            else:
+                fn = files[0]
+        else:
+            return
+        
+        self.profiledata = PyProfileDialog()
+        self.profiledata.show()
+        self.profiledata.start(fn)
+        
+    def __showContextMenuShow(self):
+        """
+        Private slot called before the show menu is shown.
+        """
+        fn = self.getMainScript(True)
+        if fn is not None:
+            tfn = Utilities.getTestFileName(fn)
+            basename = os.path.splitext(fn)[0]
+            tbasename = os.path.splitext(tfn)[0]
+            self.codeProfileAct.setEnabled(\
+                os.path.isfile("%s.profile" % basename) or \
+                os.path.isfile("%s.profile" % tbasename))
+            self.codeCoverageAct.setEnabled(\
+                os.path.isfile("%s.coverage" % basename) or \
+                os.path.isfile("%s.coverage" % tbasename))
+        else:
+            self.codeProfileAct.setEnabled(False)
+            self.codeCoverageAct.setEnabled(False)
+        
+        self.emit(SIGNAL("showMenu"), "Show", self.showMenu)
+    
+    #########################################################################
+    ## Below is the interface to the diagrams
+    #########################################################################
+    
+    def __showContextMenuGraphics(self):
+        """
+        Private slot called before the graphics menu is shown.
+        """
+        self.emit(SIGNAL("showMenu"), "Graphics", self.graphicsMenu)
+    
+    def handleApplicationDiagram(self):
+        """
+        Private method to handle the application diagram context menu action.
+        """
+        res = QMessageBox.question(None,
+            self.trUtf8("Application Diagram"),
+            self.trUtf8("""Include module names?"""),
+            QMessageBox.StandardButtons(\
+                QMessageBox.No | \
+                QMessageBox.Yes),
+            QMessageBox.Yes)
+        
+        self.applicationDiagram = ApplicationDiagram(self, self.parent(), 
+            noModules = (res != QMessageBox.Yes))
+        self.applicationDiagram.show()
+    
+    #########################################################################
+    ## Below is the interface to the VCS monitor thread
+    #########################################################################
+    
+    def __statusMonitorStatus(self, status, statusMsg):
+        """
+        Private method to receive the status monitor status.
+        
+        It simply reemits the received status.
+        
+        @param status status of the monitoring thread (string, ok, nok or off)
+        @param statusMsg explanotory text for the signaled status (string)
+        """
+        self.emit(SIGNAL("vcsStatusMonitorStatus(QString, QString)"), status, statusMsg)
+        
+    def setStatusMonitorInterval(self, interval):
+        """
+        Public method to se the interval of the VCS status monitor thread.
+        
+        @param interval status monitor interval in seconds (integer)
+        """
+        if self.vcs is not None:
+            self.vcs.setStatusMonitorInterval(interval, self)
+        
+    def getStatusMonitorInterval(self):
+        """
+        Public method to get the monitor interval.
+        
+        @return interval in seconds (integer)
+        """
+        if self.vcs is not None:
+            return self.vcs.getStatusMonitorInterval()
+        else:
+            return 0
+        
+    def setStatusMonitorAutoUpdate(self, auto):
+        """
+        Public method to enable the auto update function.
+        
+        @param auto status of the auto update function (boolean)
+        """
+        if self.vcs is not None:
+            self.vcs.setStatusMonitorAutoUpdate(auto)
+        
+    def getStatusMonitorAutoUpdate(self):
+        """
+        Public method to retrieve the status of the auto update function.
+        
+        @return status of the auto update function (boolean)
+        """
+        if self.vcs is not None:
+            return self.vcs.getStatusMonitorAutoUpdate()
+        else:
+            return False
+        
+    def checkVCSStatus(self):
+        """
+        Public method to wake up the VCS status monitor thread.
+        """
+        if self.vcs is not None:
+            self.vcs.checkVCSStatus()
+        
+    def clearStatusMonitorCachedState(self, name):
+        """
+        Public method to clear the cached VCS state of a file/directory.
+        
+        @param name name of the entry to be cleared (string)
+        """
+        if self.vcs is not None:
+            self.vcs.clearStatusMonitorCachedState(name)
+        
+    def startStatusMonitor(self):
+        """
+        Public method to start the VCS status monitor thread.
+        """
+        if self.vcs is not None:
+            self.vcs.startStatusMonitor(self)
+        
+    def stopStatusMonitor(self):
+        """
+        Public method to stop the VCS status monitor thread.
+        """
+        if self.vcs is not None:
+            self.vcs.stopStatusMonitor()
+    
+    #########################################################################
+    ## Below are the plugin development related methods
+    #########################################################################
+    
+    def __pluginCreatePkgList(self):
+        """
+        Private slot to create a PKGLIST file needed for archive file creation.
+        """
+        pkglist = os.path.join(self.ppath, "PKGLIST")
+        if os.path.exists(pkglist):
+            res = QMessageBox.warning(None,
+                self.trUtf8("Create Package List"),
+                self.trUtf8("<p>The file <b>PKGLIST</b> already"
+                    " exists.</p><p>Overwrite it?</p>"),
+                QMessageBox.StandardButtons(\
+                    QMessageBox.No | \
+                    QMessageBox.Yes),
+                QMessageBox.No)
+            if res != QMessageBox.Yes:
+                return  # don't overwrite
+        
+        # build the list of entries
+        lst = []
+        for key in \
+            ["SOURCES", "FORMS", "RESOURCES", "TRANSLATIONS", "INTERFACES", "OTHERS"]:
+            lst.extend(self.pdata[key])
+        lst.sort()
+        if "PKGLIST" in lst:
+            lst.remove("PKGLIST")
+        
+        # write the file
+        try:
+            pkglistFile = open(pkglist, "wb")
+            pkglistFile.write("\n".join(lst))
+            pkglistFile.close()
+        except IOError, why:
+            QMessageBox.critical(None,
+                self.trUtf8("Create Package List"),
+                self.trUtf8("""<p>The file <b>PKGLIST</b> could not be created.</p>"""
+                            """<p>Reason: {0}</p>""").format(unicode(why)),
+                QMessageBox.StandardButtons(\
+                    QMessageBox.Ok))
+            return
+        
+        if not "PKGLIST" in self.pdata["OTHERS"]:
+            self.appendFile("PKGLIST")
+        
+    def __pluginCreateArchive(self, snapshot = False):
+        """
+        Private slot to create an eric4 plugin archive.
+        
+        @param snapshot flag indicating a snapshot archive (boolean)
+        """
+        pkglist = os.path.join(self.ppath, "PKGLIST")
+        if not os.path.exists(pkglist):
+            QMessageBox.critical(None,
+                self.trUtf8("Create Plugin Archive"),
+                self.trUtf8("""<p>The file <b>PKGLIST</b> does not exist. """
+                            """Aborting...</p>"""),
+                QMessageBox.StandardButtons(\
+                    QMessageBox.Ok))
+            return
+        
+        if len(self.pdata["MAINSCRIPT"]) == 0 or \
+           len(self.pdata["MAINSCRIPT"][0]) == 0:
+            QMessageBox.critical(None,
+                self.trUtf8("Create Plugin Archive"),
+                self.trUtf8("""The project does not have a main script defined. """
+                            """Aborting..."""),
+                QMessageBox.StandardButtons(\
+                    QMessageBox.Ok))
+            return
+        
+        try:
+            pkglistFile = open(pkglist, "rb")
+            names = pkglistFile.read()
+            pkglistFile.close()
+            names = names.splitlines()
+            names.sort()
+        except IOError, why:
+            QMessageBox.critical(None,
+                self.trUtf8("Create Plugin Archive"),
+                self.trUtf8("""<p>The file <b>PKGLIST</b> could not be read.</p>"""
+                            """<p>Reason: {0}</p>""").format(unicode(why)),
+                QMessageBox.StandardButtons(\
+                    QMessageBox.Ok))
+            return
+        
+        archive = \
+            os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0].replace(".py", ".zip"))
+        try:
+            try:
+                archiveFile = zipfile.ZipFile(archive, "w", zipfile.ZIP_DEFLATED)
+            except RuntimeError:
+                archiveFile = zipfile.ZipFile(archive, "w")
+        except IOError, why:
+            QMessageBox.critical(None,
+                self.trUtf8("Create Plugin Archive"),
+                self.trUtf8("""<p>The eric4 plugin archive file <b>{0}</b> could """
+                            """not be created.</p>"""
+                            """<p>Reason: {1}</p>""").format(archive, unicode(why)),
+                QMessageBox.StandardButtons(\
+                    QMessageBox.Ok))
+            return
+        
+        for name in names:
+            try:
+                self.__createZipDirEntries(os.path.split(name)[0], archiveFile)
+                if snapshot and name == self.pdata["MAINSCRIPT"][0]:
+                    snapshotSource, version = self.__createSnapshotSource(\
+                        os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0]))
+                    archiveFile.writestr(name, snapshotSource)
+                else:
+                    archiveFile.write(os.path.join(self.ppath, name), name)
+                    if name == self.pdata["MAINSCRIPT"][0]:
+                        version = self.__pluginExtractVersion(\
+                            os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0]))
+            except OSError, why:
+                QMessageBox.critical(None,
+                    self.trUtf8("Create Plugin Archive"),
+                    self.trUtf8("""<p>The file <b>{0}</b> could not be stored """
+                                """in the archive. Ignoring it.</p>"""
+                                """<p>Reason: {1}</p>""")\
+                                .format(os.path.join(self.ppath, name), unicode(why)),
+                    QMessageBox.StandardButtons(\
+                        QMessageBox.Ok))
+        archiveFile.writestr("VERSION", version)
+        archiveFile.close()
+        
+        if not archive in self.pdata["OTHERS"]:
+            self.appendFile(archive)
+        
+        QMessageBox.information(None,
+            self.trUtf8("Create Plugin Archive"),
+            self.trUtf8("""<p>The eric4 plugin archive file <b>{0}</b> was """
+                        """created successfully.</p>""").format(archive),
+            QMessageBox.StandardButtons(\
+                QMessageBox.Ok))
+    
+    def __pluginCreateSnapshotArchive(self):
+        """
+        Private slot to create an eric4 plugin archive snapshot release.
+        """
+        self.__pluginCreateArchive(True)
+    
+    def __createZipDirEntries(self, path, zipFile):
+        """
+        Private method to create dir entries in the zip file.
+        
+        @param path name of the directory entry to create (string)
+        @param zipFile open ZipFile object (zipfile.ZipFile)
+        """
+        if path == "" or path == "/" or path == "\\":
+            return
+        
+        if not path.endswith("/") and not path.endswith("\\"):
+            path = "%s/" % path
+        
+        if not path in zipFile.namelist():
+            self.__createZipDirEntries(os.path.split(path[:-1])[0], zipFile)
+            zipFile.writestr(path, "")
+    
+    def __createSnapshotSource(self, filename):
+        """
+        Private method to create a snapshot plugin version.
+        
+        The version entry in the plugin module is modified to signify
+        a snapshot version. This method appends the string "-snapshot-"
+        and date indicator to the version string.
+        
+        @param filename name of the plugin file to modify (string)
+        @return modified source (string), snapshot version string (string)
+        """
+        try:
+            f = open(filename, "r")
+            sourcelines = f.readlines()
+            f.close()
+        except IOError, why:
+            QMessageBox.critical(None,
+                self.trUtf8("Create Plugin Archive"),
+                self.trUtf8("""<p>The plugin file <b>{0}</b> could """
+                            """not be read.</p>"""
+                            """<p>Reason: {1}</p>""").format(archive, unicode(why)),
+                QMessageBox.StandardButtons(\
+                    QMessageBox.Ok))
+            return ""
+        
+        lineno = 0
+        while lineno < len(sourcelines):
+            if sourcelines[lineno].startswith("version = "):
+                # found the line to modify
+                datestr = time.strftime("%Y%m%d")
+                lineend = sourcelines[lineno].replace(sourcelines[lineno].rstrip(), "")
+                sversion = "%s-snapshot-%s" % \
+                    (sourcelines[lineno].replace("version = ", "").strip()[1:-1], 
+                     datestr)
+                sourcelines[lineno] = '%s + "-snapshot-%s"%s' % \
+                    (sourcelines[lineno].rstrip(), datestr, lineend)
+                break
+            
+            lineno += 1
+        
+        return "".join(sourcelines), sversion
+    
+    def __pluginExtractVersion(self, filename):
+        """
+        Private method to extract the version number entry.
+        
+        @param filename name of the plugin file to modify (string)
+        @return version string (string)
+        """
+        version = "0.0.0"
+        try:
+            f = open(filename, "r")
+            sourcelines = f.readlines()
+            f.close()
+        except IOError, why:
+            QMessageBox.critical(None,
+                self.trUtf8("Create Plugin Archive"),
+                self.trUtf8("""<p>The plugin file <b>{0}</b> could """
+                            """not be read.</p>"""
+                            """<p>Reason: {1}</p>""").format(archive, unicode(why)),
+                QMessageBox.StandardButtons(\
+                    QMessageBox.Ok))
+            return ""
+        
+        lineno = 0
+        while lineno < len(sourcelines):
+            if sourcelines[lineno].startswith("version = "):
+                version = sourcelines[lineno].replace("version = ", "").strip()[1:-1]
+                break
+            
+            lineno += 1
+        
+        return version

eric ide

mercurial