~ubuntu-branches/debian/experimental/spyder/experimental

« back to all changes in this revision

Viewing changes to spyderlib/plugins/shortcuts.py

  • Committer: Package Import Robot
  • Author(s): Picca Frédéric-Emmanuel
  • Date: 2013-01-20 12:19:54 UTC
  • mfrom: (1.1.16)
  • Revision ID: package-import@ubuntu.com-20130120121954-1jt1xa924bshhvh0
Tags: 2.2.0~beta1+dfsg-2
fix typo ipython-qtconsol -> ipython-qtconsole

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
#
3
 
# Copyright © 2010 Pierre Raybaut
4
 
# Licensed under the terms of the MIT License
5
 
# (see spyderlib/__init__.py for details)
6
 
 
7
 
"""Shortcut management"""
8
 
 
9
 
from spyderlib.qt.QtGui import (QVBoxLayout, QComboBox, QItemDelegate,
10
 
                                QTableView, QMessageBox, QPushButton)
11
 
from spyderlib.qt.QtCore import (Qt, QSize, QAbstractTableModel, QModelIndex,
12
 
                                 SIGNAL)
13
 
from spyderlib.qt.compat import to_qvariant, from_qvariant
14
 
 
15
 
import sys
16
 
 
17
 
# Local imports
18
 
from spyderlib.baseconfig import _
19
 
from spyderlib.config import (get_icon, get_shortcut, set_shortcut,
20
 
                              iter_shortcuts, reset_shortcuts)
21
 
from spyderlib.plugins.configdialog import GeneralConfigPage
22
 
 
23
 
 
24
 
KEYSTRINGS = ["Escape", "Tab", "Backtab", "Backspace", "Return", "Enter",
25
 
              "Delete", "Pause", "Print", "Clear", "Home", "End", "Left",
26
 
              "Up", "Right", "Down", "PageUp", "PageDown"] + \
27
 
             ["F%d" % _i for _i in range(1, 36)] + \
28
 
             ["Space", "Exclam", "QuoteDbl", "NumberSign", "Dollar", "Percent",
29
 
              "Ampersand", "Apostrophe", "ParenLeft", "ParenRight", "Asterisk",
30
 
              "Plus", "Comma", "Minus", "Period", "Slash"] + \
31
 
             [str(_i) for _i in range(10)] + \
32
 
             ["Colon", "Semicolon", "Less", "Equal", "Greater", "Question",
33
 
              "At"] + [chr(_i) for _i in range(65, 91)] + \
34
 
             ["BracketLeft", "Backslash", "BracketRight", "Underscore"]
35
 
 
36
 
 
37
 
class Key(object):
38
 
    MODIFIERS = {Qt.NoModifier: "", Qt.ShiftModifier: "Shift",
39
 
                 Qt.ControlModifier: "Ctrl", Qt.AltModifier: "Alt",
40
 
                 Qt.MetaModifier: "Meta"}
41
 
    if sys.platform == 'darwin':
42
 
        MODIFIERNAMES = {Qt.NoModifier: "", Qt.ShiftModifier: "Shift",
43
 
                         Qt.ControlModifier: "Cmd", Qt.AltModifier: "Alt",
44
 
                         Qt.MetaModifier: "Ctrl"}
45
 
    elif sys.platform == 'win32':
46
 
        MODIFIERNAMES = {Qt.NoModifier: "", Qt.ShiftModifier: "Shift",
47
 
                         Qt.ControlModifier: "Ctrl", Qt.AltModifier: "Alt",
48
 
                         Qt.MetaModifier: "Win"}
49
 
    else:
50
 
        MODIFIERNAMES = {Qt.NoModifier: "", Qt.ShiftModifier: "Shift",
51
 
                         Qt.ControlModifier: "Ctrl", Qt.AltModifier: "Alt",
52
 
                         Qt.MetaModifier: "Meta"}
53
 
    KEYS = {}
54
 
    for attr in KEYSTRINGS:
55
 
        KEYS[getattr(Qt, "Key_"+attr)] = attr
56
 
 
57
 
    def __init__(self, key, mod1=Qt.NoModifier, mod2=Qt.NoModifier,
58
 
                 mod3=Qt.NoModifier):
59
 
        modifiers = [mod1, mod2, mod3]
60
 
        assert all([mod in self.MODIFIERS for mod in modifiers])
61
 
        self.modifiers = sorted(modifiers)
62
 
        assert key in self.KEYS
63
 
        self.key = key
