diff --git a/src/calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.py b/src/calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.py
index f8b836172c..580c2e67c0 100644
--- a/src/calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.py
+++ b/src/calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.py
@@ -9,93 +9,285 @@ import copy
import os
from functools import partial
-from qt.core import QAbstractItemView, QApplication, QIcon, QMenu, Qt, QTableWidgetItem
+from qt.core import (
+ QAbstractItemView,
+ QApplication,
+ QDialog,
+ QIcon,
+ QMenu,
+ QSize,
+ QStyledItemDelegate,
+ Qt,
+ QTableWidgetItem,
+)
from calibre.constants import config_dir
from calibre.db.constants import TEMPLATE_ICON_INDICATOR
-from calibre.gui2 import gprefs
-from calibre.gui2.preferences import ConfigWidgetBase, LazyConfigWidgetBase
+from calibre.gui2 import gprefs, choose_files, pixmap_to_data
+from calibre.gui2.dialogs.template_dialog import TemplateDialog
+from calibre.gui2.library.delegates import DelegateCB
+from calibre.gui2.preferences import LazyConfigWidgetBase
from calibre.gui2.preferences.look_feel_tabs.tb_icon_rules_ui import Ui_Form
+from calibre.utils.formatter import EvalFormatter
DELETED_COLUMN = 0
CATEGORY_COLUMN = 1
VALUE_COLUMN = 2
-ICON_COLUMN = 3
-FOR_CHILDREN_COLUMN = 4
-HEADER_SECTION_COUNT = 5
+ICON_MODIFIED_COLUMN = 3
+ICON_COLUMN = 4
+FOR_CHILDREN_MODIFIED_COLUMN = 5
+FOR_CHILDREN_COLUMN = 6
+HEADER_SECTION_COUNT = 7
+
+
+class StateTableWidgetItem(QTableWidgetItem):
+
+ def __init__(self, txt):
+ super().__init__(txt)
+ self.setIcon(QIcon.cached_icon("blank.png"))
+ self.setFlags(Qt.ItemFlag.NoItemFlags)
+
+ def setText(self, txt):
+ if txt:
+ super().setText(_('Yes') if txt else '')
+ if self.column() == DELETED_COLUMN:
+ self.setIcon(QIcon.cached_icon('trash.png'))
+ else:
+ self.setIcon(QIcon.cached_icon("modified.png"))
+ else:
+ super().setText('')
+ self.setIcon(QIcon.cached_icon("blank.png"))
class CategoryTableWidgetItem(QTableWidgetItem):
- def __init__(self, lookup_name, category_icons, deleted_item, field_metadata):
- self._lookup_name = lookup_name
+ def __init__(self, lookup_name, category_icons, field_metadata, table):
txt = field_metadata[lookup_name]['name'] + f' ({lookup_name})'
super().__init__(txt)
- self._is_deleted = False
+ self._lookup_name = lookup_name
+ self._table = table
+ self._is_modified = False
self.setIcon(category_icons[lookup_name])
self._txt = txt
- self._deleted_item = deleted_item
+ self.setFlags(self.flags() & ~Qt.ItemFlag.ItemIsEditable)
@property
- def is_deleted(self):
- return self._is_deleted
+ def is_modified(self):
+ return self._is_modified
- @is_deleted.setter
- def is_deleted(self, to_what):
- self._is_deleted = to_what
- if to_what:
- self._deleted_item.setIcon(QIcon.cached_icon('trash.png'))
- else:
- self._deleted_item.setIcon(QIcon())
+ @is_modified.setter
+ def is_modified(self, to_what):
+ self._is_modified = to_what
+ deleted_item = self._table.item(self.row(), DELETED_COLUMN)
+ deleted_item.setText(to_what)
@property
def lookup_name(self):
return self._lookup_name
+ def undo(self):
+ self.is_modified = False
+
class ValueTableWidgetItem(QTableWidgetItem):
- def __init__(self, txt):
- self._key = txt
- is_template = txt == TEMPLATE_ICON_INDICATOR
+ def __init__(self, txt, table):
+ self._original_text = txt
+ self._table = table
+ self._is_template = is_template = txt == TEMPLATE_ICON_INDICATOR
super().__init__(('{' + _('template') + '}') if is_template else txt)
self.setIcon(QIcon.cached_icon('debug.png' if is_template else 'icon_choose.png'))
+ self.setFlags(self.flags() & ~Qt.ItemFlag.ItemIsEditable)
@property
- def real_value(self):
- return self._key
+ def original_text(self):
+ return self._original_text
+
+ @property
+ def is_template(self):
+ return self._is_template
+
+ @property
+ def is_modified(self):
+ return self._table.item(self.row(), CATEGORY_COLUMN).is_modified
class IconFileTableWidgetItem(QTableWidgetItem):
- def __init__(self, icon_file, value):
- self._key = icon_file
- is_template = value == TEMPLATE_ICON_INDICATOR
+ def __init__(self, icon_file, value_text, table):
super().__init__(icon_file)
+ self._new_icon = None
+ self._table = table
+ self._is_modified = False
+ self._original_text = icon_file
self.setToolTip(icon_file)
- if is_template:
- self.setIcon(QIcon.cached_icon('blank.png'))
+ if value_text == TEMPLATE_ICON_INDICATOR:
+ icon = QIcon.cached_icon('blank.png')
else:
p = os.path.join(config_dir, 'tb_icons', icon_file)
if os.path.exists(p):
icon = QIcon.ic(p)
- self.setIcon(icon)
else:
- self.setIcon(QIcon.cached_icon('dialog_error.png'))
+ icon = QIcon.cached_icon('dialog_error.png')
self.setToolTip(icon_file + '\n' + _("This icon file doesn't exist"))
+ self.setIcon(icon)
+ self._original_icon = icon
+
+ @property
+ def original_text(self):
+ return self._original_text
+
+ @property
+ def new_icon(self):
+ return self._new_icon
+
+ @new_icon.setter
+ def new_icon(self, to_what):
+ # to_what is the new icon pixmap in bytes
+ self._new_icon = to_what
+
+ @property
+ def is_modified(self):
+ return self._is_modified
+
+ @is_modified.setter
+ def is_modified(self, to_what):
+ self._is_modified = to_what
+ del_item = self._table.item(self.row(), ICON_MODIFIED_COLUMN)
+ del_item.setText(to_what)
+
+ def set_text(self, txt):
+ self.setText(txt)
+ self.setToolTip(txt)
+
+ def undo(self):
+ self.is_modified = False
+ self.set_text(self._original_text)
+ self.setIcon(self._original_icon)
+
+
+class IconColumnDelegate(QStyledItemDelegate):
+
+ def __init__(self, parent, table, changed_signal):
+ super().__init__(parent)
+ self._table = table
+ self._changed_signal = changed_signal
+
+ def createEditor(self, parent, option, index):
+ row = index.row()
+ value_item = self._table.item(row, VALUE_COLUMN)
+ icon_item = self._table.item(row, ICON_COLUMN)
+ if value_item.is_template:
+ v = {'title': 'Template Rule', 'category': self._table.item(row, CATEGORY_COLUMN).text(),
+ 'value': 'abcd', 'count': str(5), 'avg_rating': str(2.5)}
+ d = TemplateDialog(parent=self.parent(), text=self._table.item(row, ICON_COLUMN).text(),
+ mi=v, doing_emblem=True, formatter=EvalFormatter, icon_dir='tb_icons/template_icons')
+ if d.exec() == QDialog.DialogCode.Accepted:
+ icon_item.set_text(d.rule[2])
+ icon_item.is_modified = True
+ self._changed_signal.emit()
+ return None
+
+ path = choose_files(self.parent(), 'choose_category_icon',
+ _('Change icon for: %s')%value_item.text(), filters=[
+ ('Images', ['png', 'gif', 'jpg', 'jpeg'])],
+ all_files=False, select_only_single_file=True)
+ if not path:
+ return
+ new_icon = QIcon(path[0])
+ icon_item.new_icon = pixmap_to_data(new_icon.pixmap(QSize(128, 128)), format='PNG')
+ icon_item.setIcon(new_icon)
+ icon_item.is_modified = True
+ self._changed_signal.emit()
class ChildrenTableWidgetItem(QTableWidgetItem):
- def __init__(self, txt, for_child):
- super().__init__(txt)
- if for_child is None:
- icon = QIcon()
- elif for_child:
- icon = QIcon.cached_icon('ok.png')
+ def __init__(self, value, item_value, table):
+ super().__init__('')
+ self._is_modified = False
+ self._original_value = self._value = value
+ self._item_value = item_value
+ self._table = table
+ self._set_text_and_icon(value)
+
+ def _set_text_and_icon(self, value):
+ if self._item_value == TEMPLATE_ICON_INDICATOR:
+ txt = ''
else:
- icon = QIcon.cached_icon('list_remove.png')
- self.setIcon(QIcon.cached_icon(icon))
+ txt = _('Yes') if value else _('No')
+ if value is None:
+ icon = QIcon()
+ elif value:
+ icon = QIcon.cached_icon('ok.png')
+ else:
+ icon = QIcon.cached_icon('list_remove.png')
+ self.setIcon(icon)
+ self.setText(txt)
+ self._value = value
+
+ @property
+ def original_value(self):
+ return self._original_value
+
+ @property
+ def value(self):
+ return self._value
+
+ @property
+ def is_modified(self):
+ return self._is_modified
+
+ @is_modified.setter
+ def is_modified(self, to_what):
+ del_item = self._table.item(self.row(), FOR_CHILDREN_MODIFIED_COLUMN)
+ if to_what:
+ del_item.setText(to_what)
+ self._is_modified = True
+ else:
+ del_item.setText(False)
+ self._is_modified = False
+
+ def set_value(self, val):
+ self._set_text_and_icon(val)
+ self.is_modified = val != self.original_value
+
+ def undo(self):
+ self.is_modified = False
+ self._set_text_and_icon(self._original_value)
+
+
+class ChildrenColumnDelegate(QStyledItemDelegate):
+
+ def __init__(self, parent, table, changed_signal):
+ super().__init__(parent)
+ self._table = table
+ self._changed_signal = changed_signal
+
+ def createEditor(self, parent, option, index):
+ item = self._table.item(index.row(), VALUE_COLUMN)
+ if item.is_template:
+ return None
+ editor = DelegateCB(parent)
+ items = [_('Yes'), _('No'), ]
+ icons = ['ok.png', 'list_remove.png']
+ self.longest_text = ''
+ for icon, text in zip(icons, items):
+ editor.addItem(QIcon.cached_icon(icon), text)
+ if len(text) > len(self.longest_text):
+ self.longest_text = text
+ return editor
+
+ def setModelData(self, editor, model, index):
+ val = {0:True, 1:False}[editor.currentIndex()]
+ self._table.item(index.row(), index.column()).set_value(val)
+ self._changed_signal.emit()
+
+ def setEditorData(self, editor, index):
+ item = self._table.item(index.row(), index.column())
+ val = item.original_value
+ val = 0 if val else 1
+ editor.setCurrentIndex(val)
class TbIconRulesTab(LazyConfigWidgetBase, Ui_Form):
@@ -106,26 +298,35 @@ class TbIconRulesTab(LazyConfigWidgetBase, Ui_Form):
r('tag_browser_show_category_icons', gprefs)
r('tag_browser_show_value_icons', gprefs)
- self.rules_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
- self.rules_table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
+ self.rules_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectItems)
self.rules_table.setColumnCount(HEADER_SECTION_COUNT)
- self.rules_table.setHorizontalHeaderLabels(
- ('', _('Category'), _('Value'), _('Icon file or template'),_('For children')))
+ self.rules_table.setHorizontalHeaderLabels(('', _('Category'), _('Value'), '',
+ _('Icon file or template'), '', _('For children')))
self.rules_table.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.rules_table.customContextMenuRequested.connect(self.show_context_menu)
self.rules_table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
+ # Make the minimum section size smaller so the icon column icons don't
+ # have a lot of space on the right
+ self.rules_table.horizontalHeader().setMinimumSectionSize(20)
+
for i in range(HEADER_SECTION_COUNT):
item = self.rules_table.horizontalHeaderItem(i)
if i == DELETED_COLUMN:
item.setIcon(QIcon.cached_icon('trash.png'))
- item.setToolTip(_('Show this icon if the rule is deleted'))
+ item.setToolTip(_('This icon shows in the row if the rule is deleted'))
elif i == CATEGORY_COLUMN:
item.setToolTip(_('The name of the category'))
elif i == VALUE_COLUMN:
item.setToolTip(_('The value in the category the rule is applied to'))
+ elif i == ICON_MODIFIED_COLUMN:
+ item.setIcon(QIcon.cached_icon('modified.png'))
+ item.setToolTip(_('This icon shows in the row if the icon or template is modified'))
elif i == ICON_COLUMN:
item.setToolTip(_('The file name of the icon or the text of the template'))
+ elif i == FOR_CHILDREN_MODIFIED_COLUMN:
+ item.setIcon(QIcon.cached_icon('modified.png'))
+ item.setToolTip(_('This icon shows in the row if the "for children" setting is modified'))
elif i == FOR_CHILDREN_COLUMN:
item.setToolTip(_('Indicates whether the rule applies to child values'))
@@ -137,7 +338,8 @@ class TbIconRulesTab(LazyConfigWidgetBase, Ui_Form):
hh.setSortIndicatorShown(True)
self.delete_button.clicked.connect(self.delete_rule)
- self.undo_button.clicked.connect(self.undo_delete)
+ self.edit_button.clicked.connect(self.edit_column)
+ self.undo_button.clicked.connect(self.undo_changes)
self.tb_icon_rules_groupbox.setContentsMargins(0, 0, 0, 0)
self.tb_icon_rules_gridlayout.setContentsMargins(2, 2, 2, 2)
@@ -152,51 +354,65 @@ class TbIconRulesTab(LazyConfigWidgetBase, Ui_Form):
category_icons = self.gui.tags_view.model().category_custom_icons
v = gprefs['tags_browser_value_icons']
row = 0
+
+ t = self.rules_table
+ t.setItemDelegateForColumn(ICON_COLUMN, IconColumnDelegate(self, self.rules_table, self.changed_signal))
+ t.setItemDelegateForColumn(FOR_CHILDREN_COLUMN,
+ ChildrenColumnDelegate(self, self.rules_table, self.changed_signal))
+
for category,vdict in v.items():
- for value in vdict:
- self.rules_table.setRowCount(row + 1)
- d = v[category][value]
- deleted_item = QTableWidgetItem(None)
- self.rules_table.setItem(row, DELETED_COLUMN, deleted_item)
- self.rules_table.setItem(row, CATEGORY_COLUMN,
- CategoryTableWidgetItem(category, category_icons, deleted_item, field_metadata))
- self.rules_table.setItem(row, VALUE_COLUMN, ValueTableWidgetItem(value))
- self.rules_table.setItem(row, ICON_COLUMN, IconFileTableWidgetItem(d[0], value))
- if value == TEMPLATE_ICON_INDICATOR:
- txt = ''
- else:
- txt = _('Yes') if d[1] else _('No')
- item = ChildrenTableWidgetItem(txt, None if value == TEMPLATE_ICON_INDICATOR else d[1])
- self.rules_table.setItem(row, FOR_CHILDREN_COLUMN, item)
+ for item_value in vdict:
+ t.setRowCount(row + 1)
+ d = v[category][item_value]
+ t.setItem(row, DELETED_COLUMN, StateTableWidgetItem(''))
+ t.setItem(row, CATEGORY_COLUMN,
+ CategoryTableWidgetItem(category, category_icons, field_metadata, t))
+ t.setItem(row, ICON_MODIFIED_COLUMN, StateTableWidgetItem(''))
+ t.setItem(row, VALUE_COLUMN, ValueTableWidgetItem(item_value, t))
+ t.setItem(row, ICON_COLUMN, IconFileTableWidgetItem(d[0], item_value, t))
+ t.setItem(row, FOR_CHILDREN_MODIFIED_COLUMN, StateTableWidgetItem(''))
+ item = ChildrenTableWidgetItem(d[1], item_value, t)
+ t.setItem(row, FOR_CHILDREN_COLUMN, item)
row += 1
- self.category_order = 1
- self.value_order = 1
- self.icon_order = 0
- self.for_children_order = 0
+ self.section_order = [0, 1, 1, 0, 0, 0, 0]
self.do_sort(VALUE_COLUMN)
self.do_sort(CATEGORY_COLUMN)
def show_context_menu(self, point):
- clicked_item = self.rules_table.itemAt(point)
- if clicked_item is None:
+ item = self.rules_table.itemAt(point)
+ if item is None:
+ return
+ column = item.column()
+ if column in (DELETED_COLUMN, ICON_MODIFIED_COLUMN, FOR_CHILDREN_MODIFIED_COLUMN):
return
- item = self.rules_table.item(clicked_item.row(), CATEGORY_COLUMN)
m = QMenu(self)
- ac = m.addAction(_('Delete this rule'), partial(self.context_menu_handler, 'delete', item))
- ac.setEnabled(not item.is_deleted)
- ac = m.addAction(_('Undo delete'), partial(self.context_menu_handler, 'undelete', item))
- ac.setEnabled(item.is_deleted)
+ if column in (CATEGORY_COLUMN, VALUE_COLUMN):
+ ac = m.addAction(_('Delete this rule'), partial(self.context_menu_handler, 'delete', item))
+ ac.setEnabled(not item.is_modified)
+ ac = m.addAction(_('Undo delete'), partial(self.context_menu_handler, 'undo_delete', item))
+ ac.setEnabled(item.is_modified)
+ elif column in (ICON_COLUMN, FOR_CHILDREN_COLUMN):
+ ac = m.addAction(_('Modify this value'), partial(self.context_menu_handler, 'modify', item))
+ ac.setEnabled(not item.is_modified)
+ ac = m.addAction(_('Undo modification'), partial(self.context_menu_handler, 'undo_modification', item))
+ ac.setEnabled(item.is_modified)
m.addSeparator()
- m.addAction(_('Copy'), partial(self.context_menu_handler, 'copy', clicked_item))
+ m.addAction(_('Copy'), partial(self.context_menu_handler, 'copy', item))
m.exec(self.rules_table.viewport().mapToGlobal(point))
def context_menu_handler(self, action, item):
if action == 'copy':
QApplication.clipboard().setText(item.text())
return
- item.setIcon(QIcon.ic('trash.png') if action == 'delete' else QIcon())
- item.is_deleted = action == 'delete'
+ if action == "delete":
+ self.delete_rule()
+ elif action == "undo_delete":
+ self.undo_delete()
+ elif action == "modify":
+ self.edit_column()
+ elif action == "undo_modification":
+ self.undo_modification()
self.changed_signal.emit()
def keyPressEvent(self, ev):
@@ -206,18 +422,48 @@ class TbIconRulesTab(LazyConfigWidgetBase, Ui_Form):
return
return super().keyPressEvent(ev)
+ def undo_changes(self):
+ idx = self.rules_table.currentIndex()
+ if idx.isValid():
+ column = idx.column()
+ if column == DELETED_COLUMN:
+ column = CATEGORY_COLUMN
+ elif column == ICON_MODIFIED_COLUMN:
+ column = ICON_COLUMN
+ elif column == FOR_CHILDREN_MODIFIED_COLUMN:
+ column = FOR_CHILDREN_COLUMN
+
+ if column in (CATEGORY_COLUMN, VALUE_COLUMN):
+ self.undo_delete()
+ elif column in (ICON_COLUMN, FOR_CHILDREN_COLUMN):
+ self.undo_modification()
+
+ def edit_column(self):
+ idx = self.rules_table.currentIndex()
+ if idx.isValid():
+ column = idx.column()
+ if column in (ICON_COLUMN, FOR_CHILDREN_COLUMN):
+ self.rules_table.edit(idx)
+
def delete_rule(self):
idx = self.rules_table.currentIndex()
if idx.isValid():
item = self.rules_table.item(idx.row(), CATEGORY_COLUMN)
- item.is_deleted = True
+ item.is_modified = True
self.changed_signal.emit()
def undo_delete(self):
idx = self.rules_table.currentIndex()
if idx.isValid():
- item = self.rules_table.item(idx.row(), CATEGORY_COLUMN)
- item.is_deleted = False
+ self.rules_table.item(idx.row(), CATEGORY_COLUMN).undo()
+ self.changed_signal.emit()
+
+ def undo_modification(self):
+ idx = self.rules_table.currentIndex()
+ if idx.isValid():
+ item = self.rules_table.item(idx.row(), idx.column())
+ item.undo()
+ self.changed_signal.emit()
def table_column_resized(self, col, old, new):
self.table_column_widths = []
@@ -231,47 +477,70 @@ class TbIconRulesTab(LazyConfigWidgetBase, Ui_Form):
for c,w in enumerate(self.table_column_widths):
self.rules_table.setColumnWidth(c, w)
else:
- # The vertical scroll bar might not be rendered, so might not yet
- # have a width. Assume 25. Not a problem because user-changed column
- # widths will be remembered.
- w = self.tb_icon_rules_groupbox.width() - 25 - self.rules_table.verticalHeader().width()
- w //= self.rules_table.columnCount()
+ # Calculate a reasonable initial sizing. The vertical scroll bar
+ # might not be rendered, so might not yet have a width, assume 25.
+ # Assume that a button is 60 wide. Assume that the 3 icon columns
+ # are 25 wide. None of this really matters because user-changed
+ # column widths will be remembered.
+ w = self.tb_icon_rules_groupbox.width() - (4*25) - 60 - self.rules_table.verticalHeader().width()
+ w //= (self.rules_table.columnCount() - 3)
for c in range(self.rules_table.columnCount()):
- self.rules_table.setColumnWidth(c, w)
+ if c in (DELETED_COLUMN, ICON_MODIFIED_COLUMN, FOR_CHILDREN_MODIFIED_COLUMN):
+ self.rules_table.setColumnWidth(c, 20)
+ else:
+ self.rules_table.setColumnWidth(c, w)
self.table_column_widths.append(self.rules_table.columnWidth(c))
gprefs['tag_browser_rules_dialog_table_widths'] = self.table_column_widths
def do_sort(self, section):
- if section == CATEGORY_COLUMN:
- self.category_order = 1 - self.category_order
- self.rules_table.sortByColumn(CATEGORY_COLUMN, Qt.SortOrder(self.category_order))
- elif section == VALUE_COLUMN:
- self.value_order = 1 - self.value_order
- self.rules_table.sortByColumn(VALUE_COLUMN, Qt.SortOrder(self.value_order))
- elif section == ICON_COLUMN:
- self.icon_order = 1 - self.icon_order
- self.rules_table.sortByColumn(ICON_COLUMN, Qt.SortOrder(self.icon_order))
- elif section == FOR_CHILDREN_COLUMN:
- self.for_children_order = 1 - self.for_children_order
- self.rules_table.sortByColumn(FOR_CHILDREN_COLUMN, Qt.SortOrder(self.for_children_order))
+ order = 1 - self.section_order[section]
+ self.section_order[section] = order
+ self.rules_table.sortByColumn(section, Qt.SortOrder(order))
def commit(self):
v = copy.deepcopy(gprefs['tags_browser_value_icons'])
for r in range(self.rules_table.rowCount()):
cat_item = self.rules_table.item(r, CATEGORY_COLUMN)
- if cat_item.is_deleted:
- val = self.rules_table.item(r, VALUE_COLUMN).real_value
- if val != TEMPLATE_ICON_INDICATOR:
+ value_item = self.rules_table.item(r, VALUE_COLUMN)
+ value_text = value_item._original_text
+
+ if cat_item.is_modified: # deleted
+ if not value_item.is_template:
+ # Need to delete the icon file to clean up
icon_file = self.rules_table.item(r, ICON_COLUMN).text()
path = os.path.join(config_dir, 'tb_icons', icon_file)
try:
os.remove(path)
except:
pass
- v[cat_item.lookup_name].pop(val, None)
+ v[cat_item.lookup_name].pop(value_text, None)
+ continue
+
+ icon_item = self.rules_table.item(r, ICON_COLUMN)
+ d = v[cat_item.lookup_name][value_text]
+
+ if icon_item.is_modified:
+ if value_item.is_template:
+ d[0] = icon_item.text()
+ v[cat_item.lookup_name][TEMPLATE_ICON_INDICATOR] = d
+ elif icon_item.new_icon is not None:
+ # No need to delete anything. The file name stays the same.
+ p = os.path.join(config_dir, 'tb_icons')
+ if not os.path.exists(p):
+ os.makedirs(p)
+ p = os.path.join(p, icon_item.text())
+ with open(p, 'wb') as f:
+ f.write(icon_item.new_icon)
+
+ child_item = self.rules_table.item(r, FOR_CHILDREN_COLUMN)
+ if child_item.is_modified:
+ d[1] = child_item.value
+ v[cat_item.lookup_name][value_text] = d
+
# Remove categories with no rules
for category in list(v.keys()):
if len(v[category]) == 0:
v.pop(category, None)
gprefs['tags_browser_value_icons'] = v
- return ConfigWidgetBase.commit(self)
+
+ return LazyConfigWidgetBase.commit(self)
diff --git a/src/calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.ui b/src/calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.ui
index 5fb2d7f87f..7b1426cf2d 100644
--- a/src/calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.ui
+++ b/src/calibre/gui2/preferences/look_feel_tabs/tb_icon_rules.ui
@@ -66,6 +66,17 @@ this dialog using the button, the delete key, or the context menu.</p>
+ -
+
+
+
+ :/images/edit_input.png:/images/edit_input.png
+
+
+ Edit the selected column
+
+
+
-