From 5557660bbd02f4d342de6c3fddd5ab35b6255eb7 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Fri, 27 Feb 2015 16:11:56 +0100 Subject: [PATCH 1/5] Make item delete and rename take VLs into consideration --- src/calibre/db/cache.py | 55 +++++++++++++++++++---- src/calibre/db/legacy.py | 9 ++-- src/calibre/db/tables.py | 63 ++++++++++++++++++++++++++- src/calibre/gui2/tag_browser/model.py | 46 +++++++++---------- src/calibre/gui2/tag_browser/ui.py | 20 ++++++--- 5 files changed, 145 insertions(+), 48 deletions(-) diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index d665534715..ebd8f77307 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -1564,24 +1564,60 @@ class Cache(object): return val_map @write_api - def rename_items(self, field, item_id_to_new_name_map, change_index=True): + def rename_items(self, field, item_id_to_new_name_map, change_index=True, + restrict_to_book_ids=None): ''' Rename items from a many-one or many-many field such as tags or series. :param change_index: When renaming in a series-like field also change the series_index values. + :param restrict_to_book_ids: A list of books for which the rename is to be performed. None if the entire library ''' + f = self.fields[field] - try: - func = f.table.rename_item - except AttributeError: - raise ValueError('Cannot rename items for one-one fields: %s' % field) affected_books = set() - moved_books = set() - id_map = {} try: sv = f.metadata['is_multiple']['ui_to_list'] except (TypeError, KeyError, AttributeError): sv = None + + if restrict_to_book_ids: + # We have a VL. Only change the item name for those books + rtb_set = frozenset(restrict_to_book_ids) + id_map = {} + for old_id, new_name in item_id_to_new_name_map.iteritems(): + new_names = tuple(x.strip() for x in new_name.split(sv)) if sv else (new_name,) + # Get a list of books in the VL with the item + books_to_process = f.books_for(old_id) & rtb_set + # This should never be empty, but ... + if books_to_process: + affected_books.update(books_to_process) + newvals = {} + for book in books_to_process: + # Get the current values, remove the one being renamed, then add + # the new value(s) back + vals = self._field_for(field, book) + # Check for is_multiple + if isinstance(vals, tuple): + vals = set(vals) + # Don't need to worry about case here because we + # are fetching its one-true spelling + vals.remove(self.get_item_name(field, old_id)) + # This can put the name back with a different case + vals.update(new_names) + newvals[book] = vals + else: + newvals[book] = new_names[0] + # Allow case changes + self._set_field(field, newvals) + id_map[old_id] = self.get_item_id(field, new_names[0]) + return affected_books, id_map + + try: + func = f.table.rename_item + except AttributeError: + raise ValueError('Cannot rename items for one-one fields: %s' % field) + moved_books = set() + id_map = {} for item_id, new_name in item_id_to_new_name_map.iteritems(): new_names = tuple(x.strip() for x in new_name.split(sv)) if sv else (new_name,) books, new_id = func(item_id, new_names[0], self.backend) @@ -1606,10 +1642,11 @@ class Cache(object): return affected_books, id_map @write_api - def remove_items(self, field, item_ids): + def remove_items(self, field, item_ids, restrict_to_book_ids=None): ''' Delete all items in the specified field with the specified ids. Returns the set of affected book ids. ''' field = self.fields[field] - affected_books = field.table.remove_items(item_ids, self.backend) + affected_books = field.table.remove_items(item_ids, self.backend, + restrict_to_book_ids=restrict_to_book_ids) if affected_books: if hasattr(field, 'index_field'): self._set_field(field.index_field.name, {bid:1.0 for bid in affected_books}) diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index 84c2e75bb6..ab0e900347 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -820,8 +820,9 @@ for field in ( for field in ('authors', 'tags', 'publisher'): def renamer(field): - def func(self, old_id, new_name): - id_map = self.new_api.rename_items(field, {old_id:new_name})[1] + def func(self, old_id, new_name, restrict_to_book_ids=None): + id_map = self.new_api.rename_items(field, {old_id:new_name}, + restrict_to_book_ids=restrict_to_book_ids)[1] if field == 'authors': return id_map[old_id] return func @@ -877,8 +878,8 @@ for field in ('author', 'tag', 'series'): for field in ('publisher', 'series', 'tag'): def getter(field): fname = 'tags' if field == 'tag' else field - def func(self, item_id): - self.new_api.remove_items(fname, (item_id,)) + def func(self, item_id, restrict_to_book_ids=None): + self.new_api.remove_items(fname, (item_id,), restrict_to_book_ids=restrict_to_book_ids) return func setattr(LibraryDatabase, 'delete_%s_using_id' % field, MT(getter(field))) # }}} diff --git a/src/calibre/db/tables.py b/src/calibre/db/tables.py index 750714ef94..328f515f1d 100644 --- a/src/calibre/db/tables.py +++ b/src/calibre/db/tables.py @@ -265,8 +265,37 @@ class ManyToOneTable(Table): [(x,) for x in clean]) return clean - def remove_items(self, item_ids, db): + def remove_items(self, item_ids, db, restrict_to_book_ids=None): affected_books = set() + + if restrict_to_book_ids: + rtb_set = frozenset(restrict_to_book_ids) + items_to_process_normally = set() + # Check if all the books with the item are in the restriction. If + # so, process them normally + for item_id in item_ids: + books_to_process = self.col_book_map.get(item_id, set()) + books_not_to_delete = books_to_process - rtb_set + if books_not_to_delete: + # Some books not in restriction. Must do special processing + books_to_delete = books_to_process & rtb_set + # remove the books from the old id maps + self.col_book_map[item_id] = books_not_to_delete + for book_id in books_to_delete: + self.book_col_map.pop(book_id, None) + # Delete links to the affected books from the link table. As + # this is a many-to-one mapping we know that we can delete + # links without checking the item ID + db.executemany('DELETE FROM {0} WHERE {1}=?'.format(self.link_table, + 'book'), books_to_delete) + affected_books |= books_to_delete + else: + # Process normally any items where the VL was not significant + items_to_process_normally.add(item_id) + if items_to_process_normally: + affected_books |= self.remove_items(items_to_process_normally, db) + return affected_books + for item_id in item_ids: val = self.id_map.pop(item_id, null) if val is null: @@ -373,8 +402,38 @@ class ManyToManyTable(ManyToOneTable): [(x,) for x in clean]) return clean - def remove_items(self, item_ids, db): + def remove_items(self, item_ids, db, restrict_to_book_ids=None): affected_books = set() + if restrict_to_book_ids: + rtb_set = frozenset(restrict_to_book_ids) + items_to_process_normally = set() + # Check if all the books with the item are in the restriction. If + # so, process them normally + for item_id in item_ids: + books_to_process = self.col_book_map.get(item_id, set()) + books_not_to_delete = books_to_process - rtb_set + if books_not_to_delete: + # Some books not in restriction. Must do special processing + books_to_delete = books_to_process & rtb_set + # remove the books from the old id maps + self.col_book_map[item_id] = books_not_to_delete + for book_id in books_to_delete: + self.book_col_map[book_id] = tuple( + x for x in self.book_col_map.get(book_id, ()) if x != item_id) + affected_books |= books_to_delete + else: + items_to_process_normally.add(item_id) + # Delete book/item pairs from the link table. We don't need to do + # anything with the main table because books with the old ID are + # still in the library. + db.executemany('DELETE FROM {0} WHERE {1}=? and {2}=?'.format( + self.link_table, 'book', self.metadata['link_column']), + [(b, i) for b in affected_books for i in item_ids]) + # Take care of any items where the VL was not significant + if items_to_process_normally: + affected_books |= self.remove_items(items_to_process_normally, db) + return affected_books + for item_id in item_ids: val = self.id_map.pop(item_id, null) if val is null: diff --git a/src/calibre/gui2/tag_browser/model.py b/src/calibre/gui2/tag_browser/model.py index 57f9aca620..746c91bb56 100644 --- a/src/calibre/gui2/tag_browser/model.py +++ b/src/calibre/gui2/tag_browser/model.py @@ -855,6 +855,11 @@ class TagsModel(QAbstractItemModel): # {{{ self.drag_drop_finished.emit(ids) # }}} + def get_book_ids_to_use(self): + if self.db.data.get_base_restriction() or self.db.data.get_search_restriction(): + return self.db.search('', return_matches=True, sort_results=False) + return None + def _get_category_nodes(self, sort): ''' Called by __init__. Do not directly call this method. @@ -863,21 +868,17 @@ class TagsModel(QAbstractItemModel): # {{{ self.categories = {} # Get the categories - if self.db.data.get_base_restriction() or self.db.data.get_search_restriction(): - try: - data = self.db.new_api.get_categories(sort=sort, - icon_map=self.category_icon_map, - book_ids=self.db.search('', return_matches=True, sort_results=False), - first_letter_sort = self.collapse_model == 'first letter') - except: - import traceback - traceback.print_exc() - data = self.db.new_api.get_categories(sort=sort, icon_map=self.category_icon_map, - first_letter_sort = self.collapse_model == 'first letter') - self.restriction_error.emit() - else: + try: + data = self.db.new_api.get_categories(sort=sort, + icon_map=self.category_icon_map, + book_ids=self.get_book_ids_to_use(), + first_letter_sort = self.collapse_model == 'first letter') + except: + import traceback + traceback.print_exc() data = self.db.new_api.get_categories(sort=sort, icon_map=self.category_icon_map, - first_letter_sort = self.collapse_model == 'first letter') + first_letter_sort = self.collapse_model == 'first letter') + self.restriction_error.emit() # Reconstruct the user categories, putting them into metadata self.db.field_metadata.remove_dynamic_categories() @@ -1042,21 +1043,14 @@ class TagsModel(QAbstractItemModel): # {{{ item.tag.name = val self.search_item_renamed.emit() # Does a refresh else: - if key == 'series': - self.db.rename_series(item.tag.id, val) - elif key == 'publisher': - self.db.rename_publisher(item.tag.id, val) - elif key == 'tags': - self.db.rename_tag(item.tag.id, val) - elif key == 'authors': - self.db.rename_author(item.tag.id, val) - elif self.db.field_metadata[key]['is_custom']: - self.db.rename_custom_item(item.tag.id, val, - label=self.db.field_metadata[key]['label']) + restrict_to_book_ids=self.get_book_ids_to_use() + self.db.new_api.rename_items(key, {item.tag.id: val}, + restrict_to_book_ids=restrict_to_book_ids) self.tag_item_renamed.emit() item.tag.name = val item.tag.state = TAG_SEARCH_STATES['clear'] - self.rename_item_in_all_user_categories(name, key, val) + if not restrict_to_book_ids: + self.rename_item_in_all_user_categories(name, key, val) self.refresh_required.emit() return True diff --git a/src/calibre/gui2/tag_browser/ui.py b/src/calibre/gui2/tag_browser/ui.py index c46809db52..b2a0006955 100644 --- a/src/calibre/gui2/tag_browser/ui.py +++ b/src/calibre/gui2/tag_browser/ui.py @@ -223,14 +223,18 @@ class TagBrowserMixin(object): # {{{ if (category in ['tags', 'series', 'publisher'] or db.new_api.field_metadata.is_custom_field(category)): m = self.tags_view.model() - for item in to_delete: - m.delete_item_from_all_user_categories(orig_name[item], category) - for old_id in to_rename: + restrict_to_book_ids = m.get_book_ids_to_use() + if not restrict_to_book_ids: + for item in to_delete: + m.delete_item_from_all_user_categories(orig_name[item], category) + for old_id in to_rename and not restrict_to_book_ids: m.rename_item_in_all_user_categories(orig_name[old_id], category, unicode(to_rename[old_id])) - db.new_api.remove_items(category, to_delete) - db.new_api.rename_items(category, to_rename, change_index=False) + db.new_api.remove_items(category, to_delete, + restrict_to_book_ids=restrict_to_book_ids) + db.new_api.rename_items(category, to_rename, change_index=False, + restrict_to_book_ids=restrict_to_book_ids) # Clean up the library view self.do_tag_item_renamed() @@ -260,8 +264,10 @@ class TagBrowserMixin(object): # {{{ delete_func = partial(db.delete_custom_item_using_id, label=cc_label) m = self.tags_view.model() if delete_func: - delete_func(item_id) - m.delete_item_from_all_user_categories(orig_name, category) + restrict_to_book_ids=m.get_book_ids_to_use() + delete_func(item_id, restrict_to_book_ids=restrict_to_book_ids) + if not restrict_to_book_ids: + m.delete_item_from_all_user_categories(orig_name, category) # Clean up the library view self.do_tag_item_renamed() From 7e1bfa006a28b7dc6079c332daf9b93828bcf46c Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Sat, 28 Feb 2015 10:47:48 +0100 Subject: [PATCH 2/5] Don't change the order of the items when rebuilding inside a VL. Use the normal processing if all the books with the item are also in the VL --- src/calibre/db/cache.py | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index ebd8f77307..5410a25885 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -1584,32 +1584,53 @@ class Cache(object): # We have a VL. Only change the item name for those books rtb_set = frozenset(restrict_to_book_ids) id_map = {} + default_process_map = {} for old_id, new_name in item_id_to_new_name_map.iteritems(): new_names = tuple(x.strip() for x in new_name.split(sv)) if sv else (new_name,) # Get a list of books in the VL with the item - books_to_process = f.books_for(old_id) & rtb_set - # This should never be empty, but ... - if books_to_process: + books_with_id = f.books_for(old_id) + books_to_process = books_with_id & rtb_set + if len(books_with_id) == len(books_to_process): + # All the books with the ID are in the VL, so we can use + # the normal processing + default_process_map[old_id] = new_name + elif books_to_process: affected_books.update(books_to_process) newvals = {} for book in books_to_process: # Get the current values, remove the one being renamed, then add - # the new value(s) back + # the new value(s) back. vals = self._field_for(field, book) # Check for is_multiple if isinstance(vals, tuple): - vals = set(vals) + # We must preserve order. + vals = list(vals) # Don't need to worry about case here because we - # are fetching its one-true spelling - vals.remove(self.get_item_name(field, old_id)) - # This can put the name back with a different case - vals.update(new_names) - newvals[book] = vals + # are fetching its one-true spelling. But lets be + # careful anyway + try: + dex = vals.index(self.get_item_name(field, old_id)) + # This can put the name back with a different case + vals[dex] = new_names[0] + # now add any other items if they aren't already there + set_vals = frozenset(vals) + if len(new_names) > 1: + for v in new_names[1:]: + if v not in set_vals: + vals.append(v) + newvals[book] = vals + except: + traceback.print_exc() else: newvals[book] = new_names[0] # Allow case changes self._set_field(field, newvals) id_map[old_id] = self.get_item_id(field, new_names[0]) + if default_process_map: + ab, idm = self.rename_items(field, default_process_map, + change_index=change_index, restrict_to_book_ids=None) + affected_books.update(ab) + id_map.update(idm) return affected_books, id_map try: From 42dab3af4652ed699f051fbebbf892ea0d85febc Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Sat, 28 Feb 2015 11:28:52 +0100 Subject: [PATCH 3/5] Add additional context menu items for edit/delete when a VL is in effect --- src/calibre/gui2/tag_browser/model.py | 5 +++- src/calibre/gui2/tag_browser/ui.py | 22 ++++++++--------- src/calibre/gui2/tag_browser/view.py | 35 +++++++++++++++++++++------ 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/calibre/gui2/tag_browser/model.py b/src/calibre/gui2/tag_browser/model.py index 746c91bb56..b7afa288cd 100644 --- a/src/calibre/gui2/tag_browser/model.py +++ b/src/calibre/gui2/tag_browser/model.py @@ -855,6 +855,9 @@ class TagsModel(QAbstractItemModel): # {{{ self.drag_drop_finished.emit(ids) # }}} + def get_in_vl(self): + return self.db.data.get_base_restriction() or self.db.data.get_search_restriction() + def get_book_ids_to_use(self): if self.db.data.get_base_restriction() or self.db.data.get_search_restriction(): return self.db.search('', return_matches=True, sort_results=False) @@ -1043,7 +1046,7 @@ class TagsModel(QAbstractItemModel): # {{{ item.tag.name = val self.search_item_renamed.emit() # Does a refresh else: - restrict_to_book_ids=self.get_book_ids_to_use() + restrict_to_book_ids=self.get_book_ids_to_use() if item.use_vl else None self.db.new_api.rename_items(key, {item.tag.id: val}, restrict_to_book_ids=restrict_to_book_ids) self.tag_item_renamed.emit() diff --git a/src/calibre/gui2/tag_browser/ui.py b/src/calibre/gui2/tag_browser/ui.py index b2a0006955..5dafbb6953 100644 --- a/src/calibre/gui2/tag_browser/ui.py +++ b/src/calibre/gui2/tag_browser/ui.py @@ -223,31 +223,32 @@ class TagBrowserMixin(object): # {{{ if (category in ['tags', 'series', 'publisher'] or db.new_api.field_metadata.is_custom_field(category)): m = self.tags_view.model() - restrict_to_book_ids = m.get_book_ids_to_use() - if not restrict_to_book_ids: - for item in to_delete: - m.delete_item_from_all_user_categories(orig_name[item], category) - for old_id in to_rename and not restrict_to_book_ids: + for item in to_delete: + m.delete_item_from_all_user_categories(orig_name[item], category) + for old_id in to_rename: m.rename_item_in_all_user_categories(orig_name[old_id], category, unicode(to_rename[old_id])) db.new_api.remove_items(category, to_delete, - restrict_to_book_ids=restrict_to_book_ids) + restrict_to_book_ids=None) db.new_api.rename_items(category, to_rename, change_index=False, - restrict_to_book_ids=restrict_to_book_ids) + restrict_to_book_ids=None) # Clean up the library view self.do_tag_item_renamed() self.tags_view.recount() - def do_tag_item_delete(self, category, item_id, orig_name): + def do_tag_item_delete(self, category, item_id, orig_name, restrict_to_book_ids=None): ''' Delete an item from some category. ''' + if restrict_to_book_ids: + msg = _('%s will be deleted from books in the virtual library. Are you sure?')%orig_name + else: + msg = _('%s will be deleted from all books. Are you sure?')%orig_name, if not question_dialog(self.tags_view, title=_('Delete item'), - msg='

