def to_py(self, value): self._basic_py_validation(value, str) if not value: return None if utils.is_special_key(value): value = '<{}>'.format(utils.normalize_keystr(value[1:-1])) return value
def __init__(self, parent=None): super().__init__(parent) assert cmdutils.cmd_dict cmdlist = [] for obj in set(cmdutils.cmd_dict.values()): if (obj.hide or (obj.debug and not objreg.get('args').debug) or obj.deprecated): pass else: cmdlist.append((obj.name, obj.desc)) for name, cmd in config.section('aliases').items(): cmdlist.append((name, "Alias for '{}'".format(cmd))) cat = self.new_category("Commands") # map each command to its bound keys and show these in the misc column keyconf = objreg.get('key-config') cmd_to_keys = defaultdict(list) for key, cmd in keyconf.get_bindings_for('normal').items(): # put special bindings last if utils.is_special_key(key): cmd_to_keys[cmd].append(key) else: cmd_to_keys[cmd].insert(0, key) for (name, desc) in sorted(cmdlist): self.new_item(cat, name, desc, ', '.join(cmd_to_keys[name]))
def __init__(self, parent=None): super().__init__(parent) self.setStyleSheet(""" background-color: rgba(50, 50, 50, 80%); color: white; border-radius: 20px; padding: 30px; """) all_bindings = config.key_instance.get_reverse_bindings_for('normal') bindings = all_bindings.get('fullscreen --leave') if bindings: key = bindings[0] if utils.is_special_key(key): key = key.strip('<>').capitalize() self.setText("Press {} to exit fullscreen.".format(key)) else: self.setText("Page is now fullscreen.") self.resize(self.sizeHint()) if config.val.content.windowed_fullscreen: geom = self.parentWidget().geometry() else: geom = QApplication.desktop().screenGeometry(self) self.move((geom.width() - self.sizeHint().width()) / 2, 30)
def bind(self, key, command=None, *, mode='normal'): """Bind a key to a command. Args: key: The keychain or special key (inside `<...>`) to bind. command: The command to execute, with optional args, or None to print the current binding. mode: A comma-separated list of modes to bind the key in (default: `normal`). See `:help bindings.commands` for the available modes. """ if command is None: if utils.is_special_key(key): # self._keyconfig.get_command does this, but we also need it # normalized for the output below key = utils.normalize_keystr(key) cmd = self._keyconfig.get_command(key, mode) if cmd is None: message.info("{} is unbound in {} mode".format(key, mode)) else: message.info("{} is bound to '{}' in {} mode".format( key, cmd, mode)) return try: self._keyconfig.bind(key, command, mode=mode, save_yaml=True) except configexc.KeybindingError as e: raise cmdexc.CommandError("bind: {}".format(e))
def _prepare(self, key, mode): """Make sure the given mode exists and normalize the key.""" if mode not in configdata.DATA['bindings.default'].default: raise configexc.KeybindingError("Invalid mode {}!".format(mode)) if utils.is_special_key(key): # <Ctrl-t>, <ctrl-T>, and <ctrl-t> should be considered equivalent return utils.normalize_keystr(key) return key
def update_keyhint(self, modename, prefix): """Show hints for the given prefix (or hide if prefix is empty). Args: prefix: The current partial keystring. """ countstr, prefix = re.match(r'^(\d*)(.*)', prefix).groups() if not prefix: self._show_timer.stop() self.hide() return def blacklisted(keychain): return any(fnmatch.fnmatchcase(keychain, glob) for glob in config.val.keyhint.blacklist) def takes_count(cmdstr): """Return true iff this command can take a count argument.""" cmdname = cmdstr.split(' ')[0] cmd = cmdutils.cmd_dict.get(cmdname) return cmd and cmd.takes_count() bindings_dict = config.key_instance.get_bindings_for(modename) bindings = [(k, v) for (k, v) in sorted(bindings_dict.items()) if k.startswith(prefix) and not utils.is_special_key(k) and not blacklisted(k) and (takes_count(v) or not countstr)] if not bindings: self._show_timer.stop() return # delay so a quickly typed keychain doesn't display hints self._show_timer.setInterval(config.val.keyhint.delay) self._show_timer.start() suffix_color = html.escape(config.val.colors.keyhint.suffix.fg) text = '' for key, cmd in bindings: text += ( "<tr>" "<td>{}</td>" "<td style='color: {}'>{}</td>" "<td style='padding-left: 2ex'>{}</td>" "</tr>" ).format( html.escape(prefix), suffix_color, html.escape(key[len(prefix):]), html.escape(cmd) ) text = '<table>{}</table>'.format(text) self.setText(text) self.adjustSize() self.update_geometry.emit()
def update_keyhint(self, modename, prefix): """Show hints for the given prefix (or hide if prefix is empty). Args: prefix: The current partial keystring. """ countstr, prefix = re.fullmatch(r'(\d*)(.*)', prefix).groups() if not prefix: self._show_timer.stop() self.hide() return def blacklisted(keychain): return any(fnmatch.fnmatchcase(keychain, glob) for glob in config.val.keyhint.blacklist) def takes_count(cmdstr): """Return true iff this command can take a count argument.""" cmdname = cmdstr.split(' ')[0] cmd = cmdutils.cmd_dict.get(cmdname) return cmd and cmd.takes_count() bindings_dict = config.key_instance.get_bindings_for(modename) bindings = [(k, v) for (k, v) in sorted(bindings_dict.items()) if k.startswith(prefix) and not utils.is_special_key(k) and not blacklisted(k) and (takes_count(v) or not countstr)] if not bindings: self._show_timer.stop() return # delay so a quickly typed keychain doesn't display hints self._show_timer.setInterval(config.val.keyhint.delay) self._show_timer.start() suffix_color = html.escape(config.val.colors.keyhint.suffix.fg) text = '' for key, cmd in bindings: text += ( "<tr>" "<td>{}</td>" "<td style='color: {}'>{}</td>" "<td style='padding-left: 2ex'>{}</td>" "</tr>" ).format( html.escape(prefix), suffix_color, html.escape(key[len(prefix):]), html.escape(cmd) ) text = '<table>{}</table>'.format(text) self.setText(text) self.adjustSize() self.update_geometry.emit()
def _parse_key_command(self, modename, key, cmd): """Parse the keys and their command and store them in the object.""" if utils.is_special_key(key): self.special_bindings[key[1:-1]] = cmd elif self._supports_chains: self.bindings[key] = cmd elif self._warn_on_keychains: log.keyboard.warning("Ignoring keychain '{}' in mode '{}' because " "keychains are not supported there." .format(key, modename))
def get_reverse_bindings_for(self, section): """Get a dict of commands to a list of bindings for the section.""" cmd_to_keys = collections.defaultdict(list) for key, cmd in self.bindings[section].items(): # put special bindings last if utils.is_special_key(key): cmd_to_keys[cmd].append(key) else: cmd_to_keys[cmd].insert(0, key) return cmd_to_keys
def _parse_key_command(self, modename, key, cmd): """Parse the keys and their command and store them in the object.""" if utils.is_special_key(key): self.special_bindings[key[1:-1]] = cmd elif self._supports_chains: self.bindings[key] = cmd elif self._warn_on_keychains: log.keyboard.warning("Ignoring keychain '{}' in mode '{}' because " "keychains are not supported there.".format( key, modename))
def get_reverse_bindings_for(self, section): """Get a dict of commands to a list of bindings for the section.""" cmd_to_keys = {} for key, cmd in self.get_bindings_for(section).items(): cmd_to_keys.setdefault(cmd, []) # put special bindings last if utils.is_special_key(key): cmd_to_keys[cmd].append(key) else: cmd_to_keys[cmd].insert(0, key) return cmd_to_keys
def bind(self, win_id, key=None, command=None, *, mode='normal', default=False): """Bind a key to a command. If no command is given, show the current binding for the given key. Using :bind without any arguments opens a page showing all keybindings. Args: key: The keychain or special key (inside `<...>`) to bind. command: The command to execute, with optional args. mode: A comma-separated list of modes to bind the key in (default: `normal`). See `:help bindings.commands` for the available modes. default: If given, restore a default binding. """ if key is None: tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) tabbed_browser.openurl(QUrl('qute://bindings'), newtab=True) return if command is None: if default: # :bind --default: Restore default with self._handle_config_error(): self._keyconfig.bind_default(key, mode=mode, save_yaml=True) return # No --default -> print binding if utils.is_special_key(key): # self._keyconfig.get_command does this, but we also need it # normalized for the output below key = utils.normalize_keystr(key) with self._handle_config_error(): cmd = self._keyconfig.get_command(key, mode) if cmd is None: message.info("{} is unbound in {} mode".format(key, mode)) else: message.info("{} is bound to '{}' in {} mode".format( key, cmd, mode)) return with self._handle_config_error(): self._keyconfig.bind(key, command, mode=mode, save_yaml=True)
def update_keyhint(self, modename, prefix): """Show hints for the given prefix (or hide if prefix is empty). Args: prefix: The current partial keystring. """ if not prefix: self._show_timer.stop() self.hide() return blacklist = config.get('ui', 'keyhint-blacklist') or [] keyconf = objreg.get('key-config') def blacklisted(keychain): return any(fnmatch.fnmatchcase(keychain, glob) for glob in blacklist) bindings = [(k, v) for (k, v) in keyconf.get_bindings_for(modename).items() if k.startswith(prefix) and not utils.is_special_key(k) and not blacklisted(k)] if not bindings: self._show_timer.stop() return # delay so a quickly typed keychain doesn't display hints self._show_timer.setInterval(config.get('ui', 'keyhint-delay')) self._show_timer.start() suffix_color = html.escape(config.get('colors', 'keyhint.fg.suffix')) text = '' for key, cmd in bindings: text += ( "<tr>" "<td>{}</td>" "<td style='color: {}'>{}</td>" "<td style='padding-left: 2ex'>{}</td>" "</tr>" ).format( html.escape(prefix), suffix_color, html.escape(key[len(prefix):]), html.escape(cmd) ) text = '<table>{}</table>'.format(text) self.setText(text) self.adjustSize() self.update_geometry.emit()
def get_reverse_bindings_for(self, mode): """Get a dict of commands to a list of bindings for the mode.""" cmd_to_keys = {} bindings = self.get_bindings_for(mode) for key, full_cmd in sorted(bindings.items()): for cmd in full_cmd.split(';;'): cmd = cmd.strip() cmd_to_keys.setdefault(cmd, []) # put special bindings last if utils.is_special_key(key): cmd_to_keys[cmd].append(key) else: cmd_to_keys[cmd].insert(0, key) return cmd_to_keys
def update_keyhint(self, modename, prefix): """Show hints for the given prefix (or hide if prefix is empty). Args: prefix: The current partial keystring. """ if not prefix: self._show_timer.stop() self.hide() return blacklist = config.get('ui', 'keyhint-blacklist') or [] keyconf = objreg.get('key-config') def blacklisted(keychain): return any(fnmatch.fnmatchcase(keychain, glob) for glob in blacklist) bindings = [(k, v) for (k, v) in keyconf.get_bindings_for(modename).items() if k.startswith(prefix) and not utils.is_special_key(k) and not blacklisted(k)] if not bindings: self._show_timer.stop() return # delay so a quickly typed keychain doesn't display hints self._show_timer.start() suffix_color = html.escape(config.get('colors', 'keyhint.fg.suffix')) text = '' for key, cmd in bindings: text += ( "<tr>" "<td>{}</td>" "<td style='color: {}'>{}</td>" "<td style='padding-left: 2ex'>{}</td>" "</tr>" ).format( html.escape(prefix), suffix_color, html.escape(key[len(prefix):]), html.escape(cmd) ) text = '<table>{}</table>'.format(text) self.setText(text) self.adjustSize() self.update_geometry.emit()
def _is_new(self, sectname, command, keychain): """Check if a given binding is new. A binding is considered new if both the command is not bound to any key yet, and the key isn't used anywhere else in the same section. """ if utils.is_special_key(keychain): keychain = keychain.lower() try: bindings = self.keybindings[sectname] except KeyError: return True if keychain in bindings: return False else: return command not in bindings.values()
def bind(self, key, win_id, command=None, *, mode='normal', force=False): """Bind a key to a command. Args: key: The keychain or special key (inside `<...>`) to bind. command: The command to execute, with optional args, or None to print the current binding. mode: A comma-separated list of modes to bind the key in (default: `normal`). force: Rebind the key if it is already bound. """ if utils.is_special_key(key): # <Ctrl-t>, <ctrl-T>, and <ctrl-t> should be considered equivalent key = key.lower() if command is None: cmd = self.get_bindings_for(mode).get(key, None) if cmd is None: message.info(win_id, "{} is unbound in {} mode".format(key, mode)) else: message.info( win_id, "{} is bound to '{}' in {} mode".format(key, cmd, mode)) return mode = self._normalize_sectname(mode) for m in mode.split(','): if m not in configdata.KEY_DATA: raise cmdexc.CommandError("Invalid mode {}!".format(m)) try: self._validate_command(command) except KeyConfigError as e: raise cmdexc.CommandError(str(e)) try: self._add_binding(mode, key, command, force=force) except DuplicateKeychainError as e: raise cmdexc.CommandError("Duplicate keychain {} - use --force to " "override!".format(str(e.keychain))) except KeyConfigError as e: raise cmdexc.CommandError(e) for m in mode.split(','): self.changed.emit(m) self._mark_config_dirty()
def bind(self, key, win_id, command=None, *, mode='normal', force=False): """Bind a key to a command. Args: key: The keychain or special key (inside `<...>`) to bind. command: The command to execute, with optional args, or None to print the current binding. mode: A comma-separated list of modes to bind the key in (default: `normal`). force: Rebind the key if it is already bound. """ if utils.is_special_key(key): # <Ctrl-t>, <ctrl-T>, and <ctrl-t> should be considered equivalent key = key.lower() if command is None: cmd = self.get_bindings_for(mode).get(key, None) if cmd is None: message.info(win_id, "{} is unbound in {} mode".format( key, mode)) else: message.info(win_id, "{} is bound to '{}' in {} mode".format( key, cmd, mode)) return mode = self._normalize_sectname(mode) for m in mode.split(','): if m not in configdata.KEY_DATA: raise cmdexc.CommandError("Invalid mode {}!".format(m)) try: self._validate_command(command) except KeyConfigError as e: raise cmdexc.CommandError(str(e)) try: self._add_binding(mode, key, command, force=force) except DuplicateKeychainError as e: raise cmdexc.CommandError("Duplicate keychain {} - use --force to " "override!".format(str(e.keychain))) except KeyConfigError as e: raise cmdexc.CommandError(e) for m in mode.split(','): self.changed.emit(m) self._mark_config_dirty()
def _add_binding(self, sectname, keychain, command, *, force=False): """Add a new binding from keychain to command in section sectname.""" if utils.is_special_key(keychain): # <Ctrl-t>, <ctrl-T>, and <ctrl-t> should be considered equivalent keychain = keychain.lower() log.keyboard.vdebug("Adding binding {} -> {} in mode {}.".format( keychain, command, sectname)) if sectname not in self.keybindings: self.keybindings[sectname] = collections.OrderedDict() if keychain in self.get_bindings_for(sectname): if force or command == self.UNBOUND_COMMAND: self.unbind(keychain, mode=sectname) else: raise DuplicateKeychainError(keychain) section = self.keybindings[sectname] if (command != self.UNBOUND_COMMAND and section.get(keychain, None) == self.UNBOUND_COMMAND): # re-binding an unbound keybinding del section[keychain] self.keybindings[sectname][keychain] = command
def update_keyhint(self, modename, prefix): """Show hints for the given prefix (or hide if prefix is empty). Args: prefix: The current partial keystring. """ if not prefix or not self._enabled: self.hide() return keyconf = objreg.get('key-config') bindings = [(k, v) for (k, v) in keyconf.get_bindings_for(modename).items() if k.startswith(prefix) and not utils.is_special_key(k)] if not bindings: return self.show() suffix_color = html.escape(config.get('colors', 'keyhint.fg.suffix')) text = '' for key, cmd in bindings: text += ( "<tr>" "<td>{}</td>" "<td style='color: {}'>{}</td>" "<td style='padding-left: 2ex'>{}</td>" "</tr>" ).format( html.escape(prefix), suffix_color, html.escape(key[len(prefix):]), html.escape(cmd) ) text = '<table>{}</table>'.format(text) self.setText(text) self.adjustSize() self.reposition_keyhint.emit()
def __init__(self, parent=None): super().__init__(parent) self.setStyleSheet(""" background-color: rgba(50, 50, 50, 80%); color: white; border-radius: 20px; padding: 30px; """) all_bindings = config.key_instance.get_reverse_bindings_for('normal') bindings = all_bindings.get('fullscreen --leave') if bindings: key = bindings[0] if utils.is_special_key(key): key = key.strip('<>').capitalize() self.setText("Press {} to exit fullscreen.".format(key)) else: self.setText("Page is now fullscreen.") self.resize(self.sizeHint()) geom = QApplication.desktop().screenGeometry(self) self.move((geom.width() - self.sizeHint().width()) / 2, 30)
def bind(self, key, command=None, *, mode='normal', default=False): """Bind a key to a command. Args: key: The keychain or special key (inside `<...>`) to bind. command: The command to execute, with optional args, or None to print the current binding. mode: A comma-separated list of modes to bind the key in (default: `normal`). See `:help bindings.commands` for the available modes. default: If given, restore a default binding. """ if command is None: if default: # :bind --default: Restore default with self._handle_config_error(): self._keyconfig.bind_default(key, mode=mode, save_yaml=True) return # No --default -> print binding if utils.is_special_key(key): # self._keyconfig.get_command does this, but we also need it # normalized for the output below key = utils.normalize_keystr(key) with self._handle_config_error(): cmd = self._keyconfig.get_command(key, mode) if cmd is None: message.info("{} is unbound in {} mode".format(key, mode)) else: message.info("{} is bound to '{}' in {} mode".format( key, cmd, mode)) return with self._handle_config_error(): self._keyconfig.bind(key, command, mode=mode, save_yaml=True)
def unbind(self, key, mode='normal'): """Unbind a keychain. Args: key: The keychain or special key (inside <...>) to unbind. mode: A comma-separated list of modes to unbind the key in (default: `normal`). """ if utils.is_special_key(key): # <Ctrl-t>, <ctrl-T>, and <ctrl-t> should be considered equivalent key = key.lower() mode = self._normalize_sectname(mode) for m in mode.split(','): if m not in configdata.KEY_DATA: raise cmdexc.CommandError("Invalid mode {}!".format(m)) try: sect = self.keybindings[mode] except KeyError: raise cmdexc.CommandError("Can't find mode section '{}'!".format( mode)) try: del sect[key] except KeyError: raise cmdexc.CommandError("Can't find binding '{}' in section " "'{}'!".format(key, mode)) else: if key in itertools.chain.from_iterable( configdata.KEY_DATA[mode].values()): try: self._add_binding(mode, key, self.UNBOUND_COMMAND) except DuplicateKeychainError: pass for m in mode.split(','): self.changed.emit(m) self._mark_config_dirty()
def unbind(self, key, mode='normal'): """Unbind a keychain. Args: key: The keychain or special key (inside <...>) to unbind. mode: A comma-separated list of modes to unbind the key in (default: `normal`). """ if utils.is_special_key(key): # <Ctrl-t>, <ctrl-T>, and <ctrl-t> should be considered equivalent key = key.lower() mode = self._normalize_sectname(mode) for m in mode.split(','): if m not in configdata.KEY_DATA: raise cmdexc.CommandError("Invalid mode {}!".format(m)) try: sect = self.keybindings[mode] except KeyError: raise cmdexc.CommandError( "Can't find mode section '{}'!".format(mode)) try: del sect[key] except KeyError: raise cmdexc.CommandError("Can't find binding '{}' in section " "'{}'!".format(key, mode)) else: if key in itertools.chain.from_iterable( configdata.KEY_DATA[mode].values()): try: self._add_binding(mode, key, self.UNBOUND_COMMAND) except DuplicateKeychainError: pass for m in mode.split(','): self.changed.emit(m) self._mark_config_dirty()