From 61dac94abe24497d5f4260afb98437e77226ac89 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 14 May 2013 15:12:21 +0200 Subject: [PATCH] First pass at advanced icon selection rules --- src/calibre/gui2/dialogs/template_dialog.py | 117 ++++++++++++-- src/calibre/gui2/dialogs/template_dialog.ui | 171 +++++++++++++++----- src/calibre/gui2/preferences/coloring.py | 50 ++++-- src/calibre/library/server/browse.py | 1 + 4 files changed, 271 insertions(+), 68 deletions(-) diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index b60449512b..a7331f8c91 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -3,17 +3,21 @@ __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' __license__ = 'GPL v3' -import json +import json, os, traceback from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QSyntaxHighlighter, QFont, - QRegExp, QApplication, QTextCharFormat, QColor, QCursor) + QRegExp, QApplication, QTextCharFormat, QColor, QCursor, + QIcon, QSize) -from calibre.gui2 import error_dialog +from calibre import sanitize_file_name_unicode +from calibre.constants import config_dir from calibre.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog from calibre.utils.formatter_functions import formatter_functions +from calibre.utils.icu import sort_key from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.book.formatter import SafeFormat -from calibre.library.coloring import (displayable_columns) +from calibre.library.coloring import (displayable_columns, color_row_key) +from calibre.gui2 import error_dialog, choose_files, pixmap_to_data class ParenPosition: @@ -198,25 +202,55 @@ class TemplateHighlighter(QSyntaxHighlighter): class TemplateDialog(QDialog, Ui_TemplateDialog): - def __init__(self, parent, text, mi=None, fm=None, color_field=None): + def __init__(self, parent, text, mi=None, fm=None, color_field=None, + icon_file=None, icon_rule_kind=None): QDialog.__init__(self, parent) Ui_TemplateDialog.__init__(self) self.setupUi(self) self.coloring = color_field is not None + self.iconing = icon_file is not None + + cols = [] + if fm is not None: + 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 not self.coloring: + continue + from calibre.gui2.preferences.coloring import all_columns_string + name = all_columns_string if key == color_row_key else fm[key]['name'] + if name: + cols.append((name, key)) + + self.color_layout.setVisible(False) + self.icon_layout.setVisible(False) + if self.coloring: - cols = sorted([k for k in displayable_columns(fm)]) - self.colored_field.addItems(cols) + self.color_layout.setVisible(True) + for n1, k1 in cols: + self.colored_field.addItem(n1, k1) self.colored_field.setCurrentIndex(self.colored_field.findText(color_field)) colors = QColor.colorNames() colors.sort() self.color_name.addItems(colors) - else: - self.colored_field.setVisible(False) - self.colored_field_label.setVisible(False) - self.color_chooser_label.setVisible(False) - self.color_name.setVisible(False) - self.color_copy_button.setVisible(False) + elif self.iconing: + self.icon_layout.setVisible(True) + for n1, k1 in cols: + self.icon_field.addItem(n1, k1) + self.icon_file_names = [] + d = os.path.join(config_dir, 'cc_icons') + if os.path.exists(d): + for icon_file in os.listdir(d): + icon_file = icu_lower(icon_file) + if os.path.exists(os.path.join(d, icon_file)): + if icon_file.endswith('.png'): + self.icon_file_names.append(icon_file) + self.icon_file_names.sort(key=sort_key) + self.update_filename_box() + self.icon_with_text.setChecked(True) + if icon_rule_kind == 'icon_only': + self.icon_without_text.setChecked(True) + if mi: self.mi = mi else: @@ -248,6 +282,8 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK')) self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel')) self.color_copy_button.clicked.connect(self.color_to_clipboard) + self.filename_button.clicked.connect(self.filename_button_clicked) + self.icon_copy_button.clicked.connect(self.icon_to_clipboard) try: with open(P('template-functions.json'), 'rb') as f: @@ -276,11 +312,55 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): '' '%s'%tt) + 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: + icon_path = path[0] + icon_name = sanitize_file_name_unicode( + os.path.splitext( + os.path.basename(icon_path))[0]+'.png') + if icon_name not in self.icon_file_names: + self.icon_file_names.append(icon_name) + self.update_filename_box() + try: + p = QIcon(icon_path).pixmap(QSize(128, 128)) + d = os.path.join(config_dir, 'cc_icons') + if not os.path.exists(os.path.join(d, icon_name)): + if not os.path.exists(d): + os.makedirs(d) + with open(os.path.join(d, icon_name), 'wb') as f: + f.write(pixmap_to_data(p, format='PNG')) + except: + traceback.print_exc() + self.icon_files.setCurrentIndex(self.icon_files.findText(icon_name)) + self.icon_files.adjustSize() + except: + traceback.print_exc() + return + + def update_filename_box(self): + self.icon_files.clear() + self.icon_file_names.sort(key=sort_key) + self.icon_files.addItem('') + self.icon_files.addItems(self.icon_file_names) + for i,filename in enumerate(self.icon_file_names): + icon = QIcon(os.path.join(config_dir, 'cc_icons', filename)) + self.icon_files.setItemIcon(i+1, icon) + def color_to_clipboard(self): app = QApplication.instance() c = app.clipboard() c.setText(unicode(self.color_name.currentText())) + def icon_to_clipboard(self): + app = QApplication.instance() + c = app.clipboard() + c.setText(unicode(self.icon_files.currentText())) + def textbox_changed(self): cur_text = unicode(self.textbox.toPlainText()) if self.last_text != cur_text: @@ -324,5 +404,14 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): _('The template box cannot be empty'), show=True) return - self.rule = (unicode(self.colored_field.currentText()), txt) + self.rule = (unicode(self.colored_field.itemData( + self.colored_field.currentIndex()).toString()), txt) + elif self.iconing: + rt = 'icon' if self.icon_with_text.isChecked() else 'icon_only' + self.rule = (rt, + unicode(self.icon_field.itemData( + self.icon_field.currentIndex()).toString()), + txt) + else: + self.rule = ('', txt) QDialog.accept(self) diff --git a/src/calibre/gui2/dialogs/template_dialog.ui b/src/calibre/gui2/dialogs/template_dialog.ui index 0acfc0f0f8..18c2a5ee35 100644 --- a/src/calibre/gui2/dialogs/template_dialog.ui +++ b/src/calibre/gui2/dialogs/template_dialog.ui @@ -21,47 +21,136 @@ - - - - - Set the color of the column: - - - colored_field - - - - - - - - - - - Copy a color name to the clipboard: - - - color_name - - - - - - - - - - - - :/images/edit-copy.png:/images/edit-copy.png - - - Copy the selected color name to the clipboard - - - - + + + + + + Set the color of the column: + + + colored_field + + + + + + + + + + + Copy a color name to the clipboard: + + + color_name + + + + + + + + + + + + :/images/edit-copy.png:/images/edit-copy.png + + + Copy the selected color name to the clipboard + + + + + + + + + + + + + Kind + + + + + + icon with no text + + + + + + + icon with text + + + + + + + 100 + 0 + + + + + + + + Apply the icon to column: + + + icon_field + + + + + + + + + + + Copy a icon file name to the clipboard: + + + color_name + + + + + + + + + + + + + + + :/images/edit-copy.png:/images/edit-copy.png + + + Copy the selected color name to the clipboard + + + + + + + Add file + + + + + + + + diff --git a/src/calibre/gui2/preferences/coloring.py b/src/calibre/gui2/preferences/coloring.py index 8d27d14e5b..422c0ba012 100644 --- a/src/calibre/gui2/preferences/coloring.py +++ b/src/calibre/gui2/preferences/coloring.py @@ -636,10 +636,20 @@ class RulesModel(QAbstractListModel): # {{{ 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)) + if kind == 'color': + return _(''' +

