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

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

"""
Module implementing the project browser part of the eric3 UI.
"""

import os
import sys
import shutil
import glob
import string

from qt import *

from KdeQt import KQFileDialog, KQMessageBox, KQInputDialog
from KdeQt.KQProgressDialog import KQProgressDialog

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

from UI.Browser import *
from UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog
from UI.CodeMetricsDialog import CodeMetricsDialog
from UI.PyCoverageDialog import PyCoverageDialog
from UI.PyProfileDialog import PyProfileDialog
import UI.PixmapCache

from Graphics.UMLClassDiagram import UMLClassDiagram, \
    resetCachedWidgets, resetCachedWidgetsByFile
from Graphics.ImportsDiagram import ImportsDiagram
from Graphics.ApplicationDiagram import ApplicationDiagram
from Graphics.PackageDiagram import PackageDiagram

from Utilities.ModuleParser import resetParsedModules, resetParsedModule

import Utilities
import Preferences

from eric3config import getConfig

class ProjectBrowserSimpleDir(BrowserNode):
    """
    Class implementing a small wrapper around BrowserNode.
    
    It sets a pixmap depending on the open state.
    """
    def __init__(self,parent,text, path=""):
        """
        Constructor
        
        @param parent parent widget of this item (QListView or QListViewItem)
        @param text text to be displayed (string or QString)
        @param path path of the directory (string or QString)
        """
        BrowserNode.__init__(self, parent, text, None)
        
        self.directory = unicode(path)
        if not os.path.isdir(self.directory):
            self.directory = os.path.dirname(self.directory)
        
        self.setExpandable(1)
        self.setOpen(0)
        
    def setOpen(self, o):
        """
        Slot called to open/close the tree node.
        
        It sets the pixmap displayed next to the tree node depending of the
        open status of the node.
        
        @param o flag indicating the open status (boolean)
        """
        if o:
            self.setPixmap(0, UI.PixmapCache.getPixmap("dirOpen.png"))
        else:
            self.setPixmap(0, UI.PixmapCache.getPixmap("dirClosed.png"))
            
        QListViewItem.setOpen(self,o)
        
    def compare(self, item, col, ascending):
        """
        Private method to compare two items.
        """
        if issubclass(item.__class__, BrowserFile):
            if Preferences.getUI("BrowsersListFoldersFirst"):
                return ascending and -1 or 1
        
        return QListViewItem.compare(self, item, col, ascending)
        
    def fileName(self):
        """
        Public method returning the name of this directory.
        
        @return directory name (string)
        """
        return self.directory
        
    def dirName(self):
        """
        Public method returning the name of this directory.
        
        @return directory name (string)
        """
        return self.directory
        
class ProjectBrowserDirectory(BrowserDirectory):
    """
    Class implementig a BrowserNode that represents a directory.  
    """
    def __init__(self,parent,dinfo,after,full=1,bold=0,vcs=None):
        """
        Constructor
        
        @param parent the parent Browser or BrowserNode
        @param dinfo name of the directory (string or QString)
        @param after the sibling this node is position after or None, if this is
                the first child
        @param full flag indicating whether the full pathname of the directory
                should be displayed
        @param bold flag indicating whether the directory name should be displayed
                in bold
        @param vcs if a vcs object is given, the VCS status of the files
                contained in the directory is displayed
        """
        BrowserDirectory.__init__(self,parent,dinfo,after,full,bold)
        
        self.vcs = vcs
        
    def setOpen(self,o):
        """
        Slot called to open/close the directory node.
        
        @param o flag indicating the open status (boolean)
        """
        if o:
            self.dir = QDir(self.dirName)
            last = None

            for f in self.dir.entryInfoList():
                if unicode(f.fileName()) in ('.', '..'):
                    continue

                if f.isDir():
                    node = ProjectBrowserDirectory(self,
                        unicode(QDir.convertSeparators(f.absFilePath())),last,0,0,self.vcs)
                else:
                    node = BrowserFile(self,
                        unicode(QDir.convertSeparators(f.absFilePath())),last)
                if self.vcs is not None:
                    state = self.vcs.vcsRegisteredState(f.absFilePath())
                    if state == self.vcs.canBeCommitted:
                        node.setText(1, self.vcs.vcsName())
                    else:
                        node.setText(1, 
                            qApp.translate("ProjectBrowserDirectory", "local"))

                last = node
                self.children.append(node)

            self.setPixmap(0,UI.PixmapCache.getPixmap("dirOpen.png"))
        else:
            for child in self.children:
                self.takeItem(child)
                del child
            self.children = []
                
            self.setPixmap(0,UI.PixmapCache.getPixmap("dirClosed.png"))

        QListViewItem.setOpen(self,o)
        
    def compare(self, item, col, ascending):
        """
        Private method to compare two items.
        """
        if issubclass(item.__class__, BrowserFile):
            if Preferences.getUI("BrowsersListFoldersFirst"):
                return ascending and -1 or 1
        
        return QListViewItem.compare(self, item, col, ascending)


class ProjectBrowser(QTabWidget):
    """
    Class implementing the project browser part of the eric3 UI.
    
    It generates a widget with four tabs. The individual tabs contain
    the project sources browser, the project forms browser,
    the project translations browser and a browser for stuff
    that doesn't fit these categories.
    
    @signal editorSaved(string) emitted after an editor has been saved
    """
    def __init__(self, project, qtdir, parent=None, embeddedBrowser=1):
        """
        Constructor
        
        @param project reference to the project object
        @param qtdir Qt installation directory (string)
        @param parent parent widget (QWidget)
        @param embeddedBrowser flag indicating whether the file browser should
                be included. This flag is set to 0 by those layouts, that
                have the file browser in a separate window or embedded
                in the project browser instead
        """
        QTabWidget.__init__(self, parent)
        self.project = project
        
        # add the sources browser
        self.psBrowser = ProjectSourcesBrowser(self.project, self)
        self.addTab(self.psBrowser, 
            QIconSet(UI.PixmapCache.getPixmap("projectSources.png")), '')
        self.setTabToolTip(self.psBrowser, self.psBrowser.caption())
        self.connect(self.project, PYSIGNAL('projectClosed'),
                self.psBrowser.handleProjectClosed)
        self.connect(self.project, PYSIGNAL('projectOpened'),
                self.psBrowser.handleProjectOpened)
        self.connect(self.project, PYSIGNAL('newProject'),
                self.psBrowser.handleNewProject)
        self.connect(self.project, PYSIGNAL('projectSourceAdded'),
                self.psBrowser.handleProjectSourceAdded)
        self.connect(self, PYSIGNAL('editorSaved'),
                self.psBrowser.handleEditorSaved)
        self.connect(self.project, PYSIGNAL('directoryRemoved'),
                self.psBrowser.rebuildTree)
        
        # add the forms browser
        self.pfBrowser = ProjectFormsBrowser(self.project, qtdir, self)
        self.addTab(self.pfBrowser, 
            QIconSet(UI.PixmapCache.getPixmap("projectForms.png")), '')
        self.setTabToolTip(self.pfBrowser, self.pfBrowser.caption())
        self.connect(self.project, PYSIGNAL('projectClosed'),
                self.pfBrowser.handleProjectClosed)
        self.connect(self.project, PYSIGNAL('projectOpened'),
                self.pfBrowser.handleProjectOpened)
        self.connect(self.project, PYSIGNAL('newProject'),
                self.pfBrowser.handleNewProject)
        self.connect(self.project, PYSIGNAL('projectFormAdded'),
                self.pfBrowser.handleProjectFormAdded)
        self.connect(self.pfBrowser, PYSIGNAL('projectSourceAdded'),
                self.psBrowser.handleProjectSourceAdded)
        self.connect(self.project, PYSIGNAL('directoryRemoved'),
                self.pfBrowser.rebuildTree)
        
        # add the translations browser
        self.ptBrowser = ProjectTranslationsBrowser(self.project, qtdir, self)
        self.addTab(self.ptBrowser, 
            QIconSet(UI.PixmapCache.getPixmap("projectTranslations.png")), '')
        self.setTabToolTip(self.ptBrowser, self.ptBrowser.caption())
        self.connect(self.project, PYSIGNAL('projectClosed'),
                self.ptBrowser.handleProjectClosed)
        self.connect(self.project, PYSIGNAL('projectOpened'),
                self.ptBrowser.handleProjectOpened)
        self.connect(self.project, PYSIGNAL('newProject'),
                self.ptBrowser.handleNewProject)
        self.connect(self.project, PYSIGNAL('projectLanguageAdded'),
                self.ptBrowser.handleProjectLanguageAdded)
        
        # add the interfaces (IDL)  browser
        self.piBrowser = ProjectInterfacesBrowser(self.project, self)
        self.addTab(self.piBrowser, 
            QIconSet(UI.PixmapCache.getPixmap("projectInterfaces.png")), '')
        self.setTabToolTip(self.piBrowser, self.piBrowser.caption())
        self.connect(self.project, PYSIGNAL('projectClosed'),
                self.piBrowser.handleProjectClosed)
        self.connect(self.project, PYSIGNAL('projectOpened'),
                self.piBrowser.handleProjectOpened)
        self.connect(self.project, PYSIGNAL('newProject'),
                self.piBrowser.handleNewProject)
        self.connect(self.project, PYSIGNAL('projectInterfaceAdded'),
                self.piBrowser.handleProjectInterfaceAdded)
        self.connect(self.piBrowser, PYSIGNAL('projectSourceAdded'),
                self.psBrowser.handleProjectSourceAdded)
        self.connect(self.project, PYSIGNAL('directoryRemoved'),
                self.piBrowser.rebuildTree)
        
        # add the others browser
        self.poBrowser = ProjectOthersBrowser(self.project, self)
        self.addTab(self.poBrowser, 
            QIconSet(UI.PixmapCache.getPixmap("projectOthers.png")), '')
        self.setTabToolTip(self.poBrowser, self.poBrowser.caption())
        self.connect(self.project, PYSIGNAL('projectClosed'),
                self.poBrowser.handleProjectClosed)
        self.connect(self.project, PYSIGNAL('projectOpened'),
                self.poBrowser.handleProjectOpened)
        self.connect(self.project, PYSIGNAL('newProject'),
                self.poBrowser.handleNewProject)
        self.connect(self.project, PYSIGNAL('projectOthersAdded'),
                self.poBrowser.addNode)
        self.connect(self.project, PYSIGNAL('directoryRemoved'),
                self.poBrowser.rebuildTree)
        
        # add the file browser, if it should be embedded here
        self.embeddedBrowser = embeddedBrowser
        if embeddedBrowser:
            self.fileBrowser = Browser(self)
            self.addTab(self.fileBrowser, 
                QIconSet(UI.PixmapCache.getPixmap("browser.png")), '')
            self.setTabToolTip(self.fileBrowser, self.fileBrowser.caption())
            
        # add signal connection to ourself
        self.connect(self.project, PYSIGNAL('projectOpened'),
                self.handleProjectOpened)
        self.connect(self.project, PYSIGNAL('projectClosed'),
                self.handleProjectClosed)
        self.connect(self.project, PYSIGNAL('newProject'),
                self.handleNewProject)
        self.connect(self.project, PYSIGNAL('projectPropertiesChanged'),
                self.handleProjectPropertiesChanged)
        
        if self.embeddedBrowser:
            self.showPage(self.fileBrowser)
        else:
            self.showPage(self.psBrowser)
        
        self.setIcon(UI.PixmapCache.getPixmap("eric.png"))
        
    def handleProjectOpened(self):
        """
        Private slot to handle the projectOpened signal.
        """
        self.showPage(self.psBrowser)
        self.handleProjectPropertiesChanged()
        
    def handleProjectClosed(self):
        """
        Private slot to handle the projectClosed signal.
        """
        if self.embeddedBrowser:
            self.showPage(self.fileBrowser)
        else:
            self.showPage(self.psBrowser)
        self.setTabEnabled(self.pfBrowser, 1)
        self.setTabEnabled(self.ptBrowser, 1)
        self.setSourcesIcon()
        
    def handleNewProject(self):
        """
        Private slot to handle the newProject signal.
        """
        self.showPage(self.psBrowser)
        self.handleProjectPropertiesChanged()
        
    def handleProjectPropertiesChanged(self):
        """
        Private slot to handle the projectPropertiesChanged signal.
        """
        if self.project.pdata["UITYPE"][0] in ["Qt", "Kde"]:
            self.setTabEnabled(self.pfBrowser, 1)
        else:
            self.setTabEnabled(self.pfBrowser, 0)
        if self.project.pdata["UITYPE"][0] in ["Qt"]:
            self.setTabEnabled(self.ptBrowser, 1)
        else:
            self.setTabEnabled(self.ptBrowser, 0)
        self.setSourcesIcon()
        
    def setSourcesIcon(self):
        """
        Private method to set the right icon for the sources browser tab.
        """
        if not self.project.isOpen():
            iconset = QIconSet(UI.PixmapCache.getPixmap("projectSources.png"))
        else:
            if self.project.pdata["PROGLANGUAGE"][0] == "Python":
                if self.project.pdata["MIXEDLANGUAGE"][0]:
                    iconset = QIconSet(UI.PixmapCache.getPixmap("projectSourcesPyMixed.png"))
                else:
                    iconset = QIconSet(UI.PixmapCache.getPixmap("projectSourcesPy.png"))
            elif self.project.pdata["PROGLANGUAGE"][0] == "Ruby":
                if self.project.pdata["MIXEDLANGUAGE"][0]:
                    iconset = QIconSet(UI.PixmapCache.getPixmap("projectSourcesRbMixed.png"))
                else:
                    iconset = QIconSet(UI.PixmapCache.getPixmap("projectSourcesRb.png"))
            else:
                iconset = QIconSet(UI.PixmapCache.getPixmap("projectSources.png"))
        self.setTabIconSet(self.psBrowser, iconset)
    
    def handleEditorSaved(self, fn):
        """
        Public slot to handle the editorSaved signal.
        
        It simply reemits it.
        
        @param fn The filename of the saved files. (string or QString)
        """
        self.emit(PYSIGNAL('editorSaved'), (fn,))
        
