summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <git@the-compiler.org>2014-12-10 20:48:19 +0100
committerFlorian Bruhin <git@the-compiler.org>2014-12-10 20:48:19 +0100
commit9553bd44c1bcfe34bcbf0030b2b653704db249a2 (patch)
tree172288fef4253a2c3e2ca3db0aaaedfb41df8dfe
parent0ecf8661ebd9d5a8b6ad5e03b043df346c297499 (diff)
downloadqutebrowser-9553bd44c1bcfe34bcbf0030b2b653704db249a2.tar.gz
qutebrowser-9553bd44c1bcfe34bcbf0030b2b653704db249a2.zip
Add an open menu entry to downloads.
See #39.
-rw-r--r--qutebrowser/browser/downloads.py60
-rw-r--r--qutebrowser/config/configdata.py4
-rw-r--r--qutebrowser/widgets/downloads.py12
3 files changed, 56 insertions, 20 deletions
diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py
index 41d47ea65..5fa861f05 100644
--- a/qutebrowser/browser/downloads.py
+++ b/qutebrowser/browser/downloads.py
@@ -28,7 +28,8 @@ import collections
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QObject, QTimer,
QStandardPaths, Qt, QVariant, QAbstractListModel,
- QModelIndex)
+ QModelIndex, QUrl)
+from PyQt5.QtGui import QDesktopServices
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
# We need this import so PyQt can use it inside pyqtSlot
from PyQt5.QtWebKitWidgets import QWebPage # pylint: disable=unused-import
@@ -151,6 +152,7 @@ class DownloadItem(QObject):
MAX_REDIRECTS: The maximum redirection count.
Attributes:
+ done: Whether the download is finished.
stats: A DownloadItemStats object.
successful: Whether the download has completed sucessfully.
error_msg: The current error message, or None
@@ -190,6 +192,7 @@ class DownloadItem(QObject):
reply: The QNetworkReply to download.
"""
super().__init__(parent)
+ self.done = False
self.stats = DownloadItemStats(self)
self.stats.updated.connect(self.data_changed)
self.autoclose = True
@@ -234,10 +237,15 @@ class DownloadItem(QObject):
else:
remaining = utils.format_seconds(remaining)
total = utils.format_size(self.stats.total, suffix='B')
- return ('{name} [{speed:>10}|{remaining:>5}|{perc:>2}%|'
- '{down}/{total}]{errmsg}'.format(
- name=self.basename, speed=speed, remaining=remaining,
- perc=perc, down=down, total=total, errmsg=errmsg))
+ if self.done:
+ return ('{name} [{perc:>2}%|{total}]{errmsg}'.format(
+ name=self.basename, perc=perc, total=total,
+ errmsg=errmsg))
+ else:
+ return ('{name} [{speed:>10}|{remaining:>5}|{perc:>2}%|'
+ '{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."""
@@ -253,6 +261,7 @@ class DownloadItem(QObject):
self.reply.abort()
self.reply.deleteLater()
self.reply = None
+ self.done = True
if self.fileobj is not None:
try:
self.fileobj.close()
@@ -312,7 +321,15 @@ class DownloadItem(QObject):
os.remove(self._filename)
except OSError:
log.downloads.exception("Failed to remove partial file")
+ self.done = True
self.finished.emit()
+ self.data_changed.emit()
+
+ def open_file(self):
+ """Open the downloaded file."""
+ assert self.successful
+ url = QUrl.fromLocalFile(self._filename)
+ QDesktopServices.openUrl(url)
def set_filename(self, filename):
"""Set the filename to save the download to.
@@ -393,7 +410,9 @@ class DownloadItem(QObject):
self.reply.deleteLater()
self.reply = None
self.finished.emit()
+ self.done = True
log.downloads.debug("Download finished")
+ self.data_changed.emit()
@pyqtSlot()
def on_reply_finished(self):
@@ -620,8 +639,11 @@ class DownloadManager(QAbstractListModel):
log.downloads.debug("fetch: {} -> {}".format(reply.url(),
suggested_filename))
download = DownloadItem(reply, self)
- download.finished.connect(
- functools.partial(self.on_finished, download))
+ download.cancelled.connect(
+ functools.partial(self.remove_item, download))
+ if config.get('ui', 'remove-finished-downloads'):
+ download.finished.connect(
+ functools.partial(self.remove_item, download))
download.data_changed.connect(
functools.partial(self.on_data_changed, download))
download.error.connect(self.on_error)
@@ -681,19 +703,13 @@ class DownloadManager(QAbstractListModel):
download.init_reply(new_reply)
@pyqtSlot(DownloadItem)
- def on_finished(self, download):
- """Remove finished download."""
- log.downloads.debug("on_finished: {}".format(download))
- idx = self.downloads.index(download)
- self.beginRemoveRows(QModelIndex(), idx, idx)
- del self.downloads[idx]
- self.endRemoveRows()
- download.deleteLater()
-
- @pyqtSlot(DownloadItem)
def on_data_changed(self, download):
"""Emit data_changed signal when download data changed."""
- idx = self.downloads.index(download)
+ try:
+ idx = self.downloads.index(download)
+ except ValueError:
+ # download has been deleted in the meantime
+ return
model_idx = self.index(idx, 0)
qtutils.ensure_valid(model_idx)
self.dataChanged.emit(model_idx, model_idx)
@@ -726,6 +742,14 @@ class DownloadManager(QAbstractListModel):
idx = self.index(self.rowCount() - 1)
return idx
+ def remove_item(self, download):
+ """Remove a given download."""
+ idx = self.downloads.index(download)
+ self.beginRemoveRows(QModelIndex(), idx, idx)
+ del self.downloads[idx]
+ self.endRemoveRows()
+ download.deleteLater()
+
def headerData(self, section, orientation, role):
"""Simple constant header."""
if (section == 0 and orientation == Qt.Horizontal and
diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py
index 84d063117..aba5694d4 100644
--- a/qutebrowser/config/configdata.py
+++ b/qutebrowser/config/configdata.py
@@ -228,6 +228,10 @@ DATA = collections.OrderedDict([
('css-media-type',
SettingValue(typ.String(none_ok=True), ''),
"Set the CSS media type."),
+
+ ('remove-finished-downloads',
+ SettingValue(typ.Bool(), 'false'),
+ "Whether to remove finished downloads automatically."),
)),
('network', sect.KeyValue(
diff --git a/qutebrowser/widgets/downloads.py b/qutebrowser/widgets/downloads.py
index 66aeb6076..7f6f43005 100644
--- a/qutebrowser/widgets/downloads.py
+++ b/qutebrowser/widgets/downloads.py
@@ -110,8 +110,16 @@ class DownloadView(QListView):
return
item = self.model().data(index, downloads.ModelRole.item)
self._menu = QMenu(self)
- cancel = self._menu.addAction("Cancel")
- cancel.triggered.connect(item.cancel)
+ if item.done:
+ if item.successful:
+ open_action = self._menu.addAction("Open")
+ open_action.triggered.connect(item.open_file)
+ remove = self._menu.addAction("Remove")
+ remove.triggered.connect(functools.partial(
+ self.model().remove_item, item))
+ else:
+ cancel = self._menu.addAction("Cancel")
+ cancel.triggered.connect(item.cancel)
self._menu.popup(self.viewport().mapToGlobal(point))
def minimumSizeHint(self):