Mark matching books for deletion feature.

The changes to device.py fix a bug that is independent of the feature.
This commit is contained in:
Charles Haley 2010-06-16 14:30:50 +01:00
parent c484f4316e
commit 94598bd844
4 changed files with 37 additions and 5 deletions

View File

@ -471,6 +471,21 @@ class DeleteAction(object): # {{{
if ids: if ids:
self.tags_view.recount() self.tags_view.recount()
def mark_matching_for_removal(self, *args):
ids = self._get_selected_ids()
if not ids:
return
db = self.library_view.model().db
for model in (self.memory_view.model(), self.card_a_view.model(),
self.card_b_view.model()):
ids_to_mark = []
for id in ids:
uuid = db.uuid(id, index_is_id=True)
for book in model.db:
if getattr(book, 'uuid', None) == uuid:
ids_to_mark.append(id)
break
model.clear_ondevice(ids_to_mark, to_what=False)
def delete_covers(self, *args): def delete_covers(self, *args):
ids = self._get_selected_ids() ids = self._get_selected_ids()

View File

@ -1347,7 +1347,7 @@ class DeviceMixin(object): # {{{
if reset: if reset:
# First build a cache of the library, so the search isn't On**2 # First build a cache of the library, so the search isn't On**2
self.db_book_title_cache = {} self.db_book_title_cache = {}
self.db_book_uuid_cache = set() self.db_book_uuid_cache = {}
db = self.library_view.model().db db = self.library_view.model().db
for id in db.data.iterallids(): for id in db.data.iterallids():
mi = db.get_metadata(id, index_is_id=True) mi = db.get_metadata(id, index_is_id=True)
@ -1364,7 +1364,7 @@ class DeviceMixin(object): # {{{
aus = re.sub('(?u)\W|[_]', '', aus) aus = re.sub('(?u)\W|[_]', '', aus)
self.db_book_title_cache[title]['author_sort'][aus] = mi self.db_book_title_cache[title]['author_sort'][aus] = mi
self.db_book_title_cache[title]['db_ids'][mi.application_id] = mi self.db_book_title_cache[title]['db_ids'][mi.application_id] = mi
self.db_book_uuid_cache.add(mi.uuid) self.db_book_uuid_cache[mi.uuid] = mi.application_id
# Now iterate through all the books on the device, setting the # Now iterate through all the books on the device, setting the
# in_library field Fastest and most accurate key is the uuid. Second is # in_library field Fastest and most accurate key is the uuid. Second is
@ -1376,11 +1376,13 @@ class DeviceMixin(object): # {{{
for book in booklist: for book in booklist:
if getattr(book, 'uuid', None) in self.db_book_uuid_cache: if getattr(book, 'uuid', None) in self.db_book_uuid_cache:
book.in_library = True book.in_library = True
# ensure that the correct application_id is set
book.application_id = self.db_book_uuid_cache[book.uuid]
continue continue
book_title = book.title.lower() if book.title else '' book_title = book.title.lower() if book.title else ''
book_title = re.sub('(?u)\W|[_]', '', book_title) book_title = re.sub('(?u)\W|[_]', '', book_title)
book.in_library = False book.in_library = None
d = self.db_book_title_cache.get(book_title, None) d = self.db_book_title_cache.get(book_title, None)
if d is not None: if d is not None:
if getattr(book, 'application_id', None) in d['db_ids']: if getattr(book, 'application_id', None) in d['db_ids']:

View File

@ -131,6 +131,9 @@ class ToolbarMixin(object): # {{{
self.delete_all_but_selected_formats) self.delete_all_but_selected_formats)
self.delete_menu.addAction( self.delete_menu.addAction(
_('Remove covers from selected books'), self.delete_covers) _('Remove covers from selected books'), self.delete_covers)
self.delete_menu.addAction(
_('Mark matching books on device for removal'),
self.mark_matching_for_removal)
self.action_del.setMenu(self.delete_menu) self.action_del.setMenu(self.delete_menu)
self.action_open_containing_folder.setShortcut(Qt.Key_O) self.action_open_containing_folder.setShortcut(Qt.Key_O)

View File

@ -769,6 +769,7 @@ class OnDeviceSearch(SearchQueryParser): # {{{
'format', 'format',
'formats', 'formats',
'title', 'title',
'inlibrary'
] ]
@ -807,12 +808,21 @@ class OnDeviceSearch(SearchQueryParser): # {{{
'author': lambda x: ' & '.join(getattr(x, 'authors')).lower(), 'author': lambda x: ' & '.join(getattr(x, 'authors')).lower(),
'collections':lambda x: ','.join(getattr(x, 'device_collections')).lower(), 'collections':lambda x: ','.join(getattr(x, 'device_collections')).lower(),
'format':lambda x: os.path.splitext(x.path)[1].lower(), 'format':lambda x: os.path.splitext(x.path)[1].lower(),
'inlibrary':lambda x : getattr(x, 'in_library')
} }
for x in ('author', 'format'): for x in ('author', 'format'):
q[x+'s'] = q[x] q[x+'s'] = q[x]
for index, row in enumerate(self.model.db): for index, row in enumerate(self.model.db):
for locvalue in locations: for locvalue in locations:
accessor = q[locvalue] accessor = q[locvalue]
if query == 'true':
if accessor(row) is not None:
matches.add(index)
continue
if query == 'false':
if accessor(row) is None:
matches.add(index)
continue
try: try:
### Can't separate authors because comma is used for name sep and author sep ### Can't separate authors because comma is used for name sep and author sep
### Exact match might not get what you want. For that reason, turn author ### Exact match might not get what you want. For that reason, turn author
@ -888,13 +898,13 @@ class DeviceBooksModel(BooksModel): # {{{
ans.extend(v) ans.extend(v)
return ans return ans
def clear_ondevice(self, db_ids): def clear_ondevice(self, db_ids, to_what=None):
for data in self.db: for data in self.db:
if data is None: if data is None:
continue continue
app_id = getattr(data, 'application_id', None) app_id = getattr(data, 'application_id', None)
if app_id is not None and app_id in db_ids: if app_id is not None and app_id in db_ids:
data.in_library = False data.in_library = to_what
self.reset() self.reset()
def flags(self, index): def flags(self, index):
@ -1089,6 +1099,8 @@ class DeviceBooksModel(BooksModel): # {{{
elif role == Qt.DecorationRole and cname == 'inlibrary': elif role == Qt.DecorationRole and cname == 'inlibrary':
if self.db[self.map[row]].in_library: if self.db[self.map[row]].in_library:
return QVariant(self.bool_yes_icon) return QVariant(self.bool_yes_icon)
elif self.db[self.map[row]].in_library is not None:
return QVariant(self.bool_no_icon)
elif role == Qt.TextAlignmentRole: elif role == Qt.TextAlignmentRole:
cname = self.column_map[index.column()] cname = self.column_map[index.column()]
ans = Qt.AlignVCenter | ALIGNMENT_MAP[self.alignment_map.get(cname, ans = Qt.AlignVCenter | ALIGNMENT_MAP[self.alignment_map.get(cname,