class PBrowser(Browser):
    """
    Baseclass implementing common functionality for the various browsers.
    """
    def __init__(self,project,pdataKey,parent=None):
        """
        Constructor
        
        @param project reference to the project object
        @param pdataKey key of the filelist the browser object is handling (string)
        @param parent parent widget of this browser
        """
        QListView.__init__(self,parent)
        
        self.setAllColumnsShowFocus(1)
        
        self.pdataKey = pdataKey
        self.project = project
        self.setRootIsDecorated(1)
        self.setSorting(0)
        self.setShowSortIndicator(1)
        self.addColumn(self.trUtf8('Name'))
        self.addColumn(self.trUtf8('VCS Status'))
        self.setSelectionMode(QListView.Extended)
        self.selectedItemsFilter = [BrowserFile]
        
        # contains codes for special menu entries
        # 1 = specials for Others browser
        self.specialMenuEntries = []
        
        self.children = []
        self.projectOpened = 0
        
        self.hideNonPublic = Preferences.getUI("BrowsersHideNonPublic")
        
        self.populateTree()
        
        self.connect(self,SIGNAL('contextMenuRequested(QListViewItem *, const QPoint &, int)'),
                     self.handleContextMenu)
        self.connect(self,SIGNAL('returnPressed(QListViewItem *)'),self.handleOpen)

        req = QSize(250,350)

        if parent is not None:
            req = req.boundedTo(parent.size())

        self.resize(req)

        self.createPopupMenus()
        
    def createPopupMenus(self):
        """
        Private overloaded method to generate the popup menu.
        """
        # create the popup menu for source files
        self.pyMenu = QPopupMenu(self)
        self.pyMenu.insertItem(qApp.translate('Browser','Open'), self.handleOpen)
        
        # create the popup menu for general use
        self.menu = QPopupMenu(self)
        self.menu.insertItem(qApp.translate('Browser','Open'), self.handleOpen)

        # create the menu for multiple selected files
        self.multiMenu = QPopupMenu(self)
        self.multiMenu.insertItem(qApp.translate('Browser','Open'), self.handleOpen)
        
        # create the background menu
        self.backMenu = None
        
        # create the directories menu
        self.dirMenu = None
        
        # create the directory for multiple selected directories
        self.dirMultiMenu = None
        
        self.menuItems = []
        self.multiMenuItems = []
        self.dirMenuItems = []
        self.dirMultiMenuItems = []
        
        self.mainMenu = None
        
    def handleNewProject(self):
        """
        Private slot to handle the newProject signal.
        """
        self.createPopupMenus()
        
        if self.backMenu is not None:
            self.backMenu.setEnabled(1)
        self.projectOpened = 1
        
        if self.project.vcs is not None:
            self.addVCSMenu(self.mainMenu)
            self.addVCSMenuMulti(self.multiMenu)
            self.addVCSMenuBack(self.backMenu)
            self.addVCSMenuDir(self.dirMenu)
            self.addVCSMenuDirMulti(self.dirMultiMenu)
        
    def handleProjectClosed(self):
        """
        Private slot to handle the projectClosed signal.
        """
        self.children = []
        self.clear()
        if self.backMenu is not None:
            self.backMenu.setEnabled(0)
        self.projectOpened = 0
        
        self.createPopupMenus()
        
    def handleProjectOpened(self):
        """
        Private slot to handle the projectOpened signal.
        """
        qApp.processEvents()
        
        self.createPopupMenus()
        
        self.populateTree()
        
        if self.backMenu is not None:
            self.backMenu.setEnabled(1)
        self.projectOpened = 1
        
        if self.project.vcs is not None:
            self.addVCSMenu(self.mainMenu)
            self.addVCSMenuMulti(self.multiMenu)
            self.addVCSMenuBack(self.backMenu)
            self.addVCSMenuDir(self.dirMenu)
            self.addVCSMenuDirMulti(self.dirMultiMenu)
        
    def handleRemove(self):
        """
        Private method to remove a file or files from the project.
        """
        itmList = self.getSelectedItems()
        
        for itm in itmList[:]:
            fn = unicode(itm.fileName())
            self.removeNode(itm)
            self.children.remove(itm)
            del itm
            
            self.emit(PYSIGNAL('closeSourceWindow'), (fn,))
            self.project.removeFile(fn)
        
    def handleRemoveDir(self):
        """
        Private method to remove a (single) directory from the project.
        """
        itmList = self.getSelectedItems([ProjectBrowserSimpleDir])
        for itm in itmList[:]:
            dn = unicode(itm.dirName())
            self.project.removeDirectory(dn)
        
    def populateTree(self):
        """
        Private method used to populate the listview.
        """
        if self.project.vcs is not None:
            states = {}
            for fn in self.project.pdata[self.pdataKey]:
                states[os.path.normcase(os.path.join(self.project.ppath, fn))] = 0
            if self.pdataKey == "OTHERS":
                for dir in self.project.otherssubdirs:
                    if not os.path.isabs(dir):
                        dir = os.path.join(self.project.ppath, dir)
                    states = self.project.vcs.vcsAllRegisteredStates(states, dir)
            elif self.pdataKey == "TRANSLATIONS" and self.project.pdata["TRANSLATIONPREFIX"]:
                dir = os.path.dirname(self.project.pdata["TRANSLATIONPREFIX"][0])
                states = self.project.vcs.vcsAllRegisteredStates(states, 
                    os.path.join(self.project.ppath, dir))
            else:
                for dir in self.project.subdirs:
                    states = self.project.vcs.vcsAllRegisteredStates(states, 
                        os.path.join(self.project.ppath, dir))
            
        # Show the entry in bold in the others browser to make it more distinguishable
        if self.pdataKey == "OTHERS":
            bold = 1
        else:
            bold = 0
        
        if self.pdataKey == "TRANSLATIONS" and self.project.pdata["TRANSLATIONPREFIX"]:
            path = os.path.join(self.project.ppath,
                        os.path.dirname(self.project.pdata["TRANSLATIONPREFIX"][0]))
            node = ProjectBrowserSimpleDir(self, 
                        "%s_" % self.project.pdata["TRANSLATIONPREFIX"][0], path)
            self.nodeAdded(node, path)
            self.children.append(node)
            node.setOpen(1)
        
        for fn in self.project.pdata[self.pdataKey]:
            fname = os.path.join(self.project.ppath, fn)
            if self.pdataKey == "TRANSLATIONS":
                dt, ext = os.path.splitext(fn)
                dtl = dt.split('_', 1)
                dt = dtl[-1]
                dt0 = "%s_" % '_'.join(dtl[:-1])
                parent = self.findParentNode(dt0, 1)[0]
                node = BrowserFile(parent, fname, None, 1, dt)
            else:
                parent, dt = self.findParentNode(fn)
                if os.path.isdir(fname):
                    node = ProjectBrowserDirectory(parent, fname, None, 0, 
                        bold, self.project.vcs)
                else:
                    node = BrowserFile(parent, fname, None, 1, dt, bold,
                        isPyFile = (self.pdataKey == "SOURCES" and \
                                    self.project.pdata["PROGLANGUAGE"][0] == "Python"))
            self.children.append(node)
            if self.project.vcs is not None:
                if states[os.path.normcase(fname)] == self.project.vcs.canBeCommitted:
                    node.setText(1, self.project.vcs.vcsName())
                else:
                    node.setText(1, self.trUtf8("local"))
                    
                if self.pdataKey == "SOURCES":
                    for itm in self.getItemsFiltered([ProjectBrowserSimpleDir]):
                        dn = itm.dirName()
                        if self.project.vcs.vcsRegisteredState(dn) == self.project.vcs.canBeCommitted:
                            itm.setText(1, self.project.vcs.vcsName())
                        else:
                            itm.setText(1, self.trUtf8("local"))
        
    def rebuildTree(self):
        """
        Protected method to rebuild the tree.
        """
        self.children = []
        self.clear()
        self.populateTree()
        
    def findItem(self, text, column, node=None):
        """
        Reimplemented method
        
        It is used to find a specific item with text in column,
        that is a child of node. If node is None, a child of the
        QListView is searched.
        
        @param text text to search for (string or QString)
        @param column index of column to search in (int)
        @param node start point of the search
        @return the found item
        """
        if node is None:
            node = self
            
        itm = node.firstChild()
        while itm is not None:
            if QString.compare(itm.text(column), text) == 0:
                break
            itm = itm.nextSibling()
        return itm
        
    def findParentNode(self, fn, dontSplit=0):
        """
        Private method used to find or create the parent node.
        
        @param fn filename to use for the search
        @param dontSplit flag to indicate, that no path splitting should be performed
        @return tuple of two values giving the parent node and the shortened filename
        """
        if dontSplit:
            pathlist = QStringList()
            pathlist.append(fn)
            pathlist.append("ignore_me")
        else:
            pathlist = QStringList.split(QRegExp(r'/|\\'), fn)
        if len(pathlist) > 1:
            oldnode = self
            path = self.project.ppath
            for p in pathlist[:-1]:
                node = self.findItem(p, 0, oldnode)
                path = os.path.join(path, unicode(p))
                if node is None:
                    node = ProjectBrowserSimpleDir(oldnode, p, path)
                    self.nodeAdded(node, path)
                oldnode = node
            return (node, QString(pathlist[-1]))
        else:
            return (self, fn)
            
    def removeNode(self, node):
        """
        Private method to remove a parent (dir) node, if it doesn't have any children.
        
        @param node node to remove
        """
        if node is None:
            return
            
        parent = node.parent()
        if parent is None:
            self.takeItem(node)
        else:
            parent.takeItem(node)
            if parent.childCount() == 0:
                self.removeNode(parent)
                del parent
        
    def nodeAdded(self, node, name):
        """
        Public method used to perform common operations on a new node.
        
        @param node node to work on
        @param name filename belonging to this node
        """
        self.updateVCSStatus(node, name)
        
    def handleExpandAllDirs(self):
        """
        Protected slot to handle the 'Expand all directories' menu action.
        """
        itm = self.firstChild()
        while itm is not None:
            if isinstance(itm, ProjectBrowserSimpleDir) and not itm.isOpen():
                itm.setOpen(1)
            itm = itm.itemBelow()
            
    def handleCollapseAllDirs(self):
        """
        Protected slot to handle the 'Collapse all directories' menu action.
        """
        itm = self.lastItem()
        while itm is not None:
            if isinstance(itm, ProjectBrowserSimpleDir) and itm.isOpen():
                itm.setOpen(0)
            itm = itm.itemAbove()
        
    def updateVCSStatus(self, node, name):
        """
        Private method used to set the vcs status of a node.
        
        @param node node to work on
        @param name filename belonging to this node
        """
        if self.project.vcs is not None:
            state = self.project.vcs.vcsRegisteredState(name)
            if state == self.project.vcs.canBeCommitted:
                node.setText(1, self.project.vcs.vcsName())
            else:
                node.setText(1, self.trUtf8("local"))
        
    def addVCSMenu(self, menu):
        """
        Public method used to add the VCS menu to all project browsers.
        
        @param menu reference to the menu to be amended
        """
        self.vcsMenuItems = []
        self.vcsAddMenuItems = []
        
        lbl = QLabel(self.project.vcs.vcsName(), menu)
        lbl.setFrameStyle( QFrame.Panel | QFrame.Sunken )
        lbl.setAlignment(Qt.AlignHCenter)
        font = lbl.font()
        font.setBold(1)
        lbl.setFont(font)
        menu.insertItem(lbl)
        
        itm = menu.insertItem(self.trUtf8('Update from repository'), self.handleVCSUpdate)
        self.vcsMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Commit changes to repository...'), self.handleVCSCommit)
        self.vcsMenuItems.append(itm)
        menu.insertSeparator()
        itm = menu.insertItem(self.trUtf8('Add to repository'), self.handleVCSAdd)
        self.vcsAddMenuItems.append(itm)
        if 1 in self.specialMenuEntries:
            if self.project.vcs.vcsName() == "CVS":
                itm = menu.insertItem(self.trUtf8('Add to repository (binary)'), self.handleVCSAddBinary)
                self.vcsAddMenuItems.append(itm)
            self.vcsMenuAddTree = menu.insertItem(self.trUtf8('Add tree to repository'), self.handleVCSAddTree)
            self.vcsAddMenuItems.append(self.vcsMenuAddTree)
        itm = menu.insertItem(self.trUtf8('Remove from repository (and disk)'), self.handleVCSRemove)
        self.vcsMenuItems.append(itm)
        menu.insertSeparator()
        if self.project.vcs.vcsName() == "Subversion":
            itm = menu.insertItem(self.trUtf8('Copy in repository'), self.handleSVNCopy)
            self.vcsMenuItems.append(itm)
            itm = menu.insertItem(self.trUtf8('Move in repository'), self.handleSVNMove)
            self.vcsMenuItems.append(itm)
        if self.project.vcs.vcsName() == "CVS":
            itm = menu.insertItem(self.trUtf8('Edit'), self.handleCVSEdit)
            self.vcsMenuItems.append(itm)
            self.cvsMenuEdit = itm
            itm = menu.insertItem(self.trUtf8('Unedit'), self.handleCVSUnedit)
            self.vcsMenuItems.append(itm)
            self.cvsMenuUnedit = itm
        menu.insertSeparator()
        itm = menu.insertItem(self.trUtf8('Show log'), self.handleVCSLog)
        self.vcsMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Show status'), self.handleVCSStatus)
        self.vcsMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Show difference'), self.handleVCSDiff)
        self.vcsMenuItems.append(itm)
        if self.project.vcs.vcsName() == "Subversion":
            itm = menu.insertItem(self.trUtf8('Show difference (extended)'), self.handleSVNExtendedDiff)
            self.vcsMenuItems.append(itm)
        if self.project.vcs.vcsName() == "CVS":
            itm = menu.insertItem(self.trUtf8('Show history'), self.handleVCSHistory)
            self.vcsMenuItems.append(itm)
        menu.insertSeparator()
        itm = menu.insertItem(self.trUtf8('Revert changes'), self.handleVCSRevert)
        self.vcsMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Merge changes'), self.handleVCSMerge)
        self.vcsMenuItems.append(itm)
        if self.project.vcs.vcsName() == "Subversion":
            itm = menu.insertItem(self.trUtf8('Resolve conflict'), self.handleSVNResolve)
            self.vcsMenuItems.append(itm)
            menu.insertSeparator()
            itm = menu.insertItem(self.trUtf8('Set Property'), self.handleSVNSetProp)
            self.vcsMenuItems.append(itm)
            itm = menu.insertItem(self.trUtf8('List Properties'), self.handleSVNListProps)
            self.vcsMenuItems.append(itm)
            itm = menu.insertItem(self.trUtf8('Delete Property'), self.handleSVNDelProp)
            self.vcsMenuItems.append(itm)
        menu.insertSeparator()
        menu.insertItem(self.trUtf8('Select all local file entries'),
                        self.handleSelectLocalEntries)
        menu.insertItem(self.trUtf8('Select all versioned file entries'),
                        self.handleSelectVCSEntries)
        menu.insertItem(self.trUtf8('Select all local directory entries'),
                        self.handleSelectLocalDirEntries)
        menu.insertItem(self.trUtf8('Select all versioned directory entries'),
                        self.handleSelectVCSDirEntries)
        
    def addVCSMenuMulti(self, menu):
        """
        Public method used to add the VCS menu for multi selection to all project browsers.
        
        @param menu reference to the menu to be amended
        """
        self.vcsMultiMenuItems = []
        self.vcsAddMultiMenuItems = []
        
        lbl = QLabel(self.project.vcs.vcsName(), menu)
        lbl.setFrameStyle( QFrame.Panel | QFrame.Sunken )
        lbl.setAlignment(Qt.AlignHCenter)
        font = lbl.font()
        font.setBold(1)
        lbl.setFont(font)
        menu.insertItem(lbl)
        
        itm = menu.insertItem(self.trUtf8('Update from repository'), self.handleVCSUpdate)
        self.vcsMultiMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Commit changes to repository...'), self.handleVCSCommit)
        self.vcsMultiMenuItems.append(itm)
        menu.insertSeparator()
        itm = menu.insertItem(self.trUtf8('Add to repository'), self.handleVCSAdd)
        self.vcsAddMultiMenuItems.append(itm)
        if 1 in self.specialMenuEntries:
            if self.project.vcs.vcsName() == "CVS":
                itm = menu.insertItem(self.trUtf8('Add to repository (binary)'), self.handleVCSAddBinary)
                self.vcsAddMultiMenuItems.append(itm)
            self.vcsMultiMenuAddTree = menu.insertItem(self.trUtf8('Add tree to repository'), self.handleVCSAddTree)
            self.vcsAddMultiMenuItems.append(self.vcsMultiMenuAddTree)
        itm = menu.insertItem(self.trUtf8('Remove from repository (and disk)'), self.handleVCSRemove)
        self.vcsMultiMenuItems.append(itm)
        self.vcsRemoveMultiMenuItem = itm
        if self.project.vcs.vcsName() == "CVS":
            menu.insertSeparator()
            itm = menu.insertItem(self.trUtf8('Edit'), self.handleCVSEdit)
            self.vcsMultiMenuItems.append(itm)
            self.cvsMultiMenuEdit = itm
            itm = menu.insertItem(self.trUtf8('Unedit'), self.handleCVSUnedit)
            self.vcsMultiMenuItems.append(itm)
            self.cvsMultiMenuUnedit = itm
        menu.insertSeparator()
        itm = menu.insertItem(self.trUtf8('Show status'), self.handleVCSStatus)
        self.vcsMultiMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Show difference'), self.handleVCSDiff)
        self.vcsMultiMenuItems.append(itm)
        if self.project.vcs.vcsName() == "Subversion":
            itm = menu.insertItem(self.trUtf8('Show difference (extended)'), self.handleSVNExtendedDiff)
            self.vcsMultiMenuItems.append(itm)
        menu.insertSeparator()
        itm = menu.insertItem(self.trUtf8('Revert changes'), self.handleVCSRevert)
        self.vcsMultiMenuItems.append(itm)
        if self.project.vcs.vcsName() == "Subversion":
            itm = menu.insertItem(self.trUtf8('Resolve conflict'), self.handleSVNResolve)
            self.vcsMultiMenuItems.append(itm)
            menu.insertSeparator()
            itm = menu.insertItem(self.trUtf8('Set Property'), self.handleSVNSetProp)
            self.vcsMultiMenuItems.append(itm)
            itm = menu.insertItem(self.trUtf8('List Properties'), self.handleSVNListProps)
            self.vcsMultiMenuItems.append(itm)
            itm = menu.insertItem(self.trUtf8('Delete Property'), self.handleSVNDelProp)
            self.vcsMultiMenuItems.append(itm)
        menu.insertSeparator()
        menu.insertItem(self.trUtf8('Select all local file entries'),
                        self.handleSelectLocalEntries)
        menu.insertItem(self.trUtf8('Select all versioned file entries'),
                        self.handleSelectVCSEntries)
        menu.insertItem(self.trUtf8('Select all local directory entries'),
                        self.handleSelectLocalDirEntries)
        menu.insertItem(self.trUtf8('Select all versioned directory entries'),
                        self.handleSelectVCSDirEntries)
        
    def addVCSMenuBack(self, menu):
        """
        Public method used to add the VCS menu to all project browsers.
        
        @param menu reference to the menu to be amended
        """
        lbl = QLabel(self.project.vcs.vcsName(), menu)
        lbl.setFrameStyle( QFrame.Panel | QFrame.Sunken )
        lbl.setAlignment(Qt.AlignHCenter)
        font = lbl.font()
        font.setBold(1)
        lbl.setFont(font)
        menu.insertItem(lbl)
        
        menu.insertItem(self.trUtf8('Select all local file entries'),
                        self.handleSelectLocalEntries)
        menu.insertItem(self.trUtf8('Select all versioned file entries'),
                        self.handleSelectVCSEntries)
        menu.insertItem(self.trUtf8('Select all local directory entries'),
                        self.handleSelectLocalDirEntries)
        menu.insertItem(self.trUtf8('Select all versioned directory entries'),
                        self.handleSelectVCSDirEntries)
        
    def addVCSMenuDir(self, menu):
        """
        Public method used to add the VCS menu to all project browsers.
        
        @param menu reference to the menu to be amended
        """
        if menu is None:
            return
        
        self.vcsDirMenuItems = []
        self.vcsAddDirMenuItems = []
        
        lbl = QLabel(self.project.vcs.vcsName(), menu)
        lbl.setFrameStyle( QFrame.Panel | QFrame.Sunken )
        lbl.setAlignment(Qt.AlignHCenter)
        font = lbl.font()
        font.setBold(1)
        lbl.setFont(font)
        menu.insertItem(lbl)
        
        itm = menu.insertItem(self.trUtf8('Update from repository'), self.handleVCSUpdate)
        self.vcsDirMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Commit changes to repository...'), self.handleVCSCommit)
        self.vcsDirMenuItems.append(itm)
        menu.insertSeparator()
        itm = menu.insertItem(self.trUtf8('Add to repository'), self.handleVCSAdd)
        self.vcsAddDirMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Remove from repository (and disk)'), self.handleVCSRemove)
        self.vcsDirMenuItems.append(itm)
        menu.insertSeparator()
        if self.project.vcs.vcsName() == "Subversion":
            itm = menu.insertItem(self.trUtf8('Copy in repository'), self.handleSVNCopy)
            self.vcsDirMenuItems.append(itm)
            itm = menu.insertItem(self.trUtf8('Move in repository'), self.handleSVNMove)
            self.vcsDirMenuItems.append(itm)
            menu.insertSeparator()
        itm = menu.insertItem(self.trUtf8('Show log'), self.handleVCSLog)
        self.vcsDirMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Show status'), self.handleVCSStatus)
        self.vcsDirMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Show difference'), self.handleVCSDiff)
        self.vcsDirMenuItems.append(itm)
        if self.project.vcs.vcsName() == "Subversion":
            itm = menu.insertItem(self.trUtf8('Show difference (extended)'), self.handleSVNExtendedDiff)
            self.vcsDirMenuItems.append(itm)
        if self.project.vcs.vcsName() == "CVS":
            itm = menu.insertItem(self.trUtf8('Show history'), self.handleVCSHistory)
            self.vcsDirMenuItems.append(itm)
        menu.insertSeparator()
        itm = menu.insertItem(self.trUtf8('Revert changes'), self.handleVCSRevert)
        self.vcsDirMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Merge changes'), self.handleVCSMerge)
        self.vcsDirMenuItems.append(itm)
        if self.project.vcs.vcsName() == "Subversion":
            itm = menu.insertItem(self.trUtf8('Resolve conflict'), self.handleSVNResolve)
            self.vcsDirMenuItems.append(itm)
            menu.insertSeparator()
            itm = menu.insertItem(self.trUtf8('Set Property'), self.handleSVNSetProp)
            self.vcsDirMenuItems.append(itm)
            itm = menu.insertItem(self.trUtf8('List Properties'), self.handleSVNListProps)
            self.vcsDirMenuItems.append(itm)
            itm = menu.insertItem(self.trUtf8('Delete Property'), self.handleSVNDelProp)
            self.vcsDirMenuItems.append(itm)
        menu.insertSeparator()
        menu.insertItem(self.trUtf8('Select all local file entries'),
                        self.handleSelectLocalEntries)
        menu.insertItem(self.trUtf8('Select all versioned file entries'),
                        self.handleSelectVCSEntries)
        menu.insertItem(self.trUtf8('Select all local directory entries'),
                        self.handleSelectLocalDirEntries)
        menu.insertItem(self.trUtf8('Select all versioned directory entries'),
                        self.handleSelectVCSDirEntries)
        
    def addVCSMenuDirMulti(self, menu):
        """
        Public method used to add the VCS menu to all project browsers.
        
        @param menu reference to the menu to be amended
        """
        if menu is None:
            return
        
        self.vcsDirMultiMenuItems = []
        self.vcsAddDirMultiMenuItems = []
        
        lbl = QLabel(self.project.vcs.vcsName(), menu)
        lbl.setFrameStyle( QFrame.Panel | QFrame.Sunken )
        lbl.setAlignment(Qt.AlignHCenter)
        font = lbl.font()
        font.setBold(1)
        lbl.setFont(font)
        menu.insertItem(lbl)
        
        itm = menu.insertItem(self.trUtf8('Update from repository'), self.handleVCSUpdate)
        self.vcsDirMultiMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Commit changes to repository...'), self.handleVCSCommit)
        self.vcsDirMultiMenuItems.append(itm)
        menu.insertSeparator()
        itm = menu.insertItem(self.trUtf8('Add to repository'), self.handleVCSAdd)
        self.vcsAddDirMultiMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Remove from repository (and disk)'), self.handleVCSRemove)
        self.vcsDirMultiMenuItems.append(itm)
        menu.insertSeparator()
        itm = menu.insertItem(self.trUtf8('Show log'), self.handleVCSLog)
        self.vcsDirMultiMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Show status'), self.handleVCSStatus)
        self.vcsDirMultiMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Show difference'), self.handleVCSDiff)
        self.vcsDirMultiMenuItems.append(itm)
        if self.project.vcs.vcsName() == "Subversion":
            itm = menu.insertItem(self.trUtf8('Show difference (extended)'), self.handleSVNExtendedDiff)
            self.vcsDirMultiMenuItems.append(itm)
        if self.project.vcs.vcsName() == "CVS":
            itm = menu.insertItem(self.trUtf8('Show history'), self.handleVCSHistory)
            self.vcsDirMultiMenuItems.append(itm)
        menu.insertSeparator()
        itm = menu.insertItem(self.trUtf8('Revert changes'), self.handleVCSRevert)
        self.vcsDirMenuItems.append(itm)
        itm = menu.insertItem(self.trUtf8('Merge changes'), self.handleVCSMerge)
        self.vcsDirMenuItems.append(itm)
        if self.project.vcs.vcsName() == "Subversion":
            itm = menu.insertItem(self.trUtf8('Resolve conflict'), self.handleSVNResolve)
            self.vcsDirMultiMenuItems.append(itm)
            menu.insertSeparator()
            itm = menu.insertItem(self.trUtf8('Set Property'), self.handleSVNSetProp)
            self.vcsDirMultiMenuItems.append(itm)
            itm = menu.insertItem(self.trUtf8('List Properties'), self.handleSVNListProps)
            self.vcsDirMultiMenuItems.append(itm)
            itm = menu.insertItem(self.trUtf8('Delete Property'), self.handleSVNDelProp)
            self.vcsDirMultiMenuItems.append(itm)
        menu.insertSeparator()
        menu.insertItem(self.trUtf8('Select all local file entries'),
                        self.handleSelectLocalEntries)
        menu.insertItem(self.trUtf8('Select all versioned file entries'),
                        self.handleSelectVCSEntries)
        menu.insertItem(self.trUtf8('Select all local directory entries'),
                        self.handleSelectLocalDirEntries)
        menu.insertItem(self.trUtf8('Select all versioned directory entries'),
                        self.handleSelectVCSDirEntries)
        
    def handleShowPopupMenu(self, menu):
        """
        Slot called before the context menu is shown. 
        
        It enables/disables the VCS menu entries depending on the overall 
        VCS status and the file status.
        
        @param menu reference to the menu to be shown
        """
        if self.project.vcs is None:
            for itm in self.menuItems:
                menu.setItemEnabled(itm, 1)
        else:
            if unicode(self.currentItem().text(1)) == self.project.vcs.vcsName():
                for itm in self.vcsMenuItems:
                    menu.setItemEnabled(itm, 1)
                for itm in self.vcsAddMenuItems:
                    menu.setItemEnabled(itm, 0)
                for itm in self.menuItems:
                    menu.setItemEnabled(itm, 0)
            else:
                for itm in self.vcsMenuItems:
                    menu.setItemEnabled(itm, 0)
                for itm in self.vcsAddMenuItems:
                    menu.setItemEnabled(itm, 1)
                for itm in self.menuItems:
                    menu.setItemEnabled(itm, 1)
        
    def handleShowPopupMenuMulti(self, menu):
        """
        Slot called before the context menu (multiple selections) is shown. 
        
        It enables/disables the VCS menu entries depending on the overall 
        VCS status and the files status.
        
        @param menu reference to the menu to be shown
        """
        if self.project.vcs is None:
            for itm in self.multiMenuItems:
                menu.setItemEnabled(itm, 1)
        else:
            vcsName = self.project.vcs.vcsName()
            items = self.getSelectedItems()
            vcsItems = 0
            # determine number of selected items under VCS control
            for itm in items:
                if unicode(itm.text(1)) == vcsName:
                    vcsItems += 1
            
            if vcsItems > 0:
                if vcsItems != len(items):
                    for itm in self.vcsMultiMenuItems:
                        menu.setItemEnabled(itm, 0)
                else:
                    for itm in self.vcsMultiMenuItems:
                        menu.setItemEnabled(itm, 1)
                for itm in self.vcsAddMultiMenuItems:
                    menu.setItemEnabled(itm, 0)
                for itm in self.multiMenuItems:
                    menu.setItemEnabled(itm, 0)
            else:
                for itm in self.vcsMultiMenuItems:
                    menu.setItemEnabled(itm, 0)
                for itm in self.vcsAddMultiMenuItems:
                    menu.setItemEnabled(itm, 1)
                for itm in self.multiMenuItems:
                    menu.setItemEnabled(itm, 1)
        
    def handleShowPopupMenuDir(self, menu):
        """
        Slot called before the context menu is shown. 
        
        It enables/disables the VCS menu entries depending on the overall 
        VCS status and the directory status.
        
        @param menu reference to the menu to be shown
        """
        if self.project.vcs is None:
            for itm in self.dirMenuItems:
                menu.setItemEnabled(itm, 1)
        else:
            if unicode(self.currentItem().text(1)) == self.project.vcs.vcsName():
                for itm in self.vcsDirMenuItems:
                    menu.setItemEnabled(itm, 1)
                for itm in self.vcsAddDirMenuItems:
                    menu.setItemEnabled(itm, 0)
                for itm in self.dirMenuItems:
                    menu.setItemEnabled(itm, 0)
            else:
                for itm in self.vcsDirMenuItems:
                    menu.setItemEnabled(itm, 0)
                for itm in self.vcsAddDirMenuItems:
                    menu.setItemEnabled(itm, 1)
                for itm in self.dirMenuItems:
                    menu.setItemEnabled(itm, 1)
        
    def handleShowPopupMenuDirMulti(self, menu):
        """
        Slot called before the context menu is shown. 
        
        It enables/disables the VCS menu entries depending on the overall 
        VCS status and the directory status.
        
        @param menu reference to the menu to be shown
        """
        if self.project.vcs is None:
            for itm in self.dirMultiMenuItems:
                menu.setItemEnabled(itm, 1)
        else:
            vcsName = self.project.vcs.vcsName()
            items = self.getSelectedItems()
            vcsItems = 0
            # determine number of selected items under VCS control
            for itm in items:
                if unicode(itm.text(1)) == vcsName:
                    vcsItems += 1
            
            if vcsItems > 0:
                if vcsItems != len(items):
                    for itm in self.vcsDirMultiMenuItems:
                        menu.setItemEnabled(itm, 0)
                else:
                    for itm in self.vcsDirMultiMenuItems:
                        menu.setItemEnabled(itm, 1)
                for itm in self.vcsAddDirMultiMenuItems:
                    menu.setItemEnabled(itm, 0)
                for itm in self.dirMultiMenuItems:
                    menu.setItemEnabled(itm, 0)
            else:
                for itm in self.vcsDirMultiMenuItems:
                    menu.setItemEnabled(itm, 0)
                for itm in self.vcsAddDirMultiMenuItems:
                    menu.setItemEnabled(itm, 1)
                for itm in self.dirMultiMenuItems:
                    menu.setItemEnabled(itm, 1)
        
    def selectEntries(self, local=1, filter=None):
        """
        Private method to select entries based on their VCS status.
        
        @param local flag indicating local (i.e. non VCS controlled) file/directory
            entries should be selected (boolean)
        @param filter list of class to check against
        """
        if self.project.vcs is None:
            return
        
        if local:
            compareString = self.trUtf8("local")
        else:
            compareString = QString(self.project.vcs.vcsName())
        
        qApp.setOverrideCursor(Qt.waitCursor)
        qApp.processEvents()
        
        # expand all directories in order to iterate over all entries
        self.handleExpandAllDirs()
        
        # now iterate over all entries
        selectedEntries = 0
        itm = self.firstChild()
        while itm:
            if self.wantedItem(itm, filter) and \
               QString.compare(compareString, itm.text(1)) == 0:
                itm.setSelected(1)
                selectedEntries += 1
            else:
                itm.setSelected(0)
            itm.repaint()
            itm = itm.itemBelow()
        self.update()
        qApp.restoreOverrideCursor()
        if selectedEntries == 0:
            KQMessageBox.information(None,
            self.trUtf8("Select entries"),
            self.trUtf8("""There were no matching entries found."""),
            self.trUtf8("&OK"),
            None,
            None,
            0, -1)
    
    def handleSelectLocalEntries(self):
        """
        Private slot to handle the select local files context menu entries
        """
        self.selectEntries(local=1, filter=[BrowserFile])
    
    def handleSelectVCSEntries(self):
        """
        Private slot to handle the select VCS files context menu entries
        """
        self.selectEntries(local=0, filter=[BrowserFile])
    
    def handleSelectLocalDirEntries(self):
        """
        Private slot to handle the select local directories context menu entries
        """
        self.selectEntries(local=1,
                           filter=[ProjectBrowserSimpleDir, ProjectBrowserDirectory])
    
    def handleSelectVCSDirEntries(self):
        """
        Private slot to handle the select VCS directories context menu entries
        """
        self.selectEntries(local=0,
                           filter=[ProjectBrowserSimpleDir, ProjectBrowserDirectory])
    
    def handleVCSUpdate(self):
        """
        Private slot called by the context menu to update a file from the VCS repository.
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems()]
        self.project.vcs.vcsUpdate(names)
        
    def handleVCSCommit(self):
        """
        Private slot called by the context menu to commit the changes to the VCS repository.
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems()]
        if Preferences.getVCS("AutoSaveFiles"):
            vm = qApp.mainWidget().getViewManager()
            for name in names:
                vm.saveEditor(name)
        self.project.vcs.vcsCommit(names, '')
        
    def handleVCSAdd(self):
        """
        Private slot called by the context menu to add the selected file to the VCS repository.
        """
        items = self.getSelectedItems()
        names = [unicode(itm.fileName()) for itm in items]
        if not names:
            return
        if len(names) == 1:
            self.project.vcs.vcsAdd(names[0], os.path.isdir(names[0]))
        else:
            self.project.vcs.vcsAdd(names)
        for itm, fn in zip(items, names):
            self.updateVCSStatus(itm, fn)
        
    def handleVCSAddBinary(self):
        """
        Private slot called by the context menu.
        
        It is used to add the selected
        file/directory in binary mode to the VCS repository.
        """
        items = self.getSelectedItems()
        names = [unicode(itm.fileName()) for itm in items]
        if len(names) == 1:
            self.project.vcs.vcsAddBinary(names[0], os.path.isdir(names[0]))
        else:
            self.project.vcs.vcsAddBinary(names)
        for itm, fn in zip(items, names):
            self.updateVCSStatus(itm, fn)
        
    def handleVCSAddTree(self):
        """
        Private slot called by the context menu.
        
        It is used to add the selected
        directory tree to the VCS repository.
        """
        items = self.getSelectedItems()
        names = [unicode(itm.fileName()) for itm in items]
        self.project.vcs.vcsAddTree(names)
        for itm, fn in zip(items, names):
            self.updateVCSStatus(itm, fn)
            opened = itm.isOpen()
            itm.setOpen(0)
            qApp.processEvents()
            itm.setOpen(opened)
            node = self.findParentNode(fn.replace(self.project.ppath+os.sep, ''))[0]
            while node != self:
                fn = unicode(node.fileName())
                self.updateVCSStatus(node, fn)
                node = self.findParentNode(fn.replace(self.project.ppath+os.sep, ''))[0]
        
    def handleVCSRemove(self):
        """
        Private slot called by the context menu to remove the selected file from the VCS repository.
        """
        items = self.getSelectedItems()
        isRemoveDirs = len(items) == self.getSelectedItemsCount([ProjectBrowserSimpleDir])
        names = [unicode(itm.fileName()) for itm in items]
        files = [unicode(itm.fileName()).replace(self.project.ppath+os.sep, '') for itm in items]
        
        dlg = DeleteFilesConfirmationDialog(self.parent(),
            self.trUtf8("Remove from repository (and disk)"),
            self.trUtf8("Do you really want to remove these files from the repository (and disk)?"),
            self.trUtf8("&Yes"), self.trUtf8("&No"), files)
        
        if dlg.exec_loop() == QDialog.Accepted:
            status = self.project.vcs.vcsRemove(names)
            if status:
                if isRemoveDirs:
                    self.handleRemoveDir() # remove directories from Project
                else:
                    self.handleRemove() # remove file(s) from project
        
    def handleVCSLog(self):
        """
        Private slot called by the context menu to show the VCS log of a file.
        """
        itm = self.currentItem()
        fn = unicode(itm.fileName())
        self.project.vcs.vcsLog(fn)
        
    def handleVCSDiff(self):
        """
        Private slot called by the context menu to show the difference of a file to the repository.
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems()]
        self.project.vcs.vcsDiff(names)
        
    def handleVCSHistory(self):
        """
        Private slot called by the context menu to show the history of a file.
        """
        itm = self.currentItem()
        fn = unicode(itm.fileName())
        self.project.vcs.vcsHistory(fn)
        
    def handleVCSStatus(self):
        """
        Private slot called by the context menu to show the status of a file.
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems()]
        self.project.vcs.vcsStatus(names)

    def handleVCSRevert(self):
        """
        Private slot called by the context menu to revert changes made to a file.
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems()]
        self.project.vcs.vcsRevert(names)

    def handleVCSMerge(self):
        """
        Private slot called by the context menu to merge changes into to a file.
        """
        itm = self.currentItem()
        fn = unicode(itm.fileName())
        self.project.vcs.vcsMerge(fn)

    ############################################################################
    ## CVS specific methods below
    ############################################################################
    
    def handleCVSEdit(self):
        """
        Private slot called by the context menu to edit a file (CVS).
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems()]
        self.project.vcs.cvsEdit(names)
        
    def handleCVSUnedit(self):
        """
        Private slot called by the context menu to unedit a file (CVS).
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems()]
        self.project.vcs.cvsUnedit(names)
        
    ############################################################################
    ## Subversion specific methods below
    ############################################################################
    
    def handleSVNCopy(self):
        """
        Private slot called by the context menu to copy the selected file (Subversion).
        """
        itm = self.currentItem()
        fn = unicode(itm.fileName())
        self.project.vcs.svnCopy(fn, self.project)
        
    def handleSVNMove(self):
        """
        Private slot called by the context menu to move the selected file (Subversion).
        """
        itm = self.currentItem()
        fn = unicode(itm.fileName())
        movefiles = self.project.getFiles(fn)
        if self.project.vcs.svnMove(fn, self.project):
            if os.path.isfile(fn):
                self.removeNode(itm)
                try:
                    self.children.remove(itm)
                except ValueError:
                    pass
                del itm
                self.emit(PYSIGNAL('closeSourceWindow'), (fn,))
            else:
                for mf in movefiles:
                    self.emit(PYSIGNAL('closeSourceWindow'), (mf,))
        
    def handleSVNResolve(self):
        """
        Private slot called by the context menu to resolve conflicts of a file.
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems()]
        self.project.vcs.svnResolve(names)
        
    def handleSVNListProps(self):
        """
        Private slot called by the context menu to list the subversion properties of a file.
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems()]
        self.project.vcs.svnListProps(names)
        
    def handleSVNSetProp(self):
        """
        Private slot called by the context menu to set a subversion property of a file.
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems()]
        self.project.vcs.svnSetProp(names)
        
    def handleSVNDelProp(self):
        """
        Private slot called by the context menu to delete a subversion property of a file.
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems()]
        self.project.vcs.svnDelProp(names)
        
    def handleSVNExtendedDiff(self):
        """
        Private slot called by the context menu to show the difference of a file to the repository.
        
        This gives the chance to enter the revisions to compare.
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems()]
        self.project.vcs.svnExtendedDiff(names)

