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

# Copyright (c) 2002 - 2006 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 import Checkers

from UI.E3Action import E3Action
from UI.E3TabWidget import E3TabWidget
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(E3TabWidget):
    """
    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, qt4dir, 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
        """
        E3TabWidget.__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, qt4dir, 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, qt4dir, 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", "Qt4", "Kde"]:
            self.setTabEnabled(self.pfBrowser, 1)
        else:
            self.setTabEnabled(self.pfBrowser, 0)
        if self.project.pdata["UITYPE"][0] in ["Qt", "Qt4"]:
            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,))
    
    def handleEditorChanged(self, fn):
        """
        Public slot to handle the captionChanged signal.
        
        @param fn The filename of the saved files. (string or QString)
        """
        if self.project.isProjectSource(fn):
            self.psBrowser.selectFile(fn)
        elif self.project.isProjectForm(fn):
            self.pfBrowser.selectFile(fn)
        elif self.project.isProjectInterface(fn):
            self.piBrowser.selectFile(fn)
    
    def getProjectBrowsers(self):
        """
        Public method to get references to the individual project browsers.
        
        @return list of references to project browsers
        """
        return [self.psBrowser, self.pfBrowser, self.ptBrowser,
                self.piBrowser, self.poBrowser]

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.isTranslationsBrowser = 0
        
        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.vcsHelper = self.project.vcs.vcsGetProjectBrowserHelper(self, 
                self.project, self.isTranslationsBrowser)
            self.vcsHelper.addVCSMenus(self.mainMenu, self.multiMenu,
                self.backMenu, self.dirMenu, 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.vcsHelper = self.project.vcs.vcsGetProjectBrowserHelper(self, 
                self.project, self.isTranslationsBrowser)
            self.vcsHelper.addVCSMenus(self.mainMenu, self.multiMenu,
                self.backMenu, self.dirMenu, 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 selectFile(self, fn):
        """
        Public method to highlight a node given its filename.
        
        @param fn filename of file to be highlighted (string or QString)
        """
        newfn = os.path.abspath(unicode(fn))
        newfn = newfn.replace(self.project.ppath + os.sep, '')
        pathlist = newfn.split(os.sep)
        node = None
        for p in pathlist:
            node = self.findItem(p, 0, node)
        if node is not None:
            self.clearSelection()
            self.setCurrentItem(node)
            self.setSelected(node, 1)
            self.ensureItemVisible(node)
        
    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 and \
               not self.project.hasEntry(parent.fileName()):
                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 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:
            self.vcsHelper.handleShowPopupMenu(menu, self.menuItems)
        
    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:
            self.vcsHelper.handleShowPopupMenuMulti(menu, self.multiMenuItems)
        
    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:
            self.vcsHelper.handleShowPopupMenuDir(menu, self.dirMenuItems)
        
    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:
            self.vcsHelper.handleShowPopupMenuDirMulti(menu, self.dirMultiMenuItems)
        
    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"),
            QString.null,
            QString.null,
            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])

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)
        """
        self.checkers = Checkers(self, parent)
        
        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.checkers.initActions()
        self.checksMenu = self.checkers.initMenu()
        
        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 files...'), self.handleAddSourceFiles)
        self.pyMenu.insertItem(self.trUtf8('Add source directory...'), self.handleAddSourceDirectory)
        self.pyMenu.insertSeparator()
        itm = self.pyMenu.insertItem(self.trUtf8('Diagrams'), self.graphicsMenu)
        if not getConfig('qtcanvas'):
            self.pyMenu.setItemEnabled(itm, False)
        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 files...'), self.handleAddSourceFiles)
        self.menu.insertItem(self.trUtf8('Add source directory...'), self.handleAddSourceDirectory)
        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 files...'), self.project.addPyFiles)
        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('Add source files...'), self.handleAddSourceFiles)
        self.dirMenu.insertItem(self.trUtf8('Add source directory...'), self.handleAddSourceDirectory)
        self.dirMenu.insertSeparator()
        itm = self.dirMenu.insertItem(self.trUtf8('Diagrams'), self.graphicsMenu)        
        if not getConfig('qtcanvas'):
            self.dirMenu.setItemEnabled(itm, False)
        self.dirMenu.insertSeparator()
        self.dirMenu.insertItem(self.trUtf8('Check'), self.checksMenu)
        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 files...'), self.handleAddSourceFiles)
        self.pyMenu.insertItem(self.trUtf8('Add source directory...'), self.handleAddSourceDirectory)
        self.pyMenu.insertSeparator()
        itm = self.pyMenu.insertItem(self.trUtf8('Diagrams'), self.graphicsMenu)
        if not getConfig('qtcanvas'):
            self.pyMenu.setItemEnabled(itm, False)
        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 files...'), self.handleAddSourceFiles)
        self.menu.insertItem(self.trUtf8('Add source directory...'), self.handleAddSourceDirectory)
        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 files...'), self.project.addPyFiles)
        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('Add source files...'), self.handleAddSourceFiles)
        self.dirMenu.insertItem(self.trUtf8('Add source directory...'), self.handleAddSourceDirectory)
        self.dirMenu.insertSeparator()
        itm = self.dirMenu.insertItem(self.trUtf8('Diagrams'), self.graphicsMenu)        
        if not getConfig('qtcanvas'):
            self.dirMenu.setItemEnabled(itm, False)
        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 handleAddSourceFiles(self):
        """
        Private method to add a source file to the project.
        """
        itm = self.currentItem()
        if isinstance(itm, BrowserFile) or \
           isinstance(itm, BrowserClass) or \
           isinstance(itm, BrowserMethod):
            dn = os.path.dirname(unicode(itm.fileName()))
        elif isinstance(itm, ProjectBrowserSimpleDir) or \
             isinstance(itm, BrowserDirectory):
            dn = unicode(itm.dirName())
        else:
            dn = None
        self.project.addFiles('py', dn)
        
    def handleAddSourceDirectory(self):
        """
        Private method to add source files of a directory to the project.
        """
        itm = self.currentItem()
        if isinstance(itm, BrowserFile) or \
           isinstance(itm, BrowserClass) or \
           isinstance(itm, BrowserMethod):
            dn = os.path.dirname(unicode(itm.fileName()))
        elif isinstance(itm, ProjectBrowserSimpleDir) or \
             isinstance(itm, BrowserDirectory):
            dn = unicode(itm.dirName())
        else:
            dn = None
        self.project.addDirectory('py', dn)
        
    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()
        if os.path.isdir(fn):
            fn = [f for f in self.project.getSources(1) if f.startswith(fn)] 
        
        self.checkers.handleTabnanny(fn)
    
    def handleSyntaxCheck(self):
        """
        Private method to handle the syntax check context menu action.
        """
        itm = self.currentItem()
        fn = itm.fileName()
        if os.path.isdir(fn):
            fn = [f for f in self.project.getSources(1) if f.startswith(fn)] 
        
        self.checkers.handleSyntaxCheck(fn)
        
    def handlePyLint(self):
        """
        Private method to handle the syntax check context menu action.
        """
        itm = self.currentItem()
        fn = itm.fileName()
        self.checkers.handlePyLint(self.project, 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"),
            QString.null,
            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"),
            QString.null,
            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"),
            QString.null,
            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,qt4dir,parent=None):
        """
        Constructor
        
        @param project reference to the project object
        @param qtdir path of the Qt installation directory (string)
        @param qt4dir path of the Qt4 installation directory (string)
        @param parent parent widget of this browser (QWidget)
        """
        self.qtdir = qtdir
        self.qt4dir = qt4dir
        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)
        
        # repeat for Qt4
        # these two lists have to stay in sync
        self.templates4 = ['dialog4.tmpl', 'widget4.tmpl', 'mainwindow4.tmpl', \
            'dialogbuttonsbottom4.tmpl', 'dialogbuttonsbottomcenter4.tmpl',
            'dialogbuttonsright4.tmpl']
        self.templateTypes4 = [ \
            unicode(self.trUtf8("Dialog")),
            unicode(self.trUtf8("Widget")),
            unicode(self.trUtf8("Main Window")),
            unicode(self.trUtf8("Dialog with Buttons (Bottom)")),
            unicode(self.trUtf8("Dialog with Buttons (Bottom-Center)")),
            unicode(self.trUtf8("Dialog with Buttons (Right)")),
        ]
        self.formTypeList4 = QStringList()
        for tType in self.templateTypes4:
            self.formTypeList4.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 or self.qt4dir 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)
            self.subclassItm = \
                self.menu.insertItem(self.trUtf8('Generate Subclass'), self.handleSubclass)
            self.formItems.append(self.subclassItm)
            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()
        itm1 = self.menu.insertItem(self.trUtf8('Preview form'), self.handleUIPreview)
        itm2 = self.menu.insertItem(self.trUtf8('Preview translations'), self.handleTRPreview)
        if not getConfig('qtui'):
            self.menu.setItemEnabled(itm1, False)
            self.menu.setItemEnabled(itm2, False)
        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 or self.qt4dir is not None:
            self.menu.insertItem(self.trUtf8('New form...'), self.handleNewForm)
        self.menu.insertItem(self.trUtf8('Add forms...'), self.handleAddFormFiles)
        self.menu.insertItem(self.trUtf8('Add forms directory...'), self.handleAddFormsDirectory)
        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 or self.qt4dir 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 forms...'), self.project.addUiFiles)
        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 or self.qt4dir 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()
        itm = self.multiMenu.insertItem(self.trUtf8('Preview translations'), self.handleTRPreview)
        if not getConfig('qtui'):
            self.multiMenu.setItemEnabled(itm, False)
        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 or self.qt4dir 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 or self.qt4dir is not None:
            self.dirMenu.insertItem(self.trUtf8('New form...'), self.handleNewForm)
        self.dirMenu.insertItem(self.trUtf8('Add forms...'), self.handleAddFormFiles)
        self.dirMenu.insertItem(self.trUtf8('Add forms directory...'), self.handleAddFormsDirectory)
        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 or self.qt4dir is not None:
            self.dirMultiMenu.insertItem(self.trUtf8('Compile all forms'), self.handleCompileAll)
            self.dirMultiMenu.insertSeparator()
        self.dirMultiMenu.insertItem(self.trUtf8('Add forms...'), self.project.addUiFiles)
        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)
        if self.project.pdata["UITYPE"][0] == "Qt4":
            self.menu.setItemEnabled(self.subclassItm, False)
        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 handleAddFormFiles(self):
        """
        Private method to add form files to the project.
        """
        itm = self.currentItem()
        if isinstance(itm, BrowserFile):
            dn = os.path.dirname(unicode(itm.fileName()))
        elif isinstance(itm, ProjectBrowserSimpleDir) or \
             isinstance(itm, BrowserDirectory):
            dn = unicode(itm.dirName())
        else:
            dn = None
        self.project.addFiles('ui', dn)
        
    def handleAddFormsDirectory(self):
        """
        Private method to add source files of a directory to the project.
        """
        itm = self.currentItem()
        if isinstance(itm, BrowserFile):
            dn = os.path.dirname(unicode(itm.fileName()))
        elif isinstance(itm, ProjectBrowserSimpleDir) or \
             isinstance(itm, BrowserDirectory):
            dn = unicode(itm.dirName())
        else:
            dn = None
        self.project.addDirectory('ui', dn)
        
    def handleOpen(self):
        """
        Private slot to handle the Open menu action.
        
        This uses the projects UI type to determine the Qt Designer
        version to use.
        """
        if self.project.getUiType() == "Qt4":
            version = 4
        else:
            version = 3
        
        itmList = self.getSelectedItems()
        for itm in itmList[:]:
            try:
                if isinstance(itm, BrowserFile):
                    self.emit(PYSIGNAL('designerFile'),(itm.fileName(),version))
            except:
                pass
        
    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.project.pdata["UITYPE"][0] == "Qt4" and self.formTypeList4 or self.formTypeList,
            0, 0)
        if not ok:
            # user pressed cancel
            return
            
        templateIndex = self.project.pdata["UITYPE"][0] == "Qt4" and \
                        self.templateTypes4.index(unicode(selectedForm)) or \
                        self.templateTypes.index(unicode(selectedForm))
        templateFile = os.path.join(getConfig('ericTemplatesDir'),
            self.project.pdata["UITYPE"][0] == "Qt4" and \
            self.templates4[templateIndex] or \
            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"),
                QString.null, 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/rbuic 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/rbuic process.
        """
        while self.compileProc and self.compileProc.canReadLineStderr():
            s = QString(self.uicompiler + ': ')
            s.append(self.compileProc.readLineStderr())
            self.emit(PYSIGNAL('appendStderr'), (s,))
        
    def handleCompileUIDone(self):
        """
        Private slot to handle the processExit signal of the pyuic/rbuic process.
        """
        self.compileRunning = 0
        qApp.mainWidget().getViewManager().enableEditorsCheckFocusIn(1)
        if self.compileProc.normalExit() and \
           (self.project.pdata["UITYPE"][0] == "Qt4" or self.buf):
            if self.subclass:
                vm = self.project.parent().getViewManager()
                vm.newEditor()
                aw = vm.activeWindow()
                aw.insertAt(self.buf, 0, 0)
                if self.project.pdata["PROGLANGUAGE"][0] == "Python":
                   aw.setLanguage('dummy.py')
                elif self.project.pdata["PROGLANGUAGE"][0] == "Ruby":
                   aw.setLanguage('dummy.rb')
            else:
                kdeUI = self.project.pdata["UITYPE"][0] == "Kde"
                ofn = os.path.join(self.project.ppath, self.compiledFile)
                try:
                    if self.project.pdata["UITYPE"][0] != "Qt4":
                        f = open(ofn, "wb")
                        for line in unicode(self.buf).splitlines():
                            if kdeUI and self.project.pdata["PROGLANGUAGE"][0] == "Python":
                                # The following code was taken from 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/.rb 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()
        
        if self.project.pdata["PROGLANGUAGE"][0] == "Python":
            if self.project.pdata["UITYPE"][0] == "Qt4":
                self.uicompiler = 'pyuic4'
            else:
                self.uicompiler = 'pyuic'
        elif self.project.pdata["PROGLANGUAGE"][0] == "Ruby":
            self.uicompiler = 'rbuic'
        
        uic = self.uicompiler
        if sys.platform == "win32":
            uic = uic + '.exe'
        self.compileProc.addArgument(uic)
        
        (ofn, ext) = os.path.splitext(fn)
        fn = os.path.join(self.project.ppath, fn)
        
        self.buf = QString("")
        if subclass:
            if self.project.pdata["UITYPE"][0] == "Qt4":
                KQMessageBox.critical(self,
                    self.trUtf8('Error compiling form'),
                    self.trUtf8(
                        '<p>Subclass generation is not supported for Qt4.</p>'
                    ).arg(fn),
                    self.trUtf8('OK'))
                return None
            
            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)
            if self.project.pdata["PROGLANGUAGE"][0] == "Ruby" and \
               self.project.pdata["UITYPE"][0] == "Kde":
                self.compileProc.addArgument('-kde')
            path, fn = os.path.split(fn)
            self.compileProc.setWorkingDirectory(QDir(path))
        else:
            self.subclass = 0
            if self.project.pdata["PROGLANGUAGE"][0] == "Python":
                if self.project.pdata["UITYPE"][0] == "Qt4":
                    dirname, filename = os.path.split(ofn)
                    self.compiledFile = os.path.join(dirname, "Ui_" + filename + ".py")
                else:
                    self.compiledFile = ofn + '.py'
            elif self.project.pdata["PROGLANGUAGE"][0] == "Ruby":
                self.compiledFile = ofn + '.rb'
                if self.project.pdata["UITYPE"][0] == "Kde":
                    self.compileProc.addArgument('-kde')
            self.compileProc.addArgument('-x')
            
        self.compileProc.addArgument(fn)
        if self.project.pdata["UITYPE"][0] == "Qt4":
            self.compileProc.addArgument("-o")
            self.compileProc.addArgument(os.path.join(self.project.ppath, self.compiledFile))
        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 %1.<br>'
                    'Ensure that it is in the search path.'
                ).arg(self.uicompiler),
                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..."), 
            QString.null, 100, None, "progress")
        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)
                if self.project.pdata["PROGLANGUAGE"][0] == "Python":
                    if self.project.pdata["UITYPE"][0] == "Qt4":
                        dirname, filename = os.path.split(os.path.splitext(ifn)[0])
                        ofn = os.path.join(dirname, "Ui_" + filename + ".py")
                    else:
                        ofn = os.path.splitext(ifn)[0] + '.py'
                elif self.project.pdata["PROGLANGUAGE"][0] == "Ruby":
                    ofn = os.path.splitext(ifn)[0] + '.rb'
                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)
        """
        fn = unicode(fn)
        
        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,qt4dir,parent=None):
        """
        Constructor
        
        @param project reference to the project object
        @param qtdir path of the Qt installation directory (string)
        @param qt4dir path of the Qt4 installation directory (string)
        @param parent parent widget of this browser (QWidget)
        """
        self.qtdir = qtdir
        self.qt4dir = qt4dir
        PBrowser.__init__(self,project,"TRANSLATIONS",parent)
        self.isTranslationsBrowser = 1
    
        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 or self.qt4dir 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()
        itm = self.menu.insertItem(self.trUtf8('Preview translation'), self.handleTRPreview)
        if not getConfig('qtui'):
            self.menu.setItemEnabled(itm, False)
        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 or self.qt4dir 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()
        itm = self.backMenu.insertItem(self.trUtf8('Preview translations'), self.handleTRPreview)
        if not getConfig('qtui'):
            self.backMenu.setItemEnabled(itm, False)
        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 or self.qt4dir 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()
        itm = self.multiMenu.insertItem(self.trUtf8('Preview translations'), self.handleTRPreview)
        if not getConfig('qtui'):
            self.multiMenu.setItemEnabled(itm, False)
        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 or self.qt4dir 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()
        itm = self.dirMenu.insertItem(self.trUtf8('Preview translations'), self.handleTRPreview)
        if not getConfig('qtui'):
            self.dirMenu.setItemEnabled(itm, False)
        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()
        
        if self.project.pdata["UITYPE"][0] == "Qt4":
            pylupdate = 'pylupdate4'
        else:
            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()
        
        if self.project.pdata["UITYPE"][0] == "Qt4":
            lrelease = os.path.join(self.qt4dir, 'bin', 'lrelease')
        else:
            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)

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 interfaces...'), self.handleAddInterfaceFiles)
        self.menu.insertItem(self.trUtf8('Add interfaces directory...'), self.handleAddInterfacesDirectory)
        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 interfaces...'), self.project.addIdlFiles)
        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 interfaces...'), self.handleAddInterfaceFiles)
        self.dirMenu.insertItem(self.trUtf8('Add interfaces directory...'), self.handleAddInterfacesDirectory)
        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 interfaces...'), self.project.addIdlFiles)
        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 handleAddInterfaceFiles(self):
        """
        Private method to add interface files to the project.
        """
        itm = self.currentItem()
        if isinstance(itm, BrowserFile) or \
           isinstance(itm, BrowserClass) or \
           isinstance(itm, BrowserMethod):
            dn = os.path.dirname(unicode(itm.fileName()))
        elif isinstance(itm, ProjectBrowserSimpleDir) or \
             isinstance(itm, BrowserDirectory):
            dn = unicode(itm.dirName())
        else:
            dn = None
        self.project.addFiles('idl', dn)
        
    def handleAddInterfacesDirectory(self):
        """
        Private method to add source files of a directory to the project.
        """
        itm = self.currentItem()
        if isinstance(itm, BrowserFile) or \
           isinstance(itm, BrowserClass) or \
           isinstance(itm, BrowserMethod):
            dn = os.path.dirname(unicode(itm.fileName()))
        elif isinstance(itm, ProjectBrowserSimpleDir) or \
             isinstance(itm, BrowserDirectory):
            dn = unicode(itm.dirName())
        else:
            dn = None
        self.project.addDirectory('idl', dn)
        
    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 omniidl 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 omniidl 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)
        """
        fn = unicode(fn)
        
        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 files...'), self.project.addOthersFiles)
        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 files...'), self.project.addOthersFiles)
        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:
            self.vcsHelper.handleShowPopupMenu(menu, self.menuItems)
        
    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:
            self.vcsHelper.handleShowPopupMenuMulti(menu, self.multiMenuItems)
        
    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()
