Enhancement: in book details, allow displaying is_multiple categories on separate lines.

Bug fix: is_multiple composite columns incorrectly depended on a space after the comma.
This commit is contained in:
Charles Haley 2022-09-17 14:30:24 +01:00
parent a6dc4bc09a
commit 8ad1087fd4
4 changed files with 83 additions and 15 deletions

View File

@ -89,7 +89,7 @@ def mi_to_html(
mi,
field_list=None, default_author_link=None, use_roman_numbers=True,
rating_font='Liberation Serif', rtl=False, comments_heading_pos='hide',
for_qt=False,
for_qt=False, vertical_fields=()
):
if field_list is None:
field_list = get_field_list(mi)
@ -108,6 +108,12 @@ def mi_to_html(
continue
if not metadata:
continue
def value_list(sep, vals):
if field in vertical_fields:
return '<br/>'.join(vals)
return sep.join(vals)
if field == 'sort':
field = 'title_sort'
if metadata['is_custom'] and metadata['datatype'] in {'bool', 'int', 'float'}:
@ -162,11 +168,11 @@ def mi_to_html(
_('Click to see books with {0}: {1}').format(metadata['name'], a(val)), p(val))
else:
all_vals = [v.strip()
for v in val.split(metadata['is_multiple']['list_to_ui']) if v.strip()]
for v in val.split(metadata['is_multiple']['cache_to_list']) if v.strip()]
links = ['<a href="{}" title="{}">{}</a>'.format(
search_action(field, x), _('Click to see books with {0}: {1}').format(
metadata['name'], a(x)), p(x)) for x in all_vals]
val = metadata['is_multiple']['list_to_ui'].join(links)
val = value_list(metadata['is_multiple']['list_to_ui'], links)
ans.append((field, row % (name, val)))
elif field == 'path':
if mi.path:
@ -199,7 +205,7 @@ def mi_to_html(
} for x in mi.formats)
fmts = ['<a title="{bpath}{sep}{fname}.{ext}" href="{action}">{fmt}</a>'.format(**x)
for x in data]
ans.append((field, row % (name, ', '.join(fmts))))
ans.append((field, row % (name, value_list(', ', fmts))))
elif field == 'identifiers':
urls = urls_from_identifiers(mi.identifiers, sort_results=True)
links = [
@ -207,7 +213,7 @@ def mi_to_html(
action('identifier', url=url, name=namel, id_type=id_typ, value=id_val, field='identifiers', book_id=book_id),
a(id_typ), a(id_val), p(namel))
for namel, id_typ, id_val, url in urls]
links = ', '.join(links)
links = value_list(', ', links)
if links:
ans.append((field, row % (_('Ids')+':', links)))
elif field == 'authors':
@ -233,14 +239,14 @@ def mi_to_html(
authors.append('<a title="%s" href="%s">%s</a>'%(a(lt), action('author', url=link, name=aut, title=lt), aut))
else:
authors.append(aut)
ans.append((field, row % (name, ' & '.join(authors))))
ans.append((field, row % (name, value_list(' & ', authors))))
elif field == 'languages':
if not mi.languages:
continue
names = filter(None, map(calibre_langcode_to_name, mi.languages))
names = ['<a href="{}" title="{}">{}</a>'.format(search_action_with_data('languages', n, book_id), _(
'Search calibre for books with the language: {}').format(n), n) for n in names]
ans.append((field, row % (name, ', '.join(names))))
ans.append((field, row % (name, value_list(', ', names))))
elif field == 'publisher':
if not mi.publisher:
continue
@ -303,7 +309,7 @@ def mi_to_html(
search_action_with_data(st, x, book_id, field), _('Click to see books with {0}: {1}').format(
metadata['name'] or field, a(x)), p(x))
for x in all_vals]
val = metadata['is_multiple']['list_to_ui'].join(links)
val = value_list(metadata['is_multiple']['list_to_ui'], links)
elif metadata['datatype'] == 'text' or metadata['datatype'] == 'enumeration':
# text/is_multiple handled above so no need to add the test to the if
try:

View File

@ -176,7 +176,9 @@ def init_find_in_grouped_search(menu, field, value, book_info):
def render_html(mi, vertical, widget, all_fields=False, render_data_func=None, pref_name='book_display_fields'): # {{{
func = render_data_func or render_data
from calibre.gui2.ui import get_gui
func = render_data_func or partial(render_data,
vertical_fields=get_gui().current_db.prefs.get('book_details_vertical_categories') or ())
try:
table, comment_fields = func(mi, all_fields=all_fields,
use_roman_numbers=config['use_roman_numerals_for_series_number'], pref_name=pref_name)
@ -233,13 +235,15 @@ def get_field_list(fm, use_defaults=False, pref_name='book_display_fields'):
return [(f, d) for f, d in fieldlist if f in available]
def render_data(mi, use_roman_numbers=True, all_fields=False, pref_name='book_display_fields'):
def render_data(mi, use_roman_numbers=True, all_fields=False, pref_name='book_display_fields',
vertical_fields=()):
field_list = get_field_list(getattr(mi, 'field_metadata', field_metadata), pref_name=pref_name)
field_list = [(x, all_fields or display) for x, display in field_list]
return mi_to_html(
mi, field_list=field_list, use_roman_numbers=use_roman_numbers, rtl=is_rtl(),
rating_font=rating_font(), default_author_link=default_author_link(),
comments_heading_pos=gprefs['book_details_comments_heading_pos'], for_qt=True
comments_heading_pos=gprefs['book_details_comments_heading_pos'], for_qt=True,
vertical_fields=vertical_fields
)
# }}}

