Add ToC Editor to Tweak Book

This commit is contained in:
Kovid Goyal 2013-11-08 12:39:51 +05:30
parent 89c33c3713
commit 5231d7958c
5 changed files with 158 additions and 8 deletions

View File

@ -25,6 +25,7 @@ from calibre.gui2.tweak_book import set_current_container, current_container, tp
from calibre.gui2.tweak_book.undo import GlobalUndoHistory
from calibre.gui2.tweak_book.save import SaveManager
from calibre.gui2.tweak_book.preview import parse_worker
from calibre.gui2.tweak_book.toc import TOCEditor
from calibre.gui2.tweak_book.editor import editor_from_syntax, syntax_from_mime
def get_container(*args, **kwargs):
@ -86,7 +87,7 @@ class Boss(QObject):
' Convert your book to one of these formats first.') % _(' and ').join(sorted(SUPPORTED)),
show=True)
for name in editors:
for name in tuple(editors):
self.close_editor(name)
self.gui.preview.clear()
self.container_count = -1
@ -110,12 +111,20 @@ class Boss(QObject):
self.gui.action_save.setEnabled(False)
self.update_global_history_actions()
def update_editors_from_container(self, container=None):
c = container or current_container()
for name, ed in tuple(editors.iteritems()):
if c.has_name(name):
ed.replace_data(c.raw_data(name))
else:
self.close_editor(name)
def apply_container_update_to_gui(self):
container = current_container()
self.gui.file_list.build(container)
self.update_global_history_actions()
self.gui.action_save.setEnabled(True)
# TODO: Apply to other GUI elements
self.update_editors_from_container()
def delete_requested(self, spine_items, other_items):
if not self.check_dirtied():
@ -130,7 +139,8 @@ class Boss(QObject):
for name in list(spine_items) + list(other_items):
if name in editors:
self.close_editor(name)
# TODO: Update other GUI elements
if not editors:
self.gui.preview.clear()
def reorder_spine(self, items):
# TODO: If content.opf is dirty in an editor, abort, calling
@ -142,6 +152,16 @@ class Boss(QObject):
self.gui.file_list.build(current_container()) # needed as the linear flag may have changed on some items
# TODO: If content.opf is open in an editor, reload it
def edit_toc(self):
if not self.check_dirtied():
return
self.add_savepoint(_('Edit Table of Contents'))
d = TOCEditor(parent=self.gui)
if d.exec_() != d.Accepted:
self.rewind_savepoint()
return
self.update_editors_from_container()
# Renaming {{{
def rename_requested(self, oldname, newname):
if not self.check_dirtied():
@ -176,7 +196,8 @@ class Boss(QObject):
det_msg=job.traceback, show=True)
self.gui.file_list.build(current_container())
self.gui.action_save.setEnabled(True)
# TODO: Update the rest of the GUI
# TODO: Update the rest of the GUI. This means renaming open editors and
# then calling update_editors_from_container()
# }}}
# Global history {{{
@ -335,6 +356,8 @@ class Boss(QObject):
editor = editors.pop(name)
self.gui.central.close_editor(editor)
editor.break_cycles()
if not editors:
self.gui.preview.clear()
def do_editor_save(self):
ed = self.gui.central.current_editor

View File

@ -114,6 +114,18 @@ class TextEdit(QPlainTextEdit):
self.highlighter.setDocument(self.document())
self.setPlainText(text)
def replace_text(self, text):
c = self.textCursor()
pos = c.position()
c.beginEditBlock()
c.clearSelection()
c.select(c.Document)
c.insertText(text)
c.endEditBlock()
c.setPosition(pos)
self.setTextCursor(c)
self.ensureCursorVisible()
# Line numbers and cursor line {{{
def highlight_cursor_line(self):
sel = QTextEdit.ExtraSelection()

View File

@ -9,6 +9,7 @@ __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
from PyQt4.Qt import QMainWindow, Qt, QApplication, pyqtSignal
from calibre import xml_replace_entities
from calibre.gui2 import error_dialog
from calibre.gui2.tweak_book import actions
from calibre.gui2.tweak_book.editor.text import TextEdit
@ -50,6 +51,13 @@ class Editor(QMainWindow):
def get_raw_data(self):
return unicode(self.editor.toPlainText())
def replace_data(self, raw, only_if_different=True):
if isinstance(raw, bytes):
raw = raw.decode('utf-8')
current = self.get_raw_data() if only_if_different else False
if current != raw:
self.editor.replace_text(raw)
def undo(self):
self.editor.undo()
@ -107,6 +115,9 @@ class Editor(QMainWindow):
self.editor.copy()
def paste(self):
if not self.editor.canPaste():
return error_dialog(self, _('No text'), _(
'There is no suitable text in the clipboard to paste.'), show=True)
self.editor.paste()

View File

