eric6/UI/Browser.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 6987
3371a03ed0a7
diff -r f99d60d6b59b -r 2602857055c5 eric6/UI/Browser.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/UI/Browser.py	Sun Apr 14 15:09:21 2019 +0200
@@ -0,0 +1,815 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a browser with class browsing capabilities.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import QModelIndex, pyqtSignal, QUrl, Qt, QCoreApplication, \
+    QItemSelectionModel, QElapsedTimer
+from PyQt5.QtGui import QDesktopServices
+from PyQt5.QtWidgets import QTreeView, QApplication, QMenu, QAbstractItemView
+
+from E5Gui.E5Application import e5App
+from E5Gui import E5FileDialog, E5MessageBox
+
+from Project.ProjectBrowserModel import ProjectBrowserSimpleDirectoryItem
+from .BrowserModel import BrowserModel, BrowserDirectoryItem, \
+    BrowserFileItem, BrowserClassItem, BrowserMethodItem, \
+    BrowserClassAttributeItem, BrowserImportItem, BrowserImportsItem, \
+    BrowserSysPathItem, BrowserGlobalsItem
+from .BrowserSortFilterProxyModel import BrowserSortFilterProxyModel
+
+import UI.PixmapCache
+import Preferences
+import Utilities
+import Utilities.MimeTypes
+from Globals import qVersionTuple
+
+
+class Browser(QTreeView):
+    """
+    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 sourceFile(filename) emitted to open a Python file at a line (str)
+    @signal sourceFile(filename, lineno) emitted to open a Python file at a
+        line (str, int)
+    @signal sourceFile(filename, lineno, type) emitted to open a Python file
+        at a line giving an explicit file type (str, int, str)
+    @signal sourceFile(filename, linenos) emitted to open a Python file giving
+        a list of lines(str, list)
+    @signal designerFile(filename) emitted to open a Qt-Designer file (str)
+    @signal linguistFile(filename) emitted to open a Qt-Linguist (*.ts)
+        file (str)
+    @signal trpreview(filenames) emitted to preview Qt-Linguist (*.qm)
+        files (list of str)
+    @signal trpreview(filenames, ignore) emitted to preview Qt-Linguist (*.qm)
+        files indicating whether non-existent files shall be ignored
+        (list of str, bool)
+    @signal projectFile(filename) emitted to open an eric project file (str)
+    @signal multiProjectFile(filename) emitted to open an eric multi project
+        file (str)
+    @signal pixmapFile(filename) emitted to open a pixmap file (str)
+    @signal pixmapEditFile(filename) emitted to edit a pixmap file (str)
+    @signal svgFile(filename) emitted to open a SVG file (str)
+    @signal binaryFile(filename) emitted to open a file as binary (str)
+    @signal unittestOpen(filename) emitted to open a Python file for a
+        unit test (str)
+    """
+    sourceFile = pyqtSignal((str, ), (str, int), (str, list), (str, int, str))
+    designerFile = pyqtSignal(str)
+    linguistFile = pyqtSignal(str)
+    trpreview = pyqtSignal((list, ), (list, bool))
+    projectFile = pyqtSignal(str)
+    multiProjectFile = pyqtSignal(str)
+    pixmapFile = pyqtSignal(str)
+    pixmapEditFile = pyqtSignal(str)
+    svgFile = pyqtSignal(str)
+    binaryFile = pyqtSignal(str)
+    unittestOpen = pyqtSignal(str)
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent parent widget (QWidget)
+        """
+        super(Browser, self).__init__(parent)
+        
+        self.setWindowTitle(QCoreApplication.translate('Browser',
+                                                       'File-Browser'))
+        self.setWindowIcon(UI.PixmapCache.getIcon("eric.png"))
+        
+        self.__embeddedBrowser = Preferences.getUI("LayoutFileBrowserEmbedded")
+        
+        self.__model = BrowserModel()
+        self.__sortModel = BrowserSortFilterProxyModel()
+        self.__sortModel.setSourceModel(self.__model)
+        self.setModel(self.__sortModel)
+        
+        self.selectedItemsFilter = [BrowserFileItem]
+        
+        self._activating = False
+        
+        self.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.customContextMenuRequested.connect(self._contextMenuRequested)
+        self.activated.connect(self._openItem)
+        self.expanded.connect(self._resizeColumns)
+        self.collapsed.connect(self._resizeColumns)
+        
+        self.setWhatsThis(QCoreApplication.translate(
+            'Browser',
+            """<b>The Browser Window</b>"""
+            """<p>This allows you to easily navigate the hierarchy 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 hierarchies.</p>"""
+            """<p>The first hierarchy is only shown if you have opened a"""
+            """ program for debugging and its 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 hierarchy gives you easy access to most"""
+            """ of what you will need.</p>"""
+            """<p>The next hierarchy is used to easily navigate the"""
+            """ directories that are specified in the Python"""
+            """ <tt>sys.path</tt> variable.</p>"""
+            """<p>The remaining hierarchies allow you navigate your system"""
+            """ as a whole. On a UNIX system there will be a hierarchy with"""
+            """ <tt>/</tt> at its root and another with the user home"""
+            """ directory. On a Windows system there will be a hierarchy 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 hierarchies 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()
+        
+        self._init()    # perform common initialization tasks
+        
+        self._keyboardSearchString = ""
+        self._keyboardSearchTimer = QElapsedTimer()
+        self._keyboardSearchTimer.invalidate()
+        
+    def _init(self):
+        """
+        Protected method to perform initialization tasks common to this
+        base class and all derived classes.
+        """
+        self.setRootIsDecorated(True)
+        self.setAlternatingRowColors(True)
+        
+        header = self.header()
+        header.setSortIndicator(0, Qt.AscendingOrder)
+        header.setSortIndicatorShown(True)
+        if qVersionTuple() >= (5, 0, 0):
+            header.setSectionsClickable(True)
+        else:
+            header.setClickable(True)
+        
+        self.setSortingEnabled(True)
+        
+        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.setSelectionBehavior(QAbstractItemView.SelectRows)
+        
+        self.header().setStretchLastSection(True)
+        self.headerSize0 = 0
+        self.layoutDisplay()
+        
+    def layoutDisplay(self):
+        """
+        Public slot to perform a layout operation.
+        """
+        self._resizeColumns(QModelIndex())
+        self._resort()
+        
+    def _resizeColumns(self, index):
+        """
+        Protected slot to resize the view when items get expanded or collapsed.
+        
+        @param index index of item (QModelIndex)
+        """
+        w = max(100, self.sizeHintForColumn(0))
+        if w != self.headerSize0:
+            self.header().resizeSection(0, w)
+            self.headerSize0 = w
+    
+    def _resort(self):
+        """
+        Protected slot to resort the tree.
+        """
+        self.model().sort(self.header().sortIndicatorSection(),
+                          self.header().sortIndicatorOrder())
+        
+    def __createPopupMenus(self):
+        """
+        Private method to generate the various popup menus.
+        """
+        # create the popup menu for source files
+        self.sourceMenu = QMenu(self)
+        self.sourceMenu.addAction(
+            QCoreApplication.translate('Browser', 'Open'), self._openItem)
+        self.unittestAct = self.sourceMenu.addAction(
+            QCoreApplication.translate('Browser', 'Run unittest...'),
+            self.handleUnittest)
+        self.sourceMenu.addSeparator()
+        self.mimeTypeAct = self.sourceMenu.addAction(
+            QCoreApplication.translate('Browser', 'Show Mime-Type'),
+            self.__showMimeType)
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(
+            QCoreApplication.translate('Browser', 'Copy Path to Clipboard'),
+            self._copyToClipboard)
+        
+        # create the popup menu for general use
+        self.menu = QMenu(self)
+        self.menu.addAction(
+            QCoreApplication.translate('Browser', 'Open'), self._openItem)
+        self.menu.addAction(
+            QCoreApplication.translate('Browser', 'Open in Hex Editor'),
+            self._openHexEditor)
+        self.editPixmapAct = self.menu.addAction(
+            QCoreApplication.translate('Browser', 'Open in Icon Editor'),
+            self._editPixmap)
+        self.menu.addSeparator()
+        self.mimeTypeAct = self.menu.addAction(
+            QCoreApplication.translate('Browser', 'Show Mime-Type'),
+            self.__showMimeType)
+        self.menu.addSeparator()
+        self.menu.addAction(
+            QCoreApplication.translate('Browser', 'Copy Path to Clipboard'),
+            self._copyToClipboard)
+        if self.__embeddedBrowser in [1, 2]:
+            self.menu.addSeparator()
+            self.menu.addAction(
+                QCoreApplication.translate('Browser', 'Configure...'),
+                self.__configure)
+
+        # create the menu for multiple selected files
+        self.multiMenu = QMenu(self)
+        self.multiMenu.addAction(
+            QCoreApplication.translate('Browser', 'Open'), self._openItem)
+        if self.__embeddedBrowser in [1, 2]:
+            self.multiMenu.addSeparator()
+            self.multiMenu.addAction(
+                QCoreApplication.translate('Browser', 'Configure...'),
+                self.__configure)
+        
+        # create the directory menu
+        self.dirMenu = QMenu(self)
+        self.dirMenu.addAction(
+            QCoreApplication.translate('Browser', 'New toplevel directory...'),
+            self.__newToplevelDir)
+        self.addAsTopLevelAct = self.dirMenu.addAction(
+            QCoreApplication.translate('Browser', 'Add as toplevel directory'),
+            self.__addAsToplevelDir)
+        self.removeFromToplevelAct = self.dirMenu.addAction(
+            QCoreApplication.translate('Browser', 'Remove from toplevel'),
+            self.__removeToplevel)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(
+            QCoreApplication.translate('Browser', 'Refresh directory'),
+            self.__refreshDirectory)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(
+            QCoreApplication.translate('Browser', 'Find in this directory'),
+            self.__findInDirectory)
+        self.dirMenu.addAction(
+            QCoreApplication.translate(
+                'Browser', 'Find&&Replace in this directory'),
+            self.__replaceInDirectory)
+        self.dirMenu.addAction(
+            QCoreApplication.translate('Browser', 'Copy Path to Clipboard'),
+            self._copyToClipboard)
+        if self.__embeddedBrowser in [1, 2]:
+            self.dirMenu.addSeparator()
+            self.dirMenu.addAction(
+                QCoreApplication.translate('Browser', 'Configure...'),
+                self.__configure)
+        
+        # create the attribute menu
+        self.gotoMenu = QMenu(QCoreApplication.translate('Browser', "Goto"),
+                              self)
+        self.gotoMenu.aboutToShow.connect(self._showGotoMenu)
+        self.gotoMenu.triggered.connect(self._gotoAttribute)
+        
+        self.attributeMenu = QMenu(self)
+        self.attributeMenu.addAction(
+            QCoreApplication.translate('Browser', 'New toplevel directory...'),
+            self.__newToplevelDir)
+        self.attributeMenu.addSeparator()
+        self.attributeMenu.addMenu(self.gotoMenu)
+        if self.__embeddedBrowser in [1, 2]:
+            self.attributeMenu.addSeparator()
+            self.attributeMenu.addAction(
+                QCoreApplication.translate('Browser', 'Configure...'),
+                self.__configure)
+        
+        # create the background menu
+        self.backMenu = QMenu(self)
+        self.backMenu.addAction(
+            QCoreApplication.translate('Browser', 'New toplevel directory...'),
+            self.__newToplevelDir)
+        if self.__embeddedBrowser in [1, 2]:
+            self.backMenu.addSeparator()
+            self.backMenu.addAction(
+                QCoreApplication.translate('Browser', 'Configure...'),
+                self.__configure)
+
+    def mouseDoubleClickEvent(self, mouseEvent):
+        """
+        Protected method of QAbstractItemView.
+        
+        Reimplemented to disable expanding/collapsing of items when
+        double-clicking. Instead the double-clicked entry is opened.
+        
+        @param mouseEvent the mouse event (QMouseEvent)
+        """
+        index = self.indexAt(mouseEvent.pos())
+        if index.isValid():
+            itm = self.model().item(index)
+            if isinstance(itm, (
+                    BrowserDirectoryItem, BrowserImportsItem,
+                    ProjectBrowserSimpleDirectoryItem, BrowserSysPathItem,
+                    BrowserGlobalsItem)):
+                self.setExpanded(index, not self.isExpanded(index))
+            else:
+                self._openItem()
+
+    def _contextMenuRequested(self, coord):
+        """
+        Protected slot to show the context menu of the listview.
+        
+        @param coord the position of the mouse pointer (QPoint)
+        """
+        categories = self.getSelectedItemsCountCategorized(
+            [BrowserDirectoryItem, BrowserFileItem,
+             BrowserClassItem, BrowserMethodItem])
+        cnt = categories["sum"]
+        bfcnt = categories[str(BrowserFileItem)]
+        if cnt > 1 and cnt == bfcnt:
+            self.multiMenu.popup(self.mapToGlobal(coord))
+        else:
+            index = self.indexAt(coord)
+            
+            if index.isValid():
+                self.setCurrentIndex(index)
+                flags = QItemSelectionModel.SelectionFlags(
+                    QItemSelectionModel.ClearAndSelect |
+                    QItemSelectionModel.Rows)
+                self.selectionModel().select(index, flags)
+                
+                itm = self.model().item(index)
+                coord = self.mapToGlobal(coord)
+                if isinstance(itm, BrowserFileItem):
+                    if itm.isPython3File():
+                        if itm.fileName().endswith('.py'):
+                            self.unittestAct.setEnabled(True)
+                        else:
+                            self.unittestAct.setEnabled(False)
+                        self.sourceMenu.popup(coord)
+                    else:
+                        self.editPixmapAct.setVisible(itm.isPixmapFile())
+                        self.menu.popup(coord)
+                elif isinstance(itm, BrowserClassItem) or \
+                    isinstance(itm, BrowserMethodItem) or \
+                        isinstance(itm, BrowserImportItem):
+                    self.editPixmapAct.setVisible(False)
+                    self.menu.popup(coord)
+                elif isinstance(itm, BrowserClassAttributeItem):
+                    self.attributeMenu.popup(coord)
+                elif isinstance(itm, BrowserDirectoryItem):
+                    if not index.parent().isValid():
+                        self.removeFromToplevelAct.setEnabled(True)
+                        self.addAsTopLevelAct.setEnabled(False)
+                    else:
+                        self.removeFromToplevelAct.setEnabled(False)
+                        self.addAsTopLevelAct.setEnabled(True)
+                    self.dirMenu.popup(coord)
+                else:
+                    self.backMenu.popup(coord)
+            else:
+                self.backMenu.popup(self.mapToGlobal(coord))
+        
+    def _showGotoMenu(self):
+        """
+        Protected slot to prepare the goto submenu of the attribute menu.
+        """
+        self.gotoMenu.clear()
+        
+        itm = self.model().item(self.currentIndex())
+        linenos = itm.linenos()
+        fileName = itm.fileName()
+        
+        for lineno in sorted(linenos):
+            act = self.gotoMenu.addAction(
+                QCoreApplication.translate(
+                    'Browser', "Line {0}").format(lineno))
+            act.setData([fileName, lineno])
+        
+    def _gotoAttribute(self, act):
+        """
+        Protected slot to handle the selection of the goto menu.
+        
+        @param act reference to the action (E5Action)
+        """
+        fileName, lineno = act.data()
+        self.sourceFile[str, int].emit(fileName, lineno)
+        
+    def handlePreferencesChanged(self):
+        """
+        Public slot used to handle the preferencesChanged signal.
+        """
+        self.model().preferencesChanged()
+        self._resort()
+        
+    def _openItem(self):
+        """
+        Protected slot to handle the open popup menu entry.
+        """
+        itmList = self.getSelectedItems(
+            [BrowserFileItem, BrowserClassItem,
+             BrowserMethodItem, BrowserClassAttributeItem,
+             BrowserImportItem])
+        
+        if not self._activating:
+            self._activating = True
+            for itm in itmList:
+                if isinstance(itm, BrowserFileItem):
+                    if itm.isPython2File():
+                        self.sourceFile[str].emit(itm.fileName())
+                    elif itm.isPython3File():
+                        self.sourceFile[str].emit(itm.fileName())
+                    elif itm.isRubyFile():
+                        self.sourceFile[str, int, str].emit(
+                            itm.fileName(), -1, "Ruby")
+                    elif itm.isDFile():
+                        self.sourceFile[str, int, str].emit(
+                            itm.fileName(), -1, "D")
+                    elif itm.isDesignerFile():
+                        self.designerFile.emit(itm.fileName())
+                    elif itm.isLinguistFile():
+                        if itm.fileExt() == '.ts':
+                            self.linguistFile.emit(itm.fileName())
+                        else:
+                            self.trpreview.emit([itm.fileName()])
+                    elif itm.isProjectFile():
+                        self.projectFile.emit(itm.fileName())
+                    elif itm.isMultiProjectFile():
+                        self.multiProjectFile.emit(itm.fileName())
+                    elif itm.isIdlFile():
+                        self.sourceFile[str].emit(itm.fileName())
+                    elif itm.isProtobufFile():
+                        self.sourceFile[str].emit(itm.fileName())
+                    elif itm.isResourcesFile():
+                        self.sourceFile[str].emit(itm.fileName())
+                    elif itm.isSvgFile():
+                        self.svgFile.emit(itm.fileName())
+                    elif itm.isPixmapFile():
+                        self.pixmapFile.emit(itm.fileName())
+                    else:
+                        if Utilities.MimeTypes.isTextFile(itm.fileName()):
+                            self.sourceFile[str].emit(itm.fileName())
+                        else:
+                            QDesktopServices.openUrl(QUrl(itm.fileName()))
+                elif isinstance(itm, BrowserClassItem):
+                    self.sourceFile[str, int].emit(
+                        itm.fileName(), itm.classObject().lineno)
+                elif isinstance(itm, BrowserMethodItem):
+                    self.sourceFile[str, int].emit(
+                        itm.fileName(), itm.functionObject().lineno)
+                elif isinstance(itm, BrowserClassAttributeItem):
+                    self.sourceFile[str, int].emit(
+                        itm.fileName(), itm.attributeObject().lineno)
+                elif isinstance(itm, BrowserImportItem):
+                    self.sourceFile[str, list].emit(
+                        itm.fileName(), itm.linenos())
+            self._activating = False
+        
+    def __showMimeType(self):
+        """
+        Private slot to show the mime type of the selected entry.
+        """
+        itmList = self.getSelectedItems(
+            [BrowserFileItem, BrowserClassItem,
+             BrowserMethodItem, BrowserClassAttributeItem,
+             BrowserImportItem])
+        if itmList:
+            mimetype = Utilities.MimeTypes.mimeType(itmList[0].fileName())
+            if mimetype is None:
+                E5MessageBox.warning(
+                    self,
+                    QCoreApplication.translate('Browser', "Show Mime-Type"),
+                    QCoreApplication.translate(
+                        'Browser',
+                        """The mime type of the file could not be"""
+                        """ determined."""))
+            elif mimetype.split("/")[0] == "text":
+                E5MessageBox.information(
+                    self,
+                    QCoreApplication.translate('Browser', "Show Mime-Type"),
+                    QCoreApplication.translate(
+                        'Browser',
+                        """The file has the mime type <b>{0}</b>.""")
+                    .format(mimetype))
+            else:
+                textMimeTypesList = Preferences.getUI("TextMimeTypes")
+                if mimetype in textMimeTypesList:
+                    E5MessageBox.information(
+                        self,
+                        QCoreApplication.translate(
+                            'Browser', "Show Mime-Type"),
+                        QCoreApplication.translate(
+                            'Browser',
+                            """The file has the mime type <b>{0}</b>.""")
+                        .format(mimetype))
+                else:
+                    ok = E5MessageBox.yesNo(
+                        self,
+                        QCoreApplication.translate(
+                            'Browser', "Show Mime-Type"),
+                        QCoreApplication.translate(
+                            'Browser',
+                            """The file has the mime type <b>{0}</b>."""
+                            """<br/> Shall it be added to the list of"""
+                            """ text mime types?""").format(mimetype))
+                    if ok:
+                        textMimeTypesList.append(mimetype)
+                        Preferences.setUI("TextMimeTypes", textMimeTypesList)
+        
+    def _editPixmap(self):
+        """
+        Protected slot to handle the open in icon editor popup menu entry.
+        """
+        itmList = self.getSelectedItems([BrowserFileItem])
+        
+        for itm in itmList:
+            if isinstance(itm, BrowserFileItem):
+                if itm.isPixmapFile():
+                    self.pixmapEditFile.emit(itm.fileName())
+        
+    def _openHexEditor(self):
+        """
+        Protected slot to handle the open in hex editor popup menu entry.
+        """
+        itmList = self.getSelectedItems([BrowserFileItem])
+        
+        for itm in itmList:
+            if isinstance(itm, BrowserFileItem):
+                self.binaryFile.emit(itm.fileName())
+        
+    def _copyToClipboard(self):
+        """
+        Protected method to copy the text shown for an entry to the clipboard.
+        """
+        itm = self.model().item(self.currentIndex())
+        try:
+            fn = itm.fileName()
+        except AttributeError:
+            try:
+                fn = itm.dirName()
+            except AttributeError:
+                fn = ""
+        
+        if fn:
+            cb = QApplication.clipboard()
+            cb.setText(fn)
+        
+    def handleUnittest(self):
+        """
+        Public slot to handle the unittest popup menu entry.
+        """
+        try:
+            index = self.currentIndex()
+            itm = self.model().item(index)
+            pyfn = itm.fileName()
+        except AttributeError:
+            pyfn = None
+
+        if pyfn is not None:
+            self.unittestOpen.emit(pyfn)
+        
+    def __newToplevelDir(self):
+        """
+        Private slot to handle the New toplevel directory popup menu entry.
+        """
+        dname = E5FileDialog.getExistingDirectory(
+            None,
+            QCoreApplication.translate('Browser', "New toplevel directory"),
+            "",
+            E5FileDialog.Options(E5FileDialog.ShowDirsOnly))
+        if dname:
+            dname = os.path.abspath(Utilities.toNativeSeparators(dname))
+            self.__model.addTopLevelDir(dname)
+        
+    def __removeToplevel(self):
+        """
+        Private slot to handle the Remove from toplevel popup menu entry.
+        """
+        index = self.currentIndex()
+        sindex = self.model().mapToSource(index)
+        self.__model.removeToplevelDir(sindex)
+        
+    def __addAsToplevelDir(self):
+        """
+        Private slot to handle the Add as toplevel directory popup menu entry.
+        """
+        index = self.currentIndex()
+        dname = self.model().item(index).dirName()
+        self.__model.addTopLevelDir(dname)
+        
+    def __refreshDirectory(self):
+        """
+        Private slot to refresh a directory entry.
+        """
+        index = self.currentIndex()
+        refreshDir = self.model().item(index).dirName()
+        self.__model.directoryChanged(refreshDir)
+        
+    def __findInDirectory(self):
+        """
+        Private slot to handle the Find in directory popup menu entry.
+        """
+        index = self.currentIndex()
+        searchDir = self.model().item(index).dirName()
+        
+        e5App().getObject("UserInterface")\
+            .showFindFilesDialog(searchDir=searchDir)
+        
+    def __replaceInDirectory(self):
+        """
+        Private slot to handle the Find&Replace in directory popup menu entry.
+        """
+        index = self.currentIndex()
+        searchDir = self.model().item(index).dirName()
+        
+        e5App().getObject("UserInterface")\
+            .showReplaceFilesDialog(searchDir=searchDir)
+        
+    def handleProgramChange(self, fn):
+        """
+        Public slot to handle the programChange signal.
+        
+        @param fn file name (string)
+        """
+        self.__model.programChange(os.path.dirname(fn))
+        
+    def handleInterpreterChanged(self, interpreter):
+        """
+        Public slot to handle a change of the debug client's interpreter.
+        
+        @param interpreter interpreter of the debug client (string)
+        """
+        self.__model.interpreterChanged(interpreter)
+        
+    def wantedItem(self, itm, filterList=None):
+        """
+        Public method to check type of an item.
+        
+        @param itm the item to check (BrowserItem)
+        @param filterList list of classes to check against
+        @return flag indicating item is a valid type (boolean)
+        """
+        if filterList is None:
+            filterList = self.selectedItemsFilter
+        for typ in filterList:
+            if isinstance(itm, typ):
+                return True
+        return False
+        
+    def getSelectedItems(self, filterList=None):
+        """
+        Public method to get the selected items.
+        
+        @param filterList list of classes to check against
+        @return list of selected items (list of BroweserItem)
+        """
+        selectedItems = []
+        indexes = self.selectedIndexes()
+        for index in indexes:
+            if index.column() == 0:
+                itm = self.model().item(index)
+                if self.wantedItem(itm, filterList):
+                    selectedItems.append(itm)
+        return selectedItems
+        
+    def getSelectedItemsCount(self, filterList=None):
+        """
+        Public method to get the count of items selected.
+        
+        @param filterList list of classes to check against
+        @return count of items selected (integer)
+        """
+        count = 0
+        indexes = self.selectedIndexes()
+        for index in indexes:
+            if index.column() == 0:
+                itm = self.model().item(index)
+                if self.wantedItem(itm, filterList):
+                    count += 1
+        return count
+        
+    def getSelectedItemsCountCategorized(self, filterList=None):
+        """
+        Public method to get a categorized count of selected items.
+        
+        @param filterList list of classes to check against
+        @return a dictionary containing the counts of items belonging
+            to the individual filter classes. The keys of the dictionary
+            are the string representation of the classes given in the
+            filter (i.e. str(filterClass)). The dictionary contains
+            an additional entry with key "sum", that stores the sum of
+            all selected entries fulfilling the filter criteria.
+        """
+        if filterList is None:
+            filterList = self.selectedItemsFilter
+        categories = {}
+        categories["sum"] = 0
+        for typ in filterList:
+            categories[str(typ)] = 0
+        
+        indexes = self.selectedIndexes()
+        for index in indexes:
+            if index.column() == 0:
+                itm = self.model().item(index)
+                for typ in filterList:
+                    if isinstance(itm, typ):
+                        categories["sum"] += 1
+                        categories[str(typ)] += 1
+        
+        return categories
+        
+    def saveToplevelDirs(self):
+        """
+        Public slot to save the toplevel directories.
+        """
+        self.__model.saveToplevelDirs()
+    
+    def __configure(self):
+        """
+        Private method to open the configuration dialog.
+        """
+        if self.__embeddedBrowser == 1:
+            e5App().getObject("UserInterface")\
+                .showPreferences("debuggerGeneralPage")
+        elif self.__embeddedBrowser == 2:
+            e5App().getObject("UserInterface")\
+                .showPreferences("projectBrowserPage")
+    
+    def keyboardSearch(self, search):
+        """
+        Public function to search the tree via the keyboard.
+        
+        @param search the character entered via the keyboard
+        @type str
+        """
+        if self.model().rowCount() == 0:
+            return
+        
+        if self.currentIndex().isValid():
+            startIndex = self.currentIndex()
+        else:
+            startIndex = self.model().index(0, 0)
+        
+        keyboardSearchTimeWasValid = self._keyboardSearchTimer.isValid()
+        keyboardSearchTimeElapsed = self._keyboardSearchTimer.restart()
+        if not search or not keyboardSearchTimeWasValid or \
+            keyboardSearchTimeElapsed > \
+                QApplication.keyboardInputInterval():
+            self._keyboardSearchString = search.lower()
+        else:
+            self._keyboardSearchString += search.lower()
+        
+        index = startIndex
+        found = False
+        while True:
+            name = self.model().data(index)
+            if name.lower().startswith(self._keyboardSearchString) and \
+                    self._keyboardSearchType(self.model().item(index)):
+                found = True
+                break
+            
+            index = self.indexBelow(index)
+            if not index.isValid():
+                index = self.model().index(0, 0)
+            if index == startIndex:
+                break
+        
+        if found:
+            self.setCurrentIndex(index)
+    
+    def _keyboardSearchType(self, item):
+        """
+        Protected method to check, if the item is of the correct type.
+        
+        @param item reference to the item
+        @type BrowserItem
+        @return flag indicating a correct type
+        @rtype bool
+        """
+        return isinstance(
+            item, (BrowserDirectoryItem, BrowserFileItem, BrowserSysPathItem))

eric ide

mercurial