Temporary marking of books in the library

This allows you to select books from your calibre library manually and
mark them. This "mark" will remain until you restart calibre, or clear
the marks. You can easily work with only the marked subset of books.
To use this feature, go to Preferences->Toolbars and add the "Mark
Books" tool to the main toolbar.
This commit is contained in:
Kovid Goyal 2013-10-05 11:02:37 +05:30
parent ea87178c1d
commit 60c78bef61
3 changed files with 144 additions and 1 deletions

View File

@ -923,6 +923,11 @@ class ActionSortBy(InterfaceActionBase):
actual_plugin = 'calibre.gui2.actions.sort:SortByAction'
description = _('Sort the list of books')
class ActionMarkBooks(InterfaceActionBase):
name = 'Mark Books'
actual_plugin = 'calibre.gui2.actions.mark_books:MarkBooksAction'
description = _('Temporarily mark books')
class ActionStore(InterfaceActionBase):
name = 'Store'
author = 'John Schember'
@ -953,7 +958,8 @@ plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog,
ActionSendToDevice, ActionHelp, ActionPreferences, ActionSimilarBooks,
ActionAddToLibrary, ActionEditCollections, ActionMatchBooks, ActionChooseLibrary,
ActionCopyToLibrary, ActionTweakEpub, ActionNextMatch, ActionStore,
ActionPluginUpdater, ActionPickRandom, ActionEditToC, ActionSortBy]
ActionPluginUpdater, ActionPickRandom, ActionEditToC, ActionSortBy,
ActionMarkBooks]
# }}}

View File

@ -382,6 +382,12 @@ class View(object):
if func is not None:
func(old_marked_ids, cmids)
def toggle_marked_ids(self, book_ids):
book_ids = set(book_ids)
mids = set(self.marked_ids)
common = mids.intersection(book_ids)
self.set_marked_ids((mids | book_ids) - common)
def refresh(self, field=None, ascending=True, clear_caches=True, do_search=True):
self._map = tuple(sorted(self.cache.all_book_ids()))
self._map_filtered = tuple(self._map)

View File

@ -0,0 +1,131 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
from functools import partial
from PyQt4.Qt import QTimer, QApplication, Qt
from calibre.gui2 import error_dialog
from calibre.gui2.actions import InterfaceAction
class MarkBooksAction(InterfaceAction):
name = 'Mark Books'
action_spec = (_('Mark Books'), 'marked.png', _('Temporarily mark books'), 'Ctrl+M')
action_type = 'current'
action_add_menu = True
dont_add_to = frozenset([
'toolbar-device', 'context-menu-device', 'menubar-device', 'context-menu-cover-browser'])
action_menu_clone_qaction = _('Toggle mark for selected books')
accepts_drops = True
def accept_enter_event(self, event, mime_data):
if mime_data.hasFormat("application/calibre+from_library"):
return True
return False
def accept_drag_move_event(self, event, mime_data):
if mime_data.hasFormat("application/calibre+from_library"):
return True
return False
def drop_event(self, event, mime_data):
mime = 'application/calibre+from_library'
if mime_data.hasFormat(mime):
self.dropped_ids = tuple(map(int, str(mime_data.data(mime)).split()))
QTimer.singleShot(1, self.do_drop)
return True
return False
def do_drop(self):
book_ids = self.dropped_ids
del self.dropped_ids
if book_ids:
self.toggle_ids(book_ids)
def genesis(self):
self.qaction.triggered.connect(self.toolbar_triggered)
self.menu = m = self.qaction.menu()
m.aboutToShow.connect(self.about_to_show_menu)
ma = partial(self.create_menu_action, m)
self.show_marked_action = a = ma('show-marked', _('Show marked books'), icon='search.png', shortcut='Shift+Ctrl+M')
a.triggered.connect(self.show_marked)
self.clear_marked_action = a = ma('clear-all-marked', _('Clear all marked books'), icon='clear_left.png')
a.triggered.connect(self.clear_all_marked)
m.addSeparator()
self.mark_author_action = a = ma('mark-author', _('Mark all books by selected author(s)'), icon='plus.png')
a.triggered.connect(partial(self.mark_field, 'authors', True))
self.mark_series_action = a = ma('mark-series', _('Mark all books in the selected series'), icon='plus.png')
a.triggered.connect(partial(self.mark_field, 'series', True))
m.addSeparator()
self.unmark_author_action = a = ma('unmark-author', _('Clear all books by selected author(s)'), icon='minus.png')
a.triggered.connect(partial(self.mark_field, 'authors', False))
self.unmark_series_action = a = ma('unmark-series', _('Clear all books in the selected series'), icon='minus.png')
a.triggered.connect(partial(self.mark_field, 'series', False))
def about_to_show_menu(self):
db = self.gui.current_db
num = len(db.data.marked_ids)
text = ngettext('Show marked book', 'Show marked books (%d)' % num, num)
self.show_marked_action.setText(text)
def location_selected(self, loc):
enabled = loc == 'library'
self.qaction.setEnabled(enabled)
def toolbar_triggered(self):
mods = QApplication.keyboardModifiers()
if mods & Qt.ControlModifier or mods & Qt.ShiftModifier:
self.show_marked()
else:
self.toggle_selected()
def toggle_selected(self):
book_ids = self._get_selected_ids()
if book_ids:
self.toggle_ids(book_ids)
def _get_selected_ids(self):
rows = self.gui.library_view.selectionModel().selectedRows()
if not rows or len(rows) == 0:
d = error_dialog(self.gui, _('Cannot mark'), _('No books selected'))
d.exec_()
return set([])
return set(map(self.gui.library_view.model().id, rows))
def toggle_ids(self, book_ids):
self.gui.current_db.data.toggle_marked_ids(book_ids)
def show_marked(self):
self.gui.search.set_search_string('marked:true')
def clear_all_marked(self):
self.gui.current_db.data.set_marked_ids(())
if unicode(self.gui.search.text()).startswith('marked:'):
self.gui.search.set_search_string('')
def mark_field(self, field, add):
book_ids = self._get_selected_ids()
if not book_ids:
return
db = self.gui.current_db
items = set()
for book_id in book_ids:
items |= set(db.new_api.field_ids_for(field, book_id))
book_ids = set()
for item_id in items:
book_ids |= db.new_api.books_for_field(field, item_id)
mids = db.data.marked_ids.copy()
for book_id in book_ids:
if add:
mids[book_id] = True
else:
mids.pop(book_id, None)
db.data.set_marked_ids(mids)