'+ - _('%s will be deleted from all books. Are you sure?') %orig_name, + msg='

'+ msg, skip_dialog_name='tag_item_delete', skip_dialog_msg=_('Show this confirmation again')): return @@ -264,7 +265,6 @@ class TagBrowserMixin(object): # {{{ delete_func = partial(db.delete_custom_item_using_id, label=cc_label) m = self.tags_view.model() if delete_func: - restrict_to_book_ids=m.get_book_ids_to_use() delete_func(item_id, restrict_to_book_ids=restrict_to_book_ids) if not restrict_to_book_ids: m.delete_item_from_all_user_categories(orig_name, category) diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py index 21186b6226..d51fddff0a 100644 --- a/src/calibre/gui2/tag_browser/view.py +++ b/src/calibre/gui2/tag_browser/view.py @@ -297,7 +297,8 @@ class TagsView(QTreeView): # {{{ self.clear() def context_menu_handler(self, action=None, category=None, - key=None, index=None, search_state=None): + key=None, index=None, search_state=None, + use_vl=None): if not action: return try: @@ -328,7 +329,14 @@ class TagsView(QTreeView): # {{{ self.recount() return - if action == 'edit_item': + if action == 'edit_item_no_vl': + item = self.model().get_node(index) + item.use_vl = False + self.edit(index) + return + if action == 'edit_item_in_vl': + item = self.model().get_node(index) + item.use_vl = True self.edit(index) return if action == 'delete_item': @@ -441,15 +449,26 @@ class TagsView(QTreeView): # {{{ # the possibility of renaming that item. if tag.is_editable: # Add the 'rename' items + if self.model().get_in_vl(): + self.context_menu.addAction(self.rename_icon, + _('Rename %s in virtual library')%display_name(tag), + partial(self.context_menu_handler, action='edit_item_in_vl', + index=index)) self.context_menu.addAction(self.rename_icon, - _('Rename %s')%display_name(tag), - partial(self.context_menu_handler, action='edit_item', - index=index)) + _('Rename %s')%display_name(tag), + partial(self.context_menu_handler, action='edit_item_no_vl', + index=index)) if key in ('tags', 'series', 'publisher') or \ self._model.db.field_metadata.is_custom_field(key): + if self.model().get_in_vl(): + self.context_menu.addAction(self.delete_icon, + _('Delete %s in virtual library')%display_name(tag), + partial(self.context_menu_handler, action='delete_item_in_vl', + key=key, index=tag)) + self.context_menu.addAction(self.delete_icon, _('Delete %s')%display_name(tag), - partial(self.context_menu_handler, action='delete_item', + partial(self.context_menu_handler, action='delete_item_no_vl', key=key, index=tag)) if key == 'authors': self.context_menu.addAction(_('Edit sort for %s')%display_name(tag), @@ -482,7 +501,7 @@ class TagsView(QTreeView): # {{{ elif key == 'search' and tag.is_searchable: self.context_menu.addAction(self.rename_icon, _('Rename %s')%display_name(tag), - partial(self.context_menu_handler, action='edit_item', + partial(self.context_menu_handler, action='edit_item_no_vl', index=index)) self.context_menu.addAction(self.delete_icon, _('Delete search %s')%display_name(tag), @@ -512,7 +531,7 @@ class TagsView(QTreeView): # {{{ if item.can_be_edited: self.context_menu.addAction(self.rename_icon, _('Rename %s')%item.py_name, - partial(self.context_menu_handler, action='edit_item', + partial(self.context_menu_handler, action='edit_item_no_vl', index=index)) self.context_menu.addAction(self.user_category_icon, _('Add sub-category to %s')%item.py_name, From f2cc364ac029ad0aadc19468141b316beaaf2b59 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Sat, 28 Feb 2015 14:14:40 +0100 Subject: [PATCH 4/5] Fix delete not working --- src/calibre/gui2/tag_browser/ui.py | 2 +- src/calibre/gui2/tag_browser/view.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/tag_browser/ui.py b/src/calibre/gui2/tag_browser/ui.py index 5dafbb6953..65c80a785a 100644 --- a/src/calibre/gui2/tag_browser/ui.py +++ b/src/calibre/gui2/tag_browser/ui.py @@ -245,7 +245,7 @@ class TagBrowserMixin(object): # {{{ if restrict_to_book_ids: msg = _('%s will be deleted from books in the virtual library. Are you sure?')%orig_name else: - msg = _('%s will be deleted from all books. Are you sure?')%orig_name, + msg = _('%s will be deleted from all books. Are you sure?')%orig_name if not question_dialog(self.tags_view, title=_('Delete item'), msg='

