23 |
23 |
24 class FindFileDialog(QDialog, Ui_FindFileDialog): |
24 class FindFileDialog(QDialog, Ui_FindFileDialog): |
25 """ |
25 """ |
26 Class implementing a dialog to search for text in files. |
26 Class implementing a dialog to search for text in files. |
27 |
27 |
28 The occurrences found are displayed in a QTreeWidget showing the filename, the |
28 The occurrences found are displayed in a QTreeWidget showing the filename, |
29 linenumber and the found text. The file will be opened upon a double click onto |
29 the linenumber and the found text. The file will be opened upon a double |
30 the respective entry of the list. |
30 click onto the respective entry of the list. |
31 |
31 |
32 @signal sourceFile(str, int, str, int, int) emitted to open a source file at a line |
32 @signal sourceFile(str, int, str, int, int) emitted to open a source file |
|
33 at a line |
33 @signal designerFile(str) emitted to open a Qt-Designer file |
34 @signal designerFile(str) emitted to open a Qt-Designer file |
34 """ |
35 """ |
35 sourceFile = pyqtSignal(str, int, str, int, int) |
36 sourceFile = pyqtSignal(str, int, str, int, int) |
36 designerFile = pyqtSignal(str) |
37 designerFile = pyqtSignal(str) |
37 |
38 |
53 self.setWindowFlags(Qt.WindowFlags(Qt.Window)) |
54 self.setWindowFlags(Qt.WindowFlags(Qt.Window)) |
54 |
55 |
55 self.__replaceMode = replaceMode |
56 self.__replaceMode = replaceMode |
56 |
57 |
57 self.stopButton = \ |
58 self.stopButton = \ |
58 self.buttonBox.addButton(self.trUtf8("Stop"), QDialogButtonBox.ActionRole) |
59 self.buttonBox.addButton(self.trUtf8("Stop"), |
|
60 QDialogButtonBox.ActionRole) |
59 self.stopButton.setEnabled(False) |
61 self.stopButton.setEnabled(False) |
60 |
62 |
61 self.findButton = \ |
63 self.findButton = \ |
62 self.buttonBox.addButton(self.trUtf8("Find"), QDialogButtonBox.ActionRole) |
64 self.buttonBox.addButton(self.trUtf8("Find"), |
|
65 QDialogButtonBox.ActionRole) |
63 self.findButton.setEnabled(False) |
66 self.findButton.setEnabled(False) |
64 self.findButton.setDefault(True) |
67 self.findButton.setDefault(True) |
65 |
68 |
66 if self.__replaceMode: |
69 if self.__replaceMode: |
67 self.replaceButton.setEnabled(False) |
70 self.replaceButton.setEnabled(False) |
72 self.replaceButton.hide() |
75 self.replaceButton.hide() |
73 |
76 |
74 self.findProgressLabel.setMaximumWidth(550) |
77 self.findProgressLabel.setMaximumWidth(550) |
75 |
78 |
76 self.searchHistory = Preferences.toList( |
79 self.searchHistory = Preferences.toList( |
77 Preferences.Prefs.settings.value("FindFileDialog/SearchHistory")) |
80 Preferences.Prefs.settings.value( |
|
81 "FindFileDialog/SearchHistory")) |
78 self.replaceHistory = Preferences.toList( |
82 self.replaceHistory = Preferences.toList( |
79 Preferences.Prefs.settings.value("FindFileDialog/ReplaceHistory")) |
83 Preferences.Prefs.settings.value( |
|
84 "FindFileDialog/ReplaceHistory")) |
80 self.dirHistory = Preferences.toList( |
85 self.dirHistory = Preferences.toList( |
81 Preferences.Prefs.settings.value("FindFileDialog/DirectoryHistory")) |
86 Preferences.Prefs.settings.value( |
|
87 "FindFileDialog/DirectoryHistory")) |
82 self.findtextCombo.addItems(self.searchHistory) |
88 self.findtextCombo.addItems(self.searchHistory) |
83 self.replacetextCombo.addItems(self.replaceHistory) |
89 self.replacetextCombo.addItems(self.replaceHistory) |
84 self.dirCombo.addItems(self.dirHistory) |
90 self.dirCombo.addItems(self.dirHistory) |
85 |
91 |
86 self.project = project |
92 self.project = project |
114 self.__populating = False |
120 self.__populating = False |
115 |
121 |
116 self.setContextMenuPolicy(Qt.CustomContextMenu) |
122 self.setContextMenuPolicy(Qt.CustomContextMenu) |
117 self.customContextMenuRequested.connect(self.__contextMenuRequested) |
123 self.customContextMenuRequested.connect(self.__contextMenuRequested) |
118 |
124 |
119 def __createItem(self, file, line, text, start, end, replTxt = "", md5 = ""): |
125 def __createItem(self, file, line, text, start, end, replTxt="", md5=""): |
120 """ |
126 """ |
121 Private method to create an entry in the file list. |
127 Private method to create an entry in the file list. |
122 |
128 |
123 @param file filename of file (string) |
129 @param file filename of file (string) |
124 @param line line number (integer) |
130 @param line line number (integer) |
135 self.__lastFileItem.setExpanded(True) |
141 self.__lastFileItem.setExpanded(True) |
136 if self.__replaceMode: |
142 if self.__replaceMode: |
137 self.__lastFileItem.setFlags(self.__lastFileItem.flags() | \ |
143 self.__lastFileItem.setFlags(self.__lastFileItem.flags() | \ |
138 Qt.ItemFlags(Qt.ItemIsUserCheckable | Qt.ItemIsTristate)) |
144 Qt.ItemFlags(Qt.ItemIsUserCheckable | Qt.ItemIsTristate)) |
139 # Qt bug: |
145 # Qt bug: |
140 # item is not user checkable if setFirstColumnSpanned is True (< 4.5.0) |
146 # item is not user checkable if setFirstColumnSpanned |
|
147 # is True (< 4.5.0) |
141 self.__lastFileItem.setData(0, self.md5Role, md5) |
148 self.__lastFileItem.setData(0, self.md5Role, md5) |
142 |
149 |
143 itm = QTreeWidgetItem(self.__lastFileItem, [' {0:5d} '.format(line), text]) |
150 itm = QTreeWidgetItem(self.__lastFileItem, |
|
151 [' {0:5d} '.format(line), text]) |
144 itm.setTextAlignment(0, Qt.AlignRight) |
152 itm.setTextAlignment(0, Qt.AlignRight) |
145 itm.setData(0, self.lineRole, line) |
153 itm.setData(0, self.lineRole, line) |
146 itm.setData(0, self.startRole, start) |
154 itm.setData(0, self.startRole, start) |
147 itm.setData(0, self.endRole, end) |
155 itm.setData(0, self.endRole, end) |
148 itm.setData(0, self.replaceRole, replTxt) |
156 itm.setData(0, self.replaceRole, replTxt) |
175 |
183 |
176 QDialog.show(self) |
184 QDialog.show(self) |
177 |
185 |
178 def on_findtextCombo_editTextChanged(self, text): |
186 def on_findtextCombo_editTextChanged(self, text): |
179 """ |
187 """ |
180 Private slot to handle the editTextChanged signal of the find text combo. |
188 Private slot to handle the editTextChanged signal of the find |
|
189 text combo. |
181 |
190 |
182 @param text (ignored) |
191 @param text (ignored) |
183 """ |
192 """ |
184 self.__enableFindButton() |
193 self.__enableFindButton() |
185 |
194 |
186 def on_replacetextCombo_editTextChanged(self, text): |
195 def on_replacetextCombo_editTextChanged(self, text): |
187 """ |
196 """ |
188 Private slot to handle the editTextChanged signal of the replace text combo. |
197 Private slot to handle the editTextChanged signal of the replace |
|
198 text combo. |
189 |
199 |
190 @param text (ignored) |
200 @param text (ignored) |
191 """ |
201 """ |
192 self.__enableFindButton() |
202 self.__enableFindButton() |
193 |
203 |
194 def on_dirCombo_editTextChanged(self, text): |
204 def on_dirCombo_editTextChanged(self, text): |
195 """ |
205 """ |
196 Private slot to handle the textChanged signal of the directory combo box. |
206 Private slot to handle the textChanged signal of the directory |
|
207 combo box. |
197 |
208 |
198 @param text (ignored) |
209 @param text (ignored) |
199 """ |
210 """ |
200 self.__enableFindButton() |
211 self.__enableFindButton() |
201 |
212 |
232 def __enableFindButton(self): |
243 def __enableFindButton(self): |
233 """ |
244 """ |
234 Private slot called to enable the find button. |
245 Private slot called to enable the find button. |
235 """ |
246 """ |
236 if self.findtextCombo.currentText() == "" or \ |
247 if self.findtextCombo.currentText() == "" or \ |
237 (self.__replaceMode and self.replacetextCombo.currentText() == "") or \ |
248 (self.__replaceMode and \ |
|
249 self.replacetextCombo.currentText() == "") or \ |
238 (self.dirButton.isChecked() and \ |
250 (self.dirButton.isChecked() and \ |
239 (self.dirCombo.currentText() == "" or \ |
251 (self.dirCombo.currentText() == "" or \ |
240 not os.path.exists(os.path.abspath(self.dirCombo.currentText())))) or \ |
252 not os.path.exists(os.path.abspath( |
|
253 self.dirCombo.currentText())))) or \ |
241 (self.filterCheckBox.isChecked() and self.filterEdit.text() == ""): |
254 (self.filterCheckBox.isChecked() and self.filterEdit.text() == ""): |
242 self.findButton.setEnabled(False) |
255 self.findButton.setEnabled(False) |
243 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
256 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
244 else: |
257 else: |
245 self.findButton.setEnabled(True) |
258 self.findButton.setEnabled(True) |
280 |
294 |
281 if self.projectButton.isChecked(): |
295 if self.projectButton.isChecked(): |
282 if self.filterCheckBox.isChecked(): |
296 if self.filterCheckBox.isChecked(): |
283 files = [self.project.getRelativePath(file) \ |
297 files = [self.project.getRelativePath(file) \ |
284 for file in \ |
298 for file in \ |
285 self.__getFileList(self.project.getProjectPath(), filterRe)] |
299 self.__getFileList( |
|
300 self.project.getProjectPath(), filterRe)] |
286 else: |
301 else: |
287 files = [] |
302 files = [] |
288 if self.sourcesCheckBox.isChecked(): |
303 if self.sourcesCheckBox.isChecked(): |
289 files += self.project.pdata["SOURCES"] |
304 files += self.project.pdata["SOURCES"] |
290 if self.formsCheckBox.isChecked(): |
305 if self.formsCheckBox.isChecked(): |
296 elif self.dirButton.isChecked(): |
311 elif self.dirButton.isChecked(): |
297 if not self.filterCheckBox.isChecked(): |
312 if not self.filterCheckBox.isChecked(): |
298 filters = [] |
313 filters = [] |
299 if self.sourcesCheckBox.isChecked(): |
314 if self.sourcesCheckBox.isChecked(): |
300 filters.extend( |
315 filters.extend( |
301 ["^{0}$".format(assoc.replace(".", "\.").replace("*", ".*")) \ |
316 ["^{0}$".format( |
302 for assoc in list(Preferences.getEditorLexerAssocs().keys()) \ |
317 assoc.replace(".", "\.").replace("*", ".*")) |
|
318 for assoc in list( |
|
319 Preferences.getEditorLexerAssocs().keys()) |
303 if assoc not in self.formsExt + self.interfacesExt]) |
320 if assoc not in self.formsExt + self.interfacesExt]) |
304 if self.formsCheckBox.isChecked(): |
321 if self.formsCheckBox.isChecked(): |
305 filters.append(self.filterForms) |
322 filters.append(self.filterForms) |
306 if self.interfacesCheckBox.isChecked(): |
323 if self.interfacesCheckBox.isChecked(): |
307 filters.append(self.filterInterfaces) |
324 filters.append(self.filterInterfaces) |
308 if self.resourcesCheckBox.isChecked(): |
325 if self.resourcesCheckBox.isChecked(): |
309 filters.append(self.filterResources) |
326 filters.append(self.filterResources) |
310 filterString = "|".join(filters) |
327 filterString = "|".join(filters) |
311 filterRe = re.compile(filterString) |
328 filterRe = re.compile(filterString) |
312 files = self.__getFileList(os.path.abspath(self.dirCombo.currentText()), |
329 files = self.__getFileList( |
313 filterRe) |
330 os.path.abspath(self.dirCombo.currentText()), |
|
331 filterRe) |
314 elif self.openFilesButton.isChecked(): |
332 elif self.openFilesButton.isChecked(): |
315 files = e5App().getObject("ViewManager").getOpenFilenames() |
333 files = e5App().getObject("ViewManager").getOpenFilenames() |
316 |
334 |
317 self.findList.clear() |
335 self.findList.clear() |
318 QApplication.processEvents() |
336 QApplication.processEvents() |
350 if ct in self.searchHistory: |
368 if ct in self.searchHistory: |
351 self.searchHistory.remove(ct) |
369 self.searchHistory.remove(ct) |
352 self.searchHistory.insert(0, ct) |
370 self.searchHistory.insert(0, ct) |
353 self.findtextCombo.clear() |
371 self.findtextCombo.clear() |
354 self.findtextCombo.addItems(self.searchHistory) |
372 self.findtextCombo.addItems(self.searchHistory) |
355 Preferences.Prefs.settings.setValue("FindFileDialog/SearchHistory", |
373 Preferences.Prefs.settings.setValue( |
356 self.searchHistory[:30]) |
374 "FindFileDialog/SearchHistory", |
|
375 self.searchHistory[:30]) |
357 |
376 |
358 if self.__replaceMode: |
377 if self.__replaceMode: |
359 replTxt = self.replacetextCombo.currentText() |
378 replTxt = self.replacetextCombo.currentText() |
360 if replTxt in self.replaceHistory: |
379 if replTxt in self.replaceHistory: |
361 self.replaceHistory.remove(replTxt) |
380 self.replaceHistory.remove(replTxt) |
362 self.replaceHistory.insert(0, replTxt) |
381 self.replaceHistory.insert(0, replTxt) |
363 self.replacetextCombo.clear() |
382 self.replacetextCombo.clear() |
364 self.replacetextCombo.addItems(self.replaceHistory) |
383 self.replacetextCombo.addItems(self.replaceHistory) |
365 Preferences.Prefs.settings.setValue("FindFileDialog/ReplaceHistory", |
384 Preferences.Prefs.settings.setValue( |
366 self.replaceHistory[:30]) |
385 "FindFileDialog/ReplaceHistory", |
|
386 self.replaceHistory[:30]) |
367 |
387 |
368 if self.dirButton.isChecked(): |
388 if self.dirButton.isChecked(): |
369 searchDir = self.dirCombo.currentText() |
389 searchDir = self.dirCombo.currentText() |
370 if searchDir in self.dirHistory: |
390 if searchDir in self.dirHistory: |
371 self.dirHistory.remove(searchDir) |
391 self.dirHistory.remove(searchDir) |
372 self.dirHistory.insert(0, searchDir) |
392 self.dirHistory.insert(0, searchDir) |
373 self.dirCombo.clear() |
393 self.dirCombo.clear() |
374 self.dirCombo.addItems(self.dirHistory) |
394 self.dirCombo.addItems(self.dirHistory) |
375 Preferences.Prefs.settings.setValue("FindFileDialog/DirectoryHistory", |
395 Preferences.Prefs.settings.setValue( |
376 self.dirHistory[:30]) |
396 "FindFileDialog/DirectoryHistory", |
|
397 self.dirHistory[:30]) |
377 |
398 |
378 # set the button states |
399 # set the button states |
379 self.stopButton.setEnabled(True) |
400 self.stopButton.setEnabled(True) |
380 self.stopButton.setDefault(True) |
401 self.stopButton.setDefault(True) |
381 self.findButton.setEnabled(False) |
402 self.findButton.setEnabled(False) |
424 line = "{0} ...".format(line[:1024]) |
445 line = "{0} ...".format(line[:1024]) |
425 if self.__replaceMode: |
446 if self.__replaceMode: |
426 if len(rline) > 1024: |
447 if len(rline) > 1024: |
427 rline = "{0} ...".format(line[:1024]) |
448 rline = "{0} ...".format(line[:1024]) |
428 line = "- {0}\n+ {1}".format(line, rline) |
449 line = "- {0}\n+ {1}".format(line, rline) |
429 self.__createItem(file, count, line, start, end, rline, hash) |
450 self.__createItem(file, count, line, start, end, |
|
451 rline, hash) |
430 |
452 |
431 if self.feelLikeCheckBox.isChecked(): |
453 if self.feelLikeCheckBox.isChecked(): |
432 fn = os.path.join(self.project.ppath, file) |
454 fn = os.path.join(self.project.ppath, file) |
433 self.sourceFile.emit(fn, count, "", start, end) |
455 self.sourceFile.emit(fn, count, "", start, end) |
434 QApplication.processEvents() |
456 QApplication.processEvents() |
509 def __getFileList(self, path, filterRe): |
531 def __getFileList(self, path, filterRe): |
510 """ |
532 """ |
511 Private method to get a list of files to search. |
533 Private method to get a list of files to search. |
512 |
534 |
513 @param path the root directory to search in (string) |
535 @param path the root directory to search in (string) |
514 @param filterRe regular expression defining the filter criteria (regexp object) |
536 @param filterRe regular expression defining the filter |
|
537 criteria (regexp object) |
515 @return list of files to be processed (list of strings) |
538 @return list of files to be processed (list of strings) |
516 """ |
539 """ |
517 path = os.path.abspath(path) |
540 path = os.path.abspath(path) |
518 files = [] |
541 files = [] |
519 for dirname, _, names in os.walk(path): |
542 for dirname, _, names in os.walk(path): |
554 else: |
577 else: |
555 fn = file |
578 fn = file |
556 |
579 |
557 # read the file and split it into textlines |
580 # read the file and split it into textlines |
558 try: |
581 try: |
559 text, encoding, hash = Utilities.readEncodedFileWithHash(fn) |
582 text, encoding, hash = \ |
|
583 Utilities.readEncodedFileWithHash(fn) |
560 lines = text.splitlines() |
584 lines = text.splitlines() |
561 except (UnicodeError, IOError): |
585 except (UnicodeError, IOError): |
562 E5MessageBox.critical(self, |
586 E5MessageBox.critical(self, |
563 self.trUtf8("Replace in Files"), |
587 self.trUtf8("Replace in Files"), |
564 self.trUtf8("""<p>Could not read the file <b>{0}</b>.""" |
588 self.trUtf8( |
565 """ Skipping it.</p><p>Reason: {1}</p>""")\ |
589 """<p>Could not read the file <b>{0}</b>.""" |
|
590 """ Skipping it.</p><p>Reason: {1}</p>""")\ |
566 .format(fn, str(err)) |
591 .format(fn, str(err)) |
567 ) |
592 ) |
568 progress += 1 |
593 progress += 1 |
569 self.findProgress.setValue(progress) |
594 self.findProgress.setValue(progress) |
570 continue |
595 continue |
572 # Check the original and the current hash. Skip the file, |
597 # Check the original and the current hash. Skip the file, |
573 # if hashes are different. |
598 # if hashes are different. |
574 if origHash != hash: |
599 if origHash != hash: |
575 E5MessageBox.critical(self, |
600 E5MessageBox.critical(self, |
576 self.trUtf8("Replace in Files"), |
601 self.trUtf8("Replace in Files"), |
577 self.trUtf8("""<p>The current and the original hash of the""" |
602 self.trUtf8( |
578 """ file <b>{0}</b> are different. Skipping it.""" |
603 """<p>The current and the original hash of the""" |
579 """</p><p>Hash 1: {1}</p><p>Hash 2: {2}</p>""")\ |
604 """ file <b>{0}</b> are different. Skipping it.""" |
|
605 """</p><p>Hash 1: {1}</p><p>Hash 2: {2}</p>""")\ |
580 .format(fn, origHash, hash) |
606 .format(fn, origHash, hash) |
581 ) |
607 ) |
582 progress += 1 |
608 progress += 1 |
583 self.findProgress.setValue(progress) |
609 self.findProgress.setValue(progress) |
584 continue |
610 continue |
590 line = citm.data(0, self.lineRole) |
616 line = citm.data(0, self.lineRole) |
591 rline = citm.data(0, self.replaceRole) |
617 rline = citm.data(0, self.replaceRole) |
592 lines[line - 1] = rline |
618 lines[line - 1] = rline |
593 |
619 |
594 # write the file |
620 # write the file |
595 txt = Utilities.linesep().join(lines) + Utilities.linesep() |
621 if self.project.isProjectFile(fn): |
|
622 eol = self.project.getEolString() |
|
623 else: |
|
624 eol = Utilities.linesep() |
|
625 txt = eol.join(lines) + Utilities.linesep() |
596 try: |
626 try: |
597 Utilities.writeEncodedFile(fn, txt, encoding) |
627 Utilities.writeEncodedFile(fn, txt, encoding) |
598 except (IOError, Utilities.CodingError, UnicodeError) as err: |
628 except (IOError, Utilities.CodingError, UnicodeError) as err: |
599 E5MessageBox.critical(self, |
629 E5MessageBox.critical(self, |
600 self.trUtf8("Replace in Files"), |
630 self.trUtf8("Replace in Files"), |
601 self.trUtf8("""<p>Could not save the file <b>{0}</b>.""" |
631 self.trUtf8( |
602 """ Skipping it.</p><p>Reason: {1}</p>""")\ |
632 """<p>Could not save the file <b>{0}</b>.""" |
|
633 """ Skipping it.</p><p>Reason: {1}</p>""")\ |
603 .format(fn, str(err)) |
634 .format(fn, str(err)) |
604 ) |
635 ) |
605 |
636 |
606 progress += 1 |
637 progress += 1 |
607 self.findProgress.setValue(progress) |
638 self.findProgress.setValue(progress) |