64
 
        
65
 
    def __str__(self):
66
 
        tlist = []
67
 
        for mod in sorted(list(set(self.modifiers))):
68
 
            if mod != Qt.NoModifier:
69
 
                tlist.append(self.MODIFIERS[mod])
70
 
        tlist.append(self.KEYS[self.key])
71
 
        return "+".join(tlist)
72
 
    
73
 
    def __unicode__(self):
74
 
        return unicode(self.__str__())
75
 
    
76
 
    @staticmethod
77
 
    def modifier_from_str(modstr):
78
 
        for k, v in Key.MODIFIERS.iteritems():
79
 
            if v.lower() == modstr.lower():
80
 
                return k
81
 
    
82
 
    @staticmethod
83
 
    def key_from_str(keystr):
84
 
        for k, v in Key.KEYS.iteritems():
85
 
            if v.lower() == keystr.lower():
86
 
                return k
87
 
 
88
 
    @staticmethod
89
 
    def modifier_from_name(modname):
90
 
        for k, v in Key.MODIFIERNAMES.iteritems():
91
 
            if v.lower() == modname.lower():
92
 
                return k        
93
 
 
94
 
def keystr2key(keystr):
95
 
    keylist = keystr.split("+")
96
 
    mods = []
97
 
    if len(keylist) > 1:
98
 
        for modstr in keylist[:-1]:
99
 
            mods.append(Key.modifier_from_str(modstr))
100
 
    return Key(Key.key_from_str(keylist[-1]), *mods)
101
 
 
102
 
class Shortcut(object):
103
 
    def __init__(self, context, name, key=None):
104
 
        self.context = context
105
 
        self.name = name
106
 
        if isinstance(key, basestring):
107
 
            key = keystr2key(key)
108
 
        self.key = key
109
 
        
110
 
    def __str__(self):
111
 
        return "%s/%s: %s" % (self.context, self.name, self.key)
112
 
    
113
 
    def load(self):
114
 
        self.key = keystr2key(get_shortcut(self.context, self.name))
115
 
    
116
 
    def save(self):
117
 
        set_shortcut(self.context, self.name, str(self.key))
118
 
 
119
 
 
120
 
CONTEXT, NAME, MOD1, MOD2, MOD3, KEY = range(6)
121
 
 
122
 
class ShortcutsModel(QAbstractTableModel):
123
 
    def __init__(self):
124
 
        QAbstractTableModel.__init__(self)
125
 
        self.shortcuts = []
126
 
 
127
 
    def sortByName(self):
128
 
        self.shortcuts = sorted(self.shortcuts,
129
 
                                key=lambda x: x.context+'/'+x.name)
130
 
        self.reset()
131
 
 
132
 
    def flags(self, index):
133
 
        if not index.isValid():
134
 
            return Qt.ItemIsEnabled
135
 
        column = index.column()
136
 
        if column in (MOD1, MOD2, MOD3, KEY):
137
 
            return Qt.ItemFlags(QAbstractTableModel.flags(self, index)|
138
 
                                Qt.ItemIsEditable)
139
 
        else:
140
 
            return Qt.ItemFlags(QAbstractTableModel.flags(self, index))
141
 
 
142
 
    def data(self, index, role=Qt.DisplayRole):
143
 
        if not index.isValid() or \
144
 
           not (0 <= index.row() < len(self.shortcuts)):
145
 
            return to_qvariant()
146
 
        shortcut = self.shortcuts[index.row()]
147
 
        key = shortcut.key
148
 
        column = index.column()
149
 
        if role == Qt.DisplayRole:
150
 
            if column == CONTEXT:
151
 
                return to_qvariant(shortcut.context)
152
 
            elif column == NAME:
153
 
                return to_qvariant(shortcut.name)
154
 
            elif column == MOD1:
155
 
                return to_qvariant(Key.MODIFIERNAMES[key.modifiers[0]])
156
 
            elif column == MOD2:
157
 
                return to_qvariant(Key.MODIFIERNAMES[key.modifiers[1]])
158
 
            elif column == MOD3:
159
 
                return to_qvariant(Key.MODIFIERNAMES[key.modifiers[2]])
160
 
            elif column == KEY:
161
 
                return to_qvariant(Key.KEYS[key.key])
162
 
        elif role == Qt.TextAlignmentRole:
163
 
            return to_qvariant(int(Qt.AlignHCenter|Qt.AlignVCenter))
164
 
        return to_qvariant()
165
 
 
166
 
    def headerData(self, section, orientation, role=Qt.DisplayRole):