class ProjectSourcesBrowser(PBrowser):
    """
    A class used to display the Sources part of the project. 
    
    Via the context menu that
    is displayed by a right click the user can select various actions on
    the selected file.
    
    @signal closeSourceWindow(string) emitted after a file has been removed/deleted 
            from the project
    """
    def __init__(self,project,parent=None):
        """
        Constructor
        
        @param project reference to the project object
        @param parent parent widget of this browser (QWidget)
        """
        PBrowser.__init__(self,project,"SOURCES",parent)
    
        self.selectedItemsFilter = [BrowserFile, ProjectBrowserSimpleDir]
        
        self.setCaption(self.trUtf8('Sources'))

        QWhatsThis.add(self,self.trUtf8(
            """<b>Project Sources Browser</b>"""
            """<p>This allows to easily see all sources contained in the current"""
            """ project. Several actions can be executed via the context menu.</p>"""
        ))
        
    def createPopupMenus(self):
        """
        Private overloaded method to generate the popup menu.
        """
        PBrowser.createPopupMenus(self)
        self.pyMenuIds = {}
        
        if self.project.pdata["PROGLANGUAGE"][0] == "Python":
            self.createPythonPopupMenus()
        elif self.project.pdata["PROGLANGUAGE"][0] == "Ruby":
            self.createRubyPopupMenus()
        
    def createPythonPopupMenus(self):
        """
        Privat method to generate the popup menus for a Python project.
        """
        self.checksMenu = QPopupMenu()
        self.checksMenu.insertItem(self.trUtf8('Syntax...'), self.handleSyntaxCheck)
        self.checksMenu.insertItem(self.trUtf8('Indentations...'), self.handleTabnanny)
        
        self.showMenu = QPopupMenu()
        self.showMenu.insertItem(self.trUtf8('Code metrics...'), self.handleCodeMetrics)
        self.coverageMenuItem = self.showMenu.insertItem(\
            self.trUtf8('Code coverage...'), self.handleCodeCoverage)
        self.profileMenuItem = self.showMenu.insertItem(\
            self.trUtf8('Profile data...'), self.handleProfileData)
        self.cyclopsMenuItem = self.showMenu.insertItem(\
            self.trUtf8('Cyclops report...'), self.handleCyclopsReport)
        self.removeCyclopsMenuItem = self.showMenu.insertItem(\
            self.trUtf8('Remove cyclops report'), self.handleRemoveCyclopsReport)
        self.connect(self.showMenu,SIGNAL('aboutToShow()'),self.handleShowShowMenu)
        
        self.graphicsMenu = QPopupMenu()
        self.classDiagramItem = self.graphicsMenu.insertItem(\
            self.trUtf8("Class Diagram..."), self.handleClassDiagram)
        self.graphicsMenu.insertItem(\
            self.trUtf8("Package Diagram..."), self.handlePackageDiagram)
        self.importsDiagramItem = self.graphicsMenu.insertItem(\
            self.trUtf8("Imports Diagram..."), self.handleImportsDiagram)
        self.graphicsMenu.insertItem(\
            self.trUtf8("Application Diagram..."), self.handleApplicationDiagram)
        
        self.unittestItem = self.pyMenu.insertItem(\
            self.trUtf8('Run unittest...'), self.handleUnittest)
        self.pyMenu.insertSeparator()
        itm = self.pyMenu.insertItem(self.trUtf8('Rename file'), self.handleRename)
        self.menuItems.append(itm)
        itm = self.pyMenu.insertItem(self.trUtf8('Remove from project'), self.handleRemove)
        self.menuItems.append(itm)
        itm = self.pyMenu.insertItem(self.trUtf8('Delete'), self.handleDelete)
        self.menuItems.append(itm)
        self.pyMenu.insertSeparator()
        self.pyMenu.insertItem(self.trUtf8('Add source file...'), self.project.addPyFile)
        self.pyMenu.insertItem(self.trUtf8('Add source directory...'), self.project.addPyDir)
        self.pyMenu.insertSeparator()
        self.pyMenu.insertItem(self.trUtf8('Diagrams'), self.graphicsMenu)
        self.pyMenu.insertSeparator()
        self.pyMenuIds["Check"] = \
            self.pyMenu.insertItem(self.trUtf8('Check'), self.checksMenu)
        self.pyMenu.insertSeparator()
        self.pyMenuIds["Show"] = \
            self.pyMenu.insertItem(self.trUtf8('Show'), self.showMenu)
        self.pyMenu.insertSeparator()
        self.pyMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.pyMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)

        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8('Add source file...'), self.project.addPyFile)
        self.menu.insertItem(self.trUtf8('Add source directory...'), self.project.addPyDir)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.menu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)

        self.backMenu = QPopupMenu(self)
        self.backMenu.insertItem(self.trUtf8('Add source file...'), self.project.addPyFile)
        self.backMenu.insertItem(self.trUtf8('Add source directory...'), self.project.addPyDir)
        self.backMenu.insertSeparator()
        self.backMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.backMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        self.backMenu.setEnabled(0)
        
        self.multiMenu.insertSeparator()
        itm = self.multiMenu.insertItem(self.trUtf8('Remove from project'), self.handleRemove)
        self.multiMenuItems.append(itm)
        itm = self.multiMenu.insertItem(self.trUtf8('Delete'), self.handleDelete)
        self.multiMenuItems.append(itm)
        self.multiMenu.insertSeparator()
        self.multiMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.multiMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        
        self.dirMenu = QPopupMenu(self)
        itm = self.dirMenu.insertItem(self.trUtf8('Remove from project'), self.handleRemoveDir)
        self.dirMenuItems.append(itm)
        self.dirMenu.insertSeparator()
        self.dirMenu.insertItem(self.trUtf8('Diagrams'), self.graphicsMenu)        
        self.dirMenu.insertSeparator()
        self.dirMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.dirMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        
        self.dirMultiMenu = QPopupMenu(self)
        self.dirMultiMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.dirMultiMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        
        self.connect(self.pyMenu,SIGNAL('aboutToShow()'),self.handlePopupMenu)
        self.connect(self.multiMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuMulti)
        self.connect(self.dirMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuDir)
        self.connect(self.dirMultiMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuDirMulti)
        self.mainMenu = self.pyMenu
        
    def createRubyPopupMenus(self):
        """
        Privat method to generate the popup menus for a Ruby project.
        """
        self.graphicsMenu = QPopupMenu()
        self.classDiagramItem = self.graphicsMenu.insertItem(\
            self.trUtf8("Class Diagram..."), self.handleClassDiagram)
        self.graphicsMenu.insertItem(self.trUtf8("Package Diagram..."), self.handlePackageDiagram)
        self.graphicsMenu.insertItem(self.trUtf8("Application Diagram..."), self.handleApplicationDiagram)
        
        self.pyMenu.insertSeparator()
        itm = self.pyMenu.insertItem(self.trUtf8('Rename file'), self.handleRename)
        self.menuItems.append(itm)
        itm = self.pyMenu.insertItem(self.trUtf8('Remove from project'), self.handleRemove)
        self.menuItems.append(itm)
        itm = self.pyMenu.insertItem(self.trUtf8('Delete'), self.handleDelete)
        self.menuItems.append(itm)
        self.pyMenu.insertSeparator()
        self.pyMenu.insertItem(self.trUtf8('Add source file...'), self.project.addPyFile)
        self.pyMenu.insertItem(self.trUtf8('Add source directory...'), self.project.addPyDir)
        self.pyMenu.insertSeparator()
        self.pyMenu.insertItem(self.trUtf8('Diagrams'), self.graphicsMenu)
        self.pyMenu.insertSeparator()
        self.pyMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.pyMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)

        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8('Add source file...'), self.project.addPyFile)
        self.menu.insertItem(self.trUtf8('Add source directory...'), self.project.addPyDir)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.menu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)

        self.backMenu = QPopupMenu(self)
        self.backMenu.insertItem(self.trUtf8('Add source file...'), self.project.addPyFile)
        self.backMenu.insertItem(self.trUtf8('Add source directory...'), self.project.addPyDir)
        self.backMenu.insertSeparator()
        self.backMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.backMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        self.backMenu.setEnabled(0)
        
        self.multiMenu.insertSeparator()
        itm = self.multiMenu.insertItem(self.trUtf8('Remove from project'), self.handleRemove)
        self.multiMenuItems.append(itm)
        itm = self.multiMenu.insertItem(self.trUtf8('Delete'), self.handleDelete)
        self.multiMenuItems.append(itm)
        self.multiMenu.insertSeparator()
        self.multiMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.multiMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        
        self.dirMenu = QPopupMenu(self)
        itm = self.dirMenu.insertItem(self.trUtf8('Remove from project'), self.handleRemoveDir)
        self.dirMenuItems.append(itm)
        self.dirMenu.insertSeparator()
        self.dirMenu.insertItem(self.trUtf8('Diagrams'), self.graphicsMenu)        
        self.dirMenu.insertSeparator()
        self.dirMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.dirMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        
        self.dirMultiMenu = QPopupMenu(self)
        self.dirMultiMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.dirMultiMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        
        self.connect(self.pyMenu,SIGNAL('aboutToShow()'),self.handlePopupMenu)
        self.connect(self.multiMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuMulti)
        self.connect(self.dirMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuDir)
        self.connect(self.dirMultiMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuDirMulti)
        self.mainMenu = self.pyMenu
        
    def handleContextMenu(self,itm,coord,col):
        """
        Private slot to show the context menu of the listview.
        
        @param itm the selected listview item (QListViewItem)
        @param coord the position of the mouse pointer (QPoint)
        @param col the column of the mouse pointer (int)
        """
        try:
            cnt = self.getSelectedItemsCount([BrowserFile, BrowserClass, BrowserMethod, 
                                              ProjectBrowserSimpleDir])
            bfcnt = self.getSelectedItemsCount([BrowserFile])
            cmcnt = self.getSelectedItemsCount([BrowserClass, BrowserMethod])
            sdcnt = self.getSelectedItemsCount([ProjectBrowserSimpleDir])
            if cnt > 1 and cnt == bfcnt:
                self.multiMenu.popup(coord)
            elif cnt == 1 and (bfcnt == 1 or cmcnt == 1):
                if isinstance(itm, BrowserFile):
                    fn = itm.fileName()
                    if self.project.pdata["PROGLANGUAGE"][0] == "Python":
                        if fn.endswith('.py'):
                            for id in self.pyMenuIds.values():
                                self.pyMenu.setItemEnabled(id, 1)
                            self.graphicsMenu.setItemEnabled(self.classDiagramItem, 1)
                            self.graphicsMenu.setItemEnabled(self.importsDiagramItem, 1)
                            self.pyMenu.setItemEnabled(self.unittestItem, 1)
                        elif fn.endswith('.ptl'):
                            for id in self.pyMenuIds.values():
                                self.pyMenu.setItemEnabled(id, 0)
                            self.pyMenu.setItemEnabled(self.pyMenuIds["Check"], 1)
                            self.graphicsMenu.setItemEnabled(self.classDiagramItem, 1)
                            self.graphicsMenu.setItemEnabled(self.importsDiagramItem, 1)
                            self.pyMenu.setItemEnabled(self.unittestItem, 0)
                        elif fn.endswith('.rb'):    # entry for mixed mode programs
                            for id in self.pyMenuIds.values():
                                self.pyMenu.setItemEnabled(id, 0)
                            self.pyMenu.setItemEnabled(self.pyMenuIds["Check"], 1)
                            self.graphicsMenu.setItemEnabled(self.classDiagramItem, 1)
                            self.graphicsMenu.setItemEnabled(self.importsDiagramItem, 0)
                            self.pyMenu.setItemEnabled(self.unittestItem, 0)
                        else:
                            for id in self.pyMenuIds.values():
                                self.pyMenu.setItemEnabled(id, 0)
                            self.graphicsMenu.setItemEnabled(self.classDiagramItem, 0)
                            self.graphicsMenu.setItemEnabled(self.importsDiagramItem, 0)
                            self.pyMenu.setItemEnabled(self.unittestItem, 0)
                    self.pyMenu.popup(coord)
                elif isinstance(itm,BrowserClass) or \
                        isinstance(itm,BrowserMethod):
                    self.menu.popup(coord)
                else:
                    self.backMenu.popup(coord)
            elif cnt == 1 and sdcnt == 1:
                self.graphicsMenu.setItemEnabled(self.classDiagramItem, 0)
                self.dirMenu.popup(coord)
            elif cnt > 1 and cnt == sdcnt:
                self.dirMultiMenu.popup(coord)
            else:
                self.backMenu.popup(coord)
        except:
            pass
        
    def handlePopupMenu(self):
        """
        Private slot called by the pyMenu aboutToShow signal.
        """
        self.handleShowPopupMenu(self.pyMenu)
        
    def handlePopupMenuMulti(self):
        """
        Private slot called by the multiMenu aboutToShow signal.
        """
        self.handleShowPopupMenuMulti(self.multiMenu)
        
    def handlePopupMenuDir(self):
        """
        Private slot called by the dirMenu aboutToShow signal.
        """
        self.handleShowPopupMenuDir(self.dirMenu)
        
    def handlePopupMenuDirMulti(self):
        """
        Private slot called by the dirMultiMenu aboutToShow signal.
        """
        self.handleShowPopupMenuDirMulti(self.dirMultiMenu)
        
    def handleShowShowMenu(self):
        """
        Private slot called before the show menu is shown.
        """
        prEnable = 0
        coEnable = 0
        cyEnable = 0
        
        # first check if the file belongs to a project and there is
        # a project coverage file
        fn = self.project.getMainScript(1)
        if fn is not None:
            tfn = Utilities.getTestFileName(fn)
            basename = os.path.splitext(fn)[0]
            tbasename = os.path.splitext(tfn)[0]
            prEnable = prEnable or \
                os.path.isfile("%s.profile" % basename) or \
                os.path.isfile("%s.profile" % tbasename)
            coEnable = coEnable or \
                os.path.isfile("%s.coverage" % basename) or \
                os.path.isfile("%s.coverage" % tbasename)
            cyEnable = cyEnable or \
                os.path.isfile("%s.cycles.html" % basename) or \
                os.path.isfile("%s.cycles.html" % tbasename)
        
        # now check the selected item
        itm = self.currentItem()
        fn = unicode(itm.fileName())
        if fn is not None:
            basename = os.path.splitext(fn)[0]
            prEnable = prEnable or \
                os.path.isfile("%s.profile" % basename)
            coEnable = coEnable or \
                os.path.isfile("%s.coverage" % basename)
            cyEnable = cyEnable or \
                os.path.isfile("%s.cycles.html" % basename)
        
        self.showMenu.setItemEnabled(self.profileMenuItem, prEnable)
        self.showMenu.setItemEnabled(self.coverageMenuItem, coEnable)
        self.showMenu.setItemEnabled(self.cyclopsMenuItem, cyEnable)
        self.showMenu.setItemEnabled(self.removeCyclopsMenuItem, cyEnable)
        
    def handleRename(self):
        """
        Private method to rename a file of the project.
        """
        itm = self.currentItem()
        fn = unicode(itm.fileName())
        if self.project.renameFile(fn):
            self.removeNode(itm)
            self.children.remove(itm)
            del itm
            self.rebuildTree()
        
    def handleDelete(self):
        """
        Private method to delete a file from the project.
        """
        itmList = self.getSelectedItems()
        
        files = []
        fullNames = []
        for itm in itmList:
            fn2 = unicode(itm.fileName())
            fullNames.append(fn2)
            fn = fn2.replace(self.project.ppath+os.sep, '')
            files.append(fn)
        
        dlg = DeleteFilesConfirmationDialog(self.parent(),
            self.trUtf8("Delete files"),
            self.trUtf8("Do you really want to delete these files from the project?"),
            self.trUtf8("&Yes"), self.trUtf8("&No"), files)
        
        if dlg.exec_loop() == QDialog.Accepted:
            for itm, fn2, fn in zip(itmList[:], fullNames, files):
                self.emit(PYSIGNAL('closeSourceWindow'), (fn2,))
                if self.project.deleteFile(fn):
                    self.removeNode(itm)
                    self.children.remove(itm)
                    del itm
    
    def handleTabnanny(self):
        """
        Private method to handle the tabnanny context menu action.
        """
        itm = self.currentItem()
        fn = itm.fileName()
        
        self.tabnanny = TabnannyDialog(qApp.mainWidget().getViewManager())
        self.tabnanny.show()
        self.tabnanny.start(fn)
    
    def handleSyntaxCheck(self):
        """
        Private method to handle the syntax check context menu action.
        """
        itm = self.currentItem()
        fn = itm.fileName()
        
        self.syntaxcheck = SyntaxCheckerDialog(qApp.mainWidget().getViewManager())
        self.syntaxcheck.show()
        self.syntaxcheck.start(fn)
    
    def handleCodeMetrics(self):
        """
        Private method to handle the code metrics context menu action.
        """
        itm = self.currentItem()
        fn = itm.fileName()
        
        self.codemetrics = CodeMetricsDialog()
        self.codemetrics.show()
        self.codemetrics.start(fn)
    
    def handleCodeCoverage(self):
        """
        Private method to handle the code coverage context menu action.
        """
        itm = self.currentItem()
        fn = itm.fileName()
        pfn = self.project.getMainScript(1)
        
        files = []
        
        if pfn is not None:
            tpfn = Utilities.getTestFileName(pfn)
            basename = os.path.splitext(pfn)[0]
            tbasename = os.path.splitext(tpfn)[0]
            
            f = "%s.coverage" % basename
            tf = "%s.coverage" % tbasename
            if os.path.isfile(f):
                files.append(f)
            if os.path.isfile(tf):
                files.append(tf)
        
        if fn is not None:
            tfn = Utilities.getTestFileName(fn)
            basename = os.path.splitext(fn)[0]
            tbasename = os.path.splitext(tfn)[0]
            
            f = "%s.coverage" % basename
            tf = "%s.coverage" % tbasename
            if os.path.isfile(f) and not f in files:
                files.append(f)
            if os.path.isfile(tf) and not tf in files:
                files.append(tf)
                
        if files:
            if len(files) > 1:
                filelist = QStringList()
                for f in files:
                    filelist.append(f)
                pfn, ok = KQInputDialog.getItem(\
                    self.trUtf8("Code Coverage"),
                    self.trUtf8("Please select a coverage file"),
                    filelist,
                    0, 0)
                if not ok:
                    return
                pfn = unicode(pfn)
            else:
                pfn = files[0]
        else:
            return
            
        self.codecoverage = PyCoverageDialog()
        self.codecoverage.show()
        self.codecoverage.start(pfn, fn)
    
    def handleProfileData(self):
        """
        Private method to handle the show profile data context menu action.
        """
        itm = self.currentItem()
        fn = itm.fileName()
        pfn = self.project.getMainScript(1)
        
        files = []
        
        if pfn is not None:
            tpfn = Utilities.getTestFileName(pfn)
            basename = os.path.splitext(pfn)[0]
            tbasename = os.path.splitext(tpfn)[0]
            
            f = "%s.profile" % basename
            tf = "%s.profile" % tbasename
            if os.path.isfile(f):
                files.append(f)
            if os.path.isfile(tf):
                files.append(tf)
        
        if fn is not None:
            tfn = Utilities.getTestFileName(fn)
            basename = os.path.splitext(fn)[0]
            tbasename = os.path.splitext(tfn)[0]
            
            f = "%s.profile" % basename
            tf = "%s.profile" % tbasename
            if os.path.isfile(f) and not f in files:
                files.append(f)
            if os.path.isfile(tf) and not tf in files:
                files.append(tf)
                
        if files:
            if len(files) > 1:
                filelist = QStringList()
                for f in files:
                    filelist.append(f)
                pfn, ok = KQInputDialog.getItem(\
                    self.trUtf8("Profile Data"),
                    self.trUtf8("Please select a profile file"),
                    filelist,
                    0, 0)
                if not ok:
                    return
                pfn = unicode(pfn)
            else:
                pfn = files[0]
        else:
            return
            
        self.profiledata = PyProfileDialog()
        self.profiledata.show()
        self.profiledata.start(pfn, fn)
        
    def handleCyclopsReport(self):
        """
        Private method to handle the show cyclops report context menu action.
        """
        itm = self.currentItem()
        fn = itm.fileName()
        pfn = self.project.getMainScript(1)
        
        files = []
        
        if pfn is not None:
            tpfn = Utilities.getTestFileName(pfn)
            basename = os.path.splitext(pfn)[0]
            tbasename = os.path.splitext(tpfn)[0]
            
            f = "%s.cycles.html" % basename
            tf = "%s.cycles.html" % tbasename
            if os.path.isfile(f):
                files.append(f)
            if os.path.isfile(tf):
                files.append(tf)
        
        if fn is not None:
            tfn = Utilities.getTestFileName(fn)
            basename = os.path.splitext(fn)[0]
            tbasename = os.path.splitext(tfn)[0]
            
            f = "%s.cycles.html" % basename
            tf = "%s.cycles.html" % tbasename
            if os.path.isfile(f) and not f in files:
                files.append(f)
            if os.path.isfile(tf) and not tf in files:
                files.append(tf)
                
        if files:
            if len(files) > 1:
                filelist = QStringList()
                for f in files:
                    filelist.append(f)
                pfn, ok = KQInputDialog.getItem(\
                    self.trUtf8("Cyclops Report"),
                    self.trUtf8("Please select a Cyclops report file"),
                    filelist,
                    0, 0)
                if not ok:
                    return
                pfn = unicode(pfn)
            else:
                pfn = files[0]
        else:
            return
            
        qApp.mainWidget().launchHelpViewer(pfn)
        
    def handleRemoveCyclopsReport(self):
        """
        Private method to handle the Remove Cyclops report context menu action.
        """
        itm = self.currentItem()
        fn = itm.fileName()
        pfn = self.project.getMainScript(1)
        
        files = []
        
        if pfn is not None:
            tpfn = Utilities.getTestFileName(pfn)
            basename = os.path.splitext(pfn)[0]
            tbasename = os.path.splitext(tpfn)[0]
            
            f = "%s.cycles.html" % basename
            tf = "%s.cycles.html" % tbasename
            if os.path.isfile(f):
                files.append(f)
            if os.path.isfile(tf):
                files.append(tf)
        
        if fn is not None:
            tfn = Utilities.getTestFileName(fn)
            basename = os.path.splitext(fn)[0]
            tbasename = os.path.splitext(tfn)[0]
            
            f = "%s.cycles.html" % basename
            tf = "%s.cycles.html" % tbasename
            if os.path.isfile(f) and not f in files:
                files.append(f)
            if os.path.isfile(tf) and not tf in files:
                files.append(tf)
                
        if files:
            if len(files) > 1:
                filelist = QStringList()
                for f in files:
                    filelist.append(f)
                pfn, ok = KQInputDialog.getItem(\
                    self.trUtf8("Remove Cyclops Report"),
                    self.trUtf8("Please select a Cyclops report file to be removed"),
                    filelist,
                    0, 0)
                if not ok:
                    return
                pfn = unicode(pfn)
            else:
                pfn = files[0]
        else:
            return
            
        if os.path.exists(pfn):
            os.remove(pfn)
        
    def handleClassDiagram(self):
        """
        Private method to handle the class diagram context menu action.
        """
        itm = self.currentItem()
        fn = itm.fileName()
        res = KQMessageBox.question(None,
            self.trUtf8("Class Diagram"),
            self.trUtf8("""Include class attributes?"""),
            self.trUtf8("&Yes"),
            self.trUtf8("&No"),
            None,
            0, -1)
        dlg = UMLClassDiagram(fn, self, noAttrs = res)
        dlg.show()
        
    def handleImportsDiagram(self):
        """
        Private method to handle the imports diagram context menu action.
        """
        itm = self.currentItem()
        fn = itm.fileName()
        package = os.path.isdir(fn) and fn or os.path.dirname(fn)
        dlg = ImportsDiagram(package, self)
        dlg.show()
        
    def handlePackageDiagram(self):
        """
        Private method to handle the package diagram context menu action.
        """
        itm = self.currentItem()
        fn = itm.fileName()
        package = os.path.isdir(fn) and fn or os.path.dirname(fn)
        res = KQMessageBox.question(None,
            self.trUtf8("Package Diagram"),
            self.trUtf8("""Include class attributes?"""),
            self.trUtf8("&Yes"),
            self.trUtf8("&No"),
            None,
            0, -1)
        dlg = PackageDiagram(package, self, noAttrs = res)
        dlg.show()
        
    def handleApplicationDiagram(self):
        """
        Private method to handle the application diagram context menu action.
        """
        res = KQMessageBox.question(None,
            self.trUtf8("Application Diagram"),
            self.trUtf8("""Include module names?"""),
            self.trUtf8("&Yes"),
            self.trUtf8("&No"),
            None,
            0, -1)
        dlg = ApplicationDiagram(self.project, self, noModules = res)
        dlg.show()
        
    def handleProjectSourceAdded(self, fn):
        """
        Private slot to handle the projectSourceAdded signal.
        
        @param fn filename of the file that was added (string)
        """
        fname = os.path.join(self.project.ppath, fn)
        parent, dt = self.findParentNode(fn)
        node = BrowserFile(parent, fname, None, 1, dt, 
            isPyFile = self.project.pdata["PROGLANGUAGE"][0] == "Python")
        self.children.append(node)
        self.nodeAdded(node, fname)
        
    def handleProjectClosed(self):
        """
        Private slot to handle the projectClosed signal.
        """
        PBrowser.handleProjectClosed(self)
        
        # reset the module parser cache
        resetParsedModules()
        
        # reset the UMLClassDiagram cache
        resetCachedWidgets()
        
    def handleEditorSaved(self, fn):
        """
        Public slot to handle the editorSaved signal.
        
        @param fn filename of the file that was saved
        """
        if not self.projectOpened:
            return
            
        list = QStringList.split(QRegExp(r'/|\\'), fn)
        newfn = QString(list[-1])
        itm = None
        for l in list:
            itm = self.findItem(l, 0, itm)
            
        if itm is not None:
            if itm.isOpen():
                # make it reread the info by closing it and reopen it
                openItems = self.getOpenChildren(itm)
                itm.setOpen(0)
                qApp.processEvents()
                itm.setOpen(1)
                if len(openItems):
                    for oi in openItems:
                        self.setOpenChildren(itm, oi)
                        
        # reset the module parser cache for this file
        resetParsedModule(unicode(fn))
        
        # reset cached widgets of UMLClassDiagram for this file
        resetCachedWidgetsByFile(unicode(fn))
        
    def getOpenChildren(self, itm):
        """
        Private method to get a list of open siblings of QListViewItem itm.
        
        @return list of open siblings
        """
        olist = []
        child = itm.firstChild()
        while child is not None:
            if child.isOpen():
                olist.append(child.text(0))
                if child.childCount():
                    olist += self.getOpenChildren(child)
            child = child.nextSibling()
        return olist
        
    def setOpenChildren(self, itm, childstring):
        """
        Private method to find a child of a node and open it.
        
        @param itm the node to check
        @param childstring displaytext to search for (QString)
        @return flag indicating success
        """
        child = itm.firstChild()
        while child is not None:
            if child.isOpen() and child.childCount():
                s = self.setOpenChildren(child, childstring)
                if s:
                    return 1
            if child.text(0).compare(childstring) == 0:
                child.setOpen(1)
                return 1
            child = child.nextSibling()
                
        return 0
        
