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

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

"""
Module implementing a browser with class browsing capabilities.

The class browsing part is based on IDLE's classbrowser.
"""

import sys
import string
import os

from qt import *

from KdeQt import KQFileDialog

import Utilities.ClassBrowsers
import Utilities.ClassBrowsers.ClbrBaseClasses

import UI.PixmapCache
import Preferences


class BrowserNode(QListViewItem):
    """
    Class implementing a thin wrapper around QListViewItem. 
    
    It makes sure that it is positioned after the previous sibling.  
    """
    def __init__(self,parent,text,after,bold=0):
        """
        Constructor
        
        @param parent parent Browser or BrowserNode
        @param text text to be displayed by this node (string or QString)
        @param after sibling this node is positioned after
        @param bold flag indicating a highlighted font
        """
        if text is None:
            text = QString.null
        self.bold = bold
        if after is None:
            QListViewItem.__init__(self,parent,text)
        else:
            QListViewItem.__init__(self,parent,after,text)

    def paintCell(self, p, cg, column, width, alignment):
        """
        Overwritten class to set a different text color, if bold is true.
        
        @param p the painter (QPainter)
        @param cg the color group (QColorGroup)
        @param column the column (int)
        @param width width of the cell (int)
        @param alignment alignment of the cell (int)
        """
        _cg = QColorGroup(cg)
        c = _cg.text()
        
        if self.bold and column == 0:
            _cg.setColor(QColorGroup.Text, Qt.red)
            
        QListViewItem.paintCell(self, p, _cg, column, width, alignment)
        
        _cg.setColor(QColorGroup.Text, c)

class BrowserDirectory(BrowserNode):
    """
    Class implementing a BrowserNode that represents a directory.  
    """
    def __init__(self,parent,dinfo,after,full=1,bold=0):
        """
        Constructor
        
        @param parent parent Browser or BrowserNode
        @param dinfo dinfo is the string for the directory (string or QString)
        @param after sibling this node is positioned after
        @param full flag indicating full pathname should be displayed (boolean)
        @param bold flag indicating a highlighted display (boolean)
        """
        self.dirName = os.path.abspath(unicode(dinfo))

        if full:
            dn = self.dirName
        else:
            dn = os.path.basename(self.dirName)

        BrowserNode.__init__(self,parent,dn,after,bold)

        self.setExpandable(1)
        self.setPixmap(0,UI.PixmapCache.getPixmap("dirClosed.png"))

        self.children = []

    def setOpen(self,o):
        """
        Public slot to set/reset the open state.
        
        @param o flag indicating the open state
        """
        if o:
            self.dir = QDir(self.dirName)
            last = None

            if Preferences.getUI("BrowsersListFoldersFirst"):
                sortSpec = QDir.DirsFirst
            else:
                sortSpec = QDir.DefaultSort
            for f in self.dir.entryInfoList(QDir.DefaultFilter, sortSpec):
                if str(f.fileName()) in ('.', '..'):
                    continue

                if f.isDir():
                    node = BrowserDirectory(self,
                        unicode(QDir.convertSeparators(f.absFilePath())),last,0)
                else:
                    node = BrowserFile(self,
                        unicode(QDir.convertSeparators(f.absFilePath())),last)

                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 fileName(self):
        """
        Public method returning the directory name of this node.
        
        @return directory name (string)
        """
        return self.dirName

    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 BrowserSysPath(BrowserNode):
    """
    Class implementing a BrowserNode that represents the Python sys.path.  
    """
    def __init__(self,parent,after):
        """
        Constructor
        
        @param parent parent Browser or BrowserNode
        @param after sibling this node is positioned after
        """
        BrowserNode.__init__(self,parent,'sys.path',after)

        self.setExpandable(1)
        self.setPixmap(0,UI.PixmapCache.getPixmap("filePython.png"))

        self.doneOpen = 0
        self.children = []

    def setOpen(self,o):
        """
        Public slot to set/reset the open state.
        
        @param o flag indicating the open state
        """
        if o and not self.doneOpen:
            last = None

            for p in sys.path:
                if p == '':
                    p = os.getcwd()

                node = BrowserDirectory(self,p,last)
                last = node
                self.children.append(node)

            self.doneOpen = 1

        QListViewItem.setOpen(self,o)


