diff --git a/src/calibre/gui2/dialogs/tag_categories.py b/src/calibre/gui2/dialogs/tag_categories.py index af6632bb02..9bddb817cf 100644 --- a/src/calibre/gui2/dialogs/tag_categories.py +++ b/src/calibre/gui2/dialogs/tag_categories.py @@ -171,6 +171,13 @@ class TagCategories(QDialog, Ui_TagCategories): cat_name = unicode(self.input_box.text()).strip() if cat_name == '': return False + comps = [c.strip() for c in cat_name.split('.') if c.strip()] + if len(comps) == 0 or '.'.join(comps) != cat_name: + error_dialog(self, _('Invalid name'), + _('That name contains leading or trailing periods, ' + 'multiple periods in a row or spaces before ' + 'or after periods.')).exec_() + return False for c in self.categories: if strcmp(c, cat_name) == 0: error_dialog(self, _('Name already used'), @@ -193,6 +200,14 @@ class TagCategories(QDialog, Ui_TagCategories): return False if not self.current_cat_name: return False + comps = [c.strip() for c in cat_name.split('.') if c.strip()] + if len(comps) == 0 or '.'.join(comps) != cat_name: + error_dialog(self, _('Invalid name'), + _('That name contains leading or trailing periods, ' + 'multiple periods in a row or spaces before ' + 'or after periods.')).exec_() + return False + for c in self.categories: if strcmp(c, cat_name) == 0: error_dialog(self, _('Name already used'), diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 83ea97b880..e4b4552504 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -685,7 +685,9 @@ class TagsModel(QAbstractItemModel): # {{{ self.filter_categories_by = filter_categories_by self.collapse_model = collapse_model - # get_node_tree cannot return None here, because row_map is empty + # get_node_tree cannot return None here, because row_map is empty. Note + # that get_node_tree can indirectly change the user_categories dict. + data = self.get_node_tree(config['sort_tags_by']) gst = db.prefs.get('grouped_search_terms', {}) self.root_item = TagTreeItem() @@ -707,7 +709,7 @@ class TagsModel(QAbstractItemModel): # {{{ tt = _(u'The lookup/search name is "{0}"').format(r) if r.startswith('@'): - path_parts = [p.strip() for p in r.split('.') if p.strip()] + path_parts = [p for p in r.split('.')] path = '' last_category_node = self.root_item tree_root = self.category_node_tree @@ -1178,7 +1180,10 @@ class TagsModel(QAbstractItemModel): # {{{ # category display order is important here. The following works # only of all the non-user categories are displayed before the # user categories - components = [t for t in original_name(tag).split('.')] + components = [t.strip() for t in original_name(tag).split('.') + if t.strip()] + if len(components) == 0 or '.'.join(components) != original_name(tag): + components = [original_name(tag)] in_uc = fm['kind'] == 'user' if (not tag.is_hierarchical) and (in_uc or key in ['authors', 'publisher', 'news', 'formats', 'rating'] or @@ -1264,7 +1269,7 @@ class TagsModel(QAbstractItemModel): # {{{ # working with the last item and that item is deleted, in which case # we position at the parent label path = index.model().path_for_index(index) - val = unicode(value.toString()) + val = unicode(value.toString()).strip() if not val: error_dialog(self.tags_view, _('Item is blank'), _('An item cannot be set to nothing. Delete it instead.')).exec_() diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 5155afe7e9..4be2ba4340 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1181,6 +1181,22 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): return 'n=%s s=%s c=%d rt=%d rc=%d id=%s'%\ (self.n, self.s, self.c, self.rt, self.rc, self.id) + def clean_user_categories(self): + user_cats = self.prefs.get('user_categories', {}) + new_cats = {} + for k in user_cats: + comps = [c.strip() for c in k.split('.') if c.strip()] + if len(comps) == 0: + i = 1 + while True: + if unicode(i) not in user_cats: + new_cats[unicode(i)] = user_cats[k] + break + i += 1 + else: + new_cats['.'.join(comps)] = user_cats[k] + self.prefs.set('user_categories', new_cats) + return new_cats def get_categories(self, sort='name', ids=None, icon_map=None): #start = last = time.clock() @@ -1421,7 +1437,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): categories['formats'].sort(key = lambda x:x.name) #### Now do the user-defined categories. #### - user_categories = dict.copy(self.prefs['user_categories']) + user_categories = dict.copy(self.clean_user_categories()) # We want to use same node in the user category as in the source # category. To do that, we need to find the original Tag node. There is