167
 
        if role == Qt.TextAlignmentRole:
168
 
            if orientation == Qt.Horizontal:
169
 
                return to_qvariant(int(Qt.AlignHCenter|Qt.AlignVCenter))
170
 
            return to_qvariant(int(Qt.AlignRight|Qt.AlignVCenter))
171
 
        if role != Qt.DisplayRole:
172
 
            return to_qvariant()
173
 
        if orientation == Qt.Horizontal:
174
 
            if section == CONTEXT:
175
 
                return to_qvariant(_("Context"))
176
 
            elif section == NAME:
177
 
                return to_qvariant(_("Name"))
178
 
            elif section == MOD1:
179
 
                return to_qvariant(_("Mod1"))
180
 
            elif section == MOD2:
181
 
                return to_qvariant(_("Mod2"))
182
 
            elif section == MOD3:
183
 
                return to_qvariant(_("Mod3"))
184
 
            elif section == KEY:
185
 
                return to_qvariant(_("Key"))
186
 
        return to_qvariant()
187
 
 
188
 
    def rowCount(self, index=QModelIndex()):
189
 
        return len(self.shortcuts)
190
 
 
191
 
    def columnCount(self, index=QModelIndex()):
192
 
        return 6
193
 
    
194
 
    def setData(self, index, value, role=Qt.EditRole):
195
 
        if index.isValid() and 0 <= index.row() < len(self.shortcuts):
196
 
            shortcut = self.shortcuts[index.row()]
197
 
            key = shortcut.key
198
 
            column = index.column()
199
 
            text = from_qvariant(value, str)
200
 
            if column == MOD1:
201
 
                key.modifiers[0] = Key.modifier_from_name(text)
202
 
            elif column == MOD2:
203
 
                key.modifiers[1] = Key.modifier_from_name(text)
204
 
            elif column == MOD3:
205
 
                key.modifiers[2] = Key.modifier_from_name(text)
206
 
            elif column == KEY:
207
 
                key.key = Key.key_from_str(text)
208
 
            self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"),
209
 
                      index, index)
210
 
            return True
211
 
        return False
212
 
 
213
 
 
214
 
class ShortcutsDelegate(QItemDelegate):
215
 
    def __init__(self, parent=None):
216
 
        QItemDelegate.__init__(self, parent)
217
 
        self.modifiers = sorted(Key.MODIFIERNAMES.values())
218
 
        self.mod = None
219
 
        self.keys = sorted(Key.KEYS.values())
220
 
        self.key = None
221
 
        
222
 
    def sizeHint(self, option, index):
223
 
        fm = option.fontMetrics
224
 
        if index.column() in (MOD1, MOD2, MOD3):
225
 
            if self.mod is None:
226
 
                w = 0
227
 
                for mod in self.modifiers:
228
 
                    cw = fm.width(mod)
229
 
                    if cw > w:
230
 
                        w = cw
231
 
                        self.mod = mod
232
 
            else:
233
 
                w = fm.width(self.mod)
234
 
            return QSize(w+20, fm.height())
235
 
        elif index.column() == KEY:
236
 
            if self.key is None:
237
 
                w = 0
238
 
                for key in self.keys:
239
 
                    cw = fm.width(key)
240
 
                    if cw > w:
241
 
                        w = cw
242
 
                        self.key = key
243
 
            else:
244
 
                w = fm.width(self.key)
245
 
            return QSize(w+20, fm.height())
246
 
        return QItemDelegate.sizeHint(self, option, index)
247
 
 
248
 
    def createEditor(self, parent, option, index):
249
 
        if index.column() in (MOD1, MOD2, MOD3):
250
 
            combobox = QComboBox(parent)
251
 
            combobox.addItems(self.modifiers)
252
 
            return combobox
253
 
        elif index.column() == KEY:
254
 
            combobox = QComboBox(parent)
255
 
            combobox.addItems(self.keys)
256
 
            return combobox
257
 
        else:
258
 
            return QItemDelegate.createEditor(self, parent, option,
259
 
                                              index)
260
 
 
261
 
    def setEditorData(self, editor, index):
262
 
        text = from_qvariant(index.model().data(index, Qt.DisplayRole), str)
263
 
        if index.column() in (MOD1, MOD2, MOD3, KEY):
264
 
            i = editor.findText(text)
265
 
            if i == -1:
266
 
                i = 0
267
 
            editor.setCurrentIndex(i)
268
 
        else:
