Edit book: Add a tool to insert arbitrary unicode characters, such as special spaces or punctuation or characters from non-English languages into the text

This commit is contained in:
Kovid Goyal 2014-01-06 17:00:21 +05:30
parent c06d44d598
commit 0012f5cc4b
5 changed files with 245 additions and 1 deletions

201
imgsrc/character-set.svg Normal file
View File

@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<svg
xmlns:ns="http://ns.adobe.com/SaveForWeb/1.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
id="Livello_1"
width="128"
height="128"
viewBox="0 0 124.176 66.475"
overflow="visible"
enable-background="new 0 0 124.176 66.475"
xml:space="preserve"
sodipodi:version="0.32"
inkscape:version="0.48.4 r9939"
sodipodi:docname="character-set.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
sodipodi:modified="true"
inkscape:export-filename="/home/kovid/work/calibre/resources/images/character-set.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><defs
id="defs29"><linearGradient
id="linearGradient3291"
inkscape:collect="always"><stop
id="stop3293"
offset="0"
style="stop-color:#000000;stop-opacity:1;" /><stop
id="stop3295"
offset="1"
style="stop-color:#000000;stop-opacity:0;" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3291"
id="radialGradient3336"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.197802,0,92.82166)"
cx="63.912209"
cy="115.70919"
fx="63.912209"
fy="115.7093"
r="63.912209" /><linearGradient
y2="0.00048828119"
x2="22.481001"
y1="65.662102"
x1="22.481001"
gradientUnits="userSpaceOnUse"
id="XMLID_4_"><stop
id="stop8"
style="stop-color:#2E3436"
offset="0" /><stop
id="stop10"
style="stop-color:#000000"
offset="1" /></linearGradient><linearGradient
y2="15.8804"
x2="64.6689"
y1="65.662102"
x1="64.6689"
gradientUnits="userSpaceOnUse"
id="XMLID_5_"
gradientTransform="matrix(1,0,0,1.0133482,0,1.6235306)"><stop
id="stop15"
style="stop-color:#2E3436"
offset="0" /><stop
id="stop17"
style="stop-color:#000000"
offset="1" /></linearGradient><linearGradient
y2="4.2846999"
x2="104.2773"
y1="66.474602"
x1="104.2773"
gradientUnits="userSpaceOnUse"
id="XMLID_6_"
gradientTransform="translate(0,1.687)"><stop
id="stop22"
style="stop-color:#2E3436"
offset="0" /><stop
id="stop24"
style="stop-color:#000000"
offset="1" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#XMLID_4_"
id="linearGradient2218"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,-1.0127815,0,68.971157)"
x1="38.609329"
y1="0.79894531"
x2="38.366798"
y2="50.62952" /><linearGradient
inkscape:collect="always"
xlink:href="#XMLID_6_"
id="linearGradient2253"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.9813158,0,2.9290339)"
x1="104.2773"
y1="66.474602"
x2="104.39857"
y2="15.9262" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3291"
id="radialGradient4261"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.197802,0,92.82166)"
cx="63.912209"
cy="115.70919"
fx="63.912209"
fy="115.7093"
r="63.912209" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3291"
id="radialGradient4265"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.197802,0,92.82166)"
cx="63.912209"
cy="115.70919"
fx="63.912209"
fy="115.7093"
r="63.912209" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3291"
id="radialGradient4268"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.197802,0,92.82166)"
cx="63.912209"
cy="115.70919"
fx="63.912209"
fy="115.7093"
r="63.912209" /></defs><sodipodi:namedview
inkscape:window-height="1058"
inkscape:window-width="1920"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
width="128px"
height="128px"
borderlayer="false"
inkscape:showpageshadow="false"
showgrid="true"
inkscape:zoom="3.8359375"
inkscape:cx="64"
inkscape:cy="64"
inkscape:window-x="0"
inkscape:window-y="22"
inkscape:current-layer="Livello_1"
inkscape:window-maximized="0"><inkscape:grid
id="GridFromPre046Settings"
type="xygrid"
originx="0px"
originy="0px"
spacingx="4px"
spacingy="4px"
color="#3f3fff"
empcolor="#3f3fff"
opacity="0.15"
empopacity="0.38"
empspacing="0" /></sodipodi:namedview><metadata
id="metadata3"><ns:sfw><ns:slices /><ns:sliceSourceBounds
x="1.912"
y="30.763"
height="66.475"
width="124.176"
bottomLeftOrigin="true" /></ns:sfw><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><path
style="fill:url(#linearGradient2218)"
d="M 16.401176,17.7155 L 0,68.162 L 9.6406172,68.162 L 14.036496,53.853984 L 30.316406,53.853984 L 34.9245,68.162 L 44.95923,68.162 L 28.315523,17.7155 L 16.401176,17.7155 z M 21.949078,25.053731 L 22.10066,25.053731 C 22.86066,27.971554 23.690555,31.413123 24.677555,34.479827 L 28.709637,46.914905 L 15.582633,46.914905 L 19.523766,34.479827 C 20.434765,31.48807 21.190078,27.971554 21.949078,25.053731 z "
id="path2200" /><path
style="fill:#000000;fill-opacity:1"
d="M 23.1875 28 C 17.786134 28 14.719782 31.645231 14.71875 36.15625 C 14.71875 40.496737 17.942815 44 23.1875 44 C 28.354875 44 31.624999 40.519503 31.625 36.09375 C 31.625 31.583884 28.651366 28 23.25 28 L 23.1875 28 z M 23.0625 32 L 23.15625 32 C 25.677144 32 27.15625 33.919307 27.15625 36 C 27.15625 38.23883 25.677144 40 23.15625 40 C 20.721219 40 19.15625 38.075555 19.15625 36.15625 C 19.15625 33.996488 20.540461 32 23.0625 32 z "
transform="matrix(0.970125,0,0,0.970125,0,-28.8505)"
id="path2228" /><path
style="fill:url(#XMLID_5_)"
id="path19"
d="M 52.479,68.162 L 52.479,49.375538 L 46.479,53.567759 L 46.479,46.980996 L 52.479,42.788774 L 52.479,17.7155 L 61.821,17.7155 L 61.821,36.57695 L 71.694,29.841225 L 71.694,36.501962 L 61.821,43.237688 L 61.821,60.452447 L 82.859,60.452447 L 82.859,68.160987 L 52.479,68.160987 L 52.479,68.162 z " /><path
style="fill:url(#linearGradient2253)"
d="M 123.72,65.914787 C 121.518,67.002085 116.658,68.162 110.505,68.162 C 94.252,68.162 84.379,58.3773 84.379,43.519198 C 84.379,27.428564 96.075,17.7155 111.72,17.7155 C 117.872,17.7155 122.278,18.948033 124.176,19.890096 L 122.125,26.992859 C 119.694,25.978179 116.354,25.108733 112.1,25.108733 C 101.696,25.108733 94.176,31.342051 94.176,43.083494 C 94.176,53.811238 100.783,60.69615 112.025,60.69615 C 115.822,60.69615 119.771,59.970957 122.202,58.883659 L 123.72,65.914787 z "
id="path2222" /><path
style="fill:#000000;fill-opacity:1"
d="M 105.08728,13.835 L 97.0125,2.1935 L 104.22438,2.1935 L 108.69271,8.6506979 L 108.8496,8.6506979 L 113.16104,2.1935 L 120.2955,2.1935 L 112.37761,13.835 L 105.08728,13.835 z "
id="path26" /><path
sodipodi:type="arc"
style="opacity:0.38139535;fill:url(#radialGradient4268);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
id="path1563"
sodipodi:cx="63.912209"
sodipodi:cy="115.70919"
sodipodi:rx="63.912209"
sodipodi:ry="12.641975"
d="M 127.82442 115.70919 A 63.912209 12.641975 0 1 1 0,115.70919 A 63.912209 12.641975 0 1 1 127.82442 115.70919 z"
transform="matrix(0.971458,0,0,0.3981189,-2.6300491e-5,44.226475)" /></svg>

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -1068,6 +1068,9 @@ class Boss(QObject):
if not editors:
self.gui.preview.clear()
def insert_character(self):
self.gui.insert_char.show()
# Shutdown {{{
def quit(self):
if not self.confirm_quit():