class BrowserFile(BrowserNode):
    """
    Class implementing a BrowserNode that represents a file.  
    """
    def __init__(self,parent,finfo,after,special=0,dtext=QString.null,bold=0,isPyFile=0):
        """
        Constructor
        
        @param parent parent Browser or BrowserNode
        @param finfo the string for the file (string)
        @param after sibling this node is positioned after
        @param special flag indicating special treatment (boolean)
        @param dtext display text if special is set (string or QString)
        @param bold flag indicating a highlighted display (boolean)
        @param isPyFile flag indicating that this is a Python file
                even if it doesn't have the .py extension (boolean)
        """
        if dtext is None:
            dtext = QString.null
        if special:
            BrowserNode.__init__(self,parent,dtext,after,bold)
        else:
            BrowserNode.__init__(self,parent,os.path.basename(finfo),after,bold)

        if finfo.endswith('.ui.h'):
            self.fileext = '.ui.h'
        else:
            dummy, self.fileext = os.path.splitext(finfo)
        self.children = []
        self.fn = os.path.abspath(finfo)
        
        if not self.isRubyFile() and isPyFile:
            self.pyFile = 1
        else:
            self.pyFile = 0
        
        if self.isPythonFile():
            if self.fileext == '.py':
                self.setPixmap(0,UI.PixmapCache.getPixmap("filePython.png"))
            else:
                self.setPixmap(0,UI.PixmapCache.getPixmap("filePython2.png"))
            self.setExpandable(1)
            self.moduleName = os.path.basename(finfo)
            self.dirName = os.path.dirname(finfo)
        elif self.isRubyFile():
            self.setPixmap(0,UI.PixmapCache.getPixmap("fileRuby.png"))
            self.setExpandable(1)
            self.moduleName = os.path.basename(finfo)
            self.dirName = os.path.dirname(finfo)
        elif self.isDesignerFile() or self.isDesignerHeaderFile():
            self.setPixmap(0,UI.PixmapCache.getPixmap("fileDesigner.png"))
        elif self.isLinguistFile():
            self.setPixmap(0,UI.PixmapCache.getPixmap("fileLinguist.png"))
        elif self.isProjectFile():
            self.setPixmap(0,UI.PixmapCache.getPixmap("fileProject.png"))
        elif self.isIdlFile():
            self.setPixmap(0,UI.PixmapCache.getPixmap("fileIDL.png"))
            self.setExpandable(1)
            self.moduleName = os.path.basename(finfo)
            self.dirName = os.path.dirname(finfo)
        elif self.isPixmapFile():
            self.setPixmap(0,UI.PixmapCache.getPixmap("filePixmap.png"))
        else:
            self.setPixmap(0,UI.PixmapCache.getPixmap("fileMisc.png"))

    def fileName(self):
        """
        Public method returning the filename.
        
        @return filename (string)
        """
        return self.fn
        
    def isPythonFile(self):
        """
        Public method to check, if this file is a Python script.
        
        @return flag indicating a Python file (boolean)
        """
        return self.fileext in ['.py', '.ptl'] or self.pyFile
        
    def isRubyFile(self):
        """
        Public method to check, if this file is a Ruby script.
        
        @return flag indicating a Ruby file (boolean)
        """
        return self.fileext == '.rb'
        
    def isDesignerFile(self):
        """
        Public method to check, if this file is a Qt-Designer file.
        
        @return flag indicating a Qt-Designer file (boolean)
        """
        return self.fileext == '.ui'
        
    def isDesignerHeaderFile(self):
        """
        Public method to check, if this file is a Qt-Designer header file.
        
        @return flag indicating a Qt-Designer header file (boolean)
        """
        return self.fileext == '.ui.h'
        
    def isLinguistFile(self):
        """
        Public method to check, if this file is a Qt-Linguist.
        
        @return flag indicating a Qt-Linguist file (boolean)
        """
        return self.fileext == '.ts'
        
    def isProjectFile(self):
        """
        Public method to check, if this file is an eric3 project file.
        
        @return flag indicating an eric3 project file (boolean)
        """
        return self.fileext in ['.e3p', '.e3pz']
        
    def isIdlFile(self):
        """
        Public method to check, if this file is a CORBA IDL file.
        
        @return flag indicating a CORBA IDL file (boolean)
        """
        return self.fileext == '.idl'
        
    def isPixmapFile(self):
        """
        Public method to check, if this file is a pixmap file.
        
        @return flag indicating a pixmap file (boolean)
        """
        return self.fileext in ['.png', '.jpg', '.xpm', '.bmp', '.xbm',
                                '.pnm', '.pbm', '.pgm', '.ppm', '.gif',
                                '.mng']
        
    def setOpen(self,o):
        """
        Public slot to set/reset the open state.
        
        @param o flag indicating the open state
        """
        if not self.isPythonFile() and \
           not self.isRubyFile() and \
           not self.isIdlFile():
            return
            
        if o:
            try:
                dict = Utilities.ClassBrowsers.readmodule(self.moduleName, [self.dirName])
            except ImportError:
                return
                
            hideNonPublic = Preferences.getUI("BrowsersHideNonPublic")
            
            items = []
            classes = {}
            last = None
            keys = dict.keys()
            keys.sort()
            for key in keys:
                cl = dict[key]
                try:
                    if cl.module == self.moduleName:
                        if hideNonPublic and  not cl.isPublic():
                            continue
                        node = BrowserClass(self, cl, last, self.fn)
                        self.children.append(node)
                        last = node
                except AttributeError:
                    pass
        else:
            for child in self.children:
                self.takeItem(child)
                del child
            self.children = []
                
        QListViewItem.setOpen(self,o)
        
    def compare(self, item, col, ascending):
        """
        Private method to compare two items.
        """
        if not issubclass(item.__class__, BrowserFile):
            if Preferences.getUI("BrowsersListFoldersFirst"):
                return ascending and 1 or -1
                
        if issubclass(item.__class__, BrowserFile):
            if self.fn.endswith('__init__.py'):
                return ascending and -1 or 1
            if item.fileName().endswith('__init__.py'):
                return ascending and 1 or -1
        
        return QListViewItem.compare(self, item, col, ascending)