View File

@ -470,6 +470,38 @@ class TBHierarchicalFields(DisplayedFields): # {{{
# }}}
class BDVerticalCats(DisplayedFields): # {{{
def __init__(self, db, parent=None, category_icons=None):
DisplayedFields.__init__(self, db, parent, category_icons=category_icons)
from calibre.gui2.ui import get_gui
self.gui = get_gui()
def initialize(self, use_defaults=False, pref_data_override=None):
fm = self.db.field_metadata
cats = [k for k in fm if fm[k]['name'] and fm[k]['is_multiple']]
ans = []
if use_defaults:
ans = [[k, False] for k in cats]
self.changed = True
elif pref_data_override:
ph = {k:v for k,v in pref_data_override}
ans = [[k, ph.get(k, False)] for k in cats]
self.changed = True
else:
vertical_cats = self.db.prefs.get('book_details_vertical_categories') or ()
for key in cats:
ans.append([key, key in vertical_cats])
self.beginResetModel()
self.fields = ans
self.endResetModel()
def commit(self):
if self.changed:
self.db.prefs.set('book_details_vertical_categories', [k for k,v in self.fields if v])
# }}}
class Background(QWidget): # {{{
def __init__(self, parent):
@ -706,6 +738,10 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.tb_hierarchy_import_layout_button.clicked.connect(partial(self.import_layout,
model=self.tb_hierarchical_cats_model))
self.bd_vertical_cats_model = BDVerticalCats(self.gui.current_db, self.tb_hierarchical_cats)
self.bd_vertical_cats_model.dataChanged.connect(self.changed_signal)
self.bd_vertical_cats.setModel(self.bd_vertical_cats_model)
self.fill_tb_search_order_box()
self.tb_search_order_up_button.clicked.connect(self.move_tb_search_up)
self.tb_search_order_down_button.clicked.connect(self.move_tb_search_down)
@ -959,6 +995,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.tb_display_model.initialize()
self.tb_categories_to_part_model.initialize()
self.tb_hierarchical_cats_model.initialize()
self.bd_vertical_cats_model.initialize()
db = self.gui.current_db
mi = []
try:
@ -1022,6 +1059,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.display_model.restore_defaults()
self.em_display_model.restore_defaults()
self.qv_display_model.restore_defaults()
self.bd_vertical_cats_model.restore_defaults()
gprefs.set('tb_search_order', gprefs.defaults['tb_search_order'])
self.edit_rules.clear()
self.icon_rules.clear()
@ -1097,6 +1135,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.tb_display_model.commit()
self.tb_categories_to_part_model.commit()
self.tb_hierarchical_cats_model.commit()
self.bd_vertical_cats_model.commit()
self.tb_search_order_commit()
self.edit_rules.commit(self.gui.current_db.prefs)
self.icon_rules.commit(self.gui.current_db.prefs)

View File

@ -733,14 +733,14 @@ A value of zero means calculate automatically.</string>
</layout>
</widget>
</item>
<item row="5" column="0" colspan="2">
<item row="5" column="0" colspan="3">
<widget class="QPushButton" name="id_links_button">
<property name="text">
<string>Create rules to convert &amp;identifiers into links</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<item row="3" column="0" colspan="3">
<widget class="QWidget" name="default_author_link_container" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
@ -750,6 +750,25 @@ A value of zero means calculate automatically.</string>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QGroupBox" name="groupBox3">
<property name="title">
<string>Categories on separate lines</string>
</property>
<property name="toolTip">
<string>&lt;p&gt;Check the box if you want the category's values displayed on separate lines instead of separated by commas&lt;/p&gt;</string>
</property>
<layout class="QVBoxLayout" name="vblayout_3">
<item>
<widget class="QListView" name="bd_vertical_cats">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="6" column="1">
<widget class="QGroupBox" name="groupBox">
<property name="title">
@ -811,7 +830,7 @@ A value of zero means calculate automatically.</string>
</layout>
</widget>
</item>
<item row="0" column="0" colspan="2">
<item row="0" column="0" colspan="3">
<layout class="QHBoxLayout">
<item>
<widget class="QCheckBox" name="opt_bd_show_cover">
@ -855,7 +874,7 @@ A value of zero means calculate automatically.</string>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_25">