This commit is contained in:
ingkebil 2014-04-03 23:13:49 +02:00
commit 73b613fac1
17 changed files with 258 additions and 26 deletions

1
imgsrc/code.svg Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -53,9 +53,10 @@ class TOLINO(EB600):
name = 'Tolino Shine Device Interface'
gui_name = 'Tolino Shine'
description = _('Communicate with the Tolino Shine reader.')
description = _('Communicate with the Tolino Shine and Vision readers')
FORMATS = ['epub', 'pdf', 'txt']
BCD = [0x226]
PRODUCT_ID = EB600.PRODUCT_ID + [0x6033]
BCD = [0x226, 0x9999]
VENDOR_NAME = ['DEUTSCHE']
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['_TELEKOMTOLINO']

View File

@ -226,7 +226,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
PURGE_CACHE_ENTRIES_DAYS = 30
CURRENT_CC_VERSION = 73
CURRENT_CC_VERSION = 77
ZEROCONF_CLIENT_STRING = b'calibre wireless device client'

View 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())) # }}}

View File

@ -7,7 +7,7 @@ __license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
import re
from collections import Counter
from collections import Counter, defaultdict
from lxml.html.builder import OL, UL, SPAN
@ -135,8 +135,9 @@ class Level(object):
class NumberingDefinition(object):
def __init__(self, parent=None):
def __init__(self, parent=None, an_id=None):
self.levels = {}
self.abstract_numbering_definition_id = an_id
if parent is not None:
for lvl in XPath('./w:lvl')(parent):
try:
@ -146,7 +147,7 @@ class NumberingDefinition(object):
self.levels[ilvl] = Level(lvl)
def copy(self):
ans = NumberingDefinition()
ans = NumberingDefinition(an_id=self.abstract_numbering_definition_id)
for l, lvl in self.levels.iteritems():
ans.levels[l] = lvl.copy()
return ans
@ -156,7 +157,8 @@ class Numbering(object):
def __init__(self):
self.definitions = {}
self.instances = {}
self.counters = {}
self.counters = defaultdict(Counter)
self.starts = {}
self.pic_map = {}
def __call__(self, root, styles, rid_map):
@ -174,13 +176,24 @@ class Numbering(object):
if nsl:
lazy_load[an_id] = get(nsl[0], 'w:val')
else:
nd = NumberingDefinition(an)
nd = NumberingDefinition(an, an_id=an_id)
self.definitions[an_id] = nd
def create_instance(n, definition):
nd = definition.copy()
start_overrides = {}
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]:
nilvl = get(lvl, 'w:ilvl')
ilvl = nilvl if ilvl is None else ilvl
@ -188,6 +201,11 @@ class Numbering(object):
if alvl is None:
alvl = Level()
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
next_pass = {}
@ -213,7 +231,7 @@ class Numbering(object):
self.instances[num_id] = create_instance(n, d)
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):
d = self.instances.get(num_id, None)
@ -236,12 +254,17 @@ class Numbering(object):
counter[ilvl] = lvl.start
def apply_markup(self, items, body, styles, object_map, images):
seen_instances = set()
for p, num_id, ilvl in items:
d = self.instances.get(num_id, None)
if d is not None:
lvl = d.levels.get(ilvl, 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.set('value', '%s' % counter[ilvl])
p.set('list-lvl', str(ilvl))

View File

@ -262,6 +262,9 @@ class Styles(object):
if num_id is not None:
p.set('calibre_num_id', '%s:%s' % (lvl, num_id))
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:
if not (is_numbering and attr == 'text_indent'): # skip text-indent for lists

View File

@ -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

View File

@ -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)

View File

@ -16,7 +16,7 @@ def syntax_from_mime(name, mime):
return 'html'
if mime in OEB_STYLES:
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'
if mime.startswith('text/'):
return 'text'

View File

@ -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)

View File

@ -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():

View File

@ -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'))

View File

@ -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()

View File

@ -30,7 +30,7 @@ CHECKS = [('invalid_titles', _('Invalid titles'), True, False),
('missing_formats', _('Missing book formats'), False, True),
('extra_formats', _('Extra book formats'), 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),
('failed_folders', _('Folders raising exception'), False, False)
]
@ -175,7 +175,8 @@ class CheckLibrary(object):
def process_book(self, lib, book_info):
(db_path, title_dir, book_id) = book_info
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)
formats = frozenset(filter(self.is_ebook_file, filenames))
book_formats = frozenset([x[0]+'.'+x[1].lower() for x in

View File

@ -140,23 +140,29 @@ def update_custom_recipe(id_, 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, \
custom_recipe_filename
id_ = 1000
keys = tuple(map(int, custom_recipes.iterkeys()))
if keys:
id_ = max(keys)+1
id_ = str(id_)
bdir = os.path.dirname(custom_recipes.file_path)
with custom_recipes:
for title, script in script_map.iteritems():
fid = str(id_)
bdir = os.path.dirname(custom_recipes.file_path)
fname = custom_recipe_filename(id_, title)
if isinstance(script, unicode):
script = script.encode('utf-8')
fname = custom_recipe_filename(fid, title)
if isinstance(script, unicode):
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:
f.write(script)
with open(os.path.join(bdir, fname), 'wb') as f:
f.write(script)
id_ += 1
def remove_custom_recipe(id_):

View File

@ -8148,7 +8148,8 @@ void Style::drawComplexControl(ComplexControl control, const QStyleOptionComplex
drawControl(CE_ToolButtonLabel, &label, painter, widget);
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)),
r.bottom()-(LARGE_ARR_HEIGHT+(etched ? 4 : 3)),