diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 8232a163e7..bdb76a6066 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -597,5 +597,9 @@ class ActionDelete(InterfaceActionBase): name = 'Remove Books' actual_plugin = 'calibre.gui2.actions.delete:DeleteAction' +class ActionEditMetadata(InterfaceActionBase): + name = 'Edit Metadata' + actual_plugin = 'calibre.gui2.actions.delete:EditMetadataAction' + plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog, - ActionConvert, ActionDelete] + ActionConvert, ActionDelete, ActionEditMetadata] diff --git a/src/calibre/gui2/actions/__init__.py b/src/calibre/gui2/actions/__init__.py index b198a69214..4798828074 100644 --- a/src/calibre/gui2/actions/__init__.py +++ b/src/calibre/gui2/actions/__init__.py @@ -34,8 +34,10 @@ class InterfaceAction(QObject): self.create_action() self.genesis() - def create_action(self): - text, icon, tooltip, shortcut = self.action_spec + def create_action(self, spec=None, attr='qaction'): + if spec is None: + spec = self.action_spec + text, icon, tooltip, shortcut = spec if icon is not None: action = QAction(QIcon(I(icon)), text, self.gui) else: @@ -47,7 +49,7 @@ class InterfaceAction(QObject): action.setAutoRepeat(False) if shortcut: action.setShortcut(shortcut) - self.qaction = action + setattr(self, attr, action) def genesis(self): pass diff --git a/src/calibre/gui2/actions/add.py b/src/calibre/gui2/actions/add.py index 668af0957e..9aa78298dc 100644 --- a/src/calibre/gui2/actions/add.py +++ b/src/calibre/gui2/actions/add.py @@ -90,7 +90,7 @@ class AddAction(InterfaceAction): mi.isbn = x ids.add(self.gui.library_view.model().db.import_book(mi, [])) self.gui.library_view.model().books_added(len(isbns)) - self.gui.do_download_metadata(ids) + self.gui.iactions['Edit Metadata'].do_download_metadata(ids) def files_dropped(self, paths): diff --git a/src/calibre/gui2/actions/edit_metadata.py b/src/calibre/gui2/actions/edit_metadata.py index e4b2145da0..05b4bdf7fc 100644 --- a/src/calibre/gui2/actions/edit_metadata.py +++ b/src/calibre/gui2/actions/edit_metadata.py @@ -6,20 +6,64 @@ __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' import os +from functools import partial -from PyQt4.Qt import Qt, QTimer +from PyQt4.Qt import Qt, QTimer, QMenu from calibre.gui2 import error_dialog, config, warning_dialog from calibre.gui2.dialogs.metadata_single import MetadataSingleDialog from calibre.gui2.dialogs.metadata_bulk import MetadataBulkDialog from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.dialogs.tag_list_editor import TagListEditor +from calibre.gui2.actions import InterfaceAction -class EditMetadataAction(object): +class EditMetadataAction(InterfaceAction): + + name = 'Edit Metadata' + action_spec = (_('Edit metadata'), 'edit_input.svg', None, _('E')) + + def genesis(self): + self.create_action(spec=(_('Merge book records'), 'merge_books.svg', + None, _('M')), attr='action_merge') + md = QMenu() + md.addAction(_('Edit metadata individually'), + partial(self.edit_metadata, False, bulk=False)) + md.addSeparator() + md.addAction(_('Edit metadata in bulk'), + partial(self.edit_metadata, False, bulk=True)) + md.addSeparator() + md.addAction(_('Download metadata and covers'), + partial(self.download_metadata, False, covers=True), + Qt.ControlModifier+Qt.Key_D) + md.addAction(_('Download only metadata'), + partial(self.download_metadata, False, covers=False)) + md.addAction(_('Download only covers'), + partial(self.download_metadata, False, covers=True, + set_metadata=False, set_social_metadata=False)) + md.addAction(_('Download only social metadata'), + partial(self.download_metadata, False, covers=False, + set_metadata=False, set_social_metadata=True)) + self.metadata_menu = md + + mb = QMenu() + mb.addAction(_('Merge into first selected book - delete others'), + self.merge_books) + mb.addSeparator() + mb.addAction(_('Merge into first selected book - keep others'), + partial(self.merge_books, safe_merge=True)) + self.merge_menu = mb + self.action_merge.setMenu(mb) + md.addSeparator() + md.addAction(self.action_merge) + + self.qaction.triggered.connect(self.edit_metadata) + self.qaction.setMenu(md) + self.action_merge.triggered.connect(self.merge_books) def location_selected(self, loc): enabled = loc == 'library' self.qaction.setEnabled(enabled) + self.action_merge.setEnabled(enabled) def download_metadata(self, checked, covers=True, set_metadata=True, set_social_metadata=None): @@ -51,9 +95,9 @@ class EditMetadataAction(object): x = _('social metadata') else: x = _('covers') if covers and not set_metadata else _('metadata') - self.progress_indicator.start( + self.gui.progress_indicator.start( _('Downloading %s for %d book(s)')%(x, len(ids))) - self._book_metadata_download_check = QTimer(self) + self._book_metadata_download_check = QTimer(self.gui) self._book_metadata_download_check.timeout.connect(self.book_metadata_download_check, type=Qt.QueuedConnection) self._book_metadata_download_check.start(100) @@ -62,15 +106,15 @@ class EditMetadataAction(object): if self._download_book_metadata.is_alive(): return self._book_metadata_download_check.stop() - self.progress_indicator.stop() + self.gui.progress_indicator.stop() cr = self.gui.library_view.currentIndex().row() x = self._download_book_metadata self._download_book_metadata = None if x.exception is None: self.gui.library_view.model().refresh_ids( x.updated, cr) - if self.cover_flow: - self.cover_flow.dataChanged() + if self.gui.cover_flow: + self.gui.cover_flow.dataChanged() if x.failures: details = ['%s: %s'%(title, reason) for title, reason in x.failures.values()] @@ -102,22 +146,22 @@ class EditMetadataAction(object): self.gui.library_view.model().refresh_ids([id]) for row in rows: - self._metadata_view_id = self.gui.library_view.model().db.id(row.row()) - d = MetadataSingleDialog(self, row.row(), + self.gui.iactions['View'].metadata_view_id = self.gui.library_view.model().db.id(row.row()) + d = MetadataSingleDialog(self.gui, row.row(), self.gui.library_view.model().db, accepted_callback=accepted, cancel_all=rows.index(row) < len(rows)-1) - d.view_format.connect(self.metadata_view_format) + d.view_format.connect(self.gui.iactions['View'].metadata_view_format) d.exec_() if d.cancel_all: break if rows: current = self.gui.library_view.currentIndex() m = self.gui.library_view.model() - if self.cover_flow: - self.cover_flow.dataChanged() + if self.gui.cover_flow: + self.gui.cover_flow.dataChanged() m.current_changed(current, previous) - self.tags_view.recount() + self.gui.tags_view.recount() def edit_bulk_metadata(self, checked): ''' @@ -130,20 +174,20 @@ class EditMetadataAction(object): _('No books selected')) d.exec_() return - if MetadataBulkDialog(self, rows, + if MetadataBulkDialog(self.gui, rows, self.gui.library_view.model().db).changed: self.gui.library_view.model().resort(reset=False) self.gui.library_view.model().research() - self.tags_view.recount() - if self.cover_flow: - self.cover_flow.dataChanged() + self.gui.tags_view.recount() + if self.gui.cover_flow: + self.gui.cover_flow.dataChanged() # Merge books {{{ def merge_books(self, safe_merge=False): ''' Merge selected books in library. ''' - if self.stack.currentIndex() != 0: + if self.gui.stack.currentIndex() != 0: return rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: @@ -161,7 +205,7 @@ class EditMetadataAction(object): 'The second and subsequently selected books will not ' 'be deleted or changed.

' 'Please confirm you want to proceed.') - +'

', 'merge_books_safe', self): + +'

', 'merge_books_safe', self.gui): return self.add_formats(dest_id, src_books) self.merge_metadata(dest_id, src_ids) @@ -175,12 +219,12 @@ class EditMetadataAction(object): 'and any duplicate formats in the second and subsequently selected books ' 'will be permanently deleted from your computer.

' 'Are you sure you want to proceed?') - +'

', 'merge_books', self): + +'

', 'merge_books', self.gui): return if len(rows)>5: if not confirm('

'+_('You are about to merge more than 5 books. ' 'Are you sure you want to proceed?') - +'

', 'merge_too_many_books', self): + +'

', 'merge_too_many_books', self.gui): return self.add_formats(dest_id, src_books) self.merge_metadata(dest_id, src_ids) @@ -299,7 +343,7 @@ class EditMetadataAction(object): model = view.model() result = model.get_collections_with_ids() compare = (lambda x,y:cmp(x.lower(), y.lower())) - d = TagListEditor(self, tag_to_match=None, data=result, compare=compare) + d = TagListEditor(self.gui, tag_to_match=None, data=result, compare=compare) d.exec_() if d.result() == d.Accepted: to_rename = d.to_rename # dict of new text to old ids @@ -309,7 +353,7 @@ class EditMetadataAction(object): model.rename_collection(old_id, new_name=unicode(text)) for item in to_delete: model.delete_collection_using_id(item) - self.upload_collections(model.db, view=view, oncard=oncard) + self.gui.upload_collections(model.db, view=view, oncard=oncard) view.reset() diff --git a/src/calibre/gui2/actions/view.py b/src/calibre/gui2/actions/view.py index bdad55d142..4a6e545da6 100644 --- a/src/calibre/gui2/actions/view.py +++ b/src/calibre/gui2/actions/view.py @@ -18,6 +18,11 @@ from calibre.ptempfile import PersistentTemporaryFile class ViewAction(object): + name = 'View' + + def genesis(self): + self.metadata_view_id = None + def location_selected(self, loc): enabled = loc == 'library' for action in list(self.view_menu.actions())[1:]: @@ -36,7 +41,7 @@ class ViewAction(object): def metadata_view_format(self, fmt): fmt_path = self.gui.library_view.model().db.\ - format_abspath(self._metadata_view_id, + format_abspath(self.metadata_view_id, fmt, index_is_id=True) if fmt_path: self._view_file(fmt_path) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 7f8ad2d23c..359ea7465c 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -66,8 +66,9 @@ class LibraryViewMixin(object): # {{{ add_to_library = (_('Add books to library'), self.iactions['Add Books'].add_books_from_device) + edc = self.iactions['Edit Metadata'].edit_device_collections edit_device_collections = (_('Manage collections'), - partial(self.edit_device_collections, oncard=None)) + partial(edc, oncard=None)) self.memory_view.set_context_menu(None, None, None, self.action_view, self.action_save, None, None, self.action_del, None, @@ -75,7 +76,7 @@ class LibraryViewMixin(object): # {{{ edit_device_collections=edit_device_collections) edit_device_collections = (_('Manage collections'), - partial(self.edit_device_collections, oncard='carda')) + partial(edc, oncard='carda')) self.card_a_view.set_context_menu(None, None, None, self.action_view, self.action_save, None, None, self.action_del, None, @@ -83,7 +84,7 @@ class LibraryViewMixin(object): # {{{ edit_device_collections=edit_device_collections) edit_device_collections = (_('Manage collections'), - partial(self.edit_device_collections, oncard='cardb')) + partial(edc, oncard='cardb')) self.card_b_view.set_context_menu(None, None, None, self.action_view, self.action_save, None, None, self.action_del, None, diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index 74da5f53d3..582818a593 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -459,10 +459,6 @@ class MainWindowMixin(object): setattr(self, 'action_'+name, action) all_actions.append(action) - ac(0, 0, 0, 'add', _('Add books'), 'add_book.svg', _('A')) - ac(1, 1, 0, 'edit', _('Edit metadata'), 'edit_input.svg', _('E')) - ac(2, 2, 3, 'convert', _('Convert books'), 'convert.svg', _('C')) - ac(3, 3, 0, 'view', _('View'), 'view.svg', _('V')) ac(-1, 4, 0, 'sync', _('Send to device'), 'sync.svg') ac(5, 5, 3, 'choose_library', _('%d books')%0, 'lt.png', tooltip=_('Choose calibre library to work with')) @@ -473,7 +469,6 @@ class MainWindowMixin(object): ac(10, 10, 3, 'help', _('Help'), 'help.svg', _('F1'), _("Browse the calibre User Manual")) ac(11, 11, 0, 'preferences', _('Preferences'), 'config.svg', _('Ctrl+P')) - ac(-1, -1, 0, 'merge', _('Merge book records'), 'merge_books.svg', _('M')) ac(-1, -1, 0, 'open_containing_folder', _('Open containing folder'), 'document_open.svg') ac(-1, -1, 0, 'show_book_details', _('Show book details'), @@ -497,39 +492,6 @@ class MainWindowMixin(object): self.action_conn_share.setMenu(self.share_conn_menu) self.action_help.triggered.connect(self.show_help) - md = QMenu() - md.addAction(_('Edit metadata individually'), - partial(self.edit_metadata, False, bulk=False)) - md.addSeparator() - md.addAction(_('Edit metadata in bulk'), - partial(self.edit_metadata, False, bulk=True)) - md.addSeparator() - md.addAction(_('Download metadata and covers'), - partial(self.download_metadata, False, covers=True), - Qt.ControlModifier+Qt.Key_D) - md.addAction(_('Download only metadata'), - partial(self.download_metadata, False, covers=False)) - md.addAction(_('Download only covers'), - partial(self.download_metadata, False, covers=True, - set_metadata=False, set_social_metadata=False)) - md.addAction(_('Download only social metadata'), - partial(self.download_metadata, False, covers=False, - set_metadata=False, set_social_metadata=True)) - self.metadata_menu = md - - mb = QMenu() - mb.addAction(_('Merge into first selected book - delete others'), - self.merge_books) - mb.addSeparator() - mb.addAction(_('Merge into first selected book - keep others'), - partial(self.merge_books, safe_merge=True)) - self.merge_menu = mb - self.action_merge.setMenu(mb) - md.addSeparator() - md.addAction(self.action_merge) - - self.action_edit.triggered.connect(self.edit_metadata) - self.action_merge.triggered.connect(self.merge_books) self.action_save.triggered.connect(self.save_to_disk) self.save_menu = QMenu() @@ -566,7 +528,6 @@ class MainWindowMixin(object): self.action_sync.triggered.connect( self._sync_action_triggered) - self.action_edit.setMenu(md) self.action_save.setMenu(self.save_menu) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index df0c9091b5..80165ec515 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -439,12 +439,10 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ for action in self.iactions.values(): action.location_selected(location) if location == 'library': - self.action_merge.setEnabled(True) self.action_open_containing_folder.setEnabled(True) self.action_sync.setEnabled(True) self.search_restriction.setEnabled(True) else: - self.action_merge.setEnabled(False) self.action_open_containing_folder.setEnabled(False) self.action_sync.setEnabled(False) self.search_restriction.setEnabled(False)