Advanced Rule for column %(col)s: +

%(rule)s
+ ''')%dict(col=col, rule=prepare_string_for_xml(rule)) + else: + return _(''' +

Advanced Rule: set %(typ)s for column %(col)s: +

%(rule)s
+ ''')%dict(col=col, + typ=icon_rule_kinds[0][0] + if kind == icon_rule_kinds[0][1] else icon_rule_kinds[1][0], + rule=prepare_string_for_xml(rule)) + conditions = [self.condition_to_html(c) for c in rule.conditions] trans_kind = 'not found' @@ -761,7 +771,7 @@ class EditRules(QWidget): # {{{ ' 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) +# self.add_advanced_button.setVisible(False) def add_rule(self): d = RuleEditor(self.model.fm, self.pref_name) @@ -774,13 +784,23 @@ class EditRules(QWidget): # {{{ self.changed.emit() def add_advanced(self): - td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, color_field='') - if td.exec_() == td.Accepted: - col, r = td.rule - if r and col: - idx = self.model.add_rule('color', col, r) - self.rules_view.scrollTo(idx) - self.changed.emit() + if self.pref_name == 'column_color_rules': + td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, color_field='') + if td.exec_() == td.Accepted: + col, r = td.rule + if r and col: + idx = self.model.add_rule('color', col, r) + self.rules_view.scrollTo(idx) + self.changed.emit() + else: + td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, icon_file='') + if td.exec_() == td.Accepted: + print(td.rule) + typ, col, r = td.rule + if typ and r and col: + idx = self.model.add_rule(typ, col, r) + self.rules_view.scrollTo(idx) + self.changed.emit() def edit_rule(self, index): try: @@ -790,8 +810,12 @@ class EditRules(QWidget): # {{{ if isinstance(rule, Rule): d = RuleEditor(self.model.fm, self.pref_name) d.apply_rule(kind, col, rule) - else: + elif self.pref_name == 'column_color_rules': d = TemplateDialog(self, rule, mi=self.mi, fm=self.fm, color_field=col) + else: + d = TemplateDialog(self, rule, mi=self.mi, fm=self.fm, icon_file=col, + icon_rule_kind=kind) + if d.exec_() == d.Accepted: if len(d.rule) == 2: # Convert template dialog rules to a triple d.rule = ('color', d.rule[0], d.rule[1]) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index d25c34d52b..959248fdf8 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -749,6 +749,7 @@ class BrowseServer(object): @Endpoint(mimetype='application/json; charset=utf-8') def browse_booklist_page(self, ids=None, sort=None): + print('here') if sort == 'null': sort = None if ids is None: