diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index f7a87ba800..e92e596801 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -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 diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 91efa90f8e..39845d15d1 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -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() diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index f560aeb41e..b959a2d47c 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -9,6 +9,7 @@ __copyright__ = '2013, Kovid Goyal ' 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() diff --git a/src/calibre/gui2/tweak_book/toc.py b/src/calibre/gui2/tweak_book/toc.py new file mode 100644 index 0000000000..fa828a4f0f --- /dev/null +++ b/src/calibre/gui2/tweak_book/toc.py @@ -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 ' + +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) + diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index b2ad06d9e3..cfaa8391a8 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -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)