269
 
            QItemDelegate.setEditorData(self, editor, index)
270
 
 
271
 
    def setModelData(self, editor, model, index):
272
 
        if index.column() in (MOD1, MOD2, MOD3, KEY):
273
 
            model.setData(index, to_qvariant(editor.currentText()))
274
 
        else:
275
 
            QItemDelegate.setModelData(self, editor, model, index)
276
 
 
277
 
 
278
 
class ShortcutsTable(QTableView):
279
 
    def __init__(self, parent=None):
280
 
        QTableView.__init__(self, parent)
281
 
        self.model = ShortcutsModel()
282
 
        self.setModel(self.model)
283
 
        self.setItemDelegate(ShortcutsDelegate(self))
284
 
        self.load_shortcuts()
285
 
                     
286
 
    def adjust_cells(self):
287
 
        self.resizeColumnsToContents()
288
 
#        self.resizeRowsToContents()
289
 
        self.horizontalHeader().setStretchLastSection(True)
290
 
        
291
 
    def load_shortcuts(self):
292
 
        shortcuts = []
293
 
        for context, name, keystr in iter_shortcuts():
294
 
            shortcut = Shortcut(context, name, keystr)
295
 
            shortcuts.append(shortcut)
296
 
        shortcuts = sorted(shortcuts, key=lambda x: x.context+x.name)
297
 
        self.model.shortcuts = shortcuts
298
 
        self.model.reset()
299
 
        self.adjust_cells()
300
 
 
301
 
    def check_shortcuts(self):
302
 
        """Check shortcuts for conflicts"""
303
 
        conflicts = []
304
 
        for index, sh1 in enumerate(self.model.shortcuts):
305
 
            if index == len(self.model.shortcuts)-1:
306
 
                break
307
 
            for sh2 in self.model.shortcuts[index+1:]:
308
 
                if sh2 is sh1:
309
 
                    continue
310
 
                if str(sh2.key) == str(sh1.key) \
311
 
                   and (sh1.context == sh2.context or sh1.context == '_'
312
 
                        or sh2.context == '_'):
313
 
                    conflicts.append((sh1, sh2))
314
 
        if conflicts:
315
 
            self.parent().emit(SIGNAL('show_this_page()'))
316
 
            cstr = "\n".join(['%s <---> %s' % (sh1, sh2)
317
 
                              for sh1, sh2 in conflicts])
318
 
            QMessageBox.warning(self, _( "Conflicts"),
319
 
                                _("The following conflicts have been "
320
 
                                  "detected:")+"\n"+cstr, QMessageBox.Ok)
321
 
        
322
 
    def save_shortcuts(self):
323
 
        self.check_shortcuts()
324
 
        for shortcut in self.model.shortcuts:
325
 
            shortcut.save()
326
 
        
327
 
 
328
 
class ShortcutsConfigPage(GeneralConfigPage):
329
 
    CONF_SECTION = "shortcuts"
330
 
    def get_name(self):
331
 
        return _("Keyboard shortcuts")
332
 
    
333
 
    def get_icon(self):
334
 
        return get_icon("genprefs.png")
335
 
    
336
 
    def setup_page(self):
337
 
        self.table = ShortcutsTable(self)
338
 
        self.connect(self.table.model,
339
 
                     SIGNAL("dataChanged(QModelIndex,QModelIndex)"),
340
 
                     lambda i1, i2, opt='': self.has_been_modified(opt))
341
 
        vlayout = QVBoxLayout()
342
 
        vlayout.addWidget(self.table)
343
 
        reset_btn = QPushButton(_("Reset to default values"))
344
 
        self.connect(reset_btn, SIGNAL('clicked()'), self.reset_to_default)
345
 
        vlayout.addWidget(reset_btn)
346
 
        self.setLayout(vlayout)
347
 
        
348
 
    def check_settings(self):
349
 
        self.table.check_shortcuts()
350
 
        
351
 
    def reset_to_default(self):
352
 
        reset_shortcuts()
353
 
        self.main.apply_shortcuts()
354
 
        self.table.load_shortcuts()
355
 
        self.load_from_conf()
356
 
        self.set_modified(False)
357
 
            
358
 
    def apply_settings(self, options):
359
 
        self.table.save_shortcuts()
360
 
        self.main.apply_shortcuts()
361
 
 
362
 
 
363
 
def test():
364
 
    from spyderlib.utils.qthelpers import qapplication
365
 
    app = qapplication()
366
 
    table = ShortcutsTable()
