diff --git a/src/calibre/gui2/tweak_book/editor/smart/html.py b/src/calibre/gui2/tweak_book/editor/smart/html.py index 0036680232..13a390e4b0 100644 --- a/src/calibre/gui2/tweak_book/editor/smart/html.py +++ b/src/calibre/gui2/tweak_book/editor/smart/html.py @@ -6,13 +6,16 @@ from __future__ import (unicode_literals, division, absolute_import, __license__ = 'GPL v3' __copyright__ = '2014, Kovid Goyal ' -import sys +import sys, re from operator import itemgetter from . import NullSmarts from PyQt4.Qt import QTextEdit +from calibre.gui2 import error_dialog + get_offset = itemgetter(0) +PARAGRAPH_SEPARATOR = '\u2029' class Tag(object): @@ -104,6 +107,27 @@ def find_closing_tag(tag, max_tags=sys.maxint): max_tags -= 1 return None +def select_tag(cursor, tag): + cursor.setPosition(tag.start_block.position() + tag.start_offset) + cursor.setPosition(tag.end_block.position() + tag.end_offset + 1, cursor.KeepAnchor) + return unicode(cursor.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n') + +def rename_tag(cursor, opening_tag, closing_tag, new_name, insert=False): + cursor.beginEditBlock() + text = select_tag(cursor, closing_tag) + if insert: + text = '%s' % (new_name, text) + else: + text = re.sub(r'^<\s*/\s*[a-zA-Z0-9]+', '' % new_name + else: + text = re.sub(r'^<\s*[a-zA-Z0-9]+', '<%s' % new_name, text) + cursor.insertText(text) + cursor.endEditBlock() + class HTMLSmarts(NullSmarts): def get_extra_selections(self, editor): @@ -126,3 +150,34 @@ class HTMLSmarts(NullSmarts): add_tag(tag) return ans + def rename_block_tag(self, editor, new_name): + c = editor.textCursor() + block, offset = c.block(), c.positionInBlock() + tag = None + + while True: + tag = find_closest_containing_tag(block, offset) + if tag is None: + break + block, offset = tag.start_block, tag.start_offset + if tag.name in { + 'address', 'article', 'aside', 'blockquote', 'center', + 'dir', 'fieldset', 'isindex', 'menu', 'noframes', 'hgroup', + 'noscript', 'pre', 'section', 'h1', 'h2', 'h3', 'h4', 'h5', + 'h6', 'header', 'p', 'div', 'dd', 'dl', 'ul', 'ol', 'li', 'body', + 'td', 'th'}: + break + tag = None + + if tag is not None: + closing_tag = find_closing_tag(tag) + if closing_tag is None: + return error_dialog(editor, _('Invalid HTML'), _( + 'There is an unclosed %s tag. You should run the Fix HTML tool' + ' before trying to rename tags.') % tag.name, show=True) + rename_tag(c, tag, closing_tag, new_name, insert=tag.name in {'body', 'td', 'th', 'li'}) + else: + return error_dialog(editor, _('No found'), _( + 'No suitable block level tag was found to rename'), show=True) + + diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index be3fb8aae6..4012bee6f7 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -612,3 +612,7 @@ class TextEdit(PlainTextEdit): c.movePosition(c.End, c.KeepAnchor) self.setTextCursor(c) + def rename_block_tag(self, new_name): + if hasattr(self.smarts, 'rename_block_tag'): + self.smarts.rename_block_tag(self, new_name) + diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index 399c28f71d..b8092cf4ba 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -8,13 +8,30 @@ __copyright__ = '2013, Kovid Goyal ' import unicodedata -from PyQt4.Qt import QMainWindow, Qt, QApplication, pyqtSignal, QMenu +from PyQt4.Qt import ( + QMainWindow, Qt, QApplication, pyqtSignal, QMenu, qDrawShadeRect, QPainter, + QImage, QColor, QIcon, QPixmap, QToolButton) from calibre.gui2 import error_dialog from calibre.gui2.tweak_book import actions, current_container from calibre.gui2.tweak_book.editor.text import TextEdit -def register_text_editor_actions(reg): +def create_icon(text, palette=None, sz=32, divider=2): + if palette is None: + palette = QApplication.palette() + img = QImage(sz, sz, QImage.Format_ARGB32) + img.fill(Qt.transparent) + p = QPainter(img) + p.setRenderHints(p.TextAntialiasing | p.Antialiasing) + qDrawShadeRect(p, img.rect(), palette, fill=QColor('#ffffff'), lineWidth=1, midLineWidth=1) + f = p.font() + f.setFamily('Liberation Sans'), f.setPixelSize(sz // divider), f.setBold(True) + p.setFont(f), p.setPen(Qt.black) + p.drawText(img.rect().adjusted(2, 2, -2, -2), Qt.AlignCenter, text) + p.end() + return QIcon(QPixmap.fromImage(img)) + +def register_text_editor_actions(reg, palette): ac = reg('format-text-bold', _('&Bold'), ('format_text', 'bold'), 'format-text-bold', 'Ctrl+B', _('Make the selected text bold')) ac.setToolTip(_('

Bold

Make the selected text bold')) ac = reg('format-text-italic', _('&Italic'), ('format_text', 'italic'), 'format-text-italic', 'Ctrl+I', _('Make the selected text italic')) @@ -39,6 +56,12 @@ def register_text_editor_actions(reg): ac = reg('view-image', _('&Insert image'), ('insert_resource', 'image'), 'insert-image', (), _('Insert an image into the text')) ac.setToolTip(_('

Insert image

Insert an image into the text')) + for i, name in enumerate(('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p')): + text = ('&' + name) if name == 'p' else (name[0] + '&' + name[1]) + desc = _('Convert the paragraph to <%s>') % name + ac = reg(create_icon(name), text, ('rename_block_tag', name), 'rename-block-tag-' + name, 'Ctrl+%d' % (i + 1), desc) + ac.setToolTip(desc) + class Editor(QMainWindow): has_line_numbers = True @@ -176,6 +199,12 @@ class Editor(QMainWindow): self.format_bar = b = self.addToolBar(_('Format text')) for x in ('bold', 'italic', 'underline', 'strikethrough', 'subscript', 'superscript', 'color', 'background-color'): b.addAction(actions['format-text-%s' % x]) + ac = b.addAction(QIcon(I('format-text-heading.png')), _('Change paragraph to heading')) + m = QMenu() + ac.setMenu(m) + b.widgetForAction(ac).setPopupMode(QToolButton.InstantPopup) + for name in tuple('h%d' % d for d in range(1, 7)) + ('p',): + m.addAction(actions['rename-block-tag-%s' % name]) def break_cycles(self): self.modification_state_changed.disconnect() diff --git a/src/calibre/gui2/tweak_book/ui.py b/src/calibre/gui2/tweak_book/ui.py index d0308de87d..8a78e73b45 100644 --- a/src/calibre/gui2/tweak_book/ui.py +++ b/src/calibre/gui2/tweak_book/ui.py @@ -270,7 +270,9 @@ class Main(MainWindow): group = _('Global Actions') def reg(icon, text, target, sid, keys, description): - ac = actions[sid] = QAction(QIcon(I(icon)), text, self) if icon else QAction(text, self) + if not isinstance(icon, QIcon): + icon = QIcon(I(icon)) + ac = actions[sid] = QAction(icon, text, self) if icon else QAction(text, self) ac.setObjectName('action-' + sid) if target is not None: ac.triggered.connect(target) @@ -315,7 +317,7 @@ class Main(MainWindow): def ereg(icon, text, target, sid, keys, description): return reg(icon, text, partial(self.boss.editor_action, target), sid, keys, description) - register_text_editor_actions(ereg) + register_text_editor_actions(ereg, self.palette()) # Tool actions group = _('Tools')