# -*- coding: utf-8 -*-

# Copyright (c) 2002, 2003 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the project management functionality.
"""

import os
import re
import time
import shutil
import glob

from xml.sax import make_parser
from xml.sax.saxutils import escape

from qt import *

from AddLanguageDialog import AddLanguageDialog
from AddFileDialog import AddFileDialog
from AddDirectoryDialog import AddDirectoryDialog
from PropertiesDialog import PropertiesDialog
from AddFoundFilesDialog import AddFoundFilesDialog
from ProjectHandler import ProjectHandler
from SessionHandler import SessionHandler

from Icons.FileIcons import IconFilePython, IconFileIDL
from Icons.ToolIcons import IconLinguist, IconDesigner
from ProjectIcons import *

import VCS
from VCS.OptionsDialog import vcsOptionsDialog
from VCS.CommandOptionsDialog import vcsCommandOptionsDialog
from VCS.NewProjectDialog import vcsNewProjectOptionsDialog
from VCS.LoginDialog import vcsLoginDialog
from VCS.LogoutDialog import vcsLogoutDialog

from DocumentationTools import DocumentationTools

from Checks.TabnannyDialog import TabnannyDialog
from Checks.SyntaxCheckerDialog import SyntaxCheckerDialog

from UI.CodeMetricsDialog import CodeMetricsDialog
from UI.PyCoverageDialog import PyCoverageDialog
from UI.PyProfileDialog import PyProfileDialog

import Preferences
import Utilities

class Project(QObject):
    """
    Module implementing the project management functionality.
    
    Signals
    
        dirty(int) -- emitted when the dirty state changes
        
        projectSessionLoaded() -- emitted after a project session file was loaded
        
        projectLanguageAdded(string) -- emitted after a new language was added
        
        projectFormAdded(string) -- emitted after a new form was added
        
        projectSourceAdded(string) -- emitted after a new source file was added
        
        projectInterfaceAdded(string) -- emitted after a new IDL file was added
        
        newProject() -- emitted after a new project was generated
        
        pythonFile(string) -- emitted after a project file was read to open the main script
        
        projectOpened() -- emitted after a project file was read
        
        projectClosed() -- emitted after a project was closed
        
        projectOthersAdded(string) -- emitted after a file or directory was added
            to the OTHERS project data area
    """
    keynames = [
        "DESCRIPTION", "VERSION",
        "AUTHOR", "EMAIL",
        "SOURCES", "FORMS",
        "TRANSLATIONS", "MAINSCRIPT",
        "VCS", "VCSOPTIONS",
        "HAPPYDOCPARMS", "OTHERS",
        "INTERFACES"
    ]
    
    nonsplitters = [
        "DESCRIPTION", "VERSION",
        "AUTHOR", "EMAIL",
        "VCS", "VCSOPTIONS",
        "HAPPYDOCPARMS"
    ]
            
    def __init__(self, parent = None, filename = None):
        """
        Constructor
        
        Arguments
        
            parent -- parent widget (usually the ui object) (QWidget)
            
            filename -- optional filename of a project file to open (string)
        """
        QObject.__init__(self, parent)
        
        self.init()
        
        self.recent = QStringList()
        rp, ok = Preferences.Prefs.settings.readListEntry('/eric3/Recent/Projects')
        if ok:
            self.recent = rp
        
        if filename is not None:
            self.readProject(filename)
            
        self.vcs = self.initVCS()
        self.doctools = DocumentationTools(self, self.parent())
        
    def init(self):
        """
        Private method to initialize the project data part.
        """
        self.loaded = 0
        self.dirty = 0
        self.pfile = ""
        self.ppath = ""
        self.translationsRoot = ""
        self.name = ""
        self.opened = 0
        self.subdirs = [""] # record the project dir as a relative path (i.e. empty path)
        self.otherssubdirs = []
        self.vcs = None
        self.dbgCmdline = ''
        self.dbgWd = ''
        self.dbgReportExceptions = 1
        self.dbgExcList = []
        
        self.pdata = {}
        for key in self.__class__.keynames:
            self.pdata[key] = []
        self.pdata["AUTHOR"] = ['']
        self.pdata["EMAIL"] = ['']
    
    def setDirty(self, b):
        """
        Private method to set the dirty state.
        
        It emits the signal dirty(int).
        
        Arguments
        
            b -- dirty state (boolean)
        """
        self.dirty = b
        self.saveAct.setEnabled(b)
        self.emit(PYSIGNAL("dirty"), (b,))
        
    def isDirty(self):
        """
        Public method to return the dirty state.
        
        Returns
        
            dirty state (boolean)
        """
        return self.dirty
        
    def isOpen(self):
        """
        Public method to return the opened state.
        
        Returns
        
            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.
        
        Arguments
        
            index -- key of the list to be checked (string)
        """
        removed = 0
        removelist = []
        for file in self.pdata[index]:
            if not os.path.exists(os.path.join(self.ppath, file)):
                removelist.append(file)
                removed = 1
                
        if removed:
            for file in removelist:
                self.pdata[index].remove(file)
            self.setDirty(1)
        
    def ossepToToken(self, filelist):
        """
        Private method to ensure OS independance of the project file.
        
        This method converts the pathname separator (os.sep)
        to a platform neutral token (%os.sep%).
        
        Arguments
        
            filelist -- list of filenames to convert (list of strings)
            
        Returns
        
            the converted filelist
        """
        newlist = []
        for file in filelist:
            newlist.append(file.replace(os.sep, '%os.sep%'))
        return newlist
        
    def tokenToOssep(self, filelist):
        """
        Private method to ensure OS independance of the project file.
        
        This method converts the platform neutral separator token
        (%os.sep%) to the pathname separator (os.sep).
        
        Arguments
        
            filelist -- list of filenames to convert (list of strings)
            
        Returns
        
            the converted filelist
        """
        newlist = []
        for file in filelist:
            newlist.append(file.replace('%os.sep%', os.sep))
        return newlist
        
    def spaceToToken(self, filelist):
        """
        Private method to ensure OS independance of the project file.
        
        This method converts a space to a token (%20).
        
        Arguments
        
            filelist -- list of filenames to convert (list of strings)
            
        Returns
        
            the converted filelist
        """
        newlist = []
        for file in filelist:
            newlist.append(file.replace(' ', '%20'))
        return newlist
        
    def tokenToSpace(self, filelist):
        """
        Private method to ensure OS independance of the project file.
        
        This method converts the space token (%20) to a space.
        
        Arguments
        
            filelist -- list of filenames to convert (list of strings)
            
        Returns
        
            the converted filelist
        """
        newlist = []
        for file in filelist:
            newlist.append(file.replace('%20', ' '))
        return newlist
        
    def readProject(self, fn):
        """
        Public method to read in a project (.e3p) file.
        
        Arguments
        
            fn -- filename of the project file to be read (string or QString)
            
        Returns
        
            flag indicating success
        """
        fn = str(fn)
        try:
            f = open(fn, "rb")
            line = f.readline()
            f.close()
        except:
            QMessageBox.critical(None,
                self.trUtf8("Read project file"),
                self.trUtf8("The project file <b>%1</b> could not be read.").arg(fn),
                QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
            return 0
            
        self.pfile = os.path.abspath(fn)
        self.ppath = os.path.abspath(os.path.dirname(fn))
        
        # insert filename into list of recently opened projects
        self.recent.remove(self.pfile)
        self.recent.prepend(self.pfile)
        if len(self.recent) > 9:
            self.recent = self.recent[:9]
        
        # now read the file
        if line.startswith('<?xml'):
            res = self.readXMLProject(fn)
        else:
            res = self.readOldProject(fn)
            
        if res:
            if len(self.pdata["MAINSCRIPT"]) == 1:
                (self.translationsRoot, dummy) = os.path.splitext(self.pdata["MAINSCRIPT"][0])
                
            (self.name, dummy) = os.path.splitext(os.path.basename(fn))
            
            # get the names of subdirectories the files are stored
            for fn in self.pdata["SOURCES"] + self.pdata["FORMS"] + self.pdata["INTERFACES"]:
                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)
                
            # check, if the files of the project still exist in the project directory
            self.checkFilesExist("SOURCES")
            self.checkFilesExist("FORMS")
            self.checkFilesExist("INTERFACES")
            
        return res

    def readOldProject(self, fn):
        """
        Public method to read in a project (.e3p) file.
        
        Arguments
        
            fn -- filename of the project file to be read (string or QString)
            
        Returns
        
            flag indicating success
        """
        fn = str(fn)
        try:
            f = open(fn, "rb")
            lines = f.readlines()
            f.close()
        except:
            QMessageBox.critical(None,
                self.trUtf8("Read project file"),
                self.trUtf8("The project file <b>%1</b> could not be read.").arg(fn),
                QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
            return 0
            
        # now parse the lines read
        lastKey = ''
        cont = 0
        nonsplitter = 0
        pat = re.compile(r'(\+=|=)')
        cpat = re.compile('(?:\s#|^#).*$')
        for line in lines:
            # filter comments
            line = cpat.sub('', line).strip()
            if len(line) == 0:
                continue
            
            # normalize assignment symbol
            line = pat.sub(' \\1 ', line)
            
            # split the line and process it
            elements = line.split(None, 2)
            if not (cont and nonsplitter) and \
                elements[0] not in self.__class__.nonsplitters:
                elements = line.split()
                nonsplitter = 0
            else:
                nonsplitter = 1
                if cont:
                    if line == '\\':
                        elements = [' ', line]
                    else:
                        elements = [line]
            if elements[0] in self.__class__.keynames:
                lastKey = elements[0]
                if elements[1] == '+=':
                    cont = 1
                else:
                    cont = 0
                ind = 2
            elif cont:
                ind = 0
            else:
                continue
                
            while ind < len(elements):
                if elements[ind] == '\\':
                    cont = 1
                    break
                else:
                    if elements[ind][-1] == '\\':
                        if cont:
                            self.pdata[lastKey].append(elements[ind][:-1])
                        else:
                            self.pdata[lastKey] = [elements[ind][:-1]]
                        cont = 1
                        break
                    else:
                        if cont:
                            self.pdata[lastKey].append(elements[ind])
                        else:
                            self.pdata[lastKey] = [elements[ind]]
                        ind += 1
                        if ind < len(elements):
                            cont = 1
                        else:
                            cont = 0
                        
        # convert the filenames read
        self.pdata["SOURCES"] = self.tokenToOssep(self.pdata["SOURCES"])
        self.pdata["SOURCES"] = self.tokenToSpace(self.pdata["SOURCES"])
        self.pdata["FORMS"] = self.tokenToOssep(self.pdata["FORMS"])
        self.pdata["FORMS"] = self.tokenToSpace(self.pdata["FORMS"])
        self.pdata["OTHERS"] = self.tokenToOssep(self.pdata["OTHERS"])
        self.pdata["OTHERS"] = self.tokenToSpace(self.pdata["OTHERS"])
        self.pdata["INTERFACES"] = self.tokenToOssep(self.pdata["INTERFACES"])
        self.pdata["INTERFACES"] = self.tokenToSpace(self.pdata["INTERFACES"])
        
        return 1
        
    def readXMLProject(self, fn):
        """
        Public method to read the project data from an XML file.
        
        Arguments
        
            fn -- filename of the project file to be read (string or QString)
            
        Returns
        
            flag indicating success
        """
        fn = str(fn)
        
        parser = make_parser()
        handler = ProjectHandler(self)
        
        parser.setContentHandler(handler)
        
        try:
            f = open(fn, "rb")
            parser.parse(f)
            f.close()
        except IOError:
            QMessageBox.critical(None,
                self.trUtf8("Read project file"),
                self.trUtf8("The project file <b>%1</b> could not be read.").arg(fn),
                QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
            return 0
            
        return 1
        
    def writeProject(self, fn = None):
        """
        Public method to save the project infos to a project file.
        
        Arguments
        
            fn -- optional filename of the project file to be written.
                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.
                
        Returns
        
            flag indicating success
        """
        if self.vcs is not None:
            self.pdata["VCSOPTIONS"] = [str(self.vcs.vcsGetOptions())]
            
        if fn is None:
            fn = self.pfile
            
        if Preferences.getProject("XMLProjectFile"):
            res = self.writeXMLProject(fn)
        else:
            res = self.writeOldProject(fn)
            
        if res:
            self.pfile = os.path.abspath(fn)
            self.ppath = os.path.abspath(os.path.dirname(fn))
            (self.name, dummy) = os.path.splitext(os.path.basename(fn))
            self.setDirty(0)
            
            # insert filename into list of recently opened projects
            self.recent.remove(self.pfile)
            self.recent.prepend(self.pfile)
            if len(self.recent) > 9:
                self.recent = self.recent[:9]
                
        return res
        
    def writeOldProject(self, fn):
        """
        Public method to save the project infos to a project file.
        
        Arguments
        
            fn -- optional filename of the project file to be written.
                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.
                
        Returns
        
            flag indicating success
        """
        try:
            pf = open(fn, "wb")
            
            # write some header info
            if len(self.name) == 0:
                (self.name, dummy) = os.path.splitext(os.path.basename(fn))
            pf.write('#%s# Project file for project "%s"%s' % \
                (os.linesep, self.name, os.linesep))
            pf.write('# Saved: %s%s#%s' % \
                (time.strftime('%Y-%m-%d, %H:%M:%S'), os.linesep, os.linesep))
            pf.write('# Copyright (C) %s %s <%s>%s#%s%s' % \
                (time.strftime('%Y'), self.pdata["AUTHOR"][0], 
                 self.pdata["EMAIL"][0], os.linesep, os.linesep, os.linesep))
            
            for key in self.__class__.keynames:
                if key in ["SOURCES", "FORMS", "OTHERS", "INTERFACES"]:
                    # convert the filenames to be written
                    list = self.ossepToToken(self.pdata[key])
                    list = self.spaceToToken(list)
                else:
                    list = self.pdata[key]
                if len(list) > 0:
                    pf.write('%s = ' % key)
                    last = len(list) - 1
                    if last > 0:
                        pf.write('%s \\%s' % (list[0], os.linesep))
                        for i in range(1, last):
                            pf.write('\t%s \\%s' % (list[i], os.linesep))
                        pf.write('\t%s %s%s' % (list[last], os.linesep, os.linesep))
                    else:
                        pf.write('%s %s%s' % (list[0], os.linesep, os.linesep))
                        
            pf.close()
        except:
            QMessageBox.critical(None,
                self.trUtf8("Write project file"),
                self.trUtf8("The project file <b>%1</b> could not be written.").arg(fn),
                QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
            return 0
            
        return 1
        
    def _write(self, s, newline=1):
        """
        Private method used to do the real write operation.
        
        Arguments
        
            s -- string to be written to the XML file
            
            newline -- flag indicating a linebreak
        """
        self.pf.write("%s%s" % (s.encode('utf-8'), 
            newline and os.linesep or ""))
        
    def writeXMLProject(self, fn = None):
        """
        Public method to write the project data to an XML file.
        """
        try:
            self.pf = open(fn, "wb")
            
            # generate a new XML document
            self._write("<?xml version='1.0' encoding='UTF-8'?>")
            
            # add some generation comments
            if len(self.name) == 0:
                (self.name, dummy) = os.path.splitext(os.path.basename(fn))
            self._write("<!--Project file for project %s-->" % self.name)
            self._write("<!--Saved: %s-->" % time.strftime('%Y-%m-%d, %H:%M:%S'))
            self._write("<!--Copyright (C) %s %s, %s-->" % \
                    (time.strftime('%Y'), self.pdata["AUTHOR"][0], 
                     self.pdata["EMAIL"][0]))
            self._write("<Project>")
            
            # do description
            if self.pdata["DESCRIPTION"]:
                self._write("  <Description>", 0)
                for d in self.pdata["DESCRIPTION"][:-1]:
                    self._write(escape("%s" % d.strip()))
                self._write("%s</Description>" % escape(self.pdata["DESCRIPTION"][-1].strip()))
            
            #do version, author and email
            for key in ["VERSION", "AUTHOR", "EMAIL"]:
                element = key.capitalize()
                if self.pdata[key]:
                    self._write("  <%s>%s</%s>" % (element, self.pdata[key][0], element))
                
            # do the sources
            self._write("  <Sources>")
            for name in self.pdata["SOURCES"]:
                self._write("    <Source>")
                parts = name.split(os.sep)
                for p in parts[:-1]:
                    self._write("      <Dir>%s</Dir>" % p)
                self._write("      <Name>%s</Name>" % parts[-1])
                self._write("    </Source>")
            self._write("  </Sources>")
            
            # do the forms
            self._write("  <Forms>")
            for name in self.pdata["FORMS"]:
                self._write("    <Form>")
                parts = name.split(os.sep)
                for p in parts[:-1]:
                    self._write("      <Dir>%s</Dir>" % p)
                self._write("      <Name>%s</Name>" % parts[-1])
                self._write("    </Form>")
            self._write("  </Forms>")
            
            # do the translations
            self._write("  <Translations>")
            for name in self.pdata["TRANSLATIONS"]:
                self._write("    <Translation>")
                parts = name.split(os.sep)
                for p in parts[:-1]:
                    self._write("      <Dir>%s</Dir>" % p)
                self._write("      <Name>%s</Name>" % parts[-1])
                self._write("    </Translation>")
            self._write("  </Translations>")
            
            # do the interfaces (IDL)
            self._write("  <Interfaces>")
            for name in self.pdata["INTERFACES"]:
                self._write("    <Interface>")
                parts = name.split(os.sep)
                for p in parts[:-1]:
                    self._write("      <Dir>%s</Dir>" % p)
                self._write("      <Name>%s</Name>" % parts[-1])
                self._write("    </Interface>")
            self._write("  </Interfaces>")
            
            # do the others
            self._write("  <Others>")
            for name in self.pdata["OTHERS"]:
                self._write("    <Other>")
                parts = name.split(os.sep)
                for p in parts[:-1]:
                    self._write("      <Dir>%s</Dir>" % p)
                self._write("      <Name>%s</Name>" % parts[-1])
                self._write("    </Other>")
            self._write("  </Others>")
            
            # do the main script
            if self.pdata["MAINSCRIPT"]:
                self._write("  <MainScript>")
                parts = self.pdata["MAINSCRIPT"][0].split(os.sep)
                for p in parts[:-1]:
                    self._write("    <Dir>%s</Dir>" % p)
                self._write("    <Name>%s</Name>" % parts[-1])
                self._write("  </MainScript>")
            
            # do the vcs stuff
            self._write("  <Vcs>")
            if self.pdata["VCS"]:
                self._write("    <VcsType>%s</VcsType>" % self.pdata["VCS"][0])
            if self.pdata["VCSOPTIONS"]:
                self._write("    <VcsOptions>%s</VcsOptions>" % \
                    escape(self.pdata["VCSOPTIONS"][0]))
            self._write("  </Vcs>")
            
            # do the happydoc stuff
            self._write("  <HappyDoc>")
            if self.pdata["HAPPYDOCPARMS"]:
                self._write("    <HappyDocParams>%s</HappyDocParams>" % \
                    escape(self.pdata["HAPPYDOCPARMS"][0]))
            self._write("  </HappyDoc>")
            
            self._write("</Project>")
            self.pf.close()
            
        except IOError:
            QMessageBox.critical(None,
                self.trUtf8("Write project file"),
                self.trUtf8("The project file <b>%1</b> could not be written.").arg(fn),
                QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
            return 0
        
        return 1
        
    def readSession(self, quiet=0):
        """
        Private method to read in the project session file (.e3s)
        
        Arguments
        
            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 project session"),
                    self.trUtf8("Please save the project first."),
                    QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
            return
            
        fn, ext = os.path.splitext(self.pfile)
        fn = '%s.e3s' % fn
        
        try:
            f = open(fn, "rb")
            line = f.readline()
            f.close()
        except:
            if not quiet:
                QMessageBox.critical(None,
                    self.trUtf8("Read project session"),
                    self.trUtf8("The project session <b>%1</b> could not be read.").arg(fn),
                    QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
            return
            
        # now read the file
        if line.startswith('<?xml'):
            res = self.readXMLSession(fn, quiet)
        else:
            res = self.readOldSession(fn, quiet)
    
    def readOldSession(self, fn, quiet=0):
        """
        Private method to read in the project session file (.e3s)
        
        The data read is:
            - all open source filenames (fn:)
            - the active window (aw:)
            - all breakpoints (bp:)
            - the commandline (cl:)
            - the working directory (wd:)
            - the exception reporting flag (er:)
            - the list of exception types to be highlighted (ex:)
            - all bookmarks (bm:)
            
        Arguments
        
            fn -- filename of the project file to be read (string or QString)
            
            quiet -- flag indicating quiet operations.
                If this flag is true, no errors are reported.
        """
        try:
            sf = open(fn, "rb")
            lines = sf.readlines()
            sf.close()
        except:
            if not quiet:
                QMessageBox.critical(None,
                    self.trUtf8("Read project session"),
                    self.trUtf8("The project session file <b>%1</b> could not be read.")
                        .arg(fn),
                    QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
            return
            
        # get the needed objects
        vm = self.parent().getViewManager()
        dbg = self.parent().getDebugger()
        
        # clear all breakpoints and bookmarks first (in case we are rereading a session file)
        files = self.getSources(1)
        for file in files:
            editor = vm.getOpenEditor(file)
            if editor is not None:
                editor.handleClearBreakpoints()
                editor.handleClearBookmarks()

        # iterate over all lines
        self.dbgExcList = []
        for line in lines:
            line = line.strip()
            if line.startswith('fn:'):
                fname = line[4:]
                vm.handleOpen(fname)
            elif line.startswith('aw:'):
                fname = line[4:]
                vm.handleOpen(fname)
            elif line.startswith('bp:'):
                try:
                    fname, lineno, cond, temp, enabled, count = eval(line[4:])
                except ValueError:
                    # it was an old session file
                    fname, lineno, cond = eval(line[4:])
                    temp, enabled, count = 0, 1, 0
                if cond == 'None' or cond == '':
                    cond = None
                dbg.newBreakpointWithProperties(fname, lineno, (cond, temp, enabled, count))
            elif line.startswith('cl:'):
                cmdline = line[4:]
                dbg.setArgvHistory(cmdline)
                self.dbgCmdline = cmdline
            elif line.startswith('wd:'):
                wd = line[4:]
                dbg.setWdHistory(wd)
                self.dbgWd = wd
            elif line.startswith('er:'):
                try:
                    exc = int(line[4:])
                except:
                    exc = 1
                dbg.setExceptionReporting(exc)
                self.dbgReportExceptions = exc
            elif line.startswith('ex:'):
                exc = line[4:]
                self.dbgExcList.append(exc)
            elif line.startswith('bm:'):
                fname, lineno = eval(line[4:])
                editor = vm.getOpenEditor(fname)
                if editor is not None:
                    editor.handleBookmark(lineno)
                
        dbg.setExcList(self.dbgExcList)
        self.emit(PYSIGNAL('projectSessionLoaded'),())
        
    def readXMLSession(self, fn, quiet=0):
        """
        Public method to read the project data from an XML file.
        
        The data read is:
            - all open source filenames
            - the active window
            - all breakpoints
            - the commandline
            - the working directory
            - the exception reporting flag
            - the list of exception types to be highlighted
            - all bookmarks
            
        Arguments
        
            fn -- filename of the project file to be read (string or QString)
            
            quiet -- flag indicating quiet operations.
                If this flag is true, no errors are reported.
        """
        parser = make_parser()
        handler = SessionHandler(self)
        
        parser.setContentHandler(handler)
        
        try:
            f = open(fn, "rb")
            parser.parse(f)
            f.close()
        except IOError:
            if not quiet:
                QMessageBox.critical(None,
                    self.trUtf8("Read project session"),
                    self.trUtf8("The project session file <b>%1</b> could not be read.")
                        .arg(fn),
                    QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
        
    def writeSession(self, quiet=0):
        """
        Private method to write the session data to a file (.e3s).
        
        Arguments
        
            quiet -- flag indicating quiet operations.
                If this flag is true, no errors are reported.
        """
        if Preferences.getProject("XMLProjectFile"):
            self.writeXMLSession(quiet)
        else:
            self.writeOldSession(quiet)
        
    def writeOldSession(self, quiet=0):
        """
        Private method to write the session data to a file (.e3s).
        
        The data saved is:
            - all open source filenames belonging to the project (fn:)
            - the active window, if it belongs to the project (aw:)
            - all breakpoints (bp:)
            - the commandline (cl:)
            - the working directory (wd:)
            - the exception reporting flag (er:)
            - the list of exception types to be highlighted (ex:)
            - the bookmarks of files belonging to the project (bm:)
            
        Arguments
        
            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("Save project session"),
                    self.trUtf8("Please save the project first."),
                    QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
            return
            
        fn, ext = os.path.splitext(self.pfile)
        fn = '%s.e3s' % fn
        
        try:
            sf = open(fn, "wb")
            sf.write('#%s# eric3 project session file%s' % (os.linesep, os.linesep))
            sf.write('# This file was generated automatically, do not edit.%s' % os.linesep)
            sf.write('# Saved: %s%s#%s' % \
                (time.strftime('%Y-%m-%d, %H:%M:%S'), os.linesep, os.linesep))
            sf.write(os.linesep)
            
            # step 1: save all open project filenames and the active window
            vm = self.parent().getViewManager()
            allOpenFiles = vm.getOpenFilenames()
            for of in allOpenFiles:
                if str(of).startswith(self.ppath):
                    sf.write('fn: %s%s' % (of, os.linesep))
            sf.write(os.linesep)
            aw = vm.getActiveName()
            if aw and str(aw).startswith(self.ppath):
                sf.write('aw: %s%s' % (aw, os.linesep))
            sf.write(os.linesep)
            
            # step 2: save all breakpoints
            dbg = self.parent().getDebugger()
            if Preferences.getProject("SessionAllBreakpoints"):
                bpDict = dbg.getAllBreakpoints()
            else:
                bpDict = dbg.getProjectBreakpoints()
            for fname, bpList in bpDict.items():
                for (lineno, cond, temp, enabled, count) in bpList:
                    sf.write('bp: %s%s' % \
                        (str((fname, lineno, str(cond), temp, enabled, count)), os.linesep))
            sf.write(os.linesep)
            
            # step 3: save the debug info
            sf.write('cl: %s%s' % (self.dbgCmdline, os.linesep))
            sf.write('wd: %s%s' % (self.dbgWd, os.linesep))
            sf.write('er: %d%s' % (self.dbgReportExceptions, os.linesep))
            for exc in self.dbgExcList:
                sf.write('ex: %s%s' % (exc, os.linesep))
            sf.write(os.linesep)
            
            # step 4: save bookmarks of all open project files
            for of in allOpenFiles:
                if str(of).startswith(self.ppath):
                    editor = vm.getOpenEditor(of)
                    for bookmark in editor.getBookmarks():
                        sf.write('bm: %s%s' % (str((of, bookmark)), os.linesep))
            sf.write(os.linesep)
            
            sf.close()
        except:
            if not quiet:
                QMessageBox.critical(None,
                    self.trUtf8("Save project session"),
                    self.trUtf8("The project session file <b>%1</b> could not be written.")
                        .arg(fn),
                    QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
            
    def writeXMLSession(self, quiet = 0):
        """
        Public method to write the session data to an XML file (.e3s).
        
        The data saved is:
            - all open source filenames belonging to the project
            - the active window, if it belongs to the project
            - all breakpoints
            - the commandline
            - the working directory
            - the exception reporting flag
            - the list of exception types to be highlighted
            - the bookmarks of files belonging to the project
            
        Arguments
        
            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("Save project session"),
                    self.trUtf8("Please save the project first."),
                    QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
            return
            
        fn, ext = os.path.splitext(self.pfile)
        fn = '%s.e3s' % fn
        
        try:
            self.pf = open(fn, "wb")
            
            # generate a new XML document
            self._write("<?xml version='1.0' encoding='UTF-8'?>")
            
            # add some generation comments
            if len(self.name) == 0:
                (self.name, dummy) = os.path.splitext(os.path.basename(fn))
            self._write("<!--eric3 session file for project %s-->" % self.name)
            self._write("<!--This file was generated automatically, do not edit.-->")
            self._write("<!--Saved: %s-->" % time.strftime('%Y-%m-%d, %H:%M:%S'))
            self._write("<Session>")
            
            # step 1: save all open project filenames and the active window
            vm = self.parent().getViewManager()
            allOpenFiles = vm.getOpenFilenames()
            self._write("  <Filenames>")
            for of in allOpenFiles:
                if str(of).startswith(self.ppath):
                    self._write("    <Filename>%s</Filename>" % of)
            self._write("  </Filenames>")
            
            aw = vm.getActiveName()
            if aw and str(aw).startswith(self.ppath):
                self._write("  <ActiveWindow>%s</ActiveWindow>" % aw)
            
            # step 2: save all breakpoints
            dbg = self.parent().getDebugger()
            if Preferences.getProject("SessionAllBreakpoints"):
                bpDict = dbg.getAllBreakpoints()
            else:
                bpDict = dbg.getProjectBreakpoints()
            self._write("  <Breakpoints>")
            for fname, bpList in bpDict.items():
                for (lineno, cond, temp, enabled, count) in bpList:
                    self._write("    <Breakpoint>")
                    self._write("      <BpFilename>%s</BpFilename>" % fname)
                    self._write('      <Linenumber value="%d" />' % lineno)
                    self._write("      <Condition>%s</Condition>" % escape(str(cond)))
                    self._write('      <Temporary value="%d" />' % temp)
                    self._write('      <Enabled value="%d" />' % enabled)
                    self._write('      <Count value="%d" />' % count)
                    self._write("    </Breakpoint>")
            self._write("  </Breakpoints>")
            
            # step 3: save the debug info
            self._write("  <DebugInfo>")
            self._write("    <CommandLine>%s</CommandLine>" % escape(str(self.dbgCmdline)))
            self._write("    <WorkingDirectory>%s</WorkingDirectory>" % self.dbgWd)
            self._write('    <ReportExceptions value="%d" />' % self.dbgReportExceptions)
            self._write("    <Exceptions>")
            for exc in self.dbgExcList:
                self._write("      <Exception>%s</Exception>" % exc)
            self._write("    </Exceptions>")
            self._write("  </DebugInfo>")
            
            # step 4: save bookmarks of all open project files
            self._write("  <Bookmarks>")
            for of in allOpenFiles:
                if str(of).startswith(self.ppath):
                    editor = vm.getOpenEditor(of)
                    for bookmark in editor.getBookmarks():
                        self._write("    <Bookmark>")
                        self._write("      <BmFilename>%s</BmFilename>" % of)
                        self._write('      <Linenumber value="%d" />' % bookmark)
                        self._write("    </Bookmark>")
            self._write("  </Bookmarks>")
            
            self._write("</Session>")
            self.pf.close()
            
        except IOError:
            if not quiet:
                QMessageBox.critical(None,
                    self.trUtf8("Save project session"),
                    self.trUtf8("The project session file <b>%1</b> could not be written.")
                        .arg(fn),
                    QMessageBox.Abort, QMessageBox.NoButton, QMessageBox.NoButton)
        
    def setDbgInfo(self, argv, wd, excReporting, excList):
        """
        Public method to set the debugging information.
        
        Arguments
        
            argv -- command line arguments to be used (string or QString)
            
            wd -- working directory (string or QString)
            
            excReporting -- flag indicating the highlighting of exceptions
            
            excList -- list of exceptions to be highlighted (list of string)
        """
        self.dbgCmdline = str(argv)
        self.dbgWd = str(wd)
        self.dbgReportExceptions = excReporting
        self.dbgExcList = excList[:]    # keep a copy of the list
    
    def addLanguage(self):
        """
        Public slot used to add a language to the project.
        """
        if self.translationsRoot == '':
            QMessageBox.critical(None,
                self.trUtf8("Add Language"),
                self.trUtf8("You have to specify the mainscript of the project first."),
                QMessageBox.Ok, QMessageBox.NoButton, 
                QMessageBox.NoButton)
            return
                
        dlg = AddLanguageDialog(self.parent())
        if dlg.exec_loop() == QDialog.Accepted:
            lang = str(dlg.getSelectedLanguage())
            langFile = '%s_%s.ts' % (self.translationsRoot, lang)
            self.pdata["TRANSLATIONS"].append(langFile)
            self.emit(PYSIGNAL('projectLanguageAdded'), (langFile,))
            self.setDirty(1)
            
    def removeLanguage(self, lang):
        """
        Public slot to remove a translation from the project.
        
        The translation file is not deleted from the project directory.
        
        Arguments
        
            lang -- the language to be removed (string)
        """
        langFile = '%s_%s.ts' % (self.translationsRoot, lang)
        self.pdata["TRANSLATIONS"].remove(langFile)
        self.setDirty(1)
        
    def deleteLanguage(self, lang):
        """
        Public slot to delete a translation from the project directory.
        
        Arguments
        
            lang -- the language to be removed (string)
            
        Returns
        
            flag indicating success
        """
        langFile = '%s_%s.ts' % (self.translationsRoot, lang)
        try:
            os.remove(os.path.join(self.ppath, langFile))
        except:
            QMessageBox.critical(None,
                self.trUtf8("Delete translation"),
                self.trUtf8("The selected translation file <b>%1</b> could not be deleted.")
                    .arg(langFile),
                QMessageBox.Ok, QMessageBox.NoButton, 
                QMessageBox.NoButton)
            return 0
        
        self.removeLanguage(lang)
        
        # now get rid of the .qm file
        langFile = '%s_%s.qm' % (self.translationsRoot, lang)
        try:
            os.remove(os.path.join(self.ppath, langFile))
        except:
            pass    # never mind
            
        return 1
         
    def appendFile(self, fn):
        """
        Public method to append a file to the project.
        
        Arguments
        
            fn -- filename to be added to the project (string or QString)
        """
        newfn = os.path.abspath(str(fn))
        newfn = newfn.replace(self.ppath+os.sep, '')
        newdir = os.path.dirname(newfn)
        (dummy, ext) = os.path.splitext(newfn)
        dirty = 0
        if ext in ['.ui', '.py', '.idl']:
            if ext == '.ui':
                if newfn not in self.pdata["FORMS"]:
                    self.pdata["FORMS"].append(newfn)
                    self.emit(PYSIGNAL('projectFormAdded'), (newfn,))
                    dirty = 1
            elif ext == '.py':
                if newfn not in self.pdata["SOURCES"]:
                    self.pdata["SOURCES"].append(newfn)
                    self.emit(PYSIGNAL('projectSourceAdded'), (newfn,))
                    dirty = 1
            elif ext == '.idl':
                if newfn not in self.pdata["INTERFACES"]:
                    self.pdata["INTERFACES"].append(newfn)
                    self.emit(PYSIGNAL('projectInterfaceAdded'), (newfn,))
                    dirty = 1
            if newdir not in self.subdirs:
                self.subdirs.append(newdir)
        else:
            if newfn not in self.pdata["OTHERS"]:
                self.pdata['OTHERS'].append(newfn)
                self.othersAdded(newfn)
                dirty = 1
            if newdir not in self.otherssubdirs:
                self.otherssubdirs.append(newdir)
            
        if dirty:
            self.setDirty(1)
        
    def addFile(self, filter = None):
        """
        Public slot used to add a file to the project.
        
        Arguments
        
            filter -- filter to be used by the add file dialog (string)
        """
        dlg = AddFileDialog(self, self.parent(), filter)
        if dlg.exec_loop() == QDialog.Accepted:
            (fn, target) = dlg.getData()
            (dummy, ext) = os.path.splitext(fn)
            if target != '':
                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("The file <b>%1</b> already exists.")
                                    .arg(targetfile),
                                self.trUtf8("&Cancel"),
                                self.trUtf8("&Overwrite"))
                            if res == 0:
                                return  # operation was canceled
                                
                        shutil.copy(fn, target)
                        if ext == '.ui' and os.path.isfile(fn+'.h'):
                            shutil.copy(fn+'.h', target)
                    except IOError, why:
                        QMessageBox.critical(None,
                            self.trUtf8("Add file"),
                            self.trUtf8("The selected file <b>%1</b> could not be added to <b>%2</b>.")
                                .arg(fn)
                                .arg(target),
                            QMessageBox.Abort, QMessageBox.NoButton, 
                            QMessageBox.NoButton)
                        return
                        
                self.appendFile(targetfile)
            else:
                QMessageBox.critical(None,
                    self.trUtf8("Add file"),
                    self.trUtf8("The target directory must not be empty."),
                    QMessageBox.Abort, QMessageBox.NoButton, 
                    QMessageBox.NoButton)
        
    def addSingleDirectory(self, filter, source, target, quiet=0):
        """
        Private method used to add all files of a single directory to the project.
        
        Arguments
        
            filter -- filter to be applied (string)
            
            source -- source directory (string)
            
            target -- target directory (string)
            
            quiet -- flag indicating quiet operations (boolean)
        """
        sstring = "%s%s*.%s" % (source, os.sep, filter)
        files = glob.glob(sstring)
        if len(files) == 0:
            if not quiet:
                QMessageBox.information(None,
                    self.trUtf8("Add directory"),
                    self.trUtf8("The source directory doesn't contain"
                        " any files having the extension <b>%1</b>.")
                        .arg(filter),
                    QMessageBox.Ok)
            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("The target directory <b>%1</b> could not be created.")
                        .arg(normtarget),
                    QMessageBox.Abort, QMessageBox.NoButton, 
                    QMessageBox.NoButton)
                return
                
        for file in files:
            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("The file <b>%1</b> already exists.")
                                .arg(targetfile),
                            self.trUtf8("&Cancel"),
                            self.trUtf8("&Overwrite"))
                        if res == 0:
                            continue  # operation was canceled, carry on with next file
                            
                    shutil.copy(file, target)
                    if filter == 'ui' and os.path.isfile(file+'.h'):
                        shutil.copy(file+'.h', target)
                except:
                    continue
            self.appendFile(targetfile)
        
    def addRecursiveDirectory(self, filter, 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.
        
        Arguments
        
            filter -- filter to be applied (string)
            
            source -- source directory (string)
            
            target -- target directory (string)
        """
        # first perform the addition of source
        self.addSingleDirectory(filter, source, target, 1)
        
        # 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(filter, ns, nt)
        
    def addDirectory(self, filter):
        """
        Private method used to add all files of a directory to the project.
        
        Arguments
        
            filter -- filter to be applied (string)
        """
        dlg = AddDirectoryDialog(self, self.parent())
        if dlg.exec_loop() == QDialog.Accepted:
            source, target, recursive = dlg.getData()
            if target == '':
                QMessageBox.critical(None,
                    self.trUtf8("Add directory"),
                    self.trUtf8("The target directory must not be empty."),
                    QMessageBox.Abort, QMessageBox.NoButton, 
                    QMessageBox.NoButton)
                return
                
            if source == '':
                QMessageBox.critical(None,
                    self.trUtf8("Add directory"),
                    self.trUtf8("The source directory must not be empty."),
                    QMessageBox.Abort, QMessageBox.NoButton, 
                    QMessageBox.NoButton)
                return
                
            if recursive:
                self.addRecursiveDirectory(filter, source, target)
            else:
                self.addSingleDirectory(filter, source, target)
        
    def addPyFile(self):
        """
        Public slot to add a python file to the current project.
        """
        self.addFile('py')
        
    def addUiFile(self):
        """
        Public slot to add a form to the current project.
        """
        self.addFile('ui')
        
    def addIdlFile(self):
        """
        Public slot to add an IDL interface to the current project.
        """
        self.addFile('idl')
        
    def addPyDir(self):
        """
        Public slot to add all python files of a directory to the current project.
        """
        self.addDirectory('py')
        
    def addUiDir(self):
        """
        Public slot to add all forms of a directory to the current project.
        """
        self.addDirectory('ui')
        
    def addIdlDir(self):
        """
        Public slot to add all IDL interfaces of a directory to the current project.
        """
        self.addDirectory('idl')
        
    def removeFile(self, fn):
        """
        Public slot to remove a file from the project.
        
        The file is not deleted from the project directory.
        
        Arguments
        
            fn -- filename to be removed from the project
        """
        fn2 = str(fn)
        fn = fn2.replace(self.ppath+os.sep, '')
        (dummy, ext) = os.path.splitext(fn)
        if ext == '.ui':
            self.pdata["FORMS"].remove(fn)
        elif ext == '.py':
            self.pdata["SOURCES"].remove(fn)
        elif ext == '.idl':
            self.pdata["INTERFACES"].remove(fn)
        self.setDirty(1)
            
    def deleteFile(self, fn):
        """
        Public slot to delete a file from the project directory.
        
        Arguments
        
            fn -- filename to be deleted from the project
            
        Returns
        
            flag indicating success
        """
        fn = str(fn)
        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:
            QMessageBox.critical(None,
                self.trUtf8("Delete file"),
                self.trUtf8("The selected file <b>%1</b> could not be deleted.")
                    .arg(fn),
                QMessageBox.Ok, QMessageBox.NoButton, 
                QMessageBox.NoButton)
            return 0
        
        self.removeFile(fn)
        return 1
            
    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 version control
        support was selected, the relevant structures are initialzed 
        as well.
        """
        if not self.checkDirty():
            return
            
        dlg = PropertiesDialog(self, 1)
        if dlg.exec_loop() == QDialog.Accepted:
            self.closeProject()
            dlg.storeData()
            self.opened = 1
            if dlg.vcsSelected():
                vcsdlg = vcsOptionsDialog(self, self.name)
                if vcsdlg.exec_loop() == QDialog.Accepted:
                    vcsSystem, vcsDir, archive, vendor, message, release, coptions =\
                        vcsdlg.getData()
                    self.pdata["VCS"] = [vcsSystem]
                else:
                    self.pdata["VCS"] = ['None']
            else:
                self.pdata["VCS"] = ['None']
            self.vcs = self.initVCS()
            if self.vcs is not None and coptions:
                codlg = vcsCommandOptionsDialog(self.vcs)
                if codlg.exec_loop() == QDialog.Accepted:
                    self.vcs.vcsSetOptions(codlg.getOptions())
            self.setDirty(1)
            self.closeAct.setEnabled(1)
            self.saveasAct.setEnabled(1)
            self.actGrp2.setEnabled(1)
            self.propsAct.setEnabled(1)
            self.vcsAddAct.setEnabled(1)
            self.sessActGrp.setEnabled(0)
            self.menu.setItemEnabled(self.menuCheckId, 1)
            self.menu.setItemEnabled(self.menuShowId, 1)
            try:
                self.menu.setItemEnabled(self.menuApidocId, 1)
            except:
                pass
        
            # create the project directory if it doesn't exist already
            if not os.path.isdir(self.ppath):
                try:
                    os.makedirs(self.ppath)
                except:
                    QMessageBox.critical(None,
                        self.trUtf8("Create project directory"),
                        self.trUtf8("The project directory <b>%1</b> could not be created.")
                            .arg(self.ppath),
                        QMessageBox.Ok, QMessageBox.NoButton, QMessageBox.NoButton)
                    self.vcs = None
                    return
            
            if self.vcs is not None:
                # create the project in the VCS
                self.vcs.vcsInit(vcsDir, 1)
                self.saveProject()
                self.vcs.vcsImport(vcsDir, message, archive, vendor, release, self.ppath, 1)
                shutil.rmtree(self.ppath, 1)
                os.makedirs(self.ppath)
                self.vcs.vcsCheckout(vcsDir, archive, self.ppath, 1)
                vcsName = self.vcs.vcsName()
                if vcsName == 'CVS':
                    self.vcs.vcsCommit(os.path.join(self.ppath, archive), message, 1)
                    self.openProject("%s.e3p" % os.path.join(self.ppath, archive, archive))
                elif vcsName == 'Subversion':
                    self.vcs.vcsCommit(self.ppath, message, 1)
                    self.openProject("%s.e3p" % os.path.join(self.ppath, archive))
            else:
                self.emit(PYSIGNAL('newProject'), ())
            
    def showProperties(self):
        """
        Public slot to display the properties dialog.
        """
        dlg = PropertiesDialog(self, 0)
        if dlg.exec_loop() == QDialog.Accepted:
            dlg.storeData()
            self.setDirty(1)
        
    def openProject(self, fn = None):
        """
        Public slot to open a project.
        
        Arguments
        
            fn -- optional filename of the project file to be read
        """
        if not self.checkDirty():
            return
            
        if fn is None:
            fn = QFileDialog.getOpenFileName(None,
                self.trUtf8("Project Files (*.e3p);;"
                    "Qt-Designer Project Files (*.pro)"),
                self.parent(), None, self.trUtf8("Open project"))
            
            if fn.isNull():
                fn = None
            else:
                fn = str(fn)
            
        qApp.processEvents()
                
        if fn is not None:
            qApp.setOverrideCursor(Qt.waitCursor)
            qApp.processEvents()
            self.closeProject()
            if self.readProject(fn):
                self.opened = 1
                self.vcs = self.initVCS()
                if self.vcs is not None and not self.vcs.vcsRegisteredState(self.ppath):
                    self.vcs = None
                self.closeAct.setEnabled(1)
                self.saveasAct.setEnabled(1)
                self.actGrp2.setEnabled(1)
                self.propsAct.setEnabled(1)
                self.vcsAddAct.setEnabled(1)
                self.sessActGrp.setEnabled(1)
                self.menu.setItemEnabled(self.menuCheckId, 1)
                self.menu.setItemEnabled(self.menuShowId, 1)
                try:
                    self.menu.setItemEnabled(self.menuApidocId, 1)
                except:
                    pass
                if len(self.pdata["MAINSCRIPT"]) == 1:
                    self.emit(PYSIGNAL('pythonFile'), 
                        (os.path.join(self.ppath, self.pdata["MAINSCRIPT"][0]),))
                        
                self.emit(PYSIGNAL('projectOpened'), ())
                
                if Preferences.getProject("SearchNewFiles"):
                    self.searchNewFiles()
                    
                # open a project session file being quiet about errors
                if Preferences.getProject("AutoLoadSession"):
                    self.readSession(1)
                    
                qApp.restoreOverrideCursor()
            
    def saveProject(self):
        """
        Public slot to save the current project.
        
        Returns
        
            flag indicating success
        """
        if self.isDirty():
            if len(self.pfile) > 0:
                ok = self.writeProject()
            else:
                ok = self.saveProjectAs()
        self.sessActGrp.setEnabled(ok)
        return ok
            
    def saveProjectAs(self):
        """
        Public slot to save the current project to a different file.
        
        Returns
        
            flag indicating success
        """
        selectedFilter = QString('')
        fn = QFileDialog.getSaveFileName(self.ppath,
            self.trUtf8("Project Files (*.e3p)"),
            self.parent(), None, self.trUtf8("Save project as"), 
            selectedFilter, 0)
        
        if not fn.isNull():
            ext = QFileInfo(fn).extension()
            if ext.isEmpty():
                ex = selectedFilter.section('(*',1,1).section(')',0,0)
                if not ex.isEmpty():
                    fn.append(ex)
            if QFileInfo(fn).exists():
                abort = QMessageBox.warning(self.parent(),
                    self.trUtf8("Save File"),
                    self.trUtf8("The file <b>%1</b> already exists.")
                        .arg(fn),
                    self.trUtf8("&Overwrite"),
                    self.trUtf8("&Abort"), None, 1)
                if abort:
                    return 0
                
            self.name = str(QFileInfo(fn).baseName(1))
            self.emit(PYSIGNAL('projectClosed'), ())
            self.emit(PYSIGNAL('projectOpened'), ())
            ok = self.writeProject(str(fn))
            self.sessActGrp.setEnabled(ok)
    
    def checkDirty(self):
        """
        Private method to check dirty status and open a message window.
        
        Returns
        
            flag indicating whether this operation was successful
        """
        if self.isDirty():
            res = QMessageBox.warning(self.parent(), 
                self.trUtf8("Close Project"),
                self.trUtf8("The current project has unsaved changes."),
                self.trUtf8("&Save"), self.trUtf8("&Discard changes"),
                self.trUtf8("&Abort"), 0, 2)
            if res == 0:
                return self.saveProject()
            elif res == 1:
                self.setDirty(0)
                return 1
            elif res == 2:
                return 0
            
        return 1
        
    def closeProject(self):
        """
        Public slot to close the current project.
        """
        if not self.isOpen():
            return 1
            
        if not self.checkDirty():
            return 0
            
        # save the project session file being quiet about error
        if Preferences.getProject("AutoSaveSession"):
            self.writeSession(1)
        
        # save the list of recently opened projects
        ok = Preferences.Prefs.settings.writeEntry('/eric3/Recent/Projects', self.recent)
        
        # now save all open modified files of the project
        vm = self.parent().getViewManager()
        success = 1
        for key in ["SOURCES", "INTERFACES", "OTHERS"]:
            for fn in self.pdata[key]:
                fullname = os.path.join(self.ppath, fn)
                success &= vm.handleCloseWindow(fullname)
            
        if not success:
            return 0
                
        self.init()
        self.closeAct.setEnabled(0)
        self.saveasAct.setEnabled(0)
        self.saveAct.setEnabled(0)
        self.actGrp2.setEnabled(0)
        self.propsAct.setEnabled(0)
        self.vcsAddAct.setEnabled(0)
        self.sessActGrp.setEnabled(0)
        self.menu.setItemEnabled(self.menuCheckId, 0)
        self.menu.setItemEnabled(self.menuShowId, 0)
        try:
            self.menu.setItemEnabled(self.menuApidocId, 0)
        except:
            pass
        
        self.emit(PYSIGNAL('projectClosed'), ())
        
        return 1

    def saveAllScripts(self):
        """
        Public method to save all scripts belonging to the project.
        
        Returns
        
            flag indicating success
        """
        vm = self.parent().getViewManager()
        success = 1
        for fn in self.pdata["SOURCES"]:
            fullname = os.path.join(self.ppath, fn)
            success &= vm.saveEditor(fullname)
            
        return success
        
    def getMainScript(self, normalized = 0):
        """
        Public method to return the main script filename.
        
        Arguments
        
            normalized -- flag indicating a normalized filename is wanted
            
        Returns
        
            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 = 0):
        """
        Public method to return the source script files.
        
        Arguments
        
            normalized -- flag indicating a normalized filename is wanted
            
        Returns
        
            list of the projects scripts (list of string)
        """
        if len(self.pdata["SOURCES"]):
            if normalized:
                flist = []
                for fn in self.pdata["SOURCES"]:
                    flist.append(os.path.join(self.ppath, fn))
                return flist
            else:
                return (self.pdata["SOURCES"])
                
    def isProjectSource(self, fn):
        """
        Public method used to check, if the passed in filename belongs to the project.
        
        Arguments
        
            fn -- filename to be checked (string or QString)
            
        Returns
        
            flag indicating membership (boolean)
        """
        newfn = os.path.abspath(str(fn))
        newfn = newfn.replace(self.ppath + os.sep, '')
        return newfn in self.pdata["SOURCES"]
        
    def initActions(self):
        """
        Public slot to initialize the project related actions.
        """
        self.actGrp1 = QActionGroup(self)
        
        act = QAction(self.trUtf8('New project'),
                QIconSet(IconProjectNew),
                self.trUtf8('&New...'),0,
                self.actGrp1)
        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('activated()'),self.newProject)

        act = QAction(self.trUtf8('Open project'),
                QIconSet(IconProjectOpen),
                self.trUtf8('&Open...'),0,
                self.actGrp1)
        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('activated()'),self.openProject)

        self.closeAct = QAction(self.trUtf8('Close project'),
                QIconSet(IconProjectClose),
                self.trUtf8('&Close'),0, self)
        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('activated()'),self.closeProject)

        self.closeAct.setEnabled(0)
        
        self.saveAct = QAction(self.trUtf8('Save project'),
                QIconSet(IconProjectSave),
                self.trUtf8('&Save'),0, self)
        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('activated()'),self.saveProject)

        self.saveasAct = QAction(self.trUtf8('Save project as'),
                QIconSet(IconProjectSaveAs),
                self.trUtf8('Save &as...'),0, self)
        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('activated()'),self.saveProjectAs)

        self.actGrp2 = QActionGroup(self)
        
        act = QAction(self.trUtf8('Add python file to project'),
                QIconSet(IconFilePython),
                self.trUtf8('Add p&ython file...'),0,
                self.actGrp2)
        act.setStatusTip(self.trUtf8('Add a python file to the current project'))
        act.setWhatsThis(self.trUtf8(
            """<b>Add python file...</b>"""
            """<p>This opens a dialog for adding a python file"""
            """ to the current project.</p>"""
        ))
        self.connect(act,SIGNAL('activated()'),self.addPyFile)

        act = QAction(self.trUtf8('Add python directory to project'),
                QIconSet(IconFilePython),
                self.trUtf8('Add python directory...'),0,
                self.actGrp2)
        act.setStatusTip(self.trUtf8('Add a python directory to the current project'))
        act.setWhatsThis(self.trUtf8(
            """<b>Add python directory...</b>"""
            """<p>This opens a dialog for adding a python directory"""
            """ to the current project.</p>"""
        ))
        self.connect(act,SIGNAL('activated()'),self.addPyDir)

        act = QAction(self.trUtf8('Add form to project'),
                QIconSet(IconDesigner),
                self.trUtf8('Add &form...'),0,
                self.actGrp2)
        act.setStatusTip(self.trUtf8('Add a form to the current project'))
        act.setWhatsThis(self.trUtf8(
            """<b>Add form...</b>"""
            """<p>This opens a dialog for adding a form"""
            """ to the current project.</p>"""
        ))
        self.connect(act,SIGNAL('activated()'),self.addUiFile)

        act = QAction(self.trUtf8('Add forms directory to project'),
                QIconSet(IconDesigner),
                self.trUtf8('Add forms directory...'),0,
                self.actGrp2)
        act.setStatusTip(self.trUtf8('Add a forms directory to the current project'))
        act.setWhatsThis(self.trUtf8(
            """<b>Add forms directory...</b>"""
            """<p>This opens a dialog for adding a forms directory"""
            """ to the current project.</p>"""
        ))
        self.connect(act,SIGNAL('activated()'),self.addUiDir)

        act = QAction(self.trUtf8('Add translation to project'),
                QIconSet(IconLinguist),
                self.trUtf8('Add &translation...'),0,
                self.actGrp2)
        act.setStatusTip(self.trUtf8('Add a translation to the current project'))
        act.setWhatsThis(self.trUtf8(
            """<b>Add translation...</b>"""
            """<p>This opens a dialog for add a translation"""
            """ to the current project.</p>"""
        ))
        self.connect(act,SIGNAL('activated()'),self.addLanguage)

        act = QAction(self.trUtf8('Add interface file to project'),
                QIconSet(IconFileIDL),
                self.trUtf8('Add &interface file...'),0,
                self.actGrp2)
        act.setStatusTip(self.trUtf8('Add an interface (IDL) file to the current project'))
        act.setWhatsThis(self.trUtf8(
            """<b>Add interface file...</b>"""
            """<p>This opens a dialog for adding an interface (IDL) file"""
            """ to the current project.</p>"""
        ))
        self.connect(act,SIGNAL('activated()'),self.addIdlFile)

        act = QAction(self.trUtf8('Add interfaces directory to project'),
                QIconSet(IconFileIDL),
                self.trUtf8('Add interfaces directory...'),0,
                self.actGrp2)
        act.setStatusTip(self.trUtf8('Add an interfaces (IDL) directory to the current project'))
        act.setWhatsThis(self.trUtf8(
            """<b>Add interfaces directory...</b>"""
            """<p>This opens a dialog for adding an interfaces (IDL) directory"""
            """ to the current project.</p>"""
        ))
        self.connect(act,SIGNAL('activated()'),self.addIdlDir)

        act = QAction(self.trUtf8('Search new files'),
                self.trUtf8('Searc&h new files...'),0,
                self.actGrp2)
        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 (*.py, *.ui) in the project"""
            """ directory and registered subdirectories.</p>"""
        ))
        self.connect(act,SIGNAL('activated()'),self.handleSearchNewFiles)

        self.propsAct = QAction(self.trUtf8('Project properties'),
                QIconSet(IconProjectProps),
                self.trUtf8('&Properties...'),0, self)
        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('activated()'),self.showProperties)

        self.sessActGrp = QActionGroup(self)

        act = QAction(self.trUtf8('Load session'),
                self.trUtf8('Load session'), 0,
                self.sessActGrp)
        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('activated()'),self.readSession)

        act = QAction(self.trUtf8('Save session'),
                self.trUtf8('Save session'), 0,
                self.sessActGrp)
        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('activated()'),self.writeSession)
        
        self.chkGrp = QActionGroup(self)

        self.checkSyntaxAct = QAction(self.trUtf8('Check Syntax'),
                self.trUtf8('&Syntax...'),0,
                self.chkGrp)
        self.checkSyntaxAct.setStatusTip(self.trUtf8('Check project for syntax errors.'))
        self.checkSyntaxAct.setWhatsThis(self.trUtf8(
            """<b>Check Syntax...</b>"""
            """<p>This checks all Python files in the project"""
            """ for syntax errors.</p>"""
        ))
        self.connect(self.checkSyntaxAct,SIGNAL('activated()'),self.handleSyntaxCheck)

        self.tabnannyAct = QAction(self.trUtf8('Check Indentations'),
                self.trUtf8('&Indentations...'),0,
                self.chkGrp)
        self.tabnannyAct.setStatusTip(self.trUtf8('Check project for bad indentations using tabnanny.'))
        self.tabnannyAct.setWhatsThis(self.trUtf8(
            """<b>Check Indentations...</b>"""
            """<p>This checks all Python files in the project"""
            """ for bad indentations using tabnanny.</p>"""
        ))
        self.connect(self.tabnannyAct,SIGNAL('activated()'),self.handleTabnanny)

        self.codeMetricsAct = QAction(self.trUtf8('Code Metrics'),
                self.trUtf8('&Code Metrics...'),0,
                self.chkGrp)
        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('activated()'),self.handleCodeMetrics)

        self.codeCoverageAct = QAction(self.trUtf8('Python Code Coverage'),
                self.trUtf8('Code Co&verage...'),0,
                self.chkGrp)
        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('activated()'),self.handleCodeCoverage)

        self.codeProfileAct = QAction(self.trUtf8('Profile Data'),
                self.trUtf8('&Profile Data...'),0,
                self.chkGrp)
        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('activated()'),self.handleProfileData)

        self.closeAct.setEnabled(0)
        self.saveAct.setEnabled(0)
        self.saveasAct.setEnabled(0)
        self.actGrp2.setEnabled(0)
        self.propsAct.setEnabled(0)
        self.sessActGrp.setEnabled(0)
        
        self.initVCSActions()
        
    def initMenu(self):
        """
        Public slot to initialize the project menu.
        
        Returns
        
            the menu generated (QPopupMenu)
        """
        menu = QPopupMenu(self.parent())
        self.recentMenu = QPopupMenu(menu)
        self.vcsMenu = QPopupMenu(menu)
        self.checksMenu = QPopupMenu(menu)
        self.showMenu = QPopupMenu(menu)
        self.apidocMenu = self.doctools.initMenu()
        
        # connect the aboutToShow signals
        self.connect(self.recentMenu, SIGNAL('aboutToShow()'), self.handleShowRecentMenu)
        self.connect(self.vcsMenu, SIGNAL('aboutToShow()'), self.handleShowVCSMenu)
        self.connect(self.showMenu, SIGNAL('aboutToShow()'), self.handleShowShowMenu)
        
        # build the checks menu
        self.checkSyntaxAct.addTo(self.checksMenu)
        self.tabnannyAct.addTo(self.checksMenu)
        
        #build the show menu
        self.codeMetricsAct.addTo(self.showMenu)
        self.codeCoverageAct.addTo(self.showMenu)
        self.codeProfileAct.addTo(self.showMenu)
        
        # build the main menu
        menu.insertTearOffHandle()
        self.actGrp1.addTo(menu)
        menu.insertItem(self.trUtf8('Open &Recent Projects'), self.recentMenu)
        menu.insertSeparator()
        self.closeAct.addTo(menu)
        menu.insertSeparator()
        self.saveAct.addTo(menu)
        self.saveasAct.addTo(menu)
        menu.insertSeparator()
        self.sessActGrp.addTo(menu)
        menu.insertSeparator()
        self.actGrp2.addTo(menu)
        menu.insertSeparator()
        self.menuCheckId = menu.insertItem(self.trUtf8('Chec&k'), self.checksMenu)
        menu.insertSeparator()
        menu.insertItem(self.trUtf8('&Version Control'), self.vcsMenu)
        menu.insertSeparator()
        self.menuShowId = menu.insertItem(self.trUtf8('Sho&w'), self.showMenu)
        menu.insertSeparator()
        if self.apidocMenu is not None:
            self.menuApidocId = menu.insertItem(self.trUtf8('Source &Documentation'), self.apidocMenu)
            menu.insertSeparator()
        self.propsAct.addTo(menu)
        
        menu.setItemEnabled(self.menuCheckId, 0)
        menu.setItemEnabled(self.menuShowId, 0)
        try:
            menu.setItemEnabled(self.menuApidocId, 0)
        except:
            pass
        
        self.menu = menu
        return menu
        
    def initToolbar(self):
        """
        Public slot to initialize the project toolbar.
        
        Returns
        
            the toolbar generated (QToolBar)
        """
        tb = QToolBar(self.parent())
        self.actGrp1.addTo(tb)
        self.closeAct.addTo(tb)
        tb.addSeparator()
        self.saveAct.addTo(tb)
        self.saveasAct.addTo(tb)
        tb.addSeparator()
        self.propsAct.addTo(tb)
        
        return tb
        
    def handleShowRecentMenu(self):
        """
        Private method to set up the recent projects menu.
        """
        idx = 0
        self.recentMenu.clear()
        
        for rp in self.recent:
            id = self.recentMenu.insertItem('&%d. %s' % (idx+1, str(rp)),
                                            self.handleOpenRecent)
            self.recentMenu.setItemParameter(id,idx)
            
            idx = idx + 1
            
        self.recentMenu.insertSeparator()
        self.recentMenu.insertItem(self.trUtf8('Clear'), self.handleClearRecent)
        
    def handleOpenRecent(self, idx):
        """
        Private method to open a project from the list of rencently opened projects.
        """
        self.openProject(str(self.recent[idx]))
        
    def handleClearRecent(self):
        """
        Private method to clear the recent projects menu.
        """
        self.recent = QStringList()
        
    def handleSearchNewFiles(self):
        """
        Private slot used to handle the search new files action.
        """
        self.searchNewFiles(0, 1)
        
    def searchNewFiles(self, AI=1, onUserDemand=0):
        """
        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 oportunity to select the ones he wants to
        include. If 'Automatic Inclusion' is enabled, the new files are
        automatically added to the project.
        
        Arguments
        
            AI -- flag indicating whether the automatic inclusion should
                be honoured
                
            onUserDemand -- flag indicating whether this method was 
                requested by the user via a menu action
        """
        autoInclude = Preferences.getProject("AutoIncludeNewFiles")
        newFiles = QStringList()
        for dir in self.subdirs:
            curpath = os.path.join(self.ppath, dir)
            try:
                newSources = os.listdir(curpath)
            except OSError:
                newSources = []
            for ns in newSources:
                # 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):
                    continue
                # we only deal with .py, .idl and .ui, where .py and .idl would be
                # catagorized as source and .ui are forms, so deal with appropriately
                # making sure to properly normalize the extension case prior to compare.
                dummy, ext = os.path.splitext(fn)
                ext = os.path.normcase(ext)
                if (ext == '.py' and not fn in self.pdata["SOURCES"]) or \
                   (ext == '.ui' and not fn in self.pdata["FORMS"]) or \
                   (ext == '.idl' and not fn in self.pdata["INTERFACES"]):
                    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 newFiles.isEmpty():
            if onUserDemand:
                QMessageBox.information(None,
                    self.trUtf8("Search New Files"),
                    self.trUtf8("There were no new files found to be added."),
                    QMessageBox.Ok)
            return
            
        # autoInclude is not set, show a dialog
        dlg = AddFoundFilesDialog(newFiles, self.parent(), None, 1)
        res = dlg.exec_loop()
        
        # the 'Add All' button was pressed
        if res == 1:
            for file in newFiles:
                self.appendFile(str(file))
                
        # the 'Add Selected' button was pressed
        elif res == 2:
            files = dlg.getSelection()
            for file in files:
                self.appendFile(str(file))
                
    def othersAdded(self, fn):
        """
        Public slot to be called, if something was added to the OTHERS project data area.
        
        Arguments
        
            fn -- filename or directory name added (string or QString)
        """
        f = str(fn)
        self.emit(PYSIGNAL('projectOthersAdded'), (f,))
        
    def getActions(self):
        """
        Public method to get a list of all actions.
        
        Returns
        
            list of all actions (list of QAction)
        """
        actionList = []
        for act in self.queryList("QAction"):
            if not isinstance(act, QActionGroup):
                actionList.append(act)
        for act in self.doctools.queryList("QAction"):
            if not isinstance(act, QActionGroup):
                actionList.append(act)
                
        return actionList
        
    ##############################################################
    ## Below is the VCS interface
    ##############################################################
    
    def initVCS(self, vcsSystem=None):
        """
        Private method used to instantiate a vcs system.
        
        Arguments
        
            vcsSystem -- type of VCS to be used
            
        Returns
        
            a reference to the vcs object
        """
        vcs = None
        forProject = 1
        
        if vcsSystem is None:
            if len(self.pdata["VCS"]):
                if self.pdata["VCS"][0] == 'None':
                    return vcs
                else:
                    vcsSystem = self.pdata["VCS"][0]
            else:
                return vcs
        else:
            vcsSystem = str(vcsSystem)
            forProject = 0
            
        try:
            vcs = VCS.factory(vcsSystem)
        except:
            return vcs
            
        if not vcs.vcsExists():
            QMessageBox.critical(None,
                self.trUtf8("Version Control System"),
                self.trUtf8("The selected VCS <b>%1</b> could not be found.<br>"
                    "Disabling version control.").arg(self.pdata["VCS"][0]),
                self.trUtf8("OK"))
            vcs = None
            if forProject:
                self.pdata["VCS"][0] = 'None'
            return vcs
            
        if forProject:
            # set the vcs options
            try:
                vcsopt = eval(self.pdata["VCSOPTIONS"][0])
                vcs.vcsSetOptions(vcsopt)
            except:
                pass
                
        return vcs
        
    def initVCSActions(self):
        """
        Private method to initialize the VCS actions.
        """
        self.vcsActGrp = QActionGroup(self)
        
        self.vcsNewAct = QAction(self.trUtf8('New from repository'),
                self.trUtf8('&New from repository...'),0, self)
        self.vcsNewAct.setStatusTip(self.trUtf8(
            'Create a new project from the VCS repository'
        ))
        self.vcsNewAct.setWhatsThis(self.trUtf8(
            """<b>New from repository</b>"""
            """<p>This creates a new local project from the VCS repository.</p>"""
        ))
        self.connect(self.vcsNewAct,SIGNAL('activated()'),self.vcsCheckout)
        
        self.vcsUpdateAct = QAction(self.trUtf8('Update from repository'),
                self.trUtf8('&Update from repository'),0, self.vcsActGrp)
        self.vcsUpdateAct.setStatusTip(self.trUtf8(
            'Update the local project from the VCS repository'
        ))
        self.vcsUpdateAct.setWhatsThis(self.trUtf8(
            """<b>Update from repository</b>"""
            """<p>This updates the local project from the VCS repository.</p>"""
        ))
        self.connect(self.vcsUpdateAct,SIGNAL('activated()'),self.vcsUpdate)
        
        self.vcsCommitAct = QAction(self.trUtf8('Commit changes to repository'),
                self.trUtf8('&Commit changes to repository...'),0, self.vcsActGrp)
        self.vcsCommitAct.setStatusTip(self.trUtf8(
            'Commit changes to the local project to the VCS repository'
        ))
        self.vcsCommitAct.setWhatsThis(self.trUtf8(
            """<b>Commit changes to repository</b>"""
            """<p>This commits changes to the local project to the VCS repository.</p>"""
        ))
        self.connect(self.vcsCommitAct,SIGNAL('activated()'),self.vcsCommit)
        
        self.vcsAddAct = QAction(self.trUtf8('Add to repository'),
                self.trUtf8('&Add to repository...'),0, self)
        self.vcsAddAct.setStatusTip(self.trUtf8(
            'Add the local project to the VCS repository'
        ))
        self.vcsAddAct.setWhatsThis(self.trUtf8(
            """<b>Add to repository</b>"""
            """<p>This adds (imports) the local project to the VCS repository.</p>"""
        ))
        self.connect(self.vcsAddAct,SIGNAL('activated()'),self.vcsImport)
        
        self.vcsRemoveAct = QAction(self.trUtf8(
                'Remove from repository (and disk)'),
                self.trUtf8('&Remove from repository (and disk)'),
                0, self.vcsActGrp)
        self.vcsRemoveAct.setStatusTip(self.trUtf8(
            'Remove the local project from the VCS repository (and  disk)'
        ))
        self.vcsRemoveAct.setWhatsThis(self.trUtf8(
            """<b>Remove from repository</b>"""
            """<p>This removes the local project from the VCS repository (and disk).</p>"""
        ))
        self.connect(self.vcsRemoveAct,SIGNAL('activated()'),self.vcsRemove)
        
        self.vcsLogAct = QAction(self.trUtf8(
                'Show log'),
                self.trUtf8('Show &log'),
                0, self.vcsActGrp)
        self.vcsLogAct.setStatusTip(self.trUtf8(
            'Show the log of the local project'
        ))
        self.vcsLogAct.setWhatsThis(self.trUtf8(
            """<b>Show log</b>"""
            """<p>This shows the log of the local project.</p>"""
        ))
        self.connect(self.vcsLogAct,SIGNAL('activated()'),self.vcsLog)
        
        self.vcsDiffAct = QAction(self.trUtf8('Show difference to repository'),
                self.trUtf8('Show &difference to repository'),
                0, self.vcsActGrp)
        self.vcsDiffAct.setStatusTip(self.trUtf8(
            'Show the difference of the local project to the repository'
        ))
        self.vcsDiffAct.setWhatsThis(self.trUtf8(
            """<b>Show difference to repository</b>"""
            """<p>This shows the difference of the local project to the repository.</p>"""
        ))
        self.connect(self.vcsDiffAct,SIGNAL('activated()'),self.vcsDiff)
        
        self.vcsStatusAct = QAction(self.trUtf8('Show status'),
                self.trUtf8('Show &status'),
                0, self.vcsActGrp)
        self.vcsStatusAct.setStatusTip(self.trUtf8(
            'Show the status of the local project'
        ))
        self.vcsStatusAct.setWhatsThis(self.trUtf8(
            """<b>Show status</b>"""
            """<p>This shows the status of the local project.</p>"""
        ))
        self.connect(self.vcsStatusAct,SIGNAL('activated()'),self.vcsStatus)
        
        self.vcsTagAct = QAction(self.trUtf8('Tag in repository'), 
                self.trUtf8('&Tag in repository...'),
                0, self.vcsActGrp)
        self.vcsTagAct.setStatusTip(self.trUtf8(
            'Tag the local project in the repository'
        ))
        self.vcsTagAct.setWhatsThis(self.trUtf8(
            """<b>Tag in repository</b>"""
            """<p>This tags the local project in the repository.</p>"""
        ))
        self.connect(self.vcsTagAct,SIGNAL('activated()'),self.vcsTag)
        
        self.svnTagListAct = QAction(self.trUtf8('List tags'), 
                self.trUtf8('List tags...'),
                0, self.vcsActGrp)
        self.svnTagListAct.setStatusTip(self.trUtf8(
            'List tags of the project'
        ))
        self.svnTagListAct.setWhatsThis(self.trUtf8(
            """<b>List tags</b>"""
            """<p>This lists the tags of the project.</p>"""
        ))
        self.connect(self.svnTagListAct,SIGNAL('activated()'),self.svnTagList)
        
        self.svnBranchListAct = QAction(self.trUtf8('List branches'), 
                self.trUtf8('List branches...'),
                0, self.vcsActGrp)
        self.svnBranchListAct.setStatusTip(self.trUtf8(
            'List branches of the project'
        ))
        self.svnBranchListAct.setWhatsThis(self.trUtf8(
            """<b>List branches</b>"""
            """<p>This lists the branches of the project.</p>"""
        ))
        self.connect(self.svnBranchListAct,SIGNAL('activated()'),self.svnBranchList)
        
        self.vcsExportAct = QAction(self.trUtf8('Export from repository'), 
                self.trUtf8('&Export from repository...'),
                0, self)
        self.vcsExportAct.setStatusTip(self.trUtf8(
            'Export a project from the repository'
        ))
        self.vcsExportAct.setWhatsThis(self.trUtf8(
            """<b>Export from repository</b>"""
            """<p>This exports a project from the repository.</p>"""
        ))
        self.connect(self.vcsExportAct,SIGNAL('activated()'),self.vcsExport)
        
        self.vcsPropsAct = QAction(self.trUtf8('Command options'),
                self.trUtf8('Command &options...'),0, self.vcsActGrp)
        self.vcsPropsAct.setStatusTip(self.trUtf8('Show the VCS command options'))
        self.vcsPropsAct.setWhatsThis(self.trUtf8(
            """<b>Command options...</b>"""
            """<p>This shows a dialog to edit the VCS command options.</p>"""
        ))
        self.connect(self.vcsPropsAct,SIGNAL('activated()'),self.vcsCommandOptions)
        
        self.vcsLoginAct = QAction(self.trUtf8('Login to repository'),
                self.trUtf8('Login to repository...'),0, self)
        self.vcsLoginAct.setStatusTip(self.trUtf8(
            'Login to repository'
        ))
        self.vcsLoginAct.setWhatsThis(self.trUtf8(
            """<b>Login to repository</b>"""
            """<p>This performs a login to the VCS repository.</p>"""
        ))
        self.connect(self.vcsLoginAct,SIGNAL('activated()'),self.vcsLogin)
        
        self.vcsLogoutAct = QAction(self.trUtf8('Logout from repository'),
                self.trUtf8('Logout from repository...'),0, self)
        self.vcsLogoutAct.setStatusTip(self.trUtf8(
            'Logout from repository'
        ))
        self.vcsLogoutAct.setWhatsThis(self.trUtf8(
            """<b>Logout from repository</b>"""
            """<p>This performs a logout from the VCS repository.</p>"""
        ))
        self.connect(self.vcsLogoutAct,SIGNAL('activated()'),self.vcsLogout)
        
        self.vcsRevertAct = QAction(self.trUtf8('Revert changes'),
                self.trUtf8('Re&vert changes'),
                0, self.vcsActGrp)
        self.vcsRevertAct.setStatusTip(self.trUtf8(
            'Revert all changes made to the local project'
        ))
        self.vcsRevertAct.setWhatsThis(self.trUtf8(
            """<b>Revert changes</b>"""
            """<p>This reverts all changes made to the local project.</p>"""
        ))
        self.connect(self.vcsRevertAct,SIGNAL('activated()'),self.vcsRevert)
        
        self.vcsMergeAct = QAction(self.trUtf8('Merge'),
                self.trUtf8('Mer&ge changes...'),
                0, self.vcsActGrp)
        self.vcsMergeAct.setStatusTip(self.trUtf8(
            'Merge changes of a tag/revision into the local project'
        ))
        self.vcsMergeAct.setWhatsThis(self.trUtf8(
            """<b>Merge</b>"""
            """<p>This merges changes of a tag/revision into the local project.</p>"""
        ))
        self.connect(self.vcsMergeAct,SIGNAL('activated()'),self.vcsMerge)
        
        self.vcsSwitchAct = QAction(self.trUtf8('Switch'),
                self.trUtf8('S&witch...'),
                0, self.vcsActGrp)
        self.vcsSwitchAct.setStatusTip(self.trUtf8(
            'Switch the local copy to another tag/branch'
        ))
        self.vcsSwitchAct.setWhatsThis(self.trUtf8(
            """<b>Switch</b>"""
            """<p>This switches the local copy to another tag/branch.</p>"""
        ))
        self.connect(self.vcsSwitchAct,SIGNAL('activated()'),self.vcsSwitch)
        
        self.vcsResolveAct = QAction(self.trUtf8('Resolve conflicts'),
                self.trUtf8('Resolve con&flicts'),
                0, self.vcsActGrp)
        self.vcsResolveAct.setStatusTip(self.trUtf8(
            'Resolve all conflicts of the local project'
        ))
        self.vcsResolveAct.setWhatsThis(self.trUtf8(
            """<b>Resolve conflicts</b>"""
            """<p>This resolves all conflicts of the local project.</p>"""
        ))
        self.connect(self.vcsResolveAct,SIGNAL('activated()'),self.svnResolve)
        
        self.vcsCleanupAct = QAction(self.trUtf8('Cleanup'),
                self.trUtf8('Cleanu&p'),
                0, self.vcsActGrp)
        self.vcsCleanupAct.setStatusTip(self.trUtf8(
            'Cleanup the local project'
        ))
        self.vcsCleanupAct.setWhatsThis(self.trUtf8(
            """<b>Cleanup</b>"""
            """<p>This performs a cleanup of the local project.</p>"""
        ))
        self.connect(self.vcsCleanupAct,SIGNAL('activated()'),self.vcsCleanup)
        
        self.vcsCommandAct = QAction(self.trUtf8('Execute command'),
                self.trUtf8('E&xecute command...'),
                0, self.vcsActGrp)
        self.vcsCommandAct.setStatusTip(self.trUtf8(
            'Execute an arbitrary VCS command'
        ))
        self.vcsCommandAct.setWhatsThis(self.trUtf8(
            """<b>Execute command</b>"""
            """<p>This opens a dialog to enter an arbitrary VCS command.</p>"""
        ))
        self.connect(self.vcsCommandAct,SIGNAL('activated()'),self.vcsCommand)
        
        self.vcsClearAuthAct = QAction(self.trUtf8('Clear authentication info'),
                self.trUtf8('Clear authentication info'),0, self)
        self.vcsClearAuthAct.setStatusTip(self.trUtf8(
            'Clear authentication info'
        ))
        self.vcsClearAuthAct.setWhatsThis(self.trUtf8(
            """<b>Clear authentication info</b>"""
            """<p>This performs clears the stored authentication info.</p>"""
        ))
        self.connect(self.vcsClearAuthAct,SIGNAL('activated()'),self.svnClearAuth)
        
        self.svnPropSetAct = QAction(self.trUtf8('Set Property'),
                self.trUtf8('Set Property...'),
                0, self.vcsActGrp)
        self.svnPropSetAct.setStatusTip(self.trUtf8(
            'Set a property for the project files'
        ))
        self.svnPropSetAct.setWhatsThis(self.trUtf8(
            """<b>Set Property</b>"""
            """<p>This sets a property for the project files.</p>"""
        ))
        self.connect(self.svnPropSetAct,SIGNAL('activated()'),self.svnPropSet)
        
        self.svnPropListAct = QAction(self.trUtf8('List Properties'),
                self.trUtf8('List Properties...'),
                0, self.vcsActGrp)
        self.svnPropListAct.setStatusTip(self.trUtf8(
            'List properties of the project files'
        ))
        self.svnPropListAct.setWhatsThis(self.trUtf8(
            """<b>List Properties</b>"""
            """<p>This lists the properties of the project files.</p>"""
        ))
        self.connect(self.svnPropListAct,SIGNAL('activated()'),self.svnPropList)
        
        self.svnPropDelAct = QAction(self.trUtf8('Delete Property'),
                self.trUtf8('Delete Property...'),
                0, self.vcsActGrp)
        self.svnPropDelAct.setStatusTip(self.trUtf8(
            'Delete a property for the project files'
        ))
        self.svnPropDelAct.setWhatsThis(self.trUtf8(
            """<b>Delete Property</b>"""
            """<p>This deletes a property for the project files.</p>"""
        ))
        self.connect(self.svnPropDelAct,SIGNAL('activated()'),self.svnPropDel)
        
        self.vcsAddAct.setEnabled(0)
        
    def handleShowVCSMenu(self):
        """
        Private slot called before the vcs menu is shown.
        """
        self.vcsMenu.clear()
        
        if self.vcs is None:
            self.vcsNewAct.addTo(self.vcsMenu)
            self.vcsExportAct.addTo(self.vcsMenu)
            self.vcsAddAct.addTo(self.vcsMenu)
            self.vcsMenu.insertSeparator()
            self.vcsLoginAct.addTo(self.vcsMenu)
            self.vcsLogoutAct.addTo(self.vcsMenu)
        else:
            vcsName = self.vcs.vcsName()
            self.vcsUpdateAct.addTo(self.vcsMenu)
            self.vcsCommitAct.addTo(self.vcsMenu)
            self.vcsMenu.insertSeparator()
            self.vcsNewAct.addTo(self.vcsMenu)
            self.vcsExportAct.addTo(self.vcsMenu)
            self.vcsAddAct.addTo(self.vcsMenu)
            self.vcsRemoveAct.addTo(self.vcsMenu)
            self.vcsTagAct.addTo(self.vcsMenu)
            if vcsName == 'Subversion':
                self.svnTagListAct.addTo(self.vcsMenu)
                self.svnBranchListAct.addTo(self.vcsMenu)
            self.vcsLogAct.addTo(self.vcsMenu)
            self.vcsDiffAct.addTo(self.vcsMenu)
            self.vcsRevertAct.addTo(self.vcsMenu)
            self.vcsMergeAct.addTo(self.vcsMenu)
            if vcsName == 'Subversion':
                self.vcsStatusAct.addTo(self.vcsMenu)
                self.vcsResolveAct.addTo(self.vcsMenu)
            self.vcsSwitchAct.addTo(self.vcsMenu)
            if vcsName == 'Subversion':
                self.vcsMenu.insertSeparator()
                self.svnPropSetAct.addTo(self.vcsMenu)
                self.svnPropListAct.addTo(self.vcsMenu)
                self.svnPropDelAct.addTo(self.vcsMenu)
            self.vcsMenu.insertSeparator()
            self.vcsCleanupAct.addTo(self.vcsMenu)
            if vcsName == 'CVS':
                self.vcsLoginAct.addTo(self.vcsMenu)
                self.vcsLogoutAct.addTo(self.vcsMenu)
            if vcsName == 'Subversion':
                self.vcsClearAuthAct.addTo(self.vcsMenu)
            self.vcsMenu.insertSeparator()
            self.vcsCommandAct.addTo(self.vcsMenu)
            self.vcsMenu.insertSeparator()
            self.vcsPropsAct.addTo(self.vcsMenu)
        
    def vcsCheckout(self, export=0):
        """
        Private slot used to create a local project from the repository.
        
        Arguments
        
            export -- flag indicating whether an export or a checkout
                should be performed
        """
        if not self.checkDirty():
            return
            
        vcsdlg = vcsNewProjectOptionsDialog()
        if vcsdlg.exec_loop() == QDialog.Accepted:
            self.closeProject()
            vcsSystem, vcsDir, archive, tag, projectdir, coptions =\
                vcsdlg.getData()
            self.pdata["VCS"] = [vcsSystem]
            self.vcs = self.initVCS()
            if self.vcs is None:
                return
            if coptions:
                codlg = vcsCommandOptionsDialog(self.vcs)
                if codlg.exec_loop() == QDialog.Accepted:
                    self.vcs.vcsSetOptions(codlg.getOptions())
        
            # create the project directory if it doesn't exist already
            if not os.path.isdir(projectdir):
                try:
                    os.makedirs(projectdir)
                except:
                    QMessageBox.critical(None,
                        self.trUtf8("Create project directory"),
                        self.trUtf8("The project directory <b>%1</b> could not be created.")
                            .arg(projectdir),
                        QMessageBox.Ok, QMessageBox.NoButton, QMessageBox.NoButton)
                    self.vcs = None
                    return
            
            # create the project from the VCS
            if export:
                self.vcs.vcsExport(vcsDir, archive, projectdir, tag)
            else:
                self.vcs.vcsCheckout(vcsDir, archive, projectdir, 0, tag)
            vcsName = self.vcs.vcsName()
            if vcsName == 'CVS':
                projectdir = "%s%s%s" % (projectdir, os.sep, archive)
            projectdir = Utilities.normcasepath(projectdir)
            d = QDir(projectdir)
            plist = d.entryInfoList("*.e3p")
            if len(plist):
                if len(plist) == 1:
                    self.openProject(plist[0].absFilePath())
                    self.emit(PYSIGNAL('newProject'), ())
                else:
                    pfilenamelist = d.entryList("*.e3p") # same as above
                    pfilename, ok = QInputDialog.getItem(
                        self.trUtf8("New project from repository"),
                        self.trUtf8("Select a project file to open."),
                        pfilenamelist, 0, 0)
                    if ok:
                        self.openProject(QFileInfo(pfilename).absFilePath())
                        self.emit(PYSIGNAL('newProject'), ())
            else:
                res = QMessageBox.information(None,
                    self.trUtf8("New project from repository"),
                    self.trUtf8("The project retrieved from the repository"
                        " does not contain a project file (*.e3p)."
                        " Create it automatically?"),
                    self.trUtf8("&Yes"), self.trUtf8("&No"))
                if res == 0:
                    self.ppath = projectdir
                    self.opened = 1
                    
                    # search the project directory for files with extensions
                    # of 'py', 'idl', 'ui' and 'ts'
                    for filespec in ['*.py', '*.idl', '*.ui']:
                        files = Utilities.direntries(projectdir, 1, filespec)
                        for file in files:
                            self.appendFile(file)
                            
                    # special handling for translation files (*.ts)
                    tslist = d.entryList('*.ts')
                    if len(tslist) and tslist[0].contains('_'):
                        # the first entry determines the filename pattern
                        mainscriptname = tslist[0].section('_', 0, 0)
                        for ts in tslist:
                            if ts.startsWith(mainscriptname):
                                self.pdata["TRANSLATIONS"].append(str(ts))
                                self.emit(PYSIGNAL('projectLanguageAdded'), (str(ts),))
                        self.pdata["MAINSCRIPT"] = ['%s.py' % str(mainscriptname)]
                        
                    # now save the new project file and reload it
                    self.saveProjectAs()
                    self.openProject(self.pfile)
        
    def vcsExport(self):
        """
        Private slot used to export a project from the repository.
        """
        self.vcsCheckout(1)
        
    def vcsUpdate(self):
        """
        Private slot used to update the local project from the repository.
        """
        self.vcs.vcsUpdate(self.ppath)
        
    def vcsCommit(self):
        """
        Private slot used to commit changes to the local project to the repository.
        """
        self.vcs.vcsCommit(self.ppath, '')
        
    def vcsImport(self):
        """
        Private slot used to import the local project into the repository.
        
        NOTE: 
            This does not make the local project a vcs controlled project.
            You have to checkout the project from the repository in order
            to accomplish that.
        """
        pdata_vcs = self.pdata["VCS"]
        pdata_vcsoptions = self.pdata["VCSOPTIONS"]
        vcs = self.vcs
        vcsdlg = vcsOptionsDialog(self, self.name, 1)
        if vcsdlg.exec_loop() == QDialog.Accepted:
            vcsSystem, vcsDir, archive, vendor, message, release, coptions =\
                vcsdlg.getData()
            self.pdata["VCS"] = [vcsSystem]
            self.vcs = self.initVCS(vcsSystem)
            if self.vcs is not None:
                if coptions:
                    codlg = vcsCommandOptionsDialog(self.vcs)
                    if codlg.exec_loop() == QDialog.Accepted:
                        self.vcs.vcsSetOptions(codlg.getOptions())
                self.setDirty(1)
                self.saveProject()
                self.vcs.vcsInit(vcsDir, 1)
                self.vcs.vcsImport(vcsDir, message, archive, vendor, release, self.ppath)
                # revert the changes to the local project 
                # because the project dir is not a VCS directory
                self.pdata["VCS"] = pdata_vcs
                self.pdata["VCSOPTIONS"] = pdata_vcsoptions
                self.vcs = vcs
                self.setDirty(1)
                self.saveProject()
        
    def vcsRemove(self):
        """
        Private slot used to remove the local project from the repository.
        
        Depending on the parameters set in the vcs object the project
        may be removed from the local disk as well.
        """
        res = QMessageBox.warning(None,
            self.trUtf8("Remove project from repository"),
            self.trUtf8("Dou you really want to remove this project from the repository (and disk)?"),
            self.trUtf8("&Yes"), self.trUtf8("&No"), None, 1)
        if res == 0:
            self.vcs.vcsRemove(self.ppath, 1)
            self.vcsCommit()
            if not os.path.exists(self.pfile):
                ppath = self.ppath
                self.setDirty(0)
                self.closeProject()
                shutil.rmtree(ppath, 1)
            
    def vcsCommandOptions(self):
        """
        Private slot to edit the VCS command options.
        """
        codlg = vcsCommandOptionsDialog(self.vcs)
        if codlg.exec_loop() == QDialog.Accepted:
            self.vcs.vcsSetOptions(codlg.getOptions())
            self.setDirty(1)
        
    def vcsLog(self):
        """
        Private slot used to show the log of the local project.
        """
        self.vcs.vcsLog(self.ppath)
        
    def vcsDiff(self):
        """
        Private slot used to show the difference of the local project to the repository.
        """
        self.vcs.vcsDiff(self.ppath)
        
    def vcsStatus(self):
        """
        Private slot used to show the status of the local project.
        """
        self.vcs.vcsStatus(self.ppath)
        
    def vcsTag(self):
        """
        Private slot used to tag the local project in the repository.
        """
        self.vcs.vcsTag(self.ppath)
        
    def vcsLogin(self):
        """
        Private slot used to login to a remote repository.
        """
        dlg = vcsLoginDialog()
        if dlg.exec_loop() == QDialog.Accepted:
            vcsSystem, username, password, hostname, repository = dlg.getData()
            vcs = self.initVCS(vcsSystem)
            if vcs is not None:
                vcs.vcsLogin(username, password, hostname, repository)
        
    def vcsLogout(self):
        """
        Private slot used to logout from a remote repository.
        """
        dlg = vcsLogoutDialog()
        if dlg.exec_loop() == QDialog.Accepted:
            vcsSystem, username, hostname, repository = dlg.getData()
            vcs = self.initVCS(vcsSystem)
            if vcs is not None:
                vcs.vcsLogout(username, hostname, repository)
        
    def vcsRevert(self):
        """
        Private slot used to revert changes made to the local project.
        """
        self.vcs.vcsRevert(self.ppath)
        
    def vcsSwitch(self):
        """
        Private slot used to switch the local project to another tag/branch.
        """
        self.vcs.vcsSwitch(self.ppath)
        
    def vcsMerge(self):
        """
        Private slot used to merge changes of a tag/revision into the local project.
        """
        self.vcs.vcsMerge(self.ppath)
        
    def vcsCleanup(self):
        """
        Private slot used to cleanup the local project.
        """
        self.vcs.vcsCleanup(self.ppath)
        
    def vcsCommand(self):
        """
        Private slot used to execute an arbitrary vcs command.
        """
        self.vcs.vcsCommandLine(self.ppath)
        
    #########################################################################
    ## Subversion specific methods below
    #########################################################################
    
    def svnResolve(self):
        """
        Private slot used to resolve conflicts of the local project.
        """
        self.vcs.svnResolve(self.ppath)
        
    def svnClearAuth(self):
        """
        Private slot used to clear the stored authentication info.
        """
        self.vcs.svnClearAuthInfo()
        
    def svnPropList(self):
        """
        Private slot used to list the properties of the project files.
        """
        self.vcs.svnListProps(self.ppath, 1)
        
    def svnPropSet(self):
        """
        Private slot used to set a property for the project files.
        """
        self.vcs.svnSetProp(self.ppath, 1)
        
    def svnPropDel(self):
        """
        Private slot used to delete a property for the project files.
        """
        self.vcs.svnDelProp(self.ppath, 1)
        
    def svnTagList(self):
        """
        Private slot used to list the tags of the project.
        """
        self.vcs.svnListTagBranch(self.ppath, 1)
        
    def svnBranchList(self):
        """
        Private slot used to list the branches of the project.
        """
        self.vcs.svnListTagBranch(self.ppath, 0)
        
    #########################################################################
    ## Below is the interface to the checker tools
    #########################################################################
    
    def handleTabnanny(self):
        """
        Private slot used to check the project files for bad indentations.
        """
        self.saveAllScripts()
        files = [os.path.join(self.ppath, file) \
            for file in self.pdata["SOURCES"] if file.endswith(".py")]
        self.tabnanny = TabnannyDialog(qApp.mainWidget().getViewManager())
        self.tabnanny.show()
        self.tabnanny.start(files)
    
    def handleSyntaxCheck(self):
        """
        Private slot used to check the project files for bad indentations.
        """
        self.saveAllScripts()
        files = [os.path.join(self.ppath, file) \
            for file in self.pdata["SOURCES"] if file.endswith(".py")]
        self.syntaxcheck = SyntaxCheckerDialog(qApp.mainWidget().getViewManager())
        self.syntaxcheck.show()
        self.syntaxcheck.start(files)

    def handleCodeMetrics(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 handleCodeCoverage(self):
        """
        Private slot used to show the code coverage information for the project files.
        """
        fn = self.getMainScript(1)
        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"),
                self.trUtf8("&OK"))
            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 handleProfileData(self):
        """
        Private slot used to show the profiling information for the project.
        """
        fn = self.getMainScript(1)
        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"),
                self.trUtf8("&OK"))
            return
        
        self.profiledata = PyProfileDialog()
        self.profiledata.show()
        self.profiledata.start(fn)
        
    def handleShowShowMenu(self):
        """
        Private slot called before the show menu is shown.
        """
        fn = self.getMainScript(1)
        if fn is not None:
            basename = os.path.splitext(fn)[0]
            self.codeProfileAct.setEnabled(os.path.isfile("%s.profile" % basename))
            self.codeCoverageAct.setEnabled(os.path.isfile("%s.coverage" % basename))
        else:
            self.codeProfileAct.setEnabled(0)
            elf.codeCoverageAct.setEnabled(0)
