1# -*- coding: utf-8 -*- 2 3# Copyright (c) 2004 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> 4# 5 6""" 7Module implementing a dialog to search for files. 8""" 9 10import os 11import sys 12 13from PyQt5.QtCore import pyqtSignal, pyqtSlot 14from PyQt5.QtWidgets import ( 15 QWidget, QHeaderView, QApplication, QDialogButtonBox, QTreeWidgetItem 16) 17 18from E5Gui.E5PathPicker import E5PathPickerModes 19 20from .Ui_FindFileNameDialog import Ui_FindFileNameDialog 21 22from Utilities import direntries 23import Utilities 24 25 26class FindFileNameDialog(QWidget, Ui_FindFileNameDialog): 27 """ 28 Class implementing a dialog to search for files. 29 30 The occurrences found are displayed in a QTreeWidget showing the 31 filename and the pathname. The file will be opened upon a double click 32 onto the respective entry of the list. 33 34 @signal sourceFile(str) emitted to open a file in the editor 35 @signal designerFile(str) emitted to open a Qt-Designer file 36 """ 37 sourceFile = pyqtSignal(str) 38 designerFile = pyqtSignal(str) 39 40 def __init__(self, project, parent=None): 41 """ 42 Constructor 43 44 @param project reference to the project object 45 @param parent parent widget of this dialog (QWidget) 46 """ 47 super().__init__(parent) 48 self.setupUi(self) 49 50 self.searchDirPicker.setMode(E5PathPickerModes.DirectoryMode) 51 52 self.fileList.headerItem().setText(self.fileList.columnCount(), "") 53 54 self.stopButton = self.buttonBox.addButton( 55 self.tr("Stop"), QDialogButtonBox.ButtonRole.ActionRole) 56 self.stopButton.setToolTip(self.tr("Press to stop the search")) 57 self.stopButton.setEnabled(False) 58 self.buttonBox.button( 59 QDialogButtonBox.StandardButton.Open).setToolTip( 60 self.tr("Opens the selected file")) 61 self.buttonBox.button( 62 QDialogButtonBox.StandardButton.Open).setEnabled(False) 63 64 self.project = project 65 self.extsepLabel.setText(os.extsep) 66 67 self.shouldStop = False 68 69 def on_buttonBox_clicked(self, button): 70 """ 71 Private slot called by a button of the button box clicked. 72 73 @param button button that was clicked (QAbstractButton) 74 """ 75 if button == self.stopButton: 76 self.shouldStop = True 77 elif button == self.buttonBox.button( 78 QDialogButtonBox.StandardButton.Open 79 ): 80 self.__openFile() 81 82 def __openFile(self, itm=None): 83 """ 84 Private slot to open a file. 85 86 It emits the signal sourceFile or designerFile depending on the 87 file extension. 88 89 @param itm item to be opened (QTreeWidgetItem) 90 """ 91 if itm is None: 92 itm = self.fileList.currentItem() 93 if itm is not None: 94 fileName = itm.text(0) 95 filePath = itm.text(1) 96 97 if fileName.endswith('.ui'): 98 self.designerFile.emit(os.path.join(filePath, fileName)) 99 else: 100 self.sourceFile.emit(os.path.join(filePath, fileName)) 101 102 def __searchFile(self): 103 """ 104 Private slot to handle the search. 105 """ 106 fileName = self.fileNameEdit.text() 107 if not fileName: 108 self.fileList.clear() 109 return 110 fileExt = self.fileExtEdit.text() 111 if not fileExt and Utilities.isWindowsPlatform(): 112 self.fileList.clear() 113 return 114 115 patternFormat = fileExt and "{0}{1}{2}" or "{0}*{1}{2}" 116 fileNamePattern = patternFormat.format( 117 fileName, os.extsep, fileExt and fileExt or '*') 118 119 searchPaths = [] 120 if ( 121 self.searchDirCheckBox.isChecked() and 122 self.searchDirPicker.text() != "" 123 ): 124 searchPaths.append(self.searchDirPicker.text()) 125 if self.projectCheckBox.isChecked(): 126 searchPaths.append(self.project.ppath) 127 if self.syspathCheckBox.isChecked(): 128 searchPaths.extend(sys.path) 129 130 found = False 131 self.fileList.clear() 132 locations = {} 133 self.shouldStop = False 134 self.stopButton.setEnabled(True) 135 QApplication.processEvents() 136 137 for path in searchPaths: 138 if os.path.isdir(path): 139 files = direntries(path, True, fileNamePattern, 140 False, self.checkStop) 141 if files: 142 found = True 143 for file in files: 144 fp, fn = os.path.split(file) 145 if fn in locations: 146 if fp in locations[fn]: 147 continue 148 else: 149 locations[fn].append(fp) 150 else: 151 locations[fn] = [fp] 152 QTreeWidgetItem(self.fileList, [fn, fp]) 153 QApplication.processEvents() 154 155 del locations 156 self.stopButton.setEnabled(False) 157 self.fileList.header().resizeSections( 158 QHeaderView.ResizeMode.ResizeToContents) 159 self.fileList.header().setStretchLastSection(True) 160 161 if found: 162 self.fileList.setCurrentItem(self.fileList.topLevelItem(0)) 163 164 def checkStop(self): 165 """ 166 Public method to check, if the search should be stopped. 167 168 @return flag indicating the search should be stopped (boolean) 169 """ 170 QApplication.processEvents() 171 return self.shouldStop 172 173 def on_fileNameEdit_textChanged(self, text): 174 """ 175 Private slot to handle the textChanged signal of the file name edit. 176 177 @param text (ignored) 178 """ 179 self.__searchFile() 180 181 def on_fileExtEdit_textChanged(self, text): 182 """ 183 Private slot to handle the textChanged signal of the file extension 184 edit. 185 186 @param text (ignored) 187 """ 188 self.__searchFile() 189 190 def on_searchDirPicker_textChanged(self, text): 191 """ 192 Private slot to handle the textChanged signal of the search directory 193 edit. 194 195 @param text text of the search dir edit (string) 196 """ 197 self.searchDirCheckBox.setEnabled(text != "") 198 if self.searchDirCheckBox.isChecked(): 199 self.__searchFile() 200 201 def on_searchDirCheckBox_toggled(self, checked): 202 """ 203 Private slot to handle the toggled signal of the search directory 204 checkbox. 205 206 @param checked flag indicating the state of the checkbox (boolean) 207 """ 208 if self.searchDirPicker.text(): 209 self.__searchFile() 210 211 def on_projectCheckBox_toggled(self, checked): 212 """ 213 Private slot to handle the toggled signal of the project checkbox. 214 215 @param checked flag indicating the state of the checkbox (boolean) 216 """ 217 self.__searchFile() 218 219 def on_syspathCheckBox_toggled(self, checked): 220 """ 221 Private slot to handle the toggled signal of the sys.path checkbox. 222 223 @param checked flag indicating the state of the checkbox (boolean) 224 """ 225 self.__searchFile() 226 227 def on_fileList_itemActivated(self, itm, column): 228 """ 229 Private slot to handle the double click on a file item. 230 231 It emits the signal sourceFile or designerFile depending on the 232 file extension. 233 234 @param itm the double clicked listview item (QTreeWidgetItem) 235 @param column column that was double clicked (integer) (ignored) 236 """ 237 self.__openFile(itm) 238 239 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) 240 def on_fileList_currentItemChanged(self, current, previous): 241 """ 242 Private slot handling a change of the current item. 243 244 @param current current item (QTreeWidgetItem) 245 @param previous prevoius current item (QTreeWidgetItem) 246 """ 247 self.buttonBox.button(QDialogButtonBox.StandardButton.Open).setEnabled( 248 current is not None) 249 250 def show(self): 251 """ 252 Public method to enable/disable the project checkbox. 253 """ 254 if self.project and self.project.isOpen(): 255 self.projectCheckBox.setEnabled(True) 256 self.projectCheckBox.setChecked(True) 257 else: 258 self.projectCheckBox.setEnabled(False) 259 self.projectCheckBox.setChecked(False) 260 261 self.fileNameEdit.selectAll() 262 self.fileNameEdit.setFocus() 263 264 super().show() 265