mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge branch 'master' of https://github.com/cbhaley/calibre
This commit is contained in:
commit
97efb777c8
@ -420,6 +420,7 @@ def create_defs():
|
|||||||
defs['bd_show_cover'] = True
|
defs['bd_show_cover'] = True
|
||||||
defs['bd_overlay_cover_size'] = False
|
defs['bd_overlay_cover_size'] = False
|
||||||
defs['tags_browser_category_icons'] = {}
|
defs['tags_browser_category_icons'] = {}
|
||||||
|
defs['tags_browser_value_icons'] = {}
|
||||||
defs['cover_browser_reflections'] = True
|
defs['cover_browser_reflections'] = True
|
||||||
defs['book_list_extra_row_spacing'] = 0
|
defs['book_list_extra_row_spacing'] = 0
|
||||||
defs['refresh_book_list_on_bulk_edit'] = True
|
defs['refresh_book_list_on_bulk_edit'] = True
|
||||||
|
@ -488,11 +488,16 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
icon_field_key=None, icon_rule_kind=None, doing_emblem=False,
|
icon_field_key=None, icon_rule_kind=None, doing_emblem=False,
|
||||||
text_is_placeholder=False, dialog_is_st_editor=False,
|
text_is_placeholder=False, dialog_is_st_editor=False,
|
||||||
global_vars=None, all_functions=None, builtin_functions=None,
|
global_vars=None, all_functions=None, builtin_functions=None,
|
||||||
python_context_object=None, dialog_number=None):
|
python_context_object=None, dialog_number=None,
|
||||||
|
formatter=SafeFormat, icon_dir='cc_icons'):
|
||||||
# If dialog_number isn't None then we want separate non-modal windows
|
# If dialog_number isn't None then we want separate non-modal windows
|
||||||
# that don't stay on top of the main dialog. This lets Alt-Tab work to
|
# that don't stay on top of the main dialog. This lets Alt-Tab work to
|
||||||
# switch between them. dialog_number must be set only by the template
|
# switch between them. dialog_number must be set only by the template
|
||||||
# tester, not the rules dialogs etc that depend on modality.
|
# tester, not the rules dialogs etc that depend on modality.
|
||||||
|
|
||||||
|
# doing_emblem is also used for tag browser value icon rules in order to
|
||||||
|
# show the icon selection widgets.
|
||||||
|
|
||||||
if dialog_number is None:
|
if dialog_number is None:
|
||||||
QDialog.__init__(self, parent, flags=Qt.WindowType.Dialog)
|
QDialog.__init__(self, parent, flags=Qt.WindowType.Dialog)
|
||||||
else:
|
else:
|
||||||
@ -502,6 +507,8 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.setWindowIcon(self.windowIcon())
|
self.setWindowIcon(self.windowIcon())
|
||||||
|
|
||||||
|
self.formatter = formatter
|
||||||
|
self.icon_dir = icon_dir
|
||||||
self.ffml = FFMLProcessor()
|
self.ffml = FFMLProcessor()
|
||||||
self.dialog_number = dialog_number
|
self.dialog_number = dialog_number
|
||||||
self.coloring = color_field is not None
|
self.coloring = color_field is not None
|
||||||
@ -544,7 +551,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
for n1, k1 in cols:
|
for n1, k1 in cols:
|
||||||
self.icon_field.addItem(f'{n1} ({k1})', k1)
|
self.icon_field.addItem(f'{n1} ({k1})', k1)
|
||||||
self.icon_file_names = []
|
self.icon_file_names = []
|
||||||
d = os.path.join(config_dir, 'cc_icons')
|
d = os.path.join(config_dir, icon_dir)
|
||||||
if os.path.exists(d):
|
if os.path.exists(d):
|
||||||
for icon_file in os.listdir(d):
|
for icon_file in os.listdir(d):
|
||||||
icon_file = icu_lower(icon_file)
|
icon_file = icu_lower(icon_file)
|
||||||
@ -766,7 +773,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
for r in range(0, len(mi)):
|
for r in range(0, len(mi)):
|
||||||
w = QLineEdit(tv)
|
w = QLineEdit(tv)
|
||||||
w.setReadOnly(True)
|
w.setReadOnly(True)
|
||||||
w.setText(mi[r].title)
|
w.setText(mi[r].get('title', _('No title provided')))
|
||||||
tv.setCellWidget(r, 0, w)
|
tv.setCellWidget(r, 0, w)
|
||||||
tb = QToolButton()
|
tb = QToolButton()
|
||||||
tb.setContentsMargins(0, 0, 0, 0)
|
tb.setContentsMargins(0, 0, 0, 0)
|
||||||
@ -992,7 +999,7 @@ def evaluate(book, context):
|
|||||||
self.update_filename_box()
|
self.update_filename_box()
|
||||||
try:
|
try:
|
||||||
p = QIcon(icon_path).pixmap(QSize(128, 128))
|
p = QIcon(icon_path).pixmap(QSize(128, 128))
|
||||||
d = os.path.join(config_dir, 'cc_icons')
|
d = os.path.join(config_dir, self.icon_dir)
|
||||||
if not os.path.exists(os.path.join(d, icon_name)):
|
if not os.path.exists(os.path.join(d, icon_name)):
|
||||||
if not os.path.exists(d):
|
if not os.path.exists(d):
|
||||||
os.makedirs(d)
|
os.makedirs(d)
|
||||||
@ -1012,7 +1019,7 @@ def evaluate(book, context):
|
|||||||
self.icon_files.addItem('')
|
self.icon_files.addItem('')
|
||||||
self.icon_files.addItems(self.icon_file_names)
|
self.icon_files.addItems(self.icon_file_names)
|
||||||
for i,filename in enumerate(self.icon_file_names):
|
for i,filename in enumerate(self.icon_file_names):
|
||||||
icon = QIcon(os.path.join(config_dir, 'cc_icons', filename))
|
icon = QIcon(os.path.join(config_dir, self.icon_dir, filename))
|
||||||
self.icon_files.setItemIcon(i+1, icon)
|
self.icon_files.setItemIcon(i+1, icon)
|
||||||
|
|
||||||
def color_to_clipboard(self):
|
def color_to_clipboard(self):
|
||||||
@ -1078,14 +1085,14 @@ def evaluate(book, context):
|
|||||||
break_on_mi = 0 if len(l) == 0 else l[0].row()
|
break_on_mi = 0 if len(l) == 0 else l[0].row()
|
||||||
for r,mi in enumerate(self.mi):
|
for r,mi in enumerate(self.mi):
|
||||||
w = tv.cellWidget(r, 0)
|
w = tv.cellWidget(r, 0)
|
||||||
w.setText(mi.title)
|
w.setText(mi.get('title', _('No title provided')))
|
||||||
w.setCursorPosition(0)
|
w.setCursorPosition(0)
|
||||||
if self.break_box.isChecked() and r == break_on_mi and self.is_python:
|
if self.break_box.isChecked() and r == break_on_mi and self.is_python:
|
||||||
sys.settrace(self.trace_calls)
|
sys.settrace(self.trace_calls)
|
||||||
else:
|
else:
|
||||||
sys.settrace(None)
|
sys.settrace(None)
|
||||||
try:
|
try:
|
||||||
v = SafeFormat().safe_format(txt, mi, _('EXCEPTION:'),
|
v = self.formatter().safe_format(txt, mi, _('EXCEPTION:'),
|
||||||
mi, global_vars=self.global_vars,
|
mi, global_vars=self.global_vars,
|
||||||
template_functions=self.all_functions,
|
template_functions=self.all_functions,
|
||||||
break_reporter=self.break_reporter if r == break_on_mi else None,
|
break_reporter=self.break_reporter if r == break_on_mi else None,
|
||||||
|
@ -30,6 +30,7 @@ TAG_SEARCH_STATES = {'clear': 0, 'mark_plus': 1, 'mark_plusplus': 2,
|
|||||||
'mark_minus': 3, 'mark_minusminus': 4}
|
'mark_minus': 3, 'mark_minusminus': 4}
|
||||||
DRAG_IMAGE_ROLE = Qt.ItemDataRole.UserRole + 1000
|
DRAG_IMAGE_ROLE = Qt.ItemDataRole.UserRole + 1000
|
||||||
COUNT_ROLE = DRAG_IMAGE_ROLE + 1
|
COUNT_ROLE = DRAG_IMAGE_ROLE + 1
|
||||||
|
TEMPLATE_ICON_INDICATOR = ' template ' # Item values cannot start or end with space
|
||||||
|
|
||||||
_bf = None
|
_bf = None
|
||||||
|
|
||||||
@ -49,7 +50,11 @@ class TagTreeItem: # {{{
|
|||||||
TAG = 1
|
TAG = 1
|
||||||
ROOT = 2
|
ROOT = 2
|
||||||
category_custom_icons = {}
|
category_custom_icons = {}
|
||||||
|
value_icons = {}
|
||||||
|
value_icon_cache = {}
|
||||||
|
icon_config_dir = {}
|
||||||
file_icon_provider = None
|
file_icon_provider = None
|
||||||
|
eval_formatter = EvalFormatter()
|
||||||
|
|
||||||
def __init__(self, data=None, is_category=False, icon_map=None,
|
def __init__(self, data=None, is_category=False, icon_map=None,
|
||||||
parent=None, tooltip=None, category_key=None, temporary=False,
|
parent=None, tooltip=None, category_key=None, temporary=False,
|
||||||
@ -60,6 +65,7 @@ class TagTreeItem: # {{{
|
|||||||
self.children = []
|
self.children = []
|
||||||
self.blank = QIcon()
|
self.blank = QIcon()
|
||||||
self.is_gst = is_gst
|
self.is_gst = is_gst
|
||||||
|
self.icon = None
|
||||||
self.boxed = False
|
self.boxed = False
|
||||||
self.temporary = False
|
self.temporary = False
|
||||||
self.can_be_edited = False
|
self.can_be_edited = False
|
||||||
@ -117,9 +123,40 @@ class TagTreeItem: # {{{
|
|||||||
if self.is_gst:
|
if self.is_gst:
|
||||||
cc = self.category_custom_icons.get(self.root_node().category_key, None)
|
cc = self.category_custom_icons.get(self.root_node().category_key, None)
|
||||||
elif self.tag.category == 'search' and not self.tag.is_searchable:
|
elif self.tag.category == 'search' and not self.tag.is_searchable:
|
||||||
cc = self.category_custom_icons.get('search_folder:', None)
|
cc = self.category_custom_icons.get('search_folder', None)
|
||||||
|
else:
|
||||||
|
if self.icon is None:
|
||||||
|
node = self
|
||||||
|
val_icon = None
|
||||||
|
category = node.tag.category
|
||||||
|
if category in self.value_icons:
|
||||||
|
while True:
|
||||||
|
val_icon = self.value_icons.get(category, {}).get(node.tag.original_name)
|
||||||
|
if val_icon is not None:
|
||||||
|
# Have an icon. Use it if value exact match or
|
||||||
|
# it applies to children
|
||||||
|
if node != self and not val_icon[1]:
|
||||||
|
val_icon = None
|
||||||
|
break
|
||||||
|
node = node.parent
|
||||||
|
if node.type != self.TAG or node.type == self.ROOT:
|
||||||
|
break
|
||||||
|
if val_icon is None and TEMPLATE_ICON_INDICATOR in self.value_icons[category]:
|
||||||
|
t = self.eval_formatter.safe_format(self.value_icons[category][TEMPLATE_ICON_INDICATOR][0],
|
||||||
|
{'category': category, 'value': node.tag.original_name},
|
||||||
|
'VALUE_ICON_TEMPLATE_ERROR', None)
|
||||||
|
if t:
|
||||||
|
val_icon = (os.path.join('template_icons', t), False)
|
||||||
|
if val_icon is not None:
|
||||||
|
cc = self.value_icon_cache.get(val_icon[0])
|
||||||
|
if cc is None:
|
||||||
|
cc = QIcon.ic(os.path.join(self.icon_config_dir, val_icon[0]))
|
||||||
|
self.value_icon_cache[val_icon[0]] = cc
|
||||||
|
self.icon = cc
|
||||||
else:
|
else:
|
||||||
cc = self.category_custom_icons.get(self.tag.category, None)
|
cc = self.category_custom_icons.get(self.tag.category, None)
|
||||||
|
else:
|
||||||
|
cc = self.icon
|
||||||
elif self.type == self.CATEGORY:
|
elif self.type == self.CATEGORY:
|
||||||
cc = self.category_custom_icons.get(self.category_key, None)
|
cc = self.category_custom_icons.get(self.category_key, None)
|
||||||
self.icon_state_map[0] = cc or QIcon()
|
self.icon_state_map[0] = cc or QIcon()
|
||||||
@ -350,8 +387,11 @@ class TagsModel(QAbstractItemModel): # {{{
|
|||||||
self.node_map = {}
|
self.node_map = {}
|
||||||
self.category_nodes = []
|
self.category_nodes = []
|
||||||
self.category_custom_icons = {}
|
self.category_custom_icons = {}
|
||||||
|
self.value_icons = {}
|
||||||
|
self.value_icon_cache = {}
|
||||||
|
self.icon_config_dir = os.path.join(config_dir, 'tb_icons')
|
||||||
for k, v in iteritems(self.prefs['tags_browser_category_icons']):
|
for k, v in iteritems(self.prefs['tags_browser_category_icons']):
|
||||||
icon = QIcon(os.path.join(config_dir, 'tb_icons', v))
|
icon = QIcon(os.path.join(self.icon_config_dir, v))
|
||||||
if len(icon.availableSizes()) > 0:
|
if len(icon.availableSizes()) > 0:
|
||||||
self.category_custom_icons[k] = icon
|
self.category_custom_icons[k] = icon
|
||||||
self.categories_with_ratings = ['authors', 'series', 'publisher', 'tags']
|
self.categories_with_ratings = ['authors', 'series', 'publisher', 'tags']
|
||||||
@ -387,9 +427,44 @@ class TagsModel(QAbstractItemModel): # {{{
|
|||||||
new_icon = new_key + ext
|
new_icon = new_key + ext
|
||||||
new_path = os.path.join(config_dir, 'tb_icons', new_icon)
|
new_path = os.path.join(config_dir, 'tb_icons', new_icon)
|
||||||
os.replace(old_path, new_path)
|
os.replace(old_path, new_path)
|
||||||
self.set_custom_category_icon(new_key, new_path)
|
self.set_custom_category_icon(new_key, new_icon)
|
||||||
self.set_custom_category_icon(old_key, None)
|
self.set_custom_category_icon(old_key, None)
|
||||||
|
|
||||||
|
def set_value_icon(self, key, value, file_name, children):
|
||||||
|
'''
|
||||||
|
Add a 'rule' for an icon for a value in the tag browser as a dict entry:
|
||||||
|
value_icons[key] = {value: (file_name, children)}
|
||||||
|
|
||||||
|
:param key: the lookup name for the tag browser category
|
||||||
|
:param value: the item value in the category. If the value is
|
||||||
|
TEMPLATE_ICON_INDICATOR then the rule applies to all items
|
||||||
|
that don't have a specific rule.
|
||||||
|
:param file_name: the name of the icon file to use for this value. If
|
||||||
|
this is a template rule then this is the text of the template.
|
||||||
|
:param children: for specific (non-template) rules: if True then the rule
|
||||||
|
is to be used for any children of the item that don't have
|
||||||
|
a specific rule. If False then this rule is used only for
|
||||||
|
the specified item.
|
||||||
|
'''
|
||||||
|
v = self.value_icons = self.prefs['tags_browser_value_icons']
|
||||||
|
if key not in v:
|
||||||
|
self.value_icons[key] = {value: (file_name, children)}
|
||||||
|
else:
|
||||||
|
self.value_icons[key].update({value: (file_name, children)})
|
||||||
|
self.value_icon_cache.pop(file_name, None)
|
||||||
|
self.prefs['tags_browser_value_icons'] = self.value_icons
|
||||||
|
|
||||||
|
def remove_value_icon(self, key, value, file_name):
|
||||||
|
self.value_icons = self.prefs['tags_browser_value_icons']
|
||||||
|
self.value_icons.get(key).pop(value, None)
|
||||||
|
self.prefs['tags_browser_value_icons'] =self.value_icons
|
||||||
|
if file_name is not None:
|
||||||
|
path = os.path.join(config_dir, 'tb_icons', file_name)
|
||||||
|
try:
|
||||||
|
os.remove(path)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def set_custom_category_icon(self, key, path):
|
def set_custom_category_icon(self, key, path):
|
||||||
d = self.prefs['tags_browser_category_icons']
|
d = self.prefs['tags_browser_category_icons']
|
||||||
if path:
|
if path:
|
||||||
@ -403,8 +478,8 @@ class TagsModel(QAbstractItemModel): # {{{
|
|||||||
os.remove(path)
|
os.remove(path)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
del d[key]
|
d.pop(key, None)
|
||||||
del self.category_custom_icons[key]
|
self.category_custom_icons.pop(key, None)
|
||||||
self.prefs['tags_browser_category_icons'] = d
|
self.prefs['tags_browser_category_icons'] = d
|
||||||
|
|
||||||
def reread_collapse_model(self, state_map, rebuild=True):
|
def reread_collapse_model(self, state_map, rebuild=True):
|
||||||
@ -417,6 +492,7 @@ class TagsModel(QAbstractItemModel): # {{{
|
|||||||
|
|
||||||
def set_database(self, db, hidden_categories=None):
|
def set_database(self, db, hidden_categories=None):
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
|
self.value_icons = self.prefs['tags_browser_value_icons']
|
||||||
hidden_cats = db.new_api.pref('tag_browser_hidden_categories', None)
|
hidden_cats = db.new_api.pref('tag_browser_hidden_categories', None)
|
||||||
# migrate from config to db prefs
|
# migrate from config to db prefs
|
||||||
if hidden_cats is None:
|
if hidden_cats is None:
|
||||||
@ -514,8 +590,8 @@ class TagsModel(QAbstractItemModel): # {{{
|
|||||||
data = self._get_category_nodes(config['sort_tags_by'])
|
data = self._get_category_nodes(config['sort_tags_by'])
|
||||||
gst = self.db.new_api.pref('grouped_search_terms', {})
|
gst = self.db.new_api.pref('grouped_search_terms', {})
|
||||||
|
|
||||||
if self.category_custom_icons.get('search_folder:', None) is None:
|
if self.category_custom_icons.get('search_folder', None) is None:
|
||||||
self.category_custom_icons['search_folder:'] = QIcon.ic('folder_saved_search')
|
self.category_custom_icons['search_folder'] = QIcon.ic('folder_saved_search')
|
||||||
last_category_node = None
|
last_category_node = None
|
||||||
category_node_map = {}
|
category_node_map = {}
|
||||||
self.user_category_node_tree = {}
|
self.user_category_node_tree = {}
|
||||||
@ -1393,6 +1469,9 @@ class TagsModel(QAbstractItemModel): # {{{
|
|||||||
node = TagTreeItem(*args, **kwargs)
|
node = TagTreeItem(*args, **kwargs)
|
||||||
self.node_map[id(node)] = node
|
self.node_map[id(node)] = node
|
||||||
node.category_custom_icons = self.category_custom_icons
|
node.category_custom_icons = self.category_custom_icons
|
||||||
|
node.value_icons = self.value_icons
|
||||||
|
node.value_icon_cache = self.value_icon_cache
|
||||||
|
node.icon_config_dir = self.icon_config_dir
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def get_node(self, idx):
|
def get_node(self, idx):
|
||||||
|
@ -48,7 +48,8 @@ from calibre.ebooks.metadata import rating_to_stars
|
|||||||
from calibre.gui2 import FunctionDispatcher, choose_files, config, empty_index, gprefs, pixmap_to_data, question_dialog, rating_font, safe_open_url
|
from calibre.gui2 import FunctionDispatcher, choose_files, config, empty_index, gprefs, pixmap_to_data, question_dialog, rating_font, safe_open_url
|
||||||
from calibre.gui2.complete2 import EditWithComplete
|
from calibre.gui2.complete2 import EditWithComplete
|
||||||
from calibre.gui2.dialogs.edit_category_notes import EditNoteDialog
|
from calibre.gui2.dialogs.edit_category_notes import EditNoteDialog
|
||||||
from calibre.gui2.tag_browser.model import COUNT_ROLE, DRAG_IMAGE_ROLE, TAG_SEARCH_STATES, TagsModel, TagTreeItem, rename_only_in_vl_question
|
from calibre.gui2.tag_browser.model import COUNT_ROLE, DRAG_IMAGE_ROLE, TAG_SEARCH_STATES, TEMPLATE_ICON_INDICATOR
|
||||||
|
from calibre.gui2.tag_browser.model import TagsModel, TagTreeItem, rename_only_in_vl_question
|
||||||
from calibre.gui2.widgets import EnLineEdit
|
from calibre.gui2.widgets import EnLineEdit
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.utils.serialize import json_loads
|
from calibre.utils.serialize import json_loads
|
||||||
@ -634,6 +635,12 @@ class TagsView(QTreeView): # {{{
|
|||||||
key=None, index=None, search_state=None,
|
key=None, index=None, search_state=None,
|
||||||
is_first_letter=False, ignore_vl=False,
|
is_first_letter=False, ignore_vl=False,
|
||||||
extra=None):
|
extra=None):
|
||||||
|
'''
|
||||||
|
action: a string specifying the operation
|
||||||
|
category: the human readable label for the category
|
||||||
|
key: the lookup name for the category
|
||||||
|
index: the index of the item, if there is one.
|
||||||
|
'''
|
||||||
if not action:
|
if not action:
|
||||||
return
|
return
|
||||||
from calibre.gui2.ui import get_gui
|
from calibre.gui2.ui import get_gui
|
||||||
@ -654,8 +661,48 @@ class TagsView(QTreeView): # {{{
|
|||||||
self.db.prefs.set('tag_browser_dont_collapse', extra)
|
self.db.prefs.set('tag_browser_dont_collapse', extra)
|
||||||
self.recount()
|
self.recount()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# category is None if the user asked to specify a template
|
||||||
|
# index is None if the user clicked on a category (top level) node
|
||||||
|
# extra is a tuple: (icon_file_name: string or None, children: True or False)
|
||||||
|
def make_icon_name(key, index):
|
||||||
|
icon_file_name = 'icon_' + sanitize_file_name(key)
|
||||||
|
if index is not None:
|
||||||
|
item_val = self._model.get_node(index).tag.original_name
|
||||||
|
icon_file_name = icon_file_name + '@@' + sanitize_file_name(item_val)
|
||||||
|
else:
|
||||||
|
item_val = None
|
||||||
|
icon_file_name += '.png'
|
||||||
|
return item_val, icon_file_name
|
||||||
|
|
||||||
if action == 'set_icon':
|
if action == 'set_icon':
|
||||||
|
if category is None:
|
||||||
|
if index is not None:
|
||||||
|
current_item = self._model.get_node(index).tag.original_name
|
||||||
|
else:
|
||||||
|
current_item = _('No value available')
|
||||||
|
template = self._model.value_icons.get(key, {}).get(TEMPLATE_ICON_INDICATOR, ('', False))[0]
|
||||||
|
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
||||||
|
from calibre.utils.formatter import EvalFormatter
|
||||||
|
d = TemplateDialog(parent=self, text=template,
|
||||||
|
mi={'title': key, 'category': key, 'value': current_item},
|
||||||
|
doing_emblem=True,
|
||||||
|
# fm=None, color_field=None, icon_field_key=None,
|
||||||
|
# icon_rule_kind=None, text_is_placeholder=False,
|
||||||
|
# dialog_is_st_editor=False,
|
||||||
|
# global_vars=None, all_functions=None, builtin_functions=None,
|
||||||
|
# python_context_object=None, dialog_number=None,
|
||||||
|
formatter=EvalFormatter, icon_dir='tb_icons/template_icons')
|
||||||
|
if d.exec():
|
||||||
|
self._model.set_value_icon(key, TEMPLATE_ICON_INDICATOR, d.rule[2], False)
|
||||||
|
self.recount()
|
||||||
|
return
|
||||||
|
(icon_file_name, for_children) = extra
|
||||||
|
item_val, desired_file_name = make_icon_name(key, index)
|
||||||
|
if icon_file_name is None:
|
||||||
|
# User wants to specify a specific icon
|
||||||
try:
|
try:
|
||||||
|
icon_file_name = desired_file_name
|
||||||
path = choose_files(self, 'choose_category_icon',
|
path = choose_files(self, 'choose_category_icon',
|
||||||
_('Change icon for: %s')%key, filters=[
|
_('Change icon for: %s')%key, filters=[
|
||||||
('Images', ['png', 'gif', 'jpg', 'jpeg'])],
|
('Images', ['png', 'gif', 'jpg', 'jpeg'])],
|
||||||
@ -666,16 +713,27 @@ class TagsView(QTreeView): # {{{
|
|||||||
d = os.path.join(config_dir, 'tb_icons')
|
d = os.path.join(config_dir, 'tb_icons')
|
||||||
if not os.path.exists(d):
|
if not os.path.exists(d):
|
||||||
os.makedirs(d)
|
os.makedirs(d)
|
||||||
with open(os.path.join(d, 'icon_' + sanitize_file_name(key)+'.png'), 'wb') as f:
|
with open(os.path.join(d, icon_file_name), 'wb') as f:
|
||||||
f.write(pixmap_to_data(p, format='PNG'))
|
f.write(pixmap_to_data(p, format='PNG'))
|
||||||
path = os.path.basename(f.name)
|
|
||||||
self._model.set_custom_category_icon(key, str(path))
|
|
||||||
self.recount()
|
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
else:
|
||||||
|
# Already have an icon. User wants to change whether it applies to children
|
||||||
|
icon_file_name = desired_file_name
|
||||||
|
if index is None: # category icon
|
||||||
|
self._model.set_custom_category_icon(key, str(icon_file_name))
|
||||||
|
self.recount()
|
||||||
|
else: # value icon
|
||||||
|
self._model.set_value_icon(key, item_val, icon_file_name, bool(for_children))
|
||||||
|
self.recount()
|
||||||
return
|
return
|
||||||
if action == 'clear_icon':
|
if action == 'clear_icon':
|
||||||
|
if index is not None:
|
||||||
|
val, icon_name = make_icon_name(key, index)
|
||||||
|
self._model.remove_value_icon(key, val, icon_name)
|
||||||
|
else:
|
||||||
self._model.set_custom_category_icon(key, None)
|
self._model.set_custom_category_icon(key, None)
|
||||||
|
self._model.remove_value_icon(key, TEMPLATE_ICON_INDICATOR, None)
|
||||||
self.recount()
|
self.recount()
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -1163,9 +1221,9 @@ class TagsView(QTreeView): # {{{
|
|||||||
category=key)).setIcon(QIcon.ic('minus.png'))
|
category=key)).setIcon(QIcon.ic('minus.png'))
|
||||||
add_show_hidden_categories()
|
add_show_hidden_categories()
|
||||||
|
|
||||||
if tag is None:
|
|
||||||
cm = self.context_menu
|
cm = self.context_menu
|
||||||
cm.addSeparator()
|
cm.addSeparator()
|
||||||
|
if tag is None:
|
||||||
acategory = category.replace('&', '&&')
|
acategory = category.replace('&', '&&')
|
||||||
sm = cm.addAction(_('Change {} category icon').format(acategory),
|
sm = cm.addAction(_('Change {} category icon').format(acategory),
|
||||||
partial(self.context_menu_handler, action='set_icon',
|
partial(self.context_menu_handler, action='set_icon',
|
||||||
@ -1178,13 +1236,56 @@ class TagsView(QTreeView): # {{{
|
|||||||
if key == 'search' and 'search' in self.db.new_api.pref('categories_using_hierarchy', ()):
|
if key == 'search' and 'search' in self.db.new_api.pref('categories_using_hierarchy', ()):
|
||||||
sm = cm.addAction(_('Change Saved searches folder icon'),
|
sm = cm.addAction(_('Change Saved searches folder icon'),
|
||||||
partial(self.context_menu_handler, action='set_icon',
|
partial(self.context_menu_handler, action='set_icon',
|
||||||
key='search_folder:', category=_('Saved searches folder')))
|
key='search_folder', category=_('Saved searches folder')))
|
||||||
sm.setIcon(QIcon.ic('icon_choose.png'))
|
sm.setIcon(QIcon.ic('icon_choose.png'))
|
||||||
sm = cm.addAction(_('Restore Saved searches folder default icon'),
|
sm = cm.addAction(_('Restore Saved searches folder default icon'),
|
||||||
partial(self.context_menu_handler, action='clear_icon',
|
partial(self.context_menu_handler, action='clear_icon',
|
||||||
key='search_folder:', category=_('Saved searches folder')))
|
key='search_folder', category=_('Saved searches folder')))
|
||||||
sm.setIcon(QIcon.ic('edit-clear.png'))
|
sm.setIcon(QIcon.ic('edit-clear.png'))
|
||||||
|
if key not in ('search', 'formats') and not key.startswith('@'):
|
||||||
|
im = cm.addMenu(_('Manage value icons'))
|
||||||
|
def get_rule_data(tag, key):
|
||||||
|
if tag is None:
|
||||||
|
return (None, None, None)
|
||||||
|
name = tag.original_name
|
||||||
|
cat_rules = self._model.value_icons.get(key, {})
|
||||||
|
icon_name, for_child = cat_rules.get(name, (None, None))
|
||||||
|
return (name, icon_name, for_child)
|
||||||
|
|
||||||
|
name,icon_name,for_child = get_rule_data(tag, key)
|
||||||
|
if name is not None:
|
||||||
|
im.addSection(_('Current value: {}').format(name))
|
||||||
|
else:
|
||||||
|
im.addSection(_('No value available'))
|
||||||
|
im.addSeparator
|
||||||
|
ma = im.addAction(_('Choose an icon for this value but not its children'),
|
||||||
|
partial(self.context_menu_handler, action='set_icon',
|
||||||
|
key=key, index=index, category=category, extra=(None, False)))
|
||||||
|
ma.setEnabled(name is not None)
|
||||||
|
ma = im.addAction(_('Choose an icon for this value and children'),
|
||||||
|
partial(self.context_menu_handler, action='set_icon',
|
||||||
|
key=key, index=index, category=category, extra=(None, True)))
|
||||||
|
ma.setEnabled(name is not None)
|
||||||
|
im.addSeparator()
|
||||||
|
ma = im.addAction(_('Use the existing icon for this value but not its children'),
|
||||||
|
partial(self.context_menu_handler, action='set_icon',
|
||||||
|
key=key, index=index, category=category, extra=(icon_name, False)))
|
||||||
|
ma.setEnabled(icon_name is not None and for_child)
|
||||||
|
ma = im.addAction(_('Use the existing icon for this value and its children'),
|
||||||
|
partial(self.context_menu_handler, action='set_icon',
|
||||||
|
key=key, index=index, category=category, extra=(icon_name, True)))
|
||||||
|
ma.setEnabled(icon_name is not None and not for_child)
|
||||||
|
im.addAction(_('Use the default icon for this value'),
|
||||||
|
partial(self.context_menu_handler, action='clear_icon',
|
||||||
|
key=key, index=index, category=category))
|
||||||
|
im.addSection(_('Defaults'))
|
||||||
|
im.addAction(_('Use a template to choose the default value icon'),
|
||||||
|
partial(self.context_menu_handler, action='set_icon',
|
||||||
|
key=key, index=index, category=None, extra=(None, None)))
|
||||||
|
im.addAction(_('Use the category icon for the default value icon'),
|
||||||
|
partial(self.context_menu_handler, action='clear_icon',
|
||||||
|
key=key, index=None, category=category))
|
||||||
|
im.addSeparator()
|
||||||
# Always show the User categories editor
|
# Always show the User categories editor
|
||||||
self.context_menu.addSeparator()
|
self.context_menu.addSeparator()
|
||||||
if key.startswith('@') and \
|
if key.startswith('@') and \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user