367
 
    table.show()
368
 
    app.exec_()
369
 
    print [str(s) for s in table.model.shortcuts]
370
 
    table.check_shortcuts()
371
 
 
372
 
if __name__ == '__main__':
 
1
# -*- coding: utf-8 -*-
 
2
#
 
3
# Copyright © 2010 Pierre Raybaut
 
4
# Licensed under the terms of the MIT License
 
5
# (see spyderlib/__init__.py for details)
 
6
 
 
7
"""Shortcut management"""
 
8
 
 
9
from spyderlib.qt.QtGui import (QVBoxLayout, QComboBox, QItemDelegate,
 
10
                                QTableView, QMessageBox, QPushButton)
 
11
from spyderlib.qt.QtCore import (Qt, QSize, QAbstractTableModel, QModelIndex,
 
12
                                 SIGNAL)
 
13
from spyderlib.qt.compat import to_qvariant, from_qvariant
 
14
 
 
15
import sys
 
16
 
 
17
# Local imports
 
18
from spyderlib.baseconfig import _
 
19
from spyderlib.guiconfig import (get_icon, get_shortcut, set_shortcut,
 
20
                                 iter_shortcuts, reset_shortcuts)
 
21
from spyderlib.plugins.configdialog import GeneralConfigPage
 
22
 
 
23
 
 
24
KEYSTRINGS = ["Escape", "Tab", "Backtab", "Backspace", "Return", "Enter",
 
25
              "Delete", "Pause", "Print", "Clear", "Home", "End", "Left",
 
26
              "Up", "Right", "Down", "PageUp", "PageDown"] + \
 
27
             ["F%d" % _i for _i in range(1, 36)] + \
 
28
             ["Space", "Exclam", "QuoteDbl", "NumberSign", "Dollar", "Percent",
 
29
              "Ampersand", "Apostrophe", "ParenLeft", "ParenRight", "Asterisk",
 
30
              "Plus", "Comma", "Minus", "Period", "Slash"] + \
 
31
             [str(_i) for _i in range(10)] + \
 
32
             ["Colon", "Semicolon", "Less", "Equal", "Greater", "Question",
 
33
              "At"] + [chr(_i) for _i in range(65, 91)] + \
 
34
             ["BracketLeft", "Backslash", "BracketRight", "Underscore"]
 
35
 
 
36
 
 
37
class Key(object):
 
38
    MODIFIERS = {Qt.NoModifier: "", Qt.ShiftModifier: "Shift",
 
39
                 Qt.ControlModifier: "Ctrl", Qt.AltModifier: "Alt",
 
40
                 Qt.MetaModifier: "Meta"}
 
41
    if sys.platform == 'darwin':
 
42
        MODIFIERNAMES = {Qt.NoModifier: "", Qt.ShiftModifier: "Shift",
 
43
                         Qt.ControlModifier: "Cmd", Qt.AltModifier: "Alt",
 
44
                         Qt.MetaModifier: "Ctrl"}
 
45
    elif sys.platform == 'win32':
 
46
        MODIFIERNAMES = {Qt.NoModifier: "", Qt.ShiftModifier: "Shift",
 
47
                         Qt.ControlModifier: "Ctrl", Qt.AltModifier: "Alt",
 
48
                         Qt.MetaModifier: "Win"}
 
49
    else:
 
50
        MODIFIERNAMES = {Qt.NoModifier: "", Qt.ShiftModifier: "Shift",
 
51
                         Qt.ControlModifier: "Ctrl", Qt.AltModifier: "Alt",
 
52
                         Qt.MetaModifier: "Meta"}
 
53
    KEYS = {}
 
54
    for attr in KEYSTRINGS:
 
55
        KEYS[getattr(Qt, "Key_"+attr)] = attr
 
56
 
 
57
    def __init__(self, key, mod1=Qt.NoModifier, mod2=Qt.NoModifier,
 
58
                 mod3=Qt.NoModifier):
 
59
        modifiers = [mod1, mod2, mod3]
 
60
        assert all([mod in self.MODIFIERS for mod in modifiers])
 
61
        self.modifiers = sorted(modifiers)
 
62
        assert key in self.KEYS
 
63
        self.key = key
 
64
        
 
65
    def __str__(self):
 
66
        tlist = []
 
67
        for mod in sorted(list(set(self.modifiers))):
 
68
            if mod != Qt.NoModifier:
 
69
                tlist.append(self.MODIFIERS[mod])
 
70
        tlist.append(self.KEYS[self.key])
 
