mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
First pass at advanced icon selection rules
This commit is contained in:
parent
3dee6cab80
commit
61dac94abe
@ -3,17 +3,21 @@ __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
|
|
||||||
import json
|
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)
|
||||||
|
|
||||||
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.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog
|
||||||
from calibre.utils.formatter_functions import formatter_functions
|
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.base import Metadata
|
||||||
from calibre.ebooks.metadata.book.formatter import SafeFormat
|
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:
|
class ParenPosition:
|
||||||
@ -198,25 +202,55 @@ class TemplateHighlighter(QSyntaxHighlighter):
|
|||||||
|
|
||||||
class TemplateDialog(QDialog, Ui_TemplateDialog):
|
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)
|
QDialog.__init__(self, parent)
|
||||||
Ui_TemplateDialog.__init__(self)
|
Ui_TemplateDialog.__init__(self)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
self.coloring = color_field is not None
|
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:
|
if self.coloring:
|
||||||
cols = sorted([k for k in displayable_columns(fm)])
|
self.color_layout.setVisible(True)
|
||||||
self.colored_field.addItems(cols)
|
for n1, k1 in cols:
|
||||||
|
self.colored_field.addItem(n1, k1)
|
||||||
self.colored_field.setCurrentIndex(self.colored_field.findText(color_field))
|
self.colored_field.setCurrentIndex(self.colored_field.findText(color_field))
|
||||||
colors = QColor.colorNames()
|
colors = QColor.colorNames()
|
||||||
colors.sort()
|
colors.sort()
|
||||||
self.color_name.addItems(colors)
|
self.color_name.addItems(colors)
|
||||||
else:
|
elif self.iconing:
|
||||||
self.colored_field.setVisible(False)
|
self.icon_layout.setVisible(True)
|
||||||
self.colored_field_label.setVisible(False)
|
for n1, k1 in cols:
|
||||||
self.color_chooser_label.setVisible(False)
|
self.icon_field.addItem(n1, k1)
|
||||||
self.color_name.setVisible(False)
|
self.icon_file_names = []
|
||||||
self.color_copy_button.setVisible(False)
|
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:
|
if mi:
|
||||||
self.mi = mi
|
self.mi = mi
|
||||||
else:
|
else:
|
||||||
@ -248,6 +282,8 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK'))
|
self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK'))
|
||||||
self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel'))
|
self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel'))
|
||||||
self.color_copy_button.clicked.connect(self.color_to_clipboard)
|
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:
|
try:
|
||||||
with open(P('template-functions.json'), 'rb') as f:
|
with open(P('template-functions.json'), 'rb') as f:
|
||||||
@ -276,11 +312,55 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
'<a href="http://manual.calibre-ebook.com/template_ref.html">'
|
'<a href="http://manual.calibre-ebook.com/template_ref.html">'
|
||||||
'%s</a>'%tt)
|
'%s</a>'%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):
|
def color_to_clipboard(self):
|
||||||
app = QApplication.instance()
|
app = QApplication.instance()
|
||||||
c = app.clipboard()
|
c = app.clipboard()
|
||||||
c.setText(unicode(self.color_name.currentText()))
|
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):
|
def textbox_changed(self):
|
||||||
cur_text = unicode(self.textbox.toPlainText())
|
cur_text = unicode(self.textbox.toPlainText())
|
||||||
if self.last_text != cur_text:
|
if self.last_text != cur_text:
|
||||||
@ -324,5 +404,14 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
_('The template box cannot be empty'), show=True)
|
_('The template box cannot be empty'), show=True)
|
||||||
return
|
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)
|
QDialog.accept(self)
|
||||||
|
@ -21,47 +21,136 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout">
|
<widget class="QWidget" name="color_layout">
|
||||||
<item row="0" column="0">
|
<layout class="QGridLayout">
|
||||||
<widget class="QLabel" name="colored_field_label">
|
<item row="0" column="0">
|
||||||
<property name="text">
|
<widget class="QLabel" name="colored_field_label">
|
||||||
<string>Set the color of the column:</string>
|
<property name="text">
|
||||||
</property>
|
<string>Set the color of the column:</string>
|
||||||
<property name="buddy">
|
</property>
|
||||||
<cstring>colored_field</cstring>
|
<property name="buddy">
|
||||||
</property>
|
<cstring>colored_field</cstring>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
</widget>
|
||||||
<item row="0" column="1">
|
</item>
|
||||||
<widget class="QComboBox" name="colored_field">
|
<item row="0" column="1">
|
||||||
</widget>
|
<widget class="QComboBox" name="colored_field">
|
||||||
</item>
|
</widget>
|
||||||
<item row="1" column="0">
|
</item>
|
||||||
<widget class="QLabel" name="color_chooser_label">
|
<item row="1" column="0">
|
||||||
<property name="text">
|
<widget class="QLabel" name="color_chooser_label">
|
||||||
<string>Copy a color name to the clipboard:</string>
|
<property name="text">
|
||||||
</property>
|
<string>Copy a color name to the clipboard:</string>
|
||||||
<property name="buddy">
|
</property>
|
||||||
<cstring>color_name</cstring>
|
<property name="buddy">
|
||||||
</property>
|
<cstring>color_name</cstring>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
</widget>
|
||||||
<item row="1" column="1">
|
</item>
|
||||||
<widget class="QComboBox" name="color_name">
|
<item row="1" column="1">
|
||||||
</widget>
|
<widget class="QComboBox" name="color_name">
|
||||||
</item>
|
</widget>
|
||||||
<item row="1" column="2">
|
</item>
|
||||||
<widget class="QToolButton" name="color_copy_button">
|
<item row="1" column="2">
|
||||||
<property name="icon">
|
<widget class="QToolButton" name="color_copy_button">
|
||||||
<iconset resource="../../../../resources/images.qrc">
|
<property name="icon">
|
||||||
<normaloff>:/images/edit-copy.png</normaloff>:/images/edit-copy.png</iconset>
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
</property>
|
<normaloff>:/images/edit-copy.png</normaloff>:/images/edit-copy.png</iconset>
|
||||||
<property name="toolTip">
|
</property>
|
||||||
<string>Copy the selected color name to the clipboard</string>
|
<property name="toolTip">
|
||||||
</property>
|
<string>Copy the selected color name to the clipboard</string>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
</widget>
|
||||||
</layout>
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="icon_layout">
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Kind</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="icon_without_text">
|
||||||
|
<property name="text">
|
||||||
|
<string>icon with no text</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<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 row="1" column="0">
|
||||||
|
<widget class="QLabel" name="icon_chooser_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Apply the icon to column:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>icon_field</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="icon_field">
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="image_chooser_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Copy a icon file name to the clipboard:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>color_name</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QWidget">
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="icon_files">
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="icon_copy_button">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/edit-copy.png</normaloff>:/images/edit-copy.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Copy the selected color name to the clipboard</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="filename_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Add file</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPlainTextEdit" name="textbox"/>
|
<widget class="QPlainTextEdit" name="textbox"/>
|
||||||
|
@ -636,10 +636,20 @@ class RulesModel(QAbstractListModel): # {{{
|
|||||||
|
|
||||||
def rule_to_html(self, kind, col, rule):
|
def rule_to_html(self, kind, col, rule):
|
||||||
if not isinstance(rule, Rule):
|
if not isinstance(rule, Rule):
|
||||||
return _('''
|
if kind == 'color':
|
||||||
<p>Advanced Rule for column <b>%(col)s</b>:
|
return _('''
|
||||||
<pre>%(rule)s</pre>
|
<p>Advanced Rule for column <b>%(col)s</b>:
|
||||||
''')%dict(col=col, rule=prepare_string_for_xml(rule))
|
<pre>%(rule)s</pre>
|
||||||
|
''')%dict(col=col, rule=prepare_string_for_xml(rule))
|
||||||
|
else:
|
||||||
|
return _('''
|
||||||
|
<p>Advanced Rule: set <b>%(typ)s</b> for column <b>%(col)s</b>:
|
||||||
|
<pre>%(rule)s</pre>
|
||||||
|
''')%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]
|
conditions = [self.condition_to_html(c) for c in rule.conditions]
|
||||||
|
|
||||||
trans_kind = 'not found'
|
trans_kind = 'not found'
|
||||||
@ -761,7 +771,7 @@ class EditRules(QWidget): # {{{
|
|||||||
' what icon to use. Click the Add Rule button below'
|
' what icon to use. Click the Add Rule button below'
|
||||||
' to get started.<p>You can <b>change an existing rule</b> by'
|
' to get started.<p>You can <b>change an existing rule</b> by'
|
||||||
' double clicking it.'))
|
' double clicking it.'))
|
||||||
self.add_advanced_button.setVisible(False)
|
# self.add_advanced_button.setVisible(False)
|
||||||
|
|
||||||
def add_rule(self):
|
def add_rule(self):
|
||||||
d = RuleEditor(self.model.fm, self.pref_name)
|
d = RuleEditor(self.model.fm, self.pref_name)
|
||||||
@ -774,13 +784,23 @@ class EditRules(QWidget): # {{{
|
|||||||
self.changed.emit()
|
self.changed.emit()
|
||||||
|
|
||||||
def add_advanced(self):
|
def add_advanced(self):
|
||||||
td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, color_field='')
|
if self.pref_name == 'column_color_rules':
|
||||||
if td.exec_() == td.Accepted:
|
td = TemplateDialog(self, '', mi=self.mi, fm=self.fm, color_field='')
|
||||||
col, r = td.rule
|
if td.exec_() == td.Accepted:
|
||||||
if r and col:
|
col, r = td.rule
|
||||||
idx = self.model.add_rule('color', col, r)
|
if r and col:
|
||||||
self.rules_view.scrollTo(idx)
|
idx = self.model.add_rule('color', col, r)
|
||||||
self.changed.emit()
|
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):
|
def edit_rule(self, index):
|
||||||
try:
|
try:
|
||||||
@ -790,8 +810,12 @@ class EditRules(QWidget): # {{{
|
|||||||
if isinstance(rule, Rule):
|
if isinstance(rule, Rule):
|
||||||
d = RuleEditor(self.model.fm, self.pref_name)
|
d = RuleEditor(self.model.fm, self.pref_name)
|
||||||
d.apply_rule(kind, col, rule)
|
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)
|
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 d.exec_() == d.Accepted:
|
||||||
if len(d.rule) == 2: # Convert template dialog rules to a triple
|
if len(d.rule) == 2: # Convert template dialog rules to a triple
|
||||||
d.rule = ('color', d.rule[0], d.rule[1])
|
d.rule = ('color', d.rule[0], d.rule[1])
|
||||||
|
@ -749,6 +749,7 @@ class BrowseServer(object):
|
|||||||
|
|
||||||
@Endpoint(mimetype='application/json; charset=utf-8')
|
@Endpoint(mimetype='application/json; charset=utf-8')
|
||||||
def browse_booklist_page(self, ids=None, sort=None):
|
def browse_booklist_page(self, ids=None, sort=None):
|
||||||
|
print('here')
|
||||||
if sort == 'null':
|
if sort == 'null':
|
||||||
sort = None
|
sort = None
|
||||||
if ids is None:
|
if ids is None:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user