Move engineabout dialog into a new python file.
[platform/upstream/ibus.git] / setup / keyboardshortcut.py
1 # vim:set et sts=4 sw=4:
2 #
3 # ibus - The Input Bus
4 #
5 # Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
6 #
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.
11 #
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.
16 #
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
21
22 __all__ = (
23     "KeyboardShortcutSelection",
24     "KeyboardShortcutSelectionDialog",
25 );
26
27 import gobject
28 import gtk
29 from gtk import gdk
30 from gtk import keysyms
31
32 from gettext import dgettext
33 _  = lambda a : dgettext("ibus", a)
34 N_ = lambda a : a
35
36 MAX_HOTKEY = 6
37
38 class KeyboardShortcutSelection(gtk.VBox):
39     def __init__(self, shortcuts = None):
40         super(KeyboardShortcutSelection, self).__init__()
41         self.__init_ui()
42         self.set_shortcuts(shortcuts)
43
44     def __init_ui(self):
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)
49
50         # shortcuts view
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)
62
63         # key code
64         hbox = gtk.HBox()
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)
69
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)
77
78         # modifiers
79         hbox = gtk.HBox()
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)
84
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)
97
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)
108
109         # buttons
110         hbox = gtk.HBox()
111         # add button
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)
115         # apply 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)
119         # delete 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)
124
125     def set_shortcuts(self, shortcuts = None):
126         if shortcuts == None:
127             shortcuts = []
128         model = self.__shortcut_view.get_model()
129         model.clear()
130
131         added = []
132         for shortcut in shortcuts:
133             if shortcut not in added:
134                 model.insert(-1, (shortcut,))
135                 added.append(shortcut)
136
137     def get_shortcuts(self):
138         model = self.__shortcut_view.get_model()
139         return [i[0] for i in model]
140
141     def add_shortcut(self, shortcut):
142         model = self.__shortcut_view.get_model()
143         if len(model) > MAX_HOTKEY:
144             return
145         if shortcut in self.get_shortcuts():
146             return
147         model.insert(-1, (shortcut,))
148
149     def __get_shortcut_from_buttons(self):
150         modifiers = []
151         keycode = self.__keycode_entry.get_text()
152         if gdk.keyval_from_name(keycode) == 0:
153             return None
154
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)
162         return shortcut
163
164     def __set_shortcut_to_buttons(self, shortcut):
165         keys = shortcut.split("+")
166         mods = keys[:-1]
167         for name, button, mask in self.__modifier_buttons:
168             if name in mods:
169                 button.set_active(True)
170             else:
171                 button.set_active(False)
172         self.__keycode_entry.set_text(keys[-1])
173
174     def __get_selected_shortcut(self):
175         model = self.__shortcut_view.get_model()
176         path, column = self.__shortcut_view.get_cursor()
177         if path == None:
178             return None
179         return model[path[0]][0]
180
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
185
186     def __del_selected_shortcut(self):
187         model = self.__shortcut_view.get_model()
188         path, column = self.__shortcut_view.get_cursor()
189         del model[path[0]]
190
191     def __shortcut_view_cursor_changed_cb(self, treeview):
192         shortcut = self.__get_selected_shortcut()
193         self.__set_shortcut_to_buttons(shortcut)
194         if shortcut != None:
195             self.__delete_button.set_sensitive(True)
196         else:
197             self.__delete_button.set_sensitive(False)
198
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)
212
213     def __modifier_button_toggled_cb(self, button, name):
214         self.__update_add_and_apply_buttons()
215
216     def __keycode_entry_notify_cb(self, entry, arg):
217         self.__update_add_and_apply_buttons()
218
219     def __keycode_button_clicked_cb(self, button):
220         out = []
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)"))
225
226         def __key_release_event(d, k, out):
227             out.append(k.copy())
228             d.response(gtk.RESPONSE_OK)
229
230         dlg.connect("key-release-event", __key_release_event, out)
231         id = dlg.run()
232         dlg.destroy()
233         if id != gtk.RESPONSE_OK or not out:
234             return
235         keyevent = out[0]
236         state = keyevent.state & (gdk.CONTROL_MASK | \
237                                   gdk.SHIFT_MASK   | \
238                                   gdk.MOD1_MASK    | \
239                                   gdk.META_MASK    | \
240                                   gdk.SUPER_MASK   | \
241                                   gdk.HYPER_MASK)
242         if state == 0:
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
256
257         for name, button, mask in self.__modifier_buttons:
258             if state & mask:
259                 button.set_active(True)
260             else:
261                 button.set_active(False)
262         self.__keycode_entry.set_text(gdk.keyval_name(keyevent.keyval))
263
264     def __add_button_clicked_cb(self, button):
265         shortcut = self.__get_shortcut_from_buttons()
266         self.add_shortcut(shortcut)
267
268     def __apply_button_clicked_cb(self, button):
269         shortcut = self.__get_shortcut_from_buttons()
270         self.__set_selected_shortcut(shortcut)
271
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)
276
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)
282         self.vbox.show_all()
283
284     def set_shortcuts(self, shotrcuts = None):
285         self.__selection_view.set_shortcuts(shotrcuts)
286
287     def add_shortcut(self, shotrcut):
288         self.__selection_view.add_shortcut(shotrcut)
289
290     def get_shortcuts(self):
291         return self.__selection_view.get_shortcuts()
292
293
294
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)
301     print dlg.run()
302     print dlg.get_shortcuts()
303