qutebrowser/qutebrowser/widgets/statusbar/command.py

175 lines
6.2 KiB
Python
Raw Normal View History

2014-01-29 15:30:19 +01:00
"""The commandline part of the statusbar."""
2014-02-06 14:01:23 +01:00
# 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/>.
2014-01-27 21:42:00 +01:00
import logging
2014-01-30 22:29:01 +01:00
from PyQt5.QtWidgets import QLineEdit, QShortcut, QSizePolicy
2014-01-27 21:42:00 +01:00
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QValidator, QKeySequence
import qutebrowser.commands.keys as keys
2014-01-27 21:42:00 +01:00
class Command(QLineEdit):
2014-01-29 15:30:19 +01:00
"""The commandline part of the statusbar."""
2014-01-28 23:04:02 +01:00
# Emitted when a command is triggered by the user
got_cmd = pyqtSignal(str)
2014-01-29 21:06:56 +01:00
# Emitted for searches triggered by the user
got_search = pyqtSignal(str)
got_search_rev = pyqtSignal(str)
2014-01-29 08:36:44 +01:00
statusbar = None # The status bar object
2014-01-28 23:04:02 +01:00
esc_pressed = pyqtSignal() # Emitted when escape is pressed
tab_pressed = pyqtSignal(bool) # Emitted when tab is pressed (arg: shift)
hide_completion = pyqtSignal() # Hide completion window
history = [] # The command history, with newer commands at the bottom
2014-01-27 21:42:00 +01:00
_tmphist = []
_histpos = None
# FIXME won't the tab key switch to the next widget?
2014-01-29 08:36:44 +01:00
# See [0] for a possible fix.
# [0] http://www.saltycrane.com/blog/2008/01/how-to-capture-tab-key-press-event-with/ # noqa # pylint: disable=line-too-long
2014-01-27 21:42:00 +01:00
2014-01-29 08:36:44 +01:00
def __init__(self, statusbar):
super().__init__(statusbar)
2014-01-27 21:42:00 +01:00
# FIXME
2014-01-29 08:36:44 +01:00
self.statusbar = statusbar
2014-01-30 20:30:18 +01:00
self.setStyleSheet("border: 0px; padding-left: 1px;")
2014-01-27 21:42:00 +01:00
self.setValidator(Validator())
self.returnPressed.connect(self.process_cmdline)
2014-01-27 21:42:00 +01:00
self.textEdited.connect(self._histbrowse_stop)
2014-01-30 22:29:01 +01:00
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
2014-01-27 21:42:00 +01:00
2014-01-29 15:30:19 +01:00
for (key, handler) in [
(Qt.Key_Escape, self.esc_pressed),
(Qt.Key_Up, self.key_up_handler),
(Qt.Key_Down, self.key_down_handler),
(Qt.Key_Tab | Qt.SHIFT, lambda: self.tab_pressed.emit(True)),
(Qt.Key_Tab, lambda: self.tab_pressed.emit(False))
]:
2014-01-27 21:42:00 +01:00
sc = QShortcut(self)
sc.setKey(QKeySequence(key))
sc.setContext(Qt.WidgetWithChildrenShortcut)
sc.activated.connect(handler)
def process_cmdline(self):
2014-01-29 15:30:19 +01:00
"""Handle the command in the status bar."""
2014-01-29 21:06:56 +01:00
signals = {
':': self.got_cmd,
'/': self.got_search,
'?': self.got_search_rev,
}
2014-01-27 21:42:00 +01:00
self._histbrowse_stop()
text = self.text()
2014-01-27 21:42:00 +01:00
if not self.history or text != self.history[-1]:
self.history.append(text)
self.setText('')
2014-01-29 21:06:56 +01:00
if text[0] in signals:
signals[text[0]].emit(text.lstrip(text[0]))
2014-01-27 21:42:00 +01:00
def set_cmd(self, text):
2014-01-29 15:30:19 +01:00
"""Preset the statusbar to some text."""
self.setText(text)
2014-01-27 21:42:00 +01:00
self.setFocus()
def append_cmd(self, text):
2014-01-29 15:30:19 +01:00
"""Append text to the commandline."""
2014-01-27 21:42:00 +01:00
# FIXME do the right thing here
self.setText(':' + text)
self.setFocus()
def focusOutEvent(self, e):
2014-01-29 15:30:19 +01:00
"""Clear the statusbar text if it's explicitely unfocused."""
2014-01-27 21:42:00 +01:00
if e.reason() in [Qt.MouseFocusReason, Qt.TabFocusReason,
Qt.BacktabFocusReason, Qt.OtherFocusReason]:
self.setText('')
self._histbrowse_stop()
self.hide_completion.emit()
super().focusOutEvent(e)
def focusInEvent(self, e):
2014-01-29 15:30:19 +01:00
"""Clear error message when the statusbar is focused."""
2014-01-29 08:36:44 +01:00
self.statusbar.clear_error()
2014-01-27 21:42:00 +01:00
super().focusInEvent(e)
def _histbrowse_start(self):
2014-01-29 15:30:19 +01:00
"""Start browsing to the history.
Called when the user presses the up/down key and wasn't browsing the
history already.
"""
pre = self.text().strip()
2014-01-27 21:42:00 +01:00
logging.debug('Preset text: "{}"'.format(pre))
if pre:
self._tmphist = [e for e in self.history if e.startswith(pre)]
else:
self._tmphist = self.history
self._histpos = len(self._tmphist) - 1
def _histbrowse_stop(self):
2014-01-29 15:30:19 +01:00
"""Stop browsing the history."""
2014-01-27 21:42:00 +01:00
self._histpos = None
def key_up_handler(self):
2014-01-29 15:30:19 +01:00
"""Handle Up presses (go back in history)."""
2014-01-27 21:42:00 +01:00
logging.debug("history up [pre]: pos {}".format(self._histpos))
if self._histpos is None:
self._histbrowse_start()
elif self._histpos <= 0:
return
else:
self._histpos -= 1
if not self._tmphist:
return
logging.debug("history up: {} / len {} / pos {}".format(
self._tmphist, len(self._tmphist), self._histpos))
self.set_cmd(self._tmphist[self._histpos])
def key_down_handler(self):
2014-01-29 15:30:19 +01:00
"""Handle Down presses (go forward in history)."""
2014-01-27 21:42:00 +01:00
logging.debug("history up [pre]: pos {}".format(self._histpos,
2014-01-28 23:04:02 +01:00
self._tmphist, len(self._tmphist), self._histpos))
2014-01-27 21:42:00 +01:00
if (self._histpos is None or
self._histpos >= len(self._tmphist) - 1 or
not self._tmphist):
return
self._histpos += 1
logging.debug("history up: {} / len {} / pos {}".format(
self._tmphist, len(self._tmphist), self._histpos))
self.set_cmd(self._tmphist[self._histpos])
2014-01-28 23:04:02 +01:00
2014-01-27 21:42:00 +01:00
class Validator(QValidator):
"""Validator to prevent the : from getting deleted"""
2014-01-29 15:30:19 +01:00
2014-01-27 21:42:00 +01:00
def validate(self, string, pos):
2014-01-29 15:30:19 +01:00
"""Overrides QValidator::validate.
string -- The string to validate.
pos -- The current curser position.
Returns a tuple (status, string, pos) as a QValidator should.\
"""
if any(string.startswith(c) for c in keys.startchars):
2014-01-27 21:42:00 +01:00
return (QValidator.Acceptable, string, pos)
else:
return (QValidator.Invalid, string, pos)