diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index d79c92befa..72c8e0629f 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -99,8 +99,7 @@ class BooksModel(QAbstractTableModel): # {{{ self.ids_to_highlight_set = set() self.current_highlighted_idx = None self.highlight_only = False - self.column_color_list = [] - self.colors = [unicode(c) for c in QColor.colorNames()] + self.colors = frozenset([unicode(c) for c in QColor.colorNames()]) self.read_config() def change_alignment(self, colname, alignment): @@ -156,7 +155,6 @@ class BooksModel(QAbstractTableModel): # {{{ self.headers[col] = self.custom_columns[col]['name'] self.build_data_convertors() - self.set_color_templates(reset=False) self.reset() self.database_changed.emit(db) self.stop_metadata_backup() @@ -545,16 +543,6 @@ class BooksModel(QAbstractTableModel): # {{{ img = self.default_image return img - def set_color_templates(self, reset=True): - self.column_color_list = [] - for i in range(1,self.db.column_color_count+1): - name = self.db.prefs.get('column_color_name_'+str(i)) - if name: - self.column_color_list.append((name, - self.db.prefs.get('column_color_template_'+str(i)))) - if reset: - self.reset() - def build_data_convertors(self): def authors(r, idx=-1): au = self.db.data[r][idx] @@ -726,14 +714,16 @@ class BooksModel(QAbstractTableModel): # {{{ return QVariant(QColor('lightgreen')) elif role == Qt.ForegroundRole: key = self.column_map[col] - for k,fmt in self.column_color_list: + mi = None + for k, fmt in self.db.prefs['column_color_rules']: if k != key: continue id_ = self.id(index) if id_ in self.color_cache: if key in self.color_cache[id_]: return self.color_cache[id_][key] - mi = self.db.get_metadata(self.id(index), index_is_id=True) + if mi is None: + mi = self.db.get_metadata(id_, index_is_id=True) try: color = composite_formatter.safe_format(fmt, mi, '', mi) if color in self.colors: @@ -743,7 +733,7 @@ class BooksModel(QAbstractTableModel): # {{{ self.color_cache[id_][key] = color return color except: - return NONE + continue if self.is_custom_column(key) and \ self.custom_columns[key]['datatype'] == 'enumeration': cc = self.custom_columns[self.column_map[col]]['display'] diff --git a/src/calibre/gui2/preferences/coloring.py b/src/calibre/gui2/preferences/coloring.py index a8a9f666b9..ec5fef1304 100644 --- a/src/calibre/gui2/preferences/coloring.py +++ b/src/calibre/gui2/preferences/coloring.py @@ -10,10 +10,11 @@ __docformat__ = 'restructuredtext en' from PyQt4.Qt import (QWidget, QDialog, QLabel, QGridLayout, QComboBox, QSize, QLineEdit, QIntValidator, QDoubleValidator, QFrame, QColor, Qt, QIcon, QScrollArea, QPushButton, QVBoxLayout, QDialogButtonBox, QToolButton, - QListView, QAbstractListModel) + QListView, QAbstractListModel, pyqtSignal) from calibre.utils.icu import sort_key from calibre.gui2 import error_dialog +from calibre.gui2.dialogs.template_dialog import TemplateDialog from calibre.gui2.metadata.single_download import RichTextDelegate from calibre.library.coloring import (Rule, conditionable_columns, displayable_columns, rule_from_template) @@ -402,6 +403,10 @@ class RulesModel(QAbstractListModel): self.dataChanged.emit(idx, idx) return idx + def clear(self): + self.rules = [] + self.reset() + def rule_to_html(self, col, rule): if isinstance(rule, basestring): return _(''' @@ -422,6 +427,8 @@ class RulesModel(QAbstractListModel): class EditRules(QWidget): + changed = pyqtSignal() + def __init__(self, parent=None): QWidget.__init__(self, parent) @@ -479,13 +486,20 @@ class EditRules(QWidget): self.rules_view.setModel(self.model) def add_rule(self): - d = RuleEditor(db.field_metadata) + d = RuleEditor(self.model.fm) d.add_blank_condition() if d.exec_() == d.Accepted: col, r = d.rule if r is not None and col: idx = self.model.add_rule(col, r) self.rules_view.scrollTo(idx) + self.changed.emit() + + def add_advanced(self): + td = TemplateDialog(self, '', None) + if td.exec_() == td.Accepted: + self.changed.emit() + pass # TODO def edit_rule(self, index): try: @@ -493,18 +507,19 @@ class EditRules(QWidget): except: return if isinstance(rule, Rule): - d = RuleEditor(db.field_metadata) + d = RuleEditor(self.model.fm) d.apply_rule(col, rule) if d.exec_() == d.Accepted: col, r = d.rule if r is not None and col: self.model.replace_rule(index, col, r) self.rules_view.scrollTo(index) + self.changed.emit() else: - pass # TODO - - def add_advanced(self): - pass + td = TemplateDialog(self, rule, None) + if td.exec_() == td.Accepted: + self.changed.emit() + pass # TODO def get_selected_row(self, txt): sm = self.rules_view.selectionModel() @@ -519,6 +534,7 @@ class EditRules(QWidget): row = self.get_selected_row(_('removal')) if row is not None: self.model.remove_rule(row) + self.changed.emit() def move_up(self): idx = self.rules_view.currentIndex() @@ -528,6 +544,7 @@ class EditRules(QWidget): sm = self.rules_view.selectionModel() sm.select(idx, sm.ClearAndSelect) self.rules_view.setCurrentIndex(idx) + self.changed.emit() def move_down(self): idx = self.rules_view.currentIndex() @@ -537,6 +554,11 @@ class EditRules(QWidget): sm = self.rules_view.selectionModel() sm.select(idx, sm.ClearAndSelect) self.rules_view.setCurrentIndex(idx) + self.changed.emit() + + def clear(self): + self.model.clear() + self.changed.emit() def commit(self, prefs): self.model.commit(prefs) diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 7a8c1fb69c..feaf3dd677 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -5,21 +5,19 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from functools import partial - from PyQt4.Qt import (QApplication, QFont, QFontInfo, QFontDialog, - QAbstractListModel, Qt, QColor, QIcon, QToolButton, QComboBox) + QAbstractListModel, Qt, QIcon) from calibre.gui2.preferences import ConfigWidgetBase, test_widget, CommaSeparatedList from calibre.gui2.preferences.look_feel_ui import Ui_Form from calibre.gui2 import config, gprefs, qt_app -from calibre.gui2.dialogs.template_line_editor import TemplateLineEditor from calibre.utils.localization import (available_translations, get_language, get_lang) from calibre.utils.config import prefs from calibre.utils.icu import sort_key from calibre.gui2 import NONE from calibre.gui2.book_details import get_field_list +from calibre.gui2.preferences.coloring import EditRules class DisplayedFields(QAbstractListModel): # {{{ @@ -162,117 +160,11 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.df_up_button.clicked.connect(self.move_df_up) self.df_down_button.clicked.connect(self.move_df_down) - self.color_help_text.setText('