class ProjectFormsBrowser(PBrowser):
    """
    A class used to display the forms part of the project. 
    
    Via the context menu that
    is displayed by a right click the user can select various actions on
    the selected file.
    
    @signal appendStdout(string) emitted after something was received from
            a QProcess on stdout
    @signal appendStderr(string) emitted after something was received from
            a QProcess on stderr
    @signal projectSourceAdded(string) emitted after a compile finished successfully
    @signal pythonFile(string) emitted to open a forms file in an editor
    @signal uipreview(string) emitted to preview a forms file
    @signal trpreview(string list) emitted to preview form files in the 
            translations previewer
    @signal closeSourceWindow(string) emitted after a file has been removed/deleted 
            from the project
    """
    def __init__(self,project,qtdir,parent=None):
        """
        Constructor
        
        @param project reference to the project object
        @param qtdir path of the Qt installation directory (string)
        @param parent parent widget of this browser (QWidget)
        """
        self.qtdir = qtdir
        PBrowser.__init__(self,project,"FORMS",parent)
    
        self.selectedItemsFilter = [BrowserFile, ProjectBrowserSimpleDir]
        
        self.setCaption(self.trUtf8('Forms'))
        
        QWhatsThis.add(self,self.trUtf8(
            """<b>Project Forms Browser</b>"""
            """<p>This allows to easily see all forms contained in the current"""
            """ project. Several actions can be executed via the context menu.</p>"""
        ))
        
        # these two lists have to stay in sync
        self.templates = ['dialog.tmpl', 'wizard.tmpl', 'widget.tmpl', \
            'configurationdialog.tmpl', 'dialogbuttonsbottom.tmpl', \
            'dialogbuttonsright.tmpl', 'tabdialog.tmpl']
        self.templateTypes = [ \
            unicode(self.trUtf8("Dialog")),
            unicode(self.trUtf8("Wizard")),
            unicode(self.trUtf8("Widget")),
            unicode(self.trUtf8("Configuration Dialog")),
            unicode(self.trUtf8("Dialog with Buttons (Bottom)")),
            unicode(self.trUtf8("Dialog with Buttons (Right)")),
            unicode(self.trUtf8("Tab Dialog"))
        ]
        self.formTypeList = QStringList()
        for tType in self.templateTypes:
            self.formTypeList.append(tType)
        
    def createPopupMenus(self):
        """
        Private overloaded method to generate the popup menu.
        """
        self.menuItems = []
        self.formItems = []
        self.multiMenuItems = []
        self.multiFormItems = []
        self.dirMenuItems = []
        self.dirMultiMenuItems = []
        
        self.menu = QPopupMenu(self)
        if self.qtdir is not None:
            itm = self.menu.insertItem(self.trUtf8('Compile form'), self.handleCompile)
            self.formItems.append(itm)
            itm = self.menu.insertItem(self.trUtf8('Compile all forms'), self.handleCompileAll)
            self.formItems.append(itm)
            itm = self.menu.insertItem(self.trUtf8('Generate Subclass'), self.handleSubclass)
            self.formItems.append(itm)
            self.menu.insertSeparator()
            itm = self.menu.insertItem(self.trUtf8('Open in Qt-Designer'), self.handleOpen)
            self.formItems.append(itm)
        self.menu.insertItem(self.trUtf8('Open in Editor'), self.handleOpenEditor)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8('Preview form'), self.handleUIPreview)
        self.menu.insertItem(self.trUtf8('Preview translations'), self.handleTRPreview)
        self.menu.insertSeparator()
        itm = self.menu.insertItem(self.trUtf8('Rename file'), self.handleRename)
        self.menuItems.append(itm)
        itm = self.menu.insertItem(self.trUtf8('Remove from project'), self.handleRemove)
        self.menuItems.append(itm)
        itm = self.menu.insertItem(self.trUtf8('Delete'), self.handleDelete)
        self.menuItems.append(itm)
        self.menu.insertSeparator()
        if self.qtdir is not None:
            self.menu.insertItem(self.trUtf8('New form...'), self.handleNewForm)
        self.menu.insertItem(self.trUtf8('Add form...'), self.project.addUiFile)
        self.menu.insertItem(self.trUtf8('Add forms directory...'), self.project.addUiDir)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.menu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)

        self.backMenu = QPopupMenu(self)
        if self.qtdir is not None:
            self.backMenu.insertItem(self.trUtf8('Compile all forms'), self.handleCompileAll)
            self.backMenu.insertSeparator()
            self.backMenu.insertItem(self.trUtf8('New form...'), self.handleNewForm)
        self.backMenu.insertItem(self.trUtf8('Add form...'), self.project.addUiFile)
        self.backMenu.insertItem(self.trUtf8('Add forms directory...'), self.project.addUiDir)
        self.backMenu.insertSeparator()
        self.backMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.backMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        self.backMenu.setEnabled(0)

        # create the menu for multiple selected files
        self.multiMenu = QPopupMenu(self)
        if self.qtdir is not None:
            itm = self.multiMenu.insertItem(self.trUtf8('Compile forms'), self.handleCompileSelected)
            self.multiMenu.insertSeparator()
            itm = self.multiMenu.insertItem(self.trUtf8('Open in Qt-Designer'), self.handleOpen)
            self.multiFormItems.append(itm)
        self.multiMenu.insertItem(self.trUtf8('Open in Editor'), self.handleOpenEditor)
        self.multiMenu.insertSeparator()
        self.multiMenu.insertItem(self.trUtf8('Preview translations'), self.handleTRPreview)
        self.multiMenu.insertSeparator()
        itm = self.multiMenu.insertItem(self.trUtf8('Remove from project'), self.handleRemove)
        self.multiMenuItems.append(itm)
        itm = self.multiMenu.insertItem(self.trUtf8('Delete'), self.handleDelete)
        self.multiMenuItems.append(itm)
        self.multiMenu.insertSeparator()
        self.multiMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.multiMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)

        self.dirMenu = QPopupMenu(self)
        if self.qtdir is not None:
            self.dirMenu.insertItem(self.trUtf8('Compile all forms'), self.handleCompileAll)
            self.dirMenu.insertSeparator()
        itm = self.dirMenu.insertItem(self.trUtf8('Remove from project'), self.handleRemoveDir)
        self.dirMenuItems.append(itm)
        self.dirMenu.insertSeparator()
        if self.qtdir is not None:
            self.dirMenu.insertItem(self.trUtf8('New form...'), self.handleNewForm)
        self.dirMenu.insertItem(self.trUtf8('Add form...'), self.project.addUiFile)
        self.dirMenu.insertItem(self.trUtf8('Add forms directory...'), self.project.addUiDir)
        self.dirMenu.insertSeparator()
        self.dirMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.dirMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        
        self.dirMultiMenu = None
        self.dirMultiMenu = QPopupMenu(self)
        if self.qtdir is not None:
            self.dirMultiMenu.insertItem(self.trUtf8('Compile all forms'), self.handleCompileAll)
            self.dirMultiMenu.insertSeparator()
        self.dirMultiMenu.insertItem(self.trUtf8('Add form...'), self.project.addUiFile)
        self.dirMultiMenu.insertItem(self.trUtf8('Add forms directory...'), self.project.addUiDir)
        self.dirMultiMenu.insertSeparator()
        self.dirMultiMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.dirMultiMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        
        self.connect(self.menu,SIGNAL('aboutToShow()'),self.handlePopupMenu)
        self.connect(self.multiMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuMulti)
        self.connect(self.dirMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuDir)
        self.connect(self.dirMultiMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuDirMulti)
        self.mainMenu = self.menu
        
    def handleContextMenu(self,itm,coord,col):
        """
        Private slot to show the context menu of the listview.
        
        @param itm the selected listview item (QListViewItem)
        @param coord the position of the mouse pointer (QPoint)
        @param col the column of the mouse pointer (int)
        """
        try:
            cnt = self.getSelectedItemsCount([BrowserFile, ProjectBrowserSimpleDir])
            bfcnt = self.getSelectedItemsCount([BrowserFile])
            sdcnt = self.getSelectedItemsCount([ProjectBrowserSimpleDir])
            if cnt > 1 and cnt == bfcnt:
                self.multiMenu.popup(coord)
            elif cnt == 1 and bfcnt == 1:
                self.menu.popup(coord)
            elif cnt == 1 and sdcnt == 1:
                self.dirMenu.popup(coord)
            elif cnt > 1 and cnt == sdcnt:
                self.dirMultiMenu.popup(coord)
            else:
                self.backMenu.popup(coord)
        except:
            pass
        
    def handlePopupMenu(self):
        """
        Private slot called by the pyMenu aboutToShow signal.
        """
        if self.currentItem().isDesignerHeaderFile():
            enable = 0
        else:
            enable = 1
        for itm in self.formItems:
            self.menu.setItemEnabled(itm, enable)
        self.handleShowPopupMenu(self.menu)
        
    def handlePopupMenuMulti(self):
        """
        Private slot called by the multiMenu aboutToShow signal.
        """
        items =self.getSelectedItems()
        if self.__itemsHaveDesignerHeaderFiles(items):
            enable = 0
        else:
            enable = 1
        for itm in self.multiFormItems:
            self.multiMenu.setItemEnabled(itm, enable)
        self.handleShowPopupMenuMulti(self.multiMenu)
        
    def handlePopupMenuDir(self):
        """
        Private slot called by the dirMenu aboutToShow signal.
        """
        self.handleShowPopupMenuDir(self.dirMenu)
        
    def handlePopupMenuDirMulti(self):
        """
        Private slot called by the dirMultiMenu aboutToShow signal.
        """
        self.handleShowPopupMenuDirMulti(self.dirMultiMenu)
        
    def handleOpenEditor(self):
        """
        Private slot to handle the Open in Editor menu action.
        """
        itmList = self.getSelectedItems()
        for itm in itmList[:]:
            self.emit(PYSIGNAL('pythonFile'),(itm.fileName(),))
        
    def handleUIPreview(self):
        """
        Private slot to handle the Preview menu action.
        """
        itmList = self.getSelectedItems()
        self.emit(PYSIGNAL('uipreview'),(itmList[0].fileName(),))
    
    def handleTRPreview(self):
        """
        Private slot to handle the Preview translations action.
        """
        fileNames = []
        for itm in self.getSelectedItems():
            fileNames.append(itm.fileName())
        self.emit(PYSIGNAL('trpreview'),(fileNames,))
    
    def handleNewForm(self):
        """
        Private slot to handle the New Form menu action.
        """
        itm = self.currentItem()
        if itm is None:
            path = self.project.ppath
        else:
            try:
                path = os.path.dirname(unicode(itm.fileName()))
            except AttributeError:
                path = os.path.join(self.project.ppath, unicode(itm.text(0)))
            
        selectedForm, ok = KQInputDialog.getItem(\
            self.trUtf8("New Form"),
            self.trUtf8("Select a form type:"),
            self.formTypeList,
            0, 0)
        if not ok:
            # user pressed cancel
            return
            
        templateIndex = self.templateTypes.index(unicode(selectedForm))
        templateFile = os.path.join(getConfig('ericTemplatesDir'),
            self.templates[templateIndex])
            
        selectedFilter = QString('')
        fname = KQFileDialog.getSaveFileName(path,
            self.trUtf8("Forms File (*.ui);;All Files (*)"),
            self, None, self.trUtf8("New Form"), selectedFilter, 0)
            
        if fname.isEmpty():
            # user aborted or didn't enter a filename
            return
            
        ext = QFileInfo(fname).extension()
        if ext.isEmpty():
            ex = selectedFilter.section('(*',1,1).section(')',0,0)
            if not ex.isEmpty():
                fname.append(ex)
        
        fname = unicode(fname)
        if os.path.exists(fname):
            res = KQMessageBox.warning(self,
                self.trUtf8("New Form"),
                self.trUtf8("The file already exists!"),
                self.trUtf8("&Overwrite"),
                self.trUtf8("&Abort"),
                None, 1, 1)
            if res == 1:
                # user selected to abort the operation
                return
                
        try:
            shutil.copy(templateFile, fname)
        except IOError, e:
            KQMessageBox.critical(self,
                self.trUtf8("New Form"),
                self.trUtf8("<p>The new form file <b>%1</b> could not be created.<br>"
                    "Problem: %2</p>").arg(fname).arg(unicode(e)),
                self.trUtf8("&OK"))
            return
            
        self.project.appendFile(fname)
        self.emit(PYSIGNAL('designerFile'),(fname,))
        
    def handleRename(self):
        """
        Private method to rename a file of the project.
        """
        itm = self.currentItem()
        fn = unicode(itm.fileName())
        if self.project.renameFile(fn):
            self.removeNode(itm)
            self.children.remove(itm)
            del itm
            self.rebuildTree()
        
    def handleDelete(self):
        """
        Private method to delete a file from the project.
        """
        itmList = self.getSelectedItems()
        
        files = []
        fullNames = []
        for itm in itmList:
            fn2 = unicode(itm.fileName())
            fullNames.append(fn2)
            fn = fn2.replace(self.project.ppath+os.sep, '')
            files.append(fn)
        
        dlg = DeleteFilesConfirmationDialog(self.parent(),
            self.trUtf8("Delete forms"),
            self.trUtf8("Do you really want to delete these forms from the project?"),
            self.trUtf8("&Yes"), self.trUtf8("&No"), files)
        
        if dlg.exec_loop() == QDialog.Accepted:
            for itm, fn2, fn in zip(itmList[:], fullNames, files):
                self.emit(PYSIGNAL('closeSourceWindow'), (fn2,))
                if self.project.deleteFile(fn):
                    self.removeNode(itm)
                    self.children.remove(itm)
                    del itm
    
    def handleStdout(self):
        """
        Private slot to handle the readyReadStdout signal of the pyuic process.
        """
        while self.compileProc and self.compileProc.canReadLineStdout():
            self.buf.append(self.compileProc.readLineStdout())
            self.buf.append(os.linesep)
        
    def handleStderr(self):
        """
        Private slot to handle the readyReadStderr signal of the pyuic process.
        """
        while self.compileProc and self.compileProc.canReadLineStderr():
            s = QString('pyuic: ')
            s.append(self.compileProc.readLineStderr())
            self.emit(PYSIGNAL('appendStderr'), (s,))
        
    def handleCompileUIDone(self):
        """
        Private slot to handle the processExit signal of the pyuic process.
        """
        self.compileRunning = 0
        qApp.mainWidget().getViewManager().enableEditorsCheckFocusIn(1)
        if self.compileProc.normalExit():
            if self.subclass:
                vm = self.project.parent().getViewManager()
                vm.newEditor()
                aw = vm.activeWindow()
                aw.insertAt(self.buf, 0, 0)
                aw.setLanguage('py')
            else:
                kdeUI = self.project.pdata["UITYPE"][0] == "Kde"
                ofn = os.path.join(self.project.ppath, self.compiledFile)
                try:
                    f = open(ofn, "wb")
                    for line in unicode(self.buf).splitlines():
                        if kdeUI:
                            # The following code was taken fro kdepyuic, which is part of PyKDE
                            # Copyright (c) 2002 Jim Bublitz (jbublitz@nwinternet.com)
                            if line.strip() == 'from qt import *':
                                f.write(line + os.linesep)
                                f.write ('from kdecore import KCmdLineArgs, KApplication' + os.linesep)
                                f.write ('from kdeui import *' + os.linesep + os.linesep)
                            elif line.strip() == 'a = QApplication(sys.argv)':
                                indent = 0
                                while line[indent] in string.whitespace:
                                    indent = indent + 1
                                indent = line[:indent]
                                f.write (indent + 'appname     = ""' + os.linesep)
                                f.write (indent + 'description = ""' + os.linesep)
                                f.write (indent + 'version     = ""' + os.linesep)
                                f.write (os.linesep)
                                f.write (indent + 'KCmdLineArgs.init (sys.argv, appname, description, version)' + os.linesep)
                                f.write (indent + 'a = KApplication ()' + os.linesep + os.linesep)
                            elif line.find(" = KDatePicker(") != -1:
                                o = line.find(",")
                                f.write(line[:o] + ",QDate.currentDate()" + line[o:] + os.linesep)
                            else:
                                f.write(line + os.linesep)
                            # end of kdepyuic code
                        else:
                            f.write(line + os.linesep)
                    f.close()
                    if self.compiledFile not in self.project.pdata["SOURCES"]:
                        self.project.pdata["SOURCES"].append(self.compiledFile)
                        self.emit(PYSIGNAL('projectSourceAdded'), (self.compiledFile,))
                        self.project.setDirty(1)
                    if not self.noDialog:
                        KQMessageBox.information(None,
                            self.trUtf8("Form Compilation"),
                            self.trUtf8("The compilation of the form file was successful."),
                            self.trUtf8("&OK"))
                except IOError, msg:
                    if not self.noDialog:
                        KQMessageBox.information(None,
                            self.trUtf8("Form Compilation"),
                            self.trUtf8("<p>The compilation of the form file failed.</p><p>Reason: %1</p>")\
                                .arg(unicode(msg)),
                            self.trUtf8("&OK"))
        else:
            if not self.noDialog:
                KQMessageBox.information(None,
                    self.trUtf8("Form Compilation"),
                    self.trUtf8("The compilation of the form file failed."),
                    self.trUtf8("&OK"))
        self.compileProc = None
        
    def handleCompileUI(self, fn, noDialog = 0, progress = None, subclass = 0):
        """
        Privat method to compile a .ui file to a .py file.
        
        @param fn filename of the .ui file to be compiled
        @param noDialog flag indicating silent operations
        @param progress reference to the progress dialog
        @param subclass flag indicating, if a subclass generation is to be done
        @return reference to the compile process (QProcess)
        """
        self.compileProc = QProcess()
        
        pyuic = 'pyuic'
        if sys.platform == "win32":
            pyuic = pyuic + '.exe'
        self.compileProc.addArgument(pyuic)
        
        (ofn, ext) = os.path.splitext(fn)
        fn = os.path.join(self.project.ppath, fn)
        
        self.buf = QString("")
        if subclass:
            self.subclass = 1
            # get classname from .ui file
            try:
                f = open(fn)
                rx = QRegExp("<class>([^<]*)</class>.*")
                classname = None
                while 1:
                    line = f.readline()
                    if line == "":
                        break
                    elif rx.exactMatch(line):
                        classname = unicode(rx.cap(1))
                        break
                    
                f.close()
            except IOError:
                KQMessageBox.critical(self,
                    self.trUtf8('Error compiling form'),
                    self.trUtf8(
                        '<p>The form file <b>%1</b> could not be read.</p>'
                    ).arg(fn),
                    self.trUtf8('OK'))
                return None
                
            if classname is None:
                KQMessageBox.critical(self,
                    self.trUtf8('Error compiling form'),
                    self.trUtf8(
                        '<p>The form file <b>%1</b> does not contain a class definition.</p>'
                    ).arg(fn),
                    self.trUtf8('OK'))
                return None
                
            # get name of subclass
            subclassname, ok = KQInputDialog.getText(\
                self.trUtf8("Subclass Generation"),
                self.trUtf8("Enter name of subclass:"),
                QLineEdit.Normal, "%s_Impl" % classname)
            if not ok or subclassname.isEmpty():
                return None

            self.compileProc.addArgument('-subimpl')
            self.compileProc.addArgument(subclassname)
            path, fn = os.path.split(fn)
            self.compileProc.setWorkingDirectory(QDir(path))
        else:
            self.subclass = 0
            self.compiledFile = ofn + '.py'
            self.compileProc.addArgument('-x')
            
        self.compileProc.addArgument(fn)
        self.connect(self.compileProc, SIGNAL('processExited()'), self.handleCompileUIDone)
        self.connect(self.compileProc, SIGNAL('readyReadStdout()'), self.handleStdout)
        self.connect(self.compileProc, SIGNAL('readyReadStderr()'), self.handleStderr)
        
        self.compileRunning = 1
        self.noDialog = noDialog
        if self.compileProc.start():
            qApp.mainWidget().getViewManager().enableEditorsCheckFocusIn(0)
            return self.compileProc
        else:
            if progress is not None:
                progress.cancel()
            KQMessageBox.critical(self,
                self.trUtf8('Process Generation Error'),
                self.trUtf8(
                    'Could not start pyuic.<br>'
                    'Ensure that it is in the search path.'
                ),
                self.trUtf8('OK'))
            return None
        
    def handleSubclass(self):
        """
        Private method to generate a subclass for the form.
        """
        qApp.mainWidget().showLogTab("stderr")
        itm = self.currentItem()
        fn2 = unicode(itm.fileName())
        fn = fn2.replace(self.project.ppath+os.sep, '')
        self.handleCompileUI(fn, 0, None, 1)
        
    def handleCompile(self):
        """
        Private method to compile a form to a python file.
        """
        qApp.mainWidget().showLogTab("stderr")
        itm = self.currentItem()
        fn2 = unicode(itm.fileName())
        fn = fn2.replace(self.project.ppath+os.sep, '')
        self.handleCompileUI(fn)
        
    def handleCompileAll(self):
        """
        Private method to compile all forms to python files.
        """
        qApp.mainWidget().showLogTab("stderr")
        numForms = len(self.project.pdata["FORMS"])
        progress = KQProgressDialog(self.trUtf8("Compiling forms..."), 
            self.trUtf8("Abort"), numForms, self, "progress", 1)
        progress.setMinimumDuration(0)
        i = 0
        
        for fn in self.project.pdata["FORMS"]:
            progress.setProgress(i)
            if progress.wasCancelled():
                break
            if not fn.endswith('.ui.h'):
                proc = self.handleCompileUI(fn, 1, progress)
                if proc is not None:
                    while proc.isRunning():
                        qApp.processEvents()
                        QThread.msleep(300)
                        qApp.processEvents()
                else:
                    break
            i += 1
            
        progress.setProgress(numForms)
        
    def handleCompileSelected(self):
        """
        Private method to compile selected forms to python files.
        """
        qApp.mainWidget().showLogTab("stderr")
        items = self.getSelectedItems()
        
        files = [unicode(itm.fileName()).replace(self.project.ppath+os.sep, '') \
                 for itm in items]
        numForms = len(files)
        progress = KQProgressDialog(self.trUtf8("Compiling forms..."), 
            self.trUtf8("Abort"), numForms, self, "progress", 1)
        progress.setMinimumDuration(0)
        i = 0
        
        for fn in files:
            progress.setProgress(i)
            if progress.wasCancelled():
                break
            if not fn.endswith('.ui.h'):
                proc = self.handleCompileUI(fn, 1, progress)
                if proc is not None:
                    while proc.isRunning():
                        qApp.processEvents()
                        QThread.msleep(300)
                        qApp.processEvents()
                else:
                    break
            i += 1
            
        progress.setProgress(numForms)
        
    def handleCompileChangedForms(self):
        """
        Public method to compile all changed forms to python files.
        """
        qApp.mainWidget().showLogTab("stderr")
        progress = KQProgressDialog(self.trUtf8("Compiling forms..."), 
            None, 100, None, "progress", 1)
        progress.setMinimumDuration(0)
        i = 0
        
        # get list of changed forms
        changedForms = []
        progress.setTotalSteps(len(self.project.pdata["FORMS"]))
        for fn in self.project.pdata["FORMS"]:
            progress.setProgress(i)
            qApp.processEvents()
            if not fn.endswith('.ui.h'):
                ifn = os.path.join(self.project.ppath, fn)
                ofn = os.path.splitext(ifn)[0] + '.py'
                if not os.path.exists(ofn) or \
                   os.stat(ifn).st_mtime > os.stat(ofn).st_mtime:
                    changedForms.append(fn)
            i += 1
        progress.setProgress(i)
        qApp.processEvents()
        
        if changedForms:
            i = 0
            progress.setProgress(i)
            qApp.processEvents()
            progress.setTotalSteps(len(changedForms))
            for fn in changedForms:
                progress.setProgress(i)
                proc = self.handleCompileUI(fn, 1, progress)
                if proc is not None:
                    while proc.isRunning():
                        qApp.processEvents()
                        QThread.msleep(300)
                        qApp.processEvents()
                else:
                    break
                i += 1
            progress.setProgress(len(changedForms))
            qApp.processEvents()
        
    def handleProjectFormAdded(self, fn):
        """
        Private slot to handle the projectFormAdded signal.
        
        @param fn Filename of the added forms file. (QString)
        """
        fname = os.path.join(self.project.ppath, fn)
        parent, dt = self.findParentNode(fn)
        node = BrowserFile(parent, fname, None, 1, dt)
        self.children.append(node)
        self.nodeAdded(node, fname)
        
    def __itemsHaveDesignerHeaderFiles(self, items):
        """
        Private method to check, if items contain designer header files.
        
        @param items items to check (list of QListViewItems)
        @return flag indicating designer header files were found (boolean)
        """
        for itm in items:
            if itm.isDesignerHeaderFile():
                return 1
        return 0
        
