mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Book details panel: Make series and tags clickable. Clicking on them will search the calibre library for all books in the same series/ having the same tag
Fixes #1341297 [[Enhancement] make series name (in book details) a link/clickable](https://bugs.launchpad.net/calibre/+bug/1341297)
This commit is contained in:
parent
d60337d4ab
commit
689d9f6e60
@ -31,12 +31,12 @@ table.fields td.title {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The HTML that this styleshhet applies to looks like this:
|
The HTML that this stylesheet applies to looks like this:
|
||||||
|
|
||||||
<table class="fields">
|
<table class="fields">
|
||||||
<tr id="formats" class="datatype_text"><td class="title">Formats:</td><td><a href="format:572:EPUB">EPUB</a>, <a href="format:572:LIT">LIT</a></td></tr>
|
<tr id="formats" class="datatype_text"><td class="title">Formats:</td><td><a href="format:572:EPUB">EPUB</a>, <a href="format:572:LIT">LIT</a></td></tr>
|
||||||
<tr id="series" class="datatype_series"><td class="title">Series:</td><td>Book II of <span class="series_name">The Sea Beggars</span></td></tr>
|
<tr id="series" class="datatype_series"><td class="title">Series:</td><td>Book II of <a href="..."><span class="series_name">The Sea Beggars</span></a></td></tr>
|
||||||
<tr id="tags" class="datatype_text"><td class="title">Tags:</td><td>Fantasy, Fiction</td></tr>
|
<tr id="tags" class="datatype_text"><td class="title">Tags:</td><td><a href="...">Fantasy</a>, <a href="...">Fiction</a></td></tr>
|
||||||
<tr id="path" class="datatype_text"><td class="title">Path:</td><td><a href="path:572" title="/home/kovid/test library/Paul Kearney/This Forsaken Earth (572)">Click to open</a></td></tr>
|
<tr id="path" class="datatype_text"><td class="title">Path:</td><td><a href="path:572" title="/home/kovid/test library/Paul Kearney/This Forsaken Earth (572)">Click to open</a></td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from binascii import hexlify
|
||||||
|
|
||||||
from calibre import prepare_string_for_xml, force_unicode
|
from calibre import prepare_string_for_xml, force_unicode
|
||||||
from calibre.ebooks.metadata import fmt_sidx
|
from calibre.ebooks.metadata import fmt_sidx
|
||||||
@ -45,6 +46,10 @@ def get_field_list(mi):
|
|||||||
for field in sorted(displayable_field_keys(mi), key=partial(field_sort, mi)):
|
for field in sorted(displayable_field_keys(mi), key=partial(field_sort, mi)):
|
||||||
yield field, True
|
yield field, True
|
||||||
|
|
||||||
|
def search_href(search_term, value):
|
||||||
|
search = '%s:"=%s"' % (search_term, value.replace('"', '\\"'))
|
||||||
|
return prepare_string_for_xml('search:' + hexlify(search.encode('utf-8')), True)
|
||||||
|
|
||||||
def mi_to_html(mi, field_list=None, default_author_link=None, use_roman_numbers=True, rating_font='Liberation Serif'):
|
def mi_to_html(mi, field_list=None, default_author_link=None, use_roman_numbers=True, rating_font='Liberation Serif'):
|
||||||
if field_list is None:
|
if field_list is None:
|
||||||
field_list = get_field_list(mi)
|
field_list = get_field_list(mi)
|
||||||
@ -166,13 +171,36 @@ def mi_to_html(mi, field_list=None, default_author_link=None, use_roman_numbers=
|
|||||||
sidx = mi.get(field+'_index')
|
sidx = mi.get(field+'_index')
|
||||||
if sidx is None:
|
if sidx is None:
|
||||||
sidx = 1.0
|
sidx = 1.0
|
||||||
val = _('Book %(sidx)s of <span class="series_name">%(series)s</span>')%dict(
|
try:
|
||||||
sidx=fmt_sidx(sidx, use_roman=use_roman_numbers),
|
st = metadata['search_terms'][0]
|
||||||
series=p(getattr(mi, field)))
|
except Exception:
|
||||||
|
st = field
|
||||||
|
series = getattr(mi, field)
|
||||||
|
val = _(
|
||||||
|
'Book %(sidx)s of <a href="%(href)s" title="%(tt)s">'
|
||||||
|
'<span class="%(cls)s">%(series)s</span></a>') % dict(
|
||||||
|
sidx=fmt_sidx(sidx, use_roman=use_roman_numbers), cls="series_name",
|
||||||
|
series=p(series), href=search_href(st, series),
|
||||||
|
tt=p(_('Click to see books in this series')))
|
||||||
elif metadata['datatype'] == 'datetime':
|
elif metadata['datatype'] == 'datetime':
|
||||||
aval = getattr(mi, field)
|
aval = getattr(mi, field)
|
||||||
if is_date_undefined(aval):
|
if is_date_undefined(aval):
|
||||||
continue
|
continue
|
||||||
|
elif metadata['datatype'] == 'text' and metadata['is_multiple']:
|
||||||
|
try:
|
||||||
|
st = metadata['search_terms'][0]
|
||||||
|
except Exception:
|
||||||
|
st = field
|
||||||
|
links = ['<a href="%s" title="%s">%s</a>' % (
|
||||||
|
search_href(st, x), _('Click to see books with {0}: {1}').format(metadata['name'], x), x)
|
||||||
|
for x in mi.get(field)]
|
||||||
|
val = metadata['is_multiple']['list_to_ui'].join(links)
|
||||||
|
elif metadata['datatype'] == 'enumeration':
|
||||||
|
try:
|
||||||
|
st = metadata['search_terms'][0]
|
||||||
|
except Exception:
|
||||||
|
st = field
|
||||||
|
val = '<a href="%s" title="%s">%s</a>' % (search_href(st, val), _('Click to see books with {0}: {1}').format(metadata['name'], val), val)
|
||||||
|
|
||||||
ans.append((field, row % (name, val)))
|
ans.append((field, row % (name, val)))
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from binascii import unhexlify
|
||||||
|
|
||||||
from PyQt4.Qt import (QPixmap, QSize, QWidget, Qt, pyqtSignal, QUrl, QIcon,
|
from PyQt4.Qt import (QPixmap, QSize, QWidget, Qt, pyqtSignal, QUrl, QIcon,
|
||||||
QPropertyAnimation, QEasingCurve, QApplication, QFontInfo, QAction,
|
QPropertyAnimation, QEasingCurve, QApplication, QFontInfo, QAction,
|
||||||
QSizePolicy, QPainter, QRect, pyqtProperty, QLayout, QPalette, QMenu,
|
QSizePolicy, QPainter, QRect, pyqtProperty, QLayout, QPalette, QMenu,
|
||||||
@ -500,6 +502,7 @@ class BookDetails(QWidget): # {{{
|
|||||||
show_book_info = pyqtSignal()
|
show_book_info = pyqtSignal()
|
||||||
open_containing_folder = pyqtSignal(int)
|
open_containing_folder = pyqtSignal(int)
|
||||||
view_specific_format = pyqtSignal(int, object)
|
view_specific_format = pyqtSignal(int, object)
|
||||||
|
search_requested = pyqtSignal(object)
|
||||||
remove_specific_format = pyqtSignal(int, object)
|
remove_specific_format = pyqtSignal(int, object)
|
||||||
save_specific_format = pyqtSignal(int, object)
|
save_specific_format = pyqtSignal(int, object)
|
||||||
restore_specific_format = pyqtSignal(int, object)
|
restore_specific_format = pyqtSignal(int, object)
|
||||||
@ -581,7 +584,7 @@ class BookDetails(QWidget): # {{{
|
|||||||
self.setCursor(Qt.PointingHandCursor)
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
|
|
||||||
def handle_click(self, link):
|
def handle_click(self, link):
|
||||||
typ, _, val = link.partition(':')
|
typ, val = link.partition(':')[0::2]
|
||||||
if typ == 'path':
|
if typ == 'path':
|
||||||
self.open_containing_folder.emit(int(val))
|
self.open_containing_folder.emit(int(val))
|
||||||
elif typ == 'format':
|
elif typ == 'format':
|
||||||
@ -589,6 +592,8 @@ class BookDetails(QWidget): # {{{
|
|||||||
self.view_specific_format.emit(int(id_), fmt)
|
self.view_specific_format.emit(int(id_), fmt)
|
||||||
elif typ == 'devpath':
|
elif typ == 'devpath':
|
||||||
self.view_device_book.emit(val)
|
self.view_device_book.emit(val)
|
||||||
|
elif typ == 'search':
|
||||||
|
self.search_requested.emit(unhexlify(val).decode('utf-8'))
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
open_url(QUrl(link, QUrl.TolerantMode))
|
open_url(QUrl(link, QUrl.TolerantMode))
|
||||||
|
@ -445,6 +445,7 @@ class LayoutMixin(object): # {{{
|
|||||||
type=Qt.QueuedConnection)
|
type=Qt.QueuedConnection)
|
||||||
self.book_details.open_containing_folder.connect(self.iactions['View'].view_folder_for_id)
|
self.book_details.open_containing_folder.connect(self.iactions['View'].view_folder_for_id)
|
||||||
self.book_details.view_specific_format.connect(self.iactions['View'].view_format_by_id)
|
self.book_details.view_specific_format.connect(self.iactions['View'].view_format_by_id)
|
||||||
|
self.book_details.search_requested.connect(self.search.set_search_string)
|
||||||
self.book_details.remove_specific_format.connect(
|
self.book_details.remove_specific_format.connect(
|
||||||
self.iactions['Remove Books'].remove_format_by_id)
|
self.iactions['Remove Books'].remove_format_by_id)
|
||||||
self.book_details.save_specific_format.connect(
|
self.book_details.save_specific_format.connect(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user