Blame setup/keyboardshortcut.py

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