From be10ae02fda678a1ec0e2da16ae001d2b667996e Mon Sep 17 00:00:00 2001
From: Li Fanxi '+
_('%s has been updated to version %s. '
'See the new features.')%(__appname__, version))
+ '">new features. Only update if one of the '
+ 'new features or bug fixes is important to you.')%(__appname__, version))
self.label.setOpenExternalLinks(True)
self.label.setWordWrap(True)
self.setWindowTitle(_('Update available!'))
From 456b6b423eab32be26a8aba637d5ee57fa16ae33 Mon Sep 17 00:00:00 2001
From: Kovid Goyal ' +
+ _('You can enter more than one tag per box, separated by commas. '
+ 'The comparison ignores letter case. ' +
+ _('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.') + ' ' +
+ _('This box is usually filled in only on the last test. If it is '
+ 'filled in before the last test, then the color for tag found box '
+ 'must be empty or all the rest of the tests will be ignored.') + ' ' +
_('You can enter more than one tag per box, separated by commas. '
'The comparison ignores letter case.
'
+ 'A tag value can be a regular expression. '
+ 'When using regular expressions, note that the wizard '
+ 'puts anchors (^ and $) around the expression, so you '
+ 'must ensure your expression matches from the beginning '
+ 'to the end of the tag.
'
+ 'Regular expression examples:') + '' +
+ _('
.*
matches any tag. No empty tags are '
+ 'checked, so you don\'t need to worry about empty stringsA.*
matches any tag beginning with A.*mystery.*
matches any tag containing '
+ 'the word "mystery"
'
- 'A tag value can be a regular expression. '
- 'When using regular expressions, note that the wizard '
+ 'A tag value can be a regular expression. Check the box to turn '
+ 'them on. When using regular expressions, note that the wizard '
'puts anchors (^ and $) around the expression, so you '
'must ensure your expression matches from the beginning '
'to the end of the tag.
'
@@ -85,22 +85,29 @@ class TagWizard(QDialog):
'.*mystery.*
matches any tag containing '
'the word "mystery"
' + + _('Check this box if the tag box contains regular expressions') + '
') + l.addWidget(c, 0, 1, 1, 1) + c = QLabel(_('Color if tag found')) c.setToolTip('' + _('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.') + '
') - l.addWidget(c, 0, 1, 1, 1) + l.addWidget(c, 0, 2, 1, 1) c = QLabel(_('Color if tag not found')) c.setToolTip('' + _('This box is usually filled in only on the last test. If it is ' 'filled in before the last test, then the color for tag found box ' 'must be empty or all the rest of the tests will be ignored.') + '
') - l.addWidget(c, 0, 2, 1, 1) + l.addWidget(c, 0, 3, 1, 1) self.tagboxes = [] self.colorboxes = [] self.nfcolorboxes = [] + self.reboxes = [] self.colors = [unicode(s) for s in list(QColor.colorNames())] self.colors.insert(0, '') for i in range(0, 10): @@ -109,14 +116,20 @@ class TagWizard(QDialog): tb.update_items_cache(self.tags) self.tagboxes.append(tb) l.addWidget(tb, i+1, 0, 1, 1) - cb = QComboBox(self) - cb.addItems(self.colors) - self.colorboxes.append(cb) - l.addWidget(cb, i+1, 1, 1, 1) - cb = QComboBox(self) - cb.addItems(self.colors) - self.nfcolorboxes.append(cb) - l.addWidget(cb, i+1, 2, 1, 1) + + w = QCheckBox(self) + self.reboxes.append(w) + l.addWidget(w, i+1, 1, 1, 1) + + w = QComboBox(self) + w.addItems(self.colors) + self.colorboxes.append(w) + l.addWidget(w, i+1, 2, 1, 1) + + w = QComboBox(self) + w.addItems(self.colors) + self.nfcolorboxes.append(w) + l.addWidget(w, i+1, 3, 1, 1) if txt: lines = txt.split('\n')[3:] @@ -127,18 +140,20 @@ class TagWizard(QDialog): if len(vals) == 2: t, c = vals nc = '' + re = False else: - t,c,nc = vals + t,c,nc,re = vals try: self.colorboxes[i].setCurrentIndex(self.colorboxes[i].findText(c)) self.nfcolorboxes[i].setCurrentIndex(self.nfcolorboxes[i].findText(nc)) self.tagboxes[i].setText(t) + self.reboxes[i].setChecked(re == '2') except: pass i += 1 bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self) - l.addWidget(bb, 100, 1, 1, 1) + l.addWidget(bb, 100, 2, 1, 2) bb.accepted.connect(self.accepted) bb.rejected.connect(self.reject) self.template = '' @@ -147,11 +162,16 @@ class TagWizard(QDialog): res = ("program:\n#tag wizard -- do not directly edit\n" " t = field('tags');\n first_non_empty(\n") lines = [] - for tb, cb, nfcb in zip(self.tagboxes, self.colorboxes, self.nfcolorboxes): + for tb, cb, nfcb, reb in zip(self.tagboxes, self.colorboxes, + self.nfcolorboxes, self.reboxes): tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()] - tags = '$|^'.join(tags) c = unicode(cb.currentText()).strip() nfc = unicode(nfcb.currentText()).strip() + re = reb.checkState() + if re == 2: + tags = '$|^'.join(tags) + else: + tags = ','.join(tags) if not tags or not (c or nfc): continue if c not in self.colors: @@ -164,18 +184,25 @@ class TagWizard(QDialog): _('The color {0} is not valid').format(nfc), show=True, show_copy_button=False) return False - lines.append(" in_list(t, ',', '^{0}$', '{1}', '{2}')".format(tags, c, nfc)) + if re == 2: + lines.append(" in_list(t, ',', '^{0}$', '{1}', '{2}')".\ + format(tags, c, nfc)) + else: + lines.append(" str_in_list(t, ',', '{0}', '{1}', '{2}')".\ + format(tags, c, nfc)) res += ',\n'.join(lines) res += ')\n' self.template = res res = '' - for tb, cb, nfcb in zip(self.tagboxes, self.colorboxes, self.nfcolorboxes): + for tb, cb, nfcb, reb in zip(self.tagboxes, self.colorboxes, + self.nfcolorboxes, self.reboxes): t = unicode(tb.text()).strip() if t.endswith(','): t = t[:-1] c = unicode(cb.currentText()).strip() nfc = unicode(nfcb.currentText()).strip() + re = unicode(reb.checkState()) if t and c: - res += '#' + t + ':|:' + c + ':|:' + nfc + '\n' + res += '#' + t + ':|:' + c + ':|:' + nfc + ':|:' + re + '\n' self.template += res self.accept() diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index fcdd56fd5f..37e4588b9b 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -209,8 +209,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): mi=None try: idx = gui.library_view.currentIndex().row() - if idx: - mi = db.get_metadata(idx, index_is_id=False) + mi = db.get_metadata(idx, index_is_id=False) except: pass diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index 76faf04941..7d5dbe3e0e 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -8,7 +8,7 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal'+ + self.label = QLabel(('
'+ _('%s has been updated to version %s. ' 'See the new features. Only update if one of the ' - 'new features or bug fixes is important to you.')%(__appname__, version)) + '">new features.') + '
'+_('Update only if one of the '
+ 'new features or bug fixes is important to you. '
+ 'If the current version works well for you, do not update.'))%(__appname__, version))
self.label.setOpenExternalLinks(True)
self.label.setWordWrap(True)
self.setWindowTitle(_('Update available!'))
From 19261f15eede6fdf17ec5769a31f8e28c69486bf Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 29 May 2011 20:12:31 +0100
Subject: [PATCH 19/58] Add field box to color wizard
---
.../gui2/dialogs/template_line_editor.py | 169 ++++++++++++------
src/calibre/gui2/preferences/look_feel.py | 3 +-
src/calibre/utils/formatter_functions.py | 16 +-
3 files changed, 127 insertions(+), 61 deletions(-)
diff --git a/src/calibre/gui2/dialogs/template_line_editor.py b/src/calibre/gui2/dialogs/template_line_editor.py
index 3d199b156c..bea2c4e316 100644
--- a/src/calibre/gui2/dialogs/template_line_editor.py
+++ b/src/calibre/gui2/dialogs/template_line_editor.py
@@ -5,12 +5,16 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal ' +
- _('You can enter more than one tag per box, separated by commas. '
+ _('You can enter more than one value per box, separated by commas. '
'The comparison ignores letter case.
'
- 'A tag 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 '
'puts anchors (^ and $) around the expression, so you '
'must ensure your expression matches from the beginning '
- 'to the end of the tag.
'
+ 'to the end of the column you are checking.
'
'Regular expression examples:') + '' +
- _('
.*
matches any tag. No empty tags are '
- 'checked, so you don\'t need to worry about empty stringsA.*
matches any tag beginning with A.*mystery.*
matches any tag containing '
+ _('.*
matches anything in the column. No '
+ 'empty values are checked, so you don\'t need to worry about '
+ 'empty stringsA.*
matches anything beginning with A.*mystery.*
matches anything containing '
'the word "mystery"
' + - _('Check this box if the tag box contains regular expressions') + '
') - l.addWidget(c, 0, 1, 1, 1) + _('Check this box if the values box contains regular expressions') + '') + l.addWidget(c, 0, 2, 1, 1) - c = QLabel(_('Color if tag found')) + c = QLabel(_('Color if value found')) c.setToolTip('' + _('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.') + '
') - l.addWidget(c, 0, 2, 1, 1) - c = QLabel(_('Color if tag not found')) + l.addWidget(c, 0, 3, 1, 1) + c = QLabel(_('Color if value not found')) c.setToolTip('' + _('This box is usually filled in only on the last test. If it is ' - 'filled in before the last test, then the color for tag found box ' + '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.') + '
') - l.addWidget(c, 0, 3, 1, 1) + l.addWidget(c, 0, 4, 1, 1) self.tagboxes = [] self.colorboxes = [] self.nfcolorboxes = [] self.reboxes = [] + self.colboxes = [] self.colors = [unicode(s) for s in list(QColor.colorNames())] self.colors.insert(0, '') for i in range(0, 10): + w = QComboBox() + w.addItems(self.columns) + l.addWidget(w, i+1, 0, 1, 1) + self.colboxes.append(w) + tb = MultiCompleteLineEdit(self) tb.set_separator(', ') - tb.update_items_cache(self.tags) self.tagboxes.append(tb) - l.addWidget(tb, i+1, 0, 1, 1) + l.addWidget(tb, i+1, 1, 1, 1) + w.currentIndexChanged[str].connect(partial(self.column_changed, valbox=tb)) w = QCheckBox(self) self.reboxes.append(w) - l.addWidget(w, i+1, 1, 1, 1) - - w = QComboBox(self) - w.addItems(self.colors) - self.colorboxes.append(w) l.addWidget(w, i+1, 2, 1, 1) w = QComboBox(self) w.addItems(self.colors) - self.nfcolorboxes.append(w) + self.colorboxes.append(w) l.addWidget(w, i+1, 3, 1, 1) + w = QComboBox(self) + w.addItems(self.colors) + self.nfcolorboxes.append(w) + l.addWidget(w, i+1, 4, 1, 1) + if txt: lines = txt.split('\n')[3:] i = 0 @@ -141,37 +175,59 @@ class TagWizard(QDialog): t, c = vals nc = '' re = False + f = 'tags' else: - t,c,nc,re = vals + t,c,f,nc,re = vals try: self.colorboxes[i].setCurrentIndex(self.colorboxes[i].findText(c)) self.nfcolorboxes[i].setCurrentIndex(self.nfcolorboxes[i].findText(nc)) self.tagboxes[i].setText(t) self.reboxes[i].setChecked(re == '2') + self.colboxes[i].setCurrentIndex(self.colboxes[i].findText(f)) except: pass i += 1 bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self) - l.addWidget(bb, 100, 2, 1, 2) + l.addWidget(bb, 100, 3, 1, 2) bb.accepted.connect(self.accepted) bb.rejected.connect(self.reject) self.template = '' + def column_changed(self, s, valbox=None): + k = unicode(s) + if k in self.completion_values: + valbox.update_items_cache(self.completion_values[k]['v']) + if self.completion_values[k]['m']: + valbox.set_separator(', ') + else: + valbox.set_separator(None) + else: + valbox.update_items_cache([]) + valbox.set_separator(None) + def accepted(self): res = ("program:\n#tag wizard -- do not directly edit\n" - " t = field('tags');\n first_non_empty(\n") + " first_non_empty(\n") lines = [] - for tb, cb, nfcb, reb in zip(self.tagboxes, self.colorboxes, - self.nfcolorboxes, self.reboxes): - tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()] + for tb, cb, fb, nfcb, reb in zip(self.tagboxes, self.colorboxes, + self.colboxes, self.nfcolorboxes, self.reboxes): + f = unicode(fb.currentText()) + if not f: + continue + m = self.completion_values[f]['m'] c = unicode(cb.currentText()).strip() nfc = unicode(nfcb.currentText()).strip() re = reb.checkState() - if re == 2: - tags = '$|^'.join(tags) + if m: + tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()] + if re == 2: + tags = '$|^'.join(tags) + else: + tags = ','.join(tags) else: - tags = ','.join(tags) + tags = unicode(tb.text()).strip() + if not tags or not (c or nfc): continue if c not in self.colors: @@ -185,24 +241,33 @@ class TagWizard(QDialog): show=True, show_copy_button=False) return False if re == 2: - lines.append(" in_list(t, ',', '^{0}$', '{1}', '{2}')".\ - format(tags, c, nfc)) + if m: + lines.append(" in_list(field('{3}'), ',', '^{0}$', '{1}', '{2}')".\ + format(tags, c, nfc, f)) + else: + lines.append(" contains(field('{3}'), '{0}', '{1}', '{2}')".\ + format(tags, c, nfc, f)) else: - lines.append(" str_in_list(t, ',', '{0}', '{1}', '{2}')".\ - format(tags, c, nfc)) + if m: + lines.append(" str_in_list(field('{3}'), ',', '{0}', '{1}', '{2}')".\ + format(tags, c, nfc, f)) + else: + lines.append(" strcmp(field('{3}'), '{0}', '{2}', '{1}', '{2}')".\ + format(tags, c, nfc, f)) res += ',\n'.join(lines) res += ')\n' self.template = res res = '' - for tb, cb, nfcb, reb in zip(self.tagboxes, self.colorboxes, - self.nfcolorboxes, self.reboxes): + for tb, cb, fb, nfcb, reb in zip(self.tagboxes, self.colorboxes, + self.colboxes, self.nfcolorboxes, self.reboxes): t = unicode(tb.text()).strip() if t.endswith(','): t = t[:-1] c = unicode(cb.currentText()).strip() + f = unicode(fb.currentText()) nfc = unicode(nfcb.currentText()).strip() re = unicode(reb.checkState()) - if t and c: - res += '#' + t + ':|:' + c + ':|:' + nfc + ':|:' + re + '\n' + if f and t and c: + res += '#' + t + ':|:' + c + ':|:' + f +':|:' + nfc + ':|:' + re + '\n' self.template += res self.accept() diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 37e4588b9b..79db1aecf8 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -204,7 +204,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): choices.sort(key=sort_key) choices.insert(0, '') self.column_color_count = db.column_color_count+1 - tags = db.all_tags() mi=None try: @@ -217,7 +216,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('column_color_name_'+str(i), db.prefs, choices=choices) r('column_color_template_'+str(i), db.prefs) tpl = getattr(self, 'opt_column_color_template_'+str(i)) - tpl.set_tags(tags) + tpl.set_db(db) tpl.set_mi(mi) toolbutton = getattr(self, 'opt_column_color_wizard_'+str(i)) toolbutton.clicked.connect(tpl.tag_wizard) diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index 32822e1d72..b66aec2cb9 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -331,9 +331,10 @@ class BuiltinInList(BuiltinFormatterFunction): def evaluate(self, formatter, kwargs, mi, locals, val, sep, pat, fv, nfv): l = [v.strip() for v in val.split(sep) if v.strip()] - for v in l: - if re.search(pat, v, flags=re.I): - return fv + if l: + for v in l: + if re.search(pat, v, flags=re.I): + return fv return nfv class BuiltinStrInList(BuiltinFormatterFunction): @@ -349,10 +350,11 @@ class BuiltinStrInList(BuiltinFormatterFunction): def evaluate(self, formatter, kwargs, mi, locals, val, sep, str, fv, nfv): l = [v.strip() for v in val.split(sep) if v.strip()] c = [v.strip() for v in str.split(sep) if v.strip()] - for v in l: - for t in c: - if strcmp(t, v) == 0: - return fv + if l: + for v in l: + for t in c: + if strcmp(t, v) == 0: + return fv return nfv class BuiltinRe(BuiltinFormatterFunction): From cc288ff4cac08b4a3047ef3782aba344aa3b81a5 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 29 May 2011 21:21:02 +0100 Subject: [PATCH 20/58] Add a preview field to the wizard. Vary the button according to whether the wizard created the template Don't show the template if wizard created --- .../gui2/dialogs/template_line_editor.py | 88 ++++++++++++++++--- src/calibre/gui2/preferences/look_feel.py | 9 +- 2 files changed, 81 insertions(+), 16 deletions(-) diff --git a/src/calibre/gui2/dialogs/template_line_editor.py b/src/calibre/gui2/dialogs/template_line_editor.py index bea2c4e316..2e4a6595fd 100644 --- a/src/calibre/gui2/dialogs/template_line_editor.py +++ b/src/calibre/gui2/dialogs/template_line_editor.py @@ -8,9 +8,10 @@ __docformat__ = 'restructuredtext en' from functools import partial from collections import defaultdict -from PyQt4.Qt import (QLineEdit, QDialog, QGridLayout, QLabel, QCheckBox, - QDialogButtonBox, QColor, QComboBox, QIcon) +from PyQt4.Qt import (QLineEdit, QDialog, QGridLayout, QLabel, QCheckBox, QIcon, + QDialogButtonBox, QColor, QComboBox, QPushButton) +from calibre.ebooks.metadata.book.base import composite_formatter from calibre.gui2.dialogs.template_dialog import TemplateDialog from calibre.gui2.complete import MultiCompleteLineEdit from calibre.gui2 import error_dialog @@ -26,6 +27,7 @@ class TemplateLineEditor(QLineEdit): QLineEdit.__init__(self, parent) self.tags = None self.mi = None + self.txt = None def set_mi(self, mi): self.mi = mi @@ -42,46 +44,82 @@ class TemplateLineEditor(QLineEdit): menu.exec_(event.globalPos()) def open_editor(self): - t = TemplateDialog(self, self.text(), self.mi) + if self.txt: + t = TemplateDialog(self, self.txt, self.mi) + else: + t = TemplateDialog(self, self.text(), self.mi) t.setWindowTitle(_('Edit template')) if t.exec_(): self.setText(t.textbox.toPlainText()) + self.txt = None + + def show_wizard_button(self, txt): + if not txt or txt.startswith('program:\n#tag wizard'): + return True + return False + + def setText(self, txt): + txt = unicode(txt) + if txt and txt.startswith('program:\n#tag wizard'): + self.txt = txt + self.setReadOnly(True) + QLineEdit.setText(self, '') + QLineEdit.setText(self, _('Template generated by the wizard')) + self.setStyleSheet('TemplateLineEditor { color: gray }') + else: + QLineEdit.setText(self, txt) def tag_wizard(self): txt = unicode(self.text()) - if txt and not txt.startswith('program:\n#tag wizard'): + if txt and not self.txt: error_dialog(self, _('Invalid text'), _('The text in the box was not generated by this wizard'), show=True, show_copy_button=False) return - d = TagWizard(self, self.db, unicode(self.text())) + d = TagWizard(self, self.db, unicode(self.txt), self.mi) if d.exec_(): self.setText(d.template) + def text(self): + if self.txt: + return self.txt + return QLineEdit.text(self) + class TagWizard(QDialog): - def __init__(self, parent, db, txt): + def __init__(self, parent, db, txt, mi): QDialog.__init__(self, parent) self.setWindowTitle(_('Coloring Wizard')) self.setWindowIcon(QIcon(I('wizard.png'))) + self.mi = mi + self.columns = [] self.completion_values = defaultdict(dict) for k in db.all_field_keys(): m = db.metadata_for_field(k) - if m['datatype'] in ('text', 'enumeration', 'series'): + if m['datatype'] in ('text', 'enumeration', 'series') and \ + m['is_category'] and k not in ('identifiers'): self.columns.append(k) if m['is_custom']: -# self.completion_values[k] = {} self.completion_values[k]['v'] = db.all_custom(m['label']) elif k == 'tags': -# self.completion_values[k] = {} self.completion_values[k]['v'] = db.all_tags() + elif k == 'formats': + self.completion_values[k]['v'] = db.all_formats() else: - f = getattr(db, 'all' + k, None) + if k in ('publisher'): + ck = k + 's' + else: + ck = k + f = getattr(db, 'all_' + ck, None) if f: - self.completion_values[k] = {} - self.completion_values[k]['v'] = [v[1] for v in f()] + if k == 'authors': + self.completion_values[k]['v'] = [v[1].\ + replace('|', ',') for v in f()] + else: + self.completion_values[k]['v'] = [v[1] for v in f()] + if k in self.completion_values: self.completion_values[k]['m'] = m['is_multiple'] @@ -188,12 +226,28 @@ class TagWizard(QDialog): pass i += 1 + w = QLabel(_('Preview')) + l.addWidget(w, 99, 0, 1, 1) + w = self.test_box = QLineEdit(self) + w.setReadOnly(True) + l.addWidget(w, 99, 1, 1, 1) + w = QPushButton(_('Test')) + l.addWidget(w, 99, 3, 1, 1) + w.clicked.connect(self.preview) + bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self) l.addWidget(bb, 100, 3, 1, 2) bb.accepted.connect(self.accepted) bb.rejected.connect(self.reject) self.template = '' + def preview(self): + if not self.generate_program(): + return + t = composite_formatter.safe_format(self.template, self.mi, + _('EXCEPTION'), self.mi) + self.test_box.setText(t) + def column_changed(self, s, valbox=None): k = unicode(s) if k in self.completion_values: @@ -206,7 +260,7 @@ class TagWizard(QDialog): valbox.update_items_cache([]) valbox.set_separator(None) - def accepted(self): + def generate_program(self): res = ("program:\n#tag wizard -- do not directly edit\n" " first_non_empty(\n") lines = [] @@ -270,4 +324,10 @@ class TagWizard(QDialog): if f and t and c: res += '#' + t + ':|:' + c + ':|:' + f +':|:' + nfc + ':|:' + re + '\n' self.template += res - self.accept() + return True + + def accepted(self): + if self.generate_program(): + self.accept() + else: + self.template = '' diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 79db1aecf8..d292cada4b 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -6,7 +6,7 @@ __copyright__ = '2010, Kovid Goyal' + - _('If you want to color a field based on tags, then click the ' - 'button next to an empty line to open the tags wizard. ' + _('If you want to color a field based on contents of columns, ' + 'then click the button next to an empty line to open the wizard. ' 'It will build a template for you. You can later edit that ' - 'template with the same wizard. If you edit it by hand, the ' - 'wizard might not work or might restore old values.') + + 'template with the same wizard.') + '
' +
_('The template must evaluate to one of the color names shown '
'below. You can use any legal template expression. '
From 1ae716a115664b46b51a02355fa11df6c3c5972b Mon Sep 17 00:00:00 2001
From: Kovid Goyal This wizard will help you choose an appropriate font size key for your needs. Just enter the base font size of the input document and then enter an input font size. The wizard will display what font size it will be mapped to, by the font rescaling algorithm. You can adjust the algorithm by adjusting the output base font size and font key below. When you find values suitable for you, click OK. By default, if the output base font size is zero and/or no font size key is specified, calibre will use the values from the current Output Profile. See the User Manual for a discussion of how font size rescaling works. See the User Manual for a discussion of how font size rescaling works. Search and replace uses regular expressions. See the regular expressions tutorial to get started with regular expressions. Also clicking the wizard buttons below will allow you to test your regular expression against the current input document."
+msgid " Search and replace uses regular expressions. See the regular expressions tutorial to get started with regular expressions. Also clicking the wizard buttons below will allow you to test your regular expression against the current input document."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:173
@@ -6286,7 +6292,7 @@ msgid "(A regular expression)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/xpath_wizard_ui.py:89
-msgid " For example, to match all h2 tags that have class=\"chapter\", set tag to h2, attribute to class and value to chapter. Leaving attribute blank will match any attribute and leaving value blank will match any value. Setting tag to * will match any tag. To learn more advanced usage of XPath see the XPath Tutorial."
+msgid " For example, to match all h2 tags that have class=\"chapter\", set tag to h2, attribute to class and value to chapter. Leaving attribute blank will match any attribute and leaving value blank will match any value. Setting tag to * will match any tag. To learn more advanced usage of XPath see the XPath Tutorial."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/cover_flow.py:128
@@ -6312,8 +6318,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:128
#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:148
#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:230
-#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:263
-#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:267
+#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:279
+#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:283
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1139
msgid "Undefined"
msgstr ""
@@ -6569,7 +6575,7 @@ msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget.py:148
-#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:421
+#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:437
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugboard.py:273
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/save_template.py:61
msgid "Invalid template"
@@ -6577,7 +6583,7 @@ msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget.py:149
-#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:422
+#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:438
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugboard.py:274
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/save_template.py:62
msgid "The template %s is invalid:"
@@ -6937,7 +6943,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:97
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:211
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:73
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:189
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:181
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:199
msgid "&Title:"
msgstr ""
@@ -6951,12 +6958,12 @@ msgid "&Profile:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comments_dialog.py:24
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_dialog.py:218
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_dialog.py:222
msgid "&OK"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comments_dialog.py:25
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_dialog.py:219
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_dialog.py:223
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tweak_epub_ui.py:65
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:233
msgid "&Cancel"
@@ -7011,7 +7018,7 @@ msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/delete_matching_from_device.py:76
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:68
-#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1019
+#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1025
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:32
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:73
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:321
@@ -8019,91 +8026,105 @@ msgid "Negate"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:198
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:196
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:176
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:206
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:168
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:186
msgid "Advanced Search"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:199
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:197
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:177
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:207
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:169
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:187
msgid "&What kind of match to use:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:200
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:198
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:178
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:208
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:170
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:188
msgid "Contains: the word or phrase matches anywhere in the metadata field"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:201
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:199
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:179
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:209
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:171
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:189
msgid "Equals: the word or phrase must match the entire metadata field"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:202
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:200
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:180
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:210
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:172
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:190
msgid "Regular expression: the expression must match anywhere in the metadata field"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:203
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:201
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:181
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:211
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:173
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:191
msgid "Find entries that have..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:204
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:202
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:182
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:212
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:174
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:192
msgid "&All these words:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:205
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:203
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:183
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:213
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:175
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:193
msgid "This exact &phrase:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:206
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:204
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:184
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:214
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:176
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:194
msgid "&One or more of these words:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:207
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:205
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:185
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:215
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:177
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:195
msgid "But dont show entries that have..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:208
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:206
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:186
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:216
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:178
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:196
msgid "Any of these &unwanted words:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:209
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:207
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:187
-msgid "See the User Manual for more help"
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:217
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:197
+msgid "See the User Manual for more help"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:210
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:208
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:188
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:218
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:180
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:198
msgid "A&dvanced Search"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:212
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:210
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:190
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:220
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:182
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:200
msgid "Enter the title."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:213
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:191
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:183
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:201
msgid "&Author:"
msgstr ""
@@ -8126,14 +8147,16 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:219
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/template_functions_ui.py:101
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:213
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:193
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:223
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:184
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:203
msgid "&Clear"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:220
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:214
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:194
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:224
+#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/adv_search_builder_ui.py:185
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:204
msgid "Search only in specific fields:"
msgstr ""
@@ -8341,6 +8364,10 @@ msgstr ""
msgid "Ctrl+S"
msgstr ""
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_dialog.py:249
+msgid "EXCEPTION: "
+msgstr ""
+
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_dialog_ui.py:71
msgid "Function &name:"
msgstr ""
@@ -8355,53 +8382,90 @@ msgid "Python &code:"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:32
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:36
msgid "Open Template Editor"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:35
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:39
msgid "Open Tag Wizard"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:41
-#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:408
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:45
+#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:424
msgid "Edit template"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:48
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:52
msgid "Invalid text"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:49
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:53
msgid "The text in the box was not generated by this wizard"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:60
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:64
msgid "Tag Wizard"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:68
-msgid "Tags (more than one per box permitted)"
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:72
+msgid "Tags (see the popup help for more information)"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:69
-msgid "Color"
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:74
+msgid "You can enter more than one tag per box, separated by commas. The comparison ignores letter case. Set a regular expression pattern to use when trying to guess ebook metadata from filenames. A tutorial on using regular expressions is available. A tutorial on using regular expressions is available. Use the Test functionality below to test your regular expression on a few sample filenames (remember to include the file extension). The group names for the various metadata entries are documented in tooltips. Date format. Use 1-4 'd's for day, 1-4 'M's for month, and 2 or 4 'y's for year. For example:\n"
@@ -10215,68 +10289,86 @@ msgid ""
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:219
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:235
msgid "Use MMM yyyy for month + year, yyyy for year only"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:220
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:236
msgid "Default: dd MMM yyyy."
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:221
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:237
+msgid ""
+" The format specifier must begin with Default: Not formatted. For format language details see the python documentation"
+msgstr ""
+
+#:
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:241
msgid "Format for &dates"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:222
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:242
+msgid "Format for &numbers"
+msgstr ""
+
+#:
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:243
msgid "&Template"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:223
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:244
msgid "Field template. Uses the same syntax as save templates."
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:224
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:245
msgid "Similar to save templates. For example, {title} {isbn}"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:225
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:246
msgid "Default: (nothing)"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:226
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:247
msgid "&Sort/search column by"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:227
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:248
msgid "How this column should handled in the GUI when sorting and searching"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:228
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:249
msgid "If checked, this column will appear in the tags browser as a category"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:229
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:250
msgid "Show in tags browser"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:230
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:235
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:251
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:256
msgid "Values"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:231
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:252
msgid ""
"A comma-separated list of permitted values. The empty value is always\n"
"included, and is the default. For example, the list 'one,two,three' has\n"
@@ -10284,19 +10376,19 @@ msgid ""
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:234
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:255
msgid "The empty string is always the first value"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:236
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:257
msgid ""
"A list of color names to use when displaying an item. The\n"
"list must be empty or contain a color for each value."
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:238
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:259
msgid "Colors"
msgstr ""
@@ -10418,7 +10510,7 @@ msgid "Always"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel.py:132
-msgid "Automatic"
+msgid "If there is enough room"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel.py:133
@@ -10438,7 +10530,7 @@ msgid "Partitioned"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel.py:163
-msgid "Here you can specify coloring rules for columns shown in the library view. Choose the column you wish to color, then supply a template that specifies the color to use based on the values in the column. There is a tutorial on using templates."
+msgid "Here you can specify coloring rules for columns shown in the library view. Choose the column you wish to color, then supply a template that specifies the color to use based on the values in the column. There is a tutorial on using templates."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel.py:170
@@ -10920,7 +11012,7 @@ msgid "Search for plugin"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:230
-#: /home/kovid/work/calibre/src/calibre/gui2/store/search/search.py:297
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/search.py:321
msgid "No matches"
msgstr ""
@@ -11561,7 +11653,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:95
#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:279
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/chooser_widget_ui.py:58
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/chooser_widget_ui.py:80
#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread/store_dialog_ui.py:76
#: /home/kovid/work/calibre/src/calibre/gui2/store/search/search_ui.py:134
#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:109
@@ -11646,6 +11738,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/shortcuts.py:48
#: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:78
#: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:83
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/chooser_widget_ui.py:83
#: /home/kovid/work/calibre/src/calibre/gui2/store/search/search_ui.py:138
#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:113
#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:351
@@ -11718,54 +11811,87 @@ msgid "Open store in external web browswer"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:209
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:219
msgid "&Name:"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:211
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:221
msgid "&Description:"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:212
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:222
msgid "&Headquarters:"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:216
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:226
msgid "Enabled:"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:217
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:227
msgid "DRM:"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:218
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:220
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:228
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:230
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:233
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:207
msgid "true"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:219
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:221
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:229
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:231
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:234
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:208
msgid "false"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:222
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:232
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/adv_search_builder_ui.py:206
+msgid "Affiliate:"
+msgstr ""
+
+#:
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/adv_search_builder_ui.py:235
msgid "Nam&e/Description ..."
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/chooser_widget_ui.py:56
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/chooser_widget_ui.py:78
#: /home/kovid/work/calibre/src/calibre/gui2/store/search/search_ui.py:132
#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:108
msgid "Query:"
msgstr ""
+#:
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/chooser_widget_ui.py:81
+msgid "Enable"
+msgstr ""
+
+#:
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/chooser_widget_ui.py:82
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/search_ui.py:136
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:111
+msgid "All"
+msgstr ""
+
+#:
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/chooser_widget_ui.py:84
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search/search_ui.py:137
+#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:112
+msgid "Invert"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/models.py:21
+msgid "Affiliate"
+msgstr ""
+
#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/models.py:21
msgid "Enabled"
msgstr ""
@@ -11779,33 +11905,44 @@ msgid "No DRM"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/models.py:108
-msgid " This store is currently diabled and cannot be used in other parts of calibre. This store is currently enabled and can be used in other parts of calibre. This store only distributes ebooks with DRM. This store distributes ebooks with DRM. It may have some titles without DRM, but you will need to check on a per title basis. This store is headquartered in %s. This is a good indication of what market the store caters to. However, this does not necessarily mean that the store is limited to that market only. This store distributes ebooks in the following formats: %s
A tag value can be a regular expression. Check the box to turn them on. When using regular expressions, note that the wizard puts anchors (^ and $) around the expression, so you must ensure your expression matches from the beginning to the end of the tag.
Regular expression examples:"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:115
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_line_editor.py:82
+msgid ".*
matches any tag. No empty tags are checked, so you don't need to worry about empty stringsA.*
matches any tag beginning with A.*mystery.*
matches any tag containing the word \"mystery\"
"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:158
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:159
msgid "Hide column %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:163
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:164
msgid "Sort on %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:164
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:165
msgid "Ascending"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:167
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:168
msgid "Descending"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:179
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:180
msgid "Change text alignment for %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:181
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:182
msgid "Left"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:181
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:182
msgid "Right"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:182
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:183
msgid "Center"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:201
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:202
msgid "Show column"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:213
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:214
msgid "Restore default layout"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:855
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:858
msgid "Dropping onto a device is not supported. First add the book to the calibre library."
msgstr ""
@@ -9995,7 +10059,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:41
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:66
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:73
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:153
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:156
msgid "Yes/No"
msgstr ""
@@ -10027,7 +10091,7 @@ msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:65
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:152
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:155
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/emailp.py:27
#: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/models.py:21
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:124
@@ -10071,117 +10135,127 @@ msgid "Selected column is not a user-defined column"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:154
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:157
msgid "My Tags"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:155
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:158
msgid "My Series"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:156
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:159
msgid "My Rating"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:157
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:160
msgid "People"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:185
-msgid "No lookup name was provided"
-msgstr ""
-
-#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:189
-msgid "The lookup name must contain only lower case letters, digits and underscores, and start with a letter"
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:187
+msgid "Examples: The format {0:0>4d}
gives a 4-digit number with leading zeros. The format {0:d} days
prints the number then the word \"days\""
msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:192
+msgid "Examples: The format {0:.1f}
gives a floating point number with 1 digit after the decimal point. The format Price: $ {0:,.2f}
prints \"Price $ \" then displays the number with 2 digits after the decimal point and thousands separated by commas."
+msgstr ""
+
+#:
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:201
+msgid "No lookup name was provided"
+msgstr ""
+
+#:
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:205
+msgid "The lookup name must contain only lower case letters, digits and underscores, and start with a letter"
+msgstr ""
+
+#:
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:208
msgid "Lookup names cannot end with _index, because these names are reserved for the index of a series column."
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:202
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:218
msgid "No column heading was provided"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:212
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:228
msgid "The lookup name %s is already used"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:224
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:240
msgid "The heading %s is already used"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:235
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:251
msgid "You must enter a template for composite columns"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:244
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:260
msgid "You must enter at least one value for enumeration columns"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:248
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:264
msgid "You cannot provide the empty value, as it is included by default"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:252
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:268
msgid "The value \"{0}\" is in the list more than once"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:260
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:276
msgid "The colors box must be empty or contain the same number of items as the value box"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:265
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:281
msgid "The color {0} is unknown"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:201
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:217
msgid "&Lookup name"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:202
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:218
msgid "Column &heading"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:203
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:219
msgid "Used for searching the column. Must contain only digits and lower case letters."
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:204
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:220
msgid "Column heading in the library view and category name in the tag browser"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:205
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:221
msgid "&Column type"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:206
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:222
msgid "What kind of information will be kept in the column."
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:207
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:223
msgid ""
"Show check marks in the GUI. Values of 'yes', 'checked', and 'true'\n"
"will show a green check. Values of 'no', 'unchecked', and 'false' will show a red X.\n"
@@ -10189,22 +10263,22 @@ msgid ""
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:210
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:226
msgid "Show checkmarks"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:211
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:227
msgid "Check this box if this column contains names, like the authors column."
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:212
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:228
msgid "Contains names"
msgstr ""
#:
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:213
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:229
msgid ""
"{0:
\n"
+"and end with }
You can have text before and after the format specifier.\n"
+" "
+msgstr ""
+
+#:
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column_ui.py:240
+msgid "
Migrating old database to ebook library in %s
' + + _('Set this box to indicate that the two conditions must both ' + 'be true to return the "color if value found". For example, you ' + '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 ' + 'some tag and has a particular format.')) + l.addWidget(h, 0, 0, 1, 1) h = QLabel(_('Column')) - l.addWidget(h, 0, 0, 1, 1) + h.setAlignment(Qt.AlignCenter) + l.addWidget(h, 0, 1, 1, 1) h = QLabel(_('Values (see the popup help for more information)')) h.setToolTip('
' +
_('You can enter more than one value per box, separated by commas. '
- 'The comparison ignores letter case.
'
+ 'The comparison ignores letter case. Special note: you can '
+ 'enter at most one author.
'
'A value can be a regular expression. Check the box to turn '
'them on. When using regular expressions, note that the wizard '
'puts anchors (^ and $) around the expression, so you '
@@ -158,12 +172,12 @@ class TagWizard(QDialog):
'
A.*
matches anything beginning with A.*mystery.*
matches anything containing '
'the word "mystery"' + _('Check this box if the values box contains regular expressions') + '
') - l.addWidget(c, 0, 2, 1, 1) + l.addWidget(c, 0, 3, 1, 1) c = QLabel(_('Color if value found')) c.setToolTip('' + @@ -171,13 +185,15 @@ class TagWizard(QDialog): '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.') + '
') - l.addWidget(c, 0, 3, 1, 1) + l.addWidget(c, 0, 4, 1, 1) c = QLabel(_('Color if value not found')) c.setToolTip('' + _('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.') + '
') - l.addWidget(c, 0, 4, 1, 1) + l.addWidget(c, 0, 5, 1, 1) + + self.andboxes = [] self.tagboxes = [] self.colorboxes = [] self.nfcolorboxes = [] @@ -185,31 +201,42 @@ class TagWizard(QDialog): self.colboxes = [] self.colors = [unicode(s) for s in list(QColor.colorNames())] self.colors.insert(0, '') - for i in range(0, 10): - w = QComboBox() + + maxlines = 10 + for i in range(1, maxlines+1): + ab = QCheckBox(self) + self.andboxes.append(ab) + if i != maxlines: + # let the last box float in space + l.addWidget(ab, i, 0, 2, 1) + ab.stateChanged.connect(partial(self.and_box_changed, line=i-1)) + else: + ab.setVisible(False) + + w = QComboBox(self) w.addItems(self.columns) - l.addWidget(w, i+1, 0, 1, 1) + l.addWidget(w, i, 1, 1, 1) self.colboxes.append(w) tb = MultiCompleteLineEdit(self) tb.set_separator(', ') self.tagboxes.append(tb) - l.addWidget(tb, i+1, 1, 1, 1) + l.addWidget(tb, i, 2, 1, 1) w.currentIndexChanged[str].connect(partial(self.column_changed, valbox=tb)) w = QCheckBox(self) self.reboxes.append(w) - l.addWidget(w, i+1, 2, 1, 1) + l.addWidget(w, i, 3, 1, 1) w = QComboBox(self) w.addItems(self.colors) self.colorboxes.append(w) - l.addWidget(w, i+1, 3, 1, 1) + l.addWidget(w, i, 4, 1, 1) w = QComboBox(self) w.addItems(self.colors) self.nfcolorboxes.append(w) - l.addWidget(w, i+1, 4, 1, 1) + l.addWidget(w, i, 5, 1, 1) if txt: lines = txt.split('\n')[3:] @@ -222,25 +249,29 @@ class TagWizard(QDialog): nc = '' re = False f = 'tags' + a = False else: - t,c,f,nc,re = vals + t,c,f,nc,re,a = vals try: - self.colorboxes[i].setCurrentIndex(self.colorboxes[i].findText(c)) - self.nfcolorboxes[i].setCurrentIndex(self.nfcolorboxes[i].findText(nc)) + self.colboxes[i].setCurrentIndex(self.colboxes[i].findText(f)) + self.colorboxes[i].setCurrentIndex( + self.colorboxes[i].findText(c)) + self.nfcolorboxes[i].setCurrentIndex( + self.nfcolorboxes[i].findText(nc)) self.tagboxes[i].setText(t) self.reboxes[i].setChecked(re == '2') - self.colboxes[i].setCurrentIndex(self.colboxes[i].findText(f)) + self.andboxes[i].setChecked(a == '2') + i += 1 except: pass - i += 1 w = QLabel(_('Preview')) - l.addWidget(w, 99, 0, 1, 1) + l.addWidget(w, 99, 1, 1, 1) w = self.test_box = QLineEdit(self) w.setReadOnly(True) - l.addWidget(w, 99, 1, 1, 1) + l.addWidget(w, 99, 2, 1, 1) w = QPushButton(_('Test')) - l.addWidget(w, 99, 3, 1, 1) + l.addWidget(w, 99, 4, 1, 1) w.clicked.connect(self.preview) bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self) @@ -272,8 +303,11 @@ class TagWizard(QDialog): res = ("program:\n#tag wizard -- do not directly edit\n" " first_non_empty(\n") lines = [] - for tb, cb, fb, nfcb, reb in zip(self.tagboxes, self.colorboxes, - self.colboxes, self.nfcolorboxes, self.reboxes): + was_and = False + had_line = False + + for l, (tb, cb, fb, nfcb, reb, ab) in enumerate(zip(self.tagboxes, self.colorboxes, + self.colboxes, self.nfcolorboxes, self.reboxes, self.andboxes)): f = unicode(fb.currentText()) if not f: continue @@ -281,6 +315,8 @@ class TagWizard(QDialog): c = unicode(cb.currentText()).strip() nfc = unicode(nfcb.currentText()).strip() re = reb.checkState() + a = ab.checkState() + if m: tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()] if re == 2: @@ -289,9 +325,15 @@ class TagWizard(QDialog): tags = ','.join(tags) else: tags = unicode(tb.text()).strip() + if f == 'authors': + tags.replace(',', '|') + + if (tags or f) and not (tags and f and (a == 2 or c)): + error_dialog(self, _('Invalid line'), + _('Line number {0} is not valid').format(l), + show=True, show_copy_button=False) + return False - if not tags or not (c or nfc): - continue if c not in self.colors: error_dialog(self, _('Invalid color'), _('The color {0} is not valid').format(c), @@ -302,26 +344,41 @@ class TagWizard(QDialog): _('The color {0} is not valid').format(nfc), show=True, show_copy_button=False) return False + + if not was_and: + if had_line: + lines[-1] += ',' + had_line = True + lines.append(" test(and(") + else: + lines[-1] += ',' + if re == 2: if m: - lines.append(" in_list(field('{3}'), ',', '^{0}$', '{1}', '{2}')".\ - format(tags, c, nfc, f)) + lines.append(" in_list(field('{1}'), ',', '^{0}$', '1', '')".\ + format(tags, f)) else: - lines.append(" contains(field('{3}'), '{0}', '{1}', '{2}')".\ - format(tags, c, nfc, f)) + lines.append(" contains(field('{1}'), '{0}', '1', '')".\ + format(tags, f)) else: if m: - lines.append(" str_in_list(field('{3}'), ',', '{0}', '{1}', '{2}')".\ - format(tags, c, nfc, f)) + lines.append(" str_in_list(field('{1}'), ',', '{0}', '1', '')".\ + format(tags, f)) else: - lines.append(" strcmp(field('{3}'), '{0}', '{2}', '{1}', '{2}')".\ - format(tags, c, nfc, f)) - res += ',\n'.join(lines) + lines.append(" strcmp(field('{1}'), '{0}', '', '1', '')".\ + format(tags, f)) + if a == 2: + was_and = True + else: + was_and = False + lines.append(" ), '{0}', '{1}')".format(c, nfc)) + + res += '\n'.join(lines) res += ')\n' self.template = res res = '' - for tb, cb, fb, nfcb, reb in zip(self.tagboxes, self.colorboxes, - self.colboxes, self.nfcolorboxes, self.reboxes): + for tb, cb, fb, nfcb, reb, ab in zip(self.tagboxes, self.colorboxes, + self.colboxes, self.nfcolorboxes, self.reboxes, self.andboxes): t = unicode(tb.text()).strip() if t.endswith(','): t = t[:-1] @@ -329,11 +386,23 @@ class TagWizard(QDialog): f = unicode(fb.currentText()) nfc = unicode(nfcb.currentText()).strip() re = unicode(reb.checkState()) - if f and t and c: - res += '#' + t + ':|:' + c + ':|:' + f +':|:' + nfc + ':|:' + re + '\n' + a = unicode(ab.checkState()) + if f and t and (a == '2' or c): + res += '#' + t + ':|:' + c + ':|:' + f + ':|:' + \ + nfc + ':|:' + re + ':|:' + a + '\n' self.template += res return True + def and_box_changed(self, state, line=None): + if state == 2: + self.colorboxes[line].setCurrentIndex(0) + self.colorboxes[line].setEnabled(False) + self.nfcolorboxes[line].setCurrentIndex(0) + self.nfcolorboxes[line].setEnabled(False) + else: + self.colorboxes[line].setEnabled(True) + self.nfcolorboxes[line].setEnabled(True) + def accepted(self): if self.generate_program(): self.accept() diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index bde590f30b..7a8c1fb69c 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -5,12 +5,15 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal' + - _('The template must evaluate to one of the color names shown ' - 'below. You can use any legal template expression. ' + _('If you manually construct a template, then the template must ' + 'evaluate to a valid color name shown in the color names box.' + 'You can use any legal template expression. ' 'For example, you can set the title to always display in ' 'green using the template "green" (without the quotes). ' 'To show the title in the color named in the custom column ' @@ -199,6 +204,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): 'of values", it is often easier to specify the ' 'colors in the column definition dialog. There you can ' 'provide a color for each value without using a template.')+ '
') + self.color_help_scrollArea.setVisible(False) + self.color_help_button.clicked.connect(self.change_help_text) + self.colors_scrollArea.setVisible(False) + self.colors_label.setVisible(False) + self.colors_button.clicked.connect(self.change_colors_text) + choices = db.field_metadata.displayable_field_keys() choices.sort(key=sort_key) choices.insert(0, '') @@ -211,22 +222,58 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): except: pass + l = self.column_color_layout for i in range(1, self.column_color_count): + ccn = QComboBox(parent=self) + setattr(self, 'opt_column_color_name_'+str(i), ccn) + l.addWidget(ccn, i, 0, 1, 1) + + wtb = QToolButton(parent=self) + setattr(self, 'opt_column_color_wizard_'+str(i), wtb) + wtb.setIcon(QIcon(I('wizard.png'))) + l.addWidget(wtb, i, 1, 1, 1) + + ttb = QToolButton(parent=self) + setattr(self, 'opt_column_color_tpledit_'+str(i), ttb) + ttb.setIcon(QIcon(I('edit_input.png'))) + l.addWidget(ttb, i, 2, 1, 1) + + tpl = TemplateLineEditor(parent=self) + setattr(self, 'opt_column_color_template_'+str(i), tpl) + tpl.textChanged.connect(partial(self.tpl_edit_text_changed, ctrl=i)) + tpl.set_db(db) + tpl.set_mi(mi) + l.addWidget(tpl, i, 3, 1, 1) + + wtb.clicked.connect(tpl.tag_wizard) + ttb.clicked.connect(tpl.open_editor) + r('column_color_name_'+str(i), db.prefs, choices=choices) r('column_color_template_'+str(i), db.prefs) txt = db.prefs.get('column_color_template_'+str(i), None) - tpl = getattr(self, 'opt_column_color_template_'+str(i)) - tpl.set_db(db) - tpl.set_mi(mi) - toolbutton = getattr(self, 'opt_column_color_wizard_'+str(i)) - if tpl.show_wizard_button(txt): - toolbutton.clicked.connect(tpl.tag_wizard) - else: - toolbutton.clicked.connect(tpl.open_editor) - toolbutton.setIcon(QIcon(I('edit_input.png'))) + + wtb.setEnabled(tpl.enable_wizard_button(txt)) + ttb.setEnabled(not tpl.enable_wizard_button(txt) or not txt) + all_colors = [unicode(s) for s in list(QColor.colorNames())] self.colors_box.setText(', '.join(all_colors)) + def change_help_text(self): + self.color_help_scrollArea.setVisible(not self.color_help_scrollArea.isVisible()) + + def change_colors_text(self): + self.colors_scrollArea.setVisible(not self.colors_scrollArea.isVisible()) + self.colors_label.setVisible(not self.colors_label.isVisible()) + + def tpl_edit_text_changed(self, ign, ctrl=None): + tpl = getattr(self, 'opt_column_color_template_'+str(ctrl)) + txt = unicode(tpl.text()) + wtb = getattr(self, 'opt_column_color_wizard_'+str(ctrl)) + ttb = getattr(self, 'opt_column_color_tpledit_'+str(ctrl)) + wtb.setEnabled(tpl.enable_wizard_button(txt)) + ttb.setEnabled(not tpl.enable_wizard_button(txt) or not txt) + tpl.setFocus() + def initialize(self): ConfigWidgetBase.initialize(self) font = gprefs['font'] diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index fe6134f235..def1bdd41c 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -416,114 +416,95 @@ then the tags will be displayed each on their own line.' + @@ -155,7 +155,21 @@ class TagWizard(QDialog): h.setAlignment(Qt.AlignCenter) l.addWidget(h, 0, 1, 1, 1) + h = QLabel(_('Not')) + h.setToolTip('
' + + _('Set this box to indicate that the value must not 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) + l.addWidget(h, 0, 2, 1, 1) + h = QLabel(_('Values (see the popup help for more information)')) + h.setAlignment(Qt.AlignCenter) h.setToolTip('
' + _('You can enter more than one value per box, separated by commas. ' 'The comparison ignores letter case. Special note: you can ' @@ -172,12 +186,12 @@ class TagWizard(QDialog): '
A.*
matches anything beginning with A.*mystery.*
matches anything containing '
'the word "mystery"' + _('Check this box if the values box contains regular expressions') + '
') - l.addWidget(c, 0, 3, 1, 1) + l.addWidget(c, 0, 4, 1, 1) c = QLabel(_('Color if value found')) c.setToolTip('' + @@ -185,15 +199,16 @@ class TagWizard(QDialog): '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.') + '
') - l.addWidget(c, 0, 4, 1, 1) + l.addWidget(c, 0, 5, 1, 1) c = QLabel(_('Color if value not found')) c.setToolTip('' + _('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.') + '
') - l.addWidget(c, 0, 5, 1, 1) + l.addWidget(c, 0, 6, 1, 1) self.andboxes = [] + self.notboxes = [] self.tagboxes = [] self.colorboxes = [] self.nfcolorboxes = [] @@ -218,26 +233,30 @@ class TagWizard(QDialog): l.addWidget(w, i, 1, 1, 1) self.colboxes.append(w) + nb = QCheckBox(self) + self.notboxes.append(nb) + l.addWidget(nb, i, 2, 1, 1) + tb = MultiCompleteLineEdit(self) tb.set_separator(', ') self.tagboxes.append(tb) - l.addWidget(tb, i, 2, 1, 1) + l.addWidget(tb, i, 3, 1, 1) w.currentIndexChanged[str].connect(partial(self.column_changed, valbox=tb)) w = QCheckBox(self) self.reboxes.append(w) - l.addWidget(w, i, 3, 1, 1) - - w = QComboBox(self) - w.addItems(self.colors) - self.colorboxes.append(w) l.addWidget(w, i, 4, 1, 1) w = QComboBox(self) w.addItems(self.colors) - self.nfcolorboxes.append(w) + self.colorboxes.append(w) l.addWidget(w, i, 5, 1, 1) + w = QComboBox(self) + w.addItems(self.colors) + self.nfcolorboxes.append(w) + l.addWidget(w, i, 6, 1, 1) + if txt: lines = txt.split('\n')[3:] i = 0 @@ -250,8 +269,9 @@ class TagWizard(QDialog): re = False f = 'tags' a = False + n = False else: - t,c,f,nc,re,a = vals + t,c,f,nc,re,a,n = vals try: self.colboxes[i].setCurrentIndex(self.colboxes[i].findText(f)) self.colorboxes[i].setCurrentIndex( @@ -261,6 +281,7 @@ class TagWizard(QDialog): self.tagboxes[i].setText(t) self.reboxes[i].setChecked(re == '2') self.andboxes[i].setChecked(a == '2') + self.notboxes[i].setChecked(n == '2') i += 1 except: pass @@ -269,9 +290,9 @@ class TagWizard(QDialog): l.addWidget(w, 99, 1, 1, 1) w = self.test_box = QLineEdit(self) w.setReadOnly(True) - l.addWidget(w, 99, 2, 1, 1) + l.addWidget(w, 99, 3, 1, 1) w = QPushButton(_('Test')) - l.addWidget(w, 99, 4, 1, 1) + l.addWidget(w, 99, 5, 1, 1) w.clicked.connect(self.preview) bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self) @@ -306,8 +327,10 @@ class TagWizard(QDialog): was_and = False had_line = False - for l, (tb, cb, fb, nfcb, reb, ab) in enumerate(zip(self.tagboxes, self.colorboxes, - self.colboxes, self.nfcolorboxes, self.reboxes, self.andboxes)): + line = 0 + for tb, cb, fb, nfcb, reb, ab, nb in zip( + self.tagboxes, self.colorboxes, self.colboxes, + self.nfcolorboxes, self.reboxes, self.andboxes, self.notboxes): f = unicode(fb.currentText()) if not f: continue @@ -316,6 +339,15 @@ class TagWizard(QDialog): nfc = unicode(nfcb.currentText()).strip() re = reb.checkState() a = ab.checkState() + n = nb.checkState() + line += 1 + + if n == 2: + tval = '' + fval = '1' + else: + tval = '1' + fval = '' if m: tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()] @@ -330,7 +362,7 @@ class TagWizard(QDialog): if (tags or f) and not (tags and f and (a == 2 or c)): error_dialog(self, _('Invalid line'), - _('Line number {0} is not valid').format(l), + _('Line number {0} is not valid').format(line), show=True, show_copy_button=False) return False @@ -355,18 +387,18 @@ class TagWizard(QDialog): if re == 2: if m: - lines.append(" in_list(field('{1}'), ',', '^{0}$', '1', '')".\ - format(tags, f)) + lines.append(" in_list(field('{1}'), ',', '^{0}$', '{2}', '{3}')".\ + format(tags, f, tval, fval)) else: - lines.append(" contains(field('{1}'), '{0}', '1', '')".\ - format(tags, f)) + lines.append(" contains(field('{1}'), '{0}', '{2}', '{3}')".\ + format(tags, f, tval, fval)) else: if m: - lines.append(" str_in_list(field('{1}'), ',', '{0}', '1', '')".\ - format(tags, f)) + lines.append(" str_in_list(field('{1}'), ',', '{0}', '{2}', '{3}')".\ + format(tags, f, tval, fval)) else: - lines.append(" strcmp(field('{1}'), '{0}', '', '1', '')".\ - format(tags, f)) + lines.append(" strcmp(field('{1}'), '{0}', '{3}', '{2}', '{3}')".\ + format(tags, f, tval, fval)) if a == 2: was_and = True else: @@ -377,8 +409,9 @@ class TagWizard(QDialog): res += ')\n' self.template = res res = '' - for tb, cb, fb, nfcb, reb, ab in zip(self.tagboxes, self.colorboxes, - self.colboxes, self.nfcolorboxes, self.reboxes, self.andboxes): + for tb, cb, fb, nfcb, reb, ab, nb in zip( + self.tagboxes, self.colorboxes, self.colboxes, + self.nfcolorboxes, self.reboxes, self.andboxes, self.notboxes): t = unicode(tb.text()).strip() if t.endswith(','): t = t[:-1] @@ -387,9 +420,10 @@ class TagWizard(QDialog): nfc = unicode(nfcb.currentText()).strip() re = unicode(reb.checkState()) a = unicode(ab.checkState()) + n = unicode(nb.checkState()) if f and t and (a == '2' or c): res += '#' + t + ':|:' + c + ':|:' + f + ':|:' + \ - nfc + ':|:' + re + ':|:' + a + '\n' + nfc + ':|:' + re + ':|:' + a + ':|:' + n + '\n' self.template += res return True From 7f3d6f6b155d13d5bc240d3e47fe2fb303f64c4d Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 30 May 2011 15:53:04 +0100 Subject: [PATCH 28/58] Add missing reverse apostrophe in search documentation --- src/calibre/manual/gui.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/manual/gui.rst b/src/calibre/manual/gui.rst index a4e18c2e07..e2758bc257 100644 --- a/src/calibre/manual/gui.rst +++ b/src/calibre/manual/gui.rst @@ -352,7 +352,7 @@ The syntax for searching for dates is:: If the date is ambiguous, the current locale is used for date comparison. For example, in an mm/dd/yyyy locale, 2/1/2009 is interpreted as 1 Feb 2009. In a dd/mm/yyyy locale, it is interpreted as 2 Jan 2009. Some special date strings are available. The string ``today`` translates to today's date, whatever it is. The -strings `yesterday`` and ``thismonth`` also work. In addition, the string ``daysago`` can be used to compare +strings ``yesterday`` and ``thismonth`` also work. In addition, the string ``daysago`` can be used to compare to a date some number of days ago, for example: date:>10daysago, date:<=45daysago. You can search for books that have a format of a certain size like this:: From e27a3638d222bef6e7692d83a340ae5757a5376b Mon Sep 17 00:00:00 2001 From: Kovid Goyal'+_('Choose formats not to be deleted'), ids) + '
'+_('Choose formats not to be deleted.
Note that ' + 'this will never remove all formats from a book.'), ids) if fmts is None: return for id in ids: diff --git a/src/calibre/gui2/dialogs/select_formats.py b/src/calibre/gui2/dialogs/select_formats.py index 5934c8c0f9..aea56ad196 100644 --- a/src/calibre/gui2/dialogs/select_formats.py +++ b/src/calibre/gui2/dialogs/select_formats.py @@ -44,7 +44,7 @@ class SelectFormats(QDialog): self.setLayout(self._l) self.setWindowTitle(_('Choose formats')) self._m = QLabel(msg) - self._m.setWordWrap = True + self._m.setWordWrap(True) self._l.addWidget(self._m) self.formats = Formats(fmt_list) self.fview = QListView(self) From cdeb8ade3d36fc14bf2f84fbd774c617b4e277ae Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 31 May 2011 12:13:08 +0100 Subject: [PATCH 39/58] Yet another improvement on the color wizard. Make 'None' compare to zero in the formatter function 'cmp'. --- .../gui2/dialogs/template_line_editor.py | 327 ++++++++++-------- src/calibre/utils/formatter_functions.py | 4 +- 2 files changed, 187 insertions(+), 144 deletions(-) diff --git a/src/calibre/gui2/dialogs/template_line_editor.py b/src/calibre/gui2/dialogs/template_line_editor.py index b24328f0ad..6a0b07200e 100644 --- a/src/calibre/gui2/dialogs/template_line_editor.py +++ b/src/calibre/gui2/dialogs/template_line_editor.py @@ -46,8 +46,8 @@ class TemplateLineEditor(QLineEdit): menu.exec_(event.globalPos()) def clear_field(self): - self.setText('') self.txt = None + self.setText('') self.setReadOnly(False) self.setStyleSheet('TemplateLineEditor { color: black }') @@ -95,6 +95,39 @@ class TemplateLineEditor(QLineEdit): 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): QDialog.__init__(self, parent) self.setWindowTitle(_('Coloring Wizard')) @@ -106,11 +139,19 @@ class TagWizard(QDialog): self.completion_values = defaultdict(dict) for k in db.all_field_keys(): m = db.metadata_for_field(k) - if m['datatype'] in ('text', 'enumeration', 'series') and \ - m['is_category'] and k not in ('identifiers'): + if k.endswith('_index') or ( + 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.completion_values[k]['dt'] = m['datatype'] if m['is_custom']: - self.completion_values[k]['v'] = db.all_custom(m['label']) + 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']) elif k == 'tags': self.completion_values[k]['v'] = db.all_tags() elif k == 'formats': @@ -127,12 +168,15 @@ class TagWizard(QDialog): replace('|', ',') for v in f()] else: 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 == 'authors': - self.completion_values[k]['m'] = None + mult = '&' 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.insert(0, '') @@ -140,12 +184,12 @@ class TagWizard(QDialog): l = QGridLayout() self.setLayout(l) l.setColumnStretch(2, 10) - l.setColumnMinimumWidth(3, 300) + l.setColumnMinimumWidth(5, 300) h = QLabel(_('And')) h.setToolTip('
' + _('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 ' 'and a #read custom column is checked, or if a book has ' 'some tag and has a particular format.')) @@ -155,107 +199,106 @@ class TagWizard(QDialog): h.setAlignment(Qt.AlignCenter) l.addWidget(h, 0, 1, 1, 1) - h = QLabel(_('Not')) - h.setToolTip('
' + - _('Set this box to indicate that the value must not 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 = QLabel(_('is')) h.setAlignment(Qt.AlignCenter) l.addWidget(h, 0, 2, 1, 1) - h = QLabel(_('Values (see the popup help for more information)')) + h = QLabel(_('not')) + h.setToolTip('
' + + _('Check this box to indicate that the value must not match ' + 'to use the color. For example, you can check if a tag does ' + 'not exist by entering that tag and checking this box.') + '
') + h.setAlignment(Qt.AlignCenter) + l.addWidget(h, 0, 3, 1, 1) + + c = QLabel(_('empty')) + c.setToolTip('' + + _('Check this box to check if the column is empty') + '
') + l.addWidget(c, 0, 4, 1, 1) + + h = QLabel(_('Values')) h.setAlignment(Qt.AlignCenter) h.setToolTip('' +
_('You can enter more than one value per box, separated by commas. '
- 'The comparison ignores letter case. Special note: you can '
- 'enter at most one author.
'
+ 'The comparison ignores letter case. Special note: authors are '
+ 'separated by ampersands (&).
'
'A value can be a regular expression. Check the box to turn '
'them on. When using regular expressions, note that the wizard '
'puts anchors (^ and $) around the expression, so you '
'must ensure your expression matches from the beginning '
- 'to the end of the column you are checking.
'
+ 'to the end of the column/value you are checking.
'
'Regular expression examples:') + '
.*
matches anything in the column. No '
- 'empty values are checked, so you don\'t need to worry about '
- 'empty strings.*
matches anything in the column.A.*
matches anything beginning with A.*mystery.*
matches anything containing '
'the word "mystery"' + _('Check this box if the values box contains regular expressions') + '
') - l.addWidget(c, 0, 4, 1, 1) - - c = QLabel(_('Color if value found')) - c.setToolTip('' + - _('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.') + '
') - l.addWidget(c, 0, 5, 1, 1) - c = QLabel(_('Color if value not found')) - c.setToolTip('' + - _('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.') + '
') l.addWidget(c, 0, 6, 1, 1) - self.andboxes = [] - self.notboxes = [] - self.tagboxes = [] - self.colorboxes = [] - self.nfcolorboxes = [] - self.reboxes = [] - self.colboxes = [] + c = QLabel(_('color')) + c.setAlignment(Qt.AlignCenter) + c.setToolTip('' + + _('Use this color if the column matches the tests.') + '
') + l.addWidget(c, 0, 7, 1, 1) + + self.andboxes = [] + self.notboxes = [] + self.tagboxes = [] + self.colorboxes = [] + self.reboxes = [] + self.colboxes = [] + self.emptyboxes = [] + self.colors = [unicode(s) for s in list(QColor.colorNames())] 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 for i in range(1, maxlines+1): - ab = QCheckBox(self) - self.andboxes.append(ab) - if i != maxlines: - # let the last box float in space - l.addWidget(ab, i, 0, 2, 1) - ab.stateChanged.connect(partial(self.and_box_changed, line=i-1)) - else: - ab.setVisible(False) + w = create_widget(QCheckBox, self.andboxes, l, i, 0, None, rowspan=True) + w.stateChanged.connect(partial(self.and_box_changed, line=i-1)) + if i == maxlines: + # last box is invisible + w.setVisible(False) - w = QComboBox(self) - w.addItems(self.columns) - l.addWidget(w, i, 1, 1, 1) - self.colboxes.append(w) + w = create_widget(QComboBox, self.colboxes, l, i, 1, self.columns) + w.currentIndexChanged[str].connect(partial(self.column_changed, line=i-1)) - nb = QCheckBox(self) - self.notboxes.append(nb) - l.addWidget(nb, i, 2, 1, 1) + w = QLabel(self) + w.setText(_('is')) + l.addWidget(w, i, 2, 1, 1) - tb = MultiCompleteLineEdit(self) - tb.set_separator(', ') - self.tagboxes.append(tb) - l.addWidget(tb, i, 3, 1, 1) - w.currentIndexChanged[str].connect(partial(self.column_changed, valbox=tb)) + create_widget(QCheckBox, self.notboxes, l, i, 3, None) - w = QCheckBox(self) - self.reboxes.append(w) - l.addWidget(w, i, 4, 1, 1) + w = create_widget(QCheckBox, self.emptyboxes, l, i, 4, None) + w.stateChanged.connect(partial(self.empty_box_changed, line=i-1)) - w = QComboBox(self) - w.addItems(self.colors) - self.colorboxes.append(w) - l.addWidget(w, i, 5, 1, 1) + create_widget(MultiCompleteLineEdit, self.tagboxes, l, i, 5, None, align=0) + create_widget(QCheckBox, self.reboxes, l, i, 6, None) + create_widget(QComboBox, self.colorboxes, l, i, 7, self.colors) - w = QComboBox(self) - w.addItems(self.colors) - self.nfcolorboxes.append(w) - l.addWidget(w, i, 6, 1, 1) + w = create_widget(QLabel, None, l, maxlines+1, 5, None) + w.setText(_('If none of the tests match, set the color to')) + self.elsebox = create_widget(QComboBox, None, l, maxlines+1, 7, self.colors) + self.elsebox.setToolTip('' + + _('If this box contains a color, it will be used if none ' + 'of the above rules match.') + '
') if txt: lines = txt.split('\n')[3:] @@ -263,25 +306,27 @@ class TagWizard(QDialog): for line in lines: if line.startswith('#'): 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: t, c = vals - nc = '' - re = False f = 'tags' - a = False - n = False + a = n = e = re = False else: - t,c,f,nc,re,a,n = vals + t,c,f,re,a,n,e = vals try: self.colboxes[i].setCurrentIndex(self.colboxes[i].findText(f)) self.colorboxes[i].setCurrentIndex( self.colorboxes[i].findText(c)) - self.nfcolorboxes[i].setCurrentIndex( - self.nfcolorboxes[i].findText(nc)) self.tagboxes[i].setText(t) self.reboxes[i].setChecked(re == '2') self.andboxes[i].setChecked(a == '2') self.notboxes[i].setChecked(n == '2') + self.emptyboxes[i].setChecked(e == '2') i += 1 except: pass @@ -290,13 +335,17 @@ class TagWizard(QDialog): l.addWidget(w, 99, 1, 1, 1) w = self.test_box = QLineEdit(self) w.setReadOnly(True) - l.addWidget(w, 99, 3, 1, 1) + l.addWidget(w, 99, 2, 1, 5) w = QPushButton(_('Test')) - l.addWidget(w, 99, 5, 1, 1) + w.setToolTip('' +
+ _('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)
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.rejected.connect(self.reject)
self.template = ''
@@ -308,14 +357,22 @@ class TagWizard(QDialog):
_('EXCEPTION'), self.mi)
self.test_box.setText(t)
- def column_changed(self, s, valbox=None):
+ def column_changed(self, s, line=None):
k = unicode(s)
if k in self.completion_values:
+ valbox = self.tagboxes[line]
valbox.update_items_cache(self.completion_values[k]['v'])
if self.completion_values[k]['m']:
valbox.set_separator(', ')
else:
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:
valbox.update_items_cache([])
valbox.set_separator(None)
@@ -324,59 +381,44 @@ class TagWizard(QDialog):
res = ("program:\n#tag wizard -- do not directly edit\n"
" first_non_empty(\n")
lines = []
- was_and = False
- had_line = False
+ was_and = had_line = False
line = 0
- for tb, cb, fb, nfcb, reb, ab, nb in zip(
- self.tagboxes, self.colorboxes, self.colboxes,
- self.nfcolorboxes, self.reboxes, self.andboxes, self.notboxes):
+ for tb, cb, fb, reb, ab, nb, eb in zip(
+ self.tagboxes, self.colorboxes, self.colboxes,
+ self.reboxes, self.andboxes, self.notboxes, self.emptyboxes):
f = unicode(fb.currentText())
if not f:
continue
m = self.completion_values[f]['m']
+ dt = self.completion_values[f]['dt']
c = unicode(cb.currentText()).strip()
- nfc = unicode(nfcb.currentText()).strip()
re = reb.checkState()
a = ab.checkState()
n = nb.checkState()
+ e = eb.checkState()
line += 1
- if n == 2:
- tval = ''
- fval = '1'
- else:
- tval = '1'
- fval = ''
+ tval = '' if n == 2 else '1'
+ fval = '1' if n == 2 else ''
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:
tags = '$|^'.join(tags)
else:
- tags = ','.join(tags)
+ tags = m.join(tags)
+ if m == '&':
+ tags = tags.replace(',', '|')
else:
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'),
_('Line number {0} is not valid').format(line),
show=True, show_copy_button=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 had_line:
lines[-1] += ','
@@ -385,57 +427,58 @@ class TagWizard(QDialog):
else:
lines[-1] += ','
- if re == 2:
- if m:
- lines.append(" in_list(field('{1}'), ',', '^{0}$', '{2}', '{3}')".\
- 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))
+ key = dt + ('.mult' if m else '') + ('.empty' if e else '') + ('.re' if re else '')
+ template = self.templates[key]
+ lines.append(template.format(v=tags, f=f, tv=tval, fv=fval, mult=m))
+
if a == 2:
was_and = True
else:
was_and = False
- lines.append(" ), '{0}', '{1}')".format(c, nfc))
+ lines.append(" ), '{0}', '')".format(c))
res += '\n'.join(lines)
+ else_txt = unicode(self.elsebox.currentText())
+ if else_txt:
+ res += ",\n '" + else_txt + "'"
res += ')\n'
self.template = res
res = ''
- for tb, cb, fb, nfcb, reb, ab, nb in zip(
- self.tagboxes, self.colorboxes, self.colboxes,
- self.nfcolorboxes, self.reboxes, self.andboxes, self.notboxes):
+ for tb, cb, fb, reb, ab, nb, eb in zip(
+ self.tagboxes, self.colorboxes, self.colboxes,
+ self.reboxes, self.andboxes, self.notboxes, self.emptyboxes):
t = unicode(tb.text()).strip()
if t.endswith(','):
t = t[:-1]
c = unicode(cb.currentText()).strip()
f = unicode(fb.currentText())
- nfc = unicode(nfcb.currentText()).strip()
re = unicode(reb.checkState())
a = unicode(ab.checkState())
n = unicode(nb.checkState())
- if f and t and (a == '2' or c):
- res += '#' + t + ':|:' + c + ':|:' + f + ':|:' + \
- nfc + ':|:' + re + ':|:' + a + ':|:' + n + '\n'
+ e = unicode(eb.checkState())
+ if f and (t or e) and (a == '2' or c):
+ res += '#' + t + ':|:' + c + ':|:' + f + ':|:' + re + ':|:' + \
+ a + ':|:' + n + ':|:' + e + '\n'
+ res += '#else:' + else_txt + '\n'
self.template += res
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):
if state == 2:
self.colorboxes[line].setCurrentIndex(0)
self.colorboxes[line].setEnabled(False)
- self.nfcolorboxes[line].setCurrentIndex(0)
- self.nfcolorboxes[line].setEnabled(False)
else:
self.colorboxes[line].setEnabled(True)
- self.nfcolorboxes[line].setEnabled(True)
def accepted(self):
if self.generate_program():
diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py
index b66aec2cb9..7b9a2ac51c 100644
--- a/src/calibre/utils/formatter_functions.py
+++ b/src/calibre/utils/formatter_functions.py
@@ -106,8 +106,8 @@ class BuiltinCmp(BuiltinFormatterFunction):
'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):
- x = float(x if x else 0)
- y = float(y if y else 0)
+ x = float(x if x and x != 'None' else 0)
+ y = float(y if y and y != 'None' else 0)
if x < y:
return lt
if x == y:
From f4affb57e3479d5ba811442a4c0654591966e2a7 Mon Sep 17 00:00:00 2001
From: Kovid Goyal ' +
- _('Check this box to indicate that the value must not match '
- 'to use the color. For example, you can check if a tag does '
- 'not exist by entering that tag and checking this box.') + '