Blame setup/keyboardshortcut.py

Packit 3ff832
# vim:set et sts=4 sw=4:
Packit 3ff832
#
Packit 3ff832
# ibus - The Input Bus
Packit 3ff832
#
Packit 3ff832
# Copyright (c) 2007-2015 Peng Huang <shawn.p.huang@gmail.com>
Packit 3ff832
# Copyright (c) 2007-2015 Red Hat, Inc.
Packit 3ff832
#
Packit 3ff832
# This library is free software; you can redistribute it and/or
Packit 3ff832
# modify it under the terms of the GNU Lesser General Public
Packit 3ff832
# License as published by the Free Software Foundation; either
Packit 3ff832
# version 2.1 of the License, or (at your option) any later version.
Packit 3ff832
#
Packit 3ff832
# This library is distributed in the hope that it will be useful,
Packit 3ff832
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 3ff832
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 3ff832
# Lesser General Public License for more details.
Packit 3ff832
#
Packit 3ff832
# You should have received a copy of the GNU Lesser General Public
Packit 3ff832
# License along with this library; if not, write to the Free Software
Packit 3ff832
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
Packit 3ff832
# USA
Packit 3ff832
Packit 3ff832
__all__ = (
Packit 3ff832
    "KeyboardShortcutSelection",
Packit 3ff832
    "KeyboardShortcutSelectionDialog",
Packit 3ff832
);
Packit 3ff832
Packit 3ff832
from gi.repository import Gdk
Packit 3ff832
from gi.repository import GObject
Packit 3ff832
from gi.repository import Gtk
Packit 3ff832
from gi.repository import IBus
Packit 3ff832
from gi.repository import Pango
Packit 3ff832
Packit 3ff832
from i18n import _, N_
Packit 3ff832
Packit 3ff832
MAX_HOTKEY = 6
Packit 3ff832
Packit 3ff832
class KeyboardShortcutSelection(Gtk.Box):
Packit 3ff832
    def __init__(self, shortcuts = None):
Packit 3ff832
        super(KeyboardShortcutSelection, self).__init__(
Packit 3ff832
                orientation=Gtk.Orientation.VERTICAL)
Packit 3ff832
        self.__init_ui()
Packit 3ff832
        self.set_shortcuts(shortcuts)
Packit 3ff832
Packit 3ff832
    def __init_ui(self):
Packit 3ff832
        # label = Gtk.Label(_("Keyboard shortcuts:"))
Packit 3ff832
        # label.set_justify(Gtk.Justification.LEFT)
Packit 3ff832
        # label.set_alignment(0.0, 0.5)
Packit 3ff832
        # self.pack_start(label, False, True, 4)
Packit 3ff832
Packit 3ff832
        # shortcuts view
Packit 3ff832
        self.__shortcut_view = Gtk.TreeView(
Packit 3ff832
                model = Gtk.ListStore(GObject.TYPE_STRING))
Packit 3ff832
        renderer = Gtk.CellRendererText()
Packit 3ff832
        column = Gtk.TreeViewColumn(_("Keyboard shortcuts"), renderer, text = 0)
Packit 3ff832
        self.__shortcut_view.append_column(column)
Packit 3ff832
        self.__shortcut_view.connect("cursor-changed", self.__shortcut_view_cursor_changed_cb)
Packit 3ff832
        scrolledwindow = Gtk.ScrolledWindow()
Packit 3ff832
        scrolledwindow.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
Packit 3ff832
        scrolledwindow.set_min_content_height(100)
Packit 3ff832
        scrolledwindow.add(self.__shortcut_view)
Packit 3ff832
        scrolledwindow.set_shadow_type(Gtk.ShadowType.IN)
Packit 3ff832
        self.pack_start(scrolledwindow, True, True, 4)
Packit 3ff832
Packit 3ff832
        # key code
Packit 3ff832
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
Packit 3ff832
        label = Gtk.Label(label = _("Key code:"))
Packit 3ff832
        label.set_justify(Gtk.Justification.LEFT)
Packit 3ff832
        label.set_alignment(0.0, 0.5)
Packit 3ff832
        hbox.pack_start(label, False, True, 4)
Packit 3ff832
Packit 3ff832
        self.__keycode_entry = Gtk.Entry()
Packit 3ff832
        self.__keycode_entry.connect("notify::text", self.__keycode_entry_notify_cb)