' + - _('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.') + - '

' + - _('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. This is by far the easiest ' - 'way to specify a template.') + - '

' + - _('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 ' - '#column, use "{#column}". To show the title in blue if the ' - 'custom column #column contains the value "foo", in red if the ' - 'column contains the value "bar", otherwise in black, use ' - '

{#column:switch(foo,blue,bar,red,black)}
' - 'To show the title in blue if the book has the exact tag ' - '"Science Fiction", red if the book has the exact tag ' - '"Mystery", or black if the book has neither tag, use' - "
program: \n"
-                  "    t = field('tags'); \n"
-                  "    first_non_empty(\n"
-                  "        in_list(t, ',', '^Science Fiction$', 'blue', ''), \n"
-                  "        in_list(t, ',', '^Mystery$', 'red', 'black'))
" - 'To show the title in green if it has one format, blue if it ' - 'two formats, and red if more, use' - "
program:cmp(count(field('formats'),','), 2, 'green', 'blue', 'red')
") + - '

' + - _('You can access a multi-line template editor from the ' - 'context menu (right-click).') + '

' + - _('Note: if you want to color a "custom column with a fixed set ' - '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, '') - self.column_color_count = db.column_color_count+1 - - mi=None - try: - idx = gui.library_view.currentIndex().row() - mi = db.get_metadata(idx, index_is_id=False) - 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) - - 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() + self.edit_rules = EditRules(self.tabWidget) + self.edit_rules.changed.connect(self.changed_signal) + self.tabWidget.addTab(self.edit_rules, + QIcon(I('format-fill-color.png')), _('Column coloring')) + self.tabWidget.setCurrentIndex(0) def initialize(self): ConfigWidgetBase.initialize(self) @@ -283,6 +175,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.current_font = self.initial_font = font self.update_font_display() self.display_model.initialize() + db = self.gui.current_db + self.edit_rules.initialize(db.field_metadata, db.prefs) def restore_defaults(self): ConfigWidgetBase.restore_defaults(self) @@ -292,6 +186,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.changed_signal.emit() self.update_font_display() self.display_model.restore_defaults() + self.edit_rules.clear() self.changed_signal.emit() def build_font_obj(self): @@ -341,12 +236,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.changed_signal.emit() def commit(self, *args): - for i in range(1, self.column_color_count): - col = getattr(self, 'opt_column_color_name_'+str(i)) - 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 @@ -356,10 +245,11 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): QApplication.setFont(self.font_display.font()) rr = True self.display_model.commit() + self.edit_rules.commit(self.gui.current_db.prefs) return rr def refresh_gui(self, gui): - gui.library_view.model().set_color_templates() + gui.library_view.model().reset() self.update_font_display() gui.tags_view.reread_collapse_parameters() gui.library_view.refresh_book_details() diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index def1bdd41c..cc9133a36f 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -6,7 +6,7 @@ 0 0 - 717 + 820 519 @@ -407,161 +407,6 @@ then the tags will be displayed each on their own line. - - - - :/images/format-fill-color.png:/images/format-fill-color.png - - - Column Coloring - - - - - - Column to color - - - - - - - - - Color selection template - - - - 10 - 0 - - - - - - - - The template wizard is easiest to use - - - - - - - Show/hide help text - - - - - - - Show/hide colors - - - - - - - - - Color names - - - - - - - - 16777215 - 300 - - - - true - - - - - 0 - 0 - 687 - 61 - - - - - - - true - - - Qt::AlignLeft|Qt::AlignTop - - - - - - - - - - - - 0 - 200 - - - - true - - - Qt::AlignLeft|Qt::AlignTop - - - - - 0 - 0 - 687 - 194 - - - - - - - true - - - true - - - - - - - - - - - - 0 - 10 - - - - Qt::Vertical - - - - 0 - 0 - - - - - - @@ -572,11 +417,6 @@ then the tags will be displayed each on their own line. QLineEdit
calibre/gui2/complete.h
- - TemplateLineEditor - QLineEdit -
calibre/gui2/dialogs/template_line_editor.h
-
diff --git a/src/calibre/library/coloring.py b/src/calibre/library/coloring.py index 7e2b0f67c6..80e748473a 100644 --- a/src/calibre/library/coloring.py +++ b/src/calibre/library/coloring.py @@ -61,7 +61,7 @@ class Rule(object): # {{{ {sig} test(and( {conditions} - ), {color}, ''); + ), '{color}', ''); ''').format(sig=self.signature, conditions=conditions, color=self.color) @@ -169,10 +169,21 @@ def conditionable_columns(fm): 'comments', 'text', 'enumeration', 'datetime'): yield key - def displayable_columns(fm): for key in fm.displayable_field_keys(): if key not in ('sort', 'author_sort', 'comments', 'formats', 'identifiers', 'path'): yield key +def migrate_old_rule(fm, template): + if template.startswith('program:\n#tag wizard'): + rules = [] + for line in template.splitlines(): + if line.startswith('#') and ':|:' in line: + value, color = line[1:].split(':|:') + r = Rule(fm, color=color) + r.add_condition('tags', 'has', value) + rules.append(r.template) + return rules + return template + diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index df465c919e..b3c584534e 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -211,10 +211,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): defs = self.prefs.defaults defs['gui_restriction'] = defs['cs_restriction'] = '' defs['categories_using_hierarchy'] = [] - self.column_color_count = 5 - for i in range(1,self.column_color_count+1): - defs['column_color_name_'+str(i)] = '' - defs['column_color_template_'+str(i)] = '' + defs['column_color_rules'] = [] # Migrate the bool tristate tweak defs['bools_are_tristate'] = \ @@ -222,6 +219,23 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if self.prefs.get('bools_are_tristate') is None: self.prefs.set('bools_are_tristate', defs['bools_are_tristate']) + # Migrate column coloring rules + if self.prefs.get('column_color_name_1', None) is not None: + from calibre.library.coloring import migrate_old_rule + old_rules = [] + for i in range(1, 5): + col = self.prefs.get('column_color_name_'+str(i), None) + templ = self.prefs.get('column_color_template_'+str(i), None) + if col and templ: + try: + del self.prefs['column_color_name_'+str(i)] + templ = migrate_old_rule(self.field_metadata, templ) + old_rules.append((col, templ)) + except: + pass + if old_rules: + self.prefs['column_color_rules'] += old_rules + # Migrate saved search and user categories to db preference scheme def migrate_preference(key, default): oldval = prefs[key]