mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Edit Book: Add a button to easily insert HTML tags. Useful if you want to quickly surround selected text with an arbitrary tag. You can right click the button to get a list of recently used tags.
This commit is contained in:
parent
fc4805f699
commit
b078ab5b51
1
imgsrc/code.svg
Normal file
1
imgsrc/code.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="32px" id="Layer_1" style="enable-background:new 0 0 32 32;" version="1.1" viewBox="0 0 32 32" width="32px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M14,6c0-0.984-0.813-2-2-2c-0.531,0-0.994,0.193-1.38,0.58l-9.958,9.958C0.334,14.866,0,15.271,0,16s0.279,1.08,0.646,1.447 l9.974,9.973C11.006,27.807,11.469,28,12,28c1.188,0,2-1.016,2-2c0-0.516-0.186-0.986-0.58-1.38L4.8,16l8.62-8.62 C13.814,6.986,14,6.516,14,6z M31.338,14.538L21.38,4.58C20.994,4.193,20.531,4,20,4c-1.188,0-2,1.016-2,2 c0,0.516,0.186,0.986,0.58,1.38L27.2,16l-8.62,8.62C18.186,25.014,18,25.484,18,26c0,0.984,0.813,2,2,2 c0.531,0,0.994-0.193,1.38-0.58l9.974-9.973C31.721,17.08,32,16.729,32,16S31.666,14.866,31.338,14.538z"/></svg>
|
After Width: | Height: | Size: 896 B |
BIN
resources/images/code.png
Normal file
BIN
resources/images/code.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
111
src/calibre/ebooks/constants.py
Normal file
111
src/calibre/ebooks/constants.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
html5_tags = ( # {{{
|
||||||
|
frozenset('''\
|
||||||
|
html
|
||||||
|
head
|
||||||
|
title
|
||||||
|
base
|
||||||
|
link
|
||||||
|
meta
|
||||||
|
style
|
||||||
|
script
|
||||||
|
noscript
|
||||||
|
body
|
||||||
|
section
|
||||||
|
nav
|
||||||
|
article
|
||||||
|
aside
|
||||||
|
h1
|
||||||
|
h2
|
||||||
|
h3
|
||||||
|
h4
|
||||||
|
h5
|
||||||
|
h6
|
||||||
|
header
|
||||||
|
footer
|
||||||
|
address
|
||||||
|
p
|
||||||
|
hr
|
||||||
|
br
|
||||||
|
pre
|
||||||
|
dialog
|
||||||
|
blockquote
|
||||||
|
ol
|
||||||
|
ul
|
||||||
|
li
|
||||||
|
dl
|
||||||
|
dt
|
||||||
|
dd
|
||||||
|
a
|
||||||
|
q
|
||||||
|
cite
|
||||||
|
em
|
||||||
|
strong
|
||||||
|
small
|
||||||
|
mark
|
||||||
|
dfn
|
||||||
|
abbr
|
||||||
|
time
|
||||||
|
progress
|
||||||
|
meter
|
||||||
|
code
|
||||||
|
var
|
||||||
|
samp
|
||||||
|
kbd
|
||||||
|
sub
|
||||||
|
sup
|
||||||
|
span
|
||||||
|
i
|
||||||
|
b
|
||||||
|
bdo
|
||||||
|
ruby
|
||||||
|
rt
|
||||||
|
rp
|
||||||
|
ins
|
||||||
|
del
|
||||||
|
figure
|
||||||
|
img
|
||||||
|
iframe
|
||||||
|
embed
|
||||||
|
object
|
||||||
|
param
|
||||||
|
video
|
||||||
|
audio
|
||||||
|
source
|
||||||
|
canvas
|
||||||
|
map
|
||||||
|
area
|
||||||
|
table
|
||||||
|
caption
|
||||||
|
colgroup
|
||||||
|
col
|
||||||
|
tbody
|
||||||
|
thead
|
||||||
|
tfoot
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
th
|
||||||
|
form
|
||||||
|
fieldset
|
||||||
|
label
|
||||||
|
input
|
||||||
|
button
|
||||||
|
select
|
||||||
|
datalist
|
||||||
|
optgroup
|
||||||
|
option
|
||||||
|
textarea
|
||||||
|
output
|
||||||
|
details
|
||||||
|
command
|
||||||
|
bb
|
||||||
|
menu
|
||||||
|
legend
|
||||||
|
div'''.splitlines())) # }}}
|
@ -42,6 +42,7 @@ d['folders_for_types'] = {'style':'styles', 'image':'images', 'font':'fonts', 'a
|
|||||||
d['pretty_print_on_open'] = False
|
d['pretty_print_on_open'] = False
|
||||||
d['disable_completion_popup_for_search'] = False
|
d['disable_completion_popup_for_search'] = False
|
||||||
d['saved_searches'] = []
|
d['saved_searches'] = []
|
||||||
|
d['insert_tag_mru'] = ['p', 'div', 'li', 'h1', 'h2', 'h3', 'h4', 'em', 'strong', 'td', 'tr']
|
||||||
|
|
||||||
del d
|
del d
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ from calibre.gui2.tweak_book.preferences import Preferences
|
|||||||
from calibre.gui2.tweak_book.search import validate_search_request, run_search
|
from calibre.gui2.tweak_book.search import validate_search_request, run_search
|
||||||
from calibre.gui2.tweak_book.widgets import (
|
from calibre.gui2.tweak_book.widgets import (
|
||||||
RationalizeFolders, MultiSplit, ImportForeign, QuickOpen, InsertLink,
|
RationalizeFolders, MultiSplit, ImportForeign, QuickOpen, InsertLink,
|
||||||
InsertSemantics, BusyCursor)
|
InsertSemantics, BusyCursor, InsertTag)
|
||||||
|
|
||||||
_diff_dialogs = []
|
_diff_dialogs = []
|
||||||
|
|
||||||
@ -642,6 +642,10 @@ class Boss(QObject):
|
|||||||
d = InsertLink(current_container(), edname, initial_text=ed.get_smart_selection(), parent=self.gui)
|
d = InsertLink(current_container(), edname, initial_text=ed.get_smart_selection(), parent=self.gui)
|
||||||
if d.exec_() == d.Accepted:
|
if d.exec_() == d.Accepted:
|
||||||
ed.insert_hyperlink(d.href, d.text)
|
ed.insert_hyperlink(d.href, d.text)
|
||||||
|
elif action[0] == 'insert_tag':
|
||||||
|
d = InsertTag(parent=self.gui)
|
||||||
|
if d.exec_() == d.Accepted:
|
||||||
|
ed.insert_tag(d.tag)
|
||||||
else:
|
else:
|
||||||
ed.action_triggered(action)
|
ed.action_triggered(action)
|
||||||
|
|
||||||
|
@ -232,3 +232,12 @@ class HTMLSmarts(NullSmarts):
|
|||||||
if text:
|
if text:
|
||||||
c.insertText(text)
|
c.insertText(text)
|
||||||
editor.setTextCursor(c)
|
editor.setTextCursor(c)
|
||||||
|
|
||||||
|
def insert_tag(self, editor, name):
|
||||||
|
text = self.get_smart_selection(editor, update=True)
|
||||||
|
c = editor.textCursor()
|
||||||
|
pos = min(c.position(), c.anchor())
|
||||||
|
c.insertText('<{0}>{1}</{0}>'.format(name, text))
|
||||||
|
c.setPosition(pos + 1 + len(name))
|
||||||
|
editor.setTextCursor(c)
|
||||||
|
|
||||||
|
@ -612,6 +612,10 @@ class TextEdit(PlainTextEdit):
|
|||||||
if hasattr(self.smarts, 'insert_hyperlink'):
|
if hasattr(self.smarts, 'insert_hyperlink'):
|
||||||
self.smarts.insert_hyperlink(self, target, text)
|
self.smarts.insert_hyperlink(self, target, text)
|
||||||
|
|
||||||
|
def insert_tag(self, tag):
|
||||||
|
if hasattr(self.smarts, 'insert_tag'):
|
||||||
|
self.smarts.insert_tag(self, tag)
|
||||||
|
|
||||||
def keyPressEvent(self, ev):
|
def keyPressEvent(self, ev):
|
||||||
if ev.key() == Qt.Key_X and ev.modifiers() == Qt.AltModifier:
|
if ev.key() == Qt.Key_X and ev.modifiers() == Qt.AltModifier:
|
||||||
if self.replace_possible_unicode_sequence():
|
if self.replace_possible_unicode_sequence():
|
||||||
|
@ -7,13 +7,14 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from PyQt4.Qt import (
|
from PyQt4.Qt import (
|
||||||
QMainWindow, Qt, QApplication, pyqtSignal, QMenu, qDrawShadeRect, QPainter,
|
QMainWindow, Qt, QApplication, pyqtSignal, QMenu, qDrawShadeRect, QPainter,
|
||||||
QImage, QColor, QIcon, QPixmap, QToolButton)
|
QImage, QColor, QIcon, QPixmap, QToolButton)
|
||||||
|
|
||||||
from calibre.gui2 import error_dialog
|
from calibre.gui2 import error_dialog
|
||||||
from calibre.gui2.tweak_book import actions, current_container
|
from calibre.gui2.tweak_book import actions, current_container, tprefs
|
||||||
from calibre.gui2.tweak_book.editor.text import TextEdit
|
from calibre.gui2.tweak_book.editor.text import TextEdit
|
||||||
|
|
||||||
def create_icon(text, palette=None, sz=32, divider=2):
|
def create_icon(text, palette=None, sz=32, divider=2):
|
||||||
@ -65,6 +66,10 @@ def register_text_editor_actions(reg, palette):
|
|||||||
ac = reg(create_icon(name), text, ('rename_block_tag', name), 'rename-block-tag-' + name, 'Ctrl+%d' % (i + 1), desc)
|
ac = reg(create_icon(name), text, ('rename_block_tag', name), 'rename-block-tag-' + name, 'Ctrl+%d' % (i + 1), desc)
|
||||||
ac.setToolTip(desc)
|
ac.setToolTip(desc)
|
||||||
|
|
||||||
|
ac = reg('code', _('Insert &tag'), ('insert_tag',), 'insert-tag', ('Ctrl+<'), _('Insert tag'))
|
||||||
|
ac.setToolTip(_('<h3>Insert tag</h3>Insert a tag, if some text is selected the tag will be inserted around the selected text'))
|
||||||
|
|
||||||
|
|
||||||
class Editor(QMainWindow):
|
class Editor(QMainWindow):
|
||||||
|
|
||||||
has_line_numbers = True
|
has_line_numbers = True
|
||||||
@ -147,6 +152,23 @@ class Editor(QMainWindow):
|
|||||||
def insert_hyperlink(self, href, text):
|
def insert_hyperlink(self, href, text):
|
||||||
self.editor.insert_hyperlink(href, text)
|
self.editor.insert_hyperlink(href, text)
|
||||||
|
|
||||||
|
def _build_insert_tag_button_menu(self):
|
||||||
|
m = self.insert_tag_button.menu()
|
||||||
|
m.clear()
|
||||||
|
for name in tprefs['insert_tag_mru']:
|
||||||
|
m.addAction(name, partial(self.insert_tag, name))
|
||||||
|
|
||||||
|
def insert_tag(self, name):
|
||||||
|
self.editor.insert_tag(name)
|
||||||
|
mru = tprefs['insert_tag_mru']
|
||||||
|
try:
|
||||||
|
mru.remove(name)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
mru.insert(0, name)
|
||||||
|
tprefs['insert_tag_mru'] = mru
|
||||||
|
self._build_insert_tag_button_menu()
|
||||||
|
|
||||||
def undo(self):
|
def undo(self):
|
||||||
self.editor.undo()
|
self.editor.undo()
|
||||||
|
|
||||||
@ -198,6 +220,7 @@ class Editor(QMainWindow):
|
|||||||
for x in ('cut', 'copy', 'paste'):
|
for x in ('cut', 'copy', 'paste'):
|
||||||
b.addAction(actions['editor-%s' % x])
|
b.addAction(actions['editor-%s' % x])
|
||||||
self.tools_bar = b = self.addToolBar(_('Editor tools'))
|
self.tools_bar = b = self.addToolBar(_('Editor tools'))
|
||||||
|
b.setObjectName('tools_bar')
|
||||||
if self.syntax == 'html':
|
if self.syntax == 'html':
|
||||||
b.addAction(actions['fix-html-current'])
|
b.addAction(actions['fix-html-current'])
|
||||||
if self.syntax in {'xml', 'html', 'css'}:
|
if self.syntax in {'xml', 'html', 'css'}:
|
||||||
@ -206,8 +229,18 @@ class Editor(QMainWindow):
|
|||||||
b.addAction(actions['insert-image'])
|
b.addAction(actions['insert-image'])
|
||||||
if self.syntax == 'html':
|
if self.syntax == 'html':
|
||||||
b.addAction(actions['insert-hyperlink'])
|
b.addAction(actions['insert-hyperlink'])
|
||||||
|
if self.syntax in {'xml', 'html'}:
|
||||||
|
b.addAction(actions['insert-tag'])
|
||||||
|
w = self.insert_tag_button = b.widgetForAction(actions['insert-tag'])
|
||||||
|
w.setPopupMode(QToolButton.MenuButtonPopup)
|
||||||
|
w.m = m = QMenu()
|
||||||
|
w.setMenu(m)
|
||||||
|
w.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
|
w.customContextMenuRequested.connect(self.insert_tag_button.showMenu)
|
||||||
|
self._build_insert_tag_button_menu()
|
||||||
if self.syntax == 'html':
|
if self.syntax == 'html':
|
||||||
self.format_bar = b = self.addToolBar(_('Format text'))
|
self.format_bar = b = self.addToolBar(_('Format text'))
|
||||||
|
b.setObjectName('html_format_bar')
|
||||||
for x in ('bold', 'italic', 'underline', 'strikethrough', 'subscript', 'superscript', 'color', 'background-color'):
|
for x in ('bold', 'italic', 'underline', 'strikethrough', 'subscript', 'superscript', 'color', 'background-color'):
|
||||||
b.addAction(actions['format-text-%s' % x])
|
b.addAction(actions['format-text-%s' % x])
|
||||||
ac = b.addAction(QIcon(I('format-text-heading.png')), _('Change paragraph to heading'))
|
ac = b.addAction(QIcon(I('format-text-heading.png')), _('Change paragraph to heading'))
|
||||||
|
@ -22,6 +22,7 @@ from calibre.gui2 import error_dialog, choose_files, choose_save_file, NONE, inf
|
|||||||
from calibre.gui2.tweak_book import tprefs
|
from calibre.gui2.tweak_book import tprefs
|
||||||
from calibre.utils.icu import primary_sort_key, sort_key
|
from calibre.utils.icu import primary_sort_key, sort_key
|
||||||
from calibre.utils.matcher import get_char, Matcher
|
from calibre.utils.matcher import get_char, Matcher
|
||||||
|
from calibre.gui2.complete2 import EditWithComplete
|
||||||
|
|
||||||
ROOT = QModelIndex()
|
ROOT = QModelIndex()
|
||||||
|
|
||||||
@ -69,6 +70,39 @@ class Dialog(QDialog):
|
|||||||
def setup_ui(self):
|
def setup_ui(self):
|
||||||
raise NotImplementedError('You must implement this method in Dialog subclasses')
|
raise NotImplementedError('You must implement this method in Dialog subclasses')
|
||||||
|
|
||||||
|
class InsertTag(Dialog): # {{{
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
Dialog.__init__(self, _('Choose tag name'), 'insert-tag', parent=parent)
|
||||||
|
|
||||||
|
def setup_ui(self):
|
||||||
|
from calibre.ebooks.constants import html5_tags
|
||||||
|
self.l = l = QVBoxLayout(self)
|
||||||
|
self.setLayout(l)
|
||||||
|
|
||||||
|
self.la = la = QLabel(_('Specify the name of the &tag to insert:'))
|
||||||
|
l.addWidget(la)
|
||||||
|
|
||||||
|
self.tag_input = ti = EditWithComplete(self)
|
||||||
|
ti.set_separator(None)
|
||||||
|
ti.all_items = html5_tags | frozenset(tprefs['insert_tag_mru'])
|
||||||
|
la.setBuddy(ti)
|
||||||
|
l.addWidget(ti)
|
||||||
|
l.addWidget(self.bb)
|
||||||
|
ti.setFocus(Qt.OtherFocusReason)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tag(self):
|
||||||
|
return unicode(self.tag_input.text()).strip()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def test(cls):
|
||||||
|
d = cls()
|
||||||
|
if d.exec_() == d.Accepted:
|
||||||
|
print (d.tag)
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
class RationalizeFolders(Dialog): # {{{
|
class RationalizeFolders(Dialog): # {{{
|
||||||
|
|
||||||
TYPE_MAP = (
|
TYPE_MAP = (
|
||||||
@ -852,4 +886,4 @@ class InsertSemantics(Dialog):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
InsertSemantics.test()
|
InsertTag.test()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user