From 5bec35a40f3ec65b4c37ff25cb4a7b4c1339469b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 5 Mar 2013 14:39:39 +0530 Subject: [PATCH] More work on ToC editor, item movement now complete --- src/calibre/ebooks/oeb/polish/toc.py | 1 + src/calibre/gui2/toc/main.py | 116 ++++++++++++++++++++++++--- 2 files changed, 108 insertions(+), 9 deletions(-) diff --git a/src/calibre/ebooks/oeb/polish/toc.py b/src/calibre/ebooks/oeb/polish/toc.py index b2a0c5780d..98f381cbd4 100644 --- a/src/calibre/ebooks/oeb/polish/toc.py +++ b/src/calibre/ebooks/oeb/polish/toc.py @@ -21,6 +21,7 @@ class TOC(object): def __init__(self, title=None, dest=None, frag=None): self.title, self.dest, self.frag = title, dest, frag + if self.title: self.title = self.title.strip() self.parent = None self.children = [] diff --git a/src/calibre/gui2/toc/main.py b/src/calibre/gui2/toc/main.py index 2976eeb3a0..32e9584ea4 100644 --- a/src/calibre/gui2/toc/main.py +++ b/src/calibre/gui2/toc/main.py @@ -10,9 +10,10 @@ __docformat__ = 'restructuredtext en' import sys, os from threading import Thread -from PyQt4.Qt import (QDialog, QVBoxLayout, QDialogButtonBox, QSize, - QStackedWidget, QWidget, QLabel, Qt, pyqtSignal, QIcon, - QTreeWidget, QHBoxLayout, QTreeWidgetItem) +from PyQt4.Qt import (QPushButton, + QDialog, QVBoxLayout, QDialogButtonBox, QSize, QStackedWidget, QWidget, + QLabel, Qt, pyqtSignal, QIcon, QTreeWidget, QGridLayout, QTreeWidgetItem, + QToolButton, QItemSelectionModel) from calibre.ebooks.oeb.polish.container import get_container from calibre.ebooks.oeb.polish.toc import get_toc @@ -24,11 +25,11 @@ class TOCView(QWidget): def __init__(self, parent): QWidget.__init__(self, parent) - l = self.l = QHBoxLayout() + l = self.l = QGridLayout() self.setLayout(l) self.tocw = t = QTreeWidget(self) t.setHeaderLabel(_('Table of Contents')) - icon_size = 16 + icon_size = 32 t.setIconSize(QSize(icon_size, icon_size)) t.setDragEnabled(True) t.setSelectionMode(t.SingleSelection) @@ -38,7 +39,99 @@ class TOCView(QWidget): t.setAutoScroll(True) t.setAutoScrollMargin(icon_size*2) t.setDefaultDropAction(Qt.MoveAction) - l.addWidget(t) + t.setAutoExpandDelay(1000) + t.setAnimated(True) + t.setMouseTracking(True) + l.addWidget(t, 0, 0, 3, 3) + self.up_button = b = QToolButton(self) + b.setIcon(QIcon(I('arrow-up.png'))) + l.addWidget(b, 0, 3) + b.setToolTip(_('Move item up')) + b.clicked.connect(self.move_up) + self.down_button = b = QToolButton(self) + b.setIcon(QIcon(I('arrow-down.png'))) + l.addWidget(b, 2, 3) + b.setToolTip(_('Move item down')) + b.clicked.connect(self.move_down) + self.expand_all_button = b = QPushButton(_('&Expand all')) + l.addWidget(b, 3, 0) + b.clicked.connect(self.tocw.expandAll) + self.collapse_all_button = b = QPushButton(_('&Collapse all')) + b.clicked.connect(self.tocw.collapseAll) + l.addWidget(b, 3, 1) + self.default_msg = _('Double click on an entry to change the text') + self.hl = hl = QLabel(self.default_msg) + l.addWidget(hl, 3, 2) + + l.setColumnStretch(2, 10) + l.setRowStretch(1, 10) + + def event(self, e): + if e.type() == e.StatusTip: + txt = unicode(e.tip()) or self.default_msg + self.hl.setText(txt) + return super(TOCView, self).event(e) + + def item_title(self, item): + return unicode(item.data(0, Qt.DisplayRole).toString()) + + def move_down(self): + item = self.tocw.currentItem() + if item is None: + if self.root.childCount() == 0: + return + item = self.root.child(0) + self.tocw.setCurrentItem(item, 0, QItemSelectionModel.ClearAndSelect) + self.tocw.scrollToItem(item) + return + parent = item.parent() or self.root + idx = parent.indexOfChild(item) + if idx == parent.childCount() - 1: + # At end of parent, need to become sibling of parent + if parent is self.root: + return + gp = parent.parent() or self.root + parent.removeChild(item) + gp.insertChild(gp.indexOfChild(parent)+1, item) + else: + sibling = parent.child(idx+1) + parent.removeChild(item) + sibling.insertChild(0, item) + self.tocw.setCurrentItem(item, 0, QItemSelectionModel.ClearAndSelect) + self.tocw.scrollToItem(item) + + def move_up(self): + item = self.tocw.currentItem() + if item is None: + if self.root.childCount() == 0: + return + item = self.root.child(self.root.childCount()-1) + self.tocw.setCurrentItem(item, 0, QItemSelectionModel.ClearAndSelect) + self.tocw.scrollToItem(item) + return + parent = item.parent() or self.root + idx = parent.indexOfChild(item) + if idx == 0: + # At end of parent, need to become sibling of parent + if parent is self.root: + return + gp = parent.parent() or self.root + parent.removeChild(item) + gp.insertChild(gp.indexOfChild(parent), item) + else: + sibling = parent.child(idx-1) + parent.removeChild(item) + sibling.addChild(item) + self.tocw.setCurrentItem(item, 0, QItemSelectionModel.ClearAndSelect) + self.tocw.scrollToItem(item) + + def update_status_tip(self, item): + c = item.data(0, Qt.UserRole).toPyObject() + frag = c.frag or '' + if frag: + frag = '#'+frag + item.setStatusTip(0, _('Title: {0} Dest: {1}{2}').format( + c.title, c.dest, frag)) def data_changed(self, top_left, bottom_right): for r in xrange(top_left.row(), bottom_right.row()+1): @@ -46,11 +139,13 @@ class TOCView(QWidget): new_title = unicode(idx.data(Qt.DisplayRole).toString()).strip() toc = idx.data(Qt.UserRole).toPyObject() toc.title = new_title or _('(Untitled)') + item = self.tocw.itemFromIndex(idx) + self.update_status_tip(item) def __call__(self, ebook): self.ebook = ebook self.toc = get_toc(self.ebook) - blank = QIcon(I('blank.png')) + blank = self.blank = QIcon(I('blank.png')) def process_item(node, parent): for child in node: @@ -60,9 +155,10 @@ class TOCView(QWidget): c.setFlags(Qt.ItemIsDragEnabled|Qt.ItemIsEditable|Qt.ItemIsEnabled| Qt.ItemIsSelectable|Qt.ItemIsDropEnabled) c.setData(0, Qt.DecorationRole, blank) + self.update_status_tip(c) process_item(child, c) - root = self.tocw.invisibleRootItem() + root = self.root = self.tocw.invisibleRootItem() root.setData(0, Qt.UserRole, self.toc) process_item(self.toc, root) self.tocw.model().dataChanged.connect(self.data_changed) @@ -86,7 +182,7 @@ class TOCEditor(QDialog): l.addWidget(s) self.loading_widget = lw = QWidget(self) s.addWidget(lw) - ll = QVBoxLayout() + ll = self.ll = QVBoxLayout() lw.setLayout(ll) self.pi = pi = ProgressIndicator() pi.setDisplaySize(200) @@ -120,6 +216,7 @@ class TOCEditor(QDialog): self.explode_done.emit() def read_toc(self): + self.pi.stopAnimation() self.toc_view(self.ebook) self.stacks.setCurrentIndex(1) @@ -129,4 +226,5 @@ if __name__ == '__main__': d = TOCEditor(sys.argv[-1]) d.start() d.exec_() + del d # Needed to prevent sigsegv in exit cleanup