modify Question.yank_text to Question.url

error out when question.url is None
add url to yesno prompts
add default binding in prompt mode (ctrl-y)
This commit is contained in:
Marc Jauvin 2018-01-25 17:48:45 -05:00
parent 3b1fb92b11
commit 520b473350
13 changed files with 54 additions and 26 deletions

View File

@ -1404,6 +1404,7 @@ How many steps to zoom out.
|<<prompt-accept,prompt-accept>>|Accept the current prompt.
|<<prompt-item-focus,prompt-item-focus>>|Shift the focus of the prompt file completion menu to another item.
|<<prompt-open-download,prompt-open-download>>|Immediately open a download.
|<<prompt-yank,prompt-yank>>|Yank URL.
|<<rl-backward-char,rl-backward-char>>|Move back a character.
|<<rl-backward-delete-char,rl-backward-delete-char>>|Delete the character before the cursor.
|<<rl-backward-kill-word,rl-backward-kill-word>>|Remove chars from the cursor to the beginning of the word.
@ -1609,6 +1610,10 @@ If no specific command is given, this will use the system's default application
==== note
* This command does not split arguments after the last argument and handles quotes literally.
[[prompt-yank]]
=== prompt-yank
Yank URL.
[[rl-backward-char]]
=== rl-backward-char
Move back a character.

View File

@ -627,6 +627,7 @@ Default:
* +pass:[&lt;Ctrl-W&gt;]+: +pass:[rl-unix-word-rubout]+
* +pass:[&lt;Ctrl-X&gt;]+: +pass:[prompt-open-download]+
* +pass:[&lt;Ctrl-Y&gt;]+: +pass:[rl-yank]+
* +pass:[&lt;Ctrl-y&gt;]+: +pass:[prompt-yank]+
* +pass:[&lt;Down&gt;]+: +pass:[prompt-item-focus next]+
* +pass:[&lt;Escape&gt;]+: +pass:[leave-mode]+
* +pass:[&lt;Return&gt;]+: +pass:[prompt-accept]+

View File

@ -31,7 +31,7 @@ import enum
import sip
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QObject, QModelIndex,
QTimer, QAbstractListModel)
QTimer, QAbstractListModel, QUrl)
from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.config import config
@ -166,7 +166,7 @@ def get_filename_question(*, suggested_filename, url, parent=None):
q.title = "Save file to:"
q.text = "Please enter a location for <b>{}</b>".format(
html.escape(url.toDisplayString()))
q.yank_text = url.toString()
q.url = url.toString(QUrl.RemoveUserInfo)
q.mode = usertypes.PromptMode.download
q.completed.connect(q.deleteLater)
q.default = _path_suggestion(suggested_filename)

View File