class BrowserClass(BrowserNode):
    """
    Class implementing a BrowserNode that represents a python class or function. 
    """
    def __init__(self, parent, cl, after, filename):
        """
        Constructor
        
        @param parent parent Browser or BrowserNode
        @param cl Class object to be shown
        @param after sibling this node is positioned after
        @param filename filename of the file defining this class
        """
        name = cl.name
        if hasattr(cl, 'super') and cl.super:
            supers = []
            for sup in cl.super:
                try:
                    sname = sup.name
                    if sup.module != cl.module:
                        sname = "%s.%s" % (sup.module, sname)
                except:
                    sname = sup
                supers.append(sname)
            name = name + "(%s)" % string.join(supers, ", ")

        BrowserNode.__init__(self,parent,name,after)
        
        self.name = name
        self.cl = cl
        self.file = filename
        self.children = []
        
        self.isfunction = isinstance(self.cl, 
                                     Utilities.ClassBrowsers.ClbrBaseClasses.Function)
        self.ismodule = isinstance(self.cl, 
                                   Utilities.ClassBrowsers.ClbrBaseClasses.Module)
        if self.isfunction:
            if cl.isPrivate():
                pix = UI.PixmapCache.getPixmap("method_private.png")
            elif cl.isProtected():
                pix = UI.PixmapCache.getPixmap("method_protected.png")
            else:
                pix = UI.PixmapCache.getPixmap("method.png")
            self.setPixmap(0, pix)
            self.setText(0, "%s(%s)" % (name, ", ".join(self.cl.parameters)))
            # if no defaults are wanted
            # ... % (name, ", ".join([e.split('=')[0].strip() for e in self.cl.parameters]))
        elif self.ismodule:
            pix = UI.PixmapCache.getPixmap("module.png")
            self.setPixmap(0, pix)
            self.setText(0, name)
        else:
            if cl.isPrivate():
                pix = UI.PixmapCache.getPixmap("class_private.png")
            elif cl.isProtected():
                pix = UI.PixmapCache.getPixmap("class_protected.png")
            else:
                pix = UI.PixmapCache.getPixmap("class.png")
            self.setPixmap(0, pix)
            self.setText(0, name)
        if self.cl and \
           (self.cl.methods or self.cl.classes or self.cl.attributes):
            self.setExpandable(1)
                
    def setOpen(self, o):
        """
        Public slot to set/reset the open state.
        
        @param o flag indicating the open state
        """
        if o:
            if self.cl is None:
                return
            
            hideNonPublic = Preferences.getUI("BrowsersHideNonPublic")
            
            # build sorted list of names
            keys = []
            for name in self.cl.classes.keys():
                keys.append((name, 'c'))
            for name in self.cl.methods.keys():
                keys.append((name, 'm'))
            keys.sort()
                
            last = None
            for key, kind in keys:
                if hideNonPublic and key.startswith('_'):
                    continue
                if kind == 'c':
                    node = BrowserClass(self, self.cl.classes[key], last, self.file)
                elif kind == 'm':
                    node = BrowserMethod(self, self.cl.methods[key], last, self.file)
                self.children.append(node)
                last = node
                
            if len(self.cl.attributes):
                node = BrowserClassAttributes(self, last, self.cl.attributes)
                node.setPixmap(0, UI.PixmapCache.getPixmap("attributes.png"))
                self.children.append(node)
        else:
            for child in self.children:
                self.takeItem(child)
                del child
            self.children = []

        QListViewItem.setOpen(self,o)

    def fileName(self):
        """
        Public method returning the name of the file defining this class.
        
        @return filename (string)
        """
        return self.file


