def translate(hardware_keycode, state, group): # We may need to retry several times to deal with garbled text. keymap = Gdk.Keymap.get_default() # distinct it = list(set([group, 0, 1, 2])) ok_to_return = False keyval = None keyval_lower = None for g in it: res = keymap.translate_keyboard_state(hardware_keycode, Gdk.ModifierType(0), g) if not res: # PyGTK returns None when gdk_keymap_translate_keyboard_state() # returns false. Not sure if this is a bug or a feature - the only # time I have seen this happen is when I put my laptop into sleep # mode. continue keyval = res[1] # consumed_modifiers = res[4] lbl = Gtk.accelerator_get_label(keyval, state) if is_ascii(lbl): ok_to_return = True break if not ok_to_return: logger.warning( 'translate_keyboard_state() returned no valid response. ' 'Strange key pressed?') return None, None, None, None # We want to ignore irrelevant modifiers like ScrollLock. The stored # key binding does not include modifiers that affected its keyval. mods = Gdk.ModifierType(state & Gtk.accelerator_get_default_mod_mask()) keyval_lower = Gdk.keyval_to_lower(keyval) # If lowercasing affects the keysym, then we need to include # SHIFT in the modifiers. We re-upper case when we match against # the keyval, but display and save in caseless form. if keyval != keyval_lower: mods |= Gdk.ModifierType.SHIFT_MASK # So we get (<Shift>j, Shift+J) but just (plus, +). As I # understand it. accel_label = Gtk.accelerator_get_label(keyval_lower, mods) return keyval, keyval_lower, accel_label, mods
def button_press_displayname(button, mods, shorten=False): """Converts a button number & modifier mask to a localized unicode string. """ button = int(button) mods = int(mods) if button <= 0: return None mods = Gdk.ModifierType(mods) modif_label = Gtk.accelerator_get_label(0, mods) modif_label = unicode(modif_label) separator = "" if modif_label: separator = u"+" # TRANSLATORS: "Button" refers to a mouse button # TRANSLATORS: It is part of a button map label. mouse_button_label = _("Button") if shorten: # TRANSLATORS: abbreviated "Button <number>" for forms like "Alt+Btn1" mouse_button_label = _("Btn") return "{modifiers}{plus}{btn}{button_number}".format( modifiers=modif_label, plus=separator, btn=mouse_button_label, button_number=button, )
def _key_press_cb(self, widget, event): """App-wide keypress handler for toplevel windows.""" # If an input widget has focus - their key handling is prioritized. consumed = widget.propagate_key_event(event) if consumed: return True if not self.enabled: return # See gtk sourcecode in gtkmenu.c function gtk_menu_key_press, # which uses the same code as below when changing an accelerator. keymap = Gdk.Keymap.get_default() # Instead of using event.keyval, we do it the lowlevel way. # Reason: ignoring CAPSLOCK and checking if SHIFT was pressed state = Gdk.ModifierType(event.state & ~Gdk.ModifierType.LOCK_MASK) res = keymap.translate_keyboard_state(event.hardware_keycode, state, event.group) if not res: # PyGTK returns None when gdk_keymap_translate_keyboard_state() # returns false. Not sure if this is a bug or a feature - the only # time I have seen this happen is when I put my laptop into sleep # mode. logger.warning('translate_keyboard_state() returned None. ' 'Strange key pressed?') return keyval = res[1] consumed_modifiers = res[4] # We want to ignore irrelevant modifiers like ScrollLock. The stored # key binding does not include modifiers that affected its keyval. modifiers = (event.state & Gtk.accelerator_get_default_mod_mask() & ~consumed_modifiers) # Except that key bindings are always stored in lowercase. keyval_lower = Gdk.keyval_to_lower(keyval) if keyval_lower != keyval: modifiers |= Gdk.ModifierType.SHIFT_MASK action = self.keymap.get((keyval_lower, modifiers)) if not action and not self.fallbacks_disabled(): # try hardcoded keys action = self.keymap2.get((keyval_lower, modifiers)) # Don't dispatch if the window is only sensitive to a subset of # actions, and the action is not in that set. if action is not None and isinstance(action, Gtk.Action): win_actions = self.window_actions.get(widget, None) if win_actions is not None: if action.get_name() not in win_actions: return False # If the lookup succeeded, activate the corresponding action. if action: return self.activate_keydown_event(action, event) # Otherwise, dispatch the event to the active doc. return self._dispatch_fallthru_key_press_event(widget, event)
def local_mouse_state(self, last_update=False): tdw_win = self.tdw.get_window() display = self.tdw.get_display() devmgr = display and display.get_device_manager() or None coredev = devmgr and devmgr.get_client_pointer() or None if coredev and tdw_win: win_, x, y, kbmods = tdw_win.get_device_position_double(coredev) else: x, y, kbmods = (0., 0., Gdk.ModifierType(0)) if last_update: return self.lx, self.ly, kbmods x, y = self.tdw.display_to_model(x, y) return x, y, kbmods
def button_press_name(button, mods): """Converts button number & modifier mask to a prefs-storable string. Analogous to `Gtk.accelerator_name()`. Buttonpress names look similar to GDK accelerator names, for example ``<Control><Shift>Button2`` or ``<Primary><Alt>Button4`` for newer versions of GTK. If the button is equal to zero (see `button_press_parse()`), `None` is returned. """ button = int(button) mods = int(mods) if button <= 0: return None mods = Gdk.ModifierType(mods) modif_name = Gtk.accelerator_name(0, mods) return modif_name + "Button%d" % (button, )
def button_press_parse(name): """Converts button press names to a button number & modifier mask. Analogous to `Gtk.accelerator_parse()`. This function parses the strings created by `button_press_name()`, and returns a 2-tuple containing the button number and modifier mask corresponding to `name`. If the parse fails, both values will be 0 (zero). """ if name is None: return (0, 0) name = str(name) try: mods_s, button_s = name.split("Button", 1) if button_s == '': button = 0 else: button = int(button_s) except ValueError: button = 0 mods = Gdk.ModifierType(0) else: keyval_ignored, mods = Gtk.accelerator_parse(mods_s) return button, mods
def _edit_dialog_key_press_cb(self, dialog, event, editable): if event.type != Gdk.EventType.KEY_PRESS: return False if event.is_modifier: return False if self._USE_NORMAL_DIALOG_KEYS: if event.keyval == Gdk.KEY_Return: dialog.response(Gtk.ResponseType.OK) return True elif event.keyval == Gdk.KEY_Escape: dialog.response(Gtk.ResponseType.CANCEL) return True elif event.keyval == Gdk.KEY_BackSpace: dialog.response(Gtk.ResponseType.REJECT) return True # Stolen from GTK 2.24's gtk/gtkmenu.c (gtk_menu_key_press()) # Figure out what modifiers went into determining the key symbol keymap = Gdk.Keymap.get_default() bound, keyval, effective_group, level, consumed_modifiers = ( keymap.translate_keyboard_state( event.hardware_keycode, event.state, # https://github.com/mypaint/mypaint/issues/974 # event.group 1)) keyval = Gdk.keyval_to_lower(keyval) mods = Gdk.ModifierType(event.state & Gtk.accelerator_get_default_mod_mask() & ~consumed_modifiers) # If lowercasing affects the keysym, then we need to include # SHIFT in the modifiers. We re-upper case when we match against # the keyval, but display and save in caseless form. if keyval != event.keyval: mods |= Gdk.ModifierType.SHIFT_MASK accel_label = Gtk.accelerator_get_label(keyval, mods) # So we get (<Shift>j, Shift+J) but just (plus, +). As I # understand it. # This is rejecting some legit key combinations such as the # arrowkeys, so I had to remove it... # if not Gtk.accelerator_valid(keyval, mods) # return True clash_accel_path = None clash_action_label = None for path, kv, m, changed in self._get_accel_map_entries(): if (kv, m) == (keyval, mods): clash_accel_path = path clash_action_label = _udecode( self._action_labels.get( clash_accel_path, # TRANSLATORS: Part of the keybinding dialog, refers # TRANSLATORS: to an action bound to a key combination. _(u"Unknown Action"), )) break if clash_accel_path == dialog.accel_path: # no change self._edit_dialog_set_standard_hint(dialog) label = str(accel_label) dialog.accel_label_widget.set_text(label) elif clash_accel_path: markup_tmpl = _( # TRANSLATORS: Warning message when attempting to assign a # TRANSLATORS: keyboard combination that is already used. u"<b>{accel} is already in use for '{action}'. " u"The existing assignment will be replaced.</b>") markup = markup_tmpl.format( accel=lib.xml.escape(accel_label), action=lib.xml.escape(clash_action_label), ) self._edit_dialog_set_hint(dialog, markup) label = u"%s (replace)" % (accel_label, ) dialog.accel_label_widget.set_text(str(label)) else: self._edit_dialog_set_standard_hint(dialog) label = u"%s (changed)" % (accel_label, ) dialog.accel_label_widget.set_text(label) dialog.result_mods = mods dialog.result_keyval = keyval return True