Packit 3ff832
        hbox.pack_start(self.__keycode_entry, True, True, 4)
Packit 3ff832
        self.__keycode_button = Gtk.Button(label = "...")
Packit 3ff832
        self.__keycode_button.connect("clicked", self.__keycode_button_clicked_cb)
Packit 3ff832
        hbox.pack_start(self.__keycode_button, False, True, 4)
Packit 3ff832
        self.pack_start(hbox, False, True, 4)
Packit 3ff832
Packit 3ff832
        # modifiers
Packit 3ff832
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
Packit 3ff832
        label = Gtk.Label(label = _("Modifiers:"))
Packit 3ff832
        label.set_justify(Gtk.Justification.LEFT)
Packit 3ff832
        label.set_alignment(0.0, 0.5)
Packit 3ff832
        hbox.pack_start(label, False, True, 4)
Packit 3ff832
Packit 3ff832
        table = Gtk.Table(n_rows = 4, n_columns = 2)
Packit 3ff832
        self.__modifier_buttons = []
Packit 3ff832
        self.__modifier_buttons.append(("Control",
Packit 3ff832
                                        Gtk.CheckButton.new_with_mnemonic("_Control"),
Packit 3ff832
                                        Gdk.ModifierType.CONTROL_MASK))
Packit 3ff832
        self.__modifier_buttons.append(("Alt",
Packit 3ff832
                                        Gtk.CheckButton.new_with_mnemonic("A_lt"),
Packit 3ff832
                                        Gdk.ModifierType.MOD1_MASK))
Packit 3ff832
        self.__modifier_buttons.append(("Shift",
Packit 3ff832
                                        Gtk.CheckButton.new_with_mnemonic("_Shift"),
Packit 3ff832
                                        Gdk.ModifierType.SHIFT_MASK))
Packit 3ff832
        self.__modifier_buttons.append(("Meta",
Packit 3ff832
                                        Gtk.CheckButton.new_with_mnemonic("_Meta"),
Packit 3ff832
                                        Gdk.ModifierType.META_MASK))
Packit 3ff832
        self.__modifier_buttons.append(("Super",
Packit 3ff832
                                        Gtk.CheckButton.new_with_mnemonic("S_uper"),
Packit 3ff832
                                        Gdk.ModifierType.SUPER_MASK))
Packit 3ff832
        self.__modifier_buttons.append(("Hyper",
Packit 3ff832
                                        Gtk.CheckButton.new_with_mnemonic("_Hyper"),
Packit 3ff832
                                        Gdk.ModifierType.HYPER_MASK))
Packit 3ff832
        # <CapsLock> is not parsed by gtk_accelerator_parse()
Packit 3ff832
        # <Release> is not supported by XIGrabKeycode()
Packit 3ff832
        for name, button, mask in self.__modifier_buttons:
Packit 3ff832
            button.connect("toggled", self.__modifier_button_toggled_cb, name)
Packit 3ff832
Packit 3ff832
        table.attach(self.__modifier_buttons[0][1], 0, 1, 0, 1)
Packit 3ff832
        table.attach(self.__modifier_buttons[1][1], 1, 2, 0, 1)
Packit 3ff832
        table.attach(self.__modifier_buttons[2][1], 2, 3, 0, 1)
Packit 3ff832
        table.attach(self.__modifier_buttons[3][1], 0, 1, 1, 2)
Packit 3ff832
        table.attach(self.__modifier_buttons[4][1], 1, 2, 1, 2)
Packit 3ff832
        table.attach(self.__modifier_buttons[5][1], 2, 3, 1, 2)
Packit 3ff832
        hbox.pack_start(table, True, True, 4)
Packit 3ff832
        self.pack_start(hbox, False, True, 4)
Packit 3ff832
Packit 3ff832
        # buttons
Packit 3ff832
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
Packit 3ff832
        # add button
Packit 3ff832
        self.__add_button = Gtk.Button(label = _("_Add"),
Packit 3ff832
                                       use_underline = True)
Packit 3ff832
        self.__add_button.set_sensitive(False)
Packit 3ff832
        self.__add_button.connect("clicked", self.__add_button_clicked_cb)
Packit 3ff832
        hbox.pack_start(self.__add_button, False, True, 0)
Packit 3ff832
        # apply button
