mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Enhancement #1878940: allow restricting manage authors to the current virtual library.
Other improvements: - Get rid of sort buttons, instead sorting by clicking on the header - Support sorting by the author link column - Add an "edited" icon to any cell that has been changed
This commit is contained in:
parent
2ff634034e
commit
b2e108042b
@ -47,10 +47,11 @@ class EditColumnDelegate(QItemDelegate):
|
||||
|
||||
class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
|
||||
def __init__(self, parent, db, id_to_select, select_sort, select_link):
|
||||
def __init__(self, parent, db, id_to_select, select_sort, select_link, find_aut_func):
|
||||
QDialog.__init__(self, parent)
|
||||
Ui_EditAuthorsDialog.__init__(self)
|
||||
self.setupUi(self)
|
||||
|
||||
# Remove help icon on title bar
|
||||
icon = self.windowIcon()
|
||||
self.setWindowFlags(self.windowFlags()&(~Qt.WindowContextHelpButtonHint))
|
||||
@ -68,49 +69,15 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK'))
|
||||
self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel'))
|
||||
self.buttonBox.accepted.connect(self.accepted)
|
||||
self.apply_vl_checkbox.stateChanged.connect(self.use_vl_changed)
|
||||
|
||||
# Set up the column headings
|
||||
# Set up the heading for sorting
|
||||
self.table.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
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'))
|
||||
self.auth_col = QTableWidgetItem(_('Author'))
|
||||
self.table.setHorizontalHeaderItem(0, self.auth_col)
|
||||
self.auth_col.setIcon(self.blank_icon)
|
||||
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
|
||||
completion_data = []
|
||||
for row, (_id, author, sort, link) in enumerate(auts):
|
||||
author = author.replace('|', ',')
|
||||
self.authors[_id] = (author, sort, link)
|
||||
completion_data.append(author)
|
||||
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_to_select in (_id, author):
|
||||
if select_sort:
|
||||
select_item = sort
|
||||
elif select_link:
|
||||
select_item = link
|
||||
else:
|
||||
select_item = aut
|
||||
self.table.setItemDelegate(EditColumnDelegate(completion_data))
|
||||
|
||||
self.find_aut_func = find_aut_func
|
||||
self.table.resizeColumnsToContents()
|
||||
if self.table.columnWidth(2) < 200:
|
||||
self.table.setColumnWidth(2, 200)
|
||||
@ -118,28 +85,14 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
# set up the cellChanged signal only after the table is filled
|
||||
self.table.cellChanged.connect(self.cell_changed)
|
||||
|
||||
# set up sort buttons
|
||||
self.sort_by_author.setCheckable(True)
|
||||
self.sort_by_author.setChecked(False)
|
||||
self.sort_by_author.clicked.connect(self.do_sort_by_author)
|
||||
self.author_order = 1
|
||||
|
||||
self.sort_by_author_sort.clicked.connect(self.do_sort_by_author_sort)
|
||||
self.sort_by_author_sort.setCheckable(True)
|
||||
self.sort_by_author_sort.setChecked(True)
|
||||
self.author_sort_order = 1
|
||||
|
||||
self.recalc_author_sort.clicked.connect(self.do_recalc_author_sort)
|
||||
self.auth_sort_to_author.clicked.connect(self.do_auth_sort_to_author)
|
||||
|
||||
# Position on the desired item
|
||||
if select_item is not None:
|
||||
self.table.setCurrentItem(select_item)
|
||||
self.table.editItem(select_item)
|
||||
self.start_find_pos = select_item.row() * 2 + select_item.column()
|
||||
else:
|
||||
self.table.setCurrentCell(0, 0)
|
||||
self.start_find_pos = -1
|
||||
# Capture clicks on the horizontal header to sort the table columns
|
||||
hh = self.table.horizontalHeader()
|
||||
hh.setSectionsClickable(True)
|
||||
hh.sectionClicked.connect(self.header_clicked)
|
||||
hh.sectionResized.connect(self.table_column_resized)
|
||||
|
||||
# set up the search box
|
||||
self.find_box.initialize('manage_authors_search')
|
||||
@ -165,7 +118,101 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
|
||||
self.table.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
self.table.customContextMenuRequested.connect(self.show_context_menu)
|
||||
self.do_sort_by_author_sort()
|
||||
|
||||
# Fetch the data
|
||||
self.authors = {}
|
||||
self.original_authors = {}
|
||||
auts = db.new_api.author_data()
|
||||
self.completion_data = []
|
||||
for id_, v in auts.items():
|
||||
name = v['name']
|
||||
name = name.replace('|', ',')
|
||||
self.completion_data.append(name)
|
||||
self.authors[id_] = {'name': name, 'sort': v['sort'], 'link': v['link']}
|
||||
self.original_authors[id_] = {'name': name, 'sort': v['sort'],
|
||||
'link': v['link']}
|
||||
|
||||
self.edited_icon = QIcon(I('edit_input.png'))
|
||||
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'))
|
||||
self.last_sorted_by = 'sort'
|
||||
self.author_order = 1
|
||||
self.author_sort_order = 0
|
||||
self.link_order = 1
|
||||
self.show_table(id_to_select, select_sort, select_link)
|
||||
|
||||
def use_vl_changed(self, x):
|
||||
self.show_table(None, None, None)
|
||||
|
||||
def show_table(self, id_to_select, select_sort, select_link):
|
||||
auts_to_show = [t[0] for t in
|
||||
self.find_aut_func(use_virtual_library = self.apply_vl_checkbox.isChecked())]
|
||||
self.table.blockSignals(True)
|
||||
self.table.clear()
|
||||
self.table.setColumnCount(3)
|
||||
self.auth_col = QTableWidgetItem(_('Author'))
|
||||
self.table.setHorizontalHeaderItem(0, self.auth_col)
|
||||
self.aus_col = QTableWidgetItem(_('Author sort'))
|
||||
self.table.setHorizontalHeaderItem(1, self.aus_col)
|
||||
self.aul_col = QTableWidgetItem(_('Link'))
|
||||
self.table.setHorizontalHeaderItem(2, self.aul_col)
|
||||
|
||||
self.table.setRowCount(len(auts_to_show))
|
||||
select_item = None
|
||||
row = 0
|
||||
for id_, v in self.authors.items():
|
||||
if id_ not in auts_to_show:
|
||||
continue
|
||||
name, sort, link = (v['name'], v['sort'], v['link'])
|
||||
orig = self.original_authors[id_]
|
||||
name = name.replace('|', ',')
|
||||
|
||||
name_item = tableItem(name)
|
||||
name_item.setData(Qt.UserRole, id_)
|
||||
if name != orig['name']:
|
||||
name_item.setIcon(self.edited_icon)
|
||||
sort_item = tableItem(sort)
|
||||
if sort != orig['sort']:
|
||||
sort_item.setIcon(self.edited_icon)
|
||||
link_item = tableItem(link)
|
||||
if link != orig['link']:
|
||||
link_item.setIcon(self.edited_icon)
|
||||
self.table.setItem(row, 0, name_item)
|
||||
self.table.setItem(row, 1, sort_item)
|
||||
self.table.setItem(row, 2, link_item)
|
||||
|
||||
if id_to_select and id_to_select in (id_, name):
|
||||
print('id', id_to_select)
|
||||
if select_sort:
|
||||
select_item = sort_item
|
||||
elif select_link:
|
||||
select_item = link_item
|
||||
else:
|
||||
select_item = name_item
|
||||
row += 1
|
||||
|
||||
self.table.setItemDelegate(EditColumnDelegate(self.completion_data))
|
||||
|
||||
if self.last_sorted_by == 'sort':
|
||||
self.author_sort_order = 1 if self.author_sort_order == 0 else 0
|
||||
self.do_sort_by_author_sort()
|
||||
elif self.last_sorted_by == 'author':
|
||||
self.author_order = 1 if self.author_order == 0 else 0
|
||||
self.do_sort_by_author()
|
||||
else:
|
||||
self.link_order = 1 if self.link_order == 0 else 0
|
||||
self.do_sort_by_link()
|
||||
|
||||
# Position on the desired item
|
||||
if select_item is not None:
|
||||
self.table.setCurrentItem(select_item)
|
||||
self.table.editItem(select_item)
|
||||
self.start_find_pos = select_item.row() * 2 + select_item.column()
|
||||
else:
|
||||
self.table.setCurrentCell(0, 0)
|
||||
self.start_find_pos = -1
|
||||
self.table.blockSignals(False)
|
||||
|
||||
def save_state(self):
|
||||
self.table_column_widths = []
|
||||
@ -174,6 +221,11 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
gprefs['manage_authors_table_widths'] = self.table_column_widths
|
||||
gprefs['manage_authors_dialog_geometry'] = bytearray(self.saveGeometry())
|
||||
|
||||
def table_column_resized(self, col, old, new):
|
||||
self.table_column_widths = []
|
||||
for c in range(0, self.table.columnCount()):
|
||||
self.table_column_widths.append(self.table.columnWidth(c))
|
||||
|
||||
def resizeEvent(self, *args):
|
||||
QDialog.resizeEvent(self, *args)
|
||||
if self.table_column_widths is not None:
|
||||
@ -216,7 +268,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
ca.triggered.connect(self.copy_au_to_aus)
|
||||
m.addSeparator()
|
||||
ca = m.addAction(_("Show books by author in book list"))
|
||||
ca.triggered.connect(self.search)
|
||||
ca.triggered.connect(self.search_in_book_list)
|
||||
else:
|
||||
ca = m.addAction(_('Copy to author'))
|
||||
ca.triggered.connect(self.copy_aus_to_au)
|
||||
@ -224,7 +276,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
m.addMenu(case_menu)
|
||||
m.exec_(self.table.mapToGlobal(point))
|
||||
|
||||
def search(self):
|
||||
def search_in_book_list(self):
|
||||
from calibre.gui2.ui import get_gui
|
||||
row = self.context_item.row()
|
||||
get_gui().search.set_search_string(self.table.item(row, 0).text())
|
||||
@ -294,35 +346,48 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
self.not_found_label.setVisible(True)
|
||||
self.not_found_label_timer.start(1500)
|
||||
|
||||
def header_clicked(self, idx):
|
||||
if idx == 0:
|
||||
self.do_sort_by_author()
|
||||
elif idx == 1:
|
||||
self.do_sort_by_author_sort()
|
||||
else:
|
||||
self.do_sort_by_link()
|
||||
|
||||
def do_sort_by_author(self):
|
||||
self.last_sorted_by = 'author'
|
||||
self.author_order = 1 if self.author_order == 0 else 0
|
||||
self.table.sortByColumn(0, self.author_order)
|
||||
self.sort_by_author.setChecked(True)
|
||||
self.sort_by_author_sort.setChecked(False)
|
||||
self.auth_col.setIcon(self.down_arrow_icon if self.author_order
|
||||
else self.up_arrow_icon)
|
||||
self.aus_col.setIcon(self.blank_icon)
|
||||
self.aul_col.setIcon(self.blank_icon)
|
||||
|
||||
def do_sort_by_author_sort(self):
|
||||
self.last_sorted_by = 'sort'
|
||||
self.author_sort_order = 1 if self.author_sort_order == 0 else 0
|
||||
self.table.sortByColumn(1, self.author_sort_order)
|
||||
self.sort_by_author.setChecked(False)
|
||||
self.sort_by_author_sort.setChecked(True)
|
||||
self.aus_col.setIcon(self.down_arrow_icon if self.author_sort_order
|
||||
else self.up_arrow_icon)
|
||||
self.auth_col.setIcon(self.blank_icon)
|
||||
self.aul_col.setIcon(self.blank_icon)
|
||||
|
||||
def do_sort_by_link(self):
|
||||
self.last_sorted_by = 'link'
|
||||
self.link_order = 1 if self.link_order == 0 else 0
|
||||
self.table.sortByColumn(2, self.link_order)
|
||||
self.aul_col.setIcon(self.down_arrow_icon if self.link_order
|
||||
else self.up_arrow_icon)
|
||||
self.auth_col.setIcon(self.blank_icon)
|
||||
self.aus_col.setIcon(self.blank_icon)
|
||||
|
||||
def accepted(self):
|
||||
self.save_state()
|
||||
self.result = []
|
||||
for row in range(0,self.table.rowCount()):
|
||||
id = int(self.table.item(row, 0).data(Qt.UserRole))
|
||||
aut = unicode_type(self.table.item(row, 0).text()).strip()
|
||||
sort = unicode_type(self.table.item(row, 1).text()).strip()
|
||||
link = unicode_type(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))
|
||||
for id_, v in self.authors.items():
|
||||
orig = self.original_authors[id_]
|
||||
if orig != v:
|
||||
self.result.append((id_, orig['name'], v['name'], v['sort'], v['link']))
|
||||
|
||||
def do_recalc_author_sort(self):
|
||||
self.table.cellChanged.disconnect()
|
||||
@ -347,8 +412,10 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
self.table.cellChanged.connect(self.cell_changed)
|
||||
|
||||
def cell_changed(self, row, col):
|
||||
id_ = int(self.table.item(row, 0).data(Qt.UserRole))
|
||||
if col == 0:
|
||||
item = self.table.item(row, 0)
|
||||
item.setIcon(self.edited_icon)
|
||||
aut = unicode_type(item.text()).strip()
|
||||
aut_list = string_to_authors(aut)
|
||||
if len(aut_list) != 1:
|
||||
@ -356,10 +423,18 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
_('You cannot change an author to multiple authors.')).exec_()
|
||||
aut = ' % '.join(aut_list)
|
||||
self.table.item(row, 0).setText(aut)
|
||||
self.authors[id_]['name'] = aut
|
||||
c = self.table.item(row, 1)
|
||||
c.setText(author_to_author_sort(aut))
|
||||
txt = author_to_author_sort(aut)
|
||||
c.setText(txt)
|
||||
self.authors[id_]['sort'] = txt
|
||||
item = c
|
||||
else:
|
||||
item = self.table.item(row, col)
|
||||
item.setIcon(self.edited_icon)
|
||||
if col == 1:
|
||||
self.authors[id_]['sort'] = item.text()
|
||||
else:
|
||||
self.authors[id_]['link'] = item.text()
|
||||
self.table.setCurrentItem(item)
|
||||
self.table.scrollToItem(item)
|
||||
|
@ -62,6 +62,18 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="apply_vl_checkbox">
|
||||
<property name="toolTip">
|
||||
<string><p>Show authors only if they appear in the
|
||||
current Virtual library. Edits already done may be hidden but will
|
||||
not be forgotten.</p></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Show only available items in current Virtual library</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@ -79,20 +91,6 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="sort_by_author">
|
||||
<property name="text">
|
||||
<string>Sort by &author</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="sort_by_author_sort">
|
||||
<property name="text">
|
||||
<string>Sort by author &sort</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="recalc_author_sort">
|
||||
<property name="toolTip">
|
||||
|
@ -65,9 +65,9 @@
|
||||
<item row="1" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="apply_vl_checkbox">
|
||||
<property name="toolTip">
|
||||
<string><p>Show items in the Available items box only if they appear in the
|
||||
current Virtual library. Applied items not in the Virtual library will be marked
|
||||
"not on any book".</p></string>
|
||||
<string><p>Show items only if they appear in the
|
||||
current Virtual library. Edits already done may be hidden but will
|
||||
not be forgotten.</p></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Show only available items in current Virtual library</string>
|
||||
|
@ -231,6 +231,15 @@ class TagBrowserMixin(object): # {{{
|
||||
self.tags_view.recount()
|
||||
self.user_categories_edited()
|
||||
|
||||
def get_book_ids(self, use_virtual_library, db, category):
|
||||
book_ids = None if not use_virtual_library else self.tags_view.model().get_book_ids_to_use()
|
||||
data = db.new_api.get_categories(book_ids=book_ids)
|
||||
if category in data:
|
||||
result = [(t.id, t.original_name, t.count) for t in data[category] if t.count > 0]
|
||||
else:
|
||||
result = None
|
||||
return result
|
||||
|
||||
def do_tags_list_edit(self, tag, category):
|
||||
'''
|
||||
Open the 'manage_X' dialog where X == category. If tag is not None, the
|
||||
@ -238,23 +247,15 @@ class TagBrowserMixin(object): # {{{
|
||||
'''
|
||||
|
||||
db = self.current_db
|
||||
|
||||
def get_book_ids(use_virtual_library):
|
||||
book_ids = None if not use_virtual_library else self.tags_view.model().get_book_ids_to_use()
|
||||
data = db.new_api.get_categories(book_ids=book_ids)
|
||||
if category in data:
|
||||
result = [(t.id, t.original_name, t.count) for t in data[category] if t.count > 0]
|
||||
else:
|
||||
result = None
|
||||
return result
|
||||
|
||||
if category == 'series':
|
||||
key = lambda x:sort_key(title_sort(x))
|
||||
else:
|
||||
key = sort_key
|
||||
|
||||
d = TagListEditor(self, cat_name=db.field_metadata[category]['name'],
|
||||
tag_to_match=tag, get_book_ids=get_book_ids, sorter=key)
|
||||
tag_to_match=tag,
|
||||
get_book_ids=partial(self.get_book_ids, db=db, category=category),
|
||||
sorter=key)
|
||||
d.exec_()
|
||||
if d.result() == d.Accepted:
|
||||
to_rename = d.to_rename # dict of old id to new name
|
||||
@ -361,7 +362,8 @@ class TagBrowserMixin(object): # {{{
|
||||
'''
|
||||
|
||||
db = self.library_view.model().db
|
||||
editor = EditAuthorsDialog(parent, db, id_, select_sort, select_link)
|
||||
editor = EditAuthorsDialog(parent, db, id_, select_sort, select_link,
|
||||
partial(self.get_book_ids, db=db, category='authors'))
|
||||
if editor.exec_() == editor.Accepted:
|
||||
# Save and restore the current selections. Note that some changes
|
||||
# will cause sort orders to change, so don't bother with attempting
|
||||
|
Loading…
x
Reference in New Issue
Block a user