diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index eae6dc79c3..fa7ba3c56d 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -551,7 +551,11 @@ class BulkBool(BulkBase, Bool): def setup_ui(self, parent): self.make_widgets(parent, QComboBox) - items = [_('Yes'), _('No'), _('Undefined')] + items = [_('Yes'), _('No')] + if tweaks['bool_custom_columns_are_tristate'] == 'no': + items.append('') + else: + items.append(_('Undefined')) icons = [I('ok.png'), I('list_remove.png'), I('blank.png')] self.main_widget.blockSignals(True) for icon, text in zip(icons, items): @@ -560,7 +564,10 @@ class BulkBool(BulkBase, Bool): def getter(self): val = self.main_widget.currentIndex() - return {2: None, 1: False, 0: True}[val] + if tweaks['bool_custom_columns_are_tristate'] == 'no': + return {2: False, 1: False, 0: True}[val] + else: + return {2: None, 1: False, 0: True}[val] def setter(self, val): val = {None: 2, False: 1, True: 0}[val] @@ -576,6 +583,14 @@ class BulkBool(BulkBase, Bool): val = False self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify) + def a_c_checkbox_changed(self): + if not self.ignore_change_signals: + if tweaks['bool_custom_columns_are_tristate'] == 'no' and \ + self.main_widget.currentIndex() == 2: + self.a_c_checkbox.setChecked(False) + else: + self.a_c_checkbox.setChecked(True) + class BulkInt(BulkBase): def setup_ui(self, parent): diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 3540575f81..e4096f5761 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -1292,6 +1292,16 @@ class DeviceMixin(object): # {{{ to both speed up matching and to count matches. ''' + if not self.device_manager.is_device_connected: + return False + + # It might be possible to get here without having initialized the + # library view. In this case, simply give up + try: + db = self.library_view.model().db + except: + return False + string_pat = re.compile('(?u)\W|[_]') def clean_string(x): x = x.lower() if x else '' @@ -1299,26 +1309,19 @@ class DeviceMixin(object): # {{{ update_metadata = prefs['manage_device_metadata'] == 'on_connect' + get_covers = False + if update_metadata and self.device_manager.is_device_connected: + if self.device_manager.device.WANTS_UPDATED_THUMBNAILS: + get_covers = True + # Force a reset if the caches are not initialized if reset or not hasattr(self, 'db_book_title_cache'): # Build a cache (map) of the library, so the search isn't On**2 db_book_title_cache = {} db_book_uuid_cache = {} - # It might be possible to get here without having initialized the - # library view. In this case, simply give up - try: - db = self.library_view.model().db - except: - return False - get_covers = False - if update_metadata and self.device_manager.is_device_connected: - if self.device_manager.device.WANTS_UPDATED_THUMBNAILS: - get_covers = True - - for id in db.data.iterallids(): - mi = db.get_metadata(id, index_is_id=True, get_cover=get_covers) - title = clean_string(mi.title) + for id_ in db.data.iterallids(): + title = clean_string(db.title(id_, index_is_id=True)) if title not in db_book_title_cache: db_book_title_cache[title] = \ {'authors':{}, 'author_sort':{}, 'db_ids':{}} @@ -1326,14 +1329,14 @@ class DeviceMixin(object): # {{{ # and author, then remember the last one. That is OK, because as # we can't tell the difference between the books, one is as good # as another. - if mi.authors: - authors = clean_string(authors_to_string(mi.authors)) - db_book_title_cache[title]['authors'][authors] = mi - if mi.author_sort: - aus = clean_string(mi.author_sort) - db_book_title_cache[title]['author_sort'][aus] = mi - db_book_title_cache[title]['db_ids'][mi.application_id] = mi - db_book_uuid_cache[mi.uuid] = mi + authors = clean_string(db.authors(id_, index_is_id=True)) + if authors: + db_book_title_cache[title]['authors'][authors] = id_ + if db.author_sort(id_, index_is_id=True): + aus = clean_string(db.author_sort(id_, index_is_id=True)) + db_book_title_cache[title]['author_sort'][aus] = id_ + db_book_title_cache[title]['db_ids'][id_] = id_ + db_book_uuid_cache[db.uuid(id_, index_is_id=True)] = id_ self.db_book_title_cache = db_book_title_cache self.db_book_uuid_cache = db_book_uuid_cache @@ -1341,19 +1344,22 @@ class DeviceMixin(object): # {{{ # in_library field. If the UUID matches a book in the library, then # do not consider that book for other matching. In all cases set # the application_id to the db_id of the matching book. This value - # will be used by books_on_device to indicate matches. + # will be used by books_on_device to indicate matches. While we are + # going by, update the metadata for a book if automatic management is on for booklist in booklists: for book in booklist: book.in_library = None if getattr(book, 'uuid', None) in self.db_book_uuid_cache: + id_ = db_book_uuid_cache[book.uuid] if update_metadata: - book.smart_update(self.db_book_uuid_cache[book.uuid], + book.smart_update(db.get_metadata(id_, + index_is_id=True, + get_cover=get_covers), replace_metadata=True) book.in_library = 'UUID' # ensure that the correct application_id is set - book.application_id = \ - self.db_book_uuid_cache[book.uuid].application_id + book.application_id = id_ continue # No UUID exact match. Try metadata matching. book_title = clean_string(book.title) @@ -1363,21 +1369,25 @@ class DeviceMixin(object): # {{{ # will match if any of the db_id, author, or author_sort # also match. if getattr(book, 'application_id', None) in d['db_ids']: - # app_id already matches a db_id. No need to set it. if update_metadata: - book.smart_update(d['db_ids'][book.application_id], + id_ = getattr(book, 'application_id', None) + book.smart_update(db.get_metadata(id_, + index_is_id=True, + get_cover=get_covers), replace_metadata=True) book.in_library = 'APP_ID' + # app_id already matches a db_id. No need to set it. continue # Sonys know their db_id independent of the application_id # in the metadata cache. Check that as well. if getattr(book, 'db_id', None) in d['db_ids']: if update_metadata: - book.smart_update(d['db_ids'][book.db_id], + book.smart_update(db.get_metadata(book.db_id, + index_is_id=True, + get_cover=get_covers), replace_metadata=True) book.in_library = 'DB_ID' - book.application_id = \ - d['db_ids'][book.db_id].application_id + book.application_id = book.db_id continue # We now know that the application_id is not right. Set it # to None to prevent book_on_device from accidentally @@ -1389,19 +1399,23 @@ class DeviceMixin(object): # {{{ # either can appear as the author book_authors = clean_string(authors_to_string(book.authors)) if book_authors in d['authors']: + id_ = d['authors'][book_authors] if update_metadata: - book.smart_update(d['authors'][book_authors], - replace_metadata=True) + book.smart_update(db.get_metadata(id_, + index_is_id=True, + get_cover=get_covers), + replace_metadata=True) book.in_library = 'AUTHOR' - book.application_id = \ - d['authors'][book_authors].application_id + book.application_id = id_ elif book_authors in d['author_sort']: + id_ = d['author_sort'][book_authors] if update_metadata: - book.smart_update(d['author_sort'][book_authors], + book.smart_update(db.get_metadata(id_, + index_is_id=True, + get_cover=get_covers), replace_metadata=True) book.in_library = 'AUTH_SORT' - book.application_id = \ - d['author_sort'][book_authors].application_id + book.application_id = id_ else: # Book definitely not matched. Clear its application ID book.application_id = None diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 48668d3376..88008e7ec4 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -685,7 +685,7 @@ class BooksModel(QAbstractTableModel): # {{{ self.dc[col] = functools.partial(bool_type, idx=idx) self.dc_decorator[col] = functools.partial( bool_type_decorator, idx=idx, - bool_cols_are_tristate=tweaks['bool_custom_columns_are_tristate'] == 'yes') + bool_cols_are_tristate=tweaks['bool_custom_columns_are_tristate'] != 'no') elif datatype == 'rating': self.dc[col] = functools.partial(rating_type, idx=idx) elif datatype == 'series': diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 1330d10e59..70e1fec131 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -528,7 +528,7 @@ class ResultCache(SearchQueryParser): # {{{ location[i] = db_col[loc] # get the tweak here so that the string lookup and compare aren't in the loop - bools_are_tristate = tweaks['bool_custom_columns_are_tristate'] == 'yes' + bools_are_tristate = tweaks['bool_custom_columns_are_tristate'] != 'no' for loc in location: # location is now an array of field indices if loc == db_col['authors']: @@ -812,7 +812,10 @@ class SortKeyGenerator(object): val = self.string_sort_key(val) elif dt == 'bool': - val = {True: 1, False: 2, None: 3}.get(val, 3) + if tweaks['bool_custom_columns_are_tristate'] == 'no': + val = {True: 1, False: 2, None: 2}.get(val, 2) + else: + val = {True: 1, False: 2, None: 3}.get(val, 3) yield val