8d80ce2628
Our home-brewn enum wasn't really liked by pylint (many no-member errors), so instead of adding some workaround, we just use the python 3.4 enum instead. This however also means we need to depend on Python 3.4 and not 3.3. Maybe we should use enum34 on Python < 3.3.
189 lines
6.4 KiB
Python
189 lines
6.4 KiB
Python
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
|
|
|
# Copyright 2014 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
|
#
|
|
# This file is part of qutebrowser.
|
|
#
|
|
# qutebrowser is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# qutebrowser is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
"""KeyChainParser for "hint" and "normal" modes.
|
|
|
|
Module attributes:
|
|
STARTCHARS: Possible chars for starting a commandline input.
|
|
"""
|
|
|
|
from PyQt5.QtCore import pyqtSignal, Qt
|
|
|
|
import qutebrowser.utils.message as message
|
|
import qutebrowser.config.config as config
|
|
from qutebrowser.keyinput.keyparser import CommandKeyParser
|
|
from qutebrowser.utils.usertypes import enum
|
|
from qutebrowser.utils.log import keyboard as logger
|
|
|
|
|
|
STARTCHARS = ":/?"
|
|
LastPress = enum('LastPress', 'none', 'filtertext', 'keystring')
|
|
|
|
|
|
class NormalKeyParser(CommandKeyParser):
|
|
|
|
"""KeyParser for normalmode with added STARTCHARS detection."""
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent, supports_count=True, supports_chains=True)
|
|
self.read_config('keybind')
|
|
|
|
def __repr__(self):
|
|
return '<{}>'.format(self.__class__.__name__)
|
|
|
|
def _handle_single_key(self, e):
|
|
"""Override _handle_single_key to abort if the key is a startchar.
|
|
|
|
Args:
|
|
e: the KeyPressEvent from Qt.
|
|
|
|
Return:
|
|
True if event has been handled, False otherwise.
|
|
"""
|
|
txt = e.text().strip()
|
|
if not self._keystring and any(txt == c for c in STARTCHARS):
|
|
message.set_cmd_text(txt)
|
|
return True
|
|
return super()._handle_single_key(e)
|
|
|
|
|
|
class PromptKeyParser(CommandKeyParser):
|
|
|
|
"""KeyParser for yes/no prompts."""
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent, supports_count=False, supports_chains=True)
|
|
# We don't want an extra section for this in the config, so we just
|
|
# abuse the keybind.prompt section.
|
|
self.read_config('keybind.prompt')
|
|
|
|
def __repr__(self):
|
|
return '<{}>'.format(self.__class__.__name__)
|
|
|
|
|
|
class HintKeyParser(CommandKeyParser):
|
|
|
|
"""KeyChainParser for hints.
|
|
|
|
Signals:
|
|
fire_hint: When a hint keybinding was completed.
|
|
Arg: the keystring/hint string pressed.
|
|
filter_hints: When the filter text changed.
|
|
Arg: the text to filter hints with.
|
|
|
|
Attributes:
|
|
_filtertext: The text to filter with.
|
|
_last_press: The nature of the last keypress, a LastPress member.
|
|
"""
|
|
|
|
fire_hint = pyqtSignal(str)
|
|
filter_hints = pyqtSignal(str)
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent, supports_count=False, supports_chains=True)
|
|
self._filtertext = ''
|
|
self._last_press = LastPress.none
|
|
self.read_config('keybind.hint')
|
|
|
|
def _handle_special_key(self, e):
|
|
"""Override _handle_special_key to handle string filtering.
|
|
|
|
Return True if the keypress has been handled, and False if not.
|
|
|
|
Args:
|
|
e: the KeyPressEvent from Qt.
|
|
|
|
Return:
|
|
True if event has been handled, False otherwise.
|
|
|
|
Emit:
|
|
filter_hints: Emitted when filter string has changed.
|
|
keystring_updated: Emitted when keystring has been changed.
|
|
"""
|
|
logger.debug("Got special key 0x{:x} text {}".format(
|
|
e.key(), e.text()))
|
|
if e.key() == Qt.Key_Backspace:
|
|
logger.debug("Got backspace, mode {}, filtertext '{}', keystring "
|
|
"'{}'".format(self._last_press, self._filtertext,
|
|
self._keystring))
|
|
if self._last_press == LastPress.filtertext and self._filtertext:
|
|
self._filtertext = self._filtertext[:-1]
|
|
self.filter_hints.emit(self._filtertext)
|
|
return True
|
|
elif self._last_press == LastPress.keystring and self._keystring:
|
|
self._keystring = self._keystring[:-1]
|
|
self.keystring_updated.emit(self._keystring)
|
|
return True
|
|
else:
|
|
return super()._handle_special_key(e)
|
|
elif config.get('hints', 'mode') != 'number':
|
|
return super()._handle_special_key(e)
|
|
elif not e.text():
|
|
return super()._handle_special_key(e)
|
|
else:
|
|
self._filtertext += e.text()
|
|
self.filter_hints.emit(self._filtertext)
|
|
self._last_press = LastPress.filtertext
|
|
return True
|
|
|
|
def handle(self, e):
|
|
"""Handle a new keypress and call the respective handlers.
|
|
|
|
Args:
|
|
e: the KeyPressEvent from Qt
|
|
|
|
Emit:
|
|
keystring_updated: If a new keystring should be set.
|
|
"""
|
|
handled = self._handle_single_key(e)
|
|
if handled and self._keystring:
|
|
# A key has been added to the keystring (Match.partial)
|
|
self.keystring_updated.emit(self._keystring)
|
|
self._last_press = LastPress.keystring
|
|
return handled
|
|
elif handled:
|
|
# We handled the key but the keystring is empty. This happens when
|
|
# match is Match.definitive, so a keychain has been completed.
|
|
self._last_press = LastPress.none
|
|
return handled
|
|
else:
|
|
# We couldn't find a keychain so we check if it's a special key.
|
|
return self._handle_special_key(e)
|
|
|
|
def execute(self, cmdstr, keytype, count=None):
|
|
"""Handle a completed keychain.
|
|
|
|
Emit:
|
|
fire_hint: Emitted if keytype is chain
|
|
"""
|
|
if keytype == self.Type.chain:
|
|
self.fire_hint.emit(cmdstr)
|
|
else:
|
|
# execute as command
|
|
super().execute(cmdstr, keytype, count)
|
|
|
|
def on_hint_strings_updated(self, strings):
|
|
"""Handler for HintManager's hint_strings_updated.
|
|
|
|
Args:
|
|
strings: A list of hint strings.
|
|
"""
|
|
self.bindings = {s: s for s in strings}
|
|
self._filtertext = ''
|