summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2019-07-09 12:44:42 +0200
committerFlorian Bruhin <me@the-compiler.org>2019-07-09 12:44:42 +0200
commit25a1189b80426f91a0b9331385f3beaf882161ef (patch)
tree2a13ed661af73408c4da840eb94fc5b3df84f666
parent50f530c4494cf87455da308e960301c59d14c89f (diff)
downloadqutebrowser-25a1189b80426f91a0b9331385f3beaf882161ef.tar.gz
qutebrowser-25a1189b80426f91a0b9331385f3beaf882161ef.zip
Add :prompt-accept --save for boolean prompts
Fixes #832
-rw-r--r--doc/help/commands.asciidoc5
-rw-r--r--doc/help/settings.asciidoc2
-rw-r--r--qutebrowser/browser/shared.py10
-rw-r--r--qutebrowser/config/configdata.yml2
-rw-r--r--qutebrowser/mainwindow/prompt.py73
-rw-r--r--qutebrowser/utils/message.py13
-rw-r--r--qutebrowser/utils/usertypes.py5
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:[&lt;Alt-Y&gt;]+: +pass:[prompt-yank]+
* +pass:[&lt;Escape&gt;]+: +pass:[leave-mode]+
* +pass:[&lt;Return&gt;]+: +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):