From f0999328823631b293bba300a15b2944d7d133a5 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Fri, 22 Sep 2023 18:00:36 +0100 Subject: [PATCH] Fix the icon not being updated if notes are changed from within book details. While fixing this I found other places with both notes and links weren't refreshed in book details. --- src/calibre/ebooks/metadata/book/render.py | 8 ++++- src/calibre/gui2/book_details.py | 29 ++++++++++++------- .../gui2/dialogs/show_category_note.py | 4 +++ src/calibre/gui2/tag_browser/ui.py | 17 +++++++---- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/calibre/ebooks/metadata/book/render.py b/src/calibre/ebooks/metadata/book/render.py index b1b5e4833c..e7777b9ae2 100644 --- a/src/calibre/ebooks/metadata/book/render.py +++ b/src/calibre/ebooks/metadata/book/render.py @@ -68,6 +68,10 @@ def search_action_with_data(search_term, value, book_id, field=None, **k): return search_action(search_term, value, field=field, book_id=book_id, **k) +def notes_action(**keys): + return 'notes:' + as_hex_unicode(json_dumps(keys)) + + DEFAULT_AUTHOR_LINK = f'search-{DEFAULT_AUTHOR_SOURCE}' @@ -128,7 +132,9 @@ def mi_to_html( link = '' if field_value in all_notes.get(field, set()): note = ' {2}'.format(_('Click to open note'), - action('note', field=field, value=field_value), note_markup) + notes_action(field=field, value=field_value, + item_id=all_notes.get(field).get(field_value)), + note_markup) else: note = '' return link + note diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index 61c242d98b..93c98e1e7e 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -179,7 +179,8 @@ def add_edit_notes_action(menu, book_info, field, value): gui = get_gui() from calibre.gui2.dialogs.edit_category_notes import EditNoteDialog d = EditNoteDialog(field, item_id, gui.current_db.new_api, parent=book_info) - d.exec() + if d.exec() == QDialog.DialogCode.Accepted: + gui.do_field_item_value_changed() ac = menu.addAction(_('Edit notes for {}').format(escape_for_menu(value))) ac.triggered.connect(edit_note) ac.setIcon(QIcon.ic('edit_input.png')) @@ -321,8 +322,8 @@ def render_data(mi, use_roman_numbers=True, all_fields=False, pref_name='book_di field_list = [(x, all_fields or display) for x, display in field_list] db, _ = db_for_mi(mi) db = db.new_api - all_notes = db.get_all_items_that_have_notes() - all_notes = {fld: {db.get_item_name(fld, id_) for id_ in all_notes[fld]} for fld in all_notes.keys()} + an = db.get_all_items_that_have_notes() + all_notes = {fld: {db.get_item_name(fld, id_):id_ for id_ in an[fld]} for fld in an.keys()} return mi_to_html( mi, field_list=field_list, use_roman_numbers=use_roman_numbers, rtl=is_rtl(), rating_font=rating_font(), default_author_link=default_author_link(), @@ -577,6 +578,11 @@ def details_context_menu_event(view, ev, book_info, add_popup_action=False, edit create_copy_links(copy_menu, data) copy_links_added = True reindex_fmt_added = 'reindex_fmt_added' in data + elif url and url.startswith('notes:'): + copy_links_added = True + search_internet_added = True + data = json_loads(from_hex_bytes(url.split(':', 1)[1])) + add_edit_notes_action(menu, view, data['field'], data['value']) elif url and not url.startswith('#'): ac = book_info.copy_link_action ac.current_url = url @@ -1296,14 +1302,6 @@ class BookDetails(DetailsLayout): # {{{ if dt == 'search': field = data.get('field') search_term(data['term'], data['value']) - elif dt == 'note': - field = data.get('field') - # It shouldn't be possible for the field to be invalid or the - # note not to exist, but ... - if field and db.field_supports_notes(field): - item_id = db.get_item_id(field, data['value']) - if item_id is not None and db.notes_for(field, item_id): - return self.show_notes(field, item_id) elif dt == 'author': url = data['url'] if url == 'calibre': @@ -1322,6 +1320,15 @@ class BookDetails(DetailsLayout): # {{{ self.open_data_folder.emit(int(data['loc'])) elif dt == 'devpath': self.view_device_book.emit(data['loc']) + elif typ == 'notes': + data = json_loads(from_hex_bytes(val)) + field = data.get('field') + # It shouldn't be possible for the field to be invalid or the + # note not to exist, but ... + if field and db.field_supports_notes(field): + item_id = data['item_id'] + if item_id is not None and db.notes_for(field, item_id): + return self.show_notes(field, item_id) else: browse(link) diff --git a/src/calibre/gui2/dialogs/show_category_note.py b/src/calibre/gui2/dialogs/show_category_note.py index a7149b0e18..a079efd605 100644 --- a/src/calibre/gui2/dialogs/show_category_note.py +++ b/src/calibre/gui2/dialogs/show_category_note.py @@ -124,6 +124,10 @@ class ShowNoteDialog(Dialog): def edit(self): d = EditNoteDialog(self.field, self.item_id, self.db, self) if d.exec() == QDialog.DialogCode.Accepted: + # Tell the rest of calibre that the note has changed + gui = get_gui() + if gui is not None: + gui.do_field_item_value_changed() self.refresh() def find_books(self): diff --git a/src/calibre/gui2/tag_browser/ui.py b/src/calibre/gui2/tag_browser/ui.py index 1fda4149f6..4dadb8d7be 100644 --- a/src/calibre/gui2/tag_browser/ui.py +++ b/src/calibre/gui2/tag_browser/ui.py @@ -88,7 +88,7 @@ class TagBrowserMixin: # {{{ self.tags_view.saved_search_edit.connect(self.do_saved_search_edit) self.tags_view.rebuild_saved_searches.connect(self.do_rebuild_saved_searches) self.tags_view.author_sort_edit.connect(self.do_author_sort_edit) - self.tags_view.tag_item_renamed.connect(self.do_tag_item_renamed) + self.tags_view.tag_item_renamed.connect(self.do_field_item_value_changed) self.tags_view.search_item_renamed.connect(self.saved_searches_changed) self.tags_view.drag_drop_finished.connect(self.drag_drop_finished) self.tags_view.restriction_error.connect(self.do_restriction_error, @@ -321,7 +321,7 @@ class TagBrowserMixin: # {{{ db.new_api.set_link_map(category, d.links) # Clean up the library view - self.do_tag_item_renamed() + self.do_field_item_value_changed() self.tags_view.recount() def do_tag_item_delete(self, category, item_id, orig_name, @@ -373,7 +373,7 @@ class TagBrowserMixin: # {{{ m.delete_item_from_all_user_categories(orig_name, category) # Clean up the library view - self.do_tag_item_renamed() + self.do_field_item_value_changed() self.tags_view.recount() def apply_tag_to_selected(self, field_name, item_name, remove): @@ -451,7 +451,13 @@ class TagBrowserMixin: # {{{ d.exec() def do_tag_item_renamed(self): - # Clean up library view and search + # The method name was changed. Keep the old one here for compatibility, + # in case some plugin uses it. + self.do_field_item_value_changed() + + def do_field_item_value_changed(self): + # Clean up library view and search, which also cleans up book details + # get information to redo the selection rows = [r.row() for r in self.library_view.selectionModel().selectedRows()] @@ -495,7 +501,8 @@ class TagBrowserMixin: # {{{ sort_map = {id_map.get(author_id, author_id):new_sort for author_id, old_author, new_author, new_sort, new_link in editor.result} affected_books |= db.set_sort_for_authors(sort_map) self.library_view.model().refresh_ids(affected_books, current_row=self.library_view.currentIndex().row()) - self.tags_view.recount() + self.do_field_item_value_changed() + self.tags_view.recount() def drag_drop_finished(self, ids): self.library_view.model().refresh_ids(ids)