Packit 3ff832
        self.__apply_button = Gtk.Button(label = _("_Apply"),
Packit 3ff832
                                         use_underline = True)
Packit 3ff832
        self.__apply_button.set_sensitive(False)
Packit 3ff832
        self.__apply_button.connect("clicked", self.__apply_button_clicked_cb)
Packit 3ff832
        hbox.pack_start(self.__apply_button, False, True, 0)
Packit 3ff832
        # delete button
Packit 3ff832
        self.__delete_button = Gtk.Button(label = _("_Delete"),
Packit 3ff832
                                          use_underline = True)
Packit 3ff832
        self.__delete_button.set_sensitive(False)
Packit 3ff832
        self.__delete_button.connect("clicked", self.__delete_button_clicked_cb)
Packit 3ff832
        hbox.pack_start(self.__delete_button, False, True, 0)
Packit 3ff832
        self.pack_start(hbox, False, True, 4)
Packit 3ff832
Packit 3ff832
    def set_shortcuts(self, shortcuts = None):
Packit 3ff832
        if shortcuts == None:
Packit 3ff832
            shortcuts = []
Packit 3ff832
        model = self.__shortcut_view.get_model()
Packit 3ff832
        model.clear()
Packit 3ff832
Packit 3ff832
        added = []
Packit 3ff832
        for shortcut in shortcuts:
Packit 3ff832
            if shortcut not in added:
Packit 3ff832
                it = model.insert(0)
Packit 3ff832
                model[it][0] = shortcut
Packit 3ff832
                added.append(shortcut)
Packit 3ff832
Packit 3ff832
    def get_shortcuts(self):
Packit 3ff832
        model = self.__shortcut_view.get_model()
Packit 3ff832
        try:
Packit 3ff832
            return [i[0] for i in model]
Packit 3ff832
        except:
Packit 3ff832
            return []
Packit 3ff832
Packit 3ff832
    def add_shortcut(self, shortcut):
Packit 3ff832
        model = self.__shortcut_view.get_model()
Packit 3ff832
        if len(model) > MAX_HOTKEY:
Packit 3ff832
            return
Packit 3ff832
        if shortcut in self.get_shortcuts():
Packit 3ff832
            return
Packit 3ff832
        it = model.insert(0)
Packit 3ff832
        model[it][0] = shortcut
Packit 3ff832
        self.__add_button.set_sensitive(False)
Packit 3ff832
        path = model.get_path(it)
Packit 3ff832
        self.__shortcut_view.set_cursor(path, None, False)
Packit 3ff832
Packit 3ff832
    def __get_shortcut_from_buttons(self):
Packit 3ff832
        modifiers = []
Packit 3ff832
        keycode = self.__keycode_entry.get_text()
Packit 3ff832
        if Gdk.keyval_from_name(keycode) == 0:
Packit 3ff832
            return None
Packit 3ff832
Packit 3ff832
        for name, button, mask in self.__modifier_buttons:
Packit 3ff832
            if button.get_active():
Packit 3ff832
                modifiers.append(name)
Packit 3ff832
        if keycode.startswith("_"):
Packit 3ff832
            keycode = keycode[1:]
Packit 3ff832
        shortcut = "".join(['<' + m + '>' for m in modifiers])
Packit 3ff832
        shortcut += keycode
Packit 3ff832
        return shortcut
Packit 3ff832
Packit 3ff832
    def __set_shortcut_to_buttons(self, shortcut):
Packit 3ff832
        (keyval, state) = Gtk.accelerator_parse(shortcut)
Packit 3ff832
        if keyval == 0 and state == 0:
Packit 3ff832
            return
Packit 3ff832
        for name, button, mask in self.__modifier_buttons:
Packit 3ff832
            if state & mask:
Packit 3ff832
                button.set_active(True)
Packit 3ff832
            else:
Packit 3ff832
                button.set_active(False)
Packit 3ff832
        self.__keycode_entry.set_text(shortcut.rsplit('>', 1)[-1])
Packit 3ff832
Packit 3ff832
    def __get_selected_shortcut(self):
Packit 3ff832
        model = self.__shortcut_view.get_model()
Packit 3ff832
        path, column = self.__shortcut_view.get_cursor()
Packit 3ff832
        if path == None:
Packit 3ff832
            return None
Packit 3ff832
        return model[path.get_indices()[0]][0]
