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['disable_completion_popup_for_search'] = False
|
||||
d['saved_searches'] = []
|
||||
d['insert_tag_mru'] = ['p', 'div', 'li', 'h1', 'h2', 'h3', 'h4', 'em', 'strong', 'td', 'tr']
|
||||
|
||||
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.widgets import (
|
||||
RationalizeFolders, MultiSplit, ImportForeign, QuickOpen, InsertLink,
|
||||
InsertSemantics, BusyCursor)
|
||||
InsertSemantics, BusyCursor, InsertTag)
|
||||
|
||||
_diff_dialogs = []
|
||||
|
||||
@ -642,6 +642,10 @@ class Boss(QObject):
|
||||
d = InsertLink(current_container(), edname, initial_text=ed.get_smart_selection(), parent=self.gui)
|
||||
if d.exec_() == d.Accepted:
|
||||
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:
|
||||
ed.action_triggered(action)
|
||||
|
||||
|
@ -232,3 +232,12 @@ class HTMLSmarts(NullSmarts):
|
||||
if text:
|
||||
c.insertText(text)
|
||||
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'):
|
||||
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):
|
||||
if ev.key() == Qt.Key_X and ev.modifiers() == Qt.AltModifier:
|
||||
if self.replace_possible_unicode_sequence():
|
||||
|
@ -7,13 +7,14 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import unicodedata
|
||||
from functools import partial
|
||||
|
||||
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 import actions, current_container, tprefs
|
||||
from calibre.gui2.tweak_book.editor.text import TextEdit
|
||||
|
||||
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.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):
|
||||
|
||||
has_line_numbers = True
|
||||
@ -147,6 +152,23 @@ class Editor(QMainWindow):
|
||||
def insert_hyperlink(self, 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):
|
||||
self.editor.undo()
|
||||
|
||||
@ -198,6 +220,7 @@ class Editor(QMainWindow):
|
||||
for x in ('cut', 'copy', 'paste'):
|
||||
b.addAction(actions['editor-%s' % x])
|
||||
self.tools_bar = b = self.addToolBar(_('Editor tools'))
|
||||
b.setObjectName('tools_bar')
|
||||
if self.syntax == 'html':
|
||||
b.addAction(actions['fix-html-current'])
|
||||
if self.syntax in {'xml', 'html', 'css'}:
|
||||
@ -206,8 +229,18 @@ class Editor(QMainWindow):
|
||||
b.addAction(actions['insert-image'])
|
||||
if self.syntax == 'html':
|
||||
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':
|
||||
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'):
|
||||
b.addAction(actions['format-text-%s' % x])
|
||||
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.utils.icu import primary_sort_key, sort_key
|
||||
from calibre.utils.matcher import get_char, Matcher
|
||||
from calibre.gui2.complete2 import EditWithComplete
|
||||
|
||||
ROOT = QModelIndex()
|
||||
|
||||
@ -69,6 +70,39 @@ class Dialog(QDialog):
|
||||
def setup_ui(self):
|
||||
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): # {{{
|
||||
|
||||
TYPE_MAP = (
|
||||
@ -852,4 +886,4 @@ class InsertSemantics(Dialog):
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication([])
|
||||
InsertSemantics.test()
|
||||
InsertTag.test()
|
||||
|
Loading…
x
Reference in New Issue
Block a user