class ProjectTranslationsBrowser(PBrowser):
    """
    A class used to display the translations part of the project. 
    
    Via the context menu that is displayed by a right click the user can 
    select various actions on the selected file.
    
    @signal linguistFile(string) emitted to open a translation file with
            Qt-Linguist
    @signal appendStdout(string) emitted after something was received from
            a QProcess on stdout
    @signal appendStderr(string) emitted after something was received from
            a QProcess on stderr
    @signal pythonFile(string) emitted to open a translation file in an editor
    @signal closeSourceWindow(string) emitted after a file has been removed/deleted 
            from the project
    @signal trpreview(string list) emitted to preview form files in the 
            translations previewer
    """
    def __init__(self,project,qtdir,parent=None):
        """
        Constructor
        
        @param project reference to the project object
        @param qtdir path of the Qt installation directory (string)
        @param parent parent widget of this browser (QWidget)
        """
        self.qtdir = qtdir
        PBrowser.__init__(self,project,"TRANSLATIONS",parent)
    
        self.selectedItemsFilter = [BrowserFile, ProjectBrowserSimpleDir]
        
        self.setCaption(self.trUtf8('Translations'))

        QWhatsThis.add(self,self.trUtf8(
            """<b>Project Translations Browser</b>"""
            """<p>This allows to easily see all translations contained in the current"""
            """ project. Several actions can be executed via the context menu.</p>"""
        ))
        
        self.lreleaseProc = None
        self.pylupdateProc = None
        
        self.connect(project, PYSIGNAL("projectPropertiesChanged"),
                     self.handleProjectPropertiesChanged)
        
    def createPopupMenus(self):
        """
        Private overloaded method to generate the popup menu.
        """
        self.menuItems = []
        self.multiMenuItems = []
        self.dirMenuItems = []
        self.dirMultiMenuItems = []
        
        self.menu = QPopupMenu(self)
        if self.qtdir is not None:
            self.menu.insertItem(self.trUtf8('Generate translation'), 
                self.handleGeneration)
            self.menu.insertItem(self.trUtf8('Generate translation (with obsolete)'), 
                self.handleGenerationObsolete)
            self.menu.insertItem(self.trUtf8('Generate all translations'), 
                self.handleGenerationAll)
            self.menu.insertItem(self.trUtf8('Generate all translations (with obsolete)'), 
                self.handleGenerationObsoleteAll)
            self.menu.insertSeparator()
            self.menu.insertItem(self.trUtf8('Open in Qt-Linguist'), self.handleOpen)
            self.menu.insertItem(self.trUtf8('Open in Editor'), self.handleOpenEditor)
            self.menu.insertSeparator()
            self.menu.insertItem(self.trUtf8('Release translation'), self.handleRelease)
            self.menu.insertItem(self.trUtf8('Release all translations'), self.handleReleaseAll)
            self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8('Preview translation'), self.handleTRPreview)
        self.menu.insertSeparator()
        itm = self.menu.insertItem(self.trUtf8('Remove from project'), self.handleRemove)
        self.menuItems.append(itm)
        itm = self.menu.insertItem(self.trUtf8('Delete'), self.handleDelete)
        self.menuItems.append(itm)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8('Add translation...'), self.project.addLanguage)

        self.backMenu = QPopupMenu(self)
        if self.qtdir is not None:
            self.backMenu.insertItem(self.trUtf8('Generate translations'), 
                self.handleGenerationAll)
            self.backMenu.insertItem(self.trUtf8('Generate translations (with obsolete)'), 
                self.handleGenerationObsoleteAll)
            self.backMenu.insertItem(self.trUtf8('Release translations'), 
                self.handleReleaseAll)
            self.backMenu.insertSeparator()
        self.backMenu.insertItem(self.trUtf8('Preview translations'), self.handleTRPreview)
        self.backMenu.insertSeparator()
        self.backMenu.insertItem(self.trUtf8('Add translation...'), self.project.addLanguage)
        self.backMenu.setEnabled(0)

        # create the menu for multiple selected files
        self.multiMenu = QPopupMenu(self)
        if self.qtdir is not None:
            self.multiMenu.insertItem(self.trUtf8('Generate translations'), 
                self.handleGeneration)
            self.multiMenu.insertItem(self.trUtf8('Generate translations (with obsolete)'), 
                self.handleGenerationObsolete)
            self.multiMenu.insertSeparator()
            self.multiMenu.insertItem(self.trUtf8('Open in Qt-Linguist'), self.handleOpen)
            self.multiMenu.insertItem(self.trUtf8('Open in Editor'), self.handleOpenEditor)
            self.multiMenu.insertSeparator()
            self.multiMenu.insertItem(self.trUtf8('Release translations'), self.handleRelease)
            self.multiMenu.insertSeparator()
        self.multiMenu.insertItem(self.trUtf8('Preview translations'), self.handleTRPreview)
        self.multiMenu.insertSeparator()
        itm = self.multiMenu.insertItem(self.trUtf8('Remove from project'), self.handleRemove)
        self.multiMenuItems.append(itm)
        itm = self.multiMenu.insertItem(self.trUtf8('Delete'), self.handleDelete)
        self.multiMenuItems.append(itm)

        self.dirMenu = QPopupMenu(self)
        if self.qtdir is not None:
            self.dirMenu.insertItem(self.trUtf8('Generate translations'), 
                self.handleGenerationAll)
            self.dirMenu.insertItem(self.trUtf8('Generate translations (with obsolete)'), 
                self.handleGenerationObsoleteAll)
            self.dirMenu.insertItem(self.trUtf8('Release translations'), 
                self.handleReleaseAll)
            self.dirMenu.insertSeparator()
        self.dirMenu.insertItem(self.trUtf8('Preview translations'), self.handleTRPreview)
        self.dirMenu.insertSeparator()
        self.dirMenu.insertItem(self.trUtf8('Add translation...'), self.project.addLanguage)
        
        self.dirMultiMenu = None
        
        self.connect(self.menu,SIGNAL('aboutToShow()'),self.handlePopupMenu)
        self.connect(self.multiMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuMulti)
        self.connect(self.dirMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuDir)
        self.mainMenu = self.menu
        
    def handleContextMenu(self,itm,coord,col):
        """
        Private slot to show the context menu of the listview.
        
        @param itm the selected listview item (QListViewItem)
        @param coord the position of the mouse pointer (QPoint)
        @param col the column of the mouse pointer (int)
        """
        try:
            cnt = self.getSelectedItemsCount([BrowserFile, ProjectBrowserSimpleDir])
            bfcnt = self.getSelectedItemsCount([BrowserFile])
            sdcnt = self.getSelectedItemsCount([ProjectBrowserSimpleDir])
            if cnt > 1 and cnt == bfcnt:
                self.multiMenu.popup(coord)
            elif cnt == 1 and bfcnt == 1:
                self.menu.popup(coord)
            elif cnt == 1 and sdcnt == 1:
                self.dirMenu.popup(coord)
            else:
                self.backMenu.popup(coord)
        except:
            pass
        
    def handlePopupMenu(self):
        """
        Private slot called by the pyMenu aboutToShow signal.
        """
        self.handleShowPopupMenu(self.menu)
        
    def handlePopupMenuMulti(self):
        """
        Private slot called by the multiMenu aboutToShow signal.
        """
        self.handleShowPopupMenuMulti(self.multiMenu)
        
    def handlePopupMenuDir(self):
        """
        Private slot called by the dirMenu aboutToShow signal.
        """
        self.handleShowPopupMenuDir(self.dirMenu)
        
    def handleOpenEditor(self):
        """
        Private slot to handle the Open in Editor menu action.
        """
        itmList = self.getSelectedItems()
        for itm in itmList[:]:
            self.emit(PYSIGNAL('pythonFile'),(itm.fileName(),))
        
    def handleRemove(self):
        """
        Private method to remove a translation from the project.
        """
        itmList = self.getSelectedItems()
        
        for itm in itmList[:]:
            fn = unicode(itm.text(0))
            self.takeItem(itm)
            self.children.remove(itm)
            del itm
            
            fn = self.project.removeLanguage(fn)
            self.emit(PYSIGNAL('closeSourceWindow'), (fn,))
        
    def handleDelete(self):
        """
        Private method to delete a translation file from the project.
        """
        itmList = self.getSelectedItems()
        
        translations = [unicode(itm.text(0)) for itm in itmList]
        
        dlg = DeleteFilesConfirmationDialog(self.parent(),
            self.trUtf8("Delete translations"),
            self.trUtf8("Do you really want to delete these translations from the project?"),
            self.trUtf8("&Yes"), self.trUtf8("&No"), translations)
        
        if dlg.exec_loop() == QDialog.Accepted:
            for itm, fn in zip(itmList[:], translations):
                deleted = self.project.deleteLanguage(fn)
                if deleted:
                    self.emit(PYSIGNAL('closeSourceWindow'), (deleted,))
                    self.removeNode(itm)
                    self.children.remove(itm)
                    del itm
            
    def handleTRPreview(self):
        """
        Private slot to handle the Preview translations action.
        """
        fileNames = []
        itmList = self.getSelectedItems()
        if itmList:
            for itm in itmList:
                fn = Utilities.joinext(os.path.splitext(itm.fileName())[0], '.qm')
                fileNames.append(fn)
        else:
            trfiles = self.project.pdata["TRANSLATIONS"][:]
            trfiles.sort()
            for trfile in trfiles:
                fn = Utilities.joinext(os.path.splitext(trfile)[0], '.qm')
                fileNames.append(fn)
        self.emit(PYSIGNAL('trpreview'),(fileNames,))
    
    def writeTempProjectFile(self, langs=[]):
        """
        Private method to write a temporary project file suitable for pylupdate and lrelease.
        
        @param langs list of languages to include in the process. An empty list (default) 
            means that all translations should be included. (list of BrowserFile)
        @return flag indicating success
        """
        path, ext = os.path.splitext(self.project.pfile)
        pfile = '%s.e3x' % path
        
        # only do non-Ruby sources
        sources = [s for s in self.project.pdata["SOURCES"] if not s.endswith(".rb")]
        sections = [("SOURCES", sources)]
        if langs:
            l = [lang.fileName().replace(self.project.ppath+os.sep, '') for lang in langs]
            sections.append(("TRANSLATIONS", l))
        else:
            sections.append(("TRANSLATIONS", self.project.pdata["TRANSLATIONS"]))
        
        try:
            pf = open(pfile, "wb")
            for key, list in sections:
                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()
            self.tmpProject = pfile
            return 1
        except:
            KQMessageBox.critical(None,
                self.trUtf8("Write temporary project file"),
                self.trUtf8("<p>The temporary project file <b>%1</b> could not be written.</p>")
                    .arg(pfile),
                self.trUtf8("&Abort"))
            self.tmpProject = None
            return 0
            
    def handleStdout(self):
        """
        Private slot to handle the readyReadStdout signal of the pylupdate/lrelease process.
        """
        if self.pylupdateProc is not None:
            proc = self.pylupdateProc
            ps = 'pylupdate: '
        elif self.lreleaseProc is not None:
            proc = self.lreleaseProc
            ps = 'lrelease: '
            
        while proc and proc.canReadLineStdout():
            s = QString(ps)
            s.append(proc.readLineStdout())
            self.emit(PYSIGNAL('appendStdout'), (s,))
        
    def handleStderr(self):
        """
        Private slot to handle the readyReadStderr signal of the pylupdate/lrelease process.
        """
        if self.pylupdateProc is not None:
            proc = self.pylupdateProc
            ps = 'pylupdate: '
        elif self.lreleaseProc is not None:
            proc = self.lreleaseProc
            ps = 'lrelease: '
            
        while proc and proc.canReadLineStderr():
            s = QString(ps)
            s.append(proc.readLineStderr())
            self.emit(PYSIGNAL('appendStderr'), (s,))
        
    def handleGenerateTSFileDone(self):
        """
        Private slot to handle the processExit signal of the pylupdate/lrelease process.
        """
        self.pylupdateProcRunning = 0
        if self.pylupdateProc.normalExit():
            KQMessageBox.information(None,
                self.trUtf8("Translation file generation"),
                self.trUtf8("The generation of the translation files (*.ts) was successful."),
                self.trUtf8("&OK"))
        else:
            KQMessageBox.critical(None,
                self.trUtf8("Translation file generation"),
                self.trUtf8("The generation of the translation files (*.ts) has failed."),
                self.trUtf8("&OK"))
        self.pylupdateProc = None
        try:
            os.remove(self.tmpProject)
        except:
            pass
        self.tmpProject = None
                
    def generateTSFile(self, noobsolete = 0, generateAll=1):
        """
        Private method used to run pyludate to generate the .ts files.
        
        @param noobsolete flag indicating whether obsolete entries should be kept (boolean)
        @param generateAll flag indicating whether all translations should be generated (boolean)
        """
        qApp.mainWidget().showLogTab("stderr")
        
        # generate a minimal temporary projectfile suitable for pylupdate
        if generateAll:
            langs = []
        else:
            langs = self.getSelectedItems()
        ok = self.writeTempProjectFile(langs)
        if not ok:
            return
            
        self.pylupdateProc = QProcess()
        
        pylupdate = 'pylupdate'
        if sys.platform == "win32":
            pylupdate = pylupdate + '.exe'
        self.pylupdateProc.addArgument(pylupdate)
        
        if noobsolete:
            self.pylupdateProc.addArgument('-noobsolete')
        
        self.pylupdateProc.addArgument('-verbose')
        self.pylupdateProc.addArgument(self.tmpProject)
        self.pylupdateProc.setWorkingDirectory(QDir(self.project.ppath))
        self.connect(self.pylupdateProc, SIGNAL('processExited()'), self.handleGenerateTSFileDone)
        self.connect(self.pylupdateProc, SIGNAL('readyReadStdout()'), self.handleStdout)
        self.connect(self.pylupdateProc, SIGNAL('readyReadStderr()'), self.handleStderr)
        
        self.pylupdateProcRunning = 1
        if not self.pylupdateProc.start():
            KQMessageBox.critical(self,
                self.trUtf8('Process Generation Error'),
                self.trUtf8(
                    'Could not start pylupdate.<br>'
                    'Ensure that it is in the search path.'
                ),
                self.trUtf8('OK'))
        
    def handleGenerationAll(self):
        """
        Private method to generate the translation files (.ts) for Qt Linguist.
        
        All obsolete strings are removed from the .ts file.
        """
        self.generateTSFile(noobsolete=1, generateAll=1)
        
    def handleGenerationObsoleteAll(self):
        """
        Private method to generate the translation files (.ts) for Qt Linguist.
        
        Obsolete strings are kept.
        """
        self.generateTSFile(noobsolete=0, generateAll=1)
        
    def handleGeneration(self):
        """
        Private method to generate the translation files (.ts) for Qt Linguist.
        
        All obsolete strings are removed from the .ts file.
        """
        self.generateTSFile(noobsolete=1, generateAll=0)
        
    def handleGenerationObsolete(self):
        """
        Private method to generate the translation files (.ts) for Qt Linguist.
        
        Obsolete strings are kept.
        """
        self.generateTSFile(noobsolete=0, generateAll=0)
        
    def handleReleaseTSFileDone(self):
        """
        Private slot to handle the processExit signal of the pylupdate/lrelease process.
        """
        self.lreleaseProcRunning = 0
        if self.lreleaseProc.normalExit():
            KQMessageBox.information(None,
                self.trUtf8("Translation file release"),
                self.trUtf8("The release of the translation files (*.qm) was successful."),
                self.trUtf8("&OK"))
        else:
            KQMessageBox.critical(None,
                self.trUtf8("Translation file release"),
                self.trUtf8("The release of the translation files (*.qm) has failed."),
                self.trUtf8("&OK"))
        self.lreleaseProc = None
        try:
            os.remove(self.tmpProject)
        except:
            pass
        self.tmpProject = None
        
    def releaseTSFile(self, generateAll=0):
        """
        Private method to run lrelease to release the translation files (.qm).
        
        @param generateAll flag indicating whether all translations should be released (boolean)
        """
        qApp.mainWidget().showLogTab("stderr")
        
        # generate a minimal temporary projectfile suitable for lrelease
        if generateAll:
            langs = []
        else:
            langs = self.getSelectedItems()
        ok = self.writeTempProjectFile(langs)
        if not ok:
            return
            
        self.lreleaseProc = QProcess()
        
        lrelease = os.path.join(self.qtdir, 'bin', 'lrelease')
        if sys.platform == "win32":
            lrelease = lrelease + '.exe'
        self.lreleaseProc.addArgument(lrelease)
        
        self.lreleaseProc.addArgument('-verbose')
        self.lreleaseProc.addArgument(self.tmpProject)
        self.lreleaseProc.setWorkingDirectory(QDir(self.project.ppath))
        self.connect(self.lreleaseProc, SIGNAL('processExited()'), self.handleReleaseTSFileDone)
        self.connect(self.lreleaseProc, SIGNAL('readyReadStdout()'), self.handleStdout)
        self.connect(self.lreleaseProc, SIGNAL('readyReadStderr()'), self.handleStderr)
        
        self.lreleaseProcRunning = 1
        if not self.lreleaseProc.start():
            KQMessageBox.critical(self,
                self.trUtf8('Process Generation Error'),
                self.trUtf8(
                    '<p>Could not start lrelease.<br>'
                    'Ensure that it is available as <b>%1</b>.</p>'
                ).arg(lrelease),
                self.trUtf8('OK'))
        
    def handleRelease(self):
        """
        Private method to release the translation files (.qm).
        """
        self.releaseTSFile(generateAll=0)
        
    def handleReleaseAll(self):
        """
        Private method to release the translation files (.qm).
        """
        self.releaseTSFile(generateAll=1)
        
    def handleProjectLanguageAdded(self, fn):
        """
        Private slot to handle the projectLanguageAdded signal.
        
        @param fn Filename of the added language file (string)
        """
        fname = os.path.join(self.project.ppath, fn)
        dt, ext = os.path.splitext(fn)
        dtl = dt.split('_', 1)
        dt = dtl[-1]
        dt0 = "%s_" % '_'.join(dtl[:-1])
        parent = self.findParentNode(dt0, 1)[0]
        node = BrowserFile(parent, fname, None, 1, dt)
        self.children.append(node)
        self.nodeAdded(node, fname)
        
    def handleProjectPropertiesChanged(self):
        """
        Private slot to handle the projectPropertiesChanged signal.
        """
        if self.project.pdata["TRANSLATIONPREFIX"]:
            parent = self.findParentNode("%s_" % self.project.pdata["TRANSLATIONPREFIX"][0], 1)[0]
            if parent == self:
                path = os.path.join(self.project.ppath,
                            os.path.dirname(self.project.pdata["TRANSLATIONPREFIX"][0]))
                node = ProjectBrowserSimpleDir(self, 
                            "%s_" % self.project.pdata["TRANSLATIONPREFIX"][0], path)
                self.nodeAdded(node, path)
                self.children.append(node)
                node.setOpen(1)
        
    def handleVCSUpdate(self):
        """
        Private slot called by the context menu to update a file from the VCS repository.
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems([ProjectBrowserSimpleDir])]
        if not names:
            names = [unicode(itm.fileName()) for itm in self.getSelectedItems([BrowserFile])]
            names += ['%s.qm' % os.path.splitext(name)[0] for name in names]
        self.project.vcs.vcsUpdate(names)
        
    def handleVCSCommit(self):
        """
        Private slot called by the context menu to commit the changes to the VCS repository.
        """
        names = [unicode(itm.fileName()) for itm in self.getSelectedItems([ProjectBrowserSimpleDir])]
        if not names:
            names = [unicode(itm.fileName()) for itm in self.getSelectedItems([BrowserFile])]
            names += ['%s.qm' % os.path.splitext(name)[0] for name in names]
            if Preferences.getVCS("AutoSaveFiles"):
                vm = qApp.mainWidget().getViewManager()
                for name in names:
                    vm.saveEditor(name)
        self.project.vcs.vcsCommit(names, '')
        
    def handleVCSAdd(self):
        """
        Private slot called by the context menu to add the selected language to the VCS repository.
        """
        items = self.getSelectedItems([ProjectBrowserSimpleDir])
        if items:
            names = [unicode(itm.fileName()) for itm in items]
            self.project.vcs.vcsAdd(names)
        else:
            items = self.getSelectedItems([BrowserFile])
            names = [unicode(itm.fileName()) for itm in items]
            qnames = ['%s.qm' % os.path.splitext(name)[0] for name in names]
            
            # create empty files if they don't exist
            for name in names + qnames:
                if not os.path.exists(name):
                    try:
                        f = open(name, 'w')
                        f.close()
                    except:
                        pass
                
            if self.project.vcs.vcsName() in ["Subversion"]:
                self.project.vcs.vcsAdd(names + qnames)
            else:
                self.project.vcs.vcsAdd(names)
                self.project.vcs.vcsAddBinary(qnames)
        for itm, name in zip(items, names):
            self.updateVCSStatus(itm, name)
        
    def handleVCSRemove(self):
        """
        Private slot called by the context menu to remove the selected language from the VCS repository.
        """
        items = self.getSelectedItems([ProjectBrowserSimpleDir])
        if items:
            return      # not supported
        
        items = self.getSelectedItems([BrowserFile])
        names = [unicode(itm.fileName()) for itm in items]
        qnames = ['%s.qm' % os.path.splitext(name)[0] for name in names]
        translations = [unicode(itm.text(0)) for itm in items]
        
        dlg = DeleteFilesConfirmationDialog(self.parent(),
            self.trUtf8("Remove from repository (and disk)"),
            self.trUtf8("Do you really want to remove these translations from the repository (and disk)?"),
            self.trUtf8("&Yes"), self.trUtf8("&No"), translations)
        
        if dlg.exec_loop() == QDialog.Accepted:
            status = self.project.vcs.vcsRemove(names + qnames)
            if status:
                self.handleRemove() # remove file(s) from project
        
    def handleVCSStatus(self):
        """
        Private slot called by the context menu to show the status of a file.
        """
        items = self.getSelectedItems([ProjectBrowserSimpleDir])
        if items:
            names = [unicode(itm.fileName()) for itm in items]
        else:
            items = self.getSelectedItems([BrowserFile])
            names = [unicode(itm.fileName()) for itm in items]
            names += ['%s.qm' % os.path.splitext(name)[0] for name in names]
        self.project.vcs.vcsStatus(names)

    def handleCVSEdit(self):
        """
        Private slot called by the context menu to edit a file (CVS).
        """
        items = self.getSelectedItems()
        names = [unicode(itm.fileName()) for itm in items]
        qnames = ['%s.qm' % os.path.splitext(name)[0] for name in names]
        self.project.vcs.cvsEdit(names + qnames)
        
    def handleCVSUnedit(self):
        """
        Private slot called by the context menu to unedit a file (CVS).
        """
        items = self.getSelectedItems()
        names = [unicode(itm.fileName()) for itm in items]
        qnames = ['%s.qm' % os.path.splitext(name)[0] for name in names]
        self.project.vcs.cvsUnedit(names + qnames)
        
    def handleSVNListProps(self):
        """
        Private slot called by the context menu to list the subversion properties of a file.
        """
        items = self.getSelectedItems([ProjectBrowserSimpleDir])
        if items:
            names = [unicode(itm.fileName()) for itm in items]
        else:
            items = self.getSelectedItems([BrowserFile])
            names = [unicode(itm.fileName()) for itm in items]
            names += ['%s.qm' % os.path.splitext(name)[0] for name in names]
        self.project.vcs.svnListProps(names + qnames)
        
    def handleSVNSetProp(self):
        """
        Private slot called by the context menu to set a subversion property of a file.
        """
        items = self.getSelectedItems([ProjectBrowserSimpleDir])
        if items:
            names = [unicode(itm.fileName()) for itm in items]
        else:
            items = self.getSelectedItems([BrowserFile])
            names = [unicode(itm.fileName()) for itm in items]
            names += ['%s.qm' % os.path.splitext(name)[0] for name in names]
        self.project.vcs.svnSetProp(names + qnames)
        
    def handleSVNDelProp(self):
        """
        Private slot called by the context menu to delete a subversion property of a file.
        """
        items = self.getSelectedItems([ProjectBrowserSimpleDir])
        if items:
            names = [unicode(itm.fileName()) for itm in items]
        else:
            items = self.getSelectedItems([BrowserFile])
            names = [unicode(itm.fileName()) for itm in items]
            names += ['%s.qm' % os.path.splitext(name)[0] for name in names]
        self.project.vcs.svnDelProp(names + qnames)
        
class ProjectInterfacesBrowser(PBrowser):
    """
    A class used to display the interfaces (IDL) part of the project. 
    
    Via the context menu that
    is displayed by a right click the user can select various actions on
    the selected file.
    
    @signal closeSourceWindow(string) emitted after a file has been removed/deleted 
            from the project
    @signal appendStdout(string) emitted after something was received from
            a QProcess on stdout
    @signal appendStderr(string) emitted after something was received from
            a QProcess on stderr
    @signal projectSourceAdded(string) emitted after a compile finished successfully
    """
    def __init__(self,project,parent=None):
        """
        Constructor
        
        @param project reference to the project object
        @param parent parent widget of this browser (QWidget)
        """
        self.omniidl = unicode(Preferences.getCorba("omniidl"))
        if self.omniidl == "":
            self.omniidl = sys.platform == "win32" and "omniidl.exe" or "omniidl"
        if not Utilities.isinpath(self.omniidl):
            self.omniidl = None
            
        PBrowser.__init__(self,project,"INTERFACES",parent)
    
        self.selectedItemsFilter = [BrowserFile, ProjectBrowserSimpleDir]
        
        self.setCaption(self.trUtf8('Interfaces (IDL)'))
        
        QWhatsThis.add(self,self.trUtf8(
            """<b>Project Interfaces Browser</b>"""
            """<p>This allows to easily see all interfaces (CORBA IDL files)"""
            """ contained in the current project. Several actions can be executed"""
            """ via the context menu.</p>"""
        ))
        
    def createPopupMenus(self):
        """
        Private overloaded method to generate the popup menu.
        """
        self.menuItems = []
        self.multiMenuItems = []
        self.dirMenuItems = []
        self.dirMultiMenuItems = []
        
        self.menu = QPopupMenu(self)
        if self.omniidl is not None:
            self.menu.insertItem(self.trUtf8('Compile interface'), self.handleCompile)
            self.menu.insertItem(self.trUtf8('Compile all interfaces'), self.handleCompileAll)
        self.menu.insertItem(self.trUtf8('Open'), self.handleOpen)
        self.menu.insertSeparator()
        itm = self.menu.insertItem(self.trUtf8('Rename file'), self.handleRename)
        self.menuItems.append(itm)
        itm = self.menu.insertItem(self.trUtf8('Remove from project'), self.handleRemove)
        self.menuItems.append(itm)
        itm = self.menu.insertItem(self.trUtf8('Delete'), self.handleDelete)
        self.menuItems.append(itm)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8('Add interface...'), self.project.addIdlFile)
        self.menu.insertItem(self.trUtf8('Add interfaces directory...'), self.project.addIdlDir)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.menu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)

        self.backMenu = QPopupMenu(self)
        if self.omniidl is not None:
            self.backMenu.insertItem(self.trUtf8('Compile all interfaces'), self.handleCompileAll)
            self.backMenu.insertSeparator()
        self.backMenu.insertItem(self.trUtf8('Add interface...'), self.project.addIdlFile)
        self.backMenu.insertItem(self.trUtf8('Add interfaces directory...'), self.project.addIdlDir)
        self.backMenu.insertSeparator()
        self.backMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.backMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        self.backMenu.setEnabled(0)

        # create the menu for multiple selected files
        self.multiMenu = QPopupMenu(self)
        if self.omniidl is not None:
            self.multiMenu.insertItem(self.trUtf8('Compile interfaces'),
                self.handleCompileSelected)
        self.multiMenu.insertItem(self.trUtf8('Open'), self.handleOpen)
        self.multiMenu.insertSeparator()
        itm = self.multiMenu.insertItem(self.trUtf8('Remove from project'), self.handleRemove)
        self.multiMenuItems.append(itm)
        itm = self.multiMenu.insertItem(self.trUtf8('Delete'), self.handleDelete)
        self.multiMenuItems.append(itm)
        self.multiMenu.insertSeparator()
        self.multiMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.multiMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)

        self.dirMenu = QPopupMenu(self)
        if self.omniidl is not None:
            self.dirMenu.insertItem(self.trUtf8('Compile all interfaces'), self.handleCompileAll)
            self.dirMenu.insertSeparator()
        itm = self.dirMenu.insertItem(self.trUtf8('Remove from project'), self.handleRemoveDir)
        self.dirMenuItems.append(itm)
        self.dirMenu.insertSeparator()
        self.dirMenu.insertItem(self.trUtf8('Add interface...'), self.project.addIdlFile)
        self.dirMenu.insertItem(self.trUtf8('Add interfaces directory...'), self.project.addIdlDir)
        self.dirMenu.insertSeparator()
        self.dirMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.dirMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        
        self.dirMultiMenu = QPopupMenu(self)
        if self.omniidl is not None:
            self.dirMultiMenu.insertItem(self.trUtf8('Compile all interfaces'), self.handleCompileAll)
            self.dirMultiMenu.insertSeparator()
        self.dirMultiMenu.insertItem(self.trUtf8('Add interface...'), self.project.addIdlFile)
        self.dirMultiMenu.insertItem(self.trUtf8('Add interfaces directory...'), self.project.addIdlDir)
        self.dirMultiMenu.insertSeparator()
        self.dirMultiMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.dirMultiMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        
        self.connect(self.menu,SIGNAL('aboutToShow()'),self.handlePopupMenu)
        self.connect(self.multiMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuMulti)
        self.connect(self.dirMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuDir)
        self.connect(self.dirMultiMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuDirMulti)
        self.mainMenu = self.menu
        
    def handleContextMenu(self,itm,coord,col):
        """
        Private slot to show the context menu of the listview.
        
        @param itm the selected listview item (QListViewItem)
        @param coord the position of the mouse pointer (QPoint)
        @param col the column of the mouse pointer (int)
        """
        try:
            cnt = self.getSelectedItemsCount([BrowserFile, ProjectBrowserSimpleDir])
            bfcnt = self.getSelectedItemsCount([BrowserFile])
            sdcnt = self.getSelectedItemsCount([ProjectBrowserSimpleDir])
            if cnt > 1 and cnt == bfcnt:
                self.multiMenu.popup(coord)
            elif cnt == 1 and bfcnt == 1:
                self.menu.popup(coord)
            elif cnt == 1 and sdcnt == 1:
                self.dirMenu.popup(coord)
            elif cnt > 1 and cnt == sdcnt:
                self.dirMultiMenu.popup(coord)
            else:
                self.backMenu.popup(coord)
        except:
            pass
        
    def handlePopupMenu(self):
        """
        Private slot called by the pyMenu aboutToShow signal.
        """
        self.handleShowPopupMenu(self.menu)
        
    def handlePopupMenuMulti(self):
        """
        Private slot called by the multiMenu aboutToShow signal.
        """
        self.handleShowPopupMenuMulti(self.multiMenu)
        
    def handlePopupMenuDir(self):
        """
        Private slot called by the dirMenu aboutToShow signal.
        """
        self.handleShowPopupMenuDir(self.dirMenu)
        
    def handlePopupMenuDirMulti(self):
        """
        Private slot called by the dirMultiMenu aboutToShow signal.
        """
        self.handleShowPopupMenuDirMulti(self.dirMultiMenu)
        
    def handleRename(self):
        """
        Private method to rename a file of the project.
        """
        itm = self.currentItem()
        fn = unicode(itm.fileName())
        if self.project.renameFile(fn):
            self.removeNode(itm)
            self.children.remove(itm)
            del itm
            self.rebuildTree()
        
    def handleDelete(self):
        """
        Private method to delete a file from the project.
        """
        itmList = self.getSelectedItems()
        
        files = []
        fullNames = []
        for itm in itmList:
            fn2 = unicode(itm.fileName())
            fullNames.append(fn2)
            fn = fn2.replace(self.project.ppath+os.sep, '')
            files.append(fn)
        
        dlg = DeleteFilesConfirmationDialog(self.parent(),
            self.trUtf8("Delete interfaces"),
            self.trUtf8("Do you really want to delete these interfaces from the project?"),
            self.trUtf8("&Yes"), self.trUtf8("&No"), files)
        
        if dlg.exec_loop() == QDialog.Accepted:
            for itm, fn2, fn in zip(itmList[:], fullNames, files):
                self.emit(PYSIGNAL('closeSourceWindow'), (fn2,))
                if self.project.deleteFile(fn):
                    self.removeNode(itm)
                    self.children.remove(itm)
                    del itm
    
    def handleStdout(self):
        """
        Private slot to handle the readyReadStdout signal of the pyuic process.
        """
        while self.compileProc and self.compileProc.canReadLineStdout():
            s = QString('omniidl: ')
            s.append(self.compileProc.readLineStdout())
            self.emit(PYSIGNAL('appendStdout'), (s,))
        
    def handleStderr(self):
        """
        Private slot to handle the readyReadStderr signal of the pyuic process.
        """
        while self.compileProc and self.compileProc.canReadLineStderr():
            s = QString('omniidl: ')
            s.append(self.compileProc.readLineStderr())
            self.emit(PYSIGNAL('appendStderr'), (s,))
        
    def handleCompileIDLDone(self):
        """
        Private slot to handle the processExit signal of the omniidl process.
        """
        self.compileRunning = 0
        if self.compileProc.normalExit():
            path = os.path.dirname(self.idlFile)
            poaList = glob.glob(os.path.join(path, "*__POA"))
            npoaList = [f.replace("__POA", "") for f in poaList]
            fileList = glob.glob(os.path.join(path, "*_idl.py"))
            for dir in poaList + npoaList:
                fileList += Utilities.direntries(dir, 1, "*.py")
            for file in fileList:
                self.project.appendFile(file)
            if not self.noDialog:
                KQMessageBox.information(None,
                    self.trUtf8("Interface Compilation"),
                    self.trUtf8("The compilation of the interface file was successful."),
                    self.trUtf8("&OK"))
        else:
            if not self.noDialog:
                KQMessageBox.information(None,
                    self.trUtf8("Interface Compilation"),
                    self.trUtf8("The compilation of the interface file failed."),
                    self.trUtf8("&OK"))
        self.compileProc = None

    def handleCompileIDL(self, fn, noDialog = 0, progress = None):
        """
        Privat method to compile a .idl file to python.

        @param fn filename of the .idl file to be compiled
        @param noDialog flag indicating silent operations
        @param progress reference to the progress dialog
        @return reference to the compile process (QProcess)
        """
        self.compileProc = QProcess()

        self.compileProc.addArgument(self.omniidl)
        self.compileProc.addArgument("-bpython")
        self.compileProc.addArgument("-I.")

        fn = os.path.join(self.project.ppath, fn)
        self.idlFile = fn
        self.compileProc.addArgument("-C%s" % os.path.dirname(fn))
        self.compileProc.addArgument(fn)
        
        self.connect(self.compileProc, SIGNAL('processExited()'), self.handleCompileIDLDone)
        self.connect(self.compileProc, SIGNAL('readyReadStdout()'), self.handleStdout)
        self.connect(self.compileProc, SIGNAL('readyReadStderr()'), self.handleStderr)

        self.compileRunning = 1
        self.noDialog = noDialog
        if self.compileProc.start():
            return self.compileProc
        else:
            if progress is not None:
                progress.cancel()
            KQMessageBox.critical(self,
                self.trUtf8('Process Generation Error'),
                self.trUtf8(
                    '<p>Could not start %1.<br>'
                    'Ensure that it is in the search path.</p>'
                ).arg(self.omniidl),
                self.trUtf8('OK'))
            return None

    def handleCompile(self):
        """
        Private method to compile an interface to python.
        """
        if self.omniidl is not None:
            itm = self.currentItem()
            fn2 = unicode(itm.fileName())
            fn = fn2.replace(self.project.ppath+os.sep, '')
            self.handleCompileIDL(fn)

    def handleCompileAll(self):
        """
        Private method to compile all interfaces to python.
        """
        if self.omniidl is not None:
            numIDLs = len(self.project.pdata["INTERFACES"])
            progress = KQProgressDialog(self.trUtf8("Compiling interfaces..."), 
                self.trUtf8("Abort"), numIDLs, self, "progress", 1)
            progress.setMinimumDuration(0)
            i = 0
            
            for fn in self.project.pdata["INTERFACES"]:
                progress.setProgress(i)
                if progress.wasCancelled():
                    break
                proc = self.handleCompileIDL(fn, 1, progress)
                if proc is not None:
                    while proc.isRunning():
                        qApp.processEvents()
                        QThread.msleep(300)
                        qApp.processEvents()
                else:
                    break
                i += 1
                
            progress.setProgress(numIDLs)
        
    def handleCompileSelected(self):
        """
        Private method to compile all interfaces to python.
        """
        if self.omniidl is not None:
            items = self.getSelectedItems()
            
            files = [unicode(itm.fileName()).replace(self.project.ppath+os.sep, '') \
                     for itm in items]
            numIDLs = len(files)
            progress = KQProgressDialog(self.trUtf8("Compiling interfaces..."), 
                self.trUtf8("Abort"), numIDLs, self, "progress", 1)
            progress.setMinimumDuration(0)
            i = 0
            
            for fn in files:
                progress.setProgress(i)
                if progress.wasCancelled():
                    break
                proc = self.handleCompileIDL(fn, 1, progress)
                if proc is not None:
                    while proc.isRunning():
                        qApp.processEvents()
                        QThread.msleep(300)
                        qApp.processEvents()
                else:
                    break
                i += 1
                
            progress.setProgress(numIDLs)
        
    def handleProjectInterfaceAdded(self, fn):
        """
        Private slot to handle the projectInterfaceAdded signal.
        
        @param fn Filename of the interface file (QString)
        """
        fname = os.path.join(self.project.ppath, fn)
        parent, dt = self.findParentNode(fn)
        node = BrowserFile(parent, fname, None, 1, dt)
        self.children.append(node)
        self.nodeAdded(node, fname)
        
class ProjectOthersBrowser(PBrowser):
    """
    A class used to display the parts of the project, that don't fit the other categories.
    
    Via the context menu that is displayed by a right 
    click the user can select various actions on the selected file or 
    directory.
    
    @signal pythonFile(string) emitted to open a file
    @signal closeSourceWindow(string) emitted after a file has been removed/deleted
            from the project
    """
    def __init__(self,project,parent=None):
        """
        Constructor
        
        @param project reference to the project object
        @param parent parent widget of this browser (QWidget)
        """
        PBrowser.__init__(self,project,"OTHERS",parent)
    
        self.selectedItemsFilter = [BrowserFile, ProjectBrowserDirectory]
        self.specialMenuEntries = [1]

        self.setCaption(self.trUtf8('Others'))

        QWhatsThis.add(self,self.trUtf8(
            """<b>Project Others Browser</b>"""
            """<p>This allows to easily see all other files and directories"""
            """ contained in the current project. Several actions can be"""
            """ executed via the context menu. The entry which is registered"""
            """ in the project is shown in a different colour.</p>"""
        ))
        
    def createPopupMenus(self):
        """
        Private overloaded method to generate the popup menu.
        """
        PBrowser.createPopupMenus(self)
        
        self.menu.insertSeparator()
        itm = self.menu.insertItem(self.trUtf8('Rename file'), self.handleRename)
        self.menuItems.append(itm)
        self.renameFileItm = itm
        itm = self.menu.insertItem(self.trUtf8('Remove from project'), self.handleRemove)
        self.menuItems.append(itm)
        itm = self.menu.insertItem(self.trUtf8('Delete'), self.handleDelete)
        self.menuItems.append(itm)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8('Add file...'), self.project.addOthersFile)
        self.menu.insertItem(self.trUtf8('Add directory...'), self.project.addOthersDir)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.menu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)

        self.backMenu = QPopupMenu(self)
        self.backMenu.insertItem(self.trUtf8('Add file...'), self.project.addOthersFile)
        self.backMenu.insertItem(self.trUtf8('Add directory...'), self.project.addOthersDir)
        self.backMenu.insertSeparator()
        self.backMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.backMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        self.backMenu.setEnabled(0)

        self.multiMenu.insertSeparator()
        itm = self.multiMenu.insertItem(self.trUtf8('Remove from project'), self.handleRemove)
        self.multiMenuItems.append(itm)
        itm = self.multiMenu.insertItem(self.trUtf8('Delete'), self.handleDelete)
        self.multiMenuItems.append(itm)
        self.multiMenu.insertSeparator()
        self.multiMenu.insertItem(self.trUtf8('Expand all directories'), self.handleExpandAllDirs)
        self.multiMenu.insertItem(self.trUtf8('Collapse all directories'), self.handleCollapseAllDirs)
        
        self.connect(self.menu,SIGNAL('aboutToShow()'),self.handlePopupMenu)
        self.connect(self.multiMenu,SIGNAL('aboutToShow()'),self.handlePopupMenuMulti)
        self.mainMenu = self.menu
        
    def handleContextMenu(self,itm,coord,col):
        """
        Private slot to show the context menu of the listview.
        
        @param itm the selected item (QListViewItem)
        @param coord the position of the mouse cursor (QPoint)
        @param col the column of the mouse cursor (int)
        """
        try:
            cnt = self.getSelectedItemsCount()
            if cnt > 1:
                self.multiMenu.popup(coord)
            elif cnt == 1:
                if isinstance(itm,BrowserFile) or isinstance(itm,ProjectBrowserDirectory):
                    self.menu.popup(coord)
                else:
                    self.backMenu.popup(coord)
            else:
                self.backMenu.popup(coord)
        except:
            pass
            
    def handlePopupMenu(self):
        """
        Private slot called by the menu aboutToShow signal.
        """
        self.handleShowPopupMenu(self.menu)
        
    def handlePopupMenuMulti(self):
        """
        Private slot called by the multiMenu aboutToShow signal.
        """
        self.handleShowPopupMenuMulti(self.multiMenu)
        
    def handleShowPopupMenu(self, menu):
        """
        Slot called before the context menu is shown. 
        
        It enables/disables the VCS menu entries depending on the overall 
        VCS status and the file status.
        
        @param menu Reference to the popup menu (QPopupMenu)
        """
        if self.project.vcs is None:
            for itm in self.menuItems:
                menu.setItemEnabled(itm, 1)
            if os.path.isdir(unicode(self.currentItem().fileName())):
                menu.setItemEnabled(self.renameFileItm, 0)
        else:
            if unicode(self.currentItem().text(1)) == self.project.vcs.vcsName():
                for itm in self.vcsMenuItems:
                    menu.setItemEnabled(itm, 1)
                for itm in self.vcsAddMenuItems:
                    menu.setItemEnabled(itm, 0)
                for itm in self.menuItems:
                    menu.setItemEnabled(itm, 0)
                if os.path.isdir(unicode(self.currentItem().fileName())):
                    try:
                        menu.setItemEnabled(self.cvsMenuEdit, 0)
                        menu.setItemEnabled(self.cvsMenuUnedit, 0)
                    except:
                        pass
            else:
                for itm in self.vcsMenuItems:
                    menu.setItemEnabled(itm, 0)
                for itm in self.vcsAddMenuItems:
                    menu.setItemEnabled(itm, 1)
                if not os.path.isdir(unicode(self.currentItem().fileName())):
                    menu.setItemEnabled(self.vcsMenuAddTree, 0)
                for itm in self.menuItems:
                    menu.setItemEnabled(itm, 1)
        
    def handleShowPopupMenuMulti(self, menu):
        """
        Slot called before the context menu (multiple selections) is shown. 
        
        It enables/disables the VCS menu entries depending on the overall 
        VCS status and the files status.
        
        @param menu reference to the menu to be shown
        """
        if self.project.vcs is None:
            for itm in self.multiMenuItems:
                menu.setItemEnabled(itm, 1)
        else:
            vcsName = self.project.vcs.vcsName()
            items = self.getSelectedItems()
            vcsItems = 0
            # determine number of selected items under VCS control
            for itm in items:
                if unicode(itm.text(1)) == vcsName:
                    vcsItems += 1
            
            if vcsItems > 0:
                if vcsItems != len(items):
                    for itm in self.vcsMultiMenuItems:
                        menu.setItemEnabled(itm, 0)
                else:
                    for itm in self.vcsMultiMenuItems:
                        menu.setItemEnabled(itm, 1)
                for itm in self.vcsAddMultiMenuItems:
                    menu.setItemEnabled(itm, 0)
                for itm in self.multiMenuItems:
                    menu.setItemEnabled(itm, 0)
                if self.__itemsHaveDirectories(items):
                    try:
                        menu.setItemEnabled(self.cvsMultiMenuEdit, 0)
                        menu.setItemEnabled(self.cvsMultiMenuUnedit, 0)
                    except:
                        pass
            else:
                for itm in self.vcsMultiMenuItems:
                    menu.setItemEnabled(itm, 0)
                for itm in self.vcsAddMultiMenuItems:
                    menu.setItemEnabled(itm, 1)
                if self.__itemsHaveFiles(items):
                    menu.setItemEnabled(self.vcsMultiMenuAddTree, 0)
                for itm in self.multiMenuItems:
                    menu.setItemEnabled(itm, 1)
        
    def handleOpen(self):
        """
        Private slot to handle the open popup menu entry.
        """
        itmList = self.getSelectedItems()
        
        for itm in itmList:
            try:
                if isinstance(itm, BrowserFile):
                    if itm.isPixmapFile():
                        self.emit(PYSIGNAL('pixmapFile'),(itm.fileName(),))
                    else:
                        self.emit(PYSIGNAL('pythonFile'),(itm.fileName(),))
            except:
                pass
        
    def handleRename(self):
        """
        Private method to rename a file of the project.
        """
        itm = self.currentItem()
        fn = unicode(itm.fileName())
        if self.project.renameFile(fn):
            self.removeNode(itm)
            try:
                self.children.remove(itm)
            except ValueError:
                pass
            del itm
            self.rebuildTree()
        
    def handleRemove(self):
        """
        Private slot to remove the selected entry from the OTHERS project data area.
        """
        itmList = self.getSelectedItems()
        
        for itm in itmList[:]:
            fn2 = unicode(itm.fileName())
            fn = fn2.replace(self.project.ppath+os.sep, '')
            if fn in self.project.pdata["OTHERS"]:
                if isinstance(itm, BrowserFile):
                    self.emit(PYSIGNAL('closeSourceWindow'), (fn2,))
                    
                self.removeNode(itm)
                self.children.remove(itm)
                del itm
                
                self.project.pdata["OTHERS"].remove(fn)
                self.project.setDirty(1)
        
    def handleDelete(self):
        """
        Private method to delete the selected entry from the OTHERS project data area.
        """
        itmList = self.getSelectedItems()
        
        items = []
        files = []
        fullNames = []
        dirItems = []
        dirNames = []
        dirFullNames = []
        for itm in itmList:
            fn2 = unicode(itm.fileName())
            fn = fn2.replace(self.project.ppath+os.sep, '')
            if isinstance(itm, BrowserFile):
                items.append(itm)
                fullNames.append(fn2)
                files.append(fn)
            else:
                dirItems.append(itm)
                dirFullNames.append(fn2)
                dirNames.append(fn)
        items.extend(dirItems)
        fullNames.extend(dirFullNames)
        files.extend(dirNames)
        del itmList
        del dirFullNames
        del dirNames
        
        dlg = DeleteFilesConfirmationDialog(self.parent(),
            self.trUtf8("Delete files/directories"),
            self.trUtf8("Do you really want to delete these entries from the project?"),
            self.trUtf8("&Yes"), self.trUtf8("&No"), files)
        
        if dlg.exec_loop() == QDialog.Accepted:
            for itm, fn2, fn in zip(items[:], fullNames, files):
                if isinstance(itm, BrowserFile):
                    self.emit(PYSIGNAL('closeSourceWindow'), (fn2,))
                    try:
                        os.remove(fn2)
                    except:
                        KQMessageBox.critical(None,
                            self.trUtf8("Delete file"),
                            self.trUtf8("<p>The selected file <b>%1</b> could not be deleted.</p>")
                                .arg(fn),
                            self.trUtf8("&OK"))
                        return
                elif isinstance(itm, ProjectBrowserDirectory):
                    try:
                        shutil.rmtree(fn2)
                    except:
                        KQMessageBox.critical(None,
                            self.trUtf8("Delete directory"),
                            self.trUtf8("<p>The selected directory <b>%1</b> could not be deleted.</p>")
                                .arg(fn),
                            self.trUtf8("&OK"))
                        return
                
                self.removeNode(itm)
                try:
                    self.children.remove(itm)
                except ValueError:
                    pass
                del itm
                
                if fn in self.project.pdata["OTHERS"]:
                        self.project.pdata["OTHERS"].remove(fn)
                        self.project.setDirty(1)

    def addNode(self, name):
        """
        Public slot to add a node to this browser.
        
        @param name filename or directory of this node
        """
        name = unicode(name)
        if not os.path.isabs(name):
            fname = os.path.join(self.project.ppath, name)
        else:
            fname = name
        parent, dt = self.findParentNode(name)
        if os.path.isdir(fname):
            node = ProjectBrowserDirectory(parent, fname, None, 0, 1, self.project.vcs)
        else:
            node = BrowserFile(parent, fname, None, 1, dt, 1)
        self.children.append(node)
        self.nodeAdded(node, fname)
        
    def handleExpandAllDirs(self):
        """
        Protected slot to handle the 'Expand all directories' menu action.
        """
        itm = self.firstChild()
        while itm is not None:
            if (isinstance(itm, ProjectBrowserSimpleDir) or \
               isinstance(itm, ProjectBrowserDirectory)) and not itm.isOpen():
                itm.setOpen(1)
            itm = itm.itemBelow()
            
    def handleCollapseAllDirs(self):
        """
        Protected slot to handle the 'Collapse all directories' menu action.
        """
        itm = self.lastItem()
        while itm is not None:
            if (isinstance(itm, ProjectBrowserSimpleDir) or \
               isinstance(itm, ProjectBrowserDirectory)) and itm.isOpen():
                itm.setOpen(0)
            itm = itm.itemAbove()
        
    def __itemsHaveFiles(self, items):
        """
        Private method to check, if items contain file type items.
        
        @param items items to check (list of QListViewItems)
        @return flag indicating items contain file type items (boolean)
        """
        for itm in items:
            if isinstance(itm, BrowserFile):
                return 1
        return 0
        
    def __itemsHaveDirectories(self, items):
        """
        Private method to check, if items contain directory type items.
        
        @param items items to check (list of QListViewItems)
        @return flag indicating items contain directory type items (boolean)
        """
        for itm in items:
            if isinstance(itm, ProjectBrowserDirectory):
                return 1
        return 0