class BrowserClassAttributes(BrowserNode):
    """
    Class implementing a BrowserNode that represents the attributes of a python class. 
    """
    def __init__(self, parent, after, attributes):
        """
        Constructor
        
        @param parent parent Browser or BrowserNode
        @param after sibling this node is positioned after
        @param attributes list of attribute names
        """
        BrowserNode.__init__(self, parent, qApp.translate("BrowserClassAttributes", "Attributes"), after)
        
        self.attributes = attributes.copy()
        self.children = []
        
        self.setExpandable(1)
        
    def setOpen(self, o):
        """
        Public slot to set/reset the open state.
        
        @param o flag indicating the open state
        """
        if o:
            if not self.attributes:
                return
            
            hideNonPublic = Preferences.getUI("BrowsersHideNonPublic")
            
            last = None
            keys = self.attributes.keys()
            keys.sort()
            for key in keys:
                attrib = self.attributes[key]
                if hideNonPublic and not attrib.isPublic():
                    continue
                if attrib.isPrivate():
                    pix = UI.PixmapCache.getPixmap("attribute_private.png")
                elif attrib.isProtected():
                    pix = UI.PixmapCache.getPixmap("attribute_protected.png")
                else:
                    pix = UI.PixmapCache.getPixmap("attribute.png")
                    
                node = BrowserNode(self, attrib.name, last)
                node.setPixmap(0, pix)
                self.children.append(node)
                last = node
        else:
            for child in self.children:
                self.takeItem(child)
                del child
            self.children = []

        QListViewItem.setOpen(self,o)