@ -0,0 +1,96 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
from PyQt4.Qt import (QDialog, pyqtSignal, QIcon, QVBoxLayout, QDialogButtonBox, QStackedWidget)
from calibre.ebooks.oeb.polish.toc import commit_toc
from calibre.gui2 import gprefs, error_dialog
from calibre.gui2.toc.main import TOCView, ItemEdit
from calibre.gui2.tweak_book import current_container
class TOCEditor(QDialog):
explode_done = pyqtSignal(object)
writing_done = pyqtSignal(object)
def __init__(self, title=None, parent=None):
QDialog.__init__(self, parent)
t = title or current_container().mi.title
self.book_title = t
self.setWindowTitle(_('Edit the ToC in %s')%t)
self.setWindowIcon(QIcon(I('toc.png')))
l = self.l = QVBoxLayout()
self.setLayout(l)
self.stacks = s = QStackedWidget(self)
l.addWidget(s)
self.toc_view = TOCView(self)
self.toc_view.add_new_item.connect(self.add_new_item)
s.addWidget(self.toc_view)
self.item_edit = ItemEdit(self)
s.addWidget(self.item_edit)
bb = self.bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
l.addWidget(bb)
bb.accepted.connect(self.accept)
bb.rejected.connect(self.reject)
self.read_toc()
self.resize(950, 630)
geom = gprefs.get('toc_editor_window_geom', None)
if geom is not None:
self.restoreGeometry(bytes(geom))
def add_new_item(self, item, where):
self.item_edit(item, where)
self.stacks.setCurrentIndex(1)
def accept(self):
if self.stacks.currentIndex() == 1:
self.toc_view.update_item(*self.item_edit.result)
gprefs['toc_edit_splitter_state'] = bytearray(self.item_edit.splitter.saveState())
self.stacks.setCurrentIndex(0)
elif self.stacks.currentIndex() == 0:
self.write_toc()
super(TOCEditor, self).accept()
def really_accept(self, tb):
gprefs['toc_editor_window_geom'] = bytearray(self.saveGeometry())
if tb:
error_dialog(self, _('Failed to write book'),
_('Could not write %s. Click "Show details" for'
' more information.')%self.book_title, det_msg=tb, show=True)
gprefs['toc_editor_window_geom'] = bytearray(self.saveGeometry())
super(TOCEditor, self).reject()
return
super(TOCEditor, self).accept()
def reject(self):
if not self.bb.isEnabled():
return
if self.stacks.currentIndex() == 1:
gprefs['toc_edit_splitter_state'] = bytearray(self.item_edit.splitter.saveState())
self.stacks.setCurrentIndex(0)
else:
gprefs['toc_editor_window_geom'] = bytearray(self.saveGeometry())
super(TOCEditor, self).reject()
def read_toc(self):
self.toc_view(current_container())
self.item_edit.load(current_container())
self.stacks.setCurrentIndex(0)
def write_toc(self):
toc = self.toc_view.create_toc()
commit_toc(current_container(), toc, lang=self.toc_view.toc_lang,
uid=self.toc_view.toc_uid)

View File

@ -164,17 +164,21 @@ class Main(MainWindow):
_('Redo typing'))
self.action_editor_save = reg('save.png', _('&Save'), self.boss.do_editor_save, 'editor-save', 'Ctrl+S',
_('Save changes to the current file'))
self.action_editor_cut = reg('edit-cut.png', _('C&ut text'), self.boss.do_editor_cut, 'editor-cut', 'Ctrl+X',
self.action_editor_cut = reg('edit-cut.png', _('C&ut text'), self.boss.do_editor_cut, 'editor-cut', ('Ctrl+X', 'Shift+Delete', ),
_('Cut text'))
self.action_editor_copy = reg('edit-copy.png', _('&Copy text'), self.boss.do_editor_copy, 'editor-copy', 'Ctrl+C',
self.action_editor_copy = reg('edit-copy.png', _('&Copy text'), self.boss.do_editor_copy, 'editor-copy', ('Ctrl+C', 'Ctrl+Insert'),
_('Copy text'))
self.action_editor_paste = reg('edit-paste.png', _('&Paste text'), self.boss.do_editor_paste, 'editor-paste', 'Ctrl+V',
self.action_editor_paste = reg('edit-paste.png', _('&Paste text'), self.boss.do_editor_paste, 'editor-paste', ('Ctrl+V', 'Shift+Insert', ),
_('Paste text'))
self.action_editor_cut.setEnabled(False)
self.action_editor_copy.setEnabled(False)
self.action_editor_undo.setEnabled(False)
self.action_editor_redo.setEnabled(False)
# Tool actions
group = _('Tools')
self.action_toc = reg('toc.png', _('&Edit ToC'), self.boss.edit_toc, 'edit-toc', (), _('Edit Table of Contents'))
def create_menubar(self):
b = self.menuBar()
@ -194,13 +198,17 @@ class Main(MainWindow):
e.addAction(self.action_editor_copy)
e.addAction(self.action_editor_paste)
e = b.addMenu(_('&Tools'))
e.addAction(self.action_toc)
def create_toolbar(self):
self.global_bar = b = self.addToolBar(_('Global tool bar'))
self.global_bar = b = self.addToolBar(_('Book tool bar'))
b.setObjectName('global_bar') # Needed for saveState
b.addAction(self.action_open_book)
b.addAction(self.action_global_undo)
b.addAction(self.action_global_redo)
b.addAction(self.action_save)
b.addAction(self.action_toc)
def create_docks(self):
self.file_list_dock = d = QDockWidget(_('&Files Browser'), self)