mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from main branch
This commit is contained in:
commit
bf4027006d
@ -109,7 +109,8 @@ class ANDROID(USBMS):
|
|||||||
'SGH-T849', '_MB300', 'A70S', 'S_ANDROID', 'A101IT', 'A70H',
|
'SGH-T849', '_MB300', 'A70S', 'S_ANDROID', 'A101IT', 'A70H',
|
||||||
'IDEOS_TABLET', 'MYTOUCH_4G', 'UMS_COMPOSITE', 'SCH-I800_CARD',
|
'IDEOS_TABLET', 'MYTOUCH_4G', 'UMS_COMPOSITE', 'SCH-I800_CARD',
|
||||||
'7', 'A956', 'A955', 'A43', 'ANDROID_PLATFORM', 'TEGRA_2',
|
'7', 'A956', 'A955', 'A43', 'ANDROID_PLATFORM', 'TEGRA_2',
|
||||||
'MB860', 'MULTI-CARD', 'MID7015A', 'INCREDIBLE', 'A7EB', 'STREAK']
|
'MB860', 'MULTI-CARD', 'MID7015A', 'INCREDIBLE', 'A7EB', 'STREAK',
|
||||||
|
'MB525']
|
||||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||||
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||||
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD']
|
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD']
|
||||||
|
@ -101,7 +101,7 @@ class Container(object):
|
|||||||
return None
|
return None
|
||||||
return existing[0]
|
return existing[0]
|
||||||
|
|
||||||
def add_name_to_manifest(self, name):
|
def add_name_to_manifest(self, name, mt=None):
|
||||||
item = self.manifest_item_for_name(name)
|
item = self.manifest_item_for_name(name)
|
||||||
if item is not None:
|
if item is not None:
|
||||||
return
|
return
|
||||||
@ -109,11 +109,27 @@ class Container(object):
|
|||||||
item = manifest.makeelement('{%s}item'%OPF_NS, nsmap={'opf':OPF_NS},
|
item = manifest.makeelement('{%s}item'%OPF_NS, nsmap={'opf':OPF_NS},
|
||||||
href=self.name_to_href(name, posixpath.dirname(self.opf_name)),
|
href=self.name_to_href(name, posixpath.dirname(self.opf_name)),
|
||||||
id=self.generate_manifest_id())
|
id=self.generate_manifest_id())
|
||||||
|
if not mt:
|
||||||
mt = guess_type(posixpath.basename(name))[0]
|
mt = guess_type(posixpath.basename(name))[0]
|
||||||
if not mt:
|
if not mt:
|
||||||
mt = 'application/octest-stream'
|
mt = 'application/octest-stream'
|
||||||
item.set('media-type', mt)
|
item.set('media-type', mt)
|
||||||
manifest.append(item)
|
manifest.append(item)
|
||||||
|
self.fix_tail(item)
|
||||||
|
|
||||||
|
def fix_tail(self, item):
|
||||||
|
'''
|
||||||
|
Designed only to work with self closing elements after item has
|
||||||
|
just been inserted/appended
|
||||||
|
'''
|
||||||
|
parent = item.getparent()
|
||||||
|
idx = parent.index(item)
|
||||||
|
if idx == 0:
|
||||||
|
item.tail = parent.text
|
||||||
|
else:
|
||||||
|
item.tail = parent[idx-1].tail
|
||||||
|
if idx == len(parent)-1:
|
||||||
|
parent[idx-1].tail = parent.text
|
||||||
|
|
||||||
def generate_manifest_id(self):
|
def generate_manifest_id(self):
|
||||||
items = self.opf.xpath('//opf:manifest/opf:item[@id]',
|
items = self.opf.xpath('//opf:manifest/opf:item[@id]',
|
||||||
|
@ -46,6 +46,8 @@ cover_url = XPath("descendant::atom:link[@rel='image']/attribute::href")
|
|||||||
|
|
||||||
def get_details(browser, url, timeout): # {{{
|
def get_details(browser, url, timeout): # {{{
|
||||||
try:
|
try:
|
||||||
|
if Douban.DOUBAN_API_KEY and Douban.DOUBAN_API_KEY != '':
|
||||||
|
url = url + "?apikey=" + Douban.DOUBAN_API_KEY
|
||||||
raw = browser.open_novisit(url, timeout=timeout).read()
|
raw = browser.open_novisit(url, timeout=timeout).read()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
gc = getattr(e, 'getcode', lambda : -1)
|
gc = getattr(e, 'getcode', lambda : -1)
|
||||||
|
@ -120,7 +120,19 @@ class Split(object):
|
|||||||
for i, x in enumerate(page_breaks):
|
for i, x in enumerate(page_breaks):
|
||||||
x.set('id', x.get('id', 'calibre_pb_%d'%i))
|
x.set('id', x.get('id', 'calibre_pb_%d'%i))
|
||||||
id = x.get('id')
|
id = x.get('id')
|
||||||
page_breaks_.append((XPath('//*[@id=%r]'%id),
|
try:
|
||||||
|
xp = XPath('//*[@id="%s"]'%id)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
xp = XPath("//*[@id='%s']"%id)
|
||||||
|
except:
|
||||||
|
# The id has both a quote and an apostrophe or some other
|
||||||
|
# Just replace it since I doubt its going to work anywhere else
|
||||||
|
# either
|
||||||
|
id = 'calibre_pb_%d'%i
|
||||||
|
x.set('id', id)
|
||||||
|
xp = XPath('//*[@id=%r]'%id)
|
||||||
|
page_breaks_.append((xp,
|
||||||
x.get('pb_before', False)))
|
x.get('pb_before', False)))
|
||||||
page_break_ids.append(id)
|
page_break_ids.append(id)
|
||||||
|
|
||||||
|
@ -152,7 +152,8 @@ class DeleteAction(InterfaceAction):
|
|||||||
if not ids:
|
if not ids:
|
||||||
return
|
return
|
||||||
fmts = self._get_selected_formats(
|
fmts = self._get_selected_formats(
|
||||||
'<p>'+_('Choose formats <b>not</b> to be deleted'), ids)
|
'<p>'+_('Choose formats <b>not</b> to be deleted.<p>Note that '
|
||||||
|
'this will never remove all formats from a book.'), ids)
|
||||||
if fmts is None:
|
if fmts is None:
|
||||||
return
|
return
|
||||||
for id in ids:
|
for id in ids:
|
||||||
@ -161,6 +162,9 @@ class DeleteAction(InterfaceAction):
|
|||||||
continue
|
continue
|
||||||
bfmts = set([x.lower() for x in bfmts.split(',')])
|
bfmts = set([x.lower() for x in bfmts.split(',')])
|
||||||
rfmts = bfmts - set(fmts)
|
rfmts = bfmts - set(fmts)
|
||||||
|
if bfmts - rfmts:
|
||||||
|
# Do not delete if it will leave the book with no
|
||||||
|
# formats
|
||||||
for fmt in rfmts:
|
for fmt in rfmts:
|
||||||
self.gui.library_view.model().db.remove_format(id, fmt,
|
self.gui.library_view.model().db.remove_format(id, fmt,
|
||||||
index_is_id=True, notify=False)
|
index_is_id=True, notify=False)
|
||||||
|
@ -44,7 +44,7 @@ class SelectFormats(QDialog):
|
|||||||
self.setLayout(self._l)
|
self.setLayout(self._l)
|
||||||
self.setWindowTitle(_('Choose formats'))
|
self.setWindowTitle(_('Choose formats'))
|
||||||
self._m = QLabel(msg)
|
self._m = QLabel(msg)
|
||||||
self._m.setWordWrap = True
|
self._m.setWordWrap(True)
|
||||||
self._l.addWidget(self._m)
|
self._l.addWidget(self._m)
|
||||||
self.formats = Formats(fmt_list)
|
self.formats = Formats(fmt_list)
|
||||||
self.fview = QListView(self)
|
self.fview = QListView(self)
|
||||||
|
@ -46,8 +46,8 @@ class TemplateLineEditor(QLineEdit):
|
|||||||
menu.exec_(event.globalPos())
|
menu.exec_(event.globalPos())
|
||||||
|
|
||||||
def clear_field(self):
|
def clear_field(self):
|
||||||
self.setText('')
|
|
||||||
self.txt = None
|
self.txt = None
|
||||||
|
self.setText('')
|
||||||
self.setReadOnly(False)
|
self.setReadOnly(False)
|
||||||
self.setStyleSheet('TemplateLineEditor { color: black }')
|
self.setStyleSheet('TemplateLineEditor { color: black }')
|
||||||
|
|
||||||
@ -95,6 +95,39 @@ class TemplateLineEditor(QLineEdit):
|
|||||||
|
|
||||||
class TagWizard(QDialog):
|
class TagWizard(QDialog):
|
||||||
|
|
||||||
|
text_template = " strcmp(field('{f}'), '{v}', '{fv}', '{tv}', '{fv}')"
|
||||||
|
text_empty_template = " test(field('{f}'), '{fv}', '{tv}')"
|
||||||
|
text_re_template = " contains(field('{f}'), '{v}', '{tv}', '{fv}')"
|
||||||
|
|
||||||
|
templates = {
|
||||||
|
'text.mult' : " str_in_list(field('{f}'), '{mult}', '{v}', '{tv}', '{fv}')",
|
||||||
|
'text.mult.re' : " in_list(field('{f}'), '{mult}', '^{v}$', '{tv}', '{fv}')",
|
||||||
|
'text.mult.empty' : " test(field('{f}'), '{fv}', '{tv}')",
|
||||||
|
'text' : text_template,
|
||||||
|
'text.re' : text_re_template,
|
||||||
|
'text.empty' : text_empty_template,
|
||||||
|
'rating' : " cmp(field('{f}'), '{v}', '{fv}', '{tv}', '{fv}')",
|
||||||
|
'rating.empty' : text_empty_template,
|
||||||
|
'int' : " cmp(field('{f}'), '{v}', '{fv}', '{tv}', '{fv}')",
|
||||||
|
'int.empty' : text_empty_template,
|
||||||
|
'float' : " cmp(field('{f}'), '{v}', '{fv}', '{tv}', '{fv}')",
|
||||||
|
'float.empty' : text_empty_template,
|
||||||
|
'bool' : " strcmp(field('{f}'), '{v}', '{fv}', '{tv}', '{fv}')",
|
||||||
|
'bool.empty' : text_empty_template,
|
||||||
|
'series' : text_template,
|
||||||
|
'series.re' : text_re_template,
|
||||||
|
'series.empty' : text_empty_template,
|
||||||
|
'composite' : text_template,
|
||||||
|
'composite.re' : text_re_template,
|
||||||
|
'composite.empty' : text_empty_template,
|
||||||
|
'enumeration' : text_template,
|
||||||
|
'enumeration.re' : text_re_template,
|
||||||
|
'enumeration.empty' : text_empty_template,
|
||||||
|
'comments' : text_template,
|
||||||
|
'comments.re' : text_re_template,
|
||||||
|
'comments.empty' : text_empty_template,
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, parent, db, txt, mi):
|
def __init__(self, parent, db, txt, mi):
|
||||||
QDialog.__init__(self, parent)
|
QDialog.__init__(self, parent)
|
||||||
self.setWindowTitle(_('Coloring Wizard'))
|
self.setWindowTitle(_('Coloring Wizard'))
|
||||||
@ -106,10 +139,18 @@ class TagWizard(QDialog):
|
|||||||
self.completion_values = defaultdict(dict)
|
self.completion_values = defaultdict(dict)
|
||||||
for k in db.all_field_keys():
|
for k in db.all_field_keys():
|
||||||
m = db.metadata_for_field(k)
|
m = db.metadata_for_field(k)
|
||||||
if m['datatype'] in ('text', 'enumeration', 'series') and \
|
if k.endswith('_index') or (
|
||||||
m['is_category'] and k not in ('identifiers'):
|
m['kind'] == 'field' and m['name'] and
|
||||||
|
k not in ('ondevice', 'path', 'size', 'sort') and
|
||||||
|
m['datatype'] not in ('datetime')):
|
||||||
self.columns.append(k)
|
self.columns.append(k)
|
||||||
|
self.completion_values[k]['dt'] = m['datatype']
|
||||||
if m['is_custom']:
|
if m['is_custom']:
|
||||||
|
if m['datatype'] in ('int', 'float'):
|
||||||
|
self.completion_values[k]['v'] = []
|
||||||
|
elif m['datatype'] == 'bool':
|
||||||
|
self.completion_values[k]['v'] = [_('Yes'), _('No')]
|
||||||
|
else:
|
||||||
self.completion_values[k]['v'] = db.all_custom(m['label'])
|
self.completion_values[k]['v'] = db.all_custom(m['label'])
|
||||||
elif k == 'tags':
|
elif k == 'tags':
|
||||||
self.completion_values[k]['v'] = db.all_tags()
|
self.completion_values[k]['v'] = db.all_tags()
|
||||||
@ -127,12 +168,15 @@ class TagWizard(QDialog):
|
|||||||
replace('|', ',') for v in f()]
|
replace('|', ',') for v in f()]
|
||||||
else:
|
else:
|
||||||
self.completion_values[k]['v'] = [v[1] for v in f()]
|
self.completion_values[k]['v'] = [v[1] for v in f()]
|
||||||
|
else:
|
||||||
|
self.completion_values[k]['v'] = []
|
||||||
|
|
||||||
if k in self.completion_values:
|
if k in self.completion_values:
|
||||||
if k == 'authors':
|
if k == 'authors':
|
||||||
self.completion_values[k]['m'] = None
|
mult = '&'
|
||||||
else:
|
else:
|
||||||
self.completion_values[k]['m'] = m['is_multiple']
|
mult = ',' if m['is_multiple'] == '|' else m['is_multiple']
|
||||||
|
self.completion_values[k]['m'] = mult
|
||||||
|
|
||||||
self.columns.sort(key=sort_key)
|
self.columns.sort(key=sort_key)
|
||||||
self.columns.insert(0, '')
|
self.columns.insert(0, '')
|
||||||
@ -140,12 +184,12 @@ class TagWizard(QDialog):
|
|||||||
l = QGridLayout()
|
l = QGridLayout()
|
||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
l.setColumnStretch(2, 10)
|
l.setColumnStretch(2, 10)
|
||||||
l.setColumnMinimumWidth(3, 300)
|
l.setColumnMinimumWidth(5, 300)
|
||||||
|
|
||||||
h = QLabel(_('And'))
|
h = QLabel(_('And'))
|
||||||
h.setToolTip('<p>' +
|
h.setToolTip('<p>' +
|
||||||
_('Set this box to indicate that the two conditions must both '
|
_('Set this box to indicate that the two conditions must both '
|
||||||
'be true to return the "color if value found". For example, you '
|
'be true to use the color. For example, you '
|
||||||
'can check if two tags are present, if the book has a tag '
|
'can check if two tags are present, if the book has a tag '
|
||||||
'and a #read custom column is checked, or if a book has '
|
'and a #read custom column is checked, or if a book has '
|
||||||
'some tag and has a particular format.'))
|
'some tag and has a particular format.'))
|
||||||
@ -155,107 +199,106 @@ class TagWizard(QDialog):
|
|||||||
h.setAlignment(Qt.AlignCenter)
|
h.setAlignment(Qt.AlignCenter)
|
||||||
l.addWidget(h, 0, 1, 1, 1)
|
l.addWidget(h, 0, 1, 1, 1)
|
||||||
|
|
||||||
h = QLabel(_('Not'))
|
h = QLabel(_('is'))
|
||||||
h.setToolTip('<p>' +
|
|
||||||
_('Set this box to indicate that the value must <b>not</b> match '
|
|
||||||
'to return the "color if value found". For example, you '
|
|
||||||
'can check if a tag does not exist by entering that tag '
|
|
||||||
'and checking this box. You can check if tags are empty by '
|
|
||||||
'checking this box, entering .* (period asterisk) for the text, '
|
|
||||||
'then checking the RE box. The .* regular expression matches '
|
|
||||||
'anything, so if this box is checked, it matches nothing. '
|
|
||||||
'This box is particularly useful when using the AND box.'))
|
|
||||||
h.setAlignment(Qt.AlignCenter)
|
h.setAlignment(Qt.AlignCenter)
|
||||||
l.addWidget(h, 0, 2, 1, 1)
|
l.addWidget(h, 0, 2, 1, 1)
|
||||||
|
|
||||||
h = QLabel(_('Values (see the popup help for more information)'))
|
h = QLabel(_('not'))
|
||||||
|
h.setToolTip('<p>' +
|
||||||
|
_('Check this box to indicate that the value must <b>not</b> match '
|
||||||
|
'to use the color. For example, you can check if a tag does '
|
||||||
|
'not exist by entering that tag and checking this box.') + '</p>')
|
||||||
|
h.setAlignment(Qt.AlignCenter)
|
||||||
|
l.addWidget(h, 0, 3, 1, 1)
|
||||||
|
|
||||||
|
c = QLabel(_('empty'))
|
||||||
|
c.setToolTip('<p>' +
|
||||||
|
_('Check this box to check if the column is empty') + '</p>')
|
||||||
|
l.addWidget(c, 0, 4, 1, 1)
|
||||||
|
|
||||||
|
h = QLabel(_('Values'))
|
||||||
h.setAlignment(Qt.AlignCenter)
|
h.setAlignment(Qt.AlignCenter)
|
||||||
h.setToolTip('<p>' +
|
h.setToolTip('<p>' +
|
||||||
_('You can enter more than one value per box, separated by commas. '
|
_('You can enter more than one value per box, separated by commas. '
|
||||||
'The comparison ignores letter case. Special note: you can '
|
'The comparison ignores letter case. Special note: authors are '
|
||||||
'enter at most one author.<br>'
|
'separated by ampersands (&).<br>'
|
||||||
'A value can be a regular expression. Check the box to turn '
|
'A value can be a regular expression. Check the box to turn '
|
||||||
'them on. When using regular expressions, note that the wizard '
|
'them on. When using regular expressions, note that the wizard '
|
||||||
'puts anchors (^ and $) around the expression, so you '
|
'puts anchors (^ and $) around the expression, so you '
|
||||||
'must ensure your expression matches from the beginning '
|
'must ensure your expression matches from the beginning '
|
||||||
'to the end of the column you are checking.<br>'
|
'to the end of the column/value you are checking.<br>'
|
||||||
'Regular expression examples:') + '<ul>' +
|
'Regular expression examples:') + '<ul>' +
|
||||||
_('<li><code><b>.*</b></code> matches anything in the column. No '
|
_('<li><code><b>.*</b></code> matches anything in the column.</li>'
|
||||||
'empty values are checked, so you don\'t need to worry about '
|
|
||||||
'empty strings</li>'
|
|
||||||
'<li><code><b>A.*</b></code> matches anything beginning with A</li>'
|
'<li><code><b>A.*</b></code> matches anything beginning with A</li>'
|
||||||
'<li><code><b>.*mystery.*</b></code> matches anything containing '
|
'<li><code><b>.*mystery.*</b></code> matches anything containing '
|
||||||
'the word "mystery"</li>') + '</ul></p>')
|
'the word "mystery"</li>') + '</ul></p>')
|
||||||
l.addWidget(h , 0, 3, 1, 1)
|
l.addWidget(h , 0, 5, 1, 1)
|
||||||
|
|
||||||
c = QLabel(_('is RE'))
|
c = QLabel(_('is RE'))
|
||||||
c.setToolTip('<p>' +
|
c.setToolTip('<p>' +
|
||||||
_('Check this box if the values box contains regular expressions') + '</p>')
|
_('Check this box if the values box contains regular expressions') + '</p>')
|
||||||
l.addWidget(c, 0, 4, 1, 1)
|
|
||||||
|
|
||||||
c = QLabel(_('Color if value found'))
|
|
||||||
c.setToolTip('<p>' +
|
|
||||||
_('At least one of the two color boxes must have a value. Leave '
|
|
||||||
'one color box empty if you want the template to use the next '
|
|
||||||
'line in this wizard. If both boxes are filled in, the rest of '
|
|
||||||
'the lines in this wizard will be ignored.') + '</p>')
|
|
||||||
l.addWidget(c, 0, 5, 1, 1)
|
|
||||||
c = QLabel(_('Color if value not found'))
|
|
||||||
c.setToolTip('<p>' +
|
|
||||||
_('This box is usually filled in only on the last test. If it is '
|
|
||||||
'filled in before the last test, then the color for value found box '
|
|
||||||
'must be empty or all the rest of the tests will be ignored.') + '</p>')
|
|
||||||
l.addWidget(c, 0, 6, 1, 1)
|
l.addWidget(c, 0, 6, 1, 1)
|
||||||
|
|
||||||
|
c = QLabel(_('color'))
|
||||||
|
c.setAlignment(Qt.AlignCenter)
|
||||||
|
c.setToolTip('<p>' +
|
||||||
|
_('Use this color if the column matches the tests.') + '</p>')
|
||||||
|
l.addWidget(c, 0, 7, 1, 1)
|
||||||
|
|
||||||
self.andboxes = []
|
self.andboxes = []
|
||||||
self.notboxes = []
|
self.notboxes = []
|
||||||
self.tagboxes = []
|
self.tagboxes = []
|
||||||
self.colorboxes = []
|
self.colorboxes = []
|
||||||
self.nfcolorboxes = []
|
|
||||||
self.reboxes = []
|
self.reboxes = []
|
||||||
self.colboxes = []
|
self.colboxes = []
|
||||||
|
self.emptyboxes = []
|
||||||
|
|
||||||
self.colors = [unicode(s) for s in list(QColor.colorNames())]
|
self.colors = [unicode(s) for s in list(QColor.colorNames())]
|
||||||
self.colors.insert(0, '')
|
self.colors.insert(0, '')
|
||||||
|
|
||||||
|
def create_widget(klass, box, layout, row, col, items,
|
||||||
|
align=Qt.AlignCenter, rowspan=False):
|
||||||
|
w = klass(self)
|
||||||
|
if box is not None:
|
||||||
|
box.append(w)
|
||||||
|
if rowspan:
|
||||||
|
layout.addWidget(w, row, col, 2, 1, alignment=Qt.Alignment(align))
|
||||||
|
else:
|
||||||
|
layout.addWidget(w, row, col, 1, 1, alignment=Qt.Alignment(align))
|
||||||
|
if items:
|
||||||
|
w.addItems(items)
|
||||||
|
return w
|
||||||
|
|
||||||
maxlines = 10
|
maxlines = 10
|
||||||
for i in range(1, maxlines+1):
|
for i in range(1, maxlines+1):
|
||||||
ab = QCheckBox(self)
|
w = create_widget(QCheckBox, self.andboxes, l, i, 0, None, rowspan=True)
|
||||||
self.andboxes.append(ab)
|
w.stateChanged.connect(partial(self.and_box_changed, line=i-1))
|
||||||
if i != maxlines:
|
if i == maxlines:
|
||||||
# let the last box float in space
|
# last box is invisible
|
||||||
l.addWidget(ab, i, 0, 2, 1)
|
w.setVisible(False)
|
||||||
ab.stateChanged.connect(partial(self.and_box_changed, line=i-1))
|
|
||||||
else:
|
|
||||||
ab.setVisible(False)
|
|
||||||
|
|
||||||
w = QComboBox(self)
|
w = create_widget(QComboBox, self.colboxes, l, i, 1, self.columns)
|
||||||
w.addItems(self.columns)
|
w.currentIndexChanged[str].connect(partial(self.column_changed, line=i-1))
|
||||||
l.addWidget(w, i, 1, 1, 1)
|
|
||||||
self.colboxes.append(w)
|
|
||||||
|
|
||||||
nb = QCheckBox(self)
|
w = QLabel(self)
|
||||||
self.notboxes.append(nb)
|
w.setText(_('is'))
|
||||||
l.addWidget(nb, i, 2, 1, 1)
|
l.addWidget(w, i, 2, 1, 1)
|
||||||
|
|
||||||
tb = MultiCompleteLineEdit(self)
|
create_widget(QCheckBox, self.notboxes, l, i, 3, None)
|
||||||
tb.set_separator(', ')
|
|
||||||
self.tagboxes.append(tb)
|
|
||||||
l.addWidget(tb, i, 3, 1, 1)
|
|
||||||
w.currentIndexChanged[str].connect(partial(self.column_changed, valbox=tb))
|
|
||||||
|
|
||||||
w = QCheckBox(self)
|
w = create_widget(QCheckBox, self.emptyboxes, l, i, 4, None)
|
||||||
self.reboxes.append(w)
|
w.stateChanged.connect(partial(self.empty_box_changed, line=i-1))
|
||||||
l.addWidget(w, i, 4, 1, 1)
|
|
||||||
|
|
||||||
w = QComboBox(self)
|
create_widget(MultiCompleteLineEdit, self.tagboxes, l, i, 5, None, align=0)
|
||||||
w.addItems(self.colors)
|
create_widget(QCheckBox, self.reboxes, l, i, 6, None)
|
||||||
self.colorboxes.append(w)
|
create_widget(QComboBox, self.colorboxes, l, i, 7, self.colors)
|
||||||
l.addWidget(w, i, 5, 1, 1)
|
|
||||||
|
|
||||||
w = QComboBox(self)
|
w = create_widget(QLabel, None, l, maxlines+1, 5, None)
|
||||||
w.addItems(self.colors)
|
w.setText(_('If none of the tests match, set the color to'))
|
||||||
self.nfcolorboxes.append(w)
|
self.elsebox = create_widget(QComboBox, None, l, maxlines+1, 7, self.colors)
|
||||||
l.addWidget(w, i, 6, 1, 1)
|
self.elsebox.setToolTip('<p>' +
|
||||||
|
_('If this box contains a color, it will be used if none '
|
||||||
|
'of the above rules match.') + '</p>')
|
||||||
|
|
||||||
if txt:
|
if txt:
|
||||||
lines = txt.split('\n')[3:]
|
lines = txt.split('\n')[3:]
|
||||||
@ -263,25 +306,27 @@ class TagWizard(QDialog):
|
|||||||
for line in lines:
|
for line in lines:
|
||||||
if line.startswith('#'):
|
if line.startswith('#'):
|
||||||
vals = line[1:].split(':|:')
|
vals = line[1:].split(':|:')
|
||||||
|
if len(vals) == 1 and line.startswith('#else:'):
|
||||||
|
try:
|
||||||
|
self.elsebox.setCurrentIndex(self.elsebox.findText(line[6:]))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
continue
|
||||||
if len(vals) == 2:
|
if len(vals) == 2:
|
||||||
t, c = vals
|
t, c = vals
|
||||||
nc = ''
|
|
||||||
re = False
|
|
||||||
f = 'tags'
|
f = 'tags'
|
||||||
a = False
|
a = n = e = re = False
|
||||||
n = False
|
|
||||||
else:
|
else:
|
||||||
t,c,f,nc,re,a,n = vals
|
t,c,f,re,a,n,e = vals
|
||||||
try:
|
try:
|
||||||
self.colboxes[i].setCurrentIndex(self.colboxes[i].findText(f))
|
self.colboxes[i].setCurrentIndex(self.colboxes[i].findText(f))
|
||||||
self.colorboxes[i].setCurrentIndex(
|
self.colorboxes[i].setCurrentIndex(
|
||||||
self.colorboxes[i].findText(c))
|
self.colorboxes[i].findText(c))
|
||||||
self.nfcolorboxes[i].setCurrentIndex(
|
|
||||||
self.nfcolorboxes[i].findText(nc))
|
|
||||||
self.tagboxes[i].setText(t)
|
self.tagboxes[i].setText(t)
|
||||||
self.reboxes[i].setChecked(re == '2')
|
self.reboxes[i].setChecked(re == '2')
|
||||||
self.andboxes[i].setChecked(a == '2')
|
self.andboxes[i].setChecked(a == '2')
|
||||||
self.notboxes[i].setChecked(n == '2')
|
self.notboxes[i].setChecked(n == '2')
|
||||||
|
self.emptyboxes[i].setChecked(e == '2')
|
||||||
i += 1
|
i += 1
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@ -290,13 +335,17 @@ class TagWizard(QDialog):
|
|||||||
l.addWidget(w, 99, 1, 1, 1)
|
l.addWidget(w, 99, 1, 1, 1)
|
||||||
w = self.test_box = QLineEdit(self)
|
w = self.test_box = QLineEdit(self)
|
||||||
w.setReadOnly(True)
|
w.setReadOnly(True)
|
||||||
l.addWidget(w, 99, 3, 1, 1)
|
l.addWidget(w, 99, 2, 1, 5)
|
||||||
w = QPushButton(_('Test'))
|
w = QPushButton(_('Test'))
|
||||||
l.addWidget(w, 99, 5, 1, 1)
|
w.setToolTip('<p>' +
|
||||||
|
_('Press this button to see what color this template will '
|
||||||
|
'produce for the book that was selected when you '
|
||||||
|
'entered the preferences dialog.'))
|
||||||
|
l.addWidget(w, 99, 7, 1, 1)
|
||||||
w.clicked.connect(self.preview)
|
w.clicked.connect(self.preview)
|
||||||
|
|
||||||
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self)
|
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self)
|
||||||
l.addWidget(bb, 100, 3, 1, 2)
|
l.addWidget(bb, 100, 5, 1, 3)
|
||||||
bb.accepted.connect(self.accepted)
|
bb.accepted.connect(self.accepted)
|
||||||
bb.rejected.connect(self.reject)
|
bb.rejected.connect(self.reject)
|
||||||
self.template = ''
|
self.template = ''
|
||||||
@ -308,14 +357,22 @@ class TagWizard(QDialog):
|
|||||||
_('EXCEPTION'), self.mi)
|
_('EXCEPTION'), self.mi)
|
||||||
self.test_box.setText(t)
|
self.test_box.setText(t)
|
||||||
|
|
||||||
def column_changed(self, s, valbox=None):
|
def column_changed(self, s, line=None):
|
||||||
k = unicode(s)
|
k = unicode(s)
|
||||||
if k in self.completion_values:
|
if k in self.completion_values:
|
||||||
|
valbox = self.tagboxes[line]
|
||||||
valbox.update_items_cache(self.completion_values[k]['v'])
|
valbox.update_items_cache(self.completion_values[k]['v'])
|
||||||
if self.completion_values[k]['m']:
|
if self.completion_values[k]['m']:
|
||||||
valbox.set_separator(', ')
|
valbox.set_separator(', ')
|
||||||
else:
|
else:
|
||||||
valbox.set_separator(None)
|
valbox.set_separator(None)
|
||||||
|
|
||||||
|
dt = self.completion_values[k]['dt']
|
||||||
|
if dt in ('int', 'float', 'rating', 'bool'):
|
||||||
|
self.reboxes[line].setChecked(0)
|
||||||
|
self.reboxes[line].setEnabled(False)
|
||||||
|
else:
|
||||||
|
self.reboxes[line].setEnabled(True)
|
||||||
else:
|
else:
|
||||||
valbox.update_items_cache([])
|
valbox.update_items_cache([])
|
||||||
valbox.set_separator(None)
|
valbox.set_separator(None)
|
||||||
@ -324,59 +381,44 @@ class TagWizard(QDialog):
|
|||||||
res = ("program:\n#tag wizard -- do not directly edit\n"
|
res = ("program:\n#tag wizard -- do not directly edit\n"
|
||||||
" first_non_empty(\n")
|
" first_non_empty(\n")
|
||||||
lines = []
|
lines = []
|
||||||
was_and = False
|
was_and = had_line = False
|
||||||
had_line = False
|
|
||||||
|
|
||||||
line = 0
|
line = 0
|
||||||
for tb, cb, fb, nfcb, reb, ab, nb in zip(
|
for tb, cb, fb, reb, ab, nb, eb in zip(
|
||||||
self.tagboxes, self.colorboxes, self.colboxes,
|
self.tagboxes, self.colorboxes, self.colboxes,
|
||||||
self.nfcolorboxes, self.reboxes, self.andboxes, self.notboxes):
|
self.reboxes, self.andboxes, self.notboxes, self.emptyboxes):
|
||||||
f = unicode(fb.currentText())
|
f = unicode(fb.currentText())
|
||||||
if not f:
|
if not f:
|
||||||
continue
|
continue
|
||||||
m = self.completion_values[f]['m']
|
m = self.completion_values[f]['m']
|
||||||
|
dt = self.completion_values[f]['dt']
|
||||||
c = unicode(cb.currentText()).strip()
|
c = unicode(cb.currentText()).strip()
|
||||||
nfc = unicode(nfcb.currentText()).strip()
|
|
||||||
re = reb.checkState()
|
re = reb.checkState()
|
||||||
a = ab.checkState()
|
a = ab.checkState()
|
||||||
n = nb.checkState()
|
n = nb.checkState()
|
||||||
|
e = eb.checkState()
|
||||||
line += 1
|
line += 1
|
||||||
|
|
||||||
if n == 2:
|
tval = '' if n == 2 else '1'
|
||||||
tval = ''
|
fval = '1' if n == 2 else ''
|
||||||
fval = '1'
|
|
||||||
else:
|
|
||||||
tval = '1'
|
|
||||||
fval = ''
|
|
||||||
|
|
||||||
if m:
|
if m:
|
||||||
tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()]
|
tags = [t.strip() for t in unicode(tb.text()).split(m) if t.strip()]
|
||||||
if re == 2:
|
if re == 2:
|
||||||
tags = '$|^'.join(tags)
|
tags = '$|^'.join(tags)
|
||||||
else:
|
else:
|
||||||
tags = ','.join(tags)
|
tags = m.join(tags)
|
||||||
|
if m == '&':
|
||||||
|
tags = tags.replace(',', '|')
|
||||||
else:
|
else:
|
||||||
tags = unicode(tb.text()).strip()
|
tags = unicode(tb.text()).strip()
|
||||||
if f == 'authors':
|
|
||||||
tags.replace(',', '|')
|
|
||||||
|
|
||||||
if (tags or f) and not (tags and f and (a == 2 or c)):
|
if (tags or f) and not ((tags or e) and f and (a == 2 or c)):
|
||||||
error_dialog(self, _('Invalid line'),
|
error_dialog(self, _('Invalid line'),
|
||||||
_('Line number {0} is not valid').format(line),
|
_('Line number {0} is not valid').format(line),
|
||||||
show=True, show_copy_button=False)
|
show=True, show_copy_button=False)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if c not in self.colors:
|
|
||||||
error_dialog(self, _('Invalid color'),
|
|
||||||
_('The color {0} is not valid').format(c),
|
|
||||||
show=True, show_copy_button=False)
|
|
||||||
return False
|
|
||||||
if nfc not in self.colors:
|
|
||||||
error_dialog(self, _('Invalid color'),
|
|
||||||
_('The color {0} is not valid').format(nfc),
|
|
||||||
show=True, show_copy_button=False)
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not was_and:
|
if not was_and:
|
||||||
if had_line:
|
if had_line:
|
||||||
lines[-1] += ','
|
lines[-1] += ','
|
||||||
@ -385,57 +427,58 @@ class TagWizard(QDialog):
|
|||||||
else:
|
else:
|
||||||
lines[-1] += ','
|
lines[-1] += ','
|
||||||
|
|
||||||
if re == 2:
|
key = dt + ('.mult' if m else '') + ('.empty' if e else '') + ('.re' if re else '')
|
||||||
if m:
|
template = self.templates[key]
|
||||||
lines.append(" in_list(field('{1}'), ',', '^{0}$', '{2}', '{3}')".\
|
lines.append(template.format(v=tags, f=f, tv=tval, fv=fval, mult=m))
|
||||||
format(tags, f, tval, fval))
|
|
||||||
else:
|
|
||||||
lines.append(" contains(field('{1}'), '{0}', '{2}', '{3}')".\
|
|
||||||
format(tags, f, tval, fval))
|
|
||||||
else:
|
|
||||||
if m:
|
|
||||||
lines.append(" str_in_list(field('{1}'), ',', '{0}', '{2}', '{3}')".\
|
|
||||||
format(tags, f, tval, fval))
|
|
||||||
else:
|
|
||||||
lines.append(" strcmp(field('{1}'), '{0}', '{3}', '{2}', '{3}')".\
|
|
||||||
format(tags, f, tval, fval))
|
|
||||||
if a == 2:
|
if a == 2:
|
||||||
was_and = True
|
was_and = True
|
||||||
else:
|
else:
|
||||||
was_and = False
|
was_and = False
|
||||||
lines.append(" ), '{0}', '{1}')".format(c, nfc))
|
lines.append(" ), '{0}', '')".format(c))
|
||||||
|
|
||||||
res += '\n'.join(lines)
|
res += '\n'.join(lines)
|
||||||
|
else_txt = unicode(self.elsebox.currentText())
|
||||||
|
if else_txt:
|
||||||
|
res += ",\n '" + else_txt + "'"
|
||||||
res += ')\n'
|
res += ')\n'
|
||||||
self.template = res
|
self.template = res
|
||||||
res = ''
|
res = ''
|
||||||
for tb, cb, fb, nfcb, reb, ab, nb in zip(
|
for tb, cb, fb, reb, ab, nb, eb in zip(
|
||||||
self.tagboxes, self.colorboxes, self.colboxes,
|
self.tagboxes, self.colorboxes, self.colboxes,
|
||||||
self.nfcolorboxes, self.reboxes, self.andboxes, self.notboxes):
|
self.reboxes, self.andboxes, self.notboxes, self.emptyboxes):
|
||||||
t = unicode(tb.text()).strip()
|
t = unicode(tb.text()).strip()
|
||||||
if t.endswith(','):
|
if t.endswith(','):
|
||||||
t = t[:-1]
|
t = t[:-1]
|
||||||
c = unicode(cb.currentText()).strip()
|
c = unicode(cb.currentText()).strip()
|
||||||
f = unicode(fb.currentText())
|
f = unicode(fb.currentText())
|
||||||
nfc = unicode(nfcb.currentText()).strip()
|
|
||||||
re = unicode(reb.checkState())
|
re = unicode(reb.checkState())
|
||||||
a = unicode(ab.checkState())
|
a = unicode(ab.checkState())
|
||||||
n = unicode(nb.checkState())
|
n = unicode(nb.checkState())
|
||||||
if f and t and (a == '2' or c):
|
e = unicode(eb.checkState())
|
||||||
res += '#' + t + ':|:' + c + ':|:' + f + ':|:' + \
|
if f and (t or e) and (a == '2' or c):
|
||||||
nfc + ':|:' + re + ':|:' + a + ':|:' + n + '\n'
|
res += '#' + t + ':|:' + c + ':|:' + f + ':|:' + re + ':|:' + \
|
||||||
|
a + ':|:' + n + ':|:' + e + '\n'
|
||||||
|
res += '#else:' + else_txt + '\n'
|
||||||
self.template += res
|
self.template += res
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def empty_box_changed(self, state, line=None):
|
||||||
|
if state == 2:
|
||||||
|
self.tagboxes[line].setText('')
|
||||||
|
self.tagboxes[line].setEnabled(False)
|
||||||
|
self.reboxes[line].setChecked(0)
|
||||||
|
self.reboxes[line].setEnabled(False)
|
||||||
|
else:
|
||||||
|
self.reboxes[line].setEnabled(True)
|
||||||
|
self.tagboxes[line].setEnabled(True)
|
||||||
|
|
||||||
def and_box_changed(self, state, line=None):
|
def and_box_changed(self, state, line=None):
|
||||||
if state == 2:
|
if state == 2:
|
||||||
self.colorboxes[line].setCurrentIndex(0)
|
self.colorboxes[line].setCurrentIndex(0)
|
||||||
self.colorboxes[line].setEnabled(False)
|
self.colorboxes[line].setEnabled(False)
|
||||||
self.nfcolorboxes[line].setCurrentIndex(0)
|
|
||||||
self.nfcolorboxes[line].setEnabled(False)
|
|
||||||
else:
|
else:
|
||||||
self.colorboxes[line].setEnabled(True)
|
self.colorboxes[line].setEnabled(True)
|
||||||
self.nfcolorboxes[line].setEnabled(True)
|
|
||||||
|
|
||||||
def accepted(self):
|
def accepted(self):
|
||||||
if self.generate_program():
|
if self.generate_program():
|
||||||
|
@ -268,20 +268,14 @@ The following functions are available in addition to those described in single-f
|
|||||||
* ``subtract(x, y)`` -- returns x - y. Throws an exception if either x or y are not numbers.
|
* ``subtract(x, y)`` -- returns x - y. Throws an exception if either x or y are not numbers.
|
||||||
* ``template(x)`` -- evaluates x as a template. The evaluation is done in its own context, meaning that variables are not shared between the caller and the template evaluation. Because the `{` and `}` characters are special, you must use `[[` for the `{` character and `]]` for the '}' character; they are converted automatically. For example, ``template('[[title_sort]]') will evaluate the template ``{title_sort}`` and return its value.
|
* ``template(x)`` -- evaluates x as a template. The evaluation is done in its own context, meaning that variables are not shared between the caller and the template evaluation. Because the `{` and `}` characters are special, you must use `[[` for the `{` character and `]]` for the '}' character; they are converted automatically. For example, ``template('[[title_sort]]') will evaluate the template ``{title_sort}`` and return its value.
|
||||||
|
|
||||||
Function classification summary:
|
Function classification
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 3
|
||||||
|
|
||||||
|
template_ref
|
||||||
|
|
||||||
* Get values from metadata: ``field``. ``raw_field``. In some situations, ``lookup`` can be used in place of ``field``.
|
|
||||||
* Arithmetic: ``add``, ``subtract``, ``multiply``, ``divide``
|
|
||||||
* Boolean: ``and``, ``or``, ``not``. The function ``if_empty`` is similar to ``and`` called with one argument.
|
|
||||||
* If-then-else: ``contains``, ``test``
|
|
||||||
* Iterating over values: ``first_non_empty``, ``lookup``, ``switch``
|
|
||||||
* List lookup: ``in_list``, ``list_item``, ``select``, ``str_in_list``
|
|
||||||
* List manipulation: ``count``, ``merge_lists``, ``sublist``, ``subitems``
|
|
||||||
* Recursion: ``eval``, ``template``
|
|
||||||
* Relational: ``cmp`` (for numbers), ``strcmp`` (for strings)
|
|
||||||
* String case changes: ``lowercase``, ``uppercase``, ``titlecase``, ``capitalize``
|
|
||||||
* String manipulation: ``re``, ``shorten``, ``substr``
|
|
||||||
* Other: ``assign``, ``booksize``, ``format_date``, ``ondevice`` ``print``
|
|
||||||
|
|
||||||
.. _general_mode:
|
.. _general_mode:
|
||||||
|
|
||||||
@ -426,19 +420,8 @@ You might find the following tips useful.
|
|||||||
* In a plugboard, you can set a field to empty (or whatever is equivalent to empty) by using the special template ``{null}``. This template will always evaluate to an empty string.
|
* In a plugboard, you can set a field to empty (or whatever is equivalent to empty) by using the special template ``{null}``. This template will always evaluate to an empty string.
|
||||||
* The technique described above to show numbers even if they have a zero value works with the standard field series_index.
|
* The technique described above to show numbers even if they have a zero value works with the standard field series_index.
|
||||||
|
|
||||||
API of the Metadata objects
|
.. toctree::
|
||||||
----------------------------
|
:hidden:
|
||||||
|
|
||||||
.. module:: calibre.ebooks.metadata.book.base
|
template_ref
|
||||||
|
|
||||||
.. autoclass:: Metadata
|
|
||||||
:members:
|
|
||||||
:member-order: bysource
|
|
||||||
|
|
||||||
.. data:: STANDARD_METADATA_FIELDS
|
|
||||||
|
|
||||||
The set of standard metadata fields.
|
|
||||||
|
|
||||||
.. literalinclude:: ../ebooks/metadata/book/__init__.py
|
|
||||||
:lines: 7-
|
|
||||||
|
|
||||||
|
266
src/calibre/manual/template_ref.rst
Normal file
266
src/calibre/manual/template_ref.rst
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
.. include:: global.rst
|
||||||
|
|
||||||
|
.. _templaterefcalibre:
|
||||||
|
|
||||||
|
Reference for all builtin template language functions
|
||||||
|
========================================================
|
||||||
|
|
||||||
|
Here, we document all the builtin functions available in the |app| template language. Every function is implemented as a class in python and you can click the source links to see the source code, in case the documentation is insufficient. The functions are arranged in logical groups by type.
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:depth: 2
|
||||||
|
:local:
|
||||||
|
|
||||||
|
.. module:: calibre.utils.formatter_functions
|
||||||
|
|
||||||
|
Get values from metadata
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
field(name)
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinField
|
||||||
|
|
||||||
|
raw_field(name)
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinRaw_field
|
||||||
|
|
||||||
|
booksize()
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinBooksize
|
||||||
|
|
||||||
|
format_date(val, format_string)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinFormat_date
|
||||||
|
|
||||||
|
ondevice()
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinOndevice
|
||||||
|
|
||||||
|
Arithmetic
|
||||||
|
-------------
|
||||||
|
|
||||||
|
add(x, y)
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
.. autoclass:: BuiltinAdd
|
||||||
|
|
||||||
|
subtract(x, y)
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinSubtract
|
||||||
|
|
||||||
|
multiply(x, y)
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinMultiply
|
||||||
|
|
||||||
|
divide(x, y)
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinDivide
|
||||||
|
|
||||||
|
Boolean
|
||||||
|
------------
|
||||||
|
|
||||||
|
and(value1, value2, ...)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinAnd
|
||||||
|
|
||||||
|
or(value1, value2, ...)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinOr
|
||||||
|
|
||||||
|
not(value)
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinNot
|
||||||
|
|
||||||
|
If-then-else
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
contains(val, pattern, text if match, text if not match)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinContains
|
||||||
|
|
||||||
|
test(val, text if not empty, text if empty)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinTest
|
||||||
|
|
||||||
|
ifempty(val, text if empty)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinIfempty
|
||||||
|
|
||||||
|
Iterating over values
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
first_non_empty(value, value, ...)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinFirstNonEmpty
|
||||||
|
|
||||||
|
lookup(val, pattern, field, pattern, field, ..., else_field)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinLookup
|
||||||
|
|
||||||
|
switch(val, pattern, value, pattern, value, ..., else_value)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinSwitch
|
||||||
|
|
||||||
|
List Lookup
|
||||||
|
---------------
|
||||||
|
|
||||||
|
in_list(val, separator, pattern, found_val, not_found_val)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinInList
|
||||||
|
|
||||||
|
str_in_list(val, separator, string, found_val, not_found_val)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinStrInList
|
||||||
|
|
||||||
|
list_item(val, index, separator)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinListitem
|
||||||
|
|
||||||
|
select(val, key)
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinSelect
|
||||||
|
|
||||||
|
|
||||||
|
List Manipulation
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
count(val, separator)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinCount
|
||||||
|
|
||||||
|
merge_lists(list1, list2, separator)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinMergeLists
|
||||||
|
|
||||||
|
sublist(val, start_index, end_index, separator)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinSublist
|
||||||
|
|
||||||
|
subitems(val, start_index, end_index)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinSubitems
|
||||||
|
|
||||||
|
Recursion
|
||||||
|
-------------
|
||||||
|
|
||||||
|
eval(template)
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinEval
|
||||||
|
|
||||||
|
template(x)
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinTemplate
|
||||||
|
|
||||||
|
Relational
|
||||||
|
-----------
|
||||||
|
|
||||||
|
cmp(x, y, lt, eq, gt)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinCmp
|
||||||
|
|
||||||
|
strcmp(x, y, lt, eq, gt)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinStrcmp
|
||||||
|
|
||||||
|
String case changes
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
lowercase(val)
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinLowercase
|
||||||
|
|
||||||
|
uppercase(val)
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinUppercase
|
||||||
|
|
||||||
|
titlecase(val)
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinTitlecase
|
||||||
|
|
||||||
|
capitalize(val)
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinCapitalize
|
||||||
|
|
||||||
|
String Manipulation
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
re(val, pattern, replacement)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinRe
|
||||||
|
|
||||||
|
shorten(val, left chars, middle text, right chars)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinShorten
|
||||||
|
|
||||||
|
substr(str, start, end)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinSubstr
|
||||||
|
|
||||||
|
|
||||||
|
Other
|
||||||
|
--------
|
||||||
|
|
||||||
|
assign(id, val)
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinAssign
|
||||||
|
|
||||||
|
print(a, b, ...)
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: BuiltinPrint
|
||||||
|
|
||||||
|
|
||||||
|
API of the Metadata objects
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
The python implementation of the template functions is passed in a Metadata object. Knowing it's API is useful if you want to define your own template functions.
|
||||||
|
|
||||||
|
.. module:: calibre.ebooks.metadata.book.base
|
||||||
|
|
||||||
|
.. autoclass:: Metadata
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
|
.. data:: STANDARD_METADATA_FIELDS
|
||||||
|
|
||||||
|
The set of standard metadata fields.
|
||||||
|
|
||||||
|
.. literalinclude:: ../ebooks/metadata/book/__init__.py
|
||||||
|
:lines: 7-
|
||||||
|
|
@ -87,7 +87,7 @@ class BuiltinFormatterFunction(FormatterFunction):
|
|||||||
class BuiltinStrcmp(BuiltinFormatterFunction):
|
class BuiltinStrcmp(BuiltinFormatterFunction):
|
||||||
name = 'strcmp'
|
name = 'strcmp'
|
||||||
arg_count = 5
|
arg_count = 5
|
||||||
doc = _('strcmp(x, y, lt, eq, gt) -- does a case-insensitive comparison of x '
|
__doc__ = doc = _('strcmp(x, y, lt, eq, gt) -- does a case-insensitive comparison of x '
|
||||||
'and y as strings. Returns lt if x < y. Returns eq if x == y. '
|
'and y as strings. Returns lt if x < y. Returns eq if x == y. '
|
||||||
'Otherwise returns gt.')
|
'Otherwise returns gt.')
|
||||||
|
|
||||||
@ -102,12 +102,12 @@ class BuiltinStrcmp(BuiltinFormatterFunction):
|
|||||||
class BuiltinCmp(BuiltinFormatterFunction):
|
class BuiltinCmp(BuiltinFormatterFunction):
|
||||||
name = 'cmp'
|
name = 'cmp'
|
||||||
arg_count = 5
|
arg_count = 5
|
||||||
doc = _('cmp(x, y, lt, eq, gt) -- compares x and y after converting both to '
|
__doc__ = doc = _('cmp(x, y, lt, eq, gt) -- compares x and y after converting both to '
|
||||||
'numbers. Returns lt if x < y. Returns eq if x == y. Otherwise returns gt.')
|
'numbers. Returns lt if x < y. Returns eq if x == y. Otherwise returns gt.')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt):
|
def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt):
|
||||||
x = float(x if x else 0)
|
x = float(x if x and x != 'None' else 0)
|
||||||
y = float(y if y else 0)
|
y = float(y if y and y != 'None' else 0)
|
||||||
if x < y:
|
if x < y:
|
||||||
return lt
|
return lt
|
||||||
if x == y:
|
if x == y:
|
||||||
@ -117,7 +117,7 @@ class BuiltinCmp(BuiltinFormatterFunction):
|
|||||||
class BuiltinStrcat(BuiltinFormatterFunction):
|
class BuiltinStrcat(BuiltinFormatterFunction):
|
||||||
name = 'strcat'
|
name = 'strcat'
|
||||||
arg_count = -1
|
arg_count = -1
|
||||||
doc = _('strcat(a, b, ...) -- can take any number of arguments. Returns a '
|
__doc__ = doc = _('strcat(a, b, ...) -- can take any number of arguments. Returns a '
|
||||||
'string formed by concatenating all the arguments')
|
'string formed by concatenating all the arguments')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, *args):
|
def evaluate(self, formatter, kwargs, mi, locals, *args):
|
||||||
@ -130,7 +130,7 @@ class BuiltinStrcat(BuiltinFormatterFunction):
|
|||||||
class BuiltinAdd(BuiltinFormatterFunction):
|
class BuiltinAdd(BuiltinFormatterFunction):
|
||||||
name = 'add'
|
name = 'add'
|
||||||
arg_count = 2
|
arg_count = 2
|
||||||
doc = _('add(x, y) -- returns x + y. Throws an exception if either x or y are not numbers.')
|
__doc__ = doc = _('add(x, y) -- returns x + y. Throws an exception if either x or y are not numbers.')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, x, y):
|
def evaluate(self, formatter, kwargs, mi, locals, x, y):
|
||||||
x = float(x if x else 0)
|
x = float(x if x else 0)
|
||||||
@ -140,7 +140,7 @@ class BuiltinAdd(BuiltinFormatterFunction):
|
|||||||
class BuiltinSubtract(BuiltinFormatterFunction):
|
class BuiltinSubtract(BuiltinFormatterFunction):
|
||||||
name = 'subtract'
|
name = 'subtract'
|
||||||
arg_count = 2
|
arg_count = 2
|
||||||
doc = _('subtract(x, y) -- returns x - y. Throws an exception if either x or y are not numbers.')
|
__doc__ = doc = _('subtract(x, y) -- returns x - y. Throws an exception if either x or y are not numbers.')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, x, y):
|
def evaluate(self, formatter, kwargs, mi, locals, x, y):
|
||||||
x = float(x if x else 0)
|
x = float(x if x else 0)
|
||||||
@ -150,7 +150,7 @@ class BuiltinSubtract(BuiltinFormatterFunction):
|
|||||||
class BuiltinMultiply(BuiltinFormatterFunction):
|
class BuiltinMultiply(BuiltinFormatterFunction):
|
||||||
name = 'multiply'
|
name = 'multiply'
|
||||||
arg_count = 2
|
arg_count = 2
|
||||||
doc = _('multiply(x, y) -- returns x * y. Throws an exception if either x or y are not numbers.')
|
__doc__ = doc = _('multiply(x, y) -- returns x * y. Throws an exception if either x or y are not numbers.')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, x, y):
|
def evaluate(self, formatter, kwargs, mi, locals, x, y):
|
||||||
x = float(x if x else 0)
|
x = float(x if x else 0)
|
||||||
@ -160,7 +160,7 @@ class BuiltinMultiply(BuiltinFormatterFunction):
|
|||||||
class BuiltinDivide(BuiltinFormatterFunction):
|
class BuiltinDivide(BuiltinFormatterFunction):
|
||||||
name = 'divide'
|
name = 'divide'
|
||||||
arg_count = 2
|
arg_count = 2
|
||||||
doc = _('divide(x, y) -- returns x / y. Throws an exception if either x or y are not numbers.')
|
__doc__ = doc = _('divide(x, y) -- returns x / y. Throws an exception if either x or y are not numbers.')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, x, y):
|
def evaluate(self, formatter, kwargs, mi, locals, x, y):
|
||||||
x = float(x if x else 0)
|
x = float(x if x else 0)
|
||||||
@ -170,7 +170,7 @@ class BuiltinDivide(BuiltinFormatterFunction):
|
|||||||
class BuiltinTemplate(BuiltinFormatterFunction):
|
class BuiltinTemplate(BuiltinFormatterFunction):
|
||||||
name = 'template'
|
name = 'template'
|
||||||
arg_count = 1
|
arg_count = 1
|
||||||
doc = _('template(x) -- evaluates x as a template. The evaluation is done '
|
__doc__ = doc = _('template(x) -- evaluates x as a template. The evaluation is done '
|
||||||
'in its own context, meaning that variables are not shared between '
|
'in its own context, meaning that variables are not shared between '
|
||||||
'the caller and the template evaluation. Because the { and } '
|
'the caller and the template evaluation. Because the { and } '
|
||||||
'characters are special, you must use [[ for the { character and '
|
'characters are special, you must use [[ for the { character and '
|
||||||
@ -185,7 +185,7 @@ class BuiltinTemplate(BuiltinFormatterFunction):
|
|||||||
class BuiltinEval(BuiltinFormatterFunction):
|
class BuiltinEval(BuiltinFormatterFunction):
|
||||||
name = 'eval'
|
name = 'eval'
|
||||||
arg_count = 1
|
arg_count = 1
|
||||||
doc = _('eval(template) -- evaluates the template, passing the local '
|
__doc__ = doc = _('eval(template) -- evaluates the template, passing the local '
|
||||||
'variables (those \'assign\'ed to) instead of the book metadata. '
|
'variables (those \'assign\'ed to) instead of the book metadata. '
|
||||||
' This permits using the template processor to construct complex '
|
' This permits using the template processor to construct complex '
|
||||||
'results from local variables.')
|
'results from local variables.')
|
||||||
@ -198,7 +198,7 @@ class BuiltinEval(BuiltinFormatterFunction):
|
|||||||
class BuiltinAssign(BuiltinFormatterFunction):
|
class BuiltinAssign(BuiltinFormatterFunction):
|
||||||
name = 'assign'
|
name = 'assign'
|
||||||
arg_count = 2
|
arg_count = 2
|
||||||
doc = _('assign(id, val) -- assigns val to id, then returns val. '
|
__doc__ = doc = _('assign(id, val) -- assigns val to id, then returns val. '
|
||||||
'id must be an identifier, not an expression')
|
'id must be an identifier, not an expression')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, target, value):
|
def evaluate(self, formatter, kwargs, mi, locals, target, value):
|
||||||
@ -208,7 +208,7 @@ class BuiltinAssign(BuiltinFormatterFunction):
|
|||||||
class BuiltinPrint(BuiltinFormatterFunction):
|
class BuiltinPrint(BuiltinFormatterFunction):
|
||||||
name = 'print'
|
name = 'print'
|
||||||
arg_count = -1
|
arg_count = -1
|
||||||
doc = _('print(a, b, ...) -- prints the arguments to standard output. '
|
__doc__ = doc = _('print(a, b, ...) -- prints the arguments to standard output. '
|
||||||
'Unless you start calibre from the command line (calibre-debug -g), '
|
'Unless you start calibre from the command line (calibre-debug -g), '
|
||||||
'the output will go to a black hole.')
|
'the output will go to a black hole.')
|
||||||
|
|
||||||
@ -219,7 +219,7 @@ class BuiltinPrint(BuiltinFormatterFunction):
|
|||||||
class BuiltinField(BuiltinFormatterFunction):
|
class BuiltinField(BuiltinFormatterFunction):
|
||||||
name = 'field'
|
name = 'field'
|
||||||
arg_count = 1
|
arg_count = 1
|
||||||
doc = _('field(name) -- returns the metadata field named by name')
|
__doc__ = doc = _('field(name) -- returns the metadata field named by name')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, name):
|
def evaluate(self, formatter, kwargs, mi, locals, name):
|
||||||
return formatter.get_value(name, [], kwargs)
|
return formatter.get_value(name, [], kwargs)
|
||||||
@ -227,7 +227,7 @@ class BuiltinField(BuiltinFormatterFunction):
|
|||||||
class BuiltinRaw_field(BuiltinFormatterFunction):
|
class BuiltinRaw_field(BuiltinFormatterFunction):
|
||||||
name = 'raw_field'
|
name = 'raw_field'
|
||||||
arg_count = 1
|
arg_count = 1
|
||||||
doc = _('raw_field(name) -- returns the metadata field named by name '
|
__doc__ = doc = _('raw_field(name) -- returns the metadata field named by name '
|
||||||
'without applying any formatting.')
|
'without applying any formatting.')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, name):
|
def evaluate(self, formatter, kwargs, mi, locals, name):
|
||||||
@ -236,7 +236,7 @@ class BuiltinRaw_field(BuiltinFormatterFunction):
|
|||||||
class BuiltinSubstr(BuiltinFormatterFunction):
|
class BuiltinSubstr(BuiltinFormatterFunction):
|
||||||
name = 'substr'
|
name = 'substr'
|
||||||
arg_count = 3
|
arg_count = 3
|
||||||
doc = _('substr(str, start, end) -- returns the start\'th through the end\'th '
|
__doc__ = doc = _('substr(str, start, end) -- returns the start\'th through the end\'th '
|
||||||
'characters of str. The first character in str is the zero\'th '
|
'characters of str. The first character in str is the zero\'th '
|
||||||
'character. If end is negative, then it indicates that many '
|
'character. If end is negative, then it indicates that many '
|
||||||
'characters counting from the right. If end is zero, then it '
|
'characters counting from the right. If end is zero, then it '
|
||||||
@ -249,7 +249,7 @@ class BuiltinSubstr(BuiltinFormatterFunction):
|
|||||||
class BuiltinLookup(BuiltinFormatterFunction):
|
class BuiltinLookup(BuiltinFormatterFunction):
|
||||||
name = 'lookup'
|
name = 'lookup'
|
||||||
arg_count = -1
|
arg_count = -1
|
||||||
doc = _('lookup(val, pattern, field, pattern, field, ..., else_field) -- '
|
__doc__ = doc = _('lookup(val, pattern, field, pattern, field, ..., else_field) -- '
|
||||||
'like switch, except the arguments are field (metadata) names, not '
|
'like switch, except the arguments are field (metadata) names, not '
|
||||||
'text. The value of the appropriate field will be fetched and used. '
|
'text. The value of the appropriate field will be fetched and used. '
|
||||||
'Note that because composite columns are fields, you can use this '
|
'Note that because composite columns are fields, you can use this '
|
||||||
@ -276,7 +276,7 @@ class BuiltinLookup(BuiltinFormatterFunction):
|
|||||||
class BuiltinTest(BuiltinFormatterFunction):
|
class BuiltinTest(BuiltinFormatterFunction):
|
||||||
name = 'test'
|
name = 'test'
|
||||||
arg_count = 3
|
arg_count = 3
|
||||||
doc = _('test(val, text if not empty, text if empty) -- return `text if not '
|
__doc__ = doc = _('test(val, text if not empty, text if empty) -- return `text if not '
|
||||||
'empty` if the field is not empty, otherwise return `text if empty`')
|
'empty` if the field is not empty, otherwise return `text if empty`')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, val, value_if_set, value_not_set):
|
def evaluate(self, formatter, kwargs, mi, locals, val, value_if_set, value_not_set):
|
||||||
@ -288,7 +288,7 @@ class BuiltinTest(BuiltinFormatterFunction):
|
|||||||
class BuiltinContains(BuiltinFormatterFunction):
|
class BuiltinContains(BuiltinFormatterFunction):
|
||||||
name = 'contains'
|
name = 'contains'
|
||||||
arg_count = 4
|
arg_count = 4
|
||||||
doc = _('contains(val, pattern, text if match, text if not match) -- checks '
|
__doc__ = doc = _('contains(val, pattern, text if match, text if not match) -- checks '
|
||||||
'if field contains matches for the regular expression `pattern`. '
|
'if field contains matches for the regular expression `pattern`. '
|
||||||
'Returns `text if match` if matches are found, otherwise it returns '
|
'Returns `text if match` if matches are found, otherwise it returns '
|
||||||
'`text if no match`')
|
'`text if no match`')
|
||||||
@ -303,7 +303,7 @@ class BuiltinContains(BuiltinFormatterFunction):
|
|||||||
class BuiltinSwitch(BuiltinFormatterFunction):
|
class BuiltinSwitch(BuiltinFormatterFunction):
|
||||||
name = 'switch'
|
name = 'switch'
|
||||||
arg_count = -1
|
arg_count = -1
|
||||||
doc = _('switch(val, pattern, value, pattern, value, ..., else_value) -- '
|
__doc__ = doc = _('switch(val, pattern, value, pattern, value, ..., else_value) -- '
|
||||||
'for each `pattern, value` pair, checks if the field matches '
|
'for each `pattern, value` pair, checks if the field matches '
|
||||||
'the regular expression `pattern` and if so, returns that '
|
'the regular expression `pattern` and if so, returns that '
|
||||||
'`value`. If no pattern matches, then else_value is returned. '
|
'`value`. If no pattern matches, then else_value is returned. '
|
||||||
@ -323,7 +323,7 @@ class BuiltinSwitch(BuiltinFormatterFunction):
|
|||||||
class BuiltinInList(BuiltinFormatterFunction):
|
class BuiltinInList(BuiltinFormatterFunction):
|
||||||
name = 'in_list'
|
name = 'in_list'
|
||||||
arg_count = 5
|
arg_count = 5
|
||||||
doc = _('in_list(val, separator, pattern, found_val, not_found_val) -- '
|
__doc__ = doc = _('in_list(val, separator, pattern, found_val, not_found_val) -- '
|
||||||
'treat val as a list of items separated by separator, '
|
'treat val as a list of items separated by separator, '
|
||||||
'comparing the pattern against each value in the list. If the '
|
'comparing the pattern against each value in the list. If the '
|
||||||
'pattern matches a value, return found_val, otherwise return '
|
'pattern matches a value, return found_val, otherwise return '
|
||||||
@ -340,7 +340,7 @@ class BuiltinInList(BuiltinFormatterFunction):
|
|||||||
class BuiltinStrInList(BuiltinFormatterFunction):
|
class BuiltinStrInList(BuiltinFormatterFunction):
|
||||||
name = 'str_in_list'
|
name = 'str_in_list'
|
||||||
arg_count = 5
|
arg_count = 5
|
||||||
doc = _('str_in_list(val, separator, string, found_val, not_found_val) -- '
|
__doc__ = doc = _('str_in_list(val, separator, string, found_val, not_found_val) -- '
|
||||||
'treat val as a list of items separated by separator, '
|
'treat val as a list of items separated by separator, '
|
||||||
'comparing the string against each value in the list. If the '
|
'comparing the string against each value in the list. If the '
|
||||||
'string matches a value, return found_val, otherwise return '
|
'string matches a value, return found_val, otherwise return '
|
||||||
@ -360,7 +360,7 @@ class BuiltinStrInList(BuiltinFormatterFunction):
|
|||||||
class BuiltinRe(BuiltinFormatterFunction):
|
class BuiltinRe(BuiltinFormatterFunction):
|
||||||
name = 're'
|
name = 're'
|
||||||
arg_count = 3
|
arg_count = 3
|
||||||
doc = _('re(val, pattern, replacement) -- return the field after applying '
|
__doc__ = doc = _('re(val, pattern, replacement) -- return the field after applying '
|
||||||
'the regular expression. All instances of `pattern` are replaced '
|
'the regular expression. All instances of `pattern` are replaced '
|
||||||
'with `replacement`. As in all of calibre, these are '
|
'with `replacement`. As in all of calibre, these are '
|
||||||
'python-compatible regular expressions')
|
'python-compatible regular expressions')
|
||||||
@ -371,7 +371,7 @@ class BuiltinRe(BuiltinFormatterFunction):
|
|||||||
class BuiltinIfempty(BuiltinFormatterFunction):
|
class BuiltinIfempty(BuiltinFormatterFunction):
|
||||||
name = 'ifempty'
|
name = 'ifempty'
|
||||||
arg_count = 2
|
arg_count = 2
|
||||||
doc = _('ifempty(val, text if empty) -- return val if val is not empty, '
|
__doc__ = doc = _('ifempty(val, text if empty) -- return val if val is not empty, '
|
||||||
'otherwise return `text if empty`')
|
'otherwise return `text if empty`')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty):
|
def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty):
|
||||||
@ -383,7 +383,7 @@ class BuiltinIfempty(BuiltinFormatterFunction):
|
|||||||
class BuiltinShorten(BuiltinFormatterFunction):
|
class BuiltinShorten(BuiltinFormatterFunction):
|
||||||
name = 'shorten'
|
name = 'shorten'
|
||||||
arg_count = 4
|
arg_count = 4
|
||||||
doc = _('shorten(val, left chars, middle text, right chars) -- Return a '
|
__doc__ = doc = _('shorten(val, left chars, middle text, right chars) -- Return a '
|
||||||
'shortened version of the field, consisting of `left chars` '
|
'shortened version of the field, consisting of `left chars` '
|
||||||
'characters from the beginning of the field, followed by '
|
'characters from the beginning of the field, followed by '
|
||||||
'`middle text`, followed by `right chars` characters from '
|
'`middle text`, followed by `right chars` characters from '
|
||||||
@ -408,7 +408,7 @@ class BuiltinShorten(BuiltinFormatterFunction):
|
|||||||
class BuiltinCount(BuiltinFormatterFunction):
|
class BuiltinCount(BuiltinFormatterFunction):
|
||||||
name = 'count'
|
name = 'count'
|
||||||
arg_count = 2
|
arg_count = 2
|
||||||
doc = _('count(val, separator) -- interprets the value as a list of items '
|
__doc__ = doc = _('count(val, separator) -- interprets the value as a list of items '
|
||||||
'separated by `separator`, returning the number of items in the '
|
'separated by `separator`, returning the number of items in the '
|
||||||
'list. Most lists use a comma as the separator, but authors '
|
'list. Most lists use a comma as the separator, but authors '
|
||||||
'uses an ampersand. Examples: {tags:count(,)}, {authors:count(&)}')
|
'uses an ampersand. Examples: {tags:count(,)}, {authors:count(&)}')
|
||||||
@ -419,7 +419,7 @@ class BuiltinCount(BuiltinFormatterFunction):
|
|||||||
class BuiltinListitem(BuiltinFormatterFunction):
|
class BuiltinListitem(BuiltinFormatterFunction):
|
||||||
name = 'list_item'
|
name = 'list_item'
|
||||||
arg_count = 3
|
arg_count = 3
|
||||||
doc = _('list_item(val, index, separator) -- interpret the value as a list of '
|
__doc__ = doc = _('list_item(val, index, separator) -- interpret the value as a list of '
|
||||||
'items separated by `separator`, returning the `index`th item. '
|
'items separated by `separator`, returning the `index`th item. '
|
||||||
'The first item is number zero. The last item can be returned '
|
'The first item is number zero. The last item can be returned '
|
||||||
'using `list_item(-1,separator)`. If the item is not in the list, '
|
'using `list_item(-1,separator)`. If the item is not in the list, '
|
||||||
@ -439,7 +439,7 @@ class BuiltinListitem(BuiltinFormatterFunction):
|
|||||||
class BuiltinSelect(BuiltinFormatterFunction):
|
class BuiltinSelect(BuiltinFormatterFunction):
|
||||||
name = 'select'
|
name = 'select'
|
||||||
arg_count = 2
|
arg_count = 2
|
||||||
doc = _('select(val, key) -- interpret the value as a comma-separated list '
|
__doc__ = doc = _('select(val, key) -- interpret the value as a comma-separated list '
|
||||||
'of items, with the items being "id:value". Find the pair with the'
|
'of items, with the items being "id:value". Find the pair with the'
|
||||||
'id equal to key, and return the corresponding value.'
|
'id equal to key, and return the corresponding value.'
|
||||||
)
|
)
|
||||||
@ -456,9 +456,9 @@ class BuiltinSelect(BuiltinFormatterFunction):
|
|||||||
class BuiltinSublist(BuiltinFormatterFunction):
|
class BuiltinSublist(BuiltinFormatterFunction):
|
||||||
name = 'sublist'
|
name = 'sublist'
|
||||||
arg_count = 4
|
arg_count = 4
|
||||||
doc = _('sublist(val, start_index, end_index, separator) -- interpret the '
|
__doc__ = doc = _('sublist(val, start_index, end_index, separator) -- interpret the '
|
||||||
'value as a list of items separated by `separator`, returning a '
|
'value as a list of items separated by `separator`, returning a '
|
||||||
'new list made from the `start_index`th to the `end_index`th item. '
|
'new list made from the `start_index` to the `end_index` item. '
|
||||||
'The first item is number zero. If an index is negative, then it '
|
'The first item is number zero. If an index is negative, then it '
|
||||||
'counts from the end of the list. As a special case, an end_index '
|
'counts from the end of the list. As a special case, an end_index '
|
||||||
'of zero is assumed to be the length of the list. Examples using '
|
'of zero is assumed to be the length of the list. Examples using '
|
||||||
@ -466,7 +466,8 @@ class BuiltinSublist(BuiltinFormatterFunction):
|
|||||||
'comma-separated) contains "A, B, C": '
|
'comma-separated) contains "A, B, C": '
|
||||||
'{tags:sublist(0,1,\,)} returns "A". '
|
'{tags:sublist(0,1,\,)} returns "A". '
|
||||||
'{tags:sublist(-1,0,\,)} returns "C". '
|
'{tags:sublist(-1,0,\,)} returns "C". '
|
||||||
'{tags:sublist(0,-1,\,)} returns "A, B".')
|
'{tags:sublist(0,-1,\,)} returns "A, B".'
|
||||||
|
)
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index, sep):
|
def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index, sep):
|
||||||
if not val:
|
if not val:
|
||||||
@ -485,12 +486,12 @@ class BuiltinSublist(BuiltinFormatterFunction):
|
|||||||
class BuiltinSubitems(BuiltinFormatterFunction):
|
class BuiltinSubitems(BuiltinFormatterFunction):
|
||||||
name = 'subitems'
|
name = 'subitems'
|
||||||
arg_count = 3
|
arg_count = 3
|
||||||
doc = _('subitems(val, start_index, end_index) -- This function is used to '
|
__doc__ = doc = _('subitems(val, start_index, end_index) -- This function is used to '
|
||||||
'break apart lists of items such as genres. It interprets the value '
|
'break apart lists of items such as genres. It interprets the value '
|
||||||
'as a comma-separated list of items, where each item is a period-'
|
'as a comma-separated list of items, where each item is a period-'
|
||||||
'separated list. Returns a new list made by first finding all the '
|
'separated list. Returns a new list made by first finding all the '
|
||||||
'period-separated items, then for each such item extracting the '
|
'period-separated items, then for each such item extracting the '
|
||||||
'start_index`th to the `end_index`th components, then combining '
|
'start_index` to the `end_index` components, then combining '
|
||||||
'the results back together. The first component in a period-'
|
'the results back together. The first component in a period-'
|
||||||
'separated list has an index of zero. If an index is negative, '
|
'separated list has an index of zero. If an index is negative, '
|
||||||
'then it counts from the end of the list. As a special case, an '
|
'then it counts from the end of the list. As a special case, an '
|
||||||
@ -522,7 +523,7 @@ class BuiltinSubitems(BuiltinFormatterFunction):
|
|||||||
class BuiltinFormat_date(BuiltinFormatterFunction):
|
class BuiltinFormat_date(BuiltinFormatterFunction):
|
||||||
name = 'format_date'
|
name = 'format_date'
|
||||||
arg_count = 2
|
arg_count = 2
|
||||||
doc = _('format_date(val, format_string) -- format the value, which must '
|
__doc__ = doc = _('format_date(val, format_string) -- format the value, which must '
|
||||||
'be a date field, using the format_string, returning a string. '
|
'be a date field, using the format_string, returning a string. '
|
||||||
'The formatting codes are: '
|
'The formatting codes are: '
|
||||||
'd : the day as number without a leading zero (1 to 31) '
|
'd : the day as number without a leading zero (1 to 31) '
|
||||||
@ -550,7 +551,7 @@ class BuiltinFormat_date(BuiltinFormatterFunction):
|
|||||||
class BuiltinUppercase(BuiltinFormatterFunction):
|
class BuiltinUppercase(BuiltinFormatterFunction):
|
||||||
name = 'uppercase'
|
name = 'uppercase'
|
||||||
arg_count = 1
|
arg_count = 1
|
||||||
doc = _('uppercase(val) -- return value of the field in upper case')
|
__doc__ = doc = _('uppercase(val) -- return value of the field in upper case')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, val):
|
def evaluate(self, formatter, kwargs, mi, locals, val):
|
||||||
return val.upper()
|
return val.upper()
|
||||||
@ -558,7 +559,7 @@ class BuiltinUppercase(BuiltinFormatterFunction):
|
|||||||
class BuiltinLowercase(BuiltinFormatterFunction):
|
class BuiltinLowercase(BuiltinFormatterFunction):
|
||||||
name = 'lowercase'
|
name = 'lowercase'
|
||||||
arg_count = 1
|
arg_count = 1
|
||||||
doc = _('lowercase(val) -- return value of the field in lower case')
|
__doc__ = doc = _('lowercase(val) -- return value of the field in lower case')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, val):
|
def evaluate(self, formatter, kwargs, mi, locals, val):
|
||||||
return val.lower()
|
return val.lower()
|
||||||
@ -566,7 +567,7 @@ class BuiltinLowercase(BuiltinFormatterFunction):
|
|||||||
class BuiltinTitlecase(BuiltinFormatterFunction):
|
class BuiltinTitlecase(BuiltinFormatterFunction):
|
||||||
name = 'titlecase'
|
name = 'titlecase'
|
||||||
arg_count = 1
|
arg_count = 1
|
||||||
doc = _('titlecase(val) -- return value of the field in title case')
|
__doc__ = doc = _('titlecase(val) -- return value of the field in title case')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, val):
|
def evaluate(self, formatter, kwargs, mi, locals, val):
|
||||||
return titlecase(val)
|
return titlecase(val)
|
||||||
@ -574,7 +575,7 @@ class BuiltinTitlecase(BuiltinFormatterFunction):
|
|||||||
class BuiltinCapitalize(BuiltinFormatterFunction):
|
class BuiltinCapitalize(BuiltinFormatterFunction):
|
||||||
name = 'capitalize'
|
name = 'capitalize'
|
||||||
arg_count = 1
|
arg_count = 1
|
||||||
doc = _('capitalize(val) -- return value of the field capitalized')
|
__doc__ = doc = _('capitalize(val) -- return value of the field capitalized')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, val):
|
def evaluate(self, formatter, kwargs, mi, locals, val):
|
||||||
return capitalize(val)
|
return capitalize(val)
|
||||||
@ -582,7 +583,7 @@ class BuiltinCapitalize(BuiltinFormatterFunction):
|
|||||||
class BuiltinBooksize(BuiltinFormatterFunction):
|
class BuiltinBooksize(BuiltinFormatterFunction):
|
||||||
name = 'booksize'
|
name = 'booksize'
|
||||||
arg_count = 0
|
arg_count = 0
|
||||||
doc = _('booksize() -- return value of the size field')
|
__doc__ = doc = _('booksize() -- return value of the size field')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals):
|
def evaluate(self, formatter, kwargs, mi, locals):
|
||||||
if mi.book_size is not None:
|
if mi.book_size is not None:
|
||||||
@ -595,7 +596,7 @@ class BuiltinBooksize(BuiltinFormatterFunction):
|
|||||||
class BuiltinOndevice(BuiltinFormatterFunction):
|
class BuiltinOndevice(BuiltinFormatterFunction):
|
||||||
name = 'ondevice'
|
name = 'ondevice'
|
||||||
arg_count = 0
|
arg_count = 0
|
||||||
doc = _('ondevice() -- return Yes if ondevice is set, otherwise return '
|
__doc__ = doc = _('ondevice() -- return Yes if ondevice is set, otherwise return '
|
||||||
'the empty string')
|
'the empty string')
|
||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals):
|
def evaluate(self, formatter, kwargs, mi, locals):
|
||||||
@ -606,7 +607,7 @@ class BuiltinOndevice(BuiltinFormatterFunction):
|
|||||||
class BuiltinFirstNonEmpty(BuiltinFormatterFunction):
|
class BuiltinFirstNonEmpty(BuiltinFormatterFunction):
|
||||||
name = 'first_non_empty'
|
name = 'first_non_empty'
|
||||||
arg_count = -1
|
arg_count = -1
|
||||||
doc = _('first_non_empty(value, value, ...) -- '
|
__doc__ = doc = _('first_non_empty(value, value, ...) -- '
|
||||||
'returns the first value that is not empty. If all values are '
|
'returns the first value that is not empty. If all values are '
|
||||||
'empty, then the empty value is returned.'
|
'empty, then the empty value is returned.'
|
||||||
'You can have as many values as you want.')
|
'You can have as many values as you want.')
|
||||||
@ -622,7 +623,7 @@ class BuiltinFirstNonEmpty(BuiltinFormatterFunction):
|
|||||||
class BuiltinAnd(BuiltinFormatterFunction):
|
class BuiltinAnd(BuiltinFormatterFunction):
|
||||||
name = 'and'
|
name = 'and'
|
||||||
arg_count = -1
|
arg_count = -1
|
||||||
doc = _('and(value, value, ...) -- '
|
__doc__ = doc = _('and(value, value, ...) -- '
|
||||||
'returns the string "1" if all values are not empty, otherwise '
|
'returns the string "1" if all values are not empty, otherwise '
|
||||||
'returns the empty string. This function works well with test or '
|
'returns the empty string. This function works well with test or '
|
||||||
'first_non_empty. You can have as many values as you want.')
|
'first_non_empty. You can have as many values as you want.')
|
||||||
@ -638,7 +639,7 @@ class BuiltinAnd(BuiltinFormatterFunction):
|
|||||||
class BuiltinOr(BuiltinFormatterFunction):
|
class BuiltinOr(BuiltinFormatterFunction):
|
||||||
name = 'or'
|
name = 'or'
|
||||||
arg_count = -1
|
arg_count = -1
|
||||||
doc = _('or(value, value, ...) -- '
|
__doc__ = doc = _('or(value, value, ...) -- '
|
||||||
'returns the string "1" if any value is not empty, otherwise '
|
'returns the string "1" if any value is not empty, otherwise '
|
||||||
'returns the empty string. This function works well with test or '
|
'returns the empty string. This function works well with test or '
|
||||||
'first_non_empty. You can have as many values as you want.')
|
'first_non_empty. You can have as many values as you want.')
|
||||||
@ -654,7 +655,7 @@ class BuiltinOr(BuiltinFormatterFunction):
|
|||||||
class BuiltinNot(BuiltinFormatterFunction):
|
class BuiltinNot(BuiltinFormatterFunction):
|
||||||
name = 'not'
|
name = 'not'
|
||||||
arg_count = 1
|
arg_count = 1
|
||||||
doc = _('not(value) -- '
|
__doc__ = doc = _('not(value) -- '
|
||||||
'returns the string "1" if the value is empty, otherwise '
|
'returns the string "1" if the value is empty, otherwise '
|
||||||
'returns the empty string. This function works well with test or '
|
'returns the empty string. This function works well with test or '
|
||||||
'first_non_empty. You can have as many values as you want.')
|
'first_non_empty. You can have as many values as you want.')
|
||||||
@ -670,7 +671,7 @@ class BuiltinNot(BuiltinFormatterFunction):
|
|||||||
class BuiltinMergeLists(BuiltinFormatterFunction):
|
class BuiltinMergeLists(BuiltinFormatterFunction):
|
||||||
name = 'merge_lists'
|
name = 'merge_lists'
|
||||||
arg_count = 3
|
arg_count = 3
|
||||||
doc = _('merge_lists(list1, list2, separator) -- '
|
__doc__ = doc = _('merge_lists(list1, list2, separator) -- '
|
||||||
'return a list made by merging the items in list1 and list2, '
|
'return a list made by merging the items in list1 and list2, '
|
||||||
'removing duplicate items using a case-insensitive compare. If '
|
'removing duplicate items using a case-insensitive compare. If '
|
||||||
'items differ in case, the one in list1 is used. '
|
'items differ in case, the one in list1 is used. '
|
||||||
|
Loading…
x
Reference in New Issue
Block a user