71
        return "+".join(tlist)
 
72
    
 
73
    def __unicode__(self):
 
74
        return unicode(self.__str__())
 
75
    
 
76
    @staticmethod
 
77
    def modifier_from_str(modstr):
 
78
        for k, v in Key.MODIFIERS.iteritems():
 
79
            if v.lower() == modstr.lower():
 
80
                return k
 
81
    
 
82
    @staticmethod
 
83
    def key_from_str(keystr):
 
84
        for k, v in Key.KEYS.iteritems():
 
85
            if v.lower() == keystr.lower():
 
86
                return k
 
87
 
 
88
    @staticmethod
 
89
    def modifier_from_name(modname):
 
90
        for k, v in Key.MODIFIERNAMES.iteritems():
 
91
            if v.lower() == modname.lower():
 
92
                return k        
 
93
 
 
94
def keystr2key(keystr):
 
95
    keylist = keystr.split("+")
 
96
    mods = []
 
97
    if len(keylist) > 1:
 
98
        for modstr in keylist[:-1]:
 
99
            mods.append(Key.modifier_from_str(modstr))
 
100
    return Key(Key.key_from_str(keylist[-1]), *mods)
 
101
 
 
102
class Shortcut(object):
 
103
    def __init__(self, context, name, key=None):
 
104
        self.context = context
 
105
        self.name = name
 
106
        if isinstance(key, basestring):
 
107
            key = keystr2key(key)
 
108
        self.key = key
 
109
        
 
110
    def __str__(self):
 
111
        return "%s/%s: %s" % (self.context, self.name, self.key)
 
112
    
 
113
    def load(self):
 
114
        self.key = keystr2key(get_shortcut(self.context, self.name))
 
115
    
 
116
    def save(self):
 
117
        set_shortcut(self.context, self.name, str(self.key))
 
118
 
 
119
 
 
120
CONTEXT, NAME, MOD1, MOD2, MOD3, KEY = range(6)
 
121
 
 
122
class ShortcutsModel(QAbstractTableModel):
 
123
    def __init__(self):
 
124
        QAbstractTableModel.__init__(self)
 
125
        self.shortcuts = []
 
126
 
 
127
    def sortByName(self):
 
128
        self.shortcuts = sorted(self.shortcuts,
 
129
                                key=lambda x: x.context+'/'+x.name)
 
130
        self.reset()
 
131
 
 
132
    def flags(self, index):
 
133
        if not index.isValid():
 
134
            return Qt.ItemIsEnabled
 
135
        column = index.column()
 
136
        if column in (MOD1, MOD2, MOD3, KEY):
 
137
            return Qt.ItemFlags(QAbstractTableModel.flags(self, index)|
 
138
                                Qt.ItemIsEditable)
 
139
        else:
 
140
            return Qt.ItemFlags(QAbstractTableModel.flags(self, index))
 
141
 
 
142
    def data(self, index, role=Qt.DisplayRole):
 
143
        if not index.isValid() or \
 
144
           not (0 <= index.row() < len(self.shortcuts)):
 
145
            return to_qvariant()
 
146
        shortcut = self.shortcuts[index.row()]
 
147
        key = shortcut.key
 
148
        column = index.column()
 
149
        if role == Qt.DisplayRole:
 
150
            if column == CONTEXT:
 
151
                return to_qvariant(shortcut.context)
 
152
            elif column == NAME:
 
153
                return to_qvariant(shortcut.name)
 
154
            elif column == MOD1:
 
155
                return to_qvariant(Key.MODIFIERNAMES[key.modifiers[0]])
 
156
            elif column == MOD2:
 
157
                return to_qvariant(Key.MODIFIERNAMES[key.modifiers[1]])
 
158
            elif column == MOD3:
 
159
                return to_qvariant(Key.MODIFIERNAMES[key.modifiers[2]])
 
160
            elif column == KEY:
 
161
                return to_qvariant(Key.KEYS[key.key])
 
162
        elif role == Qt.TextAlignmentRole:
 
163
            return to_qvariant(int(Qt.AlignHCenter|Qt.AlignVCenter))
 
164
        return to_qvariant()
 
165
 
 
166
    def headerData(self, section, orientation, role=Qt.DisplayRole):
 
167
        if role == Qt.TextAlignmentRole:
 
168
            if orientation == Qt.Horizontal:
 
169
                return to_qvariant(int(Qt.AlignHCenter|Qt.AlignVCenter))
 
