diff --git a/resources/images/icon_choose.png b/resources/images/icon_choose.png new file mode 100644 index 0000000000..5a34aca1df Binary files /dev/null and b/resources/images/icon_choose.png differ diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index ba37e2c875..70fa0ccbda 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -24,7 +24,7 @@ from calibre.db.search import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH from calibre.library.caches import (MetadataBackup, force_to_bool) from calibre.library.save_to_disk import find_plugboard from calibre import strftime, isbytestring -from calibre.constants import filesystem_encoding, DEBUG +from calibre.constants import filesystem_encoding, DEBUG, config_dir from calibre.gui2.library import DEFAULT_SORT from calibre.utils.localization import calibre_langcode_to_name from calibre.library.coloring import color_row_key @@ -48,18 +48,20 @@ def default_image(): class ColumnColor(object): - def __init__(self): + def __init__(self, formatter, colors): self.mi = None + self.formatter = formatter + self.colors = colors - def __call__(self, id_, key, fmt, db, formatter, color_cache, colors): + def __call__(self, id_, key, fmt, db, color_cache): if id_ in color_cache and key in color_cache[id_]: self.mi = None return color_cache[id_][key] try: if self.mi is None: self.mi = db.get_metadata(id_, index_is_id=True) - color = formatter.safe_format(fmt, self.mi, '', self.mi) - if color in colors: + color = self.formatter.safe_format(fmt, self.mi, '', self.mi) + if color in self.colors: color = QColor(color) if color.isValid(): color = QVariant(color) @@ -70,6 +72,36 @@ class ColumnColor(object): pass +class ColumnIcon(object): + + def __init__(self, formatter): + self.mi = None + self.formatter = formatter + + def __call__(self, id_, key, fmt, kind, db, icon_cache, icon_bitmap_cache): + dex = key+kind + if id_ in icon_cache and dex in icon_cache[id_]: + self.mi = None + return icon_cache[id_][dex] + try: + if self.mi is None: + self.mi = db.get_metadata(id_, index_is_id=True) + icon = self.formatter.safe_format(fmt, self.mi, '', self.mi) + if icon: + if icon in icon_bitmap_cache: + icon_bitmap = icon_bitmap_cache[icon] + icon_cache[id_][dex] = icon_bitmap + return icon_bitmap + d = os.path.join(config_dir, 'cc_icons', icon) + if (os.path.exists(d)): + icon_bitmap = QIcon(d) + icon_cache[id_][dex] = icon_bitmap + icon_bitmap_cache[icon] = icon_bitmap + self.mi = None + return icon + except: + pass + class BooksModel(QAbstractTableModel): # {{{ about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted') @@ -97,7 +129,13 @@ class BooksModel(QAbstractTableModel): # {{{ def __init__(self, parent=None, buffer=40): QAbstractTableModel.__init__(self, parent) self.db = None - self.column_color = ColumnColor() + + self.formatter = SafeFormat() + self.colors = frozenset([unicode(c) for c in QColor.colorNames()]) + self._clear_caches() + self.column_color = ColumnColor(self.formatter, self.colors) + self.column_icon = ColumnIcon(self.formatter) + self.book_on_device = None self.editable_cols = ['title', 'authors', 'rating', 'publisher', 'tags', 'series', 'timestamp', 'pubdate', @@ -109,8 +147,6 @@ class BooksModel(QAbstractTableModel): # {{{ self.column_map = [] self.headers = {} self.alignment_map = {} - self.color_cache = defaultdict(dict) - self.color_row_fmt_cache = None self.buffer_size = buffer self.metadata_backup = None self.bool_yes_icon = QIcon(I('ok.png')) @@ -121,10 +157,14 @@ class BooksModel(QAbstractTableModel): # {{{ self.ids_to_highlight_set = set() self.current_highlighted_idx = None self.highlight_only = False - self.colors = frozenset([unicode(c) for c in QColor.colorNames()]) - self.formatter = SafeFormat() self.read_config() + def _clear_caches(self): + self.color_cache = defaultdict(dict) + self.icon_cache = defaultdict(dict) + self.icon_bitmap_cache = {} + self.color_row_fmt_cache = None + def change_alignment(self, colname, alignment): if colname in self.column_map and alignment in ('left', 'right', 'center'): old = self.alignment_map.get(colname, 'left') @@ -195,15 +235,13 @@ class BooksModel(QAbstractTableModel): # {{{ def refresh_ids(self, ids, current_row=-1): - self.color_cache = defaultdict(dict) - self.color_row_fmt_cache = None + self._clear_caches() rows = self.db.refresh_ids(ids) if rows: self.refresh_rows(rows, current_row=current_row) def refresh_rows(self, rows, current_row=-1): - self.color_cache = defaultdict(dict) - self.color_row_fmt_cache = None + self._clear_caches() for row in rows: if row == current_row: self.new_bookdisplay_data.emit( @@ -234,8 +272,7 @@ class BooksModel(QAbstractTableModel): # {{{ return ret def count_changed(self, *args): - self.color_cache = defaultdict(dict) - self.color_row_fmt_cache = None + self._clear_caches() self.count_changed_signal.emit(self.db.count()) def row_indices(self, index): @@ -366,8 +403,7 @@ class BooksModel(QAbstractTableModel): # {{{ self.resort(reset=reset) def reset(self): - self.color_cache = defaultdict(dict) - self.color_row_fmt_cache = None + self._clear_caches() QAbstractTableModel.reset(self) def resort(self, reset=True): @@ -750,7 +786,23 @@ class BooksModel(QAbstractTableModel): # {{{ # we will get asked to display columns we don't know about. Must test for this. if col >= len(self.column_to_dc_map): return NONE - if role in (Qt.DisplayRole, Qt.EditRole, Qt.ToolTipRole): + if role == Qt.DisplayRole: + rules = self.db.prefs['column_icon_rules'] + if rules: + key = self.column_map[col] + id_ = None + for kind, k, fmt in rules: + if k == key and kind == 'icon_only': + if id_ is None: + id_ = self.id(index) + self.column_icon.mi = None + ccicon = self.column_icon(id_, key, fmt, 'icon_only', self.db, + self.icon_cache, self.icon_bitmap_cache) + if ccicon is not None: + return NONE + self.icon_cache[id_][key+'icon_only'] = None + return self.column_to_dc_map[col](index.row()) + elif role in (Qt.EditRole, Qt.ToolTipRole): return self.column_to_dc_map[col](index.row()) elif role == Qt.BackgroundRole: if self.id(index) in self.ids_to_highlight_set: @@ -767,7 +819,7 @@ class BooksModel(QAbstractTableModel): # {{{ for k, fmt in self.db.prefs['column_color_rules']: if k == key: ccol = self.column_color(id_, key, fmt, self.db, - self.formatter, self.color_cache, self.colors) + self.color_cache) if ccol is not None: return ccol @@ -788,7 +840,7 @@ class BooksModel(QAbstractTableModel): # {{{ for fmt in self.color_row_fmt_cache: ccol = self.column_color(id_, color_row_key, fmt, self.db, - self.formatter, self.color_cache, self.colors) + self.color_cache) if ccol is not None: return ccol @@ -796,7 +848,30 @@ class BooksModel(QAbstractTableModel): # {{{ return NONE elif role == Qt.DecorationRole: if self.column_to_dc_decorator_map[col] is not None: - return self.column_to_dc_decorator_map[index.column()](index.row()) + ccicon = self.column_to_dc_decorator_map[index.column()](index.row()) + if ccicon != NONE: + return ccicon + + rules = self.db.prefs['column_icon_rules'] + if rules: + key = self.column_map[col] + id_ = None + need_icon_with_text = False + for kind, k, fmt in rules: + if k == key and kind in ('icon', 'icon_only'): + if id_ is None: + id_ = self.id(index) + self.column_icon.mi = None + if kind == 'icon': + need_icon_with_text = True + ccicon = self.column_icon(id_, key, fmt, kind, self.db, + self.icon_cache, self.icon_bitmap_cache) + if ccicon is not None: + return ccicon + if need_icon_with_text: + self.icon_cache[id_][key+'icon'] = self.bool_blank_icon + return self.bool_blank_icon + self.icon_cache[id_][key+'icon'] = None elif role == Qt.TextAlignmentRole: cname = self.column_map[index.column()] ans = Qt.AlignVCenter | ALIGNMENT_MAP[self.alignment_map.get(cname, diff --git a/src/calibre/gui2/preferences/coloring.py b/src/calibre/gui2/preferences/coloring.py index 72fe4fb12b..ad9a7a9af2 100644 --- a/src/calibre/gui2/preferences/coloring.py +++ b/src/calibre/gui2/preferences/coloring.py @@ -7,15 +7,18 @@ __license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' +import os + from PyQt4.Qt import (QWidget, QDialog, QLabel, QGridLayout, QComboBox, QSize, QLineEdit, QIntValidator, QDoubleValidator, QFrame, QColor, Qt, QIcon, QScrollArea, QPushButton, QVBoxLayout, QDialogButtonBox, QToolButton, QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem, QApplication) -from calibre import prepare_string_for_xml +from calibre import prepare_string_for_xml, sanitize_file_name_unicode +from calibre.constants import config_dir from calibre.utils.icu import sort_key -from calibre.gui2 import error_dialog +from calibre.gui2 import error_dialog, choose_files, pixmap_to_data from calibre.gui2.dialogs.template_dialog import TemplateDialog from calibre.gui2.metadata.single_download import RichTextDelegate from calibre.library.coloring import (Rule, conditionable_columns, @@ -25,6 +28,9 @@ from calibre.utils.icu import lower all_columns_string = _('All Columns') +icon_rule_kinds = [(_('icon with text'), 'icon'), + (_('icon with no text'), 'icon_only') ] + class ConditionEditor(QWidget): # {{{ ACTION_MAP = { @@ -207,8 +213,6 @@ class ConditionEditor(QWidget): # {{{ col = self.current_col if not col: return - m = self.fm[col] - dt = m['datatype'] action = self.current_action if not action: return @@ -245,64 +249,89 @@ class ConditionEditor(QWidget): # {{{ class RuleEditor(QDialog): # {{{ - def __init__(self, fm, parent=None): + def __init__(self, fm, pref_name, parent=None): QDialog.__init__(self, parent) self.fm = fm + if pref_name == 'column_color_rules': + self.rule_kind = 'color' + rule_text = _('coloring') + else: + self.rule_kind = 'icon' + rule_text = _('icon') + self.setWindowIcon(QIcon(I('format-fill-color.png'))) - self.setWindowTitle(_('Create/edit a column coloring rule')) + self.setWindowTitle(_('Create/edit a column {0} rule').format(rule_text)) self.l = l = QGridLayout(self) self.setLayout(l) - self.l1 = l1 = QLabel(_('Create a coloring rule by' - ' filling in the boxes below')) - l.addWidget(l1, 0, 0, 1, 5) + self.l1 = l1 = QLabel(_('Create a column {0} rule by' + ' filling in the boxes below'.format(rule_text))) + l.addWidget(l1, 0, 0, 1, 8) self.f1 = QFrame(self) self.f1.setFrameShape(QFrame.HLine) - l.addWidget(self.f1, 1, 0, 1, 5) + l.addWidget(self.f1, 1, 0, 1, 8) - self.l2 = l2 = QLabel(_('Set the color of the column:')) + self.l2 = l2 = QLabel(_('Set the')) l.addWidget(l2, 2, 0) - self.column_box = QComboBox(self) - l.addWidget(self.column_box, 2, 1) + if self.rule_kind == 'color': + l.addWidget(QLabel(_('color'))) + else: + self.kind_box = QComboBox(self) + for tt, t in icon_rule_kinds: + self.kind_box.addItem(tt, t) + l.addWidget(self.kind_box, 2, 1) - self.l3 = l3 = QLabel(_('to')) + self.l3 = l3 = QLabel(_('of the column:')) l.addWidget(l3, 2, 2) - self.color_box = QComboBox(self) - self.color_label = QLabel('Sample text Sample text') - self.color_label.setTextFormat(Qt.RichText) - l.addWidget(self.color_box, 2, 3) - l.addWidget(self.color_label, 2, 4) - l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 5) + self.column_box = QComboBox(self) + l.addWidget(self.column_box, 2, 3) - self.l4 = l4 = QLabel( + self.l4 = l4 = QLabel(_('to')) + l.addWidget(l4, 2, 4) + + if self.rule_kind == 'color': + self.color_box = QComboBox(self) + self.color_label = QLabel('Sample text Sample text') + self.color_label.setTextFormat(Qt.RichText) + l.addWidget(self.color_box, 2, 5) + l.addWidget(self.color_label, 2, 6) + else: + self.filename_box = QLabel() + l.addWidget(self.filename_box, 2, 5) + self.filename_button = QPushButton(_('Choose icon')) + l.addWidget(self.filename_button, 2, 6) + + l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 7) + + self.l5 = l5 = QLabel( _('Only if the following conditions are all satisfied:')) - l.addWidget(l4, 3, 0, 1, 6) + l.addWidget(l5, 3, 0, 1, 7) self.scroll_area = sa = QScrollArea(self) sa.setMinimumHeight(300) sa.setMinimumWidth(950) sa.setWidgetResizable(True) - l.addWidget(sa, 4, 0, 1, 6) + l.addWidget(sa, 4, 0, 1, 8) self.add_button = b = QPushButton(QIcon(I('plus.png')), _('Add another condition')) - l.addWidget(b, 5, 0, 1, 6) + l.addWidget(b, 5, 0, 1, 8) b.clicked.connect(self.add_blank_condition) - self.l5 = l5 = QLabel(_('You can disable a condition by' + self.l6 = l6 = QLabel(_('You can disable a condition by' ' blanking all of its boxes')) - l.addWidget(l5, 6, 0, 1, 6) + l.addWidget(l6, 6, 0, 1, 8) self.bb = bb = QDialogButtonBox( QDialogButtonBox.Ok|QDialogButtonBox.Cancel) bb.accepted.connect(self.accept) bb.rejected.connect(self.reject) - l.addWidget(bb, 7, 0, 1, 6) + l.addWidget(bb, 7, 0, 1, 8) self.conditions_widget = QWidget(self) sa.setWidget(self.conditions_widget) @@ -310,24 +339,32 @@ class RuleEditor(QDialog): # {{{ self.conditions_widget.layout().setAlignment(Qt.AlignTop) self.conditions = [] - for b in (self.column_box, self.color_box): - b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon) - b.setMinimumContentsLength(15) + if self.rule_kind == 'color': + for b in (self.column_box, self.color_box): + b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon) + b.setMinimumContentsLength(15) for key in sorted(displayable_columns(fm), key=lambda(k): sort_key(fm[k]['name']) if k != color_row_key else 0): + if key == color_row_key and self.rule_kind != 'color': + continue name = all_columns_string if key == color_row_key else fm[key]['name'] if name: self.column_box.addItem(name, key) self.column_box.setCurrentIndex(0) - self.color_box.addItems(QColor.colorNames()) - self.color_box.setCurrentIndex(0) + if self.rule_kind == 'color': + self.color_box.addItems(QColor.colorNames()) + self.color_box.setCurrentIndex(0) + self.update_color_label() + self.color_box.currentIndexChanged.connect(self.update_color_label) + else: + self.filename_button.clicked.connect(self.filename_button_clicked) - self.update_color_label() - self.color_box.currentIndexChanged.connect(self.update_color_label) self.resize(self.sizeHint()) + self.icon_path = None + def update_color_label(self): pal = QApplication.palette() bg1 = unicode(pal.color(pal.Base).name()) @@ -338,22 +375,54 @@ class RuleEditor(QDialog): # {{{  {st}  '''.format(c=c, bg1=bg1, bg2=bg2, st=_('Sample Text'))) + def filename_button_clicked(self): + try: + path = choose_files(self, 'choose_category_icon', + _('Select Icon'), filters=[ + ('Images', ['png', 'gif', 'jpg', 'jpeg'])], + all_files=False, select_only_single_file=True) + if path: + self.icon_path = path[0] + self.filename_box.setText( + sanitize_file_name_unicode( + os.path.splitext( + os.path.basename(self.icon_path))[0]+'.png')) + self.filename_box.adjustSize() + else: + self.icon_path = '' + except: + import traceback + traceback.print_exc() + return def add_blank_condition(self): c = ConditionEditor(self.fm, parent=self.conditions_widget) self.conditions.append(c) self.conditions_widget.layout().addWidget(c) - def apply_rule(self, col, rule): + def apply_rule(self, kind, col, rule): + if kind == 'color': + if rule.color: + idx = self.color_box.findText(rule.color) + if idx >= 0: + self.color_box.setCurrentIndex(idx) + else: + self.kind_box.setCurrentIndex(0 if kind == 'icon' else 1) + self.filename_box.setText(rule.color) + for i in range(self.column_box.count()): c = unicode(self.column_box.itemData(i).toString()) if col == c: self.column_box.setCurrentIndex(i) break if rule.color: - idx = self.color_box.findText(rule.color) - if idx >= 0: - self.color_box.setCurrentIndex(idx) + if kind == 'color': + idx = self.color_box.findText(rule.color) + if idx >= 0: + self.color_box.setCurrentIndex(idx) + else: + self.filename_box.setText(rule.color) + for c in rule.conditions: ce = ConditionEditor(self.fm, parent=self.conditions_widget) self.conditions.append(ce) @@ -366,6 +435,20 @@ class RuleEditor(QDialog): # {{{ def accept(self): + if self.rule_kind != 'color': + path = self.icon_path + if path: + try: + fname = unicode(self.filename_box.text()) + p = QIcon(path).pixmap(QSize(128, 128)) + d = os.path.join(config_dir, 'cc_icons') + if not os.path.exists(d): + os.makedirs(d) + with open(os.path.join(d, fname), 'wb') as f: + f.write(pixmap_to_data(p, format='PNG')) + except: + import traceback + traceback.print_exc() if self.validate(): QDialog.accept(self) @@ -393,32 +476,54 @@ class RuleEditor(QDialog): # {{{ @property def rule(self): r = Rule(self.fm) - r.color = unicode(self.color_box.currentText()) + if self.rule_kind != 'color': + r.color = unicode(self.filename_box.text()) + else: + r.color = unicode(self.color_box.currentText()) idx = self.column_box.currentIndex() col = unicode(self.column_box.itemData(idx).toString()) for c in self.conditions: condition = c.condition if condition is not None: r.add_condition(*condition) + if self.rule_kind == 'icon': + kind = unicode(self.kind_box.itemData( + self.kind_box.currentIndex()).toString()) + else: + kind = 'color' - return col, r + return kind, col, r # }}} class RulesModel(QAbstractListModel): # {{{ - def __init__(self, prefs, fm, parent=None): + def __init__(self, prefs, fm, pref_name, parent=None): QAbstractListModel.__init__(self, parent) self.fm = fm - rules = list(prefs['column_color_rules']) - self.rules = [] - for col, template in rules: - if col not in self.fm: continue - try: - rule = rule_from_template(self.fm, template) - except: - rule = template - self.rules.append((col, rule)) + self.pref_name = pref_name + if pref_name == 'column_color_rules': + self.rule_kind = 'color' + rules = list(prefs[pref_name]) + self.rules = [] + for col, template in rules: + if col not in self.fm and col != color_row_key: continue + try: + rule = rule_from_template(self.fm, template) + except: + rule = template + self.rules.append(('color', col, rule)) + else: + self.rule_kind = 'icon' + rules = list(prefs[pref_name]) + self.rules = [] + for kind, col, template in rules: + if col not in self.fm and col != color_row_key: continue + try: + rule = rule_from_template(self.fm, template) + except: + rule = template + self.rules.append((kind, col, rule)) def rowCount(self, *args): return len(self.rules) @@ -426,7 +531,7 @@ class RulesModel(QAbstractListModel): # {{{ def data(self, index, role): row = index.row() try: - col, rule = self.rules[row] + kind, col, rule = self.rules[row] except: return None if role == Qt.DisplayRole: @@ -434,17 +539,17 @@ class RulesModel(QAbstractListModel): # {{{ col = all_columns_string else: col = self.fm[col]['name'] - return self.rule_to_html(col, rule) + return self.rule_to_html(kind, col, rule) if role == Qt.UserRole: - return (col, rule) + return (kind, col, rule) - def add_rule(self, col, rule): - self.rules.append((col, rule)) + def add_rule(self, kind, col, rule): + self.rules.append((kind, col, rule)) self.reset() return self.index(len(self.rules)-1) - def replace_rule(self, index, col, r): - self.rules[index.row()] = (col, r) + def replace_rule(self, index, kind, col, r): + self.rules[index.row()] = (kind, col, r) self.dataChanged.emit(index, index) def remove_rule(self, index): @@ -453,12 +558,15 @@ class RulesModel(QAbstractListModel): # {{{ def commit(self, prefs): rules = [] - for col, r in self.rules: + for kind, col, r in self.rules: if isinstance(r, Rule): r = r.template if r is not None: - rules.append((col, r)) - prefs['column_color_rules'] = rules + if kind == 'color': + rules.append((col, r)) + else: + rules.append((kind, col, r)) + prefs[self.pref_name] = rules def move(self, idx, delta): row = idx.row() + delta @@ -475,18 +583,28 @@ class RulesModel(QAbstractListModel): # {{{ self.rules = [] self.reset() - def rule_to_html(self, col, rule): + def rule_to_html(self, kind, col, rule): if not isinstance(rule, Rule): return _('''