class BrowserMethod(BrowserNode):
    """
    Class implementing a BrowserNode that represents a python method.
    """
    def __init__(self,parent,fn,after,filename):
        """
        Constructor
        
        @param parent parent Browser or BrowserNode
        @param fn Function object to be shown
        @param after sibling this node is positioned after
        @param filename filename of the file defining this method
        """
        name = fn.name
        
        BrowserNode.__init__(self,parent,name,after)
        
        self.name = name
        self.fn = fn
        self.file = filename
        self.children = []
        if fn.isPrivate():
            pix = UI.PixmapCache.getPixmap("method_private.png")
        elif fn.isProtected():
            pix = UI.PixmapCache.getPixmap("method_protected.png")
        else:
            pix = UI.PixmapCache.getPixmap("method.png")
        self.setPixmap(0, pix)
        self.setText(0, "%s(%s)" % (name, ", ".join(self.fn.parameters)))
        # if no defaults are wanted
        # ... % (name, ", ".join([e.split('=')[0].strip() for e in self.fn.parameters]))
        if self.fn and \
           (self.fn.methods or self.fn.classes):
            self.setExpandable(1)

    def setOpen(self, o):
        """
        Public slot to set/reset the open state.
        
        @param o flag indicating the open state
        """
        if o:
            if self.fn is None:
                return
            
            hideNonPublic = Preferences.getUI("BrowsersHideNonPublic")
            
            # build sorted list of names
            keys = []
            for name in self.fn.classes.keys():
                keys.append((name, 'c'))
            for name in self.fn.methods.keys():
                keys.append((name, 'm'))
            keys.sort()
                
            last = None
            for key, kind in keys:
                if hideNonPublic and key.startswith('_'):
                    continue
                if kind == 'c':
                    node = BrowserClass(self, self.fn.classes[key], last, self.file)
                elif kind == 'm':
                    node = BrowserMethod(self, self.fn.methods[key], last, self.file)
                self.children.append(node)
                last = node
                
        else:
            for child in self.children:
                self.takeItem(child)
                del child
            self.children = []

        QListViewItem.setOpen(self,o)
        
    def compare(self, item, col, ascending):
        """
        Private method to compare two items.
        """
        if issubclass(item.__class__, BrowserMethod):
            if self.name.startswith('__init__'):
                return ascending and -1 or 1
            if item.name.startswith('__init__'):
                return ascending and 1 or -1
        
        return QListViewItem.compare(self, item, col, ascending)

    def fileName(self):
        """
        Public method returning the name of the file defining this class.
        
        @return filename (string)
        """
        return self.file