170
            return to_qvariant(int(Qt.AlignRight|Qt.AlignVCenter))
 
171
        if role != Qt.DisplayRole:
 
172
            return to_qvariant()
 
173
        if orientation == Qt.Horizontal:
 
174
            if section == CONTEXT:
 
175
                return to_qvariant(_("Context"))
 
176
            elif section == NAME:
 
177
                return to_qvariant(_("Name"))
 
178
            elif section == MOD1:
 
179
                return to_qvariant(_("Mod1"))
 
180
            elif section == MOD2:
 
181
                return to_qvariant(_("Mod2"))
 
182
            elif section == MOD3:
 
183
                return to_qvariant(_("Mod3"))
 
184
            elif section == KEY:
 
185
                return to_qvariant(_("Key"))
 
186
        return to_qvariant()
 
187
 
 
188
    def rowCount(self, index=QModelIndex()):
 
189
        return len(self.shortcuts)
 
190
 
 
191
    def columnCount(self, index=QModelIndex()):
 
192
        return 6
 
193
    
 
194
    def setData(self, index, value, role=Qt.EditRole):
 
195
        if index.isValid() and 0 <= index.row() < len(self.shortcuts):
 
196
            shortcut = self.shortcuts[index.row()]
 
197
            key = shortcut.key
 
198
            column = index.column()
 
199
            text = from_qvariant(value, str)
 
200
            if column == MOD1:
 
201
                key.modifiers[0] = Key.modifier_from_name(text)
 
202
            elif column == MOD2:
 
203
                key.modifiers[1] = Key.modifier_from_name(text)
 
204
            elif column == MOD3:
 
205
                key.modifiers[2] = Key.modifier_from_name(text)
 
206
            elif column == KEY:
 
207
                key.key = Key.key_from_str(text)
 
208
            self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"),
 
209
                      index, index)
 
210
            return True
 
211
        return False
 
212
 
 
213
 
 
214
class ShortcutsDelegate(QItemDelegate):
 
215
    def __init__(self, parent=None):
 
216
        QItemDelegate.__init__(self, parent)
 
217
        self.modifiers = sorted(Key.MODIFIERNAMES.values())
 
218
        self.mod = None
 
219
        self.keys = sorted(Key.KEYS.values())
 
220
        self.key = None
 
221
        
 
222
    def sizeHint(self, option, index):
 
223
        fm = option.fontMetrics
 
224
        if index.column() in (MOD1, MOD2, MOD3):
 
225
            if self.mod is None:
 
226
                w = 0
 
227
                for mod in self.modifiers:
 
228
                    cw = fm.width(mod)
 
229
                    if cw > w:
 
230
                        w = cw
 
231
                        self.mod = mod
 
232
            else:
 
233
                w = fm.width(self.mod)
 
234
            return QSize(w+20, fm.height())
 
235
        elif index.column() == KEY:
 
236
            if self.key is None:
 
237
                w = 0
 
238
                for key in self.keys:
 
239
                    cw = fm.width(key)
 
240
                    if cw > w:
 
241
                        w = cw
 
242
                        self.key = key
 
243
            else:
 
244
                w = fm.width(self.key)
 
245
            return QSize(w+20, fm.height())
 
246
        return QItemDelegate.sizeHint(self, option, index)
 
247
 
 
248
    def createEditor(self, parent, option, index):
 
249
        if index.column() in (MOD1, MOD2, MOD3):
 
250
            combobox = QComboBox(parent)
 
251
            combobox.addItems(self.modifiers)
 
252
            return combobox
 
253
        elif index.column() == KEY:
 
254
            combobox = QComboBox(parent)
 
255
            combobox.addItems(self.keys)
 
256
            return combobox
 
257
        else:
 
258
            return QItemDelegate.createEditor(self, parent, option,
 
259
                                              index)
 
260
 
 
261
    def setEditorData(self, editor, index):
 
262
        text = from_qvariant(index.model().data(index, Qt.DisplayRole), str)
 
263
        if index.column() in (MOD1, MOD2, MOD3, KEY):
 
264
            i = editor.findText(text)
 
265
            if i == -1:
 
266
                i = 0
 
267
            editor.setCurrentIndex(i)
 
268
        else:
 
269
            QItemDelegate.setEditorData(self, editor, index)
 
270
 
 
271
    def setModelData(self, editor, model, index):
 
272
        if index.column() in (MOD1, MOD2, MOD3, KEY):
 
273
            model.setData(index, to_qvariant(editor.currentText()))
 
