diff options
author | Florian Bruhin <me@the-compiler.org> | 2019-07-09 12:44:42 +0200 |
---|---|---|
committer | Florian Bruhin <me@the-compiler.org> | 2019-07-09 12:44:42 +0200 |
commit | 25a1189b80426f91a0b9331385f3beaf882161ef (patch) | |
tree | 2a13ed661af73408c4da840eb94fc5b3df84f666 | |
parent | 50f530c4494cf87455da308e960301c59d14c89f (diff) | |
download | qutebrowser-25a1189b80426f91a0b9331385f3beaf882161ef.tar.gz qutebrowser-25a1189b80426f91a0b9331385f3beaf882161ef.zip |
Add :prompt-accept --save for boolean prompts
Fixes #832
-rw-r--r-- | doc/help/commands.asciidoc | 5 | ||||
-rw-r--r-- | doc/help/settings.asciidoc | 2 | ||||
-rw-r--r-- | qutebrowser/browser/shared.py | 10 | ||||
-rw-r--r-- | qutebrowser/config/configdata.yml | 2 | ||||
-rw-r--r-- | qutebrowser/mainwindow/prompt.py | 73 | ||||
-rw-r--r-- | qutebrowser/utils/message.py | 13 | ||||
-rw-r--r-- | qutebrowser/utils/usertypes.py | 5 |
7 files changed, 85 insertions, 25 deletions
diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index ae9ada9fd..c34cfe602 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -1744,7 +1744,7 @@ How many blocks to move. [[prompt-accept]] === prompt-accept -Syntax: +:prompt-accept ['value']+ +Syntax: +:prompt-accept [*--save*] ['value']+ Accept the current prompt. @@ -1752,6 +1752,9 @@ Accept the current prompt. * +'value'+: If given, uses this value instead of the entered one. For boolean prompts, "yes"/"no" are accepted as value. +==== optional arguments +* +*-s*+, +*--save*+: Save the value to the config. + [[prompt-item-focus]] === prompt-item-focus Syntax: +:prompt-item-focus 'which'+ diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index 438a6c2b1..f6115b076 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -703,6 +703,8 @@ Default: * +pass:[<Alt-Y>]+: +pass:[prompt-yank]+ * +pass:[<Escape>]+: +pass:[leave-mode]+ * +pass:[<Return>]+: +pass:[prompt-accept]+ +* +pass:[N]+: +pass:[prompt-accept --save no]+ +* +pass:[Y]+: +pass:[prompt-accept --save yes]+ * +pass:[n]+: +pass:[prompt-accept no]+ * +pass:[y]+: +pass:[prompt-accept yes]+ diff --git a/qutebrowser/browser/shared.py b/qutebrowser/browser/shared.py index 194dc5b36..df71a9811 100644 --- a/qutebrowser/browser/shared.py +++ b/qutebrowser/browser/shared.py @@ -156,7 +156,8 @@ def ignore_certificate_errors(url, errors, abort_on): Return: True if the error should be ignored, False otherwise. """ - ssl_strict = config.instance.get('content.ssl_strict', url=url) + option = 'content.ssl_strict' + ssl_strict = config.instance.get(option, url=url) log.webview.debug("Certificate errors {!r}, strict {}".format( errors, ssl_strict)) @@ -177,7 +178,7 @@ def ignore_certificate_errors(url, errors, abort_on): urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded) ignore = message.ask(title="Certificate errors - continue?", text=msg, mode=usertypes.PromptMode.yesno, default=False, - abort_on=abort_on, url=urlstr) + abort_on=abort_on, url=urlstr, option=option) if ignore is None: # prompt aborted ignore = False @@ -225,7 +226,7 @@ def feature_permission(url, option, msg, yes_action, no_action, abort_on, if blocking: answer = message.ask(abort_on=abort_on, title='Permission request', - text=text, url=urlstr, + text=text, url=urlstr, option=option, mode=usertypes.PromptMode.yesno) if answer: yes_action() @@ -236,7 +237,8 @@ def feature_permission(url, option, msg, yes_action, no_action, abort_on, return message.confirm_async( yes_action=yes_action, no_action=no_action, cancel_action=no_action, abort_on=abort_on, - title='Permission request', text=text, url=urlstr) + title='Permission request', text=text, url=urlstr, + option=option) elif config_val: yes_action() return None diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index 50b8221a1..2a4800a11 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -2825,6 +2825,8 @@ bindings.default: <Return>: prompt-accept y: prompt-accept yes n: prompt-accept no + Y: prompt-accept --save yes + N: prompt-accept --save no <Alt-Y>: prompt-yank <Alt-Shift-Y>: prompt-yank --sel <Escape>: leave-mode diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py index 9fd3da8dd..9dfdd2d8a 100644 --- a/qutebrowser/mainwindow/prompt.py +++ b/qutebrowser/mainwindow/prompt.py @@ -26,16 +26,17 @@ import functools import attr from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QTimer, QDir, QModelIndex, - QItemSelectionModel, QObject, QEventLoop) + QItemSelectionModel, QObject, QEventLoop, QUrl) from PyQt5.QtWidgets import (QWidget, QGridLayout, QVBoxLayout, QLineEdit, QLabel, QFileSystemModel, QTreeView, QSizePolicy, QSpacerItem) from qutebrowser.browser import downloads -from qutebrowser.config import config +from qutebrowser.config import config, configtypes, configexc from qutebrowser.utils import usertypes, log, utils, qtutils, objreg, message from qutebrowser.keyinput import modeman from qutebrowser.api import cmdutils +from qutebrowser.utils import urlmatch prompt_queue = None @@ -55,7 +56,7 @@ class Error(Exception): """Base class for errors in this module.""" -class UnsupportedOperationError(Exception): +class UnsupportedOperationError(Error): """Raised when the prompt class doesn't support the requested operation.""" @@ -377,7 +378,7 @@ class PromptContainer(QWidget): @cmdutils.register(instance='prompt-container', scope='window', modes=[usertypes.KeyMode.prompt, usertypes.KeyMode.yesno]) - def prompt_accept(self, value=None): + def prompt_accept(self, value=None, *, save=False): """Accept the current prompt. // @@ -388,12 +389,15 @@ class PromptContainer(QWidget): Args: value: If given, uses this value instead of the entered one. For boolean prompts, "yes"/"no" are accepted as value. + save: Save the value to the config. """ question = self._prompt.question + try: - done = self._prompt.accept(value) + done = self._prompt.accept(value, save=save) except Error as e: raise cmdutils.CommandError(str(e)) + if done: message.global_bridge.prompt_done.emit(self._prompt.KEY_MODE) question.done() @@ -545,7 +549,12 @@ class _BasePrompt(QWidget): self._vbox.addLayout(self._key_grid) - def accept(self, value=None): + def _check_save_support(self, save): + if save: + raise UnsupportedOperationError("Saving answers is only possible " + "with yes/no prompts.") + + def accept(self, value=None, save=False): raise NotImplementedError def download_open(self, cmdline, pdfjs): @@ -577,7 +586,8 @@ class LineEditPrompt(_BasePrompt): self.setFocusProxy(self._lineedit) self._init_key_label() - def accept(self, value=None): + def accept(self, value=None, save=False): + self._check_save_support(save) text = value if value is not None else self._lineedit.text() self.question.answer = text return True @@ -693,7 +703,8 @@ class FilenamePrompt(_BasePrompt): self._file_model.directoryLoaded.connect( lambda: self._file_model.sort(0)) - def accept(self, value=None): + def accept(self, value=None, save=False): + self._check_save_support(save) text = value if value is not None else self._lineedit.text() text = downloads.transform_path(text) if text is None: @@ -763,8 +774,8 @@ class DownloadFilenamePrompt(FilenamePrompt): super().__init__(question, parent) self._file_model.setFilter(QDir.AllDirs | QDir.Drives | QDir.NoDot) - def accept(self, value=None): - done = super().accept(value) + def accept(self, value=None, save=False): + done = super().accept(value, save) answer = self.question.answer if answer is not None: self.question.answer = downloads.FileDownloadTarget(answer) @@ -817,7 +828,8 @@ class AuthenticationPrompt(_BasePrompt): assert not question.default, question.default self.setFocusProxy(self._user_lineedit) - def accept(self, value=None): + def accept(self, value=None, save=False): + self._check_save_support(save) if value is not None: if ':' not in value: raise Error("Value needs to be in the format " @@ -860,7 +872,14 @@ class YesNoPrompt(_BasePrompt): self._init_texts(question) self._init_key_label() - def accept(self, value=None): + def _check_save_support(self, save): + if self.question.option is None: + raise Error("No setting available to save the answer for this " + "question.") + + def accept(self, value=None, save=False): + self._check_save_support(save) + if value is None: if self.question.default is None: raise Error("No default value was set for this question!") @@ -871,14 +890,30 @@ class YesNoPrompt(_BasePrompt): self.question.answer = False else: raise Error("Invalid value {} - expected yes/no!".format(value)) + + if save: + opt = config.instance.get_opt(self.question.option) + assert isinstance(opt.typ, configtypes.Bool) + pattern = urlmatch.UrlPattern(self.question.url) + + try: + config.instance.set_obj(opt.name, self.question.answer, + pattern=pattern, save_yaml=True) + except configexc.Error as e: + raise Error(str(e)) + return True def _allowed_commands(self): - cmds = [ - ('prompt-accept yes', "Yes"), - ('prompt-accept no', "No"), - ('prompt-yank', "Yank URL"), - ] + cmds = [] + + cmds.append(('prompt-accept yes', "Yes")) + if self.question.option is not None: + cmds.append(('prompt-accept --save yes', "Always")) + + cmds.append(('prompt-accept no', "No")) + if self.question.option is not None: + cmds.append(('prompt-accept --save no', "Never")) if self.question.default is not None: assert self.question.default in [True, False] @@ -886,6 +921,7 @@ class YesNoPrompt(_BasePrompt): cmds.append(('prompt-accept', "Use default ({})".format(default))) cmds.append(('leave-mode', "Abort")) + cmds.append(('prompt-yank', "Yank URL")) return cmds @@ -898,7 +934,8 @@ class AlertPrompt(_BasePrompt): self._init_texts(question) self._init_key_label() - def accept(self, value=None): + def accept(self, value=None, save=False): + self._check_save_support(save) if value is not None: raise Error("No value is permitted with alert prompts!") # Simply mark prompt as done without setting self.question.answer diff --git a/qutebrowser/utils/message.py b/qutebrowser/utils/message.py index b663cbfa4..155547abd 100644 --- a/qutebrowser/utils/message.py +++ b/qutebrowser/utils/message.py @@ -83,7 +83,7 @@ def info(message: str, *, replace: bool = False) -> None: def _build_question(title, text=None, *, mode, default=None, abort_on=(), - url=None): + url=None, option=None): """Common function for ask/ask_async.""" if not isinstance(mode, usertypes.PromptMode): raise TypeError("Mode {} is no PromptMode member!".format(mode)) @@ -93,6 +93,14 @@ def _build_question(title, text=None, *, mode, default=None, abort_on=(), question.mode = mode question.default = default question.url = url + + if option is not None: + if mode != usertypes.PromptMode.yesno: + raise ValueError("Can only 'option' with PromptMode.yesno") + if url is None: + raise ValueError("Need 'url' given when 'option' is given") + question.option = option + for sig in abort_on: sig.connect(question.abort) return question @@ -106,6 +114,8 @@ def ask(*args, **kwargs): mode: A PromptMode. default: The default value to display. text: Additional text to show + option: The option for always/never question answers. + Only available with PromptMode.yesno. abort_on: A list of signals which abort the question if emitted. Return: @@ -145,6 +155,7 @@ def confirm_async(*, yes_action, no_action=None, cancel_action=None, cancel_action: Callable to be called when the user cancelled the question. default: True/False to set a default value, or None. + option: The option for always/never question answers. text: Additional text to show. Return: diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 03bd44869..0bb7badc8 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -309,6 +309,7 @@ class Question(QObject): title: The question title to show. text: The prompt text to display to the user. url: Any URL referenced in prompts. + option: Boolean option to be set when answering always/never. answer: The value the user entered (as password for user_pwd). is_aborted: Whether the question was aborted. interrupted: Whether the question was interrupted by another one. @@ -340,13 +341,15 @@ class Question(QObject): self.title = None self.text = None self.url = None + self.option = None self.answer = None self.is_aborted = False self.interrupted = False def __repr__(self): return utils.get_repr(self, title=self.title, text=self.text, - mode=self._mode, default=self.default) + mode=self._mode, default=self.default, + option=self.option) @property def mode(self): |