@ -20,6 +20,7 @@
"""Download manager."""
import io
import os
import shutil
import functools
@ -198,21 +199,23 @@ class DownloadItem(downloads.AbstractDownloadItem):
def _ask_confirm_question(self, title, msg):
no_action = functools.partial(self.cancel, remove_data=False)
url = 'file://{}'.format(self._filename)
message.confirm_async(title=title, text=msg,
yes_action=self._after_set_filename,
no_action=no_action, cancel_action=no_action,
abort_on=[self.cancelled, self.error])
abort_on=[self.cancelled, self.error], url=url)
def _ask_create_parent_question(self, title, msg,
force_overwrite, remember_directory):
no_action = functools.partial(self.cancel, remove_data=False)
url = 'file://{}'.format(os.path.dirname(self._filename))
message.confirm_async(title=title, text=msg,
yes_action=(lambda:
self._after_create_parent_question(
force_overwrite,
remember_directory)),
no_action=no_action, cancel_action=no_action,
abort_on=[self.cancelled, self.error])
abort_on=[self.cancelled, self.error], url=url)
def _set_fileobj(self, fileobj, *, autoclose=True):
"""Set the file object to write the download to.

View File

@ -21,13 +21,14 @@
import html
from PyQt5.QtCore import QUrl
from qutebrowser.config import config
from qutebrowser.utils import usertypes, message, log, objreg, jinja, utils
from qutebrowser.mainwindow import mainwindow
class CallSuper(Exception):
"""Raised when the caller should call the superclass instead."""
@ -61,9 +62,10 @@ def authentication_required(url, authenticator, abort_on):
else:
msg = '<b>{}</b> needs authentication'.format(
html.escape(url.toDisplayString()))
urlstr = url.toString(QUrl.RemoveUserInfo)
answer = message.ask(title="Authentication required", text=msg,
mode=usertypes.PromptMode.user_pwd,
abort_on=abort_on)
abort_on=abort_on, url=urlstr)
if answer is not None:
authenticator.setUser(answer.user)
authenticator.setPassword(answer.password)
@ -78,9 +80,10 @@ def javascript_confirm(url, js_msg, abort_on):
msg = 'From <b>{}</b>:<br/>{}'.format(html.escape(url.toDisplayString()),
html.escape(js_msg))
urlstr = url.toString(QUrl.RemoveUserInfo)
ans = message.ask('Javascript confirm', msg,
mode=usertypes.PromptMode.yesno,
abort_on=abort_on)
abort_on=abort_on, url=urlstr)
return bool(ans)
@ -94,10 +97,11 @@ def javascript_prompt(url, js_msg, default, abort_on):
msg = '<b>{}</b> asks:<br/>{}'.format(html.escape(url.toDisplayString()),
html.escape(js_msg))
urlstr = url.toString(QUrl.RemoveUserInfo)
answer = message.ask('Javascript prompt', msg,
mode=usertypes.PromptMode.text,
default=default,
abort_on=abort_on)
abort_on=abort_on, url=urlstr)
if answer is None:
return (False, "")
@ -116,8 +120,9 @@ def javascript_alert(url, js_msg, abort_on):
msg = 'From <b>{}</b>:<br/>{}'.format(html.escape(url.toDisplayString()),
html.escape(js_msg))
urlstr = url.toString(QUrl.RemoveUserInfo)
message.ask('Javascript alert', msg, mode=usertypes.PromptMode.alert,
abort_on=abort_on)
abort_on=abort_on, url=urlstr)
def javascript_log_message(level, source, line, msg):
@ -164,9 +169,10 @@ def ignore_certificate_errors(url, errors, abort_on):
""".strip())
msg = err_template.render(url=url, errors=errors)
urlstr = url.toString(QUrl.RemoveUserInfo)
ignore = message.ask(title="Certificate errors - continue?", text=msg,
mode=usertypes.PromptMode.yesno, default=False,
abort_on=abort_on)
abort_on=abort_on, url=urlstr)
if ignore is None:
# prompt aborted
ignore = False
@ -202,15 +208,17 @@ def feature_permission(url, option, msg, yes_action, no_action, abort_on):
config_val = config.instance.get(option)
if config_val == 'ask':
if url.isValid():
urlstr = url.toString(QUrl.RemoveUserInfo)
text = "Allow the website at <b>{}</b> to {}?".format(
html.escape(url.toDisplayString()), msg)
else:
urlstr = None
text = "Allow the website to {}?".format(msg)
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)
title='Permission request', text=text, url=urlstr)
elif config_val:
yes_action()
return None

View File

@ -161,7 +161,7 @@ class QuickmarkManager(UrlMarkManager):
"Add quickmark:", usertypes.PromptMode.text,
functools.partial(self.quickmark_add, urlstr),
text="Please enter a quickmark name for<br/><b>{}</b>".format(
html.escape(url.toDisplayString())))
html.escape(url.toDisplayString())), url=urlstr)
@cmdutils.register(instance='quickmark-manager')
def quickmark_add(self, url, name):
@ -189,10 +189,11 @@ class QuickmarkManager(UrlMarkManager):
self.changed.emit()
log.misc.debug("Added quickmark {} for {}".format(name, url))
urlstr = url.toString(QUrl.RemoveUserInfo)
if name in self.marks:
message.confirm_async(
title="Override existing quickmark?",
yes_action=set_mark, default=True)
yes_action=set_mark, default=True, url=urlstr)
else:
set_mark()

View File