274
        else:
 
275
            QItemDelegate.setModelData(self, editor, model, index)
 
276
 
 
277
 
 
278
class ShortcutsTable(QTableView):
 
279
    def __init__(self, parent=None):
 
280
        QTableView.__init__(self, parent)
 
281
        self.model = ShortcutsModel()
 
282
        self.setModel(self.model)
 
283
        self.setItemDelegate(ShortcutsDelegate(self))
 
284
        self.load_shortcuts()
 
285
                     
 
286
    def adjust_cells(self):
 
287
        self.resizeColumnsToContents()
 
288
#        self.resizeRowsToContents()
 
289
        self.horizontalHeader().setStretchLastSection(True)
 
290
        
 
291
    def load_shortcuts(self):
 
292
        shortcuts = []
 
293
        for context, name, keystr in iter_shortcuts():
 
294
            shortcut = Shortcut(context, name, keystr)
 
295
            shortcuts.append(shortcut)
 
296
        shortcuts = sorted(shortcuts, key=lambda x: x.context+x.name)
 
297
        self.model.shortcuts = shortcuts
 
298
        self.model.reset()
 
299
        self.adjust_cells()
 
300
 
 
301
    def check_shortcuts(self):
 
302
        """Check shortcuts for conflicts"""
 
303
        conflicts = []
 
304
        for index, sh1 in enumerate(self.model.shortcuts):
 
305
            if index == len(self.model.shortcuts)-1:
 
306
                break
 
307
            for sh2 in self.model.shortcuts[index+1:]:
 
308
                if sh2 is sh1:
 
309
                    continue
 
310
                if str(sh2.key) == str(sh1.key) \
 
311
                   and (sh1.context == sh2.context or sh1.context == '_'
 
312
                        or sh2.context == '_'):
 
313
                    conflicts.append((sh1, sh2))
 
314
        if conflicts:
 
315
            self.parent().emit(SIGNAL('show_this_page()'))
 
316
            cstr = "\n".join(['%s <---> %s' % (sh1, sh2)
 
317
                              for sh1, sh2 in conflicts])
 
318
            QMessageBox.warning(self, _( "Conflicts"),
 
319
                                _("The following conflicts have been "
 
320
                                  "detected:")+"\n"+cstr, QMessageBox.Ok)
 
321
        
 
322
    def save_shortcuts(self):
 
323
        self.check_shortcuts()
 
324
        for shortcut in self.model.shortcuts:
 
325
            shortcut.save()
 
326
        
 
327
 
 
328
class ShortcutsConfigPage(GeneralConfigPage):
 
329
    CONF_SECTION = "shortcuts"
 
330
    def get_name(self):
 
331
        return _("Keyboard shortcuts")
 
332
    
 
333
    def get_icon(self):
 
334
        return get_icon("genprefs.png")
 
335
    
 
336
    def setup_page(self):
 
337
        self.table = ShortcutsTable(self)
 
338
        self.connect(self.table.model,
 
339
                     SIGNAL("dataChanged(QModelIndex,QModelIndex)"),
 
340
                     lambda i1, i2, opt='': self.has_been_modified(opt))
 
341
        vlayout = QVBoxLayout()
 
342
        vlayout.addWidget(self.table)
 
343
        reset_btn = QPushButton(_("Reset to default values"))
 
344
        self.connect(reset_btn, SIGNAL('clicked()'), self.reset_to_default)
 
345
        vlayout.addWidget(reset_btn)
 
346
        self.setLayout(vlayout)
 
347
        
 
348
    def check_settings(self):
 
349
        self.table.check_shortcuts()
 
350
        
 
351
    def reset_to_default(self):
 
352
        reset_shortcuts()
 
353
        self.main.apply_shortcuts()
 
354
        self.table.load_shortcuts()
 
355
        self.load_from_conf()
 
356
        self.set_modified(False)
 
357
            
 
358
    def apply_settings(self, options):
 
359
        self.table.save_shortcuts()
 
360
        self.main.apply_shortcuts()
 
361
 
 
362
 
 
363
def test():
 
364
    from spyderlib.utils.qthelpers import qapplication
 
365
    app = qapplication()
 
366
    table = ShortcutsTable()
 
367
    table.show()
 
368
    app.exec_()
 
369
    print [str(s) for s in table.model.shortcuts]
 
370
    table.check_shortcuts()
 
371
 
 
372
if __name__ == '__main__':
373
373
    test()
 
 
b'\\ No newline at end of file'