src/eric7/UI/FindFileWidget.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
12 import time 12 import time
13 13
14 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QPoint, QUrl 14 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QPoint, QUrl
15 from PyQt6.QtGui import QCursor, QDesktopServices, QImageReader 15 from PyQt6.QtGui import QCursor, QDesktopServices, QImageReader
16 from PyQt6.QtWidgets import ( 16 from PyQt6.QtWidgets import (
17 QWidget, QApplication, QMenu, QTreeWidgetItem, QComboBox, QDialog, 17 QWidget,
18 QDialogButtonBox, QVBoxLayout 18 QApplication,
19 QMenu,
20 QTreeWidgetItem,
21 QComboBox,
22 QDialog,
23 QDialogButtonBox,
24 QVBoxLayout,
19 ) 25 )
20 26
21 from EricWidgets.EricApplication import ericApp 27 from EricWidgets.EricApplication import ericApp
22 from EricWidgets import EricMessageBox 28 from EricWidgets import EricMessageBox
23 from EricWidgets.EricPathPicker import EricPathPickerModes 29 from EricWidgets.EricPathPicker import EricPathPickerModes
31 37
32 class FindFileWidget(QWidget, Ui_FindFileWidget): 38 class FindFileWidget(QWidget, Ui_FindFileWidget):
33 """ 39 """
34 Class implementing a widget to search for text in files and replace it 40 Class implementing a widget to search for text in files and replace it
35 with some other text. 41 with some other text.
36 42
37 The occurrences found are displayed in a tree showing the file name, 43 The occurrences found are displayed in a tree showing the file name,
38 the line number and the text found. The file will be opened upon a double 44 the line number and the text found. The file will be opened upon a double
39 click onto the respective entry of the list. If the widget is in replace 45 click onto the respective entry of the list. If the widget is in replace
40 mode the line below shows the text after replacement. Replacements can 46 mode the line below shows the text after replacement. Replacements can
41 be authorized by ticking them on. Pressing the replace button performs 47 be authorized by ticking them on. Pressing the replace button performs
42 all ticked replacement operations. 48 all ticked replacement operations.
43 49
44 @signal sourceFile(str, int, str, int, int) emitted to open a source file 50 @signal sourceFile(str, int, str, int, int) emitted to open a source file
45 at a specificline 51 at a specificline
46 @signal designerFile(str) emitted to open a Qt-Designer file 52 @signal designerFile(str) emitted to open a Qt-Designer file
47 @signal linguistFile(str) emitted to open a Qt-Linguist (*.ts) file 53 @signal linguistFile(str) emitted to open a Qt-Linguist (*.ts) file
48 @signal trpreview([str]) emitted to preview Qt-Linguist (*.qm) files 54 @signal trpreview([str]) emitted to preview Qt-Linguist (*.qm) files
49 @signal pixmapFile(str) emitted to open a pixmap file 55 @signal pixmapFile(str) emitted to open a pixmap file
50 @signal svgFile(str) emitted to open a SVG file 56 @signal svgFile(str) emitted to open a SVG file
51 @signal umlFile(str) emitted to open an eric UML file 57 @signal umlFile(str) emitted to open an eric UML file
52 """ 58 """
59
53 sourceFile = pyqtSignal(str, int, str, int, int) 60 sourceFile = pyqtSignal(str, int, str, int, int)
54 designerFile = pyqtSignal(str) 61 designerFile = pyqtSignal(str)
55 linguistFile = pyqtSignal(str) 62 linguistFile = pyqtSignal(str)
56 trpreview = pyqtSignal(list) 63 trpreview = pyqtSignal(list)
57 pixmapFile = pyqtSignal(str) 64 pixmapFile = pyqtSignal(str)
58 svgFile = pyqtSignal(str) 65 svgFile = pyqtSignal(str)
59 umlFile = pyqtSignal(str) 66 umlFile = pyqtSignal(str)
60 67
61 lineRole = Qt.ItemDataRole.UserRole + 1 68 lineRole = Qt.ItemDataRole.UserRole + 1
62 startRole = Qt.ItemDataRole.UserRole + 2 69 startRole = Qt.ItemDataRole.UserRole + 2
63 endRole = Qt.ItemDataRole.UserRole + 3 70 endRole = Qt.ItemDataRole.UserRole + 3
64 replaceRole = Qt.ItemDataRole.UserRole + 4 71 replaceRole = Qt.ItemDataRole.UserRole + 4
65 md5Role = Qt.ItemDataRole.UserRole + 5 72 md5Role = Qt.ItemDataRole.UserRole + 5
66 73
67 def __init__(self, project, parent=None): 74 def __init__(self, project, parent=None):
68 """ 75 """
69 Constructor 76 Constructor
70 77
71 @param project reference to the project object 78 @param project reference to the project object
72 @type Project 79 @type Project
73 @param parent parent widget of this dialog (defaults to None) 80 @param parent parent widget of this dialog (defaults to None)
74 @type QWidget (optional) 81 @type QWidget (optional)
75 """ 82 """
76 super().__init__(parent) 83 super().__init__(parent)
77 self.setupUi(self) 84 self.setupUi(self)
78 85
79 self.layout().setContentsMargins(0, 3, 0, 0) 86 self.layout().setContentsMargins(0, 3, 0, 0)
80 87
81 self.caseToolButton.setIcon(UI.PixmapCache.getIcon("caseSensitive")) 88 self.caseToolButton.setIcon(UI.PixmapCache.getIcon("caseSensitive"))
82 self.wordToolButton.setIcon(UI.PixmapCache.getIcon("wholeWord")) 89 self.wordToolButton.setIcon(UI.PixmapCache.getIcon("wholeWord"))
83 self.regexpToolButton.setIcon(UI.PixmapCache.getIcon("regexp")) 90 self.regexpToolButton.setIcon(UI.PixmapCache.getIcon("regexp"))
84 91
85 self.dirPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) 92 self.dirPicker.setMode(EricPathPickerModes.DIRECTORY_MODE)
86 self.dirPicker.setInsertPolicy(QComboBox.InsertPolicy.InsertAtTop) 93 self.dirPicker.setInsertPolicy(QComboBox.InsertPolicy.InsertAtTop)
87 self.dirPicker.setSizeAdjustPolicy( 94 self.dirPicker.setSizeAdjustPolicy(
88 QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon) 95 QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon
89 96 )
97
90 self.stopButton.setEnabled(False) 98 self.stopButton.setEnabled(False)
91 self.stopButton.clicked.connect(self.__stopSearch) 99 self.stopButton.clicked.connect(self.__stopSearch)
92 self.stopButton.setIcon(UI.PixmapCache.getIcon("stopLoading")) 100 self.stopButton.setIcon(UI.PixmapCache.getIcon("stopLoading"))
93 self.stopButton.setAutoDefault(False) 101 self.stopButton.setAutoDefault(False)
94 102
95 self.findButton.setEnabled(False) 103 self.findButton.setEnabled(False)
96 self.findButton.clicked.connect(self.__doSearch) 104 self.findButton.clicked.connect(self.__doSearch)
97 self.findButton.setIcon(UI.PixmapCache.getIcon("find")) 105 self.findButton.setIcon(UI.PixmapCache.getIcon("find"))
98 self.findButton.setAutoDefault(False) 106 self.findButton.setAutoDefault(False)
99 107
100 self.clearButton.setEnabled(False) 108 self.clearButton.setEnabled(False)
101 self.clearButton.clicked.connect(self.__clearResults) 109 self.clearButton.clicked.connect(self.__clearResults)
102 self.clearButton.setIcon(UI.PixmapCache.getIcon("clear")) 110 self.clearButton.setIcon(UI.PixmapCache.getIcon("clear"))
103 self.clearButton.setAutoDefault(False) 111 self.clearButton.setAutoDefault(False)
104 112
105 self.replaceButton.setIcon(UI.PixmapCache.getIcon("editReplace")) 113 self.replaceButton.setIcon(UI.PixmapCache.getIcon("editReplace"))
106 self.replaceButton.setAutoDefault(False) 114 self.replaceButton.setAutoDefault(False)
107 115
108 self.modeToggleButton.clicked.connect(self.__toggleReplaceMode) 116 self.modeToggleButton.clicked.connect(self.__toggleReplaceMode)
109 117
110 self.findProgressLabel.setMaximumWidth(550) 118 self.findProgressLabel.setMaximumWidth(550)
111 119
112 self.searchHistory = Preferences.toList( 120 self.searchHistory = Preferences.toList(
113 Preferences.getSettings().value( 121 Preferences.getSettings().value("FindFileWidget/SearchHistory")
114 "FindFileWidget/SearchHistory")) 122 )
115 self.findtextCombo.lineEdit().setClearButtonEnabled(True) 123 self.findtextCombo.lineEdit().setClearButtonEnabled(True)
116 self.findtextCombo.lineEdit().returnPressed.connect(self.__doSearch) 124 self.findtextCombo.lineEdit().returnPressed.connect(self.__doSearch)
117 self.findtextCombo.setCompleter(None) 125 self.findtextCombo.setCompleter(None)
118 self.findtextCombo.addItems(self.searchHistory) 126 self.findtextCombo.addItems(self.searchHistory)
119 self.findtextCombo.setEditText("") 127 self.findtextCombo.setEditText("")
120 128
121 self.replaceHistory = Preferences.toList( 129 self.replaceHistory = Preferences.toList(
122 Preferences.getSettings().value( 130 Preferences.getSettings().value("FindFileWidget/ReplaceHistory")
123 "FindFileWidget/ReplaceHistory")) 131 )
124 self.replacetextCombo.lineEdit().setClearButtonEnabled(True) 132 self.replacetextCombo.lineEdit().setClearButtonEnabled(True)
125 self.replacetextCombo.lineEdit().returnPressed.connect(self.__doSearch) 133 self.replacetextCombo.lineEdit().returnPressed.connect(self.__doSearch)
126 self.replacetextCombo.setCompleter(None) 134 self.replacetextCombo.setCompleter(None)
127 self.replacetextCombo.addItems(self.replaceHistory) 135 self.replacetextCombo.addItems(self.replaceHistory)
128 self.replacetextCombo.setEditText("") 136 self.replacetextCombo.setEditText("")
129 137
130 self.dirHistory = Preferences.toList( 138 self.dirHistory = Preferences.toList(
131 Preferences.getSettings().value( 139 Preferences.getSettings().value("FindFileWidget/DirectoryHistory")
132 "FindFileWidget/DirectoryHistory")) 140 )
133 self.dirPicker.addItems(self.dirHistory) 141 self.dirPicker.addItems(self.dirHistory)
134 self.dirPicker.setText("") 142 self.dirPicker.setText("")
135 143
136 self.excludeHiddenCheckBox.setChecked(Preferences.toBool( 144 self.excludeHiddenCheckBox.setChecked(
137 Preferences.getSettings().value( 145 Preferences.toBool(
138 "FindFileWidget/ExcludeHidden", True) 146 Preferences.getSettings().value("FindFileWidget/ExcludeHidden", True)
139 )) 147 )
140 148 )
149
141 # ensure the file type tab is the current one 150 # ensure the file type tab is the current one
142 self.fileOptionsWidget.setCurrentWidget(self.fileTypeTab) 151 self.fileOptionsWidget.setCurrentWidget(self.fileTypeTab)
143 152
144 self.project = project 153 self.project = project
145 self.project.projectOpened.connect(self.__projectOpened) 154 self.project.projectOpened.connect(self.__projectOpened)
146 self.project.projectClosed.connect(self.__projectClosed) 155 self.project.projectClosed.connect(self.__projectClosed)
147 156
148 self.__standardListFont = self.findList.font() 157 self.__standardListFont = self.findList.font()
149 self.findList.headerItem().setText(self.findList.columnCount(), "") 158 self.findList.headerItem().setText(self.findList.columnCount(), "")
150 self.findList.header().setSortIndicator(0, Qt.SortOrder.AscendingOrder) 159 self.findList.header().setSortIndicator(0, Qt.SortOrder.AscendingOrder)
151 self.__section0Size = self.findList.header().sectionSize(0) 160 self.__section0Size = self.findList.header().sectionSize(0)
152 self.findList.setExpandsOnDoubleClick(False) 161 self.findList.setExpandsOnDoubleClick(False)
153 162
154 # Qt Designer form files 163 # Qt Designer form files
155 self.filterForms = r'.*\.ui$' 164 self.filterForms = r".*\.ui$"
156 self.formsExt = ['*.ui'] 165 self.formsExt = ["*.ui"]
157 166
158 # Corba interface files 167 # Corba interface files
159 self.filterInterfaces = r'.*\.idl$' 168 self.filterInterfaces = r".*\.idl$"
160 self.interfacesExt = ['*.idl'] 169 self.interfacesExt = ["*.idl"]
161 170
162 # Protobuf protocol files 171 # Protobuf protocol files
163 self.filterProtocols = r'.*\.proto$' 172 self.filterProtocols = r".*\.proto$"
164 self.protocolsExt = ['*.proto'] 173 self.protocolsExt = ["*.proto"]
165 174
166 # Qt resources files 175 # Qt resources files
167 self.filterResources = r'.*\.qrc$' 176 self.filterResources = r".*\.qrc$"
168 self.resourcesExt = ['*.qrc'] 177 self.resourcesExt = ["*.qrc"]
169 178
170 self.__cancelSearch = False 179 self.__cancelSearch = False
171 self.__lastFileItem = None 180 self.__lastFileItem = None
172 self.__populating = False 181 self.__populating = False
173 182
174 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) 183 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
175 self.customContextMenuRequested.connect(self.__contextMenuRequested) 184 self.customContextMenuRequested.connect(self.__contextMenuRequested)
176 185
177 self.__replaceMode = True 186 self.__replaceMode = True
178 self.__toggleReplaceMode() 187 self.__toggleReplaceMode()
179 188
180 def __createItem(self, file, line, text, start, end, replTxt="", md5=""): 189 def __createItem(self, file, line, text, start, end, replTxt="", md5=""):
181 """ 190 """
182 Private method to create an entry in the file list. 191 Private method to create an entry in the file list.
183 192
184 @param file filename of file 193 @param file filename of file
185 @type str 194 @type str
186 @param line line number 195 @param line line number
187 @type int 196 @type int
188 @param text text found 197 @param text text found
201 self.__lastFileItem = QTreeWidgetItem(self.findList, [file]) 210 self.__lastFileItem = QTreeWidgetItem(self.findList, [file])
202 self.__lastFileItem.setFirstColumnSpanned(True) 211 self.__lastFileItem.setFirstColumnSpanned(True)
203 self.__lastFileItem.setExpanded(True) 212 self.__lastFileItem.setExpanded(True)
204 if self.__replaceMode: 213 if self.__replaceMode:
205 self.__lastFileItem.setFlags( 214 self.__lastFileItem.setFlags(
206 self.__lastFileItem.flags() | 215 self.__lastFileItem.flags()
207 Qt.ItemFlag.ItemIsUserCheckable | 216 | Qt.ItemFlag.ItemIsUserCheckable
208 Qt.ItemFlag.ItemIsAutoTristate) 217 | Qt.ItemFlag.ItemIsAutoTristate
218 )
209 self.__lastFileItem.setData(0, self.md5Role, md5) 219 self.__lastFileItem.setData(0, self.md5Role, md5)
210 220
211 itm = QTreeWidgetItem(self.__lastFileItem) 221 itm = QTreeWidgetItem(self.__lastFileItem)
212 itm.setTextAlignment(0, Qt.AlignmentFlag.AlignRight) 222 itm.setTextAlignment(0, Qt.AlignmentFlag.AlignRight)
213 itm.setData(0, Qt.ItemDataRole.DisplayRole, line) 223 itm.setData(0, Qt.ItemDataRole.DisplayRole, line)
214 itm.setData(1, Qt.ItemDataRole.DisplayRole, text) 224 itm.setData(1, Qt.ItemDataRole.DisplayRole, text)
215 itm.setData(0, self.lineRole, line) 225 itm.setData(0, self.lineRole, line)
216 itm.setData(0, self.startRole, start) 226 itm.setData(0, self.startRole, start)
217 itm.setData(0, self.endRole, end) 227 itm.setData(0, self.endRole, end)
218 itm.setData(0, self.replaceRole, replTxt) 228 itm.setData(0, self.replaceRole, replTxt)
219 if self.__replaceMode: 229 if self.__replaceMode:
220 itm.setFlags(itm.flags() | 230 itm.setFlags(itm.flags() | Qt.ItemFlag.ItemIsUserCheckable)
221 Qt.ItemFlag.ItemIsUserCheckable)
222 itm.setCheckState(0, Qt.CheckState.Checked) 231 itm.setCheckState(0, Qt.CheckState.Checked)
223 self.replaceButton.setEnabled(True) 232 self.replaceButton.setEnabled(True)
224 233
225 def activate(self, replaceMode=False, txt="", searchDir="", 234 def activate(self, replaceMode=False, txt="", searchDir="", openFiles=False):
226 openFiles=False):
227 """ 235 """
228 Public method to activate the widget with a given mode, a text 236 Public method to activate the widget with a given mode, a text
229 to search for and some search parameters. 237 to search for and some search parameters.
230 238
231 @param replaceMode flag indicating replacement mode (defaults to False) 239 @param replaceMode flag indicating replacement mode (defaults to False)
232 @type bool (optional) 240 @type bool (optional)
233 @param txt text to be searched for (defaults to "") 241 @param txt text to be searched for (defaults to "")
234 @type str (optional) 242 @type str (optional)
235 @param searchDir directory to search in (defaults to "") 243 @param searchDir directory to search in (defaults to "")
242 self.projectButton.setEnabled(True) 250 self.projectButton.setEnabled(True)
243 self.projectButton.setChecked(True) 251 self.projectButton.setChecked(True)
244 else: 252 else:
245 self.projectButton.setEnabled(False) 253 self.projectButton.setEnabled(False)
246 self.dirButton.setChecked(True) 254 self.dirButton.setChecked(True)
247 255
248 self.findtextCombo.setEditText(txt) 256 self.findtextCombo.setEditText(txt)
249 self.findtextCombo.lineEdit().selectAll() 257 self.findtextCombo.lineEdit().selectAll()
250 self.findtextCombo.setFocus() 258 self.findtextCombo.setFocus()
251 259
252 if self.__replaceMode != replaceMode: 260 if self.__replaceMode != replaceMode:
253 self.__toggleReplaceMode() 261 self.__toggleReplaceMode()
254 262
255 if searchDir: 263 if searchDir:
256 self.__setSearchDirectory(searchDir) 264 self.__setSearchDirectory(searchDir)
257 if openFiles: 265 if openFiles:
258 self.__setOpenFiles() 266 self.__setOpenFiles()
259 267
260 @pyqtSlot() 268 @pyqtSlot()
261 def __toggleReplaceMode(self): 269 def __toggleReplaceMode(self):
262 """ 270 """
263 Private slot to toggle the dialog mode. 271 Private slot to toggle the dialog mode.
264 """ 272 """
265 self.__replaceMode = not self.__replaceMode 273 self.__replaceMode = not self.__replaceMode
266 274
267 # change some interface elements and properties 275 # change some interface elements and properties
268 self.findList.clear() 276 self.findList.clear()
269 self.clearButton.setEnabled(False) 277 self.clearButton.setEnabled(False)
270 278
271 if self.__replaceMode: 279 if self.__replaceMode:
272 self.replaceButton.show() 280 self.replaceButton.show()
273 self.replaceLabel.show() 281 self.replaceLabel.show()
274 self.replacetextCombo.show() 282 self.replacetextCombo.show()
275 283
276 self.replaceButton.setEnabled(False) 284 self.replaceButton.setEnabled(False)
277 self.replacetextCombo.setEditText("") 285 self.replacetextCombo.setEditText("")
278 286
279 font = Preferences.getEditorOtherFonts("MonospacedFont") 287 font = Preferences.getEditorOtherFonts("MonospacedFont")
280 self.findList.setFont(font) 288 self.findList.setFont(font)
281 289
282 self.modeToggleButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) 290 self.modeToggleButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
283 else: 291 else:
284 self.replaceLabel.hide() 292 self.replaceLabel.hide()
285 self.replacetextCombo.hide() 293 self.replacetextCombo.hide()
286 self.replaceButton.hide() 294 self.replaceButton.hide()
287 295
288 self.findList.setFont(self.__standardListFont) 296 self.findList.setFont(self.__standardListFont)
289 297
290 self.modeToggleButton.setIcon(UI.PixmapCache.getIcon("1downarrow")) 298 self.modeToggleButton.setIcon(UI.PixmapCache.getIcon("1downarrow"))
291 299
292 @pyqtSlot() 300 @pyqtSlot()
293 def __projectOpened(self): 301 def __projectOpened(self):
294 """ 302 """
295 Private slot to react to the opening of a project. 303 Private slot to react to the opening of a project.
296 """ 304 """
297 self.projectButton.setEnabled(True) 305 self.projectButton.setEnabled(True)
298 self.projectButton.setChecked(True) 306 self.projectButton.setChecked(True)
299 307
300 @pyqtSlot() 308 @pyqtSlot()
301 def __projectClosed(self): 309 def __projectClosed(self):
302 """ 310 """
303 Private slot to react to the closing of a project. 311 Private slot to react to the closing of a project.
304 """ 312 """
305 self.projectButton.setEnabled(False) 313 self.projectButton.setEnabled(False)
306 if self.projectButton.isChecked(): 314 if self.projectButton.isChecked():
307 self.dirButton.setChecked(True) 315 self.dirButton.setChecked(True)
308 316
309 @pyqtSlot(str) 317 @pyqtSlot(str)
310 def on_findtextCombo_editTextChanged(self, text): 318 def on_findtextCombo_editTextChanged(self, text):
311 """ 319 """
312 Private slot to handle the editTextChanged signal of the find 320 Private slot to handle the editTextChanged signal of the find
313 text combo. 321 text combo.
314 322
315 @param text (ignored) 323 @param text (ignored)
316 """ 324 """
317 self.__enableFindButton() 325 self.__enableFindButton()
318 326
319 @pyqtSlot(str) 327 @pyqtSlot(str)
320 def on_replacetextCombo_editTextChanged(self, text): 328 def on_replacetextCombo_editTextChanged(self, text):
321 """ 329 """
322 Private slot to handle the editTextChanged signal of the replace 330 Private slot to handle the editTextChanged signal of the replace
323 text combo. 331 text combo.
324 332
325 @param text (ignored) 333 @param text (ignored)
326 """ 334 """
327 self.__enableFindButton() 335 self.__enableFindButton()
328 336
329 @pyqtSlot(str) 337 @pyqtSlot(str)
330 def on_dirPicker_editTextChanged(self, text): 338 def on_dirPicker_editTextChanged(self, text):
331 """ 339 """
332 Private slot to handle the textChanged signal of the directory 340 Private slot to handle the textChanged signal of the directory
333 picker. 341 picker.
334 342
335 @param text (ignored) 343 @param text (ignored)
336 """ 344 """
337 self.__enableFindButton() 345 self.__enableFindButton()
338 346
339 @pyqtSlot() 347 @pyqtSlot()
340 def on_projectButton_clicked(self): 348 def on_projectButton_clicked(self):
341 """ 349 """
342 Private slot to handle the selection of the 'Project' radio button. 350 Private slot to handle the selection of the 'Project' radio button.
343 """ 351 """
344 self.__enableFindButton() 352 self.__enableFindButton()
345 353
346 @pyqtSlot() 354 @pyqtSlot()
347 def on_dirButton_clicked(self): 355 def on_dirButton_clicked(self):
348 """ 356 """
349 Private slot to handle the selection of the 'Directory' radio button. 357 Private slot to handle the selection of the 'Directory' radio button.
350 """ 358 """
351 self.__enableFindButton() 359 self.__enableFindButton()
352 360
353 @pyqtSlot() 361 @pyqtSlot()
354 def on_openFilesButton_clicked(self): 362 def on_openFilesButton_clicked(self):
355 """ 363 """
356 Private slot to handle the selection of the 'Open Files' radio button. 364 Private slot to handle the selection of the 'Open Files' radio button.
357 """ 365 """
358 self.__enableFindButton() 366 self.__enableFindButton()
359 367
360 @pyqtSlot() 368 @pyqtSlot()
361 def on_filterCheckBox_clicked(self): 369 def on_filterCheckBox_clicked(self):
362 """ 370 """
363 Private slot to handle the selection of the file filter check box. 371 Private slot to handle the selection of the file filter check box.
364 """ 372 """
365 self.__enableFindButton() 373 self.__enableFindButton()
366 374
367 @pyqtSlot(str) 375 @pyqtSlot(str)
368 def on_filterEdit_textEdited(self, text): 376 def on_filterEdit_textEdited(self, text):
369 """ 377 """
370 Private slot to handle the textChanged signal of the file filter edit. 378 Private slot to handle the textChanged signal of the file filter edit.
371 379
372 @param text (ignored) 380 @param text (ignored)
373 """ 381 """
374 self.__enableFindButton() 382 self.__enableFindButton()
375 383
376 @pyqtSlot() 384 @pyqtSlot()
377 def __enableFindButton(self): 385 def __enableFindButton(self):
378 """ 386 """
379 Private slot called to enable the find button. 387 Private slot called to enable the find button.
380 """ 388 """
381 if ( 389 if (
382 self.findtextCombo.currentText() == "" or 390 self.findtextCombo.currentText() == ""
383 (self.dirButton.isChecked() and 391 or (
384 (self.dirPicker.currentText() == "" or 392 self.dirButton.isChecked()
385 not os.path.exists(os.path.abspath( 393 and (
386 self.dirPicker.currentText())))) or 394 self.dirPicker.currentText() == ""
387 (self.filterCheckBox.isChecked() and 395 or not os.path.exists(os.path.abspath(self.dirPicker.currentText()))
388 self.filterEdit.text() == "") 396 )
397 )
398 or (self.filterCheckBox.isChecked() and self.filterEdit.text() == "")
389 ): 399 ):
390 self.findButton.setEnabled(False) 400 self.findButton.setEnabled(False)
391 else: 401 else:
392 self.findButton.setEnabled(True) 402 self.findButton.setEnabled(True)
393 403
394 def __stripEol(self, txt): 404 def __stripEol(self, txt):
395 """ 405 """
396 Private method to strip the eol part. 406 Private method to strip the eol part.
397 407
398 @param txt line of text that should be treated 408 @param txt line of text that should be treated
399 @type str 409 @type str
400 @return text with eol stripped 410 @return text with eol stripped
401 @rtype str 411 @rtype str
402 """ 412 """
403 return txt.replace("\r", "").replace("\n", "") 413 return txt.replace("\r", "").replace("\n", "")
404 414
405 @pyqtSlot() 415 @pyqtSlot()
406 def __stopSearch(self): 416 def __stopSearch(self):
407 """ 417 """
408 Private slot to handle the stop button being pressed. 418 Private slot to handle the stop button being pressed.
409 """ 419 """
410 self.__cancelSearch = True 420 self.__cancelSearch = True
411 421
412 @pyqtSlot() 422 @pyqtSlot()
413 def __doSearch(self): 423 def __doSearch(self):
414 """ 424 """
415 Private slot to handle the find button being pressed. 425 Private slot to handle the find button being pressed.
416 """ 426 """
417 if ( 427 if (
418 self.__replaceMode and 428 self.__replaceMode
419 not ericApp().getObject("ViewManager").checkAllDirty() 429 and not ericApp().getObject("ViewManager").checkAllDirty()
420 ): 430 ):
421 return 431 return
422 432
423 self.__cancelSearch = False 433 self.__cancelSearch = False
424 434
425 if self.filterCheckBox.isChecked(): 435 if self.filterCheckBox.isChecked():
426 fileFilter = self.filterEdit.text() 436 fileFilter = self.filterEdit.text()
427 fileFilterList = [ 437 fileFilterList = [
428 "^{0}$".format(filter.replace(".", r"\.").replace("*", ".*")) 438 "^{0}$".format(filter.replace(".", r"\.").replace("*", ".*"))
429 for filter in fileFilter.split(";") 439 for filter in fileFilter.split(";")
430 ] 440 ]
431 filterRe = re.compile("|".join(fileFilterList)) 441 filterRe = re.compile("|".join(fileFilterList))
432 442
433 if self.projectButton.isChecked(): 443 if self.projectButton.isChecked():
434 if self.filterCheckBox.isChecked(): 444 if self.filterCheckBox.isChecked():
435 files = [ 445 files = [
436 self.project.getRelativePath(file) 446 self.project.getRelativePath(file)
437 for file in 447 for file in self.__getFileList(
438 self.__getFileList(
439 self.project.getProjectPath(), 448 self.project.getProjectPath(),
440 filterRe, 449 filterRe,
441 excludeHiddenDirs=self.excludeHiddenCheckBox 450 excludeHiddenDirs=self.excludeHiddenCheckBox.isChecked(),
442 .isChecked(),
443 ) 451 )
444 ] 452 ]
445 else: 453 else:
446 files = [] 454 files = []
447 if self.sourcesCheckBox.isChecked(): 455 if self.sourcesCheckBox.isChecked():
455 if self.resourcesCheckBox.isChecked(): 463 if self.resourcesCheckBox.isChecked():
456 files += self.project.pdata["RESOURCES"] 464 files += self.project.pdata["RESOURCES"]
457 elif self.dirButton.isChecked(): 465 elif self.dirButton.isChecked():
458 if not self.filterCheckBox.isChecked(): 466 if not self.filterCheckBox.isChecked():
459 filters = [] 467 filters = []
460 if ( 468 if self.project.isOpen() and os.path.abspath(
461 self.project.isOpen() and 469 self.dirPicker.currentText()
462 os.path.abspath(self.dirPicker.currentText()).startswith( 470 ).startswith(self.project.getProjectPath()):
463 self.project.getProjectPath())
464 ):
465 if self.sourcesCheckBox.isChecked(): 471 if self.sourcesCheckBox.isChecked():
466 filters.extend([ 472 filters.extend(
467 "^{0}$".format( 473 [
468 assoc.replace(".", r"\.").replace("*", ".*") 474 "^{0}$".format(
469 ) for assoc in 475 assoc.replace(".", r"\.").replace("*", ".*")
470 self.project.getFiletypeAssociations("SOURCES") 476 )
471 ]) 477 for assoc in self.project.getFiletypeAssociations(
478 "SOURCES"
479 )
480 ]
481 )
472 if self.formsCheckBox.isChecked(): 482 if self.formsCheckBox.isChecked():
473 filters.extend([ 483 filters.extend(
474 "^{0}$".format( 484 [
475 assoc.replace(".", r"\.").replace("*", ".*") 485 "^{0}$".format(
476 ) for assoc in 486 assoc.replace(".", r"\.").replace("*", ".*")
477 self.project.getFiletypeAssociations("FORMS") 487 )
478 ]) 488 for assoc in self.project.getFiletypeAssociations(
489 "FORMS"
490 )
491 ]
492 )
479 if self.interfacesCheckBox.isChecked(): 493 if self.interfacesCheckBox.isChecked():
480 filters.extend([ 494 filters.extend(
481 "^{0}$".format( 495 [
482 assoc.replace(".", r"\.").replace("*", ".*") 496 "^{0}$".format(
483 ) for assoc in 497 assoc.replace(".", r"\.").replace("*", ".*")
484 self.project.getFiletypeAssociations("INTERFACES") 498 )
485 ]) 499 for assoc in self.project.getFiletypeAssociations(
500 "INTERFACES"
501 )
502 ]
503 )
486 if self.protocolsCheckBox.isChecked(): 504 if self.protocolsCheckBox.isChecked():
487 filters.extend([ 505 filters.extend(
488 "^{0}$".format( 506 [
489 assoc.replace(".", r"\.").replace("*", ".*") 507 "^{0}$".format(
490 ) for assoc in 508 assoc.replace(".", r"\.").replace("*", ".*")
491 self.project.getFiletypeAssociations("PROTOCOLS") 509 )
492 ]) 510 for assoc in self.project.getFiletypeAssociations(
511 "PROTOCOLS"
512 )
513 ]
514 )
493 if self.resourcesCheckBox.isChecked(): 515 if self.resourcesCheckBox.isChecked():
494 filters.extend([ 516 filters.extend(
495 "^{0}$".format( 517 [
496 assoc.replace(".", r"\.").replace("*", ".*") 518 "^{0}$".format(
497 ) for assoc in 519 assoc.replace(".", r"\.").replace("*", ".*")
498 self.project.getFiletypeAssociations("RESOURCES") 520 )
499 ]) 521 for assoc in self.project.getFiletypeAssociations(
522 "RESOURCES"
523 )
524 ]
525 )
500 else: 526 else:
501 if self.sourcesCheckBox.isChecked(): 527 if self.sourcesCheckBox.isChecked():
502 filters.extend([ 528 filters.extend(
503 "^{0}$".format( 529 [
504 assoc.replace(".", r"\.").replace("*", ".*")) 530 "^{0}$".format(
505 for assoc in list( 531 assoc.replace(".", r"\.").replace("*", ".*")
506 Preferences.getEditorLexerAssocs().keys()) 532 )
507 if assoc not in 533 for assoc in list(
508 self.formsExt + self.interfacesExt + 534 Preferences.getEditorLexerAssocs().keys()
509 self.protocolsExt + self.resourcesExt 535 )
510 ]) 536 if assoc
537 not in self.formsExt
538 + self.interfacesExt
539 + self.protocolsExt
540 + self.resourcesExt
541 ]
542 )
511 if self.formsCheckBox.isChecked(): 543 if self.formsCheckBox.isChecked():
512 filters.append(self.filterForms) 544 filters.append(self.filterForms)
513 if self.interfacesCheckBox.isChecked(): 545 if self.interfacesCheckBox.isChecked():
514 filters.append(self.filterInterfaces) 546 filters.append(self.filterInterfaces)
515 if self.protocolsCheckBox.isChecked(): 547 if self.protocolsCheckBox.isChecked():
526 ) 558 )
527 elif self.openFilesButton.isChecked(): 559 elif self.openFilesButton.isChecked():
528 vm = ericApp().getObject("ViewManager") 560 vm = ericApp().getObject("ViewManager")
529 vm.checkAllDirty() 561 vm.checkAllDirty()
530 files = vm.getOpenFilenames() 562 files = vm.getOpenFilenames()
531 563
532 self.findList.clear() 564 self.findList.clear()
533 QApplication.processEvents() 565 QApplication.processEvents()
534 self.findProgress.setMaximum(len(files)) 566 self.findProgress.setMaximum(len(files))
535 567
536 # retrieve the values 568 # retrieve the values
537 reg = self.regexpToolButton.isChecked() 569 reg = self.regexpToolButton.isChecked()
538 wo = self.wordToolButton.isChecked() 570 wo = self.wordToolButton.isChecked()
539 cs = self.caseToolButton.isChecked() 571 cs = self.caseToolButton.isChecked()
540 ct = self.findtextCombo.currentText() 572 ct = self.findtextCombo.currentText()
548 search = re.compile(txt, flags) 580 search = re.compile(txt, flags)
549 except re.error as why: 581 except re.error as why:
550 EricMessageBox.critical( 582 EricMessageBox.critical(
551 self, 583 self,
552 self.tr("Invalid search expression"), 584 self.tr("Invalid search expression"),
553 self.tr("""<p>The search expression is not valid.</p>""" 585 self.tr(
554 """<p>Error: {0}</p>""").format(str(why))) 586 """<p>The search expression is not valid.</p>"""
587 """<p>Error: {0}</p>"""
588 ).format(str(why)),
589 )
555 self.stopButton.setEnabled(False) 590 self.stopButton.setEnabled(False)
556 self.findButton.setEnabled(True) 591 self.findButton.setEnabled(True)
557 return 592 return
558 # reset the findtextCombo 593 # reset the findtextCombo
559 if ct in self.searchHistory: 594 if ct in self.searchHistory:
560 self.searchHistory.remove(ct) 595 self.searchHistory.remove(ct)
561 self.searchHistory.insert(0, ct) 596 self.searchHistory.insert(0, ct)
562 self.findtextCombo.clear() 597 self.findtextCombo.clear()
563 self.findtextCombo.addItems(self.searchHistory) 598 self.findtextCombo.addItems(self.searchHistory)
564 Preferences.getSettings().setValue( 599 Preferences.getSettings().setValue(
565 "FindFileWidget/SearchHistory", 600 "FindFileWidget/SearchHistory", self.searchHistory[:30]
566 self.searchHistory[:30]) 601 )
567 Preferences.getSettings().setValue( 602 Preferences.getSettings().setValue(
568 "FindFileWidget/ExcludeHidden", 603 "FindFileWidget/ExcludeHidden", self.excludeHiddenCheckBox.isChecked()
569 self.excludeHiddenCheckBox.isChecked()) 604 )
570 605
571 if self.__replaceMode: 606 if self.__replaceMode:
572 replTxt = self.replacetextCombo.currentText() 607 replTxt = self.replacetextCombo.currentText()
573 if replTxt in self.replaceHistory: 608 if replTxt in self.replaceHistory:
574 self.replaceHistory.remove(replTxt) 609 self.replaceHistory.remove(replTxt)
575 self.replaceHistory.insert(0, replTxt) 610 self.replaceHistory.insert(0, replTxt)
576 self.replacetextCombo.clear() 611 self.replacetextCombo.clear()
577 self.replacetextCombo.addItems(self.replaceHistory) 612 self.replacetextCombo.addItems(self.replaceHistory)
578 Preferences.getSettings().setValue( 613 Preferences.getSettings().setValue(
579 "FindFileWidget/ReplaceHistory", 614 "FindFileWidget/ReplaceHistory", self.replaceHistory[:30]
580 self.replaceHistory[:30]) 615 )
581 616
582 if self.dirButton.isChecked(): 617 if self.dirButton.isChecked():
583 searchDir = self.dirPicker.currentText() 618 searchDir = self.dirPicker.currentText()
584 if searchDir in self.dirHistory: 619 if searchDir in self.dirHistory:
585 self.dirHistory.remove(searchDir) 620 self.dirHistory.remove(searchDir)
586 self.dirHistory.insert(0, searchDir) 621 self.dirHistory.insert(0, searchDir)
587 self.dirPicker.clear() 622 self.dirPicker.clear()
588 self.dirPicker.addItems(self.dirHistory) 623 self.dirPicker.addItems(self.dirHistory)
589 self.dirPicker.setText(self.dirHistory[0]) 624 self.dirPicker.setText(self.dirHistory[0])
590 Preferences.getSettings().setValue( 625 Preferences.getSettings().setValue(
591 "FindFileWidget/DirectoryHistory", 626 "FindFileWidget/DirectoryHistory", self.dirHistory[:30]
592 self.dirHistory[:30]) 627 )
593 628
594 # set the button states 629 # set the button states
595 self.stopButton.setEnabled(True) 630 self.stopButton.setEnabled(True)
596 self.findButton.setEnabled(False) 631 self.findButton.setEnabled(False)
597 self.clearButton.setEnabled(False) 632 self.clearButton.setEnabled(False)
598 633
599 # now go through all the files 634 # now go through all the files
600 self.__populating = True 635 self.__populating = True
601 self.findList.setUpdatesEnabled(False) 636 self.findList.setUpdatesEnabled(False)
602 occurrences = 0 637 occurrences = 0
603 fileOccurrences = 0 638 fileOccurrences = 0
604 for progress, file in enumerate(files, start=1): 639 for progress, file in enumerate(files, start=1):
605 self.__lastFileItem = None 640 self.__lastFileItem = None
606 found = False 641 found = False
607 if self.__cancelSearch: 642 if self.__cancelSearch:
608 break 643 break
609 644
610 fn = ( 645 fn = (
611 os.path.join(self.project.getProjectPath(), file) 646 os.path.join(self.project.getProjectPath(), file)
612 if self.projectButton.isChecked() else 647 if self.projectButton.isChecked()
613 file 648 else file
614 ) 649 )
615 # read the file and split it into textlines 650 # read the file and split it into textlines
616 try: 651 try:
617 text, encoding, hashStr = Utilities.readEncodedFileWithHash(fn) 652 text, encoding, hashStr = Utilities.readEncodedFileWithHash(fn)
618 lines = text.splitlines(True) 653 lines = text.splitlines(True)
619 except (UnicodeError, OSError): 654 except (UnicodeError, OSError):
620 self.findProgress.setValue(progress) 655 self.findProgress.setValue(progress)
621 continue 656 continue
622 657
623 now = time.monotonic() 658 now = time.monotonic()
624 # now perform the search and display the lines found 659 # now perform the search and display the lines found
625 for count, line in enumerate(lines, start=1): 660 for count, line in enumerate(lines, start=1):
626 if self.__cancelSearch: 661 if self.__cancelSearch:
627 break 662 break
628 663
629 contains = search.search(line) 664 contains = search.search(line)
630 if contains: 665 if contains:
631 occurrences += 1 666 occurrences += 1
632 found = True 667 found = True
633 start = contains.start() 668 start = contains.start()
640 if len(line) > 1024: 675 if len(line) > 1024:
641 line = "{0} ...".format(line[:1024]) 676 line = "{0} ...".format(line[:1024])
642 if self.__replaceMode: 677 if self.__replaceMode:
643 if len(rline) > 1024: 678 if len(rline) > 1024:
644 rline = "{0} ...".format(line[:1024]) 679 rline = "{0} ...".format(line[:1024])
645 line = "- {0}\n+ {1}".format( 680 line = "- {0}\n+ {1}".format(line, self.__stripEol(rline))
646 line, self.__stripEol(rline)) 681 self.__createItem(file, count, line, start, end, rline, hashStr)
647 self.__createItem(file, count, line, start, end, 682
648 rline, hashStr)
649
650 if time.monotonic() - now > 0.01: 683 if time.monotonic() - now > 0.01:
651 QApplication.processEvents() 684 QApplication.processEvents()
652 now = time.monotonic() 685 now = time.monotonic()
653 686
654 if found: 687 if found:
655 fileOccurrences += 1 688 fileOccurrences += 1
656 self.findProgress.setValue(progress) 689 self.findProgress.setValue(progress)
657 690
658 if not files: 691 if not files:
659 self.findProgress.setMaximum(1) 692 self.findProgress.setMaximum(1)
660 self.findProgress.setValue(1) 693 self.findProgress.setValue(1)
661 694
662 resultFormat = self.tr("{0} / {1}", "occurrences / files") 695 resultFormat = self.tr("{0} / {1}", "occurrences / files")
663 self.findProgressLabel.setPath(resultFormat.format( 696 self.findProgressLabel.setPath(
664 self.tr("%n occurrence(s)", "", occurrences), 697 resultFormat.format(
665 self.tr("%n file(s)", "", fileOccurrences))) 698 self.tr("%n occurrence(s)", "", occurrences),
666 699 self.tr("%n file(s)", "", fileOccurrences),
700 )
701 )
702
667 self.findList.setUpdatesEnabled(True) 703 self.findList.setUpdatesEnabled(True)
668 self.findList.sortItems(self.findList.sortColumn(), 704 self.findList.sortItems(
669 self.findList.header().sortIndicatorOrder()) 705 self.findList.sortColumn(), self.findList.header().sortIndicatorOrder()
706 )
670 self.findList.resizeColumnToContents(1) 707 self.findList.resizeColumnToContents(1)
671 if self.__replaceMode: 708 if self.__replaceMode:
672 self.findList.header().resizeSection(0, self.__section0Size + 30) 709 self.findList.header().resizeSection(0, self.__section0Size + 30)
673 self.findList.header().setStretchLastSection(True) 710 self.findList.header().setStretchLastSection(True)
674 self.__populating = False 711 self.__populating = False
675 712
676 self.stopButton.setEnabled(False) 713 self.stopButton.setEnabled(False)
677 self.findButton.setEnabled(True) 714 self.findButton.setEnabled(True)
678 self.clearButton.setEnabled(self.findList.topLevelItemCount() != 0) 715 self.clearButton.setEnabled(self.findList.topLevelItemCount() != 0)
679 716
680 @pyqtSlot() 717 @pyqtSlot()
681 def __clearResults(self): 718 def __clearResults(self):
682 """ 719 """
683 Private slot to clear the current search results. 720 Private slot to clear the current search results.
684 """ 721 """
685 self.findList.clear() 722 self.findList.clear()
686 self.replaceButton.setEnabled(False) 723 self.replaceButton.setEnabled(False)
687 self.clearButton.setEnabled(False) 724 self.clearButton.setEnabled(False)
688 self.findProgressLabel.setPath("") 725 self.findProgressLabel.setPath("")
689 self.findProgress.setValue(0) 726 self.findProgress.setValue(0)
690 727
691 @pyqtSlot(QTreeWidgetItem, int) 728 @pyqtSlot(QTreeWidgetItem, int)
692 def on_findList_itemDoubleClicked(self, itm, column): 729 def on_findList_itemDoubleClicked(self, itm, column):
693 """ 730 """
694 Private slot to handle the double click on a file item. 731 Private slot to handle the double click on a file item.
695 732
696 It emits a signal depending on the file extension. 733 It emits a signal depending on the file extension.
697 734
698 @param itm the double clicked tree item 735 @param itm the double clicked tree item
699 @type QTreeWidgetItem 736 @type QTreeWidgetItem
700 @param column column that was double clicked (ignored) 737 @param column column that was double clicked (ignored)
701 @type int 738 @type int
702 """ 739 """
708 else: 745 else:
709 file = itm.text(0) 746 file = itm.text(0)
710 line = 1 747 line = 1
711 start = 0 748 start = 0
712 end = 0 749 end = 0
713 750
714 fileName = ( 751 fileName = (
715 os.path.join(self.project.getProjectPath(), file) 752 os.path.join(self.project.getProjectPath(), file)
716 if self.project.isOpen() else 753 if self.project.isOpen()
717 file 754 else file
718 ) 755 )
719 fileExt = os.path.splitext(fileName)[1] 756 fileExt = os.path.splitext(fileName)[1]
720 757
721 if fileExt == ".ui": 758 if fileExt == ".ui":
722 self.designerFile.emit(fileName) 759 self.designerFile.emit(fileName)
723 elif fileExt == ".ts": 760 elif fileExt == ".ts":
724 self.linguistFile.emit(fileName) 761 self.linguistFile.emit(fileName)
725 elif fileExt == ".qm": 762 elif fileExt == ".qm":
733 else: 770 else:
734 if Utilities.MimeTypes.isTextFile(fileName): 771 if Utilities.MimeTypes.isTextFile(fileName):
735 self.sourceFile.emit(fileName, line, "", start, end) 772 self.sourceFile.emit(fileName, line, "", start, end)
736 else: 773 else:
737 QDesktopServices.openUrl(QUrl(fileName)) 774 QDesktopServices.openUrl(QUrl(fileName))
738 775
739 def __getFileList(self, path, filterRe, excludeHiddenDirs=False, 776 def __getFileList(
740 excludeHiddenFiles=False): 777 self, path, filterRe, excludeHiddenDirs=False, excludeHiddenFiles=False
778 ):
741 """ 779 """
742 Private method to get a list of files to search. 780 Private method to get a list of files to search.
743 781
744 @param path the root directory to search in 782 @param path the root directory to search in
745 @type str 783 @type str
746 @param filterRe regular expression defining the filter 784 @param filterRe regular expression defining the filter
747 criteria 785 criteria
748 @type regexp object 786 @type regexp object
754 @rtype list of str 792 @rtype list of str
755 """ 793 """
756 path = os.path.abspath(path) 794 path = os.path.abspath(path)
757 files = [] 795 files = []
758 for dirname, dirs, filenames in os.walk(path): 796 for dirname, dirs, filenames in os.walk(path):
759 files.extend([ 797 files.extend(
760 os.path.join(dirname, f) for f in filenames 798 [
761 if (not (excludeHiddenFiles and f.startswith(".")) and 799 os.path.join(dirname, f)
762 re.match(filterRe, f)) 800 for f in filenames
763 ]) 801 if (
802 not (excludeHiddenFiles and f.startswith("."))
803 and re.match(filterRe, f)
804 )
805 ]
806 )
764 if excludeHiddenDirs: 807 if excludeHiddenDirs:
765 for d in dirs[:]: 808 for d in dirs[:]:
766 if d .startswith("."): 809 if d.startswith("."):
767 dirs.remove(d) 810 dirs.remove(d)
768 return files 811 return files
769 812
770 def __setSearchDirectory(self, searchDir): 813 def __setSearchDirectory(self, searchDir):
771 """ 814 """
772 Private slot to set the name of the directory to search in. 815 Private slot to set the name of the directory to search in.
773 816
774 @param searchDir name of the directory to search in 817 @param searchDir name of the directory to search in
775 @type str 818 @type str
776 """ 819 """
777 self.dirButton.setChecked(True) 820 self.dirButton.setChecked(True)
778 self.dirPicker.setEditText(Utilities.toNativeSeparators(searchDir)) 821 self.dirPicker.setEditText(Utilities.toNativeSeparators(searchDir))
779 822
780 @pyqtSlot() 823 @pyqtSlot()
781 def __setOpenFiles(self): 824 def __setOpenFiles(self):
782 """ 825 """
783 Private slot to set the mode to search in open files. 826 Private slot to set the mode to search in open files.
784 """ 827 """
785 self.openFilesButton.setChecked(True) 828 self.openFilesButton.setChecked(True)
786 829
787 @pyqtSlot() 830 @pyqtSlot()
788 def on_replaceButton_clicked(self): 831 def on_replaceButton_clicked(self):
789 """ 832 """
790 Private slot to perform the requested replace actions. 833 Private slot to perform the requested replace actions.
791 """ 834 """
792 self.findProgress.setMaximum(self.findList.topLevelItemCount()) 835 self.findProgress.setMaximum(self.findList.topLevelItemCount())
793 self.findProgress.setValue(0) 836 self.findProgress.setValue(0)
794 837
795 for index in range(self.findList.topLevelItemCount()): 838 for index in range(self.findList.topLevelItemCount()):
796 itm = self.findList.topLevelItem(index) 839 itm = self.findList.topLevelItem(index)
797 if itm.checkState(0) in [Qt.CheckState.PartiallyChecked, 840 if itm.checkState(0) in [
798 Qt.CheckState.Checked]: 841 Qt.CheckState.PartiallyChecked,
842 Qt.CheckState.Checked,
843 ]:
799 file = itm.text(0) 844 file = itm.text(0)
800 origHash = itm.data(0, self.md5Role) 845 origHash = itm.data(0, self.md5Role)
801 846
802 if self.projectButton.isChecked(): 847 if self.projectButton.isChecked():
803 fn = os.path.join(self.project.getProjectPath(), file) 848 fn = os.path.join(self.project.getProjectPath(), file)
804 else: 849 else:
805 fn = file 850 fn = file
806 851
807 # read the file and split it into textlines 852 # read the file and split it into textlines
808 try: 853 try:
809 text, encoding, hashStr = ( 854 text, encoding, hashStr = Utilities.readEncodedFileWithHash(fn)
810 Utilities.readEncodedFileWithHash(fn)
811 )
812 lines = text.splitlines(True) 855 lines = text.splitlines(True)
813 except (UnicodeError, OSError) as err: 856 except (UnicodeError, OSError) as err:
814 EricMessageBox.critical( 857 EricMessageBox.critical(
815 self, 858 self,
816 self.tr("Replace in Files"), 859 self.tr("Replace in Files"),
817 self.tr( 860 self.tr(
818 """<p>Could not read the file <b>{0}</b>.""" 861 """<p>Could not read the file <b>{0}</b>."""
819 """ Skipping it.</p><p>Reason: {1}</p>""") 862 """ Skipping it.</p><p>Reason: {1}</p>"""
820 .format(fn, str(err)) 863 ).format(fn, str(err)),
821 ) 864 )
822 self.findProgress.setValue(index) 865 self.findProgress.setValue(index)
823 continue 866 continue
824 867
825 # Check the original and the current hash. Skip the file, 868 # Check the original and the current hash. Skip the file,
826 # if hashes are different. 869 # if hashes are different.
827 if origHash != hashStr: 870 if origHash != hashStr:
828 EricMessageBox.critical( 871 EricMessageBox.critical(
829 self, 872 self,
830 self.tr("Replace in Files"), 873 self.tr("Replace in Files"),
831 self.tr( 874 self.tr(
832 """<p>The current and the original hash of the""" 875 """<p>The current and the original hash of the"""
833 """ file <b>{0}</b> are different. Skipping it.""" 876 """ file <b>{0}</b> are different. Skipping it."""
834 """</p><p>Hash 1: {1}</p><p>Hash 2: {2}</p>""") 877 """</p><p>Hash 1: {1}</p><p>Hash 2: {2}</p>"""
835 .format(fn, origHash, hashStr) 878 ).format(fn, origHash, hashStr),
836 ) 879 )
837 self.findProgress.setValue(index) 880 self.findProgress.setValue(index)
838 continue 881 continue
839 882
840 # replace the lines authorized by the user 883 # replace the lines authorized by the user
841 for cindex in range(itm.childCount()): 884 for cindex in range(itm.childCount()):
842 citm = itm.child(cindex) 885 citm = itm.child(cindex)
843 if citm.checkState(0) == Qt.CheckState.Checked: 886 if citm.checkState(0) == Qt.CheckState.Checked:
844 line = citm.data(0, self.lineRole) 887 line = citm.data(0, self.lineRole)
845 rline = citm.data(0, self.replaceRole) 888 rline = citm.data(0, self.replaceRole)
846 lines[line - 1] = rline 889 lines[line - 1] = rline
847 890
848 # write the file 891 # write the file
849 txt = "".join(lines) 892 txt = "".join(lines)
850 try: 893 try:
851 Utilities.writeEncodedFile(fn, txt, encoding) 894 Utilities.writeEncodedFile(fn, txt, encoding)
852 except (OSError, Utilities.CodingError, UnicodeError) as err: 895 except (OSError, Utilities.CodingError, UnicodeError) as err:
853 EricMessageBox.critical( 896 EricMessageBox.critical(
854 self, 897 self,
855 self.tr("Replace in Files"), 898 self.tr("Replace in Files"),
856 self.tr( 899 self.tr(
857 """<p>Could not save the file <b>{0}</b>.""" 900 """<p>Could not save the file <b>{0}</b>."""
858 """ Skipping it.</p><p>Reason: {1}</p>""") 901 """ Skipping it.</p><p>Reason: {1}</p>"""
859 .format(fn, str(err)) 902 ).format(fn, str(err)),
860 ) 903 )
861 904
862 self.findProgress.setValue(index + 1) 905 self.findProgress.setValue(index + 1)
863 906
864 self.findProgressLabel.setPath("") 907 self.findProgressLabel.setPath("")
865 908
866 self.findList.clear() 909 self.findList.clear()
867 self.replaceButton.setEnabled(False) 910 self.replaceButton.setEnabled(False)
868 self.findButton.setEnabled(True) 911 self.findButton.setEnabled(True)
869 self.clearButton.setEnabled(False) 912 self.clearButton.setEnabled(False)
870 913
871 @pyqtSlot(QPoint) 914 @pyqtSlot(QPoint)
872 def __contextMenuRequested(self, pos): 915 def __contextMenuRequested(self, pos):
873 """ 916 """
874 Private slot to handle the context menu request. 917 Private slot to handle the context menu request.
875 918
876 @param pos position the context menu shall be shown 919 @param pos position the context menu shall be shown
877 @type QPoint 920 @type QPoint
878 """ 921 """
879 menu = QMenu(self) 922 menu = QMenu(self)
880 923
881 menu.addAction(self.tr("Open"), self.__openFile) 924 menu.addAction(self.tr("Open"), self.__openFile)
882 menu.addAction(self.tr("Copy Path to Clipboard"), 925 menu.addAction(self.tr("Copy Path to Clipboard"), self.__copyToClipboard)
883 self.__copyToClipboard) 926
884
885 menu.exec(QCursor.pos()) 927 menu.exec(QCursor.pos())
886 928
887 @pyqtSlot() 929 @pyqtSlot()
888 def __openFile(self): 930 def __openFile(self):
889 """ 931 """
890 Private slot to open the currently selected entry. 932 Private slot to open the currently selected entry.
891 """ 933 """
892 itm = self.findList.selectedItems()[0] 934 itm = self.findList.selectedItems()[0]
893 self.on_findList_itemDoubleClicked(itm, 0) 935 self.on_findList_itemDoubleClicked(itm, 0)
894 936
895 @pyqtSlot() 937 @pyqtSlot()
896 def __copyToClipboard(self): 938 def __copyToClipboard(self):
897 """ 939 """
898 Private slot to copy the path of an entry to the clipboard. 940 Private slot to copy the path of an entry to the clipboard.
899 """ 941 """
900 itm = self.findList.selectedItems()[0] 942 itm = self.findList.selectedItems()[0]
901 fn = itm.parent().text(0) if itm.parent() else itm.text(0) 943 fn = itm.parent().text(0) if itm.parent() else itm.text(0)
902 944
903 cb = QApplication.clipboard() 945 cb = QApplication.clipboard()
904 cb.setText(fn) 946 cb.setText(fn)
905 947
906 948
907 class FindFileDialog(QDialog): 949 class FindFileDialog(QDialog):
908 """ 950 """
909 Class implementing a dialog to search for text in files and replace it 951 Class implementing a dialog to search for text in files and replace it
910 with some other text. 952 with some other text.
911 953
912 The occurrences found are displayed in a tree showing the file name, 954 The occurrences found are displayed in a tree showing the file name,
913 the line number and the text found. The file will be opened upon a double 955 the line number and the text found. The file will be opened upon a double
914 click onto the respective entry of the list. If the widget is in replace 956 click onto the respective entry of the list. If the widget is in replace
915 mode the line below shows the text after replacement. Replacements can 957 mode the line below shows the text after replacement. Replacements can
916 be authorized by ticking them on. Pressing the replace button performs 958 be authorized by ticking them on. Pressing the replace button performs
917 all ticked replacement operations. 959 all ticked replacement operations.
918 960
919 @signal sourceFile(str, int, str, int, int) emitted to open a source file 961 @signal sourceFile(str, int, str, int, int) emitted to open a source file
920 at a specificline 962 at a specificline
921 @signal designerFile(str) emitted to open a Qt-Designer file 963 @signal designerFile(str) emitted to open a Qt-Designer file
922 @signal linguistFile(str) emitted to open a Qt-Linguist (*.ts) file 964 @signal linguistFile(str) emitted to open a Qt-Linguist (*.ts) file
923 @signal trpreview([str]) emitted to preview Qt-Linguist (*.qm) files 965 @signal trpreview([str]) emitted to preview Qt-Linguist (*.qm) files
924 @signal pixmapFile(str) emitted to open a pixmap file 966 @signal pixmapFile(str) emitted to open a pixmap file
925 @signal svgFile(str) emitted to open a SVG file 967 @signal svgFile(str) emitted to open a SVG file
926 @signal umlFile(str) emitted to open an eric UML file 968 @signal umlFile(str) emitted to open an eric UML file
927 """ 969 """
970
928 sourceFile = pyqtSignal(str, int, str, int, int) 971 sourceFile = pyqtSignal(str, int, str, int, int)
929 designerFile = pyqtSignal(str) 972 designerFile = pyqtSignal(str)
930 linguistFile = pyqtSignal(str) 973 linguistFile = pyqtSignal(str)
931 trpreview = pyqtSignal(list) 974 trpreview = pyqtSignal(list)
932 pixmapFile = pyqtSignal(str) 975 pixmapFile = pyqtSignal(str)
933 svgFile = pyqtSignal(str) 976 svgFile = pyqtSignal(str)
934 umlFile = pyqtSignal(str) 977 umlFile = pyqtSignal(str)
935 978
936 def __init__(self, project, parent=None): 979 def __init__(self, project, parent=None):
937 """ 980 """
938 Constructor 981 Constructor
939 982
940 @param project reference to the project object 983 @param project reference to the project object
941 @type Project 984 @type Project
942 @param parent parent widget of this dialog (defaults to None) 985 @param parent parent widget of this dialog (defaults to None)
943 @type QWidget (optional) 986 @type QWidget (optional)
944 """ 987 """
945 super().__init__(parent) 988 super().__init__(parent)
946 self.setWindowFlags(Qt.WindowType.Window) 989 self.setWindowFlags(Qt.WindowType.Window)
947 990
948 self.__layout = QVBoxLayout() 991 self.__layout = QVBoxLayout()
949 992
950 self.__findWidget = FindFileWidget(project, self) 993 self.__findWidget = FindFileWidget(project, self)
951 self.__layout.addWidget(self.__findWidget) 994 self.__layout.addWidget(self.__findWidget)
952 995
953 self.__buttonBox = QDialogButtonBox( 996 self.__buttonBox = QDialogButtonBox(
954 QDialogButtonBox.StandardButton.Close, 997 QDialogButtonBox.StandardButton.Close, Qt.Orientation.Horizontal, self
955 Qt.Orientation.Horizontal, 998 )
956 self 999 self.__buttonBox.button(QDialogButtonBox.StandardButton.Close).setAutoDefault(
957 ) 1000 False
958 self.__buttonBox.button( 1001 )
959 QDialogButtonBox.StandardButton.Close).setAutoDefault(False)
960 self.__layout.addWidget(self.__buttonBox) 1002 self.__layout.addWidget(self.__buttonBox)
961 1003
962 self.setLayout(self.__layout) 1004 self.setLayout(self.__layout)
963 self.resize(600, 800) 1005 self.resize(600, 800)
964 1006
965 # connect the widgets 1007 # connect the widgets
966 self.__findWidget.sourceFile.connect(self.sourceFile) 1008 self.__findWidget.sourceFile.connect(self.sourceFile)
967 self.__findWidget.designerFile.connect(self.designerFile) 1009 self.__findWidget.designerFile.connect(self.designerFile)
968 self.__findWidget.linguistFile.connect(self.linguistFile) 1010 self.__findWidget.linguistFile.connect(self.linguistFile)
969 self.__findWidget.trpreview.connect(self.trpreview) 1011 self.__findWidget.trpreview.connect(self.trpreview)
970 self.__findWidget.pixmapFile.connect(self.pixmapFile) 1012 self.__findWidget.pixmapFile.connect(self.pixmapFile)
971 self.__findWidget.svgFile.connect(self.svgFile) 1013 self.__findWidget.svgFile.connect(self.svgFile)
972 self.__findWidget.umlFile.connect(self.umlFile) 1014 self.__findWidget.umlFile.connect(self.umlFile)
973 1015
974 self.__buttonBox.accepted.connect(self.accept) 1016 self.__buttonBox.accepted.connect(self.accept)
975 self.__buttonBox.rejected.connect(self.reject) 1017 self.__buttonBox.rejected.connect(self.reject)
976 1018
977 def activate(self, replaceMode=False, txt="", searchDir="", 1019 def activate(self, replaceMode=False, txt="", searchDir="", openFiles=False):
978 openFiles=False):
979 """ 1020 """
980 Public method to activate the dialog with a given mode, a text 1021 Public method to activate the dialog with a given mode, a text
981 to search for and some search parameters. 1022 to search for and some search parameters.
982 1023
983 @param replaceMode flag indicating replacement mode (defaults to False) 1024 @param replaceMode flag indicating replacement mode (defaults to False)
984 @type bool (optional) 1025 @type bool (optional)
985 @param txt text to be searched for (defaults to "") 1026 @param txt text to be searched for (defaults to "")
986 @type str (optional) 1027 @type str (optional)
987 @param searchDir directory to search in (defaults to "") 1028 @param searchDir directory to search in (defaults to "")
988 @type str (optional) 1029 @type str (optional)
989 @param openFiles flag indicating to operate on open files only 1030 @param openFiles flag indicating to operate on open files only
990 (defaults to False) 1031 (defaults to False)
991 @type bool (optional) 1032 @type bool (optional)
992 """ 1033 """
993 self.__findWidget.activate(replaceMode=replaceMode, txt=txt, 1034 self.__findWidget.activate(
994 searchDir=searchDir, openFiles=openFiles) 1035 replaceMode=replaceMode, txt=txt, searchDir=searchDir, openFiles=openFiles
995 1036 )
1037
996 self.raise_() 1038 self.raise_()
997 self.activateWindow() 1039 self.activateWindow()
998 self.show() 1040 self.show()

eric ide

mercurial