View File

@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3'
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
import unicodedata, re
import unicodedata, re, weakref
from bisect import bisect
from functools import partial
@ -548,6 +548,7 @@ class CharDelegate(QStyledItemDelegate):
class CharView(QListView):
show_name = pyqtSignal(object)
char_selected = pyqtSignal(object)
def __init__(self, parent=None):
self.last_mouse_idx = -1
@ -564,6 +565,16 @@ class CharView(QListView):
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.context_menu)
self.showing_favorites = False
pi = plugins['progress_indicator'][0]
if hasattr(pi, 'set_no_activate_on_click'):
pi.set_no_activate_on_click(self)
self.activated.connect(self.item_activated)
self.clicked.connect(self.item_activated)
def item_activated(self, index):
char_code, ok = self.model().data(index, Qt.UserRole).toInt()
if ok:
self.char_selected.emit(chr(char_code))
def set_allow_drag_and_drop(self, enabled):
if not enabled:
@ -640,6 +651,8 @@ class CharSelect(Dialog):
def __init__(self, parent=None):
self.initialized = False
Dialog.__init__(self, _('Insert character'), 'charmap_dialog', parent)
self.setWindowIcon(QIcon(I('character-set.png')))
self.focus_widget = None
def setup_ui(self):
self.l = l = QGridLayout(self)
@ -650,6 +663,7 @@ class CharSelect(Dialog):
b.setCheckable(True)
b.setChecked(False)
b.setVisible(False)
b.setDefault(True)
self.splitter = s = QSplitter(self)
s.setChildrenCollapsible(False)
@ -660,6 +674,7 @@ class CharSelect(Dialog):
self.rearrange_button.toggled[bool].connect(self.set_allow_drag_and_drop)
self.category_view.category_selected.connect(self.show_chars)
self.char_view.show_name.connect(self.show_char_info)
self.char_view.char_selected.connect(self.char_selected)
s.addWidget(self.category_view), s.addWidget(self.char_view)
self.char_info = la = QLabel('\xa0')
@ -701,10 +716,29 @@ class CharSelect(Dialog):
self.char_info.clear()
def show(self):
try:
self.focus_widget = weakref.ref(QApplication.focusWidget())
except TypeError:
self.focus_widget = None
self.initialize()
Dialog.show(self)
self.raise_()
def char_selected(self, c):
if QApplication.keyboardModifiers() & Qt.CTRL:
self.hide()
if self.focus_widget is None or self.focus_widget() is None:
QApplication.clipboard().setText(c)
return
w = self.focus_widget()
if hasattr(w, 'textCursor'):
cr = w.textCursor()
cr.insertText(c)
w.setTextCursor(cr)
elif hasattr(w, 'insert'):
w.insert(c)
if __name__ == '__main__':
app = QApplication([])
w = CharSelect()

