Preferences/ShortcutsDialog.py

changeset 0
de9c2efb9d02
child 7
c679fb30c8f3
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2003 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog for the configuration of eric4s keyboard shortcuts.
8 """
9
10 from PyQt4.QtCore import *
11 from PyQt4.QtGui import *
12
13 from E4Gui.E4Application import e4App
14
15 from Ui_ShortcutsDialog import Ui_ShortcutsDialog
16 from ShortcutDialog import ShortcutDialog
17
18 import UI.PixmapCache
19
20 import Preferences
21 from Preferences import Shortcuts
22
23
24 class ShortcutsDialog(QDialog, Ui_ShortcutsDialog):
25 """
26 Class implementing a dialog for the configuration of eric4s keyboard shortcuts.
27 """
28 objectNameRole = Qt.UserRole
29 noCheckRole = Qt.UserRole + 1
30 objectTypeRole = Qt.UserRole + 2
31
32 def __init__(self, parent = None, name = None, modal = False):
33 """
34 Constructor
35
36 @param parent The parent widget of this dialog. (QWidget)
37 @param name The name of this dialog. (string)
38 @param modal Flag indicating a modal dialog. (boolean)
39 """
40 QDialog.__init__(self, parent)
41 if name:
42 self.setObjectName(name)
43 self.setModal(modal)
44 self.setupUi(self)
45
46 self.clearSearchButton.setIcon(UI.PixmapCache.getIcon("clearLeft.png"))
47 self.shortcutsList.headerItem().setText(self.shortcutsList.columnCount(), "")
48 self.shortcutsList.header().setSortIndicator(0, Qt.AscendingOrder)
49
50 self.shortcutDialog = ShortcutDialog()
51 self.connect(self.shortcutDialog, SIGNAL('shortcutChanged'),
52 self.__shortcutChanged)
53
54 def __resort(self):
55 """
56 Private method to resort the tree.
57 """
58 self.shortcutsList.sortItems(self.shortcutsList.sortColumn(),
59 self.shortcutsList.header().sortIndicatorOrder())
60
61 def __resizeColumns(self):
62 """
63 Private method to resize the list columns.
64 """
65 self.shortcutsList.header().resizeSections(QHeaderView.ResizeToContents)
66 self.shortcutsList.header().setStretchLastSection(True)
67
68 def __generateCategoryItem(self, title):
69 """
70 Private method to generate a category item.
71
72 @param title title for the item (string)
73 @return reference to the category item (QTreeWidgetItem)
74 """
75 itm = QTreeWidgetItem(self.shortcutsList, [title])
76 itm.setExpanded(True)
77 return itm
78
79 def __generateShortcutItem(self, category, action,
80 noCheck = False, objectType = None):
81 """
82 Private method to generate a keyboard shortcut item.
83
84 @param category reference to the category item (QTreeWidgetItem)
85 @param action reference to the keyboard action (E4Action)
86 @keyparam noCheck flag indicating that no uniqueness check should
87 be performed (boolean)
88 @keyparam objectType type of the object (string). Objects of the same type
89 are not checked for duplicate shortcuts.
90 """
91 itm = QTreeWidgetItem(category,
92 [action.iconText(), action.shortcut().toString(),
93 action.alternateShortcut().toString()])
94 itm.setIcon(0, action.icon())
95 itm.setData(0, self.objectNameRole, QVariant(action.objectName()))
96 itm.setData(0, self.noCheckRole, QVariant(noCheck))
97 if objectType:
98 itm.setData(0, self.objectTypeRole, QVariant(objectType))
99 else:
100 itm.setData(0, self.objectTypeRole, QVariant())
101
102 def populate(self):
103 """
104 Public method to populate the dialog.
105 """
106 self.searchEdit.clear()
107 self.searchEdit.setFocus()
108 self.shortcutsList.clear()
109 self.actionButton.setChecked(True)
110
111 # let the plugin manager create on demand plugin objects
112 pm = e4App().getObject("PluginManager")
113 pm.initOnDemandPlugins()
114
115 # populate the various lists
116 self.projectItem = self.__generateCategoryItem(self.trUtf8("Project"))
117 for act in e4App().getObject("Project").getActions():
118 self.__generateShortcutItem(self.projectItem, act)
119
120 self.uiItem = self.__generateCategoryItem(self.trUtf8("General"))
121 for act in e4App().getObject("UserInterface").getActions('ui'):
122 self.__generateShortcutItem(self.uiItem, act)
123
124 self.wizardsItem = self.__generateCategoryItem(self.trUtf8("Wizards"))
125 for act in e4App().getObject("UserInterface").getActions('wizards'):
126 self.__generateShortcutItem(self.wizardsItem, act)
127
128 self.debugItem = self.__generateCategoryItem(self.trUtf8("Debug"))
129 for act in e4App().getObject("DebugUI").getActions():
130 self.__generateShortcutItem(self.debugItem, act)
131
132 self.editItem = self.__generateCategoryItem(self.trUtf8("Edit"))
133 for act in e4App().getObject("ViewManager").getActions('edit'):
134 self.__generateShortcutItem(self.editItem, act)
135
136 self.fileItem = self.__generateCategoryItem(self.trUtf8("File"))
137 for act in e4App().getObject("ViewManager").getActions('file'):
138 self.__generateShortcutItem(self.fileItem, act)
139
140 self.searchItem = self.__generateCategoryItem(self.trUtf8("Search"))
141 for act in e4App().getObject("ViewManager").getActions('search'):
142 self.__generateShortcutItem(self.searchItem, act)
143
144 self.viewItem = self.__generateCategoryItem(self.trUtf8("View"))
145 for act in e4App().getObject("ViewManager").getActions('view'):
146 self.__generateShortcutItem(self.viewItem, act)
147
148 self.macroItem = self.__generateCategoryItem(self.trUtf8("Macro"))
149 for act in e4App().getObject("ViewManager").getActions('macro'):
150 self.__generateShortcutItem(self.macroItem, act)
151
152 self.bookmarkItem = self.__generateCategoryItem(self.trUtf8("Bookmarks"))
153 for act in e4App().getObject("ViewManager").getActions('bookmark'):
154 self.__generateShortcutItem(self.bookmarkItem, act)
155
156 self.spellingItem = self.__generateCategoryItem(self.trUtf8("Spelling"))
157 for act in e4App().getObject("ViewManager").getActions('spelling'):
158 self.__generateShortcutItem(self.spellingItem, act)
159
160 actions = e4App().getObject("ViewManager").getActions('window')
161 if actions:
162 self.windowItem = self.__generateCategoryItem(self.trUtf8("Window"))
163 for act in actions:
164 self.__generateShortcutItem(self.windowItem, act)
165
166 self.pluginCategoryItems = []
167 for category, ref in e4App().getPluginObjects():
168 if hasattr(ref, "getActions"):
169 categoryItem = self.__generateCategoryItem(category)
170 objectType = e4App().getPluginObjectType(category)
171 for act in ref.getActions():
172 self.__generateShortcutItem(categoryItem, act,
173 objectType = objectType)
174 self.pluginCategoryItems.append(categoryItem)
175
176 self.helpViewerItem = self.__generateCategoryItem(self.trUtf8("Web Browser"))
177 for act in e4App().getObject("DummyHelpViewer").getActions():
178 self.__generateShortcutItem(self.helpViewerItem, act, True)
179
180 self.__resort()
181 self.__resizeColumns()
182
183 self.__editTopItem = None
184
185 def on_shortcutsList_itemDoubleClicked(self, itm, column):
186 """
187 Private slot to handle a double click in the shortcuts list.
188
189 @param itm the list item that was double clicked (QTreeWidgetItem)
190 @param column the list item was double clicked in (integer)
191 """
192 if itm.childCount():
193 return
194
195 self.__editTopItem = itm.parent()
196
197 self.shortcutDialog.setKeys(QKeySequence(itm.text(1)), QKeySequence(itm.text(2)),
198 itm.data(0, self.noCheckRole).toBool(),
199 itm.data(0, self.objectTypeRole).toString())
200 self.shortcutDialog.show()
201
202 def on_shortcutsList_itemClicked(self, itm, column):
203 """
204 Private slot to handle a click in the shortcuts list.
205
206 @param itm the list item that was clicked (QTreeWidgetItem)
207 @param column the list item was clicked in (integer)
208 """
209 if itm.childCount() or column not in [1, 2]:
210 return
211
212 self.shortcutsList.openPersistentEditor(itm, column)
213
214 def on_shortcutsList_itemChanged(self, itm, column):
215 """
216 Private slot to handle the edit of a shortcut key.
217
218 @param itm reference to the item changed (QTreeWidgetItem)
219 @param column column changed (integer)
220 """
221 if column != 0:
222 keystr = itm.text(column).title()
223 if not itm.data(0, self.noCheckRole).toBool() and \
224 not self.__checkShortcut(QKeySequence(keystr),
225 itm.data(0, self.objectTypeRole).toString(),
226 itm.parent()):
227 itm.setText(column, "")
228 else:
229 itm.setText(column, keystr)
230 self.shortcutsList.closePersistentEditor(itm, column)
231
232 def __shortcutChanged(self, keysequence, altKeysequence, noCheck, objectType):
233 """
234 Private slot to handle the shortcutChanged signal of the shortcut dialog.
235
236 @param keysequence the keysequence of the changed action (QKeySequence)
237 @param altKeysequence the alternative keysequence of the changed
238 action (QKeySequence)
239 @param noCheck flag indicating that no uniqueness check should
240 be performed (boolean)
241 @param objectType type of the object (string).
242 """
243 if not noCheck and \
244 (not self.__checkShortcut(keysequence, objectType, self.__editTopItem) or \
245 not self.__checkShortcut(altKeysequence, objectType, self.__editTopItem)):
246 return
247
248 self.shortcutsList.currentItem().setText(1, keysequence.toString())
249 self.shortcutsList.currentItem().setText(2, altKeysequence.toString())
250
251 self.__resort()
252 self.__resizeColumns()
253
254 def __checkShortcut(self, keysequence, objectType, origTopItem):
255 """
256 Private method to check a keysequence for uniqueness.
257
258 @param keysequence the keysequence to check (QKeySequence)
259 @param objectType type of the object (string). Entries with the same
260 object type are not checked for uniqueness.
261 @param origTopItem refrence to the parent of the item to be checked
262 (QTreeWidgetItem)
263 @return flag indicating uniqueness (boolean)
264 """
265 if keysequence.isEmpty():
266 return True
267
268 keystr = keysequence.toString()
269 keyname = self.shortcutsList.currentItem().text(0)
270 for topIndex in range(self.shortcutsList.topLevelItemCount()):
271 topItem = self.shortcutsList.topLevelItem(topIndex)
272 for index in range(topItem.childCount()):
273 itm = topItem.child(index)
274
275 # 1. shall a check be performed?
276 if itm.data(0, self.noCheckRole).toBool():
277 continue
278
279 # 2. check object type
280 itmObjectType = itm.data(0, self.objectTypeRole).toString()
281 if itmObjectType and \
282 itmObjectType == objectType and \
283 topItem != origTopItem:
284 continue
285
286 # 3. check key name
287 if itm.text(0) != keyname:
288 for col in [1, 2]: # check against primary, then alternative binding
289 itmseq = itm.text(col)
290 # step 1: check if shortcut is already allocated
291 if keystr == itmseq:
292 res = QMessageBox.warning(None,
293 self.trUtf8("Edit shortcuts"),
294 self.trUtf8(\
295 """<p><b>{0}</b> has already been allocated"""
296 """ to the <b>{1}</b> action. """
297 """Remove this binding?</p>""")
298 .format(keystr, itm.text(0)),
299 QMessageBox.StandardButtons(\
300 QMessageBox.No | \
301 QMessageBox.Yes),
302 QMessageBox.No)
303 if res == QMessageBox.Yes:
304 itm.setText(col, "")
305 return True
306 else:
307 return False
308
309 if not itmseq:
310 continue
311
312 # step 2: check if shortcut hides an already allocated
313 if itmseq.startswith("%s+" % keystr):
314 res = QMessageBox.warning(None,
315 self.trUtf8("Edit shortcuts"),
316 self.trUtf8(\
317 """<p><b>{0}</b> hides the <b>{1}</b> action. """
318 """Remove this binding?</p>""")
319 .format(keystr, itm.text(0)),
320 QMessageBox.StandardButtons(\
321 QMessageBox.No | \
322 QMessageBox.Yes),
323 QMessageBox.No)
324 if res == QMessageBox.Yes:
325 itm.setText(col, "")
326 return True
327 else:
328 return False
329
330 # step 3: check if shortcut is hidden by an already allocated
331 if keystr.startswith("%s+" % itmseq):
332 res = QMessageBox.warning(None,
333 self.trUtf8("Edit shortcuts"),
334 self.trUtf8(\
335 """<p><b>{0}</b> is hidden by the """
336 """<b>{1}</b> action. """
337 """Remove this binding?</p>""")
338 .format(keystr, itm.text(0)),
339 QMessageBox.StandardButtons(\
340 QMessageBox.No | \
341 QMessageBox.Yes),
342 QMessageBox.No)
343 if res == QMessageBox.Yes:
344 itm.setText(col, "")
345 return True
346 else:
347 return False
348
349 return True
350
351 def __saveCategoryActions(self, category, actions):
352 """
353 Private method to save the actions for a category.
354
355 @param category reference to the category item (QTreeWidgetItem)
356 @param actions list of actions for the category (list of E4Action)
357 """
358 for index in range(category.childCount()):
359 itm = category.child(index)
360 ## txt = itm.text(4) # this is one more due to empty last section
361 txt = itm.data(0, self.objectNameRole).toString()
362 for act in actions:
363 if txt == act.objectName():
364 act.setShortcut(QKeySequence(itm.text(1)))
365 act.setAlternateShortcut(QKeySequence(itm.text(2)))
366 break
367
368 def on_buttonBox_accepted(self):
369 """
370 Private slot to handle the OK button press.
371 """
372 self.__saveCategoryActions(self.projectItem,
373 e4App().getObject("Project").getActions())
374 self.__saveCategoryActions(self.uiItem,
375 e4App().getObject("UserInterface").getActions('ui'))
376 self.__saveCategoryActions(self.wizardsItem,
377 e4App().getObject("UserInterface").getActions('wizards'))
378 self.__saveCategoryActions(self.debugItem,
379 e4App().getObject("DebugUI").getActions())
380 self.__saveCategoryActions(self.editItem,
381 e4App().getObject("ViewManager").getActions('edit'))
382 self.__saveCategoryActions(self.fileItem,
383 e4App().getObject("ViewManager").getActions('file'))
384 self.__saveCategoryActions(self.searchItem,
385 e4App().getObject("ViewManager").getActions('search'))
386 self.__saveCategoryActions(self.viewItem,
387 e4App().getObject("ViewManager").getActions('view'))
388 self.__saveCategoryActions(self.macroItem,
389 e4App().getObject("ViewManager").getActions('macro'))
390 self.__saveCategoryActions(self.bookmarkItem,
391 e4App().getObject("ViewManager").getActions('bookmark'))
392 self.__saveCategoryActions(self.spellingItem,
393 e4App().getObject("ViewManager").getActions('spelling'))
394
395 actions = e4App().getObject("ViewManager").getActions('window')
396 if actions:
397 self.__saveCategoryActions(self.windowItem, actions)
398
399 for categoryItem in self.pluginCategoryItems:
400 category = categoryItem.text(0)
401 ref = e4App().getPluginObject(category)
402 if ref is not None and hasattr(ref, "getActions"):
403 self.__saveCategoryActions(categoryItem, ref.getActions())
404
405 self.__saveCategoryActions(self.helpViewerItem,
406 e4App().getObject("DummyHelpViewer").getActions())
407
408 Shortcuts.saveShortcuts()
409 Preferences.syncPreferences()
410
411 self.emit(SIGNAL('updateShortcuts'))
412 self.hide()
413
414 @pyqtSlot(str)
415 def on_searchEdit_textChanged(self, txt):
416 """
417 Private slot called, when the text in the search edit changes.
418
419 @param txt text of the search edit (string)
420 """
421 for topIndex in range(self.shortcutsList.topLevelItemCount()):
422 topItem = self.shortcutsList.topLevelItem(topIndex)
423 childHiddenCount = 0
424 for index in range(topItem.childCount()):
425 itm = topItem.child(index)
426 if (self.actionButton.isChecked() and \
427 not QRegExp(txt, Qt.CaseInsensitive).indexIn(itm.text(0)) > -1) or \
428 (self.shortcutButton.isChecked() and \
429 not txt.lower() in itm.text(1).lower() and \
430 not txt.lower() in itm.text(2).lower()):
431 itm.setHidden(True)
432 childHiddenCount += 1
433 else:
434 itm.setHidden(False)
435 topItem.setHidden(childHiddenCount == topItem.childCount())
436
437 @pyqtSlot()
438 def on_clearSearchButton_clicked(self):
439 """
440 Private slot called by a click of the clear search button.
441 """
442 self.searchEdit.clear()
443
444 @pyqtSlot(bool)
445 def on_actionButton_toggled(self, checked):
446 """
447 Private slot called, when the action radio button is toggled.
448
449 @param checked state of the action radio button (boolean)
450 """
451 if checked:
452 self.on_searchEdit_textChanged(self.searchEdit.text())
453
454 @pyqtSlot(bool)
455 def on_shortcutButton_toggled(self, checked):
456 """
457 Private slot called, when the shortcuts radio button is toggled.
458
459 @param checked state of the shortcuts radio button (boolean)
460 """
461 if checked:
462 self.on_searchEdit_textChanged(self.searchEdit.text())

eric ide

mercurial