ToC view for Edit Book

This commit is contained in:
Kovid Goyal 2013-12-12 18:53:16 +05:30
parent 75abe74e20
commit 41c0926f23
5 changed files with 143 additions and 21 deletions

View File

@ -37,3 +37,4 @@ class NonReplaceDict(dict):
actions = NonReplaceDict()
editors = NonReplaceDict()
TOP = object()

View File

@ -104,6 +104,7 @@ class Boss(QObject):
self.gui.check_book.item_activated.connect(self.check_item_activated)
self.gui.check_book.check_requested.connect(self.check_requested)
self.gui.check_book.fix_requested.connect(self.fix_requested)
self.gui.toc_view.navigate_requested.connect(self.link_clicked)
def preferences(self):
p = Preferences(self.gui)
@ -188,6 +189,7 @@ class Boss(QObject):
parse_worker.clear()
container = job.result
set_current_container(container)
with BusyCursor():
self.current_metadata = self.gui.current_metadata = container.mi
self.global_undo.open_book(container)
self.gui.update_window_title()
@ -204,6 +206,7 @@ class Boss(QObject):
self.gui.update_recent_books()
if ef:
self.gui.file_list.request_edit(ef)
self.gui.toc_view.update_if_visible()
def update_editors_from_container(self, container=None):
c = container or current_container()
@ -291,7 +294,9 @@ class Boss(QObject):
if d.exec_() != d.Accepted:
self.rewind_savepoint()
return
with BusyCursor():
self.update_editors_from_container()
self.gui.toc_view.update_if_visible()
def polish(self, action, name):
self.commit_all_editors_to_container()
@ -698,6 +703,8 @@ class Boss(QObject):
@in_thread_job
def link_clicked(self, name, anchor):
if not name:
return
if name in editors:
editor = editors[name]
self.gui.central.show_editor(editor)

View File

@ -15,7 +15,7 @@ from PyQt4.Qt import (
QTextEdit, QTextFormat, QWidget, QSize, QPainter, Qt, QRect, pyqtSlot,
QApplication, QMimeData)
from calibre.gui2.tweak_book import tprefs
from calibre.gui2.tweak_book import tprefs, TOP
from calibre.gui2.tweak_book.editor import SYNTAX_PROPERTY
from calibre.gui2.tweak_book.editor.themes import THEMES, default_theme, theme_color
from calibre.gui2.tweak_book.editor.syntax.base import SyntaxHighlighter
@ -332,6 +332,11 @@ class TextEdit(QPlainTextEdit):
return True
def go_to_anchor(self, anchor):
if anchor is TOP:
c = self.textCursor()
c.movePosition(c.Start)
self.setTextCursor(c)
return True
base = r'''%%s\s*=\s*['"]{0,1}%s''' % regex.escape(anchor)
raw = unicode(self.toPlainText())
m = regex.search(base % 'id', raw)

View File

