Refactor :undo and save/restore history.

This commit is contained in:
Florian Bruhin 2014-09-27 22:56:50 +02:00
parent 03e809a230
commit 4410536f69
3 changed files with 50 additions and 9 deletions

View File

@ -511,10 +511,9 @@ class CommandDispatcher:
@cmdutils.register(instance='command-dispatcher') @cmdutils.register(instance='command-dispatcher')
def undo(self): def undo(self):
"""Re-open a closed tab (optionally skipping [count] closed tabs).""" """Re-open a closed tab (optionally skipping [count] closed tabs)."""
url_stack = objreg.get('url-stack', None) try:
if url_stack: objreg.get('tabbed-browser').undo()
objreg.get('tabbed-browser').tabopen(url_stack.pop()) except IndexError:
else:
raise cmdexc.CommandError("Nothing to undo!") raise cmdexc.CommandError("Nothing to undo!")
@cmdutils.register(instance='command-dispatcher') @cmdutils.register(instance='command-dispatcher')

View File

@ -32,7 +32,8 @@ import sys
import operator import operator
from distutils.version import StrictVersion as Version from distutils.version import StrictVersion as Version
from PyQt5.QtCore import QEventLoop, qVersion from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray,
QIODevice)
MAXVALS = { MAXVALS = {
@ -128,6 +129,36 @@ def ensure_valid(obj):
raise QtValueError(obj) raise QtValueError(obj)
def _check_qdatastream(stream):
"""Check the status of a QDataStream and raise IOError if it's not ok."""
status_to_str = {
QDataStream.Ok: "The data stream is operating normally.",
QDataStream.ReadPastEnd: ("The data stream has read past the end of "
"the data in the underlying device."),
QDataStream.ReadCorruptData: "The data stream has read corrupt data.",
QDataStream.WriteFailed: ("The data stream cannot write to the "
"underlying device."),
}
if stream.status() != QDataStream.Ok:
raise IOError(status_to_str[stream.status()])
def serialize(obj):
"""Serialize an object into a QByteArray."""
data = QByteArray()
stream = QDataStream(data, QIODevice.WriteOnly)
stream << obj
_check_qdatastream(stream)
return data
def deserialize(data, obj):
"""Deserialize an object from a QByteArray."""
stream = QDataStream(data, QIODevice.ReadOnly)
stream >> obj
_check_qdatastream(stream)
class QtValueError(ValueError): class QtValueError(ValueError):
"""Exception which gets raised by ensure_valid.""" """Exception which gets raised by ensure_valid."""

View File

@ -20,6 +20,7 @@
"""The main tabbed browser widget.""" """The main tabbed browser widget."""
import functools import functools
import collections
from PyQt5.QtWidgets import QSizePolicy from PyQt5.QtWidgets import QSizePolicy
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QSize, QTimer, QUrl from PyQt5.QtCore import pyqtSignal, pyqtSlot, QSize, QTimer, QUrl
@ -34,6 +35,9 @@ from qutebrowser.browser import signalfilter, commands
from qutebrowser.utils import log, message, usertypes, utils, qtutils, objreg from qutebrowser.utils import log, message, usertypes, utils, qtutils, objreg
UndoEntry = collections.namedtuple('UndoEntry', ['url', 'history'])
class TabbedBrowser(tabwidget.TabWidget): class TabbedBrowser(tabwidget.TabWidget):
"""A TabWidget with QWebViews inside. """A TabWidget with QWebViews inside.
@ -53,7 +57,7 @@ class TabbedBrowser(tabwidget.TabWidget):
_tab_insert_idx_left: Where to insert a new tab with _tab_insert_idx_left: Where to insert a new tab with
tabbar -> new-tab-position set to 'left'. tabbar -> new-tab-position set to 'left'.
_tab_insert_idx_right: Same as above, for 'right'. _tab_insert_idx_right: Same as above, for 'right'.
_url_stack: Stack of URLs of closed tabs. _undo_stack: List of UndoEntry namedtuples of closed tabs.
Signals: Signals:
cur_progress: Progress of the current tab changed (loadProgress). cur_progress: Progress of the current tab changed (loadProgress).
@ -102,8 +106,7 @@ class TabbedBrowser(tabwidget.TabWidget):
self.cur_load_started.connect(self.on_cur_load_started) self.cur_load_started.connect(self.on_cur_load_started)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self._tabs = [] self._tabs = []
self._url_stack = [] self._undo_stack = []
objreg.register('url-stack', self._url_stack)
self._filter = signalfilter.SignalFilter(self) self._filter = signalfilter.SignalFilter(self)
dispatcher = commands.CommandDispatcher() dispatcher = commands.CommandDispatcher()
objreg.register('command-dispatcher', dispatcher) objreg.register('command-dispatcher', dispatcher)
@ -244,12 +247,20 @@ class TabbedBrowser(tabwidget.TabWidget):
objreg.delete('last-focused-tab') objreg.delete('last-focused-tab')
if not tab.cur_url.isEmpty(): if not tab.cur_url.isEmpty():
qtutils.ensure_valid(tab.cur_url) qtutils.ensure_valid(tab.cur_url)
self._url_stack.append(tab.cur_url) history_data = qtutils.serialize(tab.history())
entry = UndoEntry(tab.cur_url, history_data)
self._undo_stack.append(entry)
tab.shutdown() tab.shutdown()
self._tabs.remove(tab) self._tabs.remove(tab)
self.removeTab(idx) self.removeTab(idx)
tab.deleteLater() tab.deleteLater()
def undo(self):
"""Undo removing of a tab."""
url, history_data = self._undo_stack.pop()
newtab = self.tabopen(url)
qtutils.deserialize(history_data, newtab.history())
@pyqtSlot('QUrl', bool) @pyqtSlot('QUrl', bool)
def openurl(self, url, newtab): def openurl(self, url, newtab):
"""Open a URL, used as a slot. """Open a URL, used as a slot.