View File

@ -29,6 +29,7 @@ from calibre.gui2.tweak_book.preview import Preview
from calibre.gui2.tweak_book.search import SearchPanel
from calibre.gui2.tweak_book.check import Check
from calibre.gui2.tweak_book.toc import TOCViewer
from calibre.gui2.tweak_book.char_select import CharSelect
from calibre.gui2.tweak_book.editor.widget import register_text_editor_actions
from calibre.gui2.tweak_book.editor.insert_resource import InsertImage
@ -217,6 +218,7 @@ class Main(MainWindow):
self.check_book = Check(self)
self.toc_view = TOCViewer(self)
self.image_browser = InsertImage(self, for_browsing=True)
self.insert_char = CharSelect(self)
self.create_actions()
self.create_toolbars()
@ -321,6 +323,8 @@ class Main(MainWindow):
_('Beautify current file'))
self.action_pretty_all = reg('format-justify-fill.png', _('&Beautify all files'), partial(self.boss.pretty_print, False), 'pretty-all', (),
_('Beautify all files'))
self.action_insert_char = reg('character-set.png', _('&Insert special character'), self.boss.insert_character, 'insert-character', (),
_('Insert special character'))
# Polish actions
group = _('Polish Book')
@ -423,6 +427,7 @@ class Main(MainWindow):
e.addAction(self.action_editor_cut)
e.addAction(self.action_editor_copy)
e.addAction(self.action_editor_paste)
e.addAction(self.action_insert_char)
e.addSeparator()
e.addAction(self.action_preferences)
@ -505,6 +510,7 @@ class Main(MainWindow):
b.setToolTip(_('Donate to support calibre development'))
QTimer.singleShot(10, b.start_animation)
self.global_bar.addWidget(w)
self.global_bar.addAction(self.action_insert_char)
a(self.action_help)
a = create(_('Polish book tool bar'), 'polish').addAction