class Browser(QListView):
    """
    Class used to display a file system tree. 
    
    Via the context menu that
    is displayed by a right click the user can select various actions on
    the selected file.
    
    @signal pythonFile(string, int) emitted to open a Python file at a line 
    @signal designerFile(string) emitted to open a Qt-Designer file
    @signal linguistFile(string) emitted to open a Qt-Linguist file
    @signal projectFile(string) emitted to open an eric3 project file
    @signal pixmapFile(string) emitted to open a pixmap file
    @signal unittestOpen(string) emitted to open a Python file for a unittest
    """
    def __init__(self,parent=None):
        """
        Constructor
        
        @param parent parent widget (QWidget)
        """
        QListView.__init__(self,parent)

        self.setCaption(self.trUtf8('File-Browser'))
        self.setIcon(UI.PixmapCache.getPixmap("eric.png"))
        
        self.setRootIsDecorated(1)
        self.setSorting(0)
        self.setShowSortIndicator(1)
        self.addColumn(self.trUtf8('Name'))
        self.setSelectionMode(QListView.Extended)
        self.selectedItemsFilter = [BrowserFile]

        self.progDir = None
        self.last = None
        
        self.hideNonPublic = Preferences.getUI("BrowsersHideNonPublic")

        node = BrowserSysPath(self,self.last)
        self.last = node
            
        self.toplevelDirs = QStringList()
        tdp, ok = Preferences.Prefs.settings.readListEntry('/eric3/Browser/ToplevelDirs')
        if ok:
            self.toplevelDirs = tdp
        else:
            self.toplevelDirs.append(QDir.convertSeparators(QDir.homeDirPath()))
            for d in QDir.drives():
                self.toplevelDirs.append(QDir.convertSeparators(d.absFilePath()))
        
        for d in self.toplevelDirs:
            node = BrowserDirectory(self,unicode(d),self.last)
            self.last = node

        self.connect(self,SIGNAL('contextMenuRequested(QListViewItem *, const QPoint &, int)'),
                     self.handleContextMenu)
        self.connect(self,SIGNAL('returnPressed(QListViewItem *)'),self.handleOpen)

        QWhatsThis.add(self,self.trUtf8(
            """<b>The Browser Window</b>"""
            """<p>This allows you to easily navigate the hierachy of directories and"""
            """ files on your system, identify the Python programs and open them up in"""
            """ a Source Viewer window. The window displays several separate"""
            """ hierachies.</p>"""
            """<p>The first hierachy is only shown if you have opened a program for"""
            """ debugging and it's root is the directory containing that program."""
            """ Usually all of the separate files that make up a Python application are"""
            """ held in the same directory, so this hierachy gives you easy access to"""
            """ most of what you will need.</p>"""
            """<p>The next hierachy is used to easily navigate the directories that are"""
            """ specified in the Python <tt>sys.path</tt> variable.</p>"""
            """<p>The remaining hierachies allow you navigate your system as a whole."""
            """ On a UNIX system there will be a hierachy with <tt>/</tt> at its"""
            """ root and another with the user home directory."""
            """ On a Windows system there will be a hierachy for each drive on the"""
            """ system.</p>"""
            """<p>Python programs (i.e. those with a <tt>.py</tt> file name suffix)"""
            """ are identified in the hierachies with a Python icon."""
            """ The right mouse button will popup a menu which lets you"""
            """ open the file in a Source Viewer window,"""
            """ open the file for debugging or use it for a unittest run.</p>"""
            """<p>The context menu of a class, function or method allows you to open the file"""
            """ defining this class, function or method and will ensure, that the"""
            """ correct source line is visible.</p>"""
            """<p>Qt-Designer files (i.e. those with a <tt>.ui</tt> file name suffix)"""
            """ are shown with a Designer icon. The context menu of these files"""
            """ allows you to start Qt-Designer with that file.</p>"""
            """<p>Qt-Linguist files (i.e. those with a <tt>.ts</tt> file name suffix)"""
            """ are shown with a Linguist icon. The context menu of these files"""
            """ allows you to start Qt-Linguist with that file.</p>"""
        ))
        
        self.createPopupMenus()
        
    def createPopupMenus(self):
        """
        Private method to generate the various popup menus.
        """
        # create the popup menu for python files
        self.pyMenu = QPopupMenu(self)
        self.pyMenu.insertItem(qApp.translate('Browser','Open'), self.handleOpen)
        self.unittestItem = self.pyMenu.insertItem(\
            qApp.translate('Browser','Run unittest...'), self.handleUnittest)
        
        # 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 directory menu
        self.dirMenu = QPopupMenu(self)
        self.dirMenu.insertItem(qApp.translate('Browser','New toplevel directory...'), 
            self.handleNewToplevelDir)
        self.addAsTopLevelItem = self.dirMenu.insertItem(\
            qApp.translate('Browser','Add as toplevel directory'),
            self.handleAddAsToplevelDir)
        self.removeFromToplevelItem = self.dirMenu.insertItem(\
            qApp.translate('Browser','Remove from toplevel'),
            self.handleRemoveToplevel)
            
        # create the background menu
        self.backMenu = QPopupMenu(self)
        self.backMenu.insertItem(qApp.translate('Browser','New toplevel directory...'), 
            self.handleNewToplevelDir)

    def contentsMouseDoubleClickEvent(self, mouseEvent):
        """
        Protected method of QListView. 
        
        Reimplemented to disable expanding/collapsing
        of items when double-clicking. Instead the double-clicked entry is opened.
        
        @param mouseEvent the mouse event (QMouseEvent)
        """
        self.handleOpen()

    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([BrowserDirectory, BrowserFile, 
                                              BrowserClass, BrowserMethod])
            bfcnt = self.getSelectedItemsCount()
            if cnt > 1 and cnt == bfcnt:
                self.multiMenu.popup(coord)
            elif cnt == 1:
                if isinstance(itm, BrowserFile):
                    if itm.isPythonFile():
                        if itm.fileName().endswith('.py'):
                            self.pyMenu.setItemEnabled(self.unittestItem, 1)
                        else:
                            self.pyMenu.setItemEnabled(self.unittestItem, 0)
                        self.pyMenu.popup(coord)
                    else:
                        self.menu.popup(coord)
                elif isinstance(itm,BrowserClass) or \
                        isinstance(itm,BrowserMethod):
                    self.menu.popup(coord)
                elif isinstance(itm,BrowserDirectory):
                    if itm.parent() is None:
                        self.dirMenu.setItemEnabled(self.removeFromToplevelItem, 1)
                        self.dirMenu.setItemEnabled(self.addAsTopLevelItem, 0)
                    else:
                        self.dirMenu.setItemEnabled(self.removeFromToplevelItem, 0)
                        self.dirMenu.setItemEnabled(self.addAsTopLevelItem, 1)
                    self.dirMenu.popup(coord)
                else:
                    self.backMenu.popup(coord)
            else:
                self.backMenu.popup(coord)
        except:
            pass
        
    def refreshTree(self):
        """
        Private slot to refresh the tree.
        
        The tree is refreshed by closing all open items and reopening them.
        """
        itm = self.lastItem()
        while itm is not None:
            if itm.isOpen():
                itm.setOpen(0)
                itm.setOpen(1)
            itm = itm.itemAbove()
        
    def handlePreferencesChanged(self):
        """
        Public slot used to handle the preferencesChanged signal.
        """
        hideNonPublic = Preferences.getUI("BrowsersHideNonPublic")
        if hideNonPublic != self.hideNonPublic:
            self.hideNonPublic = hideNonPublic
            self.refreshTree()
        
    def handleOpen(self):
        """
        Private slot to handle the open popup menu entry.
        """
        itmList = self.getSelectedItems([BrowserFile, BrowserClass, BrowserMethod])
        
        for itm in itmList:
            try:
                if isinstance(itm, BrowserFile):
                    if itm.isPythonFile():
                        self.emit(PYSIGNAL('pythonFile'),(itm.fileName(), -1))
                    elif itm.isRubyFile():
                        self.emit(PYSIGNAL('pythonFile'),(itm.fileName(),))
                    elif itm.isDesignerFile():
                        self.emit(PYSIGNAL('designerFile'),(itm.fileName(),))
                    elif itm.isDesignerHeaderFile():
                        self.emit(PYSIGNAL('pythonFile'),(itm.fileName(),))
                    elif itm.isLinguistFile():
                        self.emit(PYSIGNAL('linguistFile'),(itm.fileName(),))
                    elif itm.isProjectFile():
                        self.emit(PYSIGNAL('projectFile'),(itm.fileName(),))
                    elif itm.isIdlFile():
                        self.emit(PYSIGNAL('pythonFile'),(itm.fileName(),))
                    elif itm.isPixmapFile():
                        self.emit(PYSIGNAL('pixmapFile'),(itm.fileName(),))
                    else:
                        self.emit(PYSIGNAL('pythonFile'),(itm.fileName(),))
                elif isinstance(itm,BrowserClass):
                    self.emit(PYSIGNAL('pythonFile'),(itm.file, itm.cl.lineno))
                elif isinstance(itm,BrowserMethod):
                    self.emit(PYSIGNAL('pythonFile'),(itm.file, itm.fn.lineno))
                
            except:
                pass

    def handleUnittest(self):
        """
        Private slot to handle the unittest popup menu entry.
        """
        try:
            pyfn = self.currentItem().fileName()
        except:
            pyfn = None

        if pyfn is not None:
            self.emit(PYSIGNAL('unittestOpen'),(pyfn,))
        
    def handleNewToplevelDir(self):
        """
        Private slot to handle the New toplevel directory popup menu entry.
        """
        d = KQFileDialog.getExistingDirectory(\
            QString.null,
            None, None,
            self.trUtf8("New toplevel directory"),
            1, 1)
        if not d.isEmpty():
            d = os.path.abspath(unicode(QDir.convertSeparators(d)))
            if not self.toplevelDirs.contains(d):
                node = BrowserDirectory(self, d, self.last)
                self.last = node
                self.toplevelDirs.append(node.fileName())
        
    def handleRemoveToplevel(self):
        """
        Private slot to handle the Remove from toplevel popup menu entry.
        """
        itm = self.currentItem()
        self.toplevelDirs.remove(itm.fileName())
        self.takeItem(itm)
        del itm
        
    def handleAddAsToplevelDir(self):
        """
        Private slot to handle the Add as toplevel directory popup menu entry.
        """
        itm = self.currentItem()
        d = itm.fileName()
        if not self.toplevelDirs.contains(d):
            node = BrowserDirectory(self, d, self.last)
            self.last = node
            self.toplevelDirs.append(node.fileName())
        
    def handleProgramChange(self,fn):
        """
        Public slot to handle the programChange signal.
        """
        # Delete the old program directory if there was one.
        if self.progDir is not None:
            self.takeItem(self.progDir)

        self.progDir = BrowserDirectory(self,os.path.dirname(fn),None)
        
    def wantedItem(self, itm, filter=None):
        """
        Private method to check type of a listview item.
        
        @param itm the listview item to check (QListViewItem)
        @param filter list of class to check against
        @return flag indicating item is a valid type (boolean)
        """
        if filter is None:
            filter = self.selectedItemsFilter
        for typ in filter:
            if isinstance(itm, typ):
                return 1
        return 0
        
    def getSelectedItems(self, filter=None):
        """
        Public method to get the selected items.
        
        @param filter list of class to check against
        @return list of selected items (list of QListViewItem)
        """
        selectedItems = []
        itm = self.firstChild()
        while itm:
            if itm.isSelected() and self.wantedItem(itm, filter):
                selectedItems.append(itm)
            itm = itm.itemBelow()
        return selectedItems
        
    def getSelectedItemsCount(self, filter=None):
        """
        Public method to get the count of items selcted.
        
        @param filter list of class to check against
        @return count of items selected (integer)
        """
        count = 0
        itm = self.firstChild()
        while itm:
            if itm.isSelected() and self.wantedItem(itm, filter):
                count += 1
            itm = itm.itemBelow()
        return count
        
    def getItemsFiltered(self, filter):
        """
        Public method to get the items of a specific type.
        
        @param filter list of class to check against
        @return list of items (list of QListViewItem)
        """
        filteredItems = []
        itm = self.firstChild()
        while itm:
            if self.wantedItem(itm, filter):
                filteredItems.append(itm)
            itm = itm.itemBelow()
        return filteredItems
        
    def saveToplevelDirs(self):
        """
        Public slot to save the toplevel directories.
        """
        ok = Preferences.Prefs.settings.writeEntry('/eric3/Browser/ToplevelDirs', 
                                                   self.toplevelDirs)