Packit 3ff832
Packit 3ff832
    def __set_selected_shortcut(self, shortcut):
Packit 3ff832
        model = self.__shortcut_view.get_model()
Packit 3ff832
        path, column = self.__shortcut_view.get_cursor()
Packit 3ff832
        model[path[0]][0] = shortcut
Packit 3ff832
        self.__update_add_and_apply_buttons()
Packit 3ff832
Packit 3ff832
    def __del_selected_shortcut(self):
Packit 3ff832
        model = self.__shortcut_view.get_model()
Packit 3ff832
        path, column = self.__shortcut_view.get_cursor()
Packit 3ff832
        model.remove(model.get_iter(path))
Packit 3ff832
        self.__update_add_and_apply_buttons()
Packit 3ff832
Packit 3ff832
    def __shortcut_view_cursor_changed_cb(self, treeview):
Packit 3ff832
        shortcut = self.__get_selected_shortcut()
Packit 3ff832
        if shortcut != None:
Packit 3ff832
            self.__set_shortcut_to_buttons(shortcut)
Packit 3ff832
            self.__delete_button.set_sensitive(True)
Packit 3ff832
        else:
Packit 3ff832
            self.__delete_button.set_sensitive(False)
Packit 3ff832
Packit 3ff832
    def __update_add_and_apply_buttons(self):
Packit 3ff832
        shortcut = self.__get_shortcut_from_buttons()
Packit 3ff832
        selected_shortcut = self.__get_selected_shortcut()
Packit 3ff832
        shortcuts = self.get_shortcuts()
Packit 3ff832
        can_add = shortcut != None and \
Packit 3ff832
                  shortcut not in shortcuts \
Packit 3ff832
                  and len(shortcuts) < MAX_HOTKEY
Packit 3ff832
        self.__add_button.set_sensitive(can_add)
Packit 3ff832
        can_apply = shortcut != selected_shortcut and \
Packit 3ff832
                    shortcut != None and \
Packit 3ff832
                    selected_shortcut != None and \
Packit 3ff832
                    shortcut not in shortcuts
Packit 3ff832
        self.__apply_button.set_sensitive(can_apply)
Packit 3ff832
Packit 3ff832
    def __modifier_button_toggled_cb(self, button, name):
Packit 3ff832
        self.__update_add_and_apply_buttons()
Packit 3ff832
Packit 3ff832
    def __keycode_entry_notify_cb(self, entry, arg):
Packit 3ff832
        self.__update_add_and_apply_buttons()
Packit 3ff832
Packit 3ff832
    def __keycode_button_clicked_cb(self, button):
Packit 3ff832
        out = []
Packit 3ff832
        dlg = Gtk.MessageDialog(transient_for = self.get_toplevel(),
Packit 3ff832
                                buttons = Gtk.ButtonsType.CLOSE)
Packit 3ff832
        message = _("Please press a key (or a key combination).\n" \
Packit 3ff832
                    "The dialog will be closed when the key is released.")
Packit 3ff832
        dlg.set_markup(message)
Packit 3ff832
        dlg.set_title(_("Please press a key (or a key combination)"))
Packit 3ff832
        sw = Gtk.ScrolledWindow()
Packit 3ff832
Packit 3ff832
        def __accel_edited_cb(c, path, keyval, state, keycode):
Packit 3ff832
            out.append(keyval)
Packit 3ff832
            out.append(state)
Packit 3ff832
            out.append(keycode)
Packit 3ff832
            dlg.response(Gtk.ResponseType.OK)
Packit 3ff832
Packit 3ff832
        model = Gtk.ListStore(GObject.TYPE_INT,
Packit 3ff832
                              GObject.TYPE_UINT,
Packit 3ff832
                              GObject.TYPE_UINT)
Packit 3ff832
        accel_view = Gtk.TreeView(model = model)
Packit 3ff832
        accel_view.set_headers_visible(False)
Packit 3ff832
        sw.add(accel_view)
Packit 3ff832
        sw.set_min_content_height(30)
Packit 3ff832
        column = Gtk.TreeViewColumn()
Packit 3ff832
        renderer = Gtk.CellRendererAccel(accel_mode=Gtk.CellRendererAccelMode.OTHER,
Packit 3ff832
                                         editable=True)
Packit 3ff832
        renderer.connect('accel-edited', __accel_edited_cb)