@ -6,12 +6,15 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
from PyQt4.Qt import (QDialog, pyqtSignal, QIcon, QVBoxLayout, QDialogButtonBox, QStackedWidget)
from PyQt4.Qt import (
QDialog, pyqtSignal, QIcon, QVBoxLayout, QDialogButtonBox, QStackedWidget,
QAction, QMenu, QTreeWidget, QTreeWidgetItem, QGridLayout, QWidget, Qt,
QSize, QStyledItemDelegate, QTimer)
from calibre.ebooks.oeb.polish.toc import commit_toc
from calibre.ebooks.oeb.polish.toc import commit_toc, get_toc
from calibre.gui2 import gprefs, error_dialog
from calibre.gui2.toc.main import TOCView, ItemEdit
from calibre.gui2.tweak_book import current_container
from calibre.gui2.tweak_book import current_container, TOP
class TOCEditor(QDialog):
@ -94,3 +97,100 @@ class TOCEditor(QDialog):
commit_toc(current_container(), toc, lang=self.toc_view.toc_lang,
uid=self.toc_view.toc_uid)
DEST_ROLE = Qt.UserRole
FRAG_ROLE = DEST_ROLE + 1
class Delegate(QStyledItemDelegate):
def sizeHint(self, *args):
ans = QStyledItemDelegate.sizeHint(self, *args)
return ans + QSize(0, 10)
class TOCViewer(QWidget):
navigate_requested = pyqtSignal(object, object)
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.l = l = QGridLayout(self)
self.setLayout(l)
l.setContentsMargins(0, 0, 0, 0)
self.is_visible = False
self.view = QTreeWidget(self)
self.delegate = Delegate(self.view)
self.view.setItemDelegate(self.delegate)
self.view.setHeaderHidden(True)
self.view.setAnimated(True)
self.view.setContextMenuPolicy(Qt.CustomContextMenu)
self.view.customContextMenuRequested.connect(self.show_context_menu, type=Qt.QueuedConnection)
self.view.itemActivated.connect(self.emit_navigate)
self.view.itemClicked.connect(self.emit_navigate)
l.addWidget(self.view)
self.refresh_action = QAction(QIcon(I('view-refresh.png')), _('&Refresh'), self)
self.refresh_action.triggered.connect(self.build)
self._last_nav_request = None
def show_context_menu(self, pos):
menu = QMenu(self)
menu.addAction(self.refresh_action)
menu.addAction(_('&Expand all'), self.view.expandAll)
menu.addAction(_('&Collapse all'), self.view.collapseAll)
menu.exec_(self.view.mapToGlobal(pos))
def iteritems(self, parent=None):
if parent is None:
parent = self.invisibleRootItem()
for i in xrange(parent.childCount()):
child = parent.child(i)
yield child
for gc in self.iteritems(parent=child):
yield gc
def emit_navigate(self, *args):
item = self.view.currentItem()
if item is not None:
dest = unicode(item.data(0, DEST_ROLE).toString())
frag = unicode(item.data(0, FRAG_ROLE).toString())
if not frag:
frag = TOP
# Debounce as on some platforms clicking causes both itemActivated
# and itemClicked to be emitted
self._last_nav_request = (dest, frag)
QTimer.singleShot(0, self._emit_navigate)
def _emit_navigate(self):
if self._last_nav_request is not None:
self.navigate_requested.emit(*self._last_nav_request)
self._last_nav_request = None
def build(self):
c = current_container()
if c is None:
return
toc = get_toc(c, verify_destinations=False)
def process_node(toc, parent):
for child in toc:
node = QTreeWidgetItem(parent)
node.setText(0, child.title or '')
node.setData(0, DEST_ROLE, child.dest or '')
node.setData(0, FRAG_ROLE, child.frag or '')
tt = _('File: {0}\nAnchor: {1}').format(
child.dest or '', child.frag or '')
node.setData(0, Qt.ToolTipRole, tt)
process_node(child, node)
self.view.clear()
process_node(toc, self.view.invisibleRootItem())
def visibility_changed(self, visible):
self.is_visible = visible
if visible:
self.build()
def update_if_visible(self):
if self.is_visible:
self.build()

View File

@ -25,6 +25,7 @@ from calibre.gui2.tweak_book.boss import Boss
from calibre.gui2.tweak_book.preview import Preview
from calibre.gui2.tweak_book.search import SearchPanel
from calibre.gui2.tweak_book.check import Check
from calibre.gui2.tweak_book.toc import TOCViewer
class Central(QStackedWidget):
@ -197,6 +198,7 @@ class Main(MainWindow):
self.central = Central(self)
self.setCentralWidget(self.central)
self.check_book = Check(self)
self.toc_view = TOCViewer(self)
self.create_actions()
self.create_toolbars()
@ -506,6 +508,13 @@ class Main(MainWindow):
d.close() # By default the inspector window is closed
d.setFeatures(d.DockWidgetClosable | d.DockWidgetMovable) # QWebInspector does not work in a floating dock
d = create(_('Table of Contents'), 'toc-viewer')
d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.BottomDockWidgetArea | Qt.TopDockWidgetArea)
d.setWidget(self.toc_view)
self.addDockWidget(Qt.LeftDockWidgetArea, d)
d.close() # Hidden by default
d.visibilityChanged.connect(self.toc_view.visibility_changed)
def resizeEvent(self, ev):
self.blocking_job.resize(ev.size())
return super(Main, self).resizeEvent(ev)