1 # vim:set et sts=4 sw=4:
5 # Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2 of the License, or (at your option) any later version.
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Lesser General Public License for more details.
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this program; if not, write to the
19 # Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20 # Boston, MA 02111-1307 USA
23 "KeyboardShortcutSelection",
24 "KeyboardShortcutSelectionDialog",
30 from gtk import keysyms
32 from gettext import dgettext
33 _ = lambda a : dgettext("ibus", a)
38 class KeyboardShortcutSelection(gtk.VBox):
39 def __init__(self, shortcuts = None):
40 super(KeyboardShortcutSelection, self).__init__()
42 self.set_shortcuts(shortcuts)
45 # label = gtk.Label(_("Keyboard shortcuts:"))
46 # label.set_justify(gtk.JUSTIFY_LEFT)
47 # label.set_alignment(0.0, 0.5)
48 # self.pack_start(label, False, True, 4)
51 self.__shortcut_view = gtk.TreeView(gtk.ListStore(gobject.TYPE_STRING))
52 self.__shortcut_view.set_size_request(-1, 100)
53 renderer = gtk.CellRendererText()
54 column = gtk.TreeViewColumn(_("Keyboard shortcuts"), renderer, text = 0)
55 self.__shortcut_view.append_column(column)
56 self.__shortcut_view.connect("cursor-changed", self.__shortcut_view_cursor_changed_cb)
57 scrolledwindow = gtk.ScrolledWindow()
58 scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
59 scrolledwindow.add(self.__shortcut_view)
60 scrolledwindow.set_shadow_type(gtk.SHADOW_IN)
61 self.pack_start(scrolledwindow, True, True, 4)
65 label = gtk.Label(_("Key code:"))
66 label.set_justify(gtk.JUSTIFY_LEFT)
67 label.set_alignment(0.0, 0.5)
68 hbox.pack_start(label, False, True, 4)
70 self.__keycode_entry = gtk.Entry()
71 self.__keycode_entry.connect("notify::text", self.__keycode_entry_notify_cb)
72 hbox.pack_start(self.__keycode_entry, True, True, 4)
73 self.__keycode_button = gtk.Button("...")
74 self.__keycode_button.connect("clicked", self.__keycode_button_clicked_cb)
75 hbox.pack_start(self.__keycode_button, False, True, 4)
76 self.pack_start(hbox, False, True, 4)
80 label = gtk.Label(_("Modifiers:"))
81 label.set_justify(gtk.JUSTIFY_LEFT)
82 label.set_alignment(0.0, 0.5)
83 hbox.pack_start(label, False, True, 4)
85 table = gtk.Table(4, 2)
86 self.__modifier_buttons = []
87 self.__modifier_buttons.append(("Control", gtk.CheckButton("_Control"), gdk.CONTROL_MASK))
88 self.__modifier_buttons.append(("Alt", gtk.CheckButton("A_lt"), gdk.MOD1_MASK))
89 self.__modifier_buttons.append(("Shift", gtk.CheckButton("_Shift"), gdk.SHIFT_MASK))
90 self.__modifier_buttons.append(("Meta", gtk.CheckButton("_Meta"), gdk.META_MASK))
91 self.__modifier_buttons.append(("Super", gtk.CheckButton("S_uper"), gdk.SUPER_MASK))
92 self.__modifier_buttons.append(("Hyper", gtk.CheckButton("_Hyper"), gdk.HYPER_MASK))
93 self.__modifier_buttons.append(("Capslock", gtk.CheckButton("Capsloc_k"), gdk.LOCK_MASK))
94 self.__modifier_buttons.append(("Release", gtk.CheckButton("_Release"), gdk.RELEASE_MASK))
95 for name, button, mask in self.__modifier_buttons:
96 button.connect("toggled", self.__modifier_button_toggled_cb, name)
98 table.attach(self.__modifier_buttons[0][1], 0, 1, 0, 1)
99 table.attach(self.__modifier_buttons[1][1], 1, 2, 0, 1)
100 table.attach(self.__modifier_buttons[2][1], 2, 3, 0, 1)
101 table.attach(self.__modifier_buttons[3][1], 3, 4, 0, 1)
102 table.attach(self.__modifier_buttons[4][1], 0, 1, 1, 2)
103 table.attach(self.__modifier_buttons[5][1], 1, 2, 1, 2)
104 table.attach(self.__modifier_buttons[6][1], 2, 3, 1, 2)
105 table.attach(self.__modifier_buttons[7][1], 3, 4, 1, 2)
106 hbox.pack_start(table, True, True, 4)
107 self.pack_start(hbox, False, True, 4)
112 self.__add_button = gtk.Button(stock = gtk.STOCK_ADD)
113 self.__add_button.connect("clicked", self.__add_button_clicked_cb)
114 hbox.pack_start(self.__add_button)
116 self.__apply_button = gtk.Button(stock = gtk.STOCK_APPLY)
117 self.__apply_button.connect("clicked", self.__apply_button_clicked_cb)
118 hbox.pack_start(self.__apply_button)
120 self.__delete_button = gtk.Button(stock = gtk.STOCK_DELETE)
121 self.__delete_button.connect("clicked", self.__delete_button_clicked_cb)
122 hbox.pack_start(self.__delete_button)
123 self.pack_start(hbox, False, True, 4)
125 def set_shortcuts(self, shortcuts = None):
126 if shortcuts == None:
128 model = self.__shortcut_view.get_model()
132 for shortcut in shortcuts:
133 if shortcut not in added:
134 model.insert(-1, (shortcut,))
135 added.append(shortcut)
137 def get_shortcuts(self):
138 model = self.__shortcut_view.get_model()
139 return [i[0] for i in model]
141 def add_shortcut(self, shortcut):
142 model = self.__shortcut_view.get_model()
143 if len(model) > MAX_HOTKEY:
145 if shortcut in self.get_shortcuts():
147 model.insert(-1, (shortcut,))
149 def __get_shortcut_from_buttons(self):
151 keycode = self.__keycode_entry.get_text()
152 if gdk.keyval_from_name(keycode) == 0:
155 for name, button, mask in self.__modifier_buttons:
156 if button.get_active():
157 modifiers.append(name)
158 if keycode.startswith("_"):
159 keycode = keycode[1:]
160 keys = modifiers + [keycode]
161 shortcut = "+".join(keys)
164 def __set_shortcut_to_buttons(self, shortcut):
165 keys = shortcut.split("+")
167 for name, button, mask in self.__modifier_buttons:
169 button.set_active(True)
171 button.set_active(False)
172 self.__keycode_entry.set_text(keys[-1])
174 def __get_selected_shortcut(self):
175 model = self.__shortcut_view.get_model()
176 path, column = self.__shortcut_view.get_cursor()
179 return model[path[0]][0]
181 def __set_selected_shortcut(self, shortcut):
182 model = self.__shortcut_view.get_model()
183 path, column = self.__shortcut_view.get_cursor()
184 model[path[0]][0] = shortcut
186 def __del_selected_shortcut(self):
187 model = self.__shortcut_view.get_model()
188 path, column = self.__shortcut_view.get_cursor()
191 def __shortcut_view_cursor_changed_cb(self, treeview):
192 shortcut = self.__get_selected_shortcut()
193 self.__set_shortcut_to_buttons(shortcut)
195 self.__delete_button.set_sensitive(True)
197 self.__delete_button.set_sensitive(False)
199 def __update_add_and_apply_buttons(self):
200 shortcut = self.__get_shortcut_from_buttons()
201 selected_shortcut = self.__get_selected_shortcut()
202 shortcuts = self.get_shortcuts()
203 can_add = shortcut != None and \
204 shortcut not in shortcuts \
205 and len(shortcuts) < MAX_HOTKEY
206 self.__add_button.set_sensitive(can_add)
207 can_apply = shortcut != selected_shortcut and \
208 shortcut != None and \
209 selected_shortcut != None and \
210 shortcut not in shortcuts
211 self.__apply_button.set_sensitive(can_apply)
213 def __modifier_button_toggled_cb(self, button, name):
214 self.__update_add_and_apply_buttons()
216 def __keycode_entry_notify_cb(self, entry, arg):
217 self.__update_add_and_apply_buttons()
219 def __keycode_button_clicked_cb(self, button):
221 dlg = gtk.MessageDialog(parent = self.get_toplevel(), buttons = gtk.BUTTONS_CLOSE)
222 message = _("Please press a key (or a key combination).\nThe dialog will be closed when the key is released.")
223 dlg.set_markup(message)
224 dlg.set_title(_("Please press a key (or a key combination)"))
226 def __key_release_event(d, k, out):
228 d.response(gtk.RESPONSE_OK)
230 dlg.connect("key-release-event", __key_release_event, out)
233 if id != gtk.RESPONSE_OK or not out:
236 state = keyevent.state & (gdk.CONTROL_MASK | \
243 state = state | gdk.RELEASE_MASK
244 elif keyevent.keyval in (keysyms.Control_L, keysyms.Control_R) and state == gdk.CONTROL_MASK:
245 state = state | gdk.RELEASE_MASK
246 elif keyevent.keyval in (keysyms.Shift_L, keysyms.Shift_R) and state == gdk.SHIFT_MASK:
247 state = state | gdk.RELEASE_MASK
248 elif keyevent.keyval in (keysyms.Alt_L, keysyms.Alt_R) and state == gdk.MOD1_MASK:
249 state = state | gdk.RELEASE_MASK
250 elif keyevent.keyval in (keysyms.Meta_L, keysyms.Meta_R) and state == gdk.META_MASK:
251 state = state | gdk.RELEASE_MASK
252 elif keyevent.keyval in (keysyms.Super_L, keysyms.Super_R) and state == gdk.SUPER_MASK:
253 state = state | gdk.RELEASE_MASK
254 elif keyevent.keyval in (keysyms.Hyper_L, keysyms.Hyper_R) and state == gdk.HYPER_MASK:
255 state = state | gdk.RELEASE_MASK
257 for name, button, mask in self.__modifier_buttons:
259 button.set_active(True)
261 button.set_active(False)
262 self.__keycode_entry.set_text(gdk.keyval_name(keyevent.keyval))
264 def __add_button_clicked_cb(self, button):
265 shortcut = self.__get_shortcut_from_buttons()
266 self.add_shortcut(shortcut)
268 def __apply_button_clicked_cb(self, button):
269 shortcut = self.__get_shortcut_from_buttons()
270 self.__set_selected_shortcut(shortcut)
272 def __delete_button_clicked_cb(self, button):
273 self.__del_selected_shortcut()
274 self.__delete_button.set_sensitive(False)
275 self.__apply_button.set_sensitive(False)
277 class KeyboardShortcutSelectionDialog(gtk.Dialog):
278 def __init__(self, title = None, parent = None, flags = 0, buttons = None):
279 super(KeyboardShortcutSelectionDialog, self).__init__(title, parent, flags, buttons)
280 self.__selection_view = KeyboardShortcutSelection()
281 self.vbox.pack_start(self.__selection_view)
284 def set_shortcuts(self, shotrcuts = None):
285 self.__selection_view.set_shortcuts(shotrcuts)
287 def add_shortcut(self, shotrcut):
288 self.__selection_view.add_shortcut(shotrcut)
290 def get_shortcuts(self):
291 return self.__selection_view.get_shortcuts()
295 if __name__ == "__main__":
296 dlg = KeyboardShortcutSelectionDialog(
297 title = "Select test",
298 buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK))
299 dlg.add_shortcut("Control+Shift+space")
300 dlg.set_shortcuts(None)
302 print dlg.get_shortcuts()