Merge remote-tracking branch 'florian/master' into quickmark-completion
This commit is contained in:
commit
9613cc0eab
@ -151,7 +151,8 @@ class Application(QApplication):
|
|||||||
log.init.debug("Initializing websettings...")
|
log.init.debug("Initializing websettings...")
|
||||||
websettings.init()
|
websettings.init()
|
||||||
log.init.debug("Initializing quickmarks...")
|
log.init.debug("Initializing quickmarks...")
|
||||||
quickmarks.init()
|
quickmark_manager = quickmarks.QuickmarkManager()
|
||||||
|
objreg.register('quickmark-manager', quickmark_manager)
|
||||||
log.init.debug("Initializing proxy...")
|
log.init.debug("Initializing proxy...")
|
||||||
proxy.init()
|
proxy.init()
|
||||||
log.init.debug("Initializing cookies...")
|
log.init.debug("Initializing cookies...")
|
||||||
@ -313,7 +314,7 @@ class Application(QApplication):
|
|||||||
quickstart_done = False
|
quickstart_done = False
|
||||||
if not quickstart_done:
|
if not quickstart_done:
|
||||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
window='current')
|
window='last-focused')
|
||||||
tabbed_browser.tabopen(
|
tabbed_browser.tabopen(
|
||||||
QUrl('http://www.qutebrowser.org/quickstart.html'))
|
QUrl('http://www.qutebrowser.org/quickstart.html'))
|
||||||
try:
|
try:
|
||||||
@ -341,6 +342,7 @@ class Application(QApplication):
|
|||||||
config_obj = objreg.get('config')
|
config_obj = objreg.get('config')
|
||||||
self.lastWindowClosed.connect(self.shutdown)
|
self.lastWindowClosed.connect(self.shutdown)
|
||||||
config_obj.style_changed.connect(style.get_stylesheet.cache_clear)
|
config_obj.style_changed.connect(style.get_stylesheet.cache_clear)
|
||||||
|
self.focusChanged.connect(self.on_focus_changed)
|
||||||
|
|
||||||
def _get_widgets(self):
|
def _get_widgets(self):
|
||||||
"""Get a string list of all widgets."""
|
"""Get a string list of all widgets."""
|
||||||
@ -541,7 +543,7 @@ class Application(QApplication):
|
|||||||
out = traceback.format_exc()
|
out = traceback.format_exc()
|
||||||
qutescheme.pyeval_output = out
|
qutescheme.pyeval_output = out
|
||||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
window='current')
|
window='last-focused')
|
||||||
tabbed_browser.openurl(QUrl('qute:pyeval'), newtab=True)
|
tabbed_browser.openurl(QUrl('qute:pyeval'), newtab=True)
|
||||||
|
|
||||||
@cmdutils.register(instance='app')
|
@cmdutils.register(instance='app')
|
||||||
@ -636,12 +638,8 @@ class Application(QApplication):
|
|||||||
self.removeEventFilter(self._event_filter)
|
self.removeEventFilter(self._event_filter)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
# Close all tabs
|
# Close all windows
|
||||||
for win_id in objreg.window_registry:
|
QApplication.closeAllWindows()
|
||||||
log.destroy.debug("Closing tabs in window {}...".format(win_id))
|
|
||||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
|
||||||
window=win_id)
|
|
||||||
tabbed_browser.shutdown()
|
|
||||||
# Shut down IPC
|
# Shut down IPC
|
||||||
try:
|
try:
|
||||||
objreg.get('ipc-server').shutdown()
|
objreg.get('ipc-server').shutdown()
|
||||||
@ -663,14 +661,19 @@ class Application(QApplication):
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
to_save.append(("keyconfig", key_config.save))
|
to_save.append(("keyconfig", key_config.save))
|
||||||
to_save += [("window geometry", self._save_geometry),
|
to_save += [("window geometry", self._save_geometry)]
|
||||||
("quickmarks", quickmarks.save)]
|
|
||||||
try:
|
try:
|
||||||
command_history = objreg.get('command-history')
|
command_history = objreg.get('command-history')
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
to_save.append(("command history", command_history.save))
|
to_save.append(("command history", command_history.save))
|
||||||
|
try:
|
||||||
|
quickmark_manager = objreg.get('quickmark-manager')
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
to_save.append(("command history", quickmark_manager.save))
|
||||||
try:
|
try:
|
||||||
state_config = objreg.get('state-config')
|
state_config = objreg.get('state-config')
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -700,6 +703,20 @@ class Application(QApplication):
|
|||||||
# segfaults.
|
# segfaults.
|
||||||
QTimer.singleShot(0, functools.partial(self.exit, status))
|
QTimer.singleShot(0, functools.partial(self.exit, status))
|
||||||
|
|
||||||
|
def on_focus_changed(self, _old, new):
|
||||||
|
"""Register currently focused main window in the object registry."""
|
||||||
|
if new is None:
|
||||||
|
window = None
|
||||||
|
else:
|
||||||
|
window = new.window()
|
||||||
|
if window is None or not isinstance(window, mainwindow.MainWindow):
|
||||||
|
try:
|
||||||
|
objreg.delete('last-focused-main-window')
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
objreg.register('last-focused-main-window', window, update=True)
|
||||||
|
|
||||||
def exit(self, status):
|
def exit(self, status):
|
||||||
"""Extend QApplication::exit to log the event."""
|
"""Extend QApplication::exit to log the event."""
|
||||||
log.destroy.debug("Now calling QApplication::exit.")
|
log.destroy.debug("Now calling QApplication::exit.")
|
||||||
|
@ -36,7 +36,7 @@ import pygments.formatters
|
|||||||
|
|
||||||
from qutebrowser.commands import userscripts, cmdexc, cmdutils
|
from qutebrowser.commands import userscripts, cmdexc, cmdutils
|
||||||
from qutebrowser.config import config
|
from qutebrowser.config import config
|
||||||
from qutebrowser.browser import quickmarks, webelem
|
from qutebrowser.browser import webelem
|
||||||
from qutebrowser.utils import (message, editor, usertypes, log, qtutils,
|
from qutebrowser.utils import (message, editor, usertypes, log, qtutils,
|
||||||
urlutils, objreg, utils)
|
urlutils, objreg, utils)
|
||||||
|
|
||||||
@ -815,7 +815,8 @@ class CommandDispatcher:
|
|||||||
@cmdutils.register(instance='command-dispatcher', scope='window')
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
||||||
def quickmark_save(self):
|
def quickmark_save(self):
|
||||||
"""Save the current page as a quickmark."""
|
"""Save the current page as a quickmark."""
|
||||||
quickmarks.prompt_save(self._win_id, self._current_url())
|
quickmark_manager = objreg.get('quickmark-manager')
|
||||||
|
quickmark_manager.prompt_save(self._win_id, self._current_url())
|
||||||
|
|
||||||
@cmdutils.register(instance='command-dispatcher', scope='window',
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
||||||
completion=[usertypes.Completion.quickmark])
|
completion=[usertypes.Completion.quickmark])
|
||||||
@ -828,7 +829,7 @@ class CommandDispatcher:
|
|||||||
bg: Load the quickmark in a new background tab.
|
bg: Load the quickmark in a new background tab.
|
||||||
window: Load the quickmark in a new window.
|
window: Load the quickmark in a new window.
|
||||||
"""
|
"""
|
||||||
url = quickmarks.get(name)
|
url = objreg.get('quickmark-manager').get(name)
|
||||||
self._open(url, tab, bg, window)
|
self._open(url, tab, bg, window)
|
||||||
|
|
||||||
@cmdutils.register(instance='command-dispatcher', name='inspector',
|
@cmdutils.register(instance='command-dispatcher', name='inspector',
|
||||||
|
@ -62,6 +62,7 @@ class DownloadItem(QObject):
|
|||||||
_reply: The QNetworkReply associated with this download.
|
_reply: The QNetworkReply associated with this download.
|
||||||
_last_done: The count of bytes which where downloaded when calculating
|
_last_done: The count of bytes which where downloaded when calculating
|
||||||
the speed the last time.
|
the speed the last time.
|
||||||
|
_error: The current error message, or None
|
||||||
|
|
||||||
Signals:
|
Signals:
|
||||||
data_changed: The downloads metadata changed.
|
data_changed: The downloads metadata changed.
|
||||||
@ -88,6 +89,7 @@ class DownloadItem(QObject):
|
|||||||
self._reply = reply
|
self._reply = reply
|
||||||
self._bytes_total = None
|
self._bytes_total = None
|
||||||
self._speed = 0
|
self._speed = 0
|
||||||
|
self._error = None
|
||||||
self.basename = '???'
|
self.basename = '???'
|
||||||
samples = int(self.SPEED_AVG_WINDOW *
|
samples = int(self.SPEED_AVG_WINDOW *
|
||||||
(1000 / self.SPEED_REFRESH_INTERVAL))
|
(1000 / self.SPEED_REFRESH_INTERVAL))
|
||||||
@ -127,9 +129,13 @@ class DownloadItem(QObject):
|
|||||||
down = utils.format_size(self._bytes_done, suffix='B')
|
down = utils.format_size(self._bytes_done, suffix='B')
|
||||||
perc = self._percentage()
|
perc = self._percentage()
|
||||||
remaining = self._remaining_time()
|
remaining = self._remaining_time()
|
||||||
|
if self._error is None:
|
||||||
|
errmsg = ""
|
||||||
|
else:
|
||||||
|
errmsg = " - {}".format(self._error)
|
||||||
if all(e is None for e in (perc, remaining, self._bytes_total)):
|
if all(e is None for e in (perc, remaining, self._bytes_total)):
|
||||||
return ('{name} [{speed:>10}|{down}]'.format(
|
return ('{name} [{speed:>10}|{down}]{errmsg}'.format(
|
||||||
name=self.basename, speed=speed, down=down))
|
name=self.basename, speed=speed, down=down, errmsg=errmsg))
|
||||||
if perc is None:
|
if perc is None:
|
||||||
perc = '??'
|
perc = '??'
|
||||||
else:
|
else:
|
||||||
@ -140,9 +146,9 @@ class DownloadItem(QObject):
|
|||||||
remaining = utils.format_seconds(remaining)
|
remaining = utils.format_seconds(remaining)
|
||||||
total = utils.format_size(self._bytes_total, suffix='B')
|
total = utils.format_size(self._bytes_total, suffix='B')
|
||||||
return ('{name} [{speed:>10}|{remaining:>5}|{perc:>2}%|'
|
return ('{name} [{speed:>10}|{remaining:>5}|{perc:>2}%|'
|
||||||
'{down}/{total}]'.format(name=self.basename, speed=speed,
|
'{down}/{total}]{errmsg}'.format(
|
||||||
remaining=remaining, perc=perc,
|
name=self.basename, speed=speed, remaining=remaining,
|
||||||
down=down, total=total))
|
perc=perc, down=down, total=total, errmsg=errmsg))
|
||||||
|
|
||||||
def _die(self, msg):
|
def _die(self, msg):
|
||||||
"""Abort the download and emit an error."""
|
"""Abort the download and emit an error."""
|
||||||
@ -150,17 +156,19 @@ class DownloadItem(QObject):
|
|||||||
self._reply.finished.disconnect()
|
self._reply.finished.disconnect()
|
||||||
self._reply.error.disconnect()
|
self._reply.error.disconnect()
|
||||||
self._reply.readyRead.disconnect()
|
self._reply.readyRead.disconnect()
|
||||||
|
self._error = msg
|
||||||
self._bytes_done = self._bytes_total
|
self._bytes_done = self._bytes_total
|
||||||
self.timer.stop()
|
self.timer.stop()
|
||||||
self.error.emit(msg)
|
self.error.emit(msg)
|
||||||
self._reply.abort()
|
self._reply.abort()
|
||||||
self._reply.deleteLater()
|
self._reply.deleteLater()
|
||||||
|
self._reply = None
|
||||||
if self._fileobj is not None:
|
if self._fileobj is not None:
|
||||||
try:
|
try:
|
||||||
self._fileobj.close()
|
self._fileobj.close()
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
self.error.emit(e.strerror)
|
self.error.emit(e.strerror)
|
||||||
self.finished.emit()
|
self.data_changed.emit()
|
||||||
|
|
||||||
def _percentage(self):
|
def _percentage(self):
|
||||||
"""The current download percentage, or None if unknown."""
|
"""The current download percentage, or None if unknown."""
|
||||||
@ -187,7 +195,10 @@ class DownloadItem(QObject):
|
|||||||
start = config.get('colors', 'downloads.bg.start')
|
start = config.get('colors', 'downloads.bg.start')
|
||||||
stop = config.get('colors', 'downloads.bg.stop')
|
stop = config.get('colors', 'downloads.bg.stop')
|
||||||
system = config.get('colors', 'downloads.bg.system')
|
system = config.get('colors', 'downloads.bg.system')
|
||||||
if self._percentage() is None:
|
error = config.get('colors', 'downloads.bg.error')
|
||||||
|
if self._error is not None:
|
||||||
|
return error
|
||||||
|
elif self._percentage() is None:
|
||||||
return start
|
return start
|
||||||
else:
|
else:
|
||||||
return utils.interpolate_color(start, stop, self._percentage(),
|
return utils.interpolate_color(start, stop, self._percentage(),
|
||||||
@ -198,6 +209,7 @@ class DownloadItem(QObject):
|
|||||||
log.downloads.debug("cancelled")
|
log.downloads.debug("cancelled")
|
||||||
self.cancelled.emit()
|
self.cancelled.emit()
|
||||||
self._is_cancelled = True
|
self._is_cancelled = True
|
||||||
|
if self._reply is not None:
|
||||||
self._reply.abort()
|
self._reply.abort()
|
||||||
self._reply.deleteLater()
|
self._reply.deleteLater()
|
||||||
if self._fileobj is not None:
|
if self._fileobj is not None:
|
||||||
@ -352,7 +364,7 @@ class DownloadManager(QAbstractListModel):
|
|||||||
page: The QWebPage to get the download from.
|
page: The QWebPage to get the download from.
|
||||||
"""
|
"""
|
||||||
if not url.isValid():
|
if not url.isValid():
|
||||||
urlutils.invalid_url_error('current', url, "start download")
|
urlutils.invalid_url_error('last-focused', url, "start download")
|
||||||
return
|
return
|
||||||
req = QNetworkRequest(url)
|
req = QNetworkRequest(url)
|
||||||
reply = page.networkAccessManager().get(req)
|
reply = page.networkAccessManager().get(req)
|
||||||
@ -389,7 +401,8 @@ class DownloadManager(QAbstractListModel):
|
|||||||
functools.partial(self.on_finished, download))
|
functools.partial(self.on_finished, download))
|
||||||
download.data_changed.connect(
|
download.data_changed.connect(
|
||||||
functools.partial(self.on_data_changed, download))
|
functools.partial(self.on_data_changed, download))
|
||||||
download.error.connect(self.on_error)
|
download.error.connect(
|
||||||
|
functools.partial(self.on_error, download))
|
||||||
download.basename = suggested_filename
|
download.basename = suggested_filename
|
||||||
idx = len(self.downloads) + 1
|
idx = len(self.downloads) + 1
|
||||||
self.beginInsertRows(QModelIndex(), idx, idx)
|
self.beginInsertRows(QModelIndex(), idx, idx)
|
||||||
@ -407,7 +420,7 @@ class DownloadManager(QAbstractListModel):
|
|||||||
self.questions.append(q)
|
self.questions.append(q)
|
||||||
download.cancelled.connect(q.abort)
|
download.cancelled.connect(q.abort)
|
||||||
message_bridge = objreg.get('message-bridge', scope='window',
|
message_bridge = objreg.get('message-bridge', scope='window',
|
||||||
window='current')
|
window='last-focused')
|
||||||
message_bridge.ask(q, blocking=False)
|
message_bridge.ask(q, blocking=False)
|
||||||
|
|
||||||
@pyqtSlot(DownloadItem)
|
@pyqtSlot(DownloadItem)
|
||||||
@ -428,10 +441,10 @@ class DownloadManager(QAbstractListModel):
|
|||||||
qtutils.ensure_valid(model_idx)
|
qtutils.ensure_valid(model_idx)
|
||||||
self.dataChanged.emit(model_idx, model_idx)
|
self.dataChanged.emit(model_idx, model_idx)
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(DownloadItem, str)
|
||||||
def on_error(self, msg):
|
def on_error(self, download, msg):
|
||||||
"""Display error message on download errors."""
|
"""Display error message on download errors."""
|
||||||
message.error('current', "Download error: {}".format(msg))
|
message.error('last-focused', "Download error: {}".format(msg))
|
||||||
|
|
||||||
def last_index(self):
|
def last_index(self):
|
||||||
"""Get the last index in the model.
|
"""Get the last index in the model.
|
||||||
@ -465,6 +478,11 @@ class DownloadManager(QAbstractListModel):
|
|||||||
data = item.bg_color()
|
data = item.bg_color()
|
||||||
elif role == ModelRole.item:
|
elif role == ModelRole.item:
|
||||||
data = item
|
data = item
|
||||||
|
elif role == Qt.ToolTipRole:
|
||||||
|
if item._error is None:
|
||||||
|
data = QVariant()
|
||||||
|
else:
|
||||||
|
return item._error
|
||||||
else:
|
else:
|
||||||
data = QVariant()
|
data = QVariant()
|
||||||
return data
|
return data
|
||||||
|
@ -82,9 +82,7 @@ class HintContext:
|
|||||||
"""Get the arguments, with {hint-url} replaced by the given URL."""
|
"""Get the arguments, with {hint-url} replaced by the given URL."""
|
||||||
args = []
|
args = []
|
||||||
for arg in self.args:
|
for arg in self.args:
|
||||||
if arg == '{hint-url}':
|
arg = arg.replace('{hint-url}', urlstr)
|
||||||
args.append(urlstr)
|
|
||||||
else:
|
|
||||||
args.append(arg)
|
args.append(arg)
|
||||||
return args
|
return args
|
||||||
|
|
||||||
@ -274,9 +272,14 @@ class HintManager(QObject):
|
|||||||
else:
|
else:
|
||||||
display = 'none'
|
display = 'none'
|
||||||
rect = elem.geometry()
|
rect = elem.geometry()
|
||||||
|
left = rect.x()
|
||||||
|
top = rect.y()
|
||||||
|
if not config.get('ui', 'zoom-text-only'):
|
||||||
|
zoom = elem.webFrame().zoomFactor()
|
||||||
|
left /= zoom
|
||||||
|
top /= zoom
|
||||||
return self.HINT_CSS.format(
|
return self.HINT_CSS.format(
|
||||||
left=rect.x(), top=rect.y(), config=objreg.get('config'),
|
left=left, top=top, config=objreg.get('config'), display=display)
|
||||||
display=display)
|
|
||||||
|
|
||||||
def _draw_label(self, elem, string):
|
def _draw_label(self, elem, string):
|
||||||
"""Draw a hint label over an element.
|
"""Draw a hint label over an element.
|
||||||
|
@ -27,38 +27,46 @@ to a file on shutdown, so it makes sense to keep them as strings here.
|
|||||||
import functools
|
import functools
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
from PyQt5.QtCore import QStandardPaths, QUrl
|
from PyQt5.QtCore import pyqtSignal, QStandardPaths, QUrl, QObject
|
||||||
|
|
||||||
from qutebrowser.utils import message, usertypes, urlutils, standarddir
|
from qutebrowser.utils import message, usertypes, urlutils, standarddir
|
||||||
from qutebrowser.commands import cmdexc, cmdutils
|
from qutebrowser.commands import cmdexc, cmdutils
|
||||||
from qutebrowser.config import lineparser
|
from qutebrowser.config import lineparser
|
||||||
|
|
||||||
|
|
||||||
marks = collections.OrderedDict()
|
class QuickmarkManager(QObject):
|
||||||
linecp = None
|
|
||||||
|
|
||||||
|
"""Manager for quickmarks.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
marks: An OrderedDict of all quickmarks.
|
||||||
|
_linecp: The LineConfigParser used for the quickmarks.
|
||||||
|
"""
|
||||||
|
|
||||||
|
changed = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
"""Initialize and read quickmarks."""
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.marks = collections.OrderedDict()
|
||||||
|
|
||||||
def init():
|
|
||||||
"""Read quickmarks from the config file."""
|
|
||||||
global linecp
|
|
||||||
confdir = standarddir.get(QStandardPaths.ConfigLocation)
|
confdir = standarddir.get(QStandardPaths.ConfigLocation)
|
||||||
linecp = lineparser.LineConfigParser(confdir, 'quickmarks')
|
self._linecp = lineparser.LineConfigParser(confdir, 'quickmarks')
|
||||||
for line in linecp:
|
for line in self._linecp:
|
||||||
try:
|
try:
|
||||||
key, url = line.rsplit(maxsplit=1)
|
key, url = line.rsplit(maxsplit=1)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
message.error(0, "Invalid quickmark '{}'".format(line))
|
message.error(0, "Invalid quickmark '{}'".format(line))
|
||||||
else:
|
else:
|
||||||
marks[key] = url
|
self.marks[key] = url
|
||||||
|
|
||||||
|
def save(self):
|
||||||
def save():
|
|
||||||
"""Save the quickmarks to disk."""
|
"""Save the quickmarks to disk."""
|
||||||
linecp.data = [' '.join(tpl) for tpl in marks.items()]
|
self._linecp.data = [' '.join(tpl) for tpl in self.marks.items()]
|
||||||
linecp.save()
|
self._linecp.save()
|
||||||
|
|
||||||
|
def prompt_save(self, win_id, url):
|
||||||
def prompt_save(win_id, url):
|
|
||||||
"""Prompt for a new quickmark name to be added and add it.
|
"""Prompt for a new quickmark name to be added and add it.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -69,12 +77,12 @@ def prompt_save(win_id, url):
|
|||||||
urlutils.invalid_url_error(win_id, url, "save quickmark")
|
urlutils.invalid_url_error(win_id, url, "save quickmark")
|
||||||
return
|
return
|
||||||
urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded)
|
urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded)
|
||||||
message.ask_async(win_id, "Add quickmark:", usertypes.PromptMode.text,
|
message.ask_async(
|
||||||
functools.partial(quickmark_add, win_id, urlstr))
|
win_id, "Add quickmark:", usertypes.PromptMode.text,
|
||||||
|
functools.partial(self.quickmark_add, win_id, urlstr))
|
||||||
|
|
||||||
|
@cmdutils.register(instance='quickmark-manager')
|
||||||
@cmdutils.register()
|
def quickmark_add(self, win_id: {'special': 'win_id'}, url, name):
|
||||||
def quickmark_add(win_id: {'special': 'win_id'}, url, name):
|
|
||||||
"""Add a new quickmark.
|
"""Add a new quickmark.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -82,8 +90,8 @@ def quickmark_add(win_id: {'special': 'win_id'}, url, name):
|
|||||||
url: The url to add as quickmark.
|
url: The url to add as quickmark.
|
||||||
name: The name for the new quickmark.
|
name: The name for the new quickmark.
|
||||||
"""
|
"""
|
||||||
# We don't raise cmdexc.CommandError here as this can be called async via
|
# We don't raise cmdexc.CommandError here as this can be called async
|
||||||
# prompt_save.
|
# via prompt_save.
|
||||||
if not name:
|
if not name:
|
||||||
message.error(win_id, "Can't set mark with empty name!")
|
message.error(win_id, "Can't set mark with empty name!")
|
||||||
return
|
return
|
||||||
@ -93,25 +101,25 @@ def quickmark_add(win_id: {'special': 'win_id'}, url, name):
|
|||||||
|
|
||||||
def set_mark():
|
def set_mark():
|
||||||
"""Really set the quickmark."""
|
"""Really set the quickmark."""
|
||||||
marks[name] = url
|
self.marks[name] = url
|
||||||
|
self.changed.emit()
|
||||||
|
|
||||||
if name in marks:
|
if name in self.marks:
|
||||||
message.confirm_async(win_id, "Override existing quickmark?", set_mark,
|
message.confirm_async(
|
||||||
default=True)
|
win_id, "Override existing quickmark?", set_mark, default=True)
|
||||||
else:
|
else:
|
||||||
set_mark()
|
set_mark()
|
||||||
|
|
||||||
|
def get(self, name):
|
||||||
def get(name):
|
|
||||||
"""Get the URL of the quickmark named name as a QUrl."""
|
"""Get the URL of the quickmark named name as a QUrl."""
|
||||||
if name not in marks:
|
if name not in self.marks:
|
||||||
raise cmdexc.CommandError(
|
raise cmdexc.CommandError(
|
||||||
"Quickmark '{}' does not exist!".format(name))
|
"Quickmark '{}' does not exist!".format(name))
|
||||||
urlstr = marks[name]
|
urlstr = self.marks[name]
|
||||||
try:
|
try:
|
||||||
url = urlutils.fuzzy_url(urlstr)
|
url = urlutils.fuzzy_url(urlstr)
|
||||||
except urlutils.FuzzyUrlError:
|
except urlutils.FuzzyUrlError:
|
||||||
raise cmdexc.CommandError(
|
raise cmdexc.CommandError(
|
||||||
"Invalid URL for quickmark {}: {} ({})".format(name, urlstr,
|
"Invalid URL for quickmark {}: {} ({})".format(
|
||||||
url.errorString()))
|
name, urlstr, url.errorString()))
|
||||||
return url
|
return url
|
||||||
|
@ -59,7 +59,7 @@ class HelpAction(argparse.Action):
|
|||||||
|
|
||||||
def __call__(self, parser, _namespace, _values, _option_string=None):
|
def __call__(self, parser, _namespace, _values, _option_string=None):
|
||||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
window='current')
|
window='last-focused')
|
||||||
tabbed_browser.tabopen(
|
tabbed_browser.tabopen(
|
||||||
QUrl('qute://help/commands.html#{}'.format(parser.name)))
|
QUrl('qute://help/commands.html#{}'.format(parser.name)))
|
||||||
parser.exit()
|
parser.exit()
|
||||||
|
@ -714,6 +714,10 @@ DATA = collections.OrderedDict([
|
|||||||
('downloads.bg.system',
|
('downloads.bg.system',
|
||||||
SettingValue(typ.ColorSystem(), 'rgb'),
|
SettingValue(typ.ColorSystem(), 'rgb'),
|
||||||
"Color gradient interpolation system for downloads."),
|
"Color gradient interpolation system for downloads."),
|
||||||
|
|
||||||
|
('downloads.bg.error',
|
||||||
|
SettingValue(typ.QtColor(), 'red'),
|
||||||
|
"Background color for downloads with errors."),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
('fonts', sect.KeyValue(
|
('fonts', sect.KeyValue(
|
||||||
|
@ -35,7 +35,7 @@ class CompletionFilterModel(QSortFilterProxyModel):
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
_pattern: The pattern to filter with.
|
_pattern: The pattern to filter with.
|
||||||
_srcmodel: The current source model.
|
srcmodel: The current source model.
|
||||||
Kept as attribute because calling `sourceModel` takes quite
|
Kept as attribute because calling `sourceModel` takes quite
|
||||||
a long time for some reason.
|
a long time for some reason.
|
||||||
"""
|
"""
|
||||||
@ -43,7 +43,7 @@ class CompletionFilterModel(QSortFilterProxyModel):
|
|||||||
def __init__(self, source, parent=None):
|
def __init__(self, source, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
super().setSourceModel(source)
|
super().setSourceModel(source)
|
||||||
self._srcmodel = source
|
self.srcmodel = source
|
||||||
self._pattern = ''
|
self._pattern = ''
|
||||||
|
|
||||||
def set_pattern(self, val):
|
def set_pattern(self, val):
|
||||||
@ -61,7 +61,7 @@ class CompletionFilterModel(QSortFilterProxyModel):
|
|||||||
self.invalidateFilter()
|
self.invalidateFilter()
|
||||||
sortcol = 0
|
sortcol = 0
|
||||||
try:
|
try:
|
||||||
self._srcmodel.sort(sortcol)
|
self.srcmodel.sort(sortcol)
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
self.sort(sortcol)
|
self.sort(sortcol)
|
||||||
self.invalidate()
|
self.invalidate()
|
||||||
@ -111,14 +111,14 @@ class CompletionFilterModel(QSortFilterProxyModel):
|
|||||||
qtutils.ensure_valid(index)
|
qtutils.ensure_valid(index)
|
||||||
index = self.mapToSource(index)
|
index = self.mapToSource(index)
|
||||||
qtutils.ensure_valid(index)
|
qtutils.ensure_valid(index)
|
||||||
self._srcmodel.mark_item(index, text)
|
self.srcmodel.mark_item(index, text)
|
||||||
|
|
||||||
def setSourceModel(self, model):
|
def setSourceModel(self, model):
|
||||||
"""Override QSortFilterProxyModel's setSourceModel to clear pattern."""
|
"""Override QSortFilterProxyModel's setSourceModel to clear pattern."""
|
||||||
log.completion.debug("Setting source model: {}".format(model))
|
log.completion.debug("Setting source model: {}".format(model))
|
||||||
self.set_pattern('')
|
self.set_pattern('')
|
||||||
super().setSourceModel(model)
|
super().setSourceModel(model)
|
||||||
self._srcmodel = model
|
self.srcmodel = model
|
||||||
|
|
||||||
def filterAcceptsRow(self, row, parent):
|
def filterAcceptsRow(self, row, parent):
|
||||||
"""Custom filter implementation.
|
"""Custom filter implementation.
|
||||||
@ -135,9 +135,9 @@ class CompletionFilterModel(QSortFilterProxyModel):
|
|||||||
"""
|
"""
|
||||||
if parent == QModelIndex():
|
if parent == QModelIndex():
|
||||||
return True
|
return True
|
||||||
idx = self._srcmodel.index(row, 0, parent)
|
idx = self.srcmodel.index(row, 0, parent)
|
||||||
qtutils.ensure_valid(idx)
|
qtutils.ensure_valid(idx)
|
||||||
data = self._srcmodel.data(idx)
|
data = self.srcmodel.data(idx)
|
||||||
# TODO more sophisticated filtering
|
# TODO more sophisticated filtering
|
||||||
if not self._pattern:
|
if not self._pattern:
|
||||||
return True
|
return True
|
||||||
@ -159,14 +159,14 @@ class CompletionFilterModel(QSortFilterProxyModel):
|
|||||||
qtutils.ensure_valid(lindex)
|
qtutils.ensure_valid(lindex)
|
||||||
qtutils.ensure_valid(rindex)
|
qtutils.ensure_valid(rindex)
|
||||||
|
|
||||||
left_sort = self._srcmodel.data(lindex, role=completion.Role.sort)
|
left_sort = self.srcmodel.data(lindex, role=completion.Role.sort)
|
||||||
right_sort = self._srcmodel.data(rindex, role=completion.Role.sort)
|
right_sort = self.srcmodel.data(rindex, role=completion.Role.sort)
|
||||||
|
|
||||||
if left_sort is not None and right_sort is not None:
|
if left_sort is not None and right_sort is not None:
|
||||||
return left_sort < right_sort
|
return left_sort < right_sort
|
||||||
|
|
||||||
left = self._srcmodel.data(lindex)
|
left = self.srcmodel.data(lindex)
|
||||||
right = self._srcmodel.data(rindex)
|
right = self.srcmodel.data(rindex)
|
||||||
|
|
||||||
leftstart = left.startswith(self._pattern)
|
leftstart = left.startswith(self._pattern)
|
||||||
rightstart = right.startswith(self._pattern)
|
rightstart = right.startswith(self._pattern)
|
||||||
|
@ -181,7 +181,6 @@ class Completer(QObject):
|
|||||||
else:
|
else:
|
||||||
self._ignore_change = True
|
self._ignore_change = True
|
||||||
self.change_completed_part.emit(data, False)
|
self.change_completed_part.emit(data, False)
|
||||||
self._ignore_change = False
|
|
||||||
|
|
||||||
@pyqtSlot(str, list, int)
|
@pyqtSlot(str, list, int)
|
||||||
def on_update_completion(self, prefix, parts, cursor_part):
|
def on_update_completion(self, prefix, parts, cursor_part):
|
||||||
@ -204,6 +203,7 @@ class Completer(QObject):
|
|||||||
cursor_part: The part the cursor is currently over.
|
cursor_part: The part the cursor is currently over.
|
||||||
"""
|
"""
|
||||||
if self._ignore_change:
|
if self._ignore_change:
|
||||||
|
self._ignore_change = False
|
||||||
log.completion.debug("Ignoring completion update")
|
log.completion.debug("Ignoring completion update")
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ class Completer(QObject):
|
|||||||
|
|
||||||
log.completion.debug(
|
log.completion.debug(
|
||||||
"New completion for {}: {}, with pattern '{}'".format(
|
"New completion for {}: {}, with pattern '{}'".format(
|
||||||
parts, model._srcmodel.__class__.__name__, pattern))
|
parts, model.srcmodel.__class__.__name__, pattern))
|
||||||
|
|
||||||
if self._model().count() == 0:
|
if self._model().count() == 0:
|
||||||
completion.hide()
|
completion.hide()
|
||||||
|
@ -136,6 +136,15 @@ def _get_window_registry(window):
|
|||||||
win = app.activeWindow()
|
win = app.activeWindow()
|
||||||
if win is None or not hasattr(win, 'win_id'):
|
if win is None or not hasattr(win, 'win_id'):
|
||||||
raise RegistryUnavailableError('window')
|
raise RegistryUnavailableError('window')
|
||||||
|
elif window == 'last-focused':
|
||||||
|
try:
|
||||||
|
win = get('last-focused-main-window')
|
||||||
|
except KeyError:
|
||||||
|
try:
|
||||||
|
win = get('last-main-window')
|
||||||
|
except KeyError:
|
||||||
|
raise RegistryUnavailableError('window')
|
||||||
|
assert hasattr(win, 'registry')
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
win = window_registry[window]
|
win = window_registry[window]
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
import functools
|
import functools
|
||||||
|
|
||||||
import sip
|
import sip
|
||||||
from PyQt5.QtCore import pyqtSlot, QSize, Qt
|
from PyQt5.QtCore import pyqtSlot, QSize, Qt, QTimer
|
||||||
from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu
|
from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu
|
||||||
|
|
||||||
from qutebrowser.browser import downloads
|
from qutebrowser.browser import downloads
|
||||||
@ -44,10 +44,17 @@ def update_geometry(obj):
|
|||||||
Original bug: https://github.com/The-Compiler/qutebrowser/issues/167
|
Original bug: https://github.com/The-Compiler/qutebrowser/issues/167
|
||||||
Workaround bug: https://github.com/The-Compiler/qutebrowser/issues/171
|
Workaround bug: https://github.com/The-Compiler/qutebrowser/issues/171
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def _update_geometry():
|
||||||
|
"""Actually update the geometry if the object still exists."""
|
||||||
if sip.isdeleted(obj):
|
if sip.isdeleted(obj):
|
||||||
return
|
return
|
||||||
obj.updateGeometry()
|
obj.updateGeometry()
|
||||||
|
|
||||||
|
# If we don't use a singleShot QTimer, the geometry isn't updated correctly
|
||||||
|
# and won't include the new item.
|
||||||
|
QTimer.singleShot(0, _update_geometry)
|
||||||
|
|
||||||
|
|
||||||
class DownloadView(QListView):
|
class DownloadView(QListView):
|
||||||
|
|
||||||
|
@ -87,7 +87,6 @@ class MainWindow(QWidget):
|
|||||||
self._downloadview.show()
|
self._downloadview.show()
|
||||||
|
|
||||||
self._tabbed_browser = tabbedbrowser.TabbedBrowser(win_id)
|
self._tabbed_browser = tabbedbrowser.TabbedBrowser(win_id)
|
||||||
self._tabbed_browser.title_changed.connect(self.setWindowTitle)
|
|
||||||
objreg.register('tabbed-browser', self._tabbed_browser, scope='window',
|
objreg.register('tabbed-browser', self._tabbed_browser, scope='window',
|
||||||
window=win_id)
|
window=win_id)
|
||||||
self._vbox.addWidget(self._tabbed_browser)
|
self._vbox.addWidget(self._tabbed_browser)
|
||||||
@ -333,3 +332,4 @@ class MainWindow(QWidget):
|
|||||||
e.accept()
|
e.accept()
|
||||||
objreg.get('app').geometry = bytes(self.saveGeometry())
|
objreg.get('app').geometry = bytes(self.saveGeometry())
|
||||||
log.destroy.debug("Closing window {}".format(self.win_id))
|
log.destroy.debug("Closing window {}".format(self.win_id))
|
||||||
|
self._tabbed_browser.shutdown()
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
"""Misc. widgets used at different places."""
|
"""Misc. widgets used at different places."""
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSlot, Qt
|
from PyQt5.QtCore import pyqtSlot, Qt
|
||||||
from PyQt5.QtWidgets import QLineEdit
|
from PyQt5.QtWidgets import QLineEdit, QApplication
|
||||||
from PyQt5.QtGui import QValidator
|
from PyQt5.QtGui import QValidator, QClipboard
|
||||||
|
|
||||||
from qutebrowser.models import cmdhistory
|
from qutebrowser.models import cmdhistory
|
||||||
from qutebrowser.utils import utils
|
from qutebrowser.utils import utils
|
||||||
@ -64,6 +64,9 @@ class CommandLineEdit(QLineEdit):
|
|||||||
self.cursorPositionChanged.connect(self.__on_cursor_position_changed)
|
self.cursorPositionChanged.connect(self.__on_cursor_position_changed)
|
||||||
self._promptlen = 0
|
self._promptlen = 0
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return utils.get_repr(self, text=self.text())
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def on_text_edited(self, _text):
|
def on_text_edited(self, _text):
|
||||||
"""Slot for textEdited. Stop history browsing."""
|
"""Slot for textEdited. Stop history browsing."""
|
||||||
@ -94,8 +97,16 @@ class CommandLineEdit(QLineEdit):
|
|||||||
if mark:
|
if mark:
|
||||||
self.setSelection(self._promptlen, oldpos - self._promptlen)
|
self.setSelection(self._promptlen, oldpos - self._promptlen)
|
||||||
|
|
||||||
def __repr__(self):
|
def keyPressEvent(self, e):
|
||||||
return utils.get_repr(self, text=self.text())
|
"""Override keyPressEvent to paste primary selection on Shift + Ins."""
|
||||||
|
if e.key() == Qt.Key_Insert and e.modifiers() == Qt.ShiftModifier:
|
||||||
|
clipboard = QApplication.clipboard()
|
||||||
|
if clipboard.supportsSelection():
|
||||||
|
e.accept()
|
||||||
|
text = clipboard.text(QClipboard.Selection)
|
||||||
|
self.insert(text)
|
||||||
|
return
|
||||||
|
super().keyPressEvent(e)
|
||||||
|
|
||||||
|
|
||||||
class _CommandValidator(QValidator):
|
class _CommandValidator(QValidator):
|
||||||
|
@ -79,8 +79,6 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
start_download: Emitted when any tab wants to start downloading
|
start_download: Emitted when any tab wants to start downloading
|
||||||
something.
|
something.
|
||||||
current_tab_changed: The current tab changed to the emitted WebView.
|
current_tab_changed: The current tab changed to the emitted WebView.
|
||||||
title_changed: Emitted when the application title should be changed.
|
|
||||||
arg: The new title as string.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cur_progress = pyqtSignal(int)
|
cur_progress = pyqtSignal(int)
|
||||||
@ -96,7 +94,6 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
resized = pyqtSignal('QRect')
|
resized = pyqtSignal('QRect')
|
||||||
got_cmd = pyqtSignal(str)
|
got_cmd = pyqtSignal(str)
|
||||||
current_tab_changed = pyqtSignal(webview.WebView)
|
current_tab_changed = pyqtSignal(webview.WebView)
|
||||||
title_changed = pyqtSignal(str)
|
|
||||||
|
|
||||||
def __init__(self, win_id, parent=None):
|
def __init__(self, win_id, parent=None):
|
||||||
super().__init__(win_id, parent)
|
super().__init__(win_id, parent)
|
||||||
@ -138,9 +135,10 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
def _change_app_title(self, text):
|
def _change_app_title(self, text):
|
||||||
"""Change the window title based on the tab text."""
|
"""Change the window title based on the tab text."""
|
||||||
if not text:
|
if not text:
|
||||||
self.title_changed.emit('qutebrowser')
|
title = 'qutebrowser'
|
||||||
else:
|
else:
|
||||||
self.title_changed.emit('{} - qutebrowser'.format(text))
|
title = '{} - qutebrowser'.format(text)
|
||||||
|
self.window().setWindowTitle(title)
|
||||||
|
|
||||||
def _connect_tab_signals(self, tab):
|
def _connect_tab_signals(self, tab):
|
||||||
"""Set up the needed signals for tab."""
|
"""Set up the needed signals for tab."""
|
||||||
|
@ -127,6 +127,13 @@ class WebView(QWebView):
|
|||||||
url = utils.elide(self.url().toDisplayString(), 50)
|
url = utils.elide(self.url().toDisplayString(), 50)
|
||||||
return utils.get_repr(self, tab_id=self.tab_id, url=url)
|
return utils.get_repr(self, tab_id=self.tab_id, url=url)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
# Explicitely releasing the page here seems to prevent some segfaults
|
||||||
|
# when quitting.
|
||||||
|
# Copied from:
|
||||||
|
# https://code.google.com/p/webscraping/source/browse/webkit.py#325
|
||||||
|
self.setPage(None)
|
||||||
|
|
||||||
def _set_load_status(self, val):
|
def _set_load_status(self, val):
|
||||||
"""Setter for load_status."""
|
"""Setter for load_status."""
|
||||||
if not isinstance(val, LoadStatus):
|
if not isinstance(val, LoadStatus):
|
||||||
@ -257,15 +264,11 @@ class WebView(QWebView):
|
|||||||
"""Shut down the webview."""
|
"""Shut down the webview."""
|
||||||
# We disable javascript because that prevents some segfaults when
|
# We disable javascript because that prevents some segfaults when
|
||||||
# quitting it seems.
|
# quitting it seems.
|
||||||
|
log.destroy.debug("Shutting down {!r}.".format(self))
|
||||||
settings = self.settings()
|
settings = self.settings()
|
||||||
settings.setAttribute(QWebSettings.JavascriptEnabled, False)
|
settings.setAttribute(QWebSettings.JavascriptEnabled, False)
|
||||||
self.stop()
|
self.stop()
|
||||||
self.page().networkAccessManager().shutdown()
|
self.page().networkAccessManager().shutdown()
|
||||||
# Explicitely releasing the page here seems to prevent some segfaults
|
|
||||||
# when quitting.
|
|
||||||
# Copied from:
|
|
||||||
# https://code.google.com/p/webscraping/source/browse/webkit.py#325
|
|
||||||
self.setPage(None)
|
|
||||||
|
|
||||||
def openurl(self, url):
|
def openurl(self, url):
|
||||||
"""Open a URL in the browser.
|
"""Open a URL in the browser.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||||
|
|
||||||
# Copyright 2014 Claude (longneck) <longneck@scratchbook.ch>
|
# Copyright 2014 Claude (longneck) <longneck@scratchbook.ch>
|
||||||
|
|
||||||
@ -49,8 +50,8 @@ def get_args():
|
|||||||
def import_chromium(bookmarks_file):
|
def import_chromium(bookmarks_file):
|
||||||
"""Import bookmarks from a HTML file generated by Chromium."""
|
"""Import bookmarks from a HTML file generated by Chromium."""
|
||||||
import bs4
|
import bs4
|
||||||
|
with open(bookmarks_file, encoding='utf-8') as f:
|
||||||
soup = bs4.BeautifulSoup(open(bookmarks_file, encoding='utf-8'))
|
soup = bs4.BeautifulSoup(f)
|
||||||
|
|
||||||
html_tags = soup.findAll('a')
|
html_tags = soup.findAll('a')
|
||||||
|
|
||||||
|
@ -52,10 +52,16 @@ class OpenEncodingChecker(checkers.BaseChecker):
|
|||||||
keyword='mode')
|
keyword='mode')
|
||||||
except utils.NoSuchArgumentError:
|
except utils.NoSuchArgumentError:
|
||||||
mode_arg = None
|
mode_arg = None
|
||||||
|
_encoding = None
|
||||||
try:
|
try:
|
||||||
_encoding = utils.get_argument_from_call(node, position=2,
|
_encoding = utils.get_argument_from_call(node, position=2)
|
||||||
|
except utils.NoSuchArgumentError:
|
||||||
|
try:
|
||||||
|
_encoding = utils.get_argument_from_call(node,
|
||||||
keyword='encoding')
|
keyword='encoding')
|
||||||
except utils.NoSuchArgumentError:
|
except utils.NoSuchArgumentError:
|
||||||
|
pass
|
||||||
|
if _encoding is None:
|
||||||
if mode_arg is not None:
|
if mode_arg is not None:
|
||||||
mode = utils.safe_infer(mode_arg)
|
mode = utils.safe_infer(mode_arg)
|
||||||
if (mode_arg is not None and isinstance(mode, astroid.Const) and
|
if (mode_arg is not None and isinstance(mode, astroid.Const) and
|
||||||
|
Loading…
Reference in New Issue
Block a user