@ -125,6 +125,7 @@ class DownloadItem(downloads.AbstractDownloadItem):
question = usertypes.Question()
question.title = title
question.text = msg
question.url = 'file://{}'.format(self._filename)
question.mode = usertypes.PromptMode.yesno
question.answered_yes.connect(self._after_set_filename)
question.answered_no.connect(no_action)
@ -139,6 +140,7 @@ class DownloadItem(downloads.AbstractDownloadItem):
question = usertypes.Question()
question.title = title
question.text = msg
question.url = 'file://{}'.format(os.path.dirname(self._filename))
question.mode = usertypes.PromptMode.yesno
question.answered_yes.connect(lambda:
self._after_create_parent_question(

View File

@ -753,10 +753,11 @@ class WebEngineTab(browsertab.AbstractTab):
"""Called when a proxy needs authentication."""
msg = "<b>{}</b> requires a username and password.".format(
html_utils.escape(proxy_host))
urlstr = url.toString(QUrl.RemoveUserInfo)
answer = message.ask(
title="Proxy authentication required", text=msg,
mode=usertypes.PromptMode.user_pwd,
abort_on=[self.shutting_down, self.load_started])
abort_on=[self.shutting_down, self.load_started], url=urlstr)
if answer is not None:
authenticator.setUser(answer.user)
authenticator.setPassword(answer.password)

View File

@ -147,7 +147,8 @@ class BrowserPage(QWebPage):
title="Open external application for {}-link?".format(scheme),
text="URL: <b>{}</b>".format(
html.escape(url.toDisplayString())),
yes_action=functools.partial(QDesktopServices.openUrl, url))
yes_action=functools.partial(QDesktopServices.openUrl, url),
url=urlstr)
return True
elif (info.domain, info.error) in ignored_errors:
log.webview.debug("Ignored error on {}: {} (error domain: {}, "

View File

@ -2350,6 +2350,7 @@ bindings.default:
<Ctrl-W>: rl-unix-word-rubout
<Alt-Backspace>: rl-backward-kill-word
<Ctrl-Y>: rl-yank
<Ctrl-y>: prompt-yank
<Ctrl-?>: rl-delete-char
<Ctrl-H>: rl-backward-delete-char
<Escape>: leave-mode

View File

@ -422,16 +422,18 @@ class PromptContainer(QWidget):
except UnsupportedOperationError:
pass
@cmdutils.register(instance='prompt-container', scope='window',
modes=[usertypes.KeyMode.prompt])
@cmdutils.register(
instance='prompt-container', scope='window',
modes=[usertypes.KeyMode.prompt, usertypes.KeyMode.yesno])
def prompt_yank(self):
"""Yank URLs or other data in prompts."""
"""Yank URL."""
question = self._prompt.question
s = None
if question and hasattr(question, 'yank_text'):
s = question.yank_text
if not question.url:
message.error('No URL found.')
return
s = question.url
utils.set_clipboard(s)
message.info("Yanked download URL to clipboard: {}".format(s))
message.info("Yanked to clipboard: {}".format(s))
class LineEdit(QLineEdit):
@ -732,7 +734,7 @@ class DownloadFilenamePrompt(FilenamePrompt):
('prompt-accept', 'Accept'),
('leave-mode', 'Abort'),
('prompt-open-download', "Open download"),
('prompt-yank', "Yank URLs in prompts"),
('prompt-yank', "Yank URL"),
]
return cmds
@ -823,6 +825,7 @@ class YesNoPrompt(_BasePrompt):
cmds = [
('prompt-accept yes', "Yes"),
('prompt-accept no', "No"),
('prompt-yank', "Yank URL"),
]
if self.question.default is not None:

View File

@ -87,7 +87,8 @@ def info(message, *, replace=False):
global_bridge.show(usertypes.MessageLevel.info, message, replace)
def _build_question(title, text=None, *, mode, default=None, abort_on=()):
def _build_question(title, text=None, *, mode, default=None, abort_on=(),
url=None):
"""Common function for ask/ask_async."""
if not isinstance(mode, usertypes.PromptMode):
raise TypeError("Mode {} is no PromptMode member!".format(mode))
@ -96,6 +97,7 @@ def _build_question(title, text=None, *, mode, default=None, abort_on=()):
question.text = text
question.mode = mode
question.default = default
question.url = url
for sig in abort_on:
sig.connect(question.abort)
return question

View File

@ -266,7 +266,7 @@ class Question(QObject):
For user_pwd, a default username as string.
title: The question title to show.
text: The prompt text to display to the user.
yank_text: The prompt text available to prompt-yank command.
url: Any URL referenced in prompts.
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.
@ -297,7 +297,7 @@ class Question(QObject):
self.default = None
self.title = None
self.text = None
self.yank_text = None
self.url = None
self.answer = None
self.is_aborted = False
self.interrupted = False