Packit 3ff832
        column.pack_start(renderer, True)
Packit 3ff832
        column.add_attribute(renderer, 'accel-mods', 0)
Packit 3ff832
        column.add_attribute(renderer, 'accel-key', 1)
Packit 3ff832
        column.add_attribute(renderer, 'keycode', 2)
Packit 3ff832
        accel_view.append_column(column)
Packit 3ff832
        it = model.append(None)
Packit 3ff832
        area = dlg.get_message_area()
Packit 3ff832
        area.pack_end(sw, True, True, 0)
Packit 3ff832
        sw.show_all()
Packit 3ff832
        id = dlg.run()
Packit 3ff832
        dlg.destroy()
Packit 3ff832
        if id != Gtk.ResponseType.OK or len(out) < 3:
Packit 3ff832
            return
Packit 3ff832
        keyval = out[0]
Packit 3ff832
        state = out[1]
Packit 3ff832
        keycode = out[2]
Packit 3ff832
Packit 3ff832
        for name, button, mask in self.__modifier_buttons:
Packit 3ff832
            if state & mask:
Packit 3ff832
                button.set_active(True)
Packit 3ff832
            else:
Packit 3ff832
                button.set_active(False)
Packit 3ff832
Packit 3ff832
        shortcut = Gtk.accelerator_name_with_keycode(None,
Packit 3ff832
                                                     keyval,
Packit 3ff832
                                                     keycode,
Packit 3ff832
                                                     state)
Packit 3ff832
        shortcut = shortcut.replace('<Primary>', '<Control>')
Packit 3ff832
        self.__keycode_entry.set_text(shortcut.rsplit('>', 1)[-1])
Packit 3ff832
Packit 3ff832
    def __add_button_clicked_cb(self, button):
Packit 3ff832
        shortcut = self.__get_shortcut_from_buttons()
Packit 3ff832
        self.add_shortcut(shortcut)
Packit 3ff832
Packit 3ff832
    def __apply_button_clicked_cb(self, button):
Packit 3ff832
        shortcut = self.__get_shortcut_from_buttons()
Packit 3ff832
        self.__set_selected_shortcut(shortcut)
Packit 3ff832
Packit 3ff832
    def __delete_button_clicked_cb(self, button):
Packit 3ff832
        self.__del_selected_shortcut()
Packit 3ff832
        self.__delete_button.set_sensitive(False)
Packit 3ff832
        self.__apply_button.set_sensitive(False)
Packit 3ff832
Packit 3ff832
class KeyboardShortcutSelectionDialog(Gtk.Dialog):
Packit 3ff832
    def __init__(self, title = None, transient_for = None, flags = 0):
Packit 3ff832
        super(KeyboardShortcutSelectionDialog, self).__init__(
Packit 3ff832
                title = title, transient_for = transient_for, flags = flags)
Packit 3ff832
        self.__selection_view = KeyboardShortcutSelection()
Packit 3ff832
        self.vbox.pack_start(self.__selection_view, False, True, 0)
Packit 3ff832
        self.vbox.show_all()
Packit 3ff832
Packit 3ff832
    def set_shortcuts(self, shotrcuts = None):
Packit 3ff832
        self.__selection_view.set_shortcuts(shotrcuts)
Packit 3ff832
Packit 3ff832
    def add_shortcut(self, shotrcut):
Packit 3ff832
        self.__selection_view.add_shortcut(shotrcut)
Packit 3ff832
Packit 3ff832
    def get_shortcuts(self):
Packit 3ff832
        return self.__selection_view.get_shortcuts()
Packit 3ff832
Packit 3ff832
Packit 3ff832
Packit 3ff832
if __name__ == "__main__":
Packit 3ff832
    dlg = KeyboardShortcutSelectionDialog(title = "Select test")
Packit 3ff832
    buttons = (_("_Cancel"), Gtk.ResponseType.CANCEL,
Packit 3ff832
               _("_OK"), Gtk.ResponseType.OK)
Packit 3ff832
    dlg.add_buttons(buttons)
Packit 3ff832
    dlg.add_shortcut("Control+Shift+space")
Packit 3ff832
    dlg.set_shortcuts(None)
Packit 3ff832
    print((dlg.run()))
Packit 3ff832
    print((dlg.get_shortcuts()))
Packit 3ff832