Tag Browser: Fix incorrect grouping of words starting with graphemes, such as ash. Fixes #1422116 [Some letters break the category splitting](https://bugs.launchpad.net/calibre/+bug/1422116)

Fix Virtual Libraries not working in the View Manager plugin if the user never clicks the Virtual Library button before using the plugin.

Merge branch 'master' of https://github.com/cbhaley/calibre
This commit is contained in:
Kovid Goyal 2015-02-16 17:58:36 +05:30
commit 69b15134df
4 changed files with 32 additions and 16 deletions

View File

@ -941,11 +941,13 @@ class Cache(object):
return self._search_api(self, query, restriction, virtual_fields=virtual_fields, book_ids=book_ids)
@api
def get_categories(self, sort='name', book_ids=None, icon_map=None, already_fixed=None):
def get_categories(self, sort='name', book_ids=None, icon_map=None, already_fixed=None,
first_letter_sort=False):
' Used internally to implement the Tag Browser '
try:
with self.safe_read_lock:
return get_categories(self, sort=sort, book_ids=book_ids, icon_map=icon_map)
return get_categories(self, sort=sort, book_ids=book_ids, icon_map=icon_map,
first_letter_sort=first_letter_sort)
except InvalidLinkTable as err:
bad_field = err.field_name
if bad_field == already_fixed:

View File

@ -15,7 +15,7 @@ from future_builtins import map
from calibre.ebooks.metadata import author_to_author_sort
from calibre.library.field_metadata import TagsIcons
from calibre.utils.config_base import tweaks
from calibre.utils.icu import sort_key
from calibre.utils.icu import sort_key, collation_order
CATEGORY_SORTS = ('name', 'popularity', 'rating') # This has to be a tuple not a set
@ -116,19 +116,22 @@ def clean_user_categories(dbcache):
pass
return new_cats
def sort_categories(items, sort):
reverse = True
def sort_categories(items, sort, first_letter_sort=False):
if sort == 'popularity':
key=attrgetter('count')
key=lambda x:(-getattr(x, 'count', 0), sort_key(x.sort or x.name))
elif sort == 'rating':
key=attrgetter('avg_rating')
key=lambda x:(-getattr(x, 'avg_rating', 0.0), sort_key(x.sort or x.name))
else:
if first_letter_sort:
key=lambda x:(collation_order(icu_upper(x.sort or x.name or ' ')),
sort_key(x.sort or x.name))
else:
key=lambda x:sort_key(x.sort or x.name)
reverse=False
items.sort(key=key, reverse=reverse)
items.sort(key=key)
return items
def get_categories(dbcache, sort='name', book_ids=None, icon_map=None):
def get_categories(dbcache, sort='name', book_ids=None, icon_map=None,
first_letter_sort=False):
if icon_map is not None and type(icon_map) != TagsIcons:
raise TypeError('icon_map passed to get_categories must be of type TagIcons')
if sort not in CATEGORY_SORTS:
@ -170,7 +173,7 @@ def get_categories(dbcache, sort='name', book_ids=None, icon_map=None):
cat['is_multiple'] and cat['display'].get('is_names', False)):
for item in cats:
item.sort = author_to_author_sort(item.sort)
sort_categories(cats, sort)
sort_categories(cats, sort, first_letter_sort=first_letter_sort)
categories[category] = cats
# Needed for legacy databases that have multiple ratings that

View File

@ -341,6 +341,7 @@ class SearchRestrictionMixin(object):
self.ar_menu = QMenu(_('Additional restriction'))
self.edit_menu = QMenu(_('Edit Virtual Library'))
self.rm_menu = QMenu(_('Remove Virtual Library'))
self.search_restriction_list_built = False
def add_virtual_library(self, db, name, search):
virt_libs = db.prefs.get('virtual_libraries', {})
@ -498,6 +499,7 @@ class SearchRestrictionMixin(object):
return name[0:MAX_VIRTUAL_LIBRARY_NAME_LENGTH].strip()
def build_search_restriction_list(self):
self.search_restriction_list_built = True
from calibre.gui2.ui import get_gui
m = self.ar_menu
m.clear()
@ -539,6 +541,8 @@ class SearchRestrictionMixin(object):
self.apply_search_restriction(index)
def apply_named_search_restriction(self, name):
if not self.search_restriction_list_built:
self.build_search_restriction_list()
if not name:
r = 0
else:
@ -549,6 +553,8 @@ class SearchRestrictionMixin(object):
self.apply_search_restriction(r)
def apply_text_search_restriction(self, search):
if not self.search_restriction_list_built:
self.build_search_restriction_list()
search = unicode(search)
if not search:
self.search_restriction.setCurrentIndex(0)
@ -567,6 +573,8 @@ class SearchRestrictionMixin(object):
self._apply_search_restriction(search, self._trim_restriction_name(s))
def apply_search_restriction(self, i):
if not self.search_restriction_list_built:
self.build_search_restriction_list()
if i == 1:
self.apply_text_search_restriction(unicode(self.search.currentText()))
elif i == 2 and unicode(self.search_restriction.currentText()).startswith('*'):

View File

@ -865,16 +865,19 @@ class TagsModel(QAbstractItemModel): # {{{
# Get the categories
if self.db.data.get_base_restriction() or self.db.data.get_search_restriction():
try:
data = self.db.get_categories(sort=sort,
data = self.db.new_api.get_categories(sort=sort,
icon_map=self.category_icon_map,
ids=self.db.search('', return_matches=True, sort_results=False))
book_ids=self.db.search('', return_matches=True, sort_results=False),
first_letter_sort = self.collapse_model == 'first letter')
except:
import traceback
traceback.print_exc()
data = self.db.get_categories(sort=sort, icon_map=self.category_icon_map)
data = self.db.new_api.get_categories(sort=sort, icon_map=self.category_icon_map,
first_letter_sort = self.collapse_model == 'first letter')
self.restriction_error.emit()
else:
data = self.db.get_categories(sort=sort, icon_map=self.category_icon_map)
data = self.db.new_api.get_categories(sort=sort, icon_map=self.category_icon_map,
first_letter_sort = self.collapse_model == 'first letter')
# Reconstruct the user categories, putting them into metadata
self.db.field_metadata.remove_dynamic_categories()