diff --git a/src/calibre/ebooks/metadata/book/render.py b/src/calibre/ebooks/metadata/book/render.py
index c4912894f2..5886b91729 100644
--- a/src/calibre/ebooks/metadata/book/render.py
+++ b/src/calibre/ebooks/metadata/book/render.py
@@ -60,9 +60,9 @@ def search_action(search_term, value, **k):
return action('search', term=search_term, value=value, **k)
-def search_action_with_data(search_term, value, book_id, field=None):
+def search_action_with_data(search_term, value, book_id, field=None, **k):
field = field or search_term
- return search_action(search_term, value, field=field, book_id=book_id)
+ return search_action(search_term, value, field=field, book_id=book_id, **k)
DEFAULT_AUTHOR_LINK = 'search-{}'.format(DEFAULT_AUTHOR_SOURCE)
@@ -281,6 +281,10 @@ def mi_to_html(
aval = getattr(mi, field)
if is_date_undefined(aval):
continue
+ val = '%s' % (
+ search_action_with_data(field, str(aval), book_id, None, original_value=val), a(
+ _('Click to see books with {0}: {1} (derived from {2})').format(
+ metadata['name'] or field, aval, val)), val)
elif metadata['datatype'] == 'text' and metadata['is_multiple']:
try:
st = metadata['search_terms'][0]
@@ -303,6 +307,27 @@ def mi_to_html(
val = '%s' % (
search_action_with_data(st, val, book_id, field), a(
_('Click to see books with {0}: {1}').format(metadata['name'] or field, val)), p(val))
+ elif metadata['datatype'] == 'bool':
+ val = '%s' % (
+ search_action_with_data(field, val, book_id, None), a(
+ _('Click to see books with {0}: {1}').format(metadata['name'] or field, val)), val)
+ else:
+ try:
+ aval = str(getattr(mi, field))
+ if not aval:
+ continue
+ if val == aval:
+ val = '%s' % (
+ search_action_with_data(field, str(aval), book_id, None, original_value=val), a(
+ _('Click to see books with {0}: {1}').format(metadata['name'] or field, val)), val)
+ else:
+ val = '%s' % (
+ search_action_with_data(field, str(aval), book_id, None, original_value=val), a(
+ _('Click to see books with {0}: {1} (derived from {2})').format(
+ metadata['name'] or field, aval, val)), val)
+ except Exception:
+ import traceback
+ traceback.print_exc()
ans.append((field, row % (name, val)))
diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py
index c5177b022b..3239a16484 100644
--- a/src/calibre/gui2/book_details.py
+++ b/src/calibre/gui2/book_details.py
@@ -92,6 +92,13 @@ def is_category(field):
return field in {x[0] for x in find_categories(fm) if fm.is_custom_field(x[0])}
+def is_boolean(field):
+ from calibre.gui2.ui import get_gui
+ gui = get_gui()
+ fm = gui.current_db.field_metadata
+ return fm.get(field, {}).get('datatype') == 'bool'
+
+
def escape_for_menu(x):
return x.replace('&', '&&')
@@ -365,10 +372,18 @@ def add_item_specific_entries(menu, data, book_info, copy_menu, search_menu):
remove_value = langnames_to_langcodes((value,)).get(value, 'Unknown')
init_find_in_tag_browser(search_menu, find_action, field, value)
init_find_in_grouped_search(search_menu, field, value, book_info)
+ else:
+ v = data.get('original_value') or data.get('value')
+ copy_menu.addAction(QIcon(I('edit-copy.png')), _('The text: {}').format(v),
+ lambda: QApplication.instance().clipboard().setText(v))
ac = book_info.remove_item_action
ac.data = (field, remove_value, book_id)
ac.setText(_('Remove %s from this book') % escape_for_menu(value))
menu.addAction(ac)
+ else:
+ v = data.get('original_value') or data.get('value')
+ copy_menu.addAction(QIcon(I('edit-copy.png')), _('The text: {}').format(v),
+ lambda: QApplication.instance().clipboard().setText(v))
return search_internet_added
@@ -1034,8 +1049,9 @@ class BookDetails(QWidget): # {{{
if mods & Qt.KeyboardModifier.ControlModifier:
append = 'AND' if mods & Qt.KeyboardModifier.ShiftModifier else 'OR'
+ fmt = '{}:{}' if is_boolean(field) else '{}:"={}"'
self.search_requested.emit(
- '{}:"={}"'.format(field, val.replace('"', '\\"')),
+ fmt.format(field, val.replace('"', '\\"')),
append
)
diff --git a/src/pyj/book_list/book_details.pyj b/src/pyj/book_list/book_details.pyj
index a2883de25f..04cbb899bb 100644
--- a/src/pyj/book_list/book_details.pyj
+++ b/src/pyj/book_list/book_details.pyj
@@ -76,8 +76,8 @@ def field_sorter(field_metadata):
return lvl + (fm.name or 'zzzzz')
-def href_for_search(name, val):
- query = '{}:"={}"'.format(name, str.replace(val, '"', r'\"'))
+def href_for_search(name, val, use_quotes=True):
+ query = ('{}:"={}"' if use_quotes else '{}:{}').format(name, str.replace(val, '"', r'\"'))
q = search_query_for(query)
return query_as_href(q)
@@ -183,7 +183,7 @@ def render_metadata(mi, table, book_id, iframe_css): # {{{
fields = filter(allowed_fields, fields)
comments = v'[]'
- def add_row(name, val, is_searchable=False, is_html=False, join=None, search_text=None):
+ def add_row(name, val, is_searchable=False, is_html=False, join=None, search_text=None, use_quotes=True):
if val is undefined or val is None:
return
def add_val(v):
@@ -194,7 +194,7 @@ def render_metadata(mi, table, book_id, iframe_css): # {{{
table.lastChild.lastChild.appendChild(E.a(
v,
title=_('Click to see books with {0}: {1}').format(name, text_rep), class_='blue-link',
- href=href_for_search(is_searchable, text_rep)
+ href=href_for_search(is_searchable, text_rep, use_quotes=use_quotes)
))
else:
if v.appendChild:
@@ -302,9 +302,9 @@ def render_metadata(mi, table, book_id, iframe_css): # {{{
def process_datetime(field, fm, name, val):
if val:
fmt = interface_data['gui_' + field + '_display_format'] or (fm['display'] or {}).date_format
- val = format_date(val, fmt)
- if val:
- add_row(name, val)
+ formatted_val = format_date(val, fmt)
+ if formatted_val:
+ add_row(name, formatted_val, is_searchable=field, search_text=val)
def process_series(field, fm, name, val):
if val:
@@ -382,15 +382,21 @@ def render_metadata(mi, table, book_id, iframe_css): # {{{
join = fm.is_multiple.list_to_ui if fm.is_multiple else None
add_row(name, val, join=join, is_searchable=field)
elif datatype is 'bool':
- add_row(name, _('Yes') if val else _('No'))
+ # We are missing checking if the pref bools_are_tristate is False
+ v = _('Yes') if val else ('' if val is undefined or val is None else _('No'))
+ if v:
+ add_row(name, v, is_searchable=field, use_quotes=False)
elif datatype is 'int' or datatype is 'float':
if val is not undefined and val is not None:
fmt = (fm.display or {}).number_format
if fmt:
- val = fmt.format(val)
+ formatted_val = fmt.format(val)
+ if formatted_val:
+ val += ''
+ add_row(name, formatted_val, is_searchable=field, search_text=val)
else:
val += ''
- add_row(name, val)
+ add_row(name, val, is_searchable=field, search_text=val)
for field in fields:
fm = field_metadata[field]