Advanced Rule for column %(col)s:

%(rule)s
''')%dict(col=col, rule=prepare_string_for_xml(rule)) conditions = [self.condition_to_html(c) for c in rule.conditions] + + trans_kind = 'not found' + if kind == 'color': + trans_kind = _('color') + else: + for tt, t in icon_rule_kinds: + if kind == t: + trans_kind = tt + break + return _('''\ -

Set the color of %(col)s to %(color)s if the following +

Set the %(kind)s of %(col)s to %(color)s if the following conditions are met:

- ''') % dict(col=col, color=rule.color, rule=''.join(conditions)) + ''') % dict(kind=trans_kind, col=col, color=rule.color, rule=''.join(conditions)) def condition_to_html(self, condition): c, a, v = condition @@ -513,12 +631,7 @@ class EditRules(QWidget): # {{{ self.l = l = QGridLayout(self) self.setLayout(l) - self.l1 = l1 = QLabel('

'+_( - 'You can control the color of columns in the' - ' book list by creating "rules" that tell calibre' - ' what color to use. Click the Add Rule button below' - ' to get started.

You can change an existing rule by double' - ' clicking it.')) + self.l1 = l1 = QLabel('') l1.setWordWrap(True) l.addWidget(l1, 0, 0, 1, 2) @@ -559,22 +672,38 @@ class EditRules(QWidget): # {{{ b.clicked.connect(self.add_advanced) l.addWidget(b, 3, 0, 1, 2) - def initialize(self, fm, prefs, mi): - self.model = RulesModel(prefs, fm) + def initialize(self, fm, prefs, mi, pref_name): + self.pref_name = pref_name + self.model = RulesModel(prefs, fm, self.pref_name) self.rules_view.setModel(self.model) self.fm = fm self.mi = mi + if pref_name == 'column_color_rules': + self.l1.setText('

