src/eric7/UI/FindFileWidget.py

branch
eric7
changeset 10492
53eac4c7d10b
parent 10486
157f119cd10d
child 10494
8db0328580d7
equal deleted inserted replaced
10491:acabc60b19a2 10492:53eac4c7d10b
5 5
6 """ 6 """
7 Module implementing a dialog to search for text in files. 7 Module implementing a dialog to search for text in files.
8 """ 8 """
9 9
10 import json
10 import os 11 import os
11 import re 12 import re
12 import time 13 import time
13 14
14 from PyQt6.QtCore import QPoint, Qt, QUrl, pyqtSignal, pyqtSlot 15 from PyQt6.QtCore import QPoint, Qt, QUrl, pyqtSignal, pyqtSlot
81 @type QWidget (optional) 82 @type QWidget (optional)
82 """ 83 """
83 super().__init__(parent) 84 super().__init__(parent)
84 self.setupUi(self) 85 self.setupUi(self)
85 86
87 self.__project = project
88
86 self.layout().setContentsMargins(0, 3, 0, 0) 89 self.layout().setContentsMargins(0, 3, 0, 0)
87 90
88 self.caseToolButton.setIcon(EricPixmapCache.getIcon("caseSensitive")) 91 self.caseToolButton.setIcon(EricPixmapCache.getIcon("caseSensitive"))
89 self.wordToolButton.setIcon(EricPixmapCache.getIcon("wholeWord")) 92 self.wordToolButton.setIcon(EricPixmapCache.getIcon("wholeWord"))
90 self.escapeToolButton.setIcon(EricPixmapCache.getIcon("esc-code")) 93 self.escapeToolButton.setIcon(EricPixmapCache.getIcon("esc-code"))
91 self.regexpToolButton.setIcon(EricPixmapCache.getIcon("regexp")) 94 self.regexpToolButton.setIcon(EricPixmapCache.getIcon("regexp"))
95 self.filtersConfigButton.setIcon(EricPixmapCache.getIcon("edit"))
92 96
93 self.dirPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) 97 self.dirPicker.setMode(EricPathPickerModes.DIRECTORY_MODE)
94 self.dirPicker.setInsertPolicy(QComboBox.InsertPolicy.InsertAtTop) 98 self.dirPicker.setInsertPolicy(QComboBox.InsertPolicy.InsertAtTop)
95 self.dirPicker.setSizeAdjustPolicy( 99 self.dirPicker.setSizeAdjustPolicy(
96 QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon 100 QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon
146 Preferences.toBool( 150 Preferences.toBool(
147 Preferences.getSettings().value("FindFileWidget/ExcludeHidden", True) 151 Preferences.getSettings().value("FindFileWidget/ExcludeHidden", True)
148 ) 152 )
149 ) 153 )
150 154
151 self.__project = project 155 self.__populateFiltersSelector()
152
153 self.populateFileCategories() 156 self.populateFileCategories()
154 157
155 # ensure the file type tab is the current one 158 # ensure the file type tab is the current one
156 self.fileOptionsWidget.setCurrentWidget(self.fileTypeTab) 159 self.fileOptionsWidget.setCurrentWidget(self.fileTypeTab)
157 160
171 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) 174 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
172 self.customContextMenuRequested.connect(self.__contextMenuRequested) 175 self.customContextMenuRequested.connect(self.__contextMenuRequested)
173 176
174 self.__replaceMode = True 177 self.__replaceMode = True
175 self.__toggleReplaceMode() 178 self.__toggleReplaceMode()
179
180 def __populateFiltersSelector(self):
181 """
182 Private method to (re-)populate the file filters selector.
183 """
184 currentFilter = self.filterComboBox.currentText()
185 self.filterComboBox.clear()
186
187 # add standard entries
188 self.filterComboBox.addItem("")
189 self.filterComboBox.addItem(self.tr("All Files"), "*")
190
191 # add configured entries
192 # filters is a dictionary with the filter text as key and the pattern as value
193 self.__filters = json.loads(
194 Preferences.getSettings().value("FindFileWidget/FileFilters", "{}")
195 # noqa: M613
196 )
197 for fileFilter in sorted(self.__filters):
198 self.filterComboBox.addItem(fileFilter, self.__filters[filter])
199
200 # reselect the current entry
201 index = self.filterComboBox.findText(currentFilter)
202 if index == -1:
203 index = 0
204 self.filterComboBox.setCurrentIndex(index)
176 205
177 def populateFileCategories(self): 206 def populateFileCategories(self):
178 """ 207 """
179 Public method to populate the search file categories list. 208 Public method to populate the search file categories list.
180 """ 209 """
368 """ 397 """
369 if checked: 398 if checked:
370 # only one of regexp or escape can be selected 399 # only one of regexp or escape can be selected
371 self.escapeToolButton.setChecked(False) 400 self.escapeToolButton.setChecked(False)
372 401
402 @pyqtSlot()
403 def on_filtersConfigButton_clicked(self):
404 """
405 Private slot to edit the list of defined file filter entries.
406 """
407 from .FindFileFiltersEditDialog import FindFileFiltersEditDialog
408
409 dlg = FindFileFiltersEditDialog(self.__filters, self)
410 if dlg.exec() == QDialog.DialogCode.Accepted:
411 filters = dlg.getFilters()
412 Preferences.getSettings().setValue(
413 "FindFileWidget/FileFilters", json.dumps(filters)
414 )
415 self.__populateFiltersSelector()
416
373 @pyqtSlot(str) 417 @pyqtSlot(str)
374 def on_findtextCombo_editTextChanged(self, text): 418 def on_findtextCombo_editTextChanged(self, text):
375 """ 419 """
376 Private slot to handle the editTextChanged signal of the find 420 Private slot to handle the editTextChanged signal of the find
377 text combo. 421 text combo.
429 """ 473 """
430 Private slot to handle the selection of the file filter check box. 474 Private slot to handle the selection of the file filter check box.
431 """ 475 """
432 self.__enableFindButton() 476 self.__enableFindButton()
433 477
434 @pyqtSlot(str) 478 @pyqtSlot(int)
435 def on_filterEdit_textEdited(self, text): 479 def on_filterComboBox_currentIndexChanged(self, index):
436 """ 480 """
437 Private slot to handle the textChanged signal of the file filter edit. 481 Private slot to handle the selection of a file filter.
438 482
439 @param text (ignored) 483 @param index index of the selected entry
440 @type str 484 @type int
441 """ 485 """
442 self.__enableFindButton() 486 self.__enableFindButton()
443 487
444 @pyqtSlot() 488 @pyqtSlot()
445 def __enableFindButton(self): 489 def __enableFindButton(self):
453 and ( 497 and (
454 self.dirPicker.currentText() == "" 498 self.dirPicker.currentText() == ""
455 or not os.path.exists(os.path.abspath(self.dirPicker.currentText())) 499 or not os.path.exists(os.path.abspath(self.dirPicker.currentText()))
456 ) 500 )
457 ) 501 )
458 or (self.filterCheckBox.isChecked() and self.filterEdit.text() == "") 502 or (
503 self.filterCheckBox.isChecked()
504 and self.filterComboBox.currentText() == ""
505 )
506 or (self.projectButton.isChecked() and not self.__project.isOpen())
459 ): 507 ):
460 self.findButton.setEnabled(False) 508 self.findButton.setEnabled(False)
461 else: 509 else:
462 self.findButton.setEnabled(True) 510 self.findButton.setEnabled(True)
463 511
469 @type str 517 @type str
470 @return text with eol stripped 518 @return text with eol stripped
471 @rtype str 519 @rtype str
472 """ 520 """
473 return txt.rstrip("\n\r") 521 return txt.rstrip("\n\r")
522
523 def __buildReFileFilter(self, fileFilter):
524 """
525 Private method to convert a file filter expression into a valid re search
526 pattern.
527
528 @param fileFilter file filter expression
529 @type str
530 @return re search pattern with dot, question mark and star converted
531 @rtype str
532 """
533 return "^{0}$".format(
534 fileFilter.strip().replace(".", r"\.").replace("*", ".*").replace("?", ".")
535 )
474 536
475 @pyqtSlot() 537 @pyqtSlot()
476 def __stopSearch(self): 538 def __stopSearch(self):
477 """ 539 """
478 Private slot to handle the stop button being pressed. 540 Private slot to handle the stop button being pressed.
491 return 553 return
492 554
493 self.__cancelSearch = False 555 self.__cancelSearch = False
494 556
495 if self.filterCheckBox.isChecked(): 557 if self.filterCheckBox.isChecked():
496 fileFilter = self.filterEdit.text() 558 fileFilter = self.filterComboBox.currentData()
497 fileFilterList = [ 559 fileFilterList = [
498 "^{0}$".format(filter.replace(".", r"\.").replace("*", ".*")) 560 self.__buildReFileFilter(filter) for filter in fileFilter.split(";")
499 for filter in fileFilter.split(";")
500 ] 561 ]
501 filterRe = re.compile("|".join(fileFilterList)) 562 filterRe = re.compile("|".join(fileFilterList))
502 563
503 if self.projectButton.isChecked(): 564 if self.projectButton.isChecked():
504 if self.filterCheckBox.isChecked(): 565 if self.filterCheckBox.isChecked():
527 for row in range(self.fileTypeList.count()): 588 for row in range(self.fileTypeList.count()):
528 itm = self.fileTypeList.item(row) 589 itm = self.fileTypeList.item(row)
529 if itm.checkState() == Qt.CheckState.Checked: 590 if itm.checkState() == Qt.CheckState.Checked:
530 filters.extend( 591 filters.extend(
531 [ 592 [
532 "^{0}$".format( 593 self.__buildReFileFilter(assoc)
533 assoc.replace(".", r"\.").replace("*", ".*")
534 )
535 for assoc in self.__project.getFiletypeAssociations( 594 for assoc in self.__project.getFiletypeAssociations(
536 itm.data(Qt.ItemDataRole.UserRole) 595 itm.data(Qt.ItemDataRole.UserRole)
537 ) 596 )
538 ] 597 ]
539 ) 598 )
543 if itm.checkState() == Qt.CheckState.Checked: 602 if itm.checkState() == Qt.CheckState.Checked:
544 fileType = itm.data(Qt.ItemDataRole.UserRole) 603 fileType = itm.data(Qt.ItemDataRole.UserRole)
545 if fileType == "SOURCES": 604 if fileType == "SOURCES":
546 filters.extend( 605 filters.extend(
547 [ 606 [
548 "^{0}$".format( 607 self.__buildReFileFilter(assoc)
549 assoc.replace(".", r"\.").replace("*", ".*")
550 )
551 for assoc in Preferences.getEditorLexerAssocs() 608 for assoc in Preferences.getEditorLexerAssocs()
552 if assoc 609 if assoc
553 not in self.__project.getFileCategoryExtension( 610 not in self.__project.getFileCategoryExtension(
554 fileType, reverse=True 611 fileType, reverse=True
555 ) 612 )
556 ] 613 ]
557 ) 614 )
558 else: 615 else:
559 filters.extend( 616 filters.extend(
560 [ 617 [
561 "^{0}$".format( 618 self.__buildReFileFilter(ext)
562 ext.replace(".", r"\.").replace("*", ".*")
563 )
564 for ext in self.__project.getFileCategoryExtension( 619 for ext in self.__project.getFileCategoryExtension(
565 # __IGNORE_WARNING__ 620 # __IGNORE_WARNING__
566 fileType 621 fileType
567 ) 622 )
568 ] 623 ]

eric ide

mercurial