Make icon rules composable.

This commit is contained in:
Charles Haley 2013-09-17 14:29:08 +02:00
parent 9c3e61cee2
commit 2fe01db004
4 changed files with 91 additions and 76 deletions

View File

@ -7,7 +7,7 @@ import json, os, traceback
from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QSyntaxHighlighter, QFont, from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QSyntaxHighlighter, QFont,
QRegExp, QApplication, QTextCharFormat, QColor, QCursor, QRegExp, QApplication, QTextCharFormat, QColor, QCursor,
QIcon, QSize) QIcon, QSize, QVariant)
from calibre import sanitize_file_name_unicode from calibre import sanitize_file_name_unicode
from calibre.constants import config_dir from calibre.constants import config_dir
@ -19,7 +19,6 @@ from calibre.ebooks.metadata.book.formatter import SafeFormat
from calibre.library.coloring import (displayable_columns, color_row_key) from calibre.library.coloring import (displayable_columns, color_row_key)
from calibre.gui2 import error_dialog, choose_files, pixmap_to_data from calibre.gui2 import error_dialog, choose_files, pixmap_to_data
class ParenPosition: class ParenPosition:
def __init__(self, block, pos, paren): def __init__(self, block, pos, paren):
@ -247,9 +246,15 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
self.icon_file_names.append(icon_file) self.icon_file_names.append(icon_file)
self.icon_file_names.sort(key=sort_key) self.icon_file_names.sort(key=sort_key)
self.update_filename_box() self.update_filename_box()
self.icon_with_text.setChecked(True)
if icon_rule_kind == 'icon_only': dex = 0
self.icon_without_text.setChecked(True) from calibre.gui2.preferences.coloring import icon_rule_kinds
for i,tup in enumerate(icon_rule_kinds):
txt,val = tup
self.icon_kind.addItem(txt, userData=QVariant(val))
if val == icon_rule_kind:
dex = i
self.icon_kind.setCurrentIndex(dex)
self.icon_field.setCurrentIndex(self.icon_field.findData(icon_field_key)) self.icon_field.setCurrentIndex(self.icon_field.findData(icon_field_key))
if mi: if mi:
@ -410,7 +415,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
self.rule = (unicode(self.colored_field.itemData( self.rule = (unicode(self.colored_field.itemData(
self.colored_field.currentIndex()).toString()), txt) self.colored_field.currentIndex()).toString()), txt)
elif self.iconing: elif self.iconing:
rt = 'icon' if self.icon_with_text.isChecked() else 'icon_only' rt = unicode(self.icon_kind.itemData(self.icon_kind.currentIndex()).toString())
self.rule = (rt, self.rule = (rt,
unicode(self.icon_field.itemData( unicode(self.icon_field.itemData(
self.icon_field.currentIndex()).toString()), self.icon_field.currentIndex()).toString()),

View File

@ -69,33 +69,19 @@
<widget class="QWidget" name="icon_layout"> <widget class="QWidget" name="icon_layout">
<layout class="QGridLayout"> <layout class="QGridLayout">
<item row="0" column="0" colspan="2"> <item row="0" column="0" colspan="2">
<widget class="QGroupBox"> <layout class="QHBoxLayout">
<property name="title"> <item>
<string>Kind</string> <widget class="QLabel">
</property> <property name="text">
<layout class="QHBoxLayout"> <string>Kind:</string>
<item> </property>
<widget class="QRadioButton" name="icon_without_text"> </widget>
<property name="text"> </item>
<string>icon with no text</string> <item>
</property> <widget class="QComboBox" name="icon_kind">
</widget> </widget>
</item> </item>
<item> </layout>
<widget class="QRadioButton" name="icon_with_text">
<property name="text">
<string>icon with text</string>
</property>
</widget>
</item>
</layout>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>100</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="icon_chooser_label"> <widget class="QLabel" name="icon_chooser_label">

View File

@ -81,38 +81,49 @@ class ColumnIcon(object): # {{{
self.formatter = formatter self.formatter = formatter
self.model = model self.model = model
def __call__(self, id_, key, fmt, kind, db, icon_cache, icon_bitmap_cache): def __call__(self, id_, key, fmts, cache_index, db, icon_cache, icon_bitmap_cache):
dex = key+kind if id_ in icon_cache and cache_index in icon_cache[id_]:
if id_ in icon_cache and dex in icon_cache[id_]:
self.mi = None self.mi = None
return icon_cache[id_][dex] return icon_cache[id_][cache_index]
try: try:
if self.mi is None: if self.mi is None:
self.mi = db.get_metadata(id_, index_is_id=True) self.mi = db.get_metadata(id_, index_is_id=True)
icons = self.formatter.safe_format(fmt, self.mi, '', self.mi) icons = []
for kind,fmt in fmts:
rule_icons = self.formatter.safe_format(fmt, self.mi, '', self.mi)
if not rule_icons:
continue
icon_list = [ic.strip() for ic in rule_icons.split(':')]
if icon_list and not kind.endswith('_composed'):
icons = icon_list
break
else:
icons.extend(icon_list)
if icons: if icons:
if icons in icon_bitmap_cache: icon_string = ':'.join(icons)
icon_bitmap = icon_bitmap_cache[icons] if icon_string in icon_bitmap_cache:
icon_cache[id_][dex] = icon_bitmap icon_bitmap = icon_bitmap_cache[icon_string]
icon_cache[id_][cache_index] = icon_bitmap
return icon_bitmap return icon_bitmap
icon_list = [ic.strip() for ic in icons.split(':')]
icon_bitmaps = [] icon_bitmaps = []
total_width = 0 total_width = 0
for icon in icon_list: for icon in icons:
d = os.path.join(config_dir, 'cc_icons', icon) d = os.path.join(config_dir, 'cc_icons', icon)
if (os.path.exists(d)): if (os.path.exists(d)):
bm = QPixmap(d) bm = QPixmap(d)
icon_bitmaps.append(bm) icon_bitmaps.append(bm)
total_width += bm.width() total_width += bm.width()
if len(icon_bitmaps) > 1: if len(icon_bitmaps) > 1:
result = QPixmap(len(icon_list)*128, 128) i = len(icon_bitmaps)
result = QPixmap((i * 128) + ((i-1)*2), 128)
result.fill(Qt.transparent) result.fill(Qt.transparent)
painter = QPainter(result) painter = QPainter(result)
x = 0 x = 0
for bm in icon_bitmaps: for bm in icon_bitmaps:
painter.drawPixmap(x, 0, bm) painter.drawPixmap(x, 0, bm)
x += bm.width() x += bm.width() + 2
painter.end() painter.end()
else: else:
result = icon_bitmaps[0] result = icon_bitmaps[0]
@ -123,8 +134,8 @@ class ColumnIcon(object): # {{{
rh = max(2, self.model.row_height - 2) rh = max(2, self.model.row_height - 2)
if result.height() > rh: if result.height() > rh:
result = result.scaledToHeight(rh, mode=Qt.SmoothTransformation) result = result.scaledToHeight(rh, mode=Qt.SmoothTransformation)
icon_cache[id_][dex] = result icon_cache[id_][cache_index] = result
icon_bitmap_cache[icons] = result icon_bitmap_cache[icon_string] = result
self.mi = None self.mi = None
return result return result
except: except:
@ -788,16 +799,21 @@ class BooksModel(QAbstractTableModel): # {{{
if rules: if rules:
key = self.column_map[col] key = self.column_map[col]
id_ = None id_ = None
fmts = []
for kind, k, fmt in rules: for kind, k, fmt in rules:
if k == key and kind == 'icon_only': if k == key and kind in ('icon_only', 'icon_only_composed'):
if id_ is None: if id_ is None:
id_ = self.id(index) id_ = self.id(index)
self.column_icon.mi = None self.column_icon.mi = None
ccicon = self.column_icon(id_, key, fmt, 'icon_only', self.db, fmts.append((kind, fmt))
self.icon_cache, self.icon_bitmap_cache)
if ccicon is not None: if fmts:
return NONE cache_index = key + ':'.join([kind for kind,fmt in fmts])
self.icon_cache[id_][key+'icon_only'] = None ccicon = self.column_icon(id_, key, fmts, cache_index, self.db,
self.icon_cache, self.icon_bitmap_cache)
if ccicon is not None:
return NONE
self.icon_cache[id_][cache_index] = None
return self.column_to_dc_map[col](index.row()) return self.column_to_dc_map[col](index.row())
elif role in (Qt.EditRole, Qt.ToolTipRole): elif role in (Qt.EditRole, Qt.ToolTipRole):
return self.column_to_dc_map[col](index.row()) return self.column_to_dc_map[col](index.row())
@ -854,21 +870,25 @@ class BooksModel(QAbstractTableModel): # {{{
key = self.column_map[col] key = self.column_map[col]
id_ = None id_ = None
need_icon_with_text = False need_icon_with_text = False
fmts = []
for kind, k, fmt in rules: for kind, k, fmt in rules:
if k == key and kind in ('icon', 'icon_only'): if k == key and kind.startswith('icon'):
if id_ is None: if id_ is None:
id_ = self.id(index) id_ = self.id(index)
self.column_icon.mi = None self.column_icon.mi = None
if kind == 'icon': fmts.append((kind, fmt))
if kind in ('icon', 'icon_composed'):
need_icon_with_text = True need_icon_with_text = True
ccicon = self.column_icon(id_, key, fmt, kind, self.db, if fmts:
self.icon_cache, self.icon_bitmap_cache) cache_index = key + ':'.join([kind for kind,fmt in fmts])
if ccicon is not None: ccicon = self.column_icon(id_, key, fmts, cache_index, self.db,
return ccicon self.icon_cache, self.icon_bitmap_cache)
if need_icon_with_text: if ccicon is not None:
self.icon_cache[id_][key+'icon'] = self.bool_blank_icon return ccicon
return self.bool_blank_icon if need_icon_with_text:
self.icon_cache[id_][key+'icon'] = None self.icon_cache[id_][cache_index] = self.bool_blank_icon
return self.bool_blank_icon
self.icon_cache[id_][cache_index] = None
elif role == Qt.TextAlignmentRole: elif role == Qt.TextAlignmentRole:
cname = self.column_map[index.column()] cname = self.column_map[index.column()]
ans = Qt.AlignVCenter | ALIGNMENT_MAP[self.alignment_map.get(cname, ans = Qt.AlignVCenter | ALIGNMENT_MAP[self.alignment_map.get(cname,

View File

@ -29,7 +29,9 @@ from calibre.utils.icu import lower
all_columns_string = _('All Columns') all_columns_string = _('All Columns')
icon_rule_kinds = [(_('icon with text'), 'icon'), icon_rule_kinds = [(_('icon with text'), 'icon'),
(_('icon with no text'), 'icon_only') ] (_('icon with no text'), 'icon_only'),
(_('composed icons w/text'), 'icon_composed'),
(_('composed icons w/no text'), 'icon_only_composed'),]
class ConditionEditor(QWidget): # {{{ class ConditionEditor(QWidget): # {{{
@ -526,7 +528,10 @@ class RuleEditor(QDialog): # {{{
if idx >= 0: if idx >= 0:
self.color_box.setCurrentIndex(idx) self.color_box.setCurrentIndex(idx)
else: else:
self.kind_box.setCurrentIndex(0 if kind == 'icon' else 1) for i,tup in enumerate(icon_rule_kinds):
if kind == tup[1]:
self.kind_box.setCurrentIndex(i)
break
self.rule_icon_files = [ic.strip() for ic in rule.color.split(':')] self.rule_icon_files = [ic.strip() for ic in rule.color.split(':')]
if len(self.rule_icon_files) > 1: if len(self.rule_icon_files) > 1:
self.multiple_icon_cb.setChecked(True) self.multiple_icon_cb.setChecked(True)
@ -691,6 +696,15 @@ class RulesModel(QAbstractListModel): # {{{
self.reset() self.reset()
def rule_to_html(self, kind, col, rule): def rule_to_html(self, kind, col, rule):
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
if not isinstance(rule, Rule): if not isinstance(rule, Rule):
if kind == 'color': if kind == 'color':
return _(''' return _('''
@ -702,21 +716,11 @@ class RulesModel(QAbstractListModel): # {{{
<p>Advanced Rule: set <b>%(typ)s</b> for column <b>%(col)s</b>: <p>Advanced Rule: set <b>%(typ)s</b> for column <b>%(col)s</b>:
<pre>%(rule)s</pre> <pre>%(rule)s</pre>
''')%dict(col=col, ''')%dict(col=col,
typ=icon_rule_kinds[0][0] typ=trans_kind,
if kind == icon_rule_kinds[0][1] else icon_rule_kinds[1][0],
rule=prepare_string_for_xml(rule)) rule=prepare_string_for_xml(rule))
conditions = [self.condition_to_html(c) for c in rule.conditions] 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 _('''\ return _('''\
<p>Set the <b>%(kind)s</b> of <b>%(col)s</b> to <b>%(color)s</b> if the following <p>Set the <b>%(kind)s</b> of <b>%(col)s</b> to <b>%(color)s</b> if the following
conditions are met:</p> conditions are met:</p>