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'".*?((?