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