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(): |
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 """ |
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() |