diff --git a/src/calibre/gui2/dialogs/template_line_editor.py b/src/calibre/gui2/dialogs/template_line_editor.py
index c3598a8abb..95727e442b 100644
--- a/src/calibre/gui2/dialogs/template_line_editor.py
+++ b/src/calibre/gui2/dialogs/template_line_editor.py
@@ -5,8 +5,12 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal '
__docformat__ = 'restructuredtext en'
-from PyQt4.Qt import QLineEdit
+from PyQt4.Qt import (QLineEdit, QDialog, QGridLayout, QLabel,
+ QDialogButtonBox, QColor, QComboBox, QIcon)
+
from calibre.gui2.dialogs.template_dialog import TemplateDialog
+from calibre.gui2.complete import MultiCompleteComboBox
+from calibre.gui2 import error_dialog
class TemplateLineEditor(QLineEdit):
@@ -14,13 +18,22 @@ class TemplateLineEditor(QLineEdit):
Extend the context menu of a QLineEdit to include more actions.
'''
+ def __init__(self, parent):
+ QLineEdit.__init__(self, parent)
+ self.tags = None
+
+ def set_tags(self, tags):
+ self.tags = tags
+
def contextMenuEvent(self, event):
menu = self.createStandardContextMenu()
menu.addSeparator()
action_open_editor = menu.addAction(_('Open Template Editor'))
-
action_open_editor.triggered.connect(self.open_editor)
+ if self.tags:
+ action_tag_wizard = menu.addAction(_('Open Tag Wizard'))
+ action_tag_wizard.triggered.connect(self.tag_wizard)
menu.exec_(event.globalPos())
def open_editor(self):
@@ -29,4 +42,89 @@ class TemplateLineEditor(QLineEdit):
if t.exec_():
self.setText(t.textbox.toPlainText())
+ def tag_wizard(self):
+ txt = unicode(self.text())
+ if txt and not txt.startswith('program:\n#tag wizard'):
+ 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.tags, unicode(self.text()))
+ if d.exec_():
+ self.setText(d.template)
+class TagWizard(QDialog):
+
+ def __init__(self, parent, tags, txt):
+ QDialog.__init__(self, parent)
+ self.setWindowTitle(_('Tag Wizard'))
+ self.setWindowIcon(QIcon(I('wizard.png')))
+
+ self.tags = tags
+ l = QGridLayout()
+ self.setLayout(l)
+ l.addWidget(QLabel(_('Tag Value')), 0, 0, 1, 1)
+ l.addWidget(QLabel(_('Color')), 0, 1, 1, 1)
+ self.tagboxes = []
+ self.colorboxes = []
+ self.colors = [unicode(s) for s in list(QColor.colorNames())]
+ self.colors.insert(0, '')
+ for i in range(0, 10):
+ tb = MultiCompleteComboBox(self)
+ tb.set_separator(', ')
+ 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)
+
+ if txt:
+ lines = txt.split('\n')[3:]
+ i = 0
+ for line in lines:
+ if line.startswith('#'):
+ t,c = line[1:].split(':|:')
+ try:
+ self.colorboxes[i].setCurrentIndex(self.colorboxes[i].findText(c))
+ self.tagboxes[i].setText(t)
+ except:
+ pass
+ i += 1
+
+ bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self)
+ l.addWidget(bb, 100, 1, 1, 1)
+ bb.accepted.connect(self.accepted)
+ bb.rejected.connect(self.reject)
+ self.template = ''
+
+ def accepted(self):
+ res = ("program:\n#tag wizard -- do not directly edit\n"
+ " t = field('tags');\n first_non_empty(\n")
+ lines = []
+ for tb, cb in zip(self.tagboxes, self.colorboxes):
+ tags = [t.strip() for t in unicode(tb.currentText()).split(',') if t.strip()]
+ c = unicode(cb.currentText()).strip()
+ if not tags or not c:
+ continue
+ 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
+ for t in tags:
+ lines.append(" in_list(t, ',', '^{0}$', '{1}', '')".format(t, c))
+ res += ',\n'.join(lines)
+ res += ')\n'
+ self.template = res
+ res = ''
+ for tb, cb in zip(self.tagboxes, self.colorboxes):
+ t = unicode(tb.currentText()).strip()
+ if t.endswith(','):
+ t = t[:-1]
+ c = unicode(cb.currentText()).strip()
+ if t and c:
+ res += '#' + t + ':|:' + c + '\n'
+ self.template += res
+ self.accept()
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index d698655746..86c5871193 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -98,6 +98,7 @@ class BooksModel(QAbstractTableModel): # {{{
self.current_highlighted_idx = None
self.highlight_only = False
self.column_color_map = {}
+ self.colors = [unicode(c) for c in QColor.colorNames()]
self.read_config()
def change_alignment(self, colname, alignment):
@@ -714,9 +715,11 @@ class BooksModel(QAbstractTableModel): # {{{
mi = self.db.get_metadata(self.id(index), index_is_id=True)
fmt = self.column_color_map[key]
try:
- color = QColor(composite_formatter.safe_format(fmt, mi, '', mi))
- if color.isValid():
- return QVariant(color)
+ color = composite_formatter.safe_format(fmt, mi, '', mi)
+ if color in self.colors:
+ color = QColor(color)
+ if color.isValid():
+ return QVariant(color)
except:
return NONE
elif self.is_custom_column(key) and \
diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py
index 1c9d4abfce..7692a8aba7 100644
--- a/src/calibre/gui2/preferences/look_feel.py
+++ b/src/calibre/gui2/preferences/look_feel.py
@@ -167,6 +167,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
''
'tutorial on using templates.') +
'
' +
+ _('If you want to color a field based on tags, then right-click '
+ 'in an empty template line and choose tags 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.') +
+ '
' +
_('The template must evaluate to one of the color names shown '
'below. You can use any legal template expression. '
'For example, you can set the title to always display in '
@@ -198,9 +204,12 @@ 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()
for i in range(1, self.column_color_count):
r('column_color_name_'+str(i), db.prefs, choices=choices)
r('column_color_template_'+str(i), db.prefs)
+ temp = getattr(self, 'opt_column_color_template_'+str(i))
+ temp.set_tags(tags)
all_colors = [unicode(s) for s in list(QColor.colorNames())]
self.colors_box.setText(', '.join(all_colors))
@@ -273,9 +282,10 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
def commit(self, *args):
for i in range(1, self.column_color_count):
col = getattr(self, 'opt_column_color_name_'+str(i))
- if not col.currentText():
- temp = getattr(self, 'opt_column_color_template_'+str(i))
- temp.setText('')
+ tpl = getattr(self, 'opt_column_color_template_'+str(i))
+ if not col.currentIndex() or not unicode(tpl.text()).strip():
+ col.setCurrentIndex(0)
+ tpl.setText('')
rr = ConfigWidgetBase.commit(self, *args)
if self.current_font != self.initial_font:
gprefs['font'] = (self.current_font[:4] if self.current_font else
diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui
index ad990a1586..31fa0df234 100644
--- a/src/calibre/gui2/preferences/look_feel.ui
+++ b/src/calibre/gui2/preferences/look_feel.ui
@@ -419,14 +419,14 @@ then the tags will be displayed each on their own line.
-
- Column name
+ Column to color
-
- Selection template
+ Color selection template
diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py
index 2e40275beb..fccd0015c1 100644
--- a/src/calibre/utils/formatter.py
+++ b/src/calibre/utils/formatter.py
@@ -215,7 +215,7 @@ class TemplateFormatter(string.Formatter):
(r'\w+', lambda x,t: (2, t)),
(r'".*?((?