mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Merge branch 'master' of https://github.com/kovidgoyal/calibre
This commit is contained in:
commit
73b613fac1
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 |
@ -53,9 +53,10 @@ class TOLINO(EB600):
|
|||||||
|
|
||||||
name = 'Tolino Shine Device Interface'
|
name = 'Tolino Shine Device Interface'
|
||||||
gui_name = 'Tolino Shine'
|
gui_name = 'Tolino Shine'
|
||||||
description = _('Communicate with the Tolino Shine reader.')
|
description = _('Communicate with the Tolino Shine and Vision readers')
|
||||||
FORMATS = ['epub', 'pdf', 'txt']
|
FORMATS = ['epub', 'pdf', 'txt']
|
||||||
BCD = [0x226]
|
PRODUCT_ID = EB600.PRODUCT_ID + [0x6033]
|
||||||
|
BCD = [0x226, 0x9999]
|
||||||
VENDOR_NAME = ['DEUTSCHE']
|
VENDOR_NAME = ['DEUTSCHE']
|
||||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['_TELEKOMTOLINO']
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['_TELEKOMTOLINO']
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
|
|
||||||
PURGE_CACHE_ENTRIES_DAYS = 30
|
PURGE_CACHE_ENTRIES_DAYS = 30
|
||||||
|
|
||||||
CURRENT_CC_VERSION = 73
|
CURRENT_CC_VERSION = 77
|
||||||
|
|
||||||
ZEROCONF_CLIENT_STRING = b'calibre wireless device client'
|
ZEROCONF_CLIENT_STRING = b'calibre wireless device client'
|
||||||
|
|
||||||
|
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())) # }}}
|
@ -7,7 +7,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from collections import Counter
|
from collections import Counter, defaultdict
|
||||||
|
|
||||||
from lxml.html.builder import OL, UL, SPAN
|
from lxml.html.builder import OL, UL, SPAN
|
||||||
|
|
||||||
@ -135,8 +135,9 @@ class Level(object):
|
|||||||
|
|
||||||
class NumberingDefinition(object):
|
class NumberingDefinition(object):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None, an_id=None):
|
||||||
self.levels = {}
|
self.levels = {}
|
||||||
|
self.abstract_numbering_definition_id = an_id
|
||||||
if parent is not None:
|
if parent is not None:
|
||||||
for lvl in XPath('./w:lvl')(parent):
|
for lvl in XPath('./w:lvl')(parent):
|
||||||
try:
|
try:
|
||||||
@ -146,7 +147,7 @@ class NumberingDefinition(object):
|
|||||||
self.levels[ilvl] = Level(lvl)
|
self.levels[ilvl] = Level(lvl)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
ans = NumberingDefinition()
|
ans = NumberingDefinition(an_id=self.abstract_numbering_definition_id)
|
||||||
for l, lvl in self.levels.iteritems():
|
for l, lvl in self.levels.iteritems():
|
||||||
ans.levels[l] = lvl.copy()
|
ans.levels[l] = lvl.copy()
|
||||||
return ans
|
return ans
|
||||||
@ -156,7 +157,8 @@ class Numbering(object):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.definitions = {}
|
self.definitions = {}
|
||||||
self.instances = {}
|
self.instances = {}
|
||||||
self.counters = {}
|
self.counters = defaultdict(Counter)
|
||||||
|
self.starts = {}
|
||||||
self.pic_map = {}
|
self.pic_map = {}
|
||||||
|
|
||||||
def __call__(self, root, styles, rid_map):
|
def __call__(self, root, styles, rid_map):
|
||||||
@ -174,13 +176,24 @@ class Numbering(object):
|
|||||||
if nsl:
|
if nsl:
|
||||||
lazy_load[an_id] = get(nsl[0], 'w:val')
|
lazy_load[an_id] = get(nsl[0], 'w:val')
|
||||||
else:
|
else:
|
||||||
nd = NumberingDefinition(an)
|
nd = NumberingDefinition(an, an_id=an_id)
|
||||||
self.definitions[an_id] = nd
|
self.definitions[an_id] = nd
|
||||||
|
|
||||||
def create_instance(n, definition):
|
def create_instance(n, definition):
|
||||||
nd = definition.copy()
|
nd = definition.copy()
|
||||||
|
start_overrides = {}
|
||||||
for lo in XPath('./w:lvlOverride')(n):
|
for lo in XPath('./w:lvlOverride')(n):
|
||||||
ilvl = get(lo, 'w:ilvl')
|
try:
|
||||||
|
ilvl = int(get(lo, 'w:ilvl'))
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
ilvl = None
|
||||||
|
for so in XPath('./w:startOverride[@w:val]')(lo):
|
||||||
|
try:
|
||||||
|
start_override = int(get(so, 'w:val'))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
start_overrides[ilvl] = start_override
|
||||||
for lvl in XPath('./w:lvl')(lo)[:1]:
|
for lvl in XPath('./w:lvl')(lo)[:1]:
|
||||||
nilvl = get(lvl, 'w:ilvl')
|
nilvl = get(lvl, 'w:ilvl')
|
||||||
ilvl = nilvl if ilvl is None else ilvl
|
ilvl = nilvl if ilvl is None else ilvl
|
||||||
@ -188,6 +201,11 @@ class Numbering(object):
|
|||||||
if alvl is None:
|
if alvl is None:
|
||||||
alvl = Level()
|
alvl = Level()
|
||||||
alvl.read_from_xml(lvl, override=True)
|
alvl.read_from_xml(lvl, override=True)
|
||||||
|
for ilvl, so in start_overrides.iteritems():
|
||||||
|
try:
|
||||||
|
nd.levels[ilvl].start = start_override
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
return nd
|
return nd
|
||||||
|
|
||||||
next_pass = {}
|
next_pass = {}
|
||||||
@ -213,7 +231,7 @@ class Numbering(object):
|
|||||||
self.instances[num_id] = create_instance(n, d)
|
self.instances[num_id] = create_instance(n, d)
|
||||||
|
|
||||||
for num_id, d in self.instances.iteritems():
|
for num_id, d in self.instances.iteritems():
|
||||||
self.counters[num_id] = Counter({lvl:d.levels[lvl].start for lvl in d.levels})
|
self.starts[num_id] = {lvl:d.levels[lvl].start for lvl in d.levels}
|
||||||
|
|
||||||
def get_pstyle(self, num_id, style_id):
|
def get_pstyle(self, num_id, style_id):
|
||||||
d = self.instances.get(num_id, None)
|
d = self.instances.get(num_id, None)
|
||||||
@ -236,12 +254,17 @@ class Numbering(object):
|
|||||||
counter[ilvl] = lvl.start
|
counter[ilvl] = lvl.start
|
||||||
|
|
||||||
def apply_markup(self, items, body, styles, object_map, images):
|
def apply_markup(self, items, body, styles, object_map, images):
|
||||||
|
seen_instances = set()
|
||||||
for p, num_id, ilvl in items:
|
for p, num_id, ilvl in items:
|
||||||
d = self.instances.get(num_id, None)
|
d = self.instances.get(num_id, None)
|
||||||
if d is not None:
|
if d is not None:
|
||||||
lvl = d.levels.get(ilvl, None)
|
lvl = d.levels.get(ilvl, None)
|
||||||
if lvl is not None:
|
if lvl is not None:
|
||||||
counter = self.counters[num_id]
|
an_id = d.abstract_numbering_definition_id
|
||||||
|
counter = self.counters[an_id]
|
||||||
|
if ilvl not in counter or num_id not in seen_instances:
|
||||||
|
counter[ilvl] = self.starts[num_id][ilvl]
|
||||||
|
seen_instances.add(num_id)
|
||||||
p.tag = 'li'
|
p.tag = 'li'
|
||||||
p.set('value', '%s' % counter[ilvl])
|
p.set('value', '%s' % counter[ilvl])
|
||||||
p.set('list-lvl', str(ilvl))
|
p.set('list-lvl', str(ilvl))
|
||||||
|
@ -262,6 +262,9 @@ class Styles(object):
|
|||||||
if num_id is not None:
|
if num_id is not None:
|
||||||
p.set('calibre_num_id', '%s:%s' % (lvl, num_id))
|
p.set('calibre_num_id', '%s:%s' % (lvl, num_id))
|
||||||
is_numbering = True
|
is_numbering = True
|
||||||
|
ps = self.numbering.get_para_style(num_id, lvl)
|
||||||
|
if ps is not None:
|
||||||
|
parent_styles.append(ps)
|
||||||
|
|
||||||
for attr in ans.all_properties:
|
for attr in ans.all_properties:
|
||||||
if not (is_numbering and attr == 'text_indent'): # skip text-indent for lists
|
if not (is_numbering and attr == 'text_indent'): # skip text-indent for lists
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ def syntax_from_mime(name, mime):
|
|||||||
return 'html'
|
return 'html'
|
||||||
if mime in OEB_STYLES:
|
if mime in OEB_STYLES:
|
||||||
return 'css'
|
return 'css'
|
||||||
if mime in {guess_type('a.opf'), guess_type('a.ncx'), guess_type('a.xml'), 'application/oebps-page-map+xml'}:
|
if mime in {guess_type('a.svg'), guess_type('a.opf'), guess_type('a.ncx'), guess_type('a.xml'), 'application/oebps-page-map+xml'}:
|
||||||
return 'xml'
|
return 'xml'
|
||||||
if mime.startswith('text/'):
|
if mime.startswith('text/'):
|
||||||
return 'text'
|
return 'text'
|
||||||
|
@ -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()
|
||||||
|
@ -30,7 +30,7 @@ CHECKS = [('invalid_titles', _('Invalid titles'), True, False),
|
|||||||
('missing_formats', _('Missing book formats'), False, True),
|
('missing_formats', _('Missing book formats'), False, True),
|
||||||
('extra_formats', _('Extra book formats'), True, False),
|
('extra_formats', _('Extra book formats'), True, False),
|
||||||
('extra_files', _('Unknown files in books'), True, False),
|
('extra_files', _('Unknown files in books'), True, False),
|
||||||
('missing_covers', _('Missing covers files'), False, True),
|
('missing_covers', _('Missing cover files'), False, True),
|
||||||
('extra_covers', _('Cover files not in database'), True, True),
|
('extra_covers', _('Cover files not in database'), True, True),
|
||||||
('failed_folders', _('Folders raising exception'), False, False)
|
('failed_folders', _('Folders raising exception'), False, False)
|
||||||
]
|
]
|
||||||
@ -175,7 +175,8 @@ class CheckLibrary(object):
|
|||||||
def process_book(self, lib, book_info):
|
def process_book(self, lib, book_info):
|
||||||
(db_path, title_dir, book_id) = book_info
|
(db_path, title_dir, book_id) = book_info
|
||||||
filenames = frozenset([f for f in os.listdir(os.path.join(lib, db_path))
|
filenames = frozenset([f for f in os.listdir(os.path.join(lib, db_path))
|
||||||
if os.path.splitext(f)[1] not in self.ignore_ext])
|
if os.path.splitext(f)[1] not in self.ignore_ext or
|
||||||
|
f == 'cover.jpg'])
|
||||||
book_id = int(book_id)
|
book_id = int(book_id)
|
||||||
formats = frozenset(filter(self.is_ebook_file, filenames))
|
formats = frozenset(filter(self.is_ebook_file, filenames))
|
||||||
book_formats = frozenset([x[0]+'.'+x[1].lower() for x in
|
book_formats = frozenset([x[0]+'.'+x[1].lower() for x in
|
||||||
|
@ -140,23 +140,29 @@ def update_custom_recipe(id_, title, script):
|
|||||||
|
|
||||||
|
|
||||||
def add_custom_recipe(title, script):
|
def add_custom_recipe(title, script):
|
||||||
|
add_custom_recipes({title:script})
|
||||||
|
|
||||||
|
def add_custom_recipes(script_map):
|
||||||
from calibre.web.feeds.recipes import custom_recipes, \
|
from calibre.web.feeds.recipes import custom_recipes, \
|
||||||
custom_recipe_filename
|
custom_recipe_filename
|
||||||
id_ = 1000
|
id_ = 1000
|
||||||
keys = tuple(map(int, custom_recipes.iterkeys()))
|
keys = tuple(map(int, custom_recipes.iterkeys()))
|
||||||
if keys:
|
if keys:
|
||||||
id_ = max(keys)+1
|
id_ = max(keys)+1
|
||||||
id_ = str(id_)
|
with custom_recipes:
|
||||||
bdir = os.path.dirname(custom_recipes.file_path)
|
for title, script in script_map.iteritems():
|
||||||
|
fid = str(id_)
|
||||||
|
bdir = os.path.dirname(custom_recipes.file_path)
|
||||||
|
|
||||||
fname = custom_recipe_filename(id_, title)
|
fname = custom_recipe_filename(fid, title)
|
||||||
if isinstance(script, unicode):
|
if isinstance(script, unicode):
|
||||||
script = script.encode('utf-8')
|
script = script.encode('utf-8')
|
||||||
|
|
||||||
custom_recipes[id_] = (title, fname)
|
custom_recipes[fid] = (title, fname)
|
||||||
|
|
||||||
with open(os.path.join(bdir, fname), 'wb') as f:
|
with open(os.path.join(bdir, fname), 'wb') as f:
|
||||||
f.write(script)
|
f.write(script)
|
||||||
|
id_ += 1
|
||||||
|
|
||||||
|
|
||||||
def remove_custom_recipe(id_):
|
def remove_custom_recipe(id_):
|
||||||
|
@ -8148,7 +8148,8 @@ void Style::drawComplexControl(ComplexControl control, const QStyleOptionComplex
|
|||||||
drawControl(CE_ToolButtonLabel, &label, painter, widget);
|
drawControl(CE_ToolButtonLabel, &label, painter, widget);
|
||||||
|
|
||||||
if (!(toolbutton->subControls&SC_ToolButtonMenu) &&
|
if (!(toolbutton->subControls&SC_ToolButtonMenu) &&
|
||||||
(toolbutton->features&QStyleOptionToolButton::HasMenu))
|
(toolbutton->features&QStyleOptionToolButton::HasMenu &&
|
||||||
|
toolbutton->features & QStyleOptionToolButton::PopupDelay))
|
||||||
{
|
{
|
||||||
QRect arrow(r.right()-(LARGE_ARR_WIDTH+(etched ? 3 : 2)),
|
QRect arrow(r.right()-(LARGE_ARR_WIDTH+(etched ? 3 : 2)),
|
||||||
r.bottom()-(LARGE_ARR_HEIGHT+(etched ? 4 : 3)),
|
r.bottom()-(LARGE_ARR_HEIGHT+(etched ? 4 : 3)),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user