'+ msg, diff --git a/src/calibre/gui2/tag_browser/view.py b/src/calibre/gui2/tag_browser/view.py index d51fddff0a..48f0e78ebe 100644 --- a/src/calibre/gui2/tag_browser/view.py +++ b/src/calibre/gui2/tag_browser/view.py @@ -84,7 +84,7 @@ class TagsView(QTreeView): # {{{ search_item_renamed = pyqtSignal() drag_drop_finished = pyqtSignal(object) restriction_error = pyqtSignal() - tag_item_delete = pyqtSignal(object, object, object) + tag_item_delete = pyqtSignal(object, object, object, object) def __init__(self, parent=None): QTreeView.__init__(self, parent=None) @@ -339,8 +339,12 @@ class TagsView(QTreeView): # {{{ item.use_vl = True self.edit(index) return - if action == 'delete_item': - self.tag_item_delete.emit(key, index.id, index.original_name) + if action == 'delete_item_in_vl': + self.tag_item_delete.emit(key, index.id, index.original_name, + self.model().get_book_ids_to_use()) + return + if action == 'delete_item_no_vl': + self.tag_item_delete.emit(key, index.id, index.original_name, None) return if action == 'open_editor': self.tags_list_edit.emit(category, key) From 77046968ae53eccdd6ebc85864a3fbe7726d12fe Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Mon, 2 Mar 2015 16:39:53 +0100 Subject: [PATCH 5/5] Make the Manage Category dialog show all items. It already works across VLs. --- src/calibre/gui2/tag_browser/ui.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/tag_browser/ui.py b/src/calibre/gui2/tag_browser/ui.py index 65c80a785a..19e2b4c4c5 100644 --- a/src/calibre/gui2/tag_browser/ui.py +++ b/src/calibre/gui2/tag_browser/ui.py @@ -201,8 +201,12 @@ class TagBrowserMixin(object): # {{{ dialog will position the editor on that item. ''' - tags_model = self.tags_view.model() - result = tags_model.get_category_editor_data(category) + db=self.library_view.model().db + data = db.new_api.get_categories() + if category in data: + result = [(t.id, t.original_name, t.count) for t in data[category] if t.count > 0] + else: + result = None if result is None: return @@ -211,7 +215,6 @@ class TagBrowserMixin(object): # {{{ else: key = sort_key - db=self.library_view.model().db d = TagListEditor(self, cat_name=db.field_metadata[category]['name'], tag_to_match=tag, data=result, sorter=key) d.exec_()