mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
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:
parent
c484f4316e
commit
94598bd844
@ -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()
|
||||||
|
@ -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']:
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user