Edit ToC: Allow the size of the panels in the location view to be adjusted

This commit is contained in:
Kovid Goyal 2013-04-13 11:13:30 +05:30
parent cd1765b935
commit 4db56199ee
2 changed files with 59 additions and 35 deletions

View File

@ -11,14 +11,14 @@ from base64 import b64encode
from PyQt4.Qt import (QWidget, QGridLayout, QListWidget, QSize, Qt, QUrl, from PyQt4.Qt import (QWidget, QGridLayout, QListWidget, QSize, Qt, QUrl,
pyqtSlot, pyqtSignal, QVBoxLayout, QFrame, QLabel, pyqtSlot, pyqtSignal, QVBoxLayout, QFrame, QLabel,
QLineEdit, QTimer, QPushButton, QIcon) QLineEdit, QTimer, QPushButton, QIcon, QSplitter)
from PyQt4.QtWebKit import QWebView, QWebPage, QWebElement from PyQt4.QtWebKit import QWebView, QWebPage, QWebElement
from calibre.ebooks.oeb.display.webview import load_html from calibre.ebooks.oeb.display.webview import load_html
from calibre.gui2 import error_dialog, question_dialog from calibre.gui2 import error_dialog, question_dialog, gprefs
from calibre.utils.logging import default_log from calibre.utils.logging import default_log
class Page(QWebPage): # {{{ class Page(QWebPage): # {{{
elem_clicked = pyqtSignal(object, object, object, object) elem_clicked = pyqtSignal(object, object, object, object)
@ -67,7 +67,7 @@ class Page(QWebPage): # {{{
self.evaljs(self.js) self.evaljs(self.js)
# }}} # }}}
class WebView(QWebView): # {{{ class WebView(QWebView): # {{{
elem_clicked = pyqtSignal(object, object, object, object) elem_clicked = pyqtSignal(object, object, object, object)
@ -106,38 +106,46 @@ class ItemEdit(QWidget):
def __init__(self, parent): def __init__(self, parent):
QWidget.__init__(self, parent) QWidget.__init__(self, parent)
self.l = l = QGridLayout() self.setLayout(QVBoxLayout())
self.setLayout(l)
self.la = la = QLabel('<b>'+_( self.la = la = QLabel('<b>'+_(
'Select a destination for the Table of Contents entry')) 'Select a destination for the Table of Contents entry'))
l.addWidget(la, 0, 0, 1, 3) self.layout().addWidget(la)
self.splitter = sp = QSplitter(self)
self.layout().addWidget(sp)
self.layout().setStretch(1, 10)
sp.setOpaqueResize(False)
sp.setChildrenCollapsible(False)
self.dest_list = dl = QListWidget(self) self.dest_list = dl = QListWidget(self)
dl.setMinimumWidth(250) dl.setMinimumWidth(250)
dl.currentItemChanged.connect(self.current_changed) dl.currentItemChanged.connect(self.current_changed)
l.addWidget(dl, 1, 0, 2, 1) sp.addWidget(dl)
w = self.w = QWidget(self)
l = w.l = QGridLayout()
w.setLayout(l)
self.view = WebView(self) self.view = WebView(self)
self.view.elem_clicked.connect(self.elem_clicked) self.view.elem_clicked.connect(self.elem_clicked)
l.addWidget(self.view, 1, 1, 1, 3) l.addWidget(self.view, 0, 0, 1, 3)
sp.addWidget(w)
self.search_text = s = QLineEdit(self)
s.setPlaceholderText(_('Search for text...'))
l.addWidget(s, 1, 0)
self.ns_button = b = QPushButton(QIcon(I('arrow-down.png')), _('Find &next'), self)
b.clicked.connect(self.find_next)
l.addWidget(b, 1, 1)
self.ps_button = b = QPushButton(QIcon(I('arrow-up.png')), _('Find &previous'), self)
l.addWidget(b, 1, 2)
b.clicked.connect(self.find_previous)
self.f = f = QFrame() self.f = f = QFrame()
f.setFrameShape(f.StyledPanel) f.setFrameShape(f.StyledPanel)
f.setMinimumWidth(250) f.setMinimumWidth(250)
l.addWidget(f, 1, 4, 2, 1)
self.search_text = s = QLineEdit(self)
s.setPlaceholderText(_('Search for text...'))
l.addWidget(s, 2, 1, 1, 1)
self.ns_button = b = QPushButton(QIcon(I('arrow-down.png')), _('Find &next'), self)
b.clicked.connect(self.find_next)
l.addWidget(b, 2, 2, 1, 1)
self.ps_button = b = QPushButton(QIcon(I('arrow-up.png')), _('Find &previous'), self)
l.addWidget(b, 2, 3, 1, 1)
b.clicked.connect(self.find_previous)
l.setRowStretch(1, 10)
l = f.l = QVBoxLayout() l = f.l = QVBoxLayout()
f.setLayout(l) f.setLayout(l)
sp.addWidget(f)
f.la = la = QLabel('<p>'+_( f.la = la = QLabel('<p>'+_(
'Here you can choose a destination for the Table of Contents\' entry' 'Here you can choose a destination for the Table of Contents\' entry'
@ -167,6 +175,10 @@ class ItemEdit(QWidget):
l.addStretch() l.addStretch()
state = gprefs.get('toc_edit_splitter_state', None)
if state is not None:
sp.restoreState(state)
def keyPressEvent(self, ev): def keyPressEvent(self, ev):
if ev.key() in (Qt.Key_Return, Qt.Key_Enter) and self.search_text.hasFocus(): if ev.key() in (Qt.Key_Return, Qt.Key_Enter) and self.search_text.hasFocus():
# Prevent pressing enter in the search box from triggering the dialog's accept() method # Prevent pressing enter in the search box from triggering the dialog's accept() method
@ -236,6 +248,7 @@ class ItemEdit(QWidget):
if item is not None: if item is not None:
if where is None: if where is None:
self.name.setText(item.data(0, Qt.DisplayRole).toString()) self.name.setText(item.data(0, Qt.DisplayRole).toString())
self.name.setCursorPosition(0)
toc = item.data(0, Qt.UserRole).toPyObject() toc = item.data(0, Qt.UserRole).toPyObject()
if toc.dest: if toc.dest:
for i in xrange(self.dest_list.count()): for i in xrange(self.dest_list.count()):
@ -272,7 +285,6 @@ class ItemEdit(QWidget):
loctext = _('Approximately %d%% from the top')%frac loctext = _('Approximately %d%% from the top')%frac
return loctext return loctext
def elem_clicked(self, tag, frac, elem_id, loc): def elem_clicked(self, tag, frac, elem_id, loc):
self.current_frag = elem_id or loc self.current_frag = elem_id or loc
base = _('Location: A &lt;%s&gt; tag inside the file')%tag base = _('Location: A &lt;%s&gt; tag inside the file')%tag

View File

@ -14,7 +14,7 @@ from functools import partial
from PyQt4.Qt import (QPushButton, QFrame, QVariant, QMenu, QInputDialog, from PyQt4.Qt import (QPushButton, QFrame, QVariant, QMenu, QInputDialog,
QDialog, QVBoxLayout, QDialogButtonBox, QSize, QStackedWidget, QWidget, QDialog, QVBoxLayout, QDialogButtonBox, QSize, QStackedWidget, QWidget,
QLabel, Qt, pyqtSignal, QIcon, QTreeWidget, QGridLayout, QTreeWidgetItem, QLabel, Qt, pyqtSignal, QIcon, QTreeWidget, QGridLayout, QTreeWidgetItem,
QToolButton, QItemSelectionModel, QCursor) QToolButton, QItemSelectionModel, QCursor, QKeySequence)
from calibre.ebooks.oeb.polish.container import get_container, AZW3Container from calibre.ebooks.oeb.polish.container import get_container, AZW3Container
from calibre.ebooks.oeb.polish.toc import ( from calibre.ebooks.oeb.polish.toc import (
@ -27,7 +27,7 @@ from calibre.utils.logging import GUILog
ICON_SIZE = 24 ICON_SIZE = 24
class XPathDialog(QDialog): # {{{ class XPathDialog(QDialog): # {{{
def __init__(self, parent): def __init__(self, parent):
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
@ -118,7 +118,7 @@ class XPathDialog(QDialog): # {{{
return [w.xpath for w in self.widgets if w.xpath.strip()] return [w.xpath for w in self.widgets if w.xpath.strip()]
# }}} # }}}
class ItemView(QFrame): # {{{ class ItemView(QFrame): # {{{
add_new_item = pyqtSignal(object, object) add_new_item = pyqtSignal(object, object)
delete_item = pyqtSignal() delete_item = pyqtSignal()
@ -207,7 +207,6 @@ class ItemView(QFrame): # {{{
))) )))
l.addWidget(b) l.addWidget(b)
l.addStretch() l.addStretch()
self.w1 = la = QLabel(_('<b>WARNING:</b> calibre only supports the ' self.w1 = la = QLabel(_('<b>WARNING:</b> calibre only supports the '
'creation of linear ToCs in AZW3 files. In a ' 'creation of linear ToCs in AZW3 files. In a '
@ -349,7 +348,9 @@ class ItemView(QFrame): # {{{
# }}} # }}}
class TreeWidget(QTreeWidget): # {{{ class TreeWidget(QTreeWidget): # {{{
edit_item = pyqtSignal()
def __init__(self, parent): def __init__(self, parent):
QTreeWidget.__init__(self, parent) QTreeWidget.__init__(self, parent)
@ -510,25 +511,30 @@ class TreeWidget(QTreeWidget): # {{{
def show_context_menu(self, point): def show_context_menu(self, point):
item = self.currentItem() item = self.currentItem()
def key(k):
sc = unicode(QKeySequence(k | Qt.CTRL).toString(QKeySequence.NativeText))
return ' [%s]'%sc
if item is not None: if item is not None:
m = QMenu() m = QMenu()
ci = unicode(item.data(0, Qt.DisplayRole).toString()) ci = unicode(item.data(0, Qt.DisplayRole).toString())
p = item.parent() or self.invisibleRootItem() p = item.parent() or self.invisibleRootItem()
idx = p.indexOfChild(item) idx = p.indexOfChild(item)
if idx > 0: if idx > 0:
m.addAction(QIcon(I('arrow-up.png')), _('Move "%s" up')%ci, self.move_up) m.addAction(QIcon(I('arrow-up.png')), (_('Move "%s" up')%ci)+key(Qt.Key_Up), self.move_up)
if idx + 1 < p.childCount(): if idx + 1 < p.childCount():
m.addAction(QIcon(I('arrow-down.png')), _('Move "%s" down')%ci, self.move_down) m.addAction(QIcon(I('arrow-down.png')), (_('Move "%s" down')%ci)+key(Qt.Key_Down), self.move_down)
m.addAction(QIcon(I('trash.png')), _('Remove all selected items'), self.del_items) m.addAction(QIcon(I('trash.png')), _('Remove all selected items'), self.del_items)
if item.parent() is not None: if item.parent() is not None:
m.addAction(QIcon(I('back.png')), _('Unindent "%s"')%ci, self.move_left) m.addAction(QIcon(I('back.png')), (_('Unindent "%s"')%ci)+key(Qt.Key_Left), self.move_left)
if idx > 0: if idx > 0:
m.addAction(QIcon(I('forward.png')), _('Indent "%s"')%ci, self.move_right) m.addAction(QIcon(I('forward.png')), (_('Indent "%s"')%ci)+key(Qt.Key_Right), self.move_right)
m.addAction(QIcon(I('edit_input.png')), _('Change the location this entry points to'), self.edit_item)
m.addAction(_('Change all selected items to title case'), self.title_case) m.addAction(_('Change all selected items to title case'), self.title_case)
m.exec_(QCursor.pos()) m.exec_(QCursor.pos())
# }}} # }}}
class TOCView(QWidget): # {{{ class TOCView(QWidget): # {{{
add_new_item = pyqtSignal(object, object) add_new_item = pyqtSignal(object, object)
@ -537,6 +543,7 @@ class TOCView(QWidget): # {{{
l = self.l = QGridLayout() l = self.l = QGridLayout()
self.setLayout(l) self.setLayout(l)
self.tocw = t = TreeWidget(self) self.tocw = t = TreeWidget(self)
self.tocw.edit_item.connect(self.edit_item)
l.addWidget(t, 0, 0, 7, 3) l.addWidget(t, 0, 0, 7, 3)
self.up_button = b = QToolButton(self) self.up_button = b = QToolButton(self)
b.setIcon(QIcon(I('arrow-up.png'))) b.setIcon(QIcon(I('arrow-up.png')))
@ -595,6 +602,9 @@ class TOCView(QWidget): # {{{
l.setColumnStretch(2, 10) l.setColumnStretch(2, 10)
def edit_item(self):
self.item_view.edit_item()
def event(self, e): def event(self, e):
if e.type() == e.StatusTip: if e.type() == e.StatusTip:
txt = unicode(e.tip()) or self.default_msg txt = unicode(e.tip()) or self.default_msg
@ -742,12 +752,12 @@ class TOCView(QWidget): # {{{
else: else:
parent = item.parent() or self.root parent = item.parent() or self.root
idx = parent.indexOfChild(item) idx = parent.indexOfChild(item)
if where == 'after': idx += 1 if where == 'after':
idx += 1
c = self.create_item(parent, child, idx=idx) c = self.create_item(parent, child, idx=idx)
self.tocw.setCurrentItem(c, 0, QItemSelectionModel.ClearAndSelect) self.tocw.setCurrentItem(c, 0, QItemSelectionModel.ClearAndSelect)
self.tocw.scrollToItem(c) self.tocw.scrollToItem(c)
def create_toc(self): def create_toc(self):
root = TOC() root = TOC()
@ -799,7 +809,7 @@ class TOCView(QWidget): # {{{
# }}} # }}}
class TOCEditor(QDialog): # {{{ class TOCEditor(QDialog): # {{{
explode_done = pyqtSignal(object) explode_done = pyqtSignal(object)
writing_done = pyqtSignal(object) writing_done = pyqtSignal(object)
@ -857,6 +867,7 @@ class TOCEditor(QDialog): # {{{
def accept(self): def accept(self):
if self.stacks.currentIndex() == 2: if self.stacks.currentIndex() == 2:
self.toc_view.update_item(*self.item_edit.result) self.toc_view.update_item(*self.item_edit.result)
gprefs['toc_edit_splitter_state'] = bytearray(self.item_edit.splitter.saveState())
self.stacks.setCurrentIndex(1) self.stacks.setCurrentIndex(1)
elif self.stacks.currentIndex() == 1: elif self.stacks.currentIndex() == 1:
self.working = False self.working = False
@ -883,6 +894,7 @@ class TOCEditor(QDialog): # {{{
if not self.bb.isEnabled(): if not self.bb.isEnabled():
return return
if self.stacks.currentIndex() == 2: if self.stacks.currentIndex() == 2:
gprefs['toc_edit_splitter_state'] = bytearray(self.item_edit.splitter.saveState())
self.stacks.setCurrentIndex(1) self.stacks.setCurrentIndex(1)
else: else:
self.working = False self.working = False
@ -938,5 +950,5 @@ if __name__ == '__main__':
d = TOCEditor(sys.argv[-1]) d = TOCEditor(sys.argv[-1])
d.start() d.start()
d.exec_() d.exec_()
del d # Needed to prevent sigsegv in exit cleanup del d # Needed to prevent sigsegv in exit cleanup