mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Edit Book: Shortcut change of current paragraph to heading
Edit Book: Add a tool to easily change the current paragraph (the paragraph containing the cursor) to a heading. Simply click the button with 'H1' on it and choose the heading level you want the current paragraph changed to.
This commit is contained in:
parent
b34eb8f460
commit
1e3a46ba82
@ -6,13 +6,16 @@ from __future__ import (unicode_literals, division, absolute_import,
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
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>%s' % (new_name, text)
|
||||
else:
|
||||
text = re.sub(r'^<\s*/\s*[a-zA-Z0-9]+', '</%s' % new_name, text)
|
||||
cursor.insertText(text)
|
||||
text = select_tag(cursor, opening_tag)
|
||||
if insert:
|
||||
text += '<%s>' % 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)
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -8,13 +8,30 @@ __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
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(_('<h3>Bold</h3>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(_('<h3>Insert image</h3>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()
|
||||
|
@ -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')
|
||||
|
Loading…
x
Reference in New Issue
Block a user