src/eric7/UI/FindLocationWidget.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 9039
3c8aa997bad8
child 9221
bf71ee032bb4
diff -r 3fc8dfeb6ebe -r b99e7fd55fd3 src/eric7/UI/FindLocationWidget.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/UI/FindLocationWidget.py	Thu Jul 07 11:23:56 2022 +0200
@@ -0,0 +1,378 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2004 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to search for files.
+"""
+
+import os
+import sys
+
+from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl
+from PyQt6.QtGui import QDesktopServices, QImageReader
+from PyQt6.QtWidgets import (
+    QWidget, QHeaderView, QApplication, QTreeWidgetItem, QDialog,
+    QDialogButtonBox, QVBoxLayout
+)
+
+from EricWidgets.EricPathPicker import EricPathPickerModes
+
+from .Ui_FindLocationWidget import Ui_FindLocationWidget
+
+import UI.PixmapCache
+from Utilities import direntries
+import Utilities
+
+
+class FindLocationWidget(QWidget, Ui_FindLocationWidget):
+    """
+    Class implementing a widget to search for files.
+    
+    The occurrences found are displayed in a QTreeWidget showing the
+    filename and the pathname. The file will be opened upon a double click
+    onto the respective entry of the list or by pressing the open button.
+    
+    @signal sourceFile(str) emitted to open a file in the editor
+    @signal designerFile(str) emitted to open a Qt-Designer file
+    @signal linguistFile(str) emitted to open a Qt-Linguist (*.ts) file
+    @signal trpreview([str]) emitted to preview Qt-Linguist (*.qm) files
+    @signal pixmapFile(str) emitted to open a pixmap file
+    @signal svgFile(str) emitted to open a SVG file
+    @signal umlFile(str) emitted to open an eric UML file
+    """
+    sourceFile = pyqtSignal(str)
+    designerFile = pyqtSignal(str)
+    linguistFile = pyqtSignal(str)
+    trpreview = pyqtSignal(list)
+    pixmapFile = pyqtSignal(str)
+    svgFile = pyqtSignal(str)
+    umlFile = pyqtSignal(str)
+    
+    def __init__(self, project, parent=None):
+        """
+        Constructor
+        
+        @param project reference to the project object
+        @type Project
+        @param parent parent widget of this dialog
+        @type QWidget
+        """
+        super().__init__(parent)
+        self.setupUi(self)
+        
+        self.layout().setContentsMargins(0, 3, 0, 0)
+        
+        self.searchDirPicker.setMode(EricPathPickerModes.DIRECTORY_MODE)
+        
+        self.fileList.headerItem().setText(self.fileList.columnCount(), "")
+        
+        self.stopButton.setEnabled(False)
+        self.stopButton.setIcon(UI.PixmapCache.getIcon("stopLoading"))
+        self.stopButton.setAutoDefault(False)
+        self.stopButton.clicked.connect(self.__stopSearch)
+        
+        self.findButton.setIcon(UI.PixmapCache.getIcon("find"))
+        self.findButton.setAutoDefault(False)
+        self.findButton.clicked.connect(self.__searchFile)
+        
+        self.clearButton.setEnabled(False)
+        self.clearButton.setIcon(UI.PixmapCache.getIcon("clear"))
+        self.clearButton.setAutoDefault(False)
+        self.clearButton.clicked.connect(self.__clearResults)
+        
+        self.openButton.setEnabled(False)
+        self.openButton.setIcon(UI.PixmapCache.getIcon("open"))
+        self.openButton.setAutoDefault(False)
+        self.openButton.clicked.connect(self.__openFile)
+        
+        self.__project = project
+        self.__project.projectOpened.connect(self.__projectOpened)
+        self.__project.projectClosed.connect(self.__projectClosed)
+        
+        self.extsepLabel.setText(os.extsep)
+        
+        self.__shouldStop = False
+        
+        self.fileNameEdit.returnPressed.connect(self.__searchFile)
+        self.fileExtEdit.returnPressed.connect(self.__searchFile)
+        
+        self.__projectClosed()
+    
+    @pyqtSlot()
+    def __stopSearch(self):
+        """
+        Private slot to handle the stop button being pressed.
+        """
+        self.__shouldStop = True
+    
+    @pyqtSlot()
+    def __openFile(self, itm=None):
+        """
+        Private slot to open a file.
+        
+        It emits a signal depending on the file extension.
+        
+        @param itm item to be opened
+        @type QTreeWidgetItem
+        """
+        if itm is None:
+            itm = self.fileList.currentItem()
+        if itm is not None:
+            fileName = itm.text(0)
+            filePath = itm.text(1)
+            fileExt = os.path.splitext(fileName)[1]
+            fullName = os.path.join(filePath, fileName)
+            
+            if fileExt == ".ui":
+                self.designerFile.emit(fullName)
+            elif fileExt == ".ts":
+                self.linguistFile.emit(fullName)
+            elif fileExt == ".qm":
+                self.trpreview.emit([fullName])
+            elif fileExt in (".egj", ".e5g"):
+                self.umlFile.emit(fullName)
+            elif fileExt == ".svg":
+                self.svgFile.emit(fullName)
+            elif fileExt[1:] in QImageReader.supportedImageFormats():
+                self.pixmapFile.emit(fullName)
+            else:
+                if Utilities.MimeTypes.isTextFile(fullName):
+                    self.sourceFile.emit(fullName)
+                else:
+                    QDesktopServices.openUrl(QUrl(fullName))
+    
+    @pyqtSlot()
+    def __searchFile(self):
+        """
+        Private slot to handle the search.
+        """
+        fileName = self.fileNameEdit.text()
+        fileExt = self.fileExtEdit.text()
+        
+        self.findStatusLabel.clear()
+        
+        patternFormat = (
+            "{0}{1}{2}"
+            if "*" in fileName or "?" in fileName else
+            "{0}*{1}{2}"
+        )
+        
+        fileNamePatterns = [patternFormat.format(
+            fileName or "*", os.extsep, fileExt or "*")]
+        
+        if not fileExt:
+            # search for files without extension as well
+            if "*" in fileName or "?" in fileName:
+                patternFormat = "{0}"
+            else:
+                patternFormat = "{0}*"
+            
+            fileNamePatterns.append(patternFormat.format(fileName or "*"))
+        
+        searchPaths = []
+        if (
+            self.searchDirCheckBox.isChecked() and
+            self.searchDirPicker.text() != ""
+        ):
+            searchPaths.append(self.searchDirPicker.text())
+        if self.projectCheckBox.isChecked():
+            searchPaths.append(self.__project.getProjectPath())
+        if self.syspathCheckBox.isChecked():
+            searchPaths.extend(sys.path)
+        
+        self.fileList.clear()
+        locations = {}
+        self.__shouldStop = False
+        self.stopButton.setEnabled(True)
+        self.clearButton.setEnabled(False)
+        QApplication.processEvents()
+        
+        for path in searchPaths:
+            if os.path.isdir(path):
+                files = direntries(path, True, fileNamePatterns,
+                                   False, self.checkStop)
+                if files:
+                    for file in files:
+                        fp, fn = os.path.split(file)
+                        if fn in locations:
+                            if fp in locations[fn]:
+                                continue
+                            else:
+                                locations[fn].append(fp)
+                        else:
+                            locations[fn] = [fp]
+                        QTreeWidgetItem(self.fileList, [fn, fp])
+                    QApplication.processEvents()
+        
+        del locations
+        self.stopButton.setEnabled(False)
+        self.fileList.sortItems(self.fileList.sortColumn(),
+                                Qt.SortOrder.AscendingOrder)
+        self.fileList.header().resizeSections(
+            QHeaderView.ResizeMode.ResizeToContents)
+        self.fileList.header().resizeSection(0, self.width() // 2)
+        self.fileList.header().setStretchLastSection(True)
+        
+        self.findStatusLabel.setText(self.tr(
+            "%n file(s) found", "", self.fileList.topLevelItemCount()))
+        
+        self.clearButton.setEnabled(self.fileList.topLevelItemCount() != 0)
+    
+    @pyqtSlot()
+    def __clearResults(self):
+        """
+        Private slot to clear the current search results.
+        """
+        self.fileList.clear()
+        self.clearButton.setEnabled(False)
+        self.openButton.setEnabled(False)
+    
+    def checkStop(self):
+        """
+        Public method to check, if the search should be stopped.
+        
+        @return flag indicating the search should be stopped
+        @rtype bool
+        """
+        QApplication.processEvents()
+        return self.__shouldStop
+    
+    @pyqtSlot(str)
+    def on_searchDirPicker_textChanged(self, text):
+        """
+        Private slot to handle the textChanged signal of the search directory
+        edit.
+        
+        @param text text of the search dir edit
+        @type str
+        """
+        self.searchDirCheckBox.setEnabled(text != "")
+    
+    @pyqtSlot(QTreeWidgetItem, int)
+    def on_fileList_itemActivated(self, itm, column):
+        """
+        Private slot to handle the double click on a file item.
+        
+        It emits the signal sourceFile or designerFile depending on the
+        file extension.
+        
+        @param itm the double clicked listview item
+        @type QTreeWidgetItem
+        @param column column that was double clicked (ignored)
+        @type int
+        """
+        self.__openFile(itm)
+    
+    @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
+    def on_fileList_currentItemChanged(self, current, previous):
+        """
+        Private slot handling a change of the current item.
+        
+        @param current current item
+        @type QTreeWidgetItem
+        @param previous prevoius current item
+        @type QTreeWidgetItem
+        """
+        self.openButton.setEnabled(current is not None)
+    
+    @pyqtSlot()
+    def __projectOpened(self):
+        """
+        Private slot to handle a project being opened.
+        """
+        self.projectCheckBox.setEnabled(True)
+        self.projectCheckBox.setChecked(True)
+    
+    @pyqtSlot()
+    def __projectClosed(self):
+        """
+        Private slot to handle a project being closed.
+        """
+        self.projectCheckBox.setEnabled(False)
+        self.projectCheckBox.setChecked(False)
+    
+    @pyqtSlot()
+    def activate(self):
+        """
+        Public slot to activate this widget.
+        """
+        self.fileNameEdit.selectAll()
+        self.fileNameEdit.setFocus()
+
+
+class FindLocationDialog(QDialog):
+    """
+    Class implementing a dialog to search for files.
+    
+    The occurrences found are displayed in a QTreeWidget showing the
+    filename and the pathname. The file will be opened upon a double click
+    onto the respective entry of the list or by pressing the open button.
+    
+    @signal sourceFile(str) emitted to open a file in the editor
+    @signal designerFile(str) emitted to open a Qt-Designer file
+    @signal linguistFile(str) emitted to open a Qt-Linguist (*.ts) file
+    @signal trpreview([str]) emitted to preview Qt-Linguist (*.qm) files
+    @signal pixmapFile(str) emitted to open a pixmap file
+    @signal svgFile(str) emitted to open a SVG file
+    @signal umlFile(str) emitted to open an eric UML file
+    """
+    sourceFile = pyqtSignal(str)
+    designerFile = pyqtSignal(str)
+    linguistFile = pyqtSignal(str)
+    trpreview = pyqtSignal(list)
+    pixmapFile = pyqtSignal(str)
+    svgFile = pyqtSignal(str)
+    umlFile = pyqtSignal(str)
+    
+    def __init__(self, project, parent=None):
+        """
+        Constructor
+        
+        @param project reference to the project object
+        @type Project
+        @param parent parent widget of this dialog (defaults to None)
+        @type QWidget (optional)
+        """
+        super().__init__(parent)
+        self.setWindowFlags(Qt.WindowType.Window)
+        
+        self.__layout = QVBoxLayout()
+        
+        self.__findWidget = FindLocationWidget(project, self)
+        self.__layout.addWidget(self.__findWidget)
+        
+        self.__buttonBox = QDialogButtonBox(
+            QDialogButtonBox.StandardButton.Close,
+            Qt.Orientation.Horizontal,
+            self
+        )
+        self.__buttonBox.button(
+            QDialogButtonBox.StandardButton.Close).setAutoDefault(False)
+        self.__layout.addWidget(self.__buttonBox)
+        
+        self.setLayout(self.__layout)
+        self.resize(600, 800)
+        
+        # connect the widgets
+        self.__findWidget.sourceFile.connect(self.sourceFile)
+        self.__findWidget.designerFile.connect(self.designerFile)
+        self.__findWidget.linguistFile.connect(self.linguistFile)
+        self.__findWidget.trpreview.connect(self.trpreview)
+        self.__findWidget.pixmapFile.connect(self.pixmapFile)
+        self.__findWidget.svgFile.connect(self.svgFile)
+        self.__findWidget.umlFile.connect(self.umlFile)
+        
+        self.__buttonBox.accepted.connect(self.accept)
+        self.__buttonBox.rejected.connect(self.reject)
+    
+    def activate(self):
+        """
+        Public method to activate the dialog.
+        """
+        self.__findWidget.activate()
+        
+        self.raise_()
+        self.activateWindow()
+        self.show()

eric ide

mercurial