'+_( + 'You can control the color of columns in the' + ' book list by creating "rules" that tell calibre' + ' what color to use. Click the Add Rule button below' + ' to get started.

You can change an existing rule by' + ' double clicking it.')) + else: + self.l1.setText('

'+_( + 'You can add icons to columns in the' + ' book list by creating "rules" that tell calibre' + ' what icon to use. Click the Add Rule button below' + ' to get started.

You can change an existing rule by' + ' double clicking it.')) + self.add_advanced_button.setVisible(False) def _add_rule(self, dlg): if dlg.exec_() == dlg.Accepted: - col, r = dlg.rule - if r and col: - idx = self.model.add_rule(col, r) + kind, col, r = dlg.rule + if kind and r and col: + idx = self.model.add_rule(kind, col, r) self.rules_view.scrollTo(idx) self.changed.emit() def add_rule(self): - d = RuleEditor(self.model.fm) + d = RuleEditor(self.model.fm, self.pref_name) d.add_blank_condition() self._add_rule(d) @@ -584,18 +713,18 @@ class EditRules(QWidget): # {{{ def edit_rule(self, index): try: - col, rule = self.model.data(index, Qt.UserRole) + kind, col, rule = self.model.data(index, Qt.UserRole) except: return if isinstance(rule, Rule): - d = RuleEditor(self.model.fm) - d.apply_rule(col, rule) + d = RuleEditor(self.model.fm, self.pref_name) + d.apply_rule(kind, col, rule) else: d = TemplateDialog(self, rule, mi=self.mi, fm=self.fm, color_field=col) if d.exec_() == d.Accepted: - col, r = d.rule - if r is not None and col: - self.model.replace_rule(index, col, r) + kind, col, r = d.rule + if kind and r is not None and col: + self.model.replace_rule(index, kind, col, r) self.rules_view.scrollTo(index) self.changed.emit() @@ -651,7 +780,7 @@ if __name__ == '__main__': db = db() if True: - d = RuleEditor(db.field_metadata) + d = RuleEditor(db.field_metadata, 'column_color_rules') d.add_blank_condition() d.exec_() diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 754ac6c6ce..b3423e5a8c 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -181,6 +181,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.edit_rules.changed.connect(self.changed_signal) self.tabWidget.addTab(self.edit_rules, QIcon(I('format-fill-color.png')), _('Column coloring')) + + self.icon_rules = EditRules(self.tabWidget) + self.icon_rules.changed.connect(self.changed_signal) + self.tabWidget.addTab(self.icon_rules, + QIcon(I('icon_choose.png')), _('Column icons')) + self.tabWidget.setCurrentIndex(0) keys = [QKeySequence('F11', QKeySequence.PortableText), QKeySequence( 'Ctrl+Shift+F', QKeySequence.PortableText)] @@ -203,7 +209,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): mi = db.get_metadata(idx, index_is_id=False) except: mi=None - self.edit_rules.initialize(db.field_metadata, db.prefs, mi) + self.edit_rules.initialize(db.field_metadata, db.prefs, mi, 'column_color_rules') + self.icon_rules.initialize(db.field_metadata, db.prefs, mi, 'column_icon_rules') def restore_defaults(self): ConfigWidgetBase.restore_defaults(self) @@ -214,6 +221,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.update_font_display() self.display_model.restore_defaults() self.edit_rules.clear() + self.icon_rules.clear() self.changed_signal.emit() def build_font_obj(self): @@ -273,6 +281,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): rr = True self.display_model.commit() self.edit_rules.commit(self.gui.current_db.prefs) + self.icon_rules.commit(self.gui.current_db.prefs) return rr def refresh_gui(self, gui): diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index a1202db44d..754a583f74 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -211,6 +211,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): defs['gui_restriction'] = defs['cs_restriction'] = '' defs['categories_using_hierarchy'] = [] defs['column_color_rules'] = [] + defs['column_icon_rules'] = [] defs['grouped_search_make_user_categories'] = [] defs['similar_authors_search_key'] = 'authors' defs['similar_authors_match_kind'] = 'match_any'