|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2002 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a configuration dialog for the tools menu. |
|
8 """ |
|
9 |
|
10 import copy |
|
11 |
|
12 from PyQt5.QtCore import Qt, pyqtSlot |
|
13 from PyQt5.QtWidgets import QDialog |
|
14 |
|
15 from E5Gui import E5MessageBox |
|
16 from E5Gui.E5PathPicker import E5PathPickerModes |
|
17 |
|
18 from .Ui_ToolConfigurationDialog import Ui_ToolConfigurationDialog |
|
19 |
|
20 import Utilities |
|
21 |
|
22 |
|
23 class ToolConfigurationDialog(QDialog, Ui_ToolConfigurationDialog): |
|
24 """ |
|
25 Class implementing a configuration dialog for the tools menu. |
|
26 """ |
|
27 def __init__(self, toollist, parent=None): |
|
28 """ |
|
29 Constructor |
|
30 |
|
31 @param toollist list of configured tools |
|
32 @param parent parent widget (QWidget) |
|
33 """ |
|
34 super().__init__(parent) |
|
35 self.setupUi(self) |
|
36 |
|
37 self.iconPicker.setMode(E5PathPickerModes.OpenFileMode) |
|
38 self.iconPicker.setFilters(self.tr("Icon files (*.png)")) |
|
39 self.executablePicker.setMode(E5PathPickerModes.OpenFileMode) |
|
40 |
|
41 self.redirectionModes = [ |
|
42 ("no", self.tr("no redirection")), |
|
43 ("show", self.tr("show output")), |
|
44 ("insert", self.tr("insert into current editor")), |
|
45 ("replaceSelection", |
|
46 self.tr("replace selection of current editor")), |
|
47 ] |
|
48 |
|
49 self.toollist = copy.deepcopy(toollist) |
|
50 for tool in toollist: |
|
51 self.toolsList.addItem(tool['menutext']) |
|
52 |
|
53 for mode in self.redirectionModes: |
|
54 self.redirectCombo.addItem(mode[1]) |
|
55 |
|
56 if len(toollist): |
|
57 self.toolsList.setCurrentRow(0) |
|
58 self.on_toolsList_currentRowChanged(0) |
|
59 |
|
60 t = self.argumentsEdit.whatsThis() |
|
61 if t: |
|
62 t += Utilities.getPercentReplacementHelp() |
|
63 self.argumentsEdit.setWhatsThis(t) |
|
64 |
|
65 def __findModeIndex(self, shortName): |
|
66 """ |
|
67 Private method to find the mode index by its short name. |
|
68 |
|
69 @param shortName short name of the mode (string) |
|
70 @return index of the mode (integer) |
|
71 """ |
|
72 for ind, mode in enumerate(self.redirectionModes): |
|
73 if mode[0] == shortName: |
|
74 return ind |
|
75 return 1 # default is "show output" |
|
76 |
|
77 @pyqtSlot() |
|
78 def on_newButton_clicked(self): |
|
79 """ |
|
80 Private slot to clear all entry fields. |
|
81 """ |
|
82 self.executablePicker.clear() |
|
83 self.menuEdit.clear() |
|
84 self.iconPicker.clear() |
|
85 self.argumentsEdit.clear() |
|
86 self.redirectCombo.setCurrentIndex(1) |
|
87 |
|
88 @pyqtSlot() |
|
89 def on_addButton_clicked(self): |
|
90 """ |
|
91 Private slot to add a new entry. |
|
92 """ |
|
93 menutext = self.menuEdit.text() |
|
94 icon = self.iconPicker.text() |
|
95 executable = self.executablePicker.text() |
|
96 arguments = self.argumentsEdit.text() |
|
97 redirect = self.redirectionModes[self.redirectCombo.currentIndex()][0] |
|
98 |
|
99 if not executable: |
|
100 E5MessageBox.critical( |
|
101 self, |
|
102 self.tr("Add tool entry"), |
|
103 self.tr( |
|
104 "You have to set an executable to add to the" |
|
105 " Tools-Menu first.")) |
|
106 return |
|
107 |
|
108 if not menutext: |
|
109 E5MessageBox.critical( |
|
110 self, |
|
111 self.tr("Add tool entry"), |
|
112 self.tr( |
|
113 "You have to insert a menuentry text to add the" |
|
114 " selected program to the Tools-Menu first.")) |
|
115 return |
|
116 |
|
117 if not Utilities.isinpath(executable): |
|
118 E5MessageBox.critical( |
|
119 self, |
|
120 self.tr("Add tool entry"), |
|
121 self.tr( |
|
122 "The selected file could not be found or" |
|
123 " is not an executable." |
|
124 " Please choose an executable filename.")) |
|
125 return |
|
126 |
|
127 if len(self.toolsList.findItems( |
|
128 menutext, Qt.MatchFlags(Qt.MatchFlag.MatchExactly))): |
|
129 E5MessageBox.critical( |
|
130 self, |
|
131 self.tr("Add tool entry"), |
|
132 self.tr("An entry for the menu text {0} already exists.") |
|
133 .format(menutext)) |
|
134 return |
|
135 |
|
136 self.toolsList.addItem(menutext) |
|
137 tool = { |
|
138 'menutext': menutext, |
|
139 'icon': icon, |
|
140 'executable': executable, |
|
141 'arguments': arguments, |
|
142 'redirect': redirect, |
|
143 } |
|
144 self.toollist.append(tool) |
|
145 |
|
146 @pyqtSlot() |
|
147 def on_changeButton_clicked(self): |
|
148 """ |
|
149 Private slot to change an entry. |
|
150 """ |
|
151 row = self.toolsList.currentRow() |
|
152 if row < 0: |
|
153 return |
|
154 |
|
155 menutext = self.menuEdit.text() |
|
156 icon = self.iconPicker.text() |
|
157 executable = self.executablePicker.text() |
|
158 arguments = self.argumentsEdit.text() |
|
159 redirect = self.redirectionModes[self.redirectCombo.currentIndex()][0] |
|
160 |
|
161 if not executable: |
|
162 E5MessageBox.critical( |
|
163 self, |
|
164 self.tr("Change tool entry"), |
|
165 self.tr( |
|
166 "You have to set an executable to change the" |
|
167 " Tools-Menu entry.")) |
|
168 return |
|
169 |
|
170 if not menutext: |
|
171 E5MessageBox.critical( |
|
172 self, |
|
173 self.tr("Change tool entry"), |
|
174 self.tr( |
|
175 "You have to insert a menuentry text to change the" |
|
176 " selected Tools-Menu entry.")) |
|
177 return |
|
178 |
|
179 if not Utilities.isinpath(executable): |
|
180 E5MessageBox.critical( |
|
181 self, |
|
182 self.tr("Change tool entry"), |
|
183 self.tr( |
|
184 "The selected file could not be found or" |
|
185 " is not an executable." |
|
186 " Please choose an existing executable filename.")) |
|
187 return |
|
188 |
|
189 self.toollist[row] = { |
|
190 'menutext': menutext, |
|
191 'icon': icon, |
|
192 'executable': executable, |
|
193 'arguments': arguments, |
|
194 'redirect': redirect, |
|
195 } |
|
196 self.toolsList.currentItem().setText(menutext) |
|
197 self.changeButton.setEnabled(False) |
|
198 |
|
199 @pyqtSlot() |
|
200 def on_deleteButton_clicked(self): |
|
201 """ |
|
202 Private slot to delete the selected entry. |
|
203 """ |
|
204 row = self.toolsList.currentRow() |
|
205 if row < 0: |
|
206 return |
|
207 |
|
208 del self.toollist[row] |
|
209 itm = self.toolsList.takeItem(row) |
|
210 del itm |
|
211 if row >= len(self.toollist): |
|
212 row -= 1 |
|
213 self.toolsList.setCurrentRow(row) |
|
214 self.on_toolsList_currentRowChanged(row) |
|
215 |
|
216 @pyqtSlot() |
|
217 def on_downButton_clicked(self): |
|
218 """ |
|
219 Private slot to move an entry down in the list. |
|
220 """ |
|
221 curr = self.toolsList.currentRow() |
|
222 self.__swap(curr, curr + 1) |
|
223 self.toolsList.clear() |
|
224 for tool in self.toollist: |
|
225 self.toolsList.addItem(tool['menutext']) |
|
226 self.toolsList.setCurrentRow(curr + 1) |
|
227 if curr + 1 == len(self.toollist): |
|
228 self.downButton.setEnabled(False) |
|
229 self.upButton.setEnabled(True) |
|
230 |
|
231 @pyqtSlot() |
|
232 def on_upButton_clicked(self): |
|
233 """ |
|
234 Private slot to move an entry up in the list. |
|
235 """ |
|
236 curr = self.toolsList.currentRow() |
|
237 self.__swap(curr - 1, curr) |
|
238 self.toolsList.clear() |
|
239 for tool in self.toollist: |
|
240 self.toolsList.addItem(tool['menutext']) |
|
241 self.toolsList.setCurrentRow(curr - 1) |
|
242 if curr - 1 == 0: |
|
243 self.upButton.setEnabled(False) |
|
244 self.downButton.setEnabled(True) |
|
245 |
|
246 @pyqtSlot() |
|
247 def on_separatorButton_clicked(self): |
|
248 """ |
|
249 Private slot to add a menu separator. |
|
250 """ |
|
251 self.toolsList.addItem('--') |
|
252 tool = { |
|
253 'menutext': '--', |
|
254 'icon': '', |
|
255 'executable': '', |
|
256 'arguments': '', |
|
257 'redirect': 'no', |
|
258 } |
|
259 self.toollist.append(tool) |
|
260 |
|
261 @pyqtSlot(str) |
|
262 def on_executablePicker_pathSelected(self, path): |
|
263 """ |
|
264 Private slot to check the executable after it has been selected. |
|
265 |
|
266 @param path path of the executable |
|
267 @type str |
|
268 """ |
|
269 if path and not Utilities.isinpath(path): |
|
270 E5MessageBox.critical( |
|
271 self, |
|
272 self.tr("Select executable"), |
|
273 self.tr( |
|
274 "The selected file is not an executable." |
|
275 " Please choose an executable filename.")) |
|
276 |
|
277 def on_toolsList_currentRowChanged(self, row): |
|
278 """ |
|
279 Private slot to set the lineedits depending on the selected entry. |
|
280 |
|
281 @param row the row of the selected entry (integer) |
|
282 """ |
|
283 if row >= 0 and row < len(self.toollist): |
|
284 if self.toollist[row]['menutext'] == '--': |
|
285 self.executablePicker.clear() |
|
286 self.menuEdit.clear() |
|
287 self.iconPicker.clear() |
|
288 self.argumentsEdit.clear() |
|
289 self.redirectCombo.setCurrentIndex(0) |
|
290 else: |
|
291 tool = self.toollist[row] |
|
292 self.menuEdit.setText(tool['menutext']) |
|
293 self.iconPicker.setText(tool['icon']) |
|
294 self.executablePicker.setText(tool['executable']) |
|
295 self.argumentsEdit.setText(tool['arguments']) |
|
296 self.redirectCombo.setCurrentIndex( |
|
297 self.__findModeIndex(tool['redirect'])) |
|
298 |
|
299 self.changeButton.setEnabled(False) |
|
300 self.deleteButton.setEnabled(True) |
|
301 |
|
302 if row != 0: |
|
303 self.upButton.setEnabled(True) |
|
304 else: |
|
305 self.upButton.setEnabled(False) |
|
306 |
|
307 if row + 1 != len(self.toollist): |
|
308 self.downButton.setEnabled(True) |
|
309 else: |
|
310 self.downButton.setEnabled(False) |
|
311 else: |
|
312 self.executablePicker.clear() |
|
313 self.menuEdit.clear() |
|
314 self.iconPicker.clear() |
|
315 self.argumentsEdit.clear() |
|
316 self.downButton.setEnabled(False) |
|
317 self.upButton.setEnabled(False) |
|
318 self.deleteButton.setEnabled(False) |
|
319 self.changeButton.setEnabled(False) |
|
320 |
|
321 def __toolEntryChanged(self): |
|
322 """ |
|
323 Private slot to perform actions when a tool entry was changed. |
|
324 """ |
|
325 row = self.toolsList.currentRow() |
|
326 if ( |
|
327 row >= 0 and |
|
328 row < len(self.toollist) and |
|
329 self.toollist[row]['menutext'] != '--' |
|
330 ): |
|
331 self.changeButton.setEnabled(True) |
|
332 |
|
333 def on_menuEdit_textChanged(self, text): |
|
334 """ |
|
335 Private slot called, when the menu text was changed. |
|
336 |
|
337 @param text the new text (string) (ignored) |
|
338 """ |
|
339 self.__toolEntryChanged() |
|
340 |
|
341 def on_iconPicker_textChanged(self, text): |
|
342 """ |
|
343 Private slot called, when the icon path was changed. |
|
344 |
|
345 @param text the new text (string) (ignored) |
|
346 """ |
|
347 self.__toolEntryChanged() |
|
348 |
|
349 def on_executablePicker_textChanged(self, text): |
|
350 """ |
|
351 Private slot called, when the executable was changed. |
|
352 |
|
353 @param text the new text (string) (ignored) |
|
354 """ |
|
355 self.__toolEntryChanged() |
|
356 |
|
357 def on_argumentsEdit_textChanged(self, text): |
|
358 """ |
|
359 Private slot called, when the arguments string was changed. |
|
360 |
|
361 @param text the new text (string) (ignored) |
|
362 """ |
|
363 self.__toolEntryChanged() |
|
364 |
|
365 @pyqtSlot(int) |
|
366 def on_redirectCombo_currentIndexChanged(self, index): |
|
367 """ |
|
368 Private slot called, when the redirection mode was changed. |
|
369 |
|
370 @param index the selected mode index (integer) (ignored) |
|
371 """ |
|
372 self.__toolEntryChanged() |
|
373 |
|
374 def getToollist(self): |
|
375 """ |
|
376 Public method to retrieve the tools list. |
|
377 |
|
378 @return a list of tuples containing the menu text, the executable, |
|
379 the executables arguments and a redirection flag |
|
380 """ |
|
381 return self.toollist[:] |
|
382 |
|
383 def __swap(self, itm1, itm2): |
|
384 """ |
|
385 Private method used two swap two list entries given by their index. |
|
386 |
|
387 @param itm1 index of first entry (int) |
|
388 @param itm2 index of second entry (int) |
|
389 """ |
|
390 tmp = self.toollist[itm1] |
|
391 self.toollist[itm1] = self.toollist[itm2] |
|
392 self.toollist[itm2] = tmp |