mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Tag browser: Allow Manage Tags to open to a selected letter when clicking on a letter. Fixes #1880175 [[Enhancement] Allow Manage Tags to open to a selected letter vs selected tag](https://bugs.launchpad.net/calibre/+bug/1880175)
Merge branch 'master' of https://github.com/cbhaley/calibre
This commit is contained in:
commit
64d4b1c52d
@ -13,7 +13,8 @@ from calibre.ebooks.metadata import author_to_author_sort, string_to_authors
|
||||
from calibre.gui2 import error_dialog, gprefs
|
||||
from calibre.gui2.dialogs.edit_authors_dialog_ui import Ui_EditAuthorsDialog
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.utils.icu import sort_key, primary_contains, contains
|
||||
from calibre.utils.config_base import tweaks
|
||||
from calibre.utils.icu import sort_key, primary_contains, contains, primary_startswith
|
||||
from polyglot.builtins import unicode_type
|
||||
|
||||
QT_HIDDEN_CLEAR_ACTION = '_q_qlineeditclearaction'
|
||||
@ -50,7 +51,8 @@ class EditColumnDelegate(QItemDelegate):
|
||||
|
||||
class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
|
||||
def __init__(self, parent, db, id_to_select, select_sort, select_link, find_aut_func):
|
||||
def __init__(self, parent, db, id_to_select, select_sort, select_link,
|
||||
find_aut_func, is_first_letter=False):
|
||||
QDialog.__init__(self, parent)
|
||||
Ui_EditAuthorsDialog.__init__(self)
|
||||
self.setupUi(self)
|
||||
@ -153,19 +155,19 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
self.author_order = 1
|
||||
self.author_sort_order = 0
|
||||
self.link_order = 1
|
||||
self.show_table(id_to_select, select_sort, select_link)
|
||||
self.show_table(id_to_select, select_sort, select_link, is_first_letter)
|
||||
|
||||
def use_vl_changed(self, x):
|
||||
self.show_table(None, None, None)
|
||||
self.show_table(None, None, None, False)
|
||||
|
||||
def clear_filter(self):
|
||||
self.filter_box.setText('')
|
||||
self.show_table(None, None, None)
|
||||
self.show_table(None, None, None, False)
|
||||
|
||||
def do_filter(self):
|
||||
self.show_table(None, None, None)
|
||||
self.show_table(None, None, None, False)
|
||||
|
||||
def show_table(self, id_to_select, select_sort, select_link):
|
||||
def show_table(self, id_to_select, select_sort, select_link, is_first_letter):
|
||||
filter_text = icu_lower(unicode_type(self.filter_box.text()))
|
||||
auts_to_show = []
|
||||
for t in self.find_aut_func(use_virtual_library=self.apply_vl_checkbox.isChecked()):
|
||||
@ -176,7 +178,6 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
self.table.setColumnCount(3)
|
||||
|
||||
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:
|
||||
@ -198,14 +199,6 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
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):
|
||||
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))
|
||||
@ -222,10 +215,28 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
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()
|
||||
if id_to_select:
|
||||
select_item = None
|
||||
use_as = tweaks['categories_use_field_for_author_name']
|
||||
for row in range(0, len(auts_to_show)):
|
||||
name_item = self.table.item(row, 1) if use_as else self.table.item(row, 0)
|
||||
if is_first_letter:
|
||||
if primary_startswith(name_item.text(), id_to_select):
|
||||
select_item = self.table.item(row, 1)
|
||||
break
|
||||
elif id_to_select == self.table.item(row, 0).data(Qt.UserRole):
|
||||
if select_sort:
|
||||
select_item = self.table.item(row, 1)
|
||||
elif select_link:
|
||||
select_item = self.table.item(row, 2)
|
||||
else:
|
||||
select_item = name_item
|
||||
break
|
||||
if select_item:
|
||||
self.table.setCurrentItem(select_item)
|
||||
if select_sort or select_link:
|
||||
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
|
||||
|
@ -13,7 +13,7 @@ from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
from calibre.gui2.widgets import EnLineEdit
|
||||
from calibre.gui2 import question_dialog, error_dialog, gprefs
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.utils.icu import sort_key, contains, primary_contains
|
||||
from calibre.utils.icu import sort_key, contains, primary_contains, primary_startswith
|
||||
from polyglot.builtins import unicode_type
|
||||
|
||||
QT_HIDDEN_CLEAR_ACTION = '_q_qlineeditclearaction'
|
||||
@ -132,7 +132,8 @@ class EditColumnDelegate(QItemDelegate):
|
||||
|
||||
class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
|
||||
def __init__(self, window, cat_name, tag_to_match, get_book_ids, sorter):
|
||||
def __init__(self, window, cat_name, tag_to_match, get_book_ids, sorter,
|
||||
ttm_is_first_letter=False):
|
||||
QDialog.__init__(self, window)
|
||||
Ui_TagListEditor.__init__(self)
|
||||
self.setupUi(self)
|
||||
@ -188,6 +189,7 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
self.string_contains = contains
|
||||
|
||||
self.delete_button.clicked.connect(self.delete_tags)
|
||||
self.table.delete_pressed.connect(self.delete_pressed)
|
||||
self.rename_button.clicked.connect(self.rename_tag)
|
||||
self.undo_button.clicked.connect(self.undo_edit)
|
||||
self.table.itemDoubleClicked.connect(self._rename_tag)
|
||||
@ -242,11 +244,11 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
pass
|
||||
# Add the data
|
||||
self.search_item_row = -1
|
||||
self.fill_in_table(None, tag_to_match)
|
||||
self.fill_in_table(None, tag_to_match, ttm_is_first_letter)
|
||||
|
||||
def vl_box_changed(self):
|
||||
self.search_item_row = -1
|
||||
self.fill_in_table(None, None)
|
||||
self.fill_in_table(None, None, False)
|
||||
|
||||
def do_search(self):
|
||||
self.not_found_label.setVisible(False)
|
||||
@ -271,7 +273,7 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
self.search_item_row = -1
|
||||
self.search_box.setText('')
|
||||
|
||||
def fill_in_table(self, tags, tag_to_match):
|
||||
def fill_in_table(self, tags, tag_to_match, ttm_is_first_letter):
|
||||
data = self.get_book_ids(self.apply_vl_checkbox.isChecked())
|
||||
self.all_tags = {}
|
||||
filter_text = icu_lower(unicode_type(self.filter_box.text()))
|
||||
@ -310,8 +312,12 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
item.setText(tag)
|
||||
item.setFlags(item.flags() | Qt.ItemIsSelectable | Qt.ItemIsEditable)
|
||||
self.table.setItem(row, 0, item)
|
||||
if tag == tag_to_match:
|
||||
select_item = item
|
||||
if select_item is None:
|
||||
if ttm_is_first_letter:
|
||||
if primary_startswith(tag, tag_to_match):
|
||||
select_item = item
|
||||
elif tag == tag_to_match:
|
||||
select_item = item
|
||||
item = CountTableWidgetItem(self.all_tags[tag]['count'])
|
||||
# only the name column can be selected
|
||||
item.setFlags(item.flags() & ~(Qt.ItemIsSelectable|Qt.ItemIsEditable))
|
||||
@ -343,10 +349,10 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
|
||||
def clear_filter(self):
|
||||
self.filter_box.setText('')
|
||||
self.fill_in_table(None, None)
|
||||
self.fill_in_table(None, None, False)
|
||||
|
||||
def do_filter(self):
|
||||
self.fill_in_table(None, None)
|
||||
self.fill_in_table(None, None, False)
|
||||
|
||||
def table_column_resized(self, col, old, new):
|
||||
self.table_column_widths = []
|
||||
@ -452,6 +458,10 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
else:
|
||||
self.table.editItem(item)
|
||||
|
||||
def delete_pressed(self):
|
||||
if self.table.currentColumn() == 0:
|
||||
self.delete_tags()
|
||||
|
||||
def delete_tags(self):
|
||||
deletes = self.table.selectedItems()
|
||||
if not deletes:
|
||||
@ -460,41 +470,26 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
return
|
||||
|
||||
to_del = []
|
||||
to_undel = []
|
||||
for item in deletes:
|
||||
if item.is_deleted:
|
||||
to_undel.append(item)
|
||||
else:
|
||||
if not item.is_deleted:
|
||||
to_del.append(item)
|
||||
|
||||
if to_del:
|
||||
ct = ', '.join([unicode_type(item.text()) for item in to_del])
|
||||
if not confirm(
|
||||
'<p>'+_('Are you sure you want to delete the following items?')+'<br>'+ct,
|
||||
'tag_list_editor_delete'):
|
||||
return
|
||||
if to_undel:
|
||||
ct = ', '.join([unicode_type(item.text()) for item in to_undel])
|
||||
if not confirm(
|
||||
'<p>'+_('Are you sure you want to undelete the following items?')+'<br>'+ct,
|
||||
'tag_list_editor_undelete'):
|
||||
return
|
||||
|
||||
row = self.table.row(deletes[0])
|
||||
self.table.blockSignals(True)
|
||||
for item in deletes:
|
||||
if item.is_deleted:
|
||||
item.set_is_deleted(False)
|
||||
self.to_delete.discard(int(item.data(Qt.UserRole)))
|
||||
orig = self.table.item(item.row(), 2)
|
||||
self.table.blockSignals(True)
|
||||
orig.setData(Qt.DisplayRole, '')
|
||||
self.table.blockSignals(False)
|
||||
else:
|
||||
id = int(item.data(Qt.UserRole))
|
||||
self.to_delete.add(id)
|
||||
item.set_is_deleted(True)
|
||||
orig = self.table.item(item.row(), 2)
|
||||
self.table.blockSignals(True)
|
||||
orig.setData(Qt.DisplayRole, item.initial_text())
|
||||
self.table.blockSignals(False)
|
||||
id_ = int(item.data(Qt.UserRole))
|
||||
self.to_delete.add(id_)
|
||||
item.set_is_deleted(True)
|
||||
orig = self.table.item(item.row(), 2)
|
||||
orig.setData(Qt.DisplayRole, item.initial_text())
|
||||
self.table.blockSignals(False)
|
||||
if row >= self.table.rowCount():
|
||||
row = self.table.rowCount() - 1
|
||||
if row >= 0:
|
||||
|
@ -197,7 +197,7 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="4">
|
||||
<widget class="QTableWidget" name="table">
|
||||
<widget class="TleTableWidget" name="table">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -224,6 +224,12 @@
|
||||
<extends>QLineEdit</extends>
|
||||
<header>calibre/gui2/widgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>TleTableWidget</class>
|
||||
<extends>QTableWidget</extends>
|
||||
<header>calibre/gui2/dialogs/tag_list_editor_table_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../../resources/images.qrc"/>
|
||||
|
21
src/calibre/gui2/dialogs/tag_list_editor_table_widget.py
Normal file
21
src/calibre/gui2/dialogs/tag_list_editor_table_widget.py
Normal file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python2
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2008, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from PyQt5.Qt import (Qt, QTableWidget, pyqtSignal)
|
||||
|
||||
|
||||
class TleTableWidget(QTableWidget):
|
||||
|
||||
delete_pressed = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QTableWidget.__init__(self, parent=parent)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if event.key() == Qt.Key_Delete:
|
||||
self.delete_pressed.emit()
|
||||
event.accept()
|
||||
return
|
||||
return QTableWidget.keyPressEvent(self, event)
|
@ -240,7 +240,7 @@ class TagBrowserMixin(object): # {{{
|
||||
result = None
|
||||
return result
|
||||
|
||||
def do_tags_list_edit(self, tag, category):
|
||||
def do_tags_list_edit(self, tag, category, is_first_letter=False):
|
||||
'''
|
||||
Open the 'manage_X' dialog where X == category. If tag is not None, the
|
||||
dialog will position the editor on that item.
|
||||
@ -255,7 +255,7 @@ class TagBrowserMixin(object): # {{{
|
||||
d = TagListEditor(self, cat_name=db.field_metadata[category]['name'],
|
||||
tag_to_match=tag,
|
||||
get_book_ids=partial(self.get_book_ids, db=db, category=category),
|
||||
sorter=key)
|
||||
sorter=key, ttm_is_first_letter=is_first_letter)
|
||||
d.exec_()
|
||||
if d.result() == d.Accepted:
|
||||
to_rename = d.to_rename # dict of old id to new name
|
||||
@ -356,14 +356,16 @@ class TagBrowserMixin(object): # {{{
|
||||
self.library_view.select_rows(ids)
|
||||
# refreshing the tags view happens at the emit()/call() site
|
||||
|
||||
def do_author_sort_edit(self, parent, id_, select_sort=True, select_link=False):
|
||||
def do_author_sort_edit(self, parent, id_, select_sort=True,
|
||||
select_link=False, is_first_letter=False):
|
||||
'''
|
||||
Open the manage authors dialog
|
||||
'''
|
||||
|
||||
db = self.library_view.model().db
|
||||
editor = EditAuthorsDialog(parent, db, id_, select_sort, select_link,
|
||||
partial(self.get_book_ids, db=db, category='authors'))
|
||||
partial(self.get_book_ids, db=db, category='authors'),
|
||||
is_first_letter)
|
||||
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
|
||||
|
@ -145,10 +145,10 @@ class TagsView(QTreeView): # {{{
|
||||
del_item_from_user_cat = pyqtSignal(object, object, object)
|
||||
add_item_to_user_cat = pyqtSignal(object, object, object)
|
||||
add_subcategory = pyqtSignal(object)
|
||||
tags_list_edit = pyqtSignal(object, object)
|
||||
tags_list_edit = pyqtSignal(object, object, object)
|
||||
saved_search_edit = pyqtSignal(object)
|
||||
rebuild_saved_searches = pyqtSignal()
|
||||
author_sort_edit = pyqtSignal(object, object, object, object)
|
||||
author_sort_edit = pyqtSignal(object, object, object, object, object)
|
||||
tag_item_renamed = pyqtSignal()
|
||||
search_item_renamed = pyqtSignal()
|
||||
drag_drop_finished = pyqtSignal(object)
|
||||
@ -390,7 +390,7 @@ class TagsView(QTreeView): # {{{
|
||||
|
||||
def context_menu_handler(self, action=None, category=None,
|
||||
key=None, index=None, search_state=None,
|
||||
use_vl=None):
|
||||
use_vl=None, is_first_letter=False):
|
||||
if not action:
|
||||
return
|
||||
try:
|
||||
@ -447,7 +447,7 @@ class TagsView(QTreeView): # {{{
|
||||
self.tag_item_delete.emit(key, index.id, index.original_name, None)
|
||||
return
|
||||
if action == 'open_editor':
|
||||
self.tags_list_edit.emit(category, key)
|
||||
self.tags_list_edit.emit(category, key, is_first_letter)
|
||||
return
|
||||
if action == 'manage_categories':
|
||||
self.edit_user_category.emit(category)
|
||||
@ -492,11 +492,14 @@ class TagsView(QTreeView): # {{{
|
||||
if action == 'manage_searches':
|
||||
self.saved_search_edit.emit(category)
|
||||
return
|
||||
if action == 'edit_authors':
|
||||
self.author_sort_edit.emit(self, index, False, False, is_first_letter)
|
||||
return
|
||||
if action == 'edit_author_sort':
|
||||
self.author_sort_edit.emit(self, index, True, False)
|
||||
self.author_sort_edit.emit(self, index, True, False, is_first_letter)
|
||||
return
|
||||
if action == 'edit_author_link':
|
||||
self.author_sort_edit.emit(self, index, False, True)
|
||||
self.author_sort_edit.emit(self, index, False, True, False)
|
||||
return
|
||||
|
||||
reset_filter_categories = True
|
||||
@ -557,9 +560,9 @@ class TagsView(QTreeView): # {{{
|
||||
if index.isValid():
|
||||
item = index.data(Qt.UserRole)
|
||||
tag = None
|
||||
tag_item = item
|
||||
|
||||
if item.type == TagTreeItem.TAG:
|
||||
tag_item = item
|
||||
tag = item.tag
|
||||
while item.type != TagTreeItem.CATEGORY:
|
||||
item = item.parent
|
||||
@ -726,13 +729,29 @@ class TagsView(QTreeView): # {{{
|
||||
self.context_menu.addSeparator()
|
||||
if key in ['tags', 'publisher', 'series'] or (
|
||||
self.db.field_metadata[key]['is_custom'] and self.db.field_metadata[key]['datatype'] != 'composite'):
|
||||
self.context_menu.addAction(_('Manage %s')%category,
|
||||
if tag_item.type == TagTreeItem.CATEGORY and tag_item.temporary:
|
||||
self.context_menu.addAction(_('Manage %s')%category,
|
||||
partial(self.context_menu_handler, action='open_editor',
|
||||
category=tag_item.name,
|
||||
key=key, is_first_letter=True))
|
||||
else:
|
||||
self.context_menu.addAction(_('Manage %s')%category,
|
||||
partial(self.context_menu_handler, action='open_editor',
|
||||
category=tag.original_name if tag else None,
|
||||
key=key))
|
||||
elif key == 'authors':
|
||||
self.context_menu.addAction(_('Manage %s')%category,
|
||||
partial(self.context_menu_handler, action='edit_author_sort'))
|
||||
if tag_item.type == TagTreeItem.CATEGORY:
|
||||
if tag_item.temporary:
|
||||
self.context_menu.addAction(_('Manage %s')%category,
|
||||
partial(self.context_menu_handler, action='edit_authors',
|
||||
index=tag_item.name, is_first_letter=True))
|
||||
else:
|
||||
self.context_menu.addAction(_('Manage %s')%category,
|
||||
partial(self.context_menu_handler, action='edit_authors'))
|
||||
else:
|
||||
self.context_menu.addAction(_('Manage %s')%category,
|
||||
partial(self.context_menu_handler, action='edit_authors',
|
||||
index=tag.id))
|
||||
elif key == 'search':
|
||||
self.context_menu.addAction(_('Manage Saved searches'),
|
||||
partial(self.context_menu_handler, action='manage_searches',
|
||||
|
Loading…
x
Reference in New Issue
Block a user