diff --git a/src/calibre/ebooks/metadata/book/__init__.py b/src/calibre/ebooks/metadata/book/__init__.py
index fae858aabd..50e7b916ee 100644
--- a/src/calibre/ebooks/metadata/book/__init__.py
+++ b/src/calibre/ebooks/metadata/book/__init__.py
@@ -86,6 +86,8 @@ CALIBRE_METADATA_FIELDS = frozenset([
# a dict of user category names, where the value is a list of item names
# from the book that are in that category
'user_categories',
+ # a dict of author to an associated hyperlink
+ 'author_link_map',
]
)
diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py
index 382cb6c5a2..c28c65f7c9 100644
--- a/src/calibre/ebooks/metadata/book/base.py
+++ b/src/calibre/ebooks/metadata/book/base.py
@@ -34,6 +34,7 @@ NULL_VALUES = {
'authors' : [_('Unknown')],
'title' : _('Unknown'),
'user_categories' : {},
+ 'author_link_map' : {},
'language' : 'und'
}
diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py
index 80fb84633b..c1cd2a739f 100644
--- a/src/calibre/ebooks/metadata/opf2.py
+++ b/src/calibre/ebooks/metadata/opf2.py
@@ -538,7 +538,8 @@ class OPF(object): # {{{
user_categories = MetadataField('user_categories', is_dc=False,
formatter=json.loads,
renderer=dump_user_categories)
-
+ author_link_map = MetadataField('author_link_map', is_dc=False,
+ formatter=json.loads)
def __init__(self, stream, basedir=os.getcwdu(), unquote_urls=True,
populate_spine=True):
@@ -1039,7 +1040,7 @@ class OPF(object): # {{{
for attr in ('title', 'authors', 'author_sort', 'title_sort',
'publisher', 'series', 'series_index', 'rating',
'isbn', 'tags', 'category', 'comments',
- 'pubdate', 'user_categories'):
+ 'pubdate', 'user_categories', 'author_link_map'):
val = getattr(mi, attr, None)
if val is not None and val != [] and val != (None, None):
setattr(self, attr, val)
@@ -1336,6 +1337,8 @@ def metadata_to_opf(mi, as_string=True):
for tag in mi.tags:
factory(DC('subject'), tag)
meta = lambda n, c: factory('meta', name='calibre:'+n, content=c)
+ if getattr(mi, 'author_link_map', None) is not None:
+ meta('author_link_map', json.dumps(mi.author_link_map))
if mi.series:
meta('series', mi.series)
if mi.series_index is not None:
diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py
index f94e179166..ef21773ae4 100644
--- a/src/calibre/gui2/book_details.py
+++ b/src/calibre/gui2/book_details.py
@@ -121,6 +121,16 @@ def render_data(mi, use_roman_numbers=True, all_fields=False):
if links:
ans.append((field, u'
%s | %s | '%(
_('Ids')+':', links)))
+ elif field == 'authors' and not isdevice:
+ authors = []
+ for aut in mi.authors:
+ if mi.author_link_map[aut]:
+ authors.append(u'%s' %
+ (mi.author_link_map[aut], aut))
+ else:
+ authors.append(aut)
+ ans.append((field, u'%s | %s | '%(name,
+ u' & '.join(authors))))
else:
val = mi.format_field(field)[-1]
if val is None:
diff --git a/src/calibre/gui2/dialogs/edit_authors_dialog.py b/src/calibre/gui2/dialogs/edit_authors_dialog.py
index a791551d27..1087c3cb82 100644
--- a/src/calibre/gui2/dialogs/edit_authors_dialog.py
+++ b/src/calibre/gui2/dialogs/edit_authors_dialog.py
@@ -33,7 +33,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
# Set up the column headings
self.table.setSelectionMode(QAbstractItemView.SingleSelection)
- self.table.setColumnCount(2)
+ self.table.setColumnCount(3)
self.down_arrow_icon = QIcon(I('arrow-down.png'))
self.up_arrow_icon = QIcon(I('arrow-up.png'))
self.blank_icon = QIcon(I('blank.png'))
@@ -43,26 +43,33 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
self.aus_col = QTableWidgetItem(_('Author sort'))
self.table.setHorizontalHeaderItem(1, self.aus_col)
self.aus_col.setIcon(self.up_arrow_icon)
+ self.aul_col = QTableWidgetItem(_('Link'))
+ self.table.setHorizontalHeaderItem(2, self.aul_col)
+ self.aus_col.setIcon(self.blank_icon)
# Add the data
self.authors = {}
auts = db.get_authors_with_ids()
self.table.setRowCount(len(auts))
select_item = None
- for row, (id, author, sort) in enumerate(auts):
+ for row, (id, author, sort, link) in enumerate(auts):
author = author.replace('|', ',')
- self.authors[id] = (author, sort)
+ self.authors[id] = (author, sort, link)
aut = tableItem(author)
aut.setData(Qt.UserRole, id)
sort = tableItem(sort)
+ link = tableItem(link)
self.table.setItem(row, 0, aut)
self.table.setItem(row, 1, sort)
+ self.table.setItem(row, 2, link)
if id == id_to_select:
if select_sort:
select_item = sort
else:
select_item = aut
self.table.resizeColumnsToContents()
+ if self.table.columnWidth(2) < 200:
+ self.table.setColumnWidth(2, 200)
# set up the cellChanged signal only after the table is filled
self.table.cellChanged.connect(self.cell_changed)
@@ -236,9 +243,10 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
id = self.table.item(row, 0).data(Qt.UserRole).toInt()[0]
aut = unicode(self.table.item(row, 0).text()).strip()
sort = unicode(self.table.item(row, 1).text()).strip()
- orig_aut,orig_sort = self.authors[id]
- if orig_aut != aut or orig_sort != sort:
- self.result.append((id, orig_aut, aut, sort))
+ link = unicode(self.table.item(row, 2).text()).strip()
+ orig_aut,orig_sort,orig_link = self.authors[id]
+ if orig_aut != aut or orig_sort != sort or orig_link != link:
+ self.result.append((id, orig_aut, aut, sort, link))
def do_recalc_author_sort(self):
self.table.cellChanged.disconnect()
@@ -276,6 +284,6 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
c.setText(author_to_author_sort(aut))
item = c
else:
- item = self.table.item(row, 1)
+ item = self.table.item(row, col)
self.table.setCurrentItem(item)
self.table.scrollToItem(item)
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index 40d6e2b6cf..d4aeada313 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -90,6 +90,7 @@ class BooksModel(QAbstractTableModel): # {{{
self.ids_to_highlight_set = set()
self.current_highlighted_idx = None
self.highlight_only = False
+ self.current_row = -1
self.colors = frozenset([unicode(c) for c in QColor.colorNames()])
self.read_config()
@@ -172,6 +173,7 @@ class BooksModel(QAbstractTableModel): # {{{
self.color_cache = defaultdict(dict)
for row in rows:
if row == current_row:
+ self.current_row = row
self.new_bookdisplay_data.emit(
self.get_book_display_info(row))
self.dataChanged.emit(self.index(row, 0), self.index(row,
@@ -329,6 +331,8 @@ class BooksModel(QAbstractTableModel): # {{{
def refresh(self, reset=True):
self.db.refresh(field=None)
self.resort(reset=reset)
+ if self.current_row >= 0:
+ self.new_bookdisplay_data.emit(self.get_book_display_info(self.current_row))
def reset(self):
self.color_cache = defaultdict(dict)
@@ -368,12 +372,14 @@ class BooksModel(QAbstractTableModel): # {{{
def current_changed(self, current, previous, emit_signal=True):
if current.isValid():
- idx = current.row()
+ self.current_row = idx = current.row()
data = self.get_book_display_info(idx)
if emit_signal:
self.new_bookdisplay_data.emit(data)
else:
return data
+ else:
+ self.current_row = -1
def get_book_info(self, index):
if isinstance(index, int):
diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py
index 3b8c27866c..21309a1592 100644
--- a/src/calibre/gui2/tag_view.py
+++ b/src/calibre/gui2/tag_view.py
@@ -2081,12 +2081,14 @@ class TagBrowserMixin(object): # {{{
editor = EditAuthorsDialog(parent, db, id, select_sort)
d = editor.exec_()
if d:
- for (id, old_author, new_author, new_sort) in editor.result:
+ for (id, old_author, new_author, new_sort, new_link) in editor.result:
if old_author != new_author:
# The id might change if the new author already exists
id = db.rename_author(id, new_author)
db.set_sort_field_for_author(id, unicode(new_sort),
commit=False, notify=False)
+ db.set_link_field_for_author(id, unicode(new_link),
+ commit=False, notify=False)
db.commit()
self.library_view.model().refresh()
self.tags_view.recount()
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 4c61438e35..9602f8ef1d 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -367,7 +367,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
'uuid',
'has_cover',
('au_map', 'authors', 'author',
- 'aum_sortconcat(link.id, authors.name, authors.sort)'),
+ 'aum_sortconcat(link.id, authors.name, authors.sort, authors.link)'),
'last_modified',
'(SELECT identifiers_concat(type, val) FROM identifiers WHERE identifiers.book=books.id) identifiers',
]
@@ -894,13 +894,17 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
aut_list = []
aum = []
aus = {}
- for (author, author_sort) in aut_list:
- aum.append(author.replace('|', ','))
- aus[author] = author_sort.replace('|', ',')
+ aul = {}
+ for (author, author_sort, link) in aut_list:
+ aut = author.replace('|', ',')
+ aum.append(aut)
+ aus[aut] = author_sort.replace('|', ',')
+ aul[aut] = link
mi.title = row[fm['title']]
mi.authors = aum
mi.author_sort = row[fm['author_sort']]
mi.author_sort_map = aus
+ mi.author_link_map = aul
mi.comments = row[fm['comments']]
mi.publisher = row[fm['publisher']]
mi.timestamp = row[fm['timestamp']]
@@ -2002,13 +2006,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
def authors_with_sort_strings(self, id, index_is_id=False):
id = id if index_is_id else self.id(id)
aut_strings = self.conn.get('''
- SELECT authors.id, authors.name, authors.sort
+ SELECT authors.id, authors.name, authors.sort, authors.link
FROM authors, books_authors_link as bl
WHERE bl.book=? and authors.id=bl.author
ORDER BY bl.id''', (id,))
result = []
- for (id_, author, sort,) in aut_strings:
- result.append((id_, author.replace('|', ','), sort))
+ for (id_, author, sort, link) in aut_strings:
+ result.append((id_, author.replace('|', ','), sort, link))
return result
# Given a book, return the author_sort string for authors of the book
@@ -2048,7 +2052,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
aum = self.authors_with_sort_strings(id_, index_is_id=True)
self.data.set(id_, self.FIELD_MAP['au_map'],
- ':#:'.join([':::'.join((au.replace(',', '|'), aus)) for (_, au, aus) in aum]),
+ ':#:'.join([':::'.join((au.replace(',', '|'), aus, aul))
+ for (_, au, aus, aul) in aum]),
row_is_id=True)
def _set_authors(self, id, authors, allow_case_change=False):
@@ -2399,7 +2404,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.conn.commit()
def get_authors_with_ids(self):
- result = self.conn.get('SELECT id,name,sort FROM authors')
+ result = self.conn.get('SELECT id,name,sort,link FROM authors')
if not result:
return []
return result
@@ -2410,6 +2415,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
(author,), all=False)
return result
+ def set_link_field_for_author(self, aid, link, commit=True, notify=False):
+ if not link:
+ link = ''
+ self.conn.execute('UPDATE authors SET link=? WHERE id=?', (link.strip(), aid))
+ if commit:
+ self.conn.commit()
+
def set_sort_field_for_author(self, old_id, new_sort, commit=True, notify=False):
self.conn.execute('UPDATE authors SET sort=? WHERE id=?', \
(new_sort.strip(), old_id))
diff --git a/src/calibre/library/schema_upgrades.py b/src/calibre/library/schema_upgrades.py
index 3fc9a2368a..3c64785178 100644
--- a/src/calibre/library/schema_upgrades.py
+++ b/src/calibre/library/schema_upgrades.py
@@ -600,4 +600,14 @@ class SchemaUpgrade(object):
with open(os.path.join(bdir, fname), 'wb') as f:
f.write(script)
+ def upgrade_version_20(self):
+ '''
+ Add a link column to the authors table.
+ '''
+
+ script = '''
+ ALTER TABLE authors ADD COLUMN link TEXT NON NULL DEFAULT "";
+ '''
+ self.conn.executescript(script)
+
diff --git a/src/calibre/library/sqlite.py b/src/calibre/library/sqlite.py
index 96874d2c27..a2a85806f5 100644
--- a/src/calibre/library/sqlite.py
+++ b/src/calibre/library/sqlite.py
@@ -144,9 +144,9 @@ class AumSortedConcatenate(object):
def __init__(self):
self.ans = {}
- def step(self, ndx, author, sort):
+ def step(self, ndx, author, sort, link):
if author is not None:
- self.ans[ndx] = author + ':::' + sort
+ self.ans[ndx] = ':::'.join((author, sort, link))
def finalize(self):
keys = self.ans.keys()
@@ -229,7 +229,7 @@ class DBThread(Thread):
load_c_extensions(self.conn)
self.conn.row_factory = sqlite.Row if self.row_factory else lambda cursor, row : list(row)
self.conn.create_aggregate('concat', 1, Concatenate)
- self.conn.create_aggregate('aum_sortconcat', 3, AumSortedConcatenate)
+ self.conn.create_aggregate('aum_sortconcat', 4, AumSortedConcatenate)
self.conn.create_collation('PYNOCASE', partial(pynocase,
encoding=encoding))
self.conn.create_function('title_sort', 1, title_sort)