mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-02 18:47:01 -05:00 
			
		
		
		
	Tag browser: Clicking on a nested category now searches for the category alone. Clicking twice searches fo rthe category and all its descendants and so on. Fixes #9166 (hierarchy indicator for no children). Also fix 9169
This commit is contained in:
		
						commit
						bc27bf06ce
					
				
							
								
								
									
										
											BIN
										
									
								
								resources/images/minusminus.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/images/minusminus.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								resources/images/plusplus.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/images/plusplus.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 4.1 KiB  | 
@ -178,8 +178,10 @@ class TagCategories(QDialog, Ui_TagCategories):
 | 
				
			|||||||
                      'multiple periods in a row or spaces before '
 | 
					                      'multiple periods in a row or spaces before '
 | 
				
			||||||
                      'or after periods.')).exec_()
 | 
					                      'or after periods.')).exec_()
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
        for c in self.categories:
 | 
					        for c in sorted(self.categories.keys(), key=sort_key):
 | 
				
			||||||
            if strcmp(c, cat_name) == 0:
 | 
					            if strcmp(c, cat_name) == 0 or \
 | 
				
			||||||
 | 
					                    (icu_lower(cat_name).startswith(icu_lower(c) + '.') and\
 | 
				
			||||||
 | 
					                     not cat_name.startswith(c + '.')):
 | 
				
			||||||
                error_dialog(self, _('Name already used'),
 | 
					                error_dialog(self, _('Name already used'),
 | 
				
			||||||
                        _('That name is already used, perhaps with different case.')).exec_()
 | 
					                        _('That name is already used, perhaps with different case.')).exec_()
 | 
				
			||||||
                return False
 | 
					                return False
 | 
				
			||||||
 | 
				
			|||||||
@ -217,11 +217,15 @@ class SearchBox2(QComboBox): # {{{
 | 
				
			|||||||
                self.clear()
 | 
					                self.clear()
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                self.normalize_state()
 | 
					                self.normalize_state()
 | 
				
			||||||
 | 
					                self.lineEdit().setCompleter(None)
 | 
				
			||||||
                self.setEditText(txt)
 | 
					                self.setEditText(txt)
 | 
				
			||||||
                self.line_edit.end(False)
 | 
					                self.line_edit.end(False)
 | 
				
			||||||
                if emit_changed:
 | 
					                if emit_changed:
 | 
				
			||||||
                    self.changed.emit()
 | 
					                    self.changed.emit()
 | 
				
			||||||
                self._do_search(store_in_history=store_in_history)
 | 
					                self._do_search(store_in_history=store_in_history)
 | 
				
			||||||
 | 
					                c = QCompleter()
 | 
				
			||||||
 | 
					                self.lineEdit().setCompleter(c)
 | 
				
			||||||
 | 
					                c.setCompletionMode(c.PopupCompletion)
 | 
				
			||||||
            self.focus_to_library.emit()
 | 
					            self.focus_to_library.emit()
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            if not store_in_history:
 | 
					            if not store_in_history:
 | 
				
			||||||
 | 
				
			|||||||
@ -21,6 +21,7 @@ from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, QFont, QSize, \
 | 
				
			|||||||
from calibre.ebooks.metadata import title_sort
 | 
					from calibre.ebooks.metadata import title_sort
 | 
				
			||||||
from calibre.gui2 import config, NONE, gprefs
 | 
					from calibre.gui2 import config, NONE, gprefs
 | 
				
			||||||
from calibre.library.field_metadata import TagsIcons, category_icon_map
 | 
					from calibre.library.field_metadata import TagsIcons, category_icon_map
 | 
				
			||||||
 | 
					from calibre.library.database2 import Tag
 | 
				
			||||||
from calibre.utils.config import tweaks
 | 
					from calibre.utils.config import tweaks
 | 
				
			||||||
from calibre.utils.icu import sort_key, lower, strcmp
 | 
					from calibre.utils.icu import sort_key, lower, strcmp
 | 
				
			||||||
from calibre.utils.search_query_parser import saved_searches
 | 
					from calibre.utils.search_query_parser import saved_searches
 | 
				
			||||||
@ -69,7 +70,8 @@ class TagDelegate(QItemDelegate): # {{{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    # }}}
 | 
					    # }}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TAG_SEARCH_STATES = {'clear': 0, 'mark_plus': 1, 'mark_minus': 2}
 | 
					TAG_SEARCH_STATES = {'clear': 0, 'mark_plus': 1, 'mark_plusplus': 2,
 | 
				
			||||||
 | 
					                     'mark_minus': 3, 'mark_minusminus': 4}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TagsView(QTreeView): # {{{
 | 
					class TagsView(QTreeView): # {{{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -127,13 +129,17 @@ class TagsView(QTreeView): # {{{
 | 
				
			|||||||
        self.set_new_model(self._model.get_filter_categories_by())
 | 
					        self.set_new_model(self._model.get_filter_categories_by())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def set_database(self, db, tag_match, sort_by):
 | 
					    def set_database(self, db, tag_match, sort_by):
 | 
				
			||||||
        self.hidden_categories = db.prefs.get('tag_browser_hidden_categories', None)
 | 
					        hidden_cats = db.prefs.get('tag_browser_hidden_categories', None)
 | 
				
			||||||
 | 
					        self.hidden_categories = []
 | 
				
			||||||
        # migrate from config to db prefs
 | 
					        # migrate from config to db prefs
 | 
				
			||||||
        if self.hidden_categories is None:
 | 
					        if hidden_cats is None:
 | 
				
			||||||
            self.hidden_categories = config['tag_browser_hidden_categories']
 | 
					            hidden_cats = config['tag_browser_hidden_categories']
 | 
				
			||||||
            db.prefs.set('tag_browser_hidden_categories', list(self.hidden_categories))
 | 
					        # strip out any non-existence field keys
 | 
				
			||||||
        else:
 | 
					        for cat in hidden_cats:
 | 
				
			||||||
            self.hidden_categories = set(self.hidden_categories)
 | 
					            if cat in db.field_metadata:
 | 
				
			||||||
 | 
					                self.hidden_categories.append(cat)
 | 
				
			||||||
 | 
					        db.prefs.set('tag_browser_hidden_categories', list(self.hidden_categories))
 | 
				
			||||||
 | 
					        self.hidden_categories = set(self.hidden_categories)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        old = getattr(self, '_model', None)
 | 
					        old = getattr(self, '_model', None)
 | 
				
			||||||
        if old is not None:
 | 
					        if old is not None:
 | 
				
			||||||
@ -370,14 +376,15 @@ class TagsView(QTreeView): # {{{
 | 
				
			|||||||
                                    action='delete_user_category', key=key))
 | 
					                                    action='delete_user_category', key=key))
 | 
				
			||||||
                    self.context_menu.addSeparator()
 | 
					                    self.context_menu.addSeparator()
 | 
				
			||||||
                # Hide/Show/Restore categories
 | 
					                # Hide/Show/Restore categories
 | 
				
			||||||
                if not key.startswith('@') or key.find('.') < 0:
 | 
					                #if not key.startswith('@') or key.find('.') < 0:
 | 
				
			||||||
                    self.context_menu.addAction(_('Hide category %s') % category,
 | 
					                self.context_menu.addAction(_('Hide category %s') % category,
 | 
				
			||||||
                        partial(self.context_menu_handler, action='hide',
 | 
					                    partial(self.context_menu_handler, action='hide',
 | 
				
			||||||
                                category=category))
 | 
					                            category=key))
 | 
				
			||||||
                if self.hidden_categories:
 | 
					                if self.hidden_categories:
 | 
				
			||||||
                    m = self.context_menu.addMenu(_('Show category'))
 | 
					                    m = self.context_menu.addMenu(_('Show category'))
 | 
				
			||||||
                    for col in sorted(self.hidden_categories, key=sort_key):
 | 
					                    for col in sorted(self.hidden_categories,
 | 
				
			||||||
                        m.addAction(col,
 | 
					                            key=lambda x: sort_key(self.db.field_metadata[x]['name'])):
 | 
				
			||||||
 | 
					                        m.addAction(self.db.field_metadata[col]['name'],
 | 
				
			||||||
                            partial(self.context_menu_handler, action='show', category=col))
 | 
					                            partial(self.context_menu_handler, action='show', category=col))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # search by category
 | 
					                # search by category
 | 
				
			||||||
@ -540,6 +547,7 @@ class TagTreeItem(object): # {{{
 | 
				
			|||||||
        self.id_set = set()
 | 
					        self.id_set = set()
 | 
				
			||||||
        self.is_gst = False
 | 
					        self.is_gst = False
 | 
				
			||||||
        self.boxed = False
 | 
					        self.boxed = False
 | 
				
			||||||
 | 
					        self.icon_state_map = list(map(QVariant, icon_map))
 | 
				
			||||||
        if self.parent is not None:
 | 
					        if self.parent is not None:
 | 
				
			||||||
            self.parent.append(self)
 | 
					            self.parent.append(self)
 | 
				
			||||||
        if data is None:
 | 
					        if data is None:
 | 
				
			||||||
@ -554,9 +562,11 @@ class TagTreeItem(object): # {{{
 | 
				
			|||||||
            self.bold_font = QVariant(self.bold_font)
 | 
					            self.bold_font = QVariant(self.bold_font)
 | 
				
			||||||
            self.category_key = category_key
 | 
					            self.category_key = category_key
 | 
				
			||||||
            self.temporary = temporary
 | 
					            self.temporary = temporary
 | 
				
			||||||
 | 
					            self.tag = Tag(data)
 | 
				
			||||||
 | 
					            self.tag.is_hierarchical = category_key.startswith('@')
 | 
				
			||||||
        elif self.type == self.TAG:
 | 
					        elif self.type == self.TAG:
 | 
				
			||||||
            icon_map[0] = data.icon
 | 
					            icon_map[0] = data.icon
 | 
				
			||||||
            self.tag, self.icon_state_map = data, list(map(QVariant, icon_map))
 | 
					            self.tag = data
 | 
				
			||||||
        if tooltip:
 | 
					        if tooltip:
 | 
				
			||||||
            self.tooltip = tooltip + ' '
 | 
					            self.tooltip = tooltip + ' '
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
@ -593,6 +603,8 @@ class TagTreeItem(object): # {{{
 | 
				
			|||||||
        if role == Qt.EditRole:
 | 
					        if role == Qt.EditRole:
 | 
				
			||||||
            return QVariant(self.py_name)
 | 
					            return QVariant(self.py_name)
 | 
				
			||||||
        if role == Qt.DecorationRole:
 | 
					        if role == Qt.DecorationRole:
 | 
				
			||||||
 | 
					            if self.tag.state:
 | 
				
			||||||
 | 
					                return self.icon_state_map[self.tag.state]
 | 
				
			||||||
            return self.icon
 | 
					            return self.icon
 | 
				
			||||||
        if role == Qt.FontRole:
 | 
					        if role == Qt.FontRole:
 | 
				
			||||||
            return self.bold_font
 | 
					            return self.bold_font
 | 
				
			||||||
@ -642,11 +654,21 @@ class TagTreeItem(object): # {{{
 | 
				
			|||||||
        '''
 | 
					        '''
 | 
				
			||||||
        set_to: None => advance the state, otherwise a value from TAG_SEARCH_STATES
 | 
					        set_to: None => advance the state, otherwise a value from TAG_SEARCH_STATES
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        if self.type == self.TAG:
 | 
					        if set_to is None:
 | 
				
			||||||
            if set_to is None:
 | 
					            while True:
 | 
				
			||||||
                self.tag.state = (self.tag.state + 1)%3
 | 
					                self.tag.state = (self.tag.state + 1)%5
 | 
				
			||||||
            else:
 | 
					                if self.tag.state == TAG_SEARCH_STATES['mark_plus'] or \
 | 
				
			||||||
                self.tag.state = set_to
 | 
					                        self.tag.state == TAG_SEARCH_STATES['mark_minus']:
 | 
				
			||||||
 | 
					                    if self.tag.is_editable:
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                elif self.tag.state == TAG_SEARCH_STATES['mark_plusplus'] or\
 | 
				
			||||||
 | 
					                        self.tag.state == TAG_SEARCH_STATES['mark_minusminus']:
 | 
				
			||||||
 | 
					                    if self.tag.is_hierarchical and len(self.children):
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self.tag.state = set_to
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def child_tags(self):
 | 
					    def child_tags(self):
 | 
				
			||||||
        res = []
 | 
					        res = []
 | 
				
			||||||
@ -677,7 +699,8 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
        self.categories_with_ratings = ['authors', 'series', 'publisher', 'tags']
 | 
					        self.categories_with_ratings = ['authors', 'series', 'publisher', 'tags']
 | 
				
			||||||
        self.drag_drop_finished = drag_drop_finished
 | 
					        self.drag_drop_finished = drag_drop_finished
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.icon_state_map = [None, QIcon(I('plus.png')), QIcon(I('minus.png'))]
 | 
					        self.icon_state_map = [None, QIcon(I('plus.png')), QIcon(I('plusplus.png')),
 | 
				
			||||||
 | 
					                               QIcon(I('minus.png')), QIcon(I('minusminus.png'))]
 | 
				
			||||||
        self.db = db
 | 
					        self.db = db
 | 
				
			||||||
        self.tags_view = parent
 | 
					        self.tags_view = parent
 | 
				
			||||||
        self.hidden_categories = hidden_categories
 | 
					        self.hidden_categories = hidden_categories
 | 
				
			||||||
@ -691,26 +714,33 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        data = self.get_node_tree(config['sort_tags_by'])
 | 
					        data = self.get_node_tree(config['sort_tags_by'])
 | 
				
			||||||
        gst = db.prefs.get('grouped_search_terms', {})
 | 
					        gst = db.prefs.get('grouped_search_terms', {})
 | 
				
			||||||
        self.root_item = TagTreeItem()
 | 
					        self.root_item = TagTreeItem(icon_map=self.icon_state_map)
 | 
				
			||||||
        self.category_nodes = []
 | 
					        self.category_nodes = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        last_category_node = None
 | 
					        last_category_node = None
 | 
				
			||||||
        category_node_map = {}
 | 
					        category_node_map = {}
 | 
				
			||||||
        self.category_node_tree = {}
 | 
					        self.category_node_tree = {}
 | 
				
			||||||
        for i, r in enumerate(self.row_map):
 | 
					        for i, key in enumerate(self.row_map):
 | 
				
			||||||
            if self.hidden_categories and self.categories[i] in self.hidden_categories:
 | 
					            if self.hidden_categories:
 | 
				
			||||||
                continue
 | 
					                if key in self.hidden_categories:
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                found = False
 | 
				
			||||||
 | 
					                for cat in self.hidden_categories:
 | 
				
			||||||
 | 
					                    if cat.startswith('@') and key.startswith(cat + '.'):
 | 
				
			||||||
 | 
					                        found = True
 | 
				
			||||||
 | 
					                if found:
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
            is_gst = False
 | 
					            is_gst = False
 | 
				
			||||||
            if r.startswith('@') and r[1:] in gst:
 | 
					            if key.startswith('@') and key[1:] in gst:
 | 
				
			||||||
                tt = _(u'The grouped search term name is "{0}"').format(r[1:])
 | 
					                tt = _(u'The grouped search term name is "{0}"').format(key[1:])
 | 
				
			||||||
                is_gst = True
 | 
					                is_gst = True
 | 
				
			||||||
            elif r == 'news':
 | 
					            elif key == 'news':
 | 
				
			||||||
                tt = ''
 | 
					                tt = ''
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                tt = _(u'The lookup/search name is "{0}"').format(r)
 | 
					                tt = _(u'The lookup/search name is "{0}"').format(key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if r.startswith('@'):
 | 
					            if key.startswith('@'):
 | 
				
			||||||
                path_parts = [p for p in r.split('.')]
 | 
					                path_parts = [p for p in key.split('.')]
 | 
				
			||||||
                path = ''
 | 
					                path = ''
 | 
				
			||||||
                last_category_node = self.root_item
 | 
					                last_category_node = self.root_item
 | 
				
			||||||
                tree_root = self.category_node_tree
 | 
					                tree_root = self.category_node_tree
 | 
				
			||||||
@ -719,9 +749,10 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
                    if path not in category_node_map:
 | 
					                    if path not in category_node_map:
 | 
				
			||||||
                        node = TagTreeItem(parent=last_category_node,
 | 
					                        node = TagTreeItem(parent=last_category_node,
 | 
				
			||||||
                                           data=p[1:] if i == 0 else p,
 | 
					                                           data=p[1:] if i == 0 else p,
 | 
				
			||||||
                                           category_icon=self.category_icon_map[r],
 | 
					                                           category_icon=self.category_icon_map[key],
 | 
				
			||||||
                                           tooltip=tt if path == r else path,
 | 
					                                           tooltip=tt if path == key else path,
 | 
				
			||||||
                                           category_key=path)
 | 
					                                           category_key=path,
 | 
				
			||||||
 | 
					                                           icon_map=self.icon_state_map)
 | 
				
			||||||
                        last_category_node = node
 | 
					                        last_category_node = node
 | 
				
			||||||
                        category_node_map[path] = node
 | 
					                        category_node_map[path] = node
 | 
				
			||||||
                        self.category_nodes.append(node)
 | 
					                        self.category_nodes.append(node)
 | 
				
			||||||
@ -736,11 +767,12 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
                    path += '.'
 | 
					                    path += '.'
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                node = TagTreeItem(parent=self.root_item,
 | 
					                node = TagTreeItem(parent=self.root_item,
 | 
				
			||||||
                                   data=self.categories[i],
 | 
					                                   data=self.categories[key],
 | 
				
			||||||
                                   category_icon=self.category_icon_map[r],
 | 
					                                   category_icon=self.category_icon_map[key],
 | 
				
			||||||
                                   tooltip=tt, category_key=r)
 | 
					                                   tooltip=tt, category_key=key,
 | 
				
			||||||
 | 
					                                   icon_map=self.icon_state_map)
 | 
				
			||||||
                node.is_gst = False
 | 
					                node.is_gst = False
 | 
				
			||||||
                category_node_map[r] = node
 | 
					                category_node_map[key] = node
 | 
				
			||||||
                last_category_node = node
 | 
					                last_category_node = node
 | 
				
			||||||
                self.category_nodes.append(node)
 | 
					                self.category_nodes.append(node)
 | 
				
			||||||
        self.refresh(data=data)
 | 
					        self.refresh(data=data)
 | 
				
			||||||
@ -1015,7 +1047,7 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
    def get_node_tree(self, sort):
 | 
					    def get_node_tree(self, sort):
 | 
				
			||||||
        old_row_map = self.row_map[:]
 | 
					        old_row_map = self.row_map[:]
 | 
				
			||||||
        self.row_map = []
 | 
					        self.row_map = []
 | 
				
			||||||
        self.categories = []
 | 
					        self.categories = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Get the categories
 | 
					        # Get the categories
 | 
				
			||||||
        if self.search_restriction:
 | 
					        if self.search_restriction:
 | 
				
			||||||
@ -1062,7 +1094,7 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
        for category in tb_categories:
 | 
					        for category in tb_categories:
 | 
				
			||||||
            if category in data: # The search category can come and go
 | 
					            if category in data: # The search category can come and go
 | 
				
			||||||
                self.row_map.append(category)
 | 
					                self.row_map.append(category)
 | 
				
			||||||
                self.categories.append(tb_categories[category]['name'])
 | 
					                self.categories[category] = tb_categories[category]['name']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if len(old_row_map) != 0 and len(old_row_map) != len(self.row_map):
 | 
					        if len(old_row_map) != 0 and len(old_row_map) != len(self.row_map):
 | 
				
			||||||
            # A category has been added or removed. We must force a rebuild of
 | 
					            # A category has been added or removed. We must force a rebuild of
 | 
				
			||||||
@ -1163,7 +1195,8 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
                            sub_cat = TagTreeItem(parent=category, data = name,
 | 
					                            sub_cat = TagTreeItem(parent=category, data = name,
 | 
				
			||||||
                                     tooltip = None, temporary=True,
 | 
					                                     tooltip = None, temporary=True,
 | 
				
			||||||
                                     category_icon = category_node.icon,
 | 
					                                     category_icon = category_node.icon,
 | 
				
			||||||
                                     category_key=category_node.category_key)
 | 
					                                     category_key=category_node.category_key,
 | 
				
			||||||
 | 
					                                     icon_map=self.icon_state_map)
 | 
				
			||||||
                            self.endInsertRows()
 | 
					                            self.endInsertRows()
 | 
				
			||||||
                    else: # by 'first letter'
 | 
					                    else: # by 'first letter'
 | 
				
			||||||
                        cl = cl_list[idx]
 | 
					                        cl = cl_list[idx]
 | 
				
			||||||
@ -1173,7 +1206,8 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
                                     data = collapse_letter,
 | 
					                                     data = collapse_letter,
 | 
				
			||||||
                                     category_icon = category_node.icon,
 | 
					                                     category_icon = category_node.icon,
 | 
				
			||||||
                                     tooltip = None, temporary=True,
 | 
					                                     tooltip = None, temporary=True,
 | 
				
			||||||
                                     category_key=category_node.category_key)
 | 
					                                     category_key=category_node.category_key,
 | 
				
			||||||
 | 
					                                     icon_map=self.icon_state_map)
 | 
				
			||||||
                    node_parent = sub_cat
 | 
					                    node_parent = sub_cat
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    node_parent = category
 | 
					                    node_parent = category
 | 
				
			||||||
@ -1284,16 +1318,19 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
                return False
 | 
					                return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            user_cats = self.db.prefs.get('user_categories', {})
 | 
					            user_cats = self.db.prefs.get('user_categories', {})
 | 
				
			||||||
 | 
					            user_cat_keys_lower = [icu_lower(k) for k in user_cats]
 | 
				
			||||||
            ckey = item.category_key[1:]
 | 
					            ckey = item.category_key[1:]
 | 
				
			||||||
 | 
					            ckey_lower = icu_lower(ckey)
 | 
				
			||||||
            dotpos = ckey.rfind('.')
 | 
					            dotpos = ckey.rfind('.')
 | 
				
			||||||
            if dotpos < 0:
 | 
					            if dotpos < 0:
 | 
				
			||||||
                nkey = val
 | 
					                nkey = val
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                nkey = ckey[:dotpos+1] + val
 | 
					                nkey = ckey[:dotpos+1] + val
 | 
				
			||||||
            for c in user_cats:
 | 
					            nkey_lower = icu_lower(nkey)
 | 
				
			||||||
                if c.startswith(ckey):
 | 
					            for c in sorted(user_cats.keys(), key=sort_key):
 | 
				
			||||||
 | 
					                if icu_lower(c).startswith(ckey_lower):
 | 
				
			||||||
                    if len(c) == len(ckey):
 | 
					                    if len(c) == len(ckey):
 | 
				
			||||||
                        if nkey in user_cats:
 | 
					                        if nkey_lower in user_cat_keys_lower:
 | 
				
			||||||
                            error_dialog(self.tags_view, _('Rename user category'),
 | 
					                            error_dialog(self.tags_view, _('Rename user category'),
 | 
				
			||||||
                                _('The name %s is already used')%nkey, show=True)
 | 
					                                _('The name %s is already used')%nkey, show=True)
 | 
				
			||||||
                            return False
 | 
					                            return False
 | 
				
			||||||
@ -1301,7 +1338,7 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
                        del user_cats[ckey]
 | 
					                        del user_cats[ckey]
 | 
				
			||||||
                    elif c[len(ckey)] == '.':
 | 
					                    elif c[len(ckey)] == '.':
 | 
				
			||||||
                        rest = c[len(ckey):]
 | 
					                        rest = c[len(ckey):]
 | 
				
			||||||
                        if (nkey + rest) in user_cats:
 | 
					                        if icu_lower(nkey + rest) in user_cat_keys_lower:
 | 
				
			||||||
                            error_dialog(self.tags_view, _('Rename user category'),
 | 
					                            error_dialog(self.tags_view, _('Rename user category'),
 | 
				
			||||||
                                _('The name %s is already used')%(nkey+rest), show=True)
 | 
					                                _('The name %s is already used')%(nkey+rest), show=True)
 | 
				
			||||||
                            return False
 | 
					                            return False
 | 
				
			||||||
@ -1477,16 +1514,15 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
    def reset_all_states(self, except_=None):
 | 
					    def reset_all_states(self, except_=None):
 | 
				
			||||||
        update_list = []
 | 
					        update_list = []
 | 
				
			||||||
        def process_tag(tag_item):
 | 
					        def process_tag(tag_item):
 | 
				
			||||||
            if tag_item.type != TagTreeItem.CATEGORY:
 | 
					            tag = tag_item.tag
 | 
				
			||||||
                tag = tag_item.tag
 | 
					            if tag is except_:
 | 
				
			||||||
                if tag is except_:
 | 
					                tag_index = self.createIndex(tag_item.row(), 0, tag_item)
 | 
				
			||||||
                    tag_index = self.createIndex(tag_item.row(), 0, tag_item)
 | 
					                self.dataChanged.emit(tag_index, tag_index)
 | 
				
			||||||
                    self.dataChanged.emit(tag_index, tag_index)
 | 
					            elif tag.state != 0 or tag in update_list:
 | 
				
			||||||
                elif tag.state != 0 or tag in update_list:
 | 
					                tag_index = self.createIndex(tag_item.row(), 0, tag_item)
 | 
				
			||||||
                    tag_index = self.createIndex(tag_item.row(), 0, tag_item)
 | 
					                tag.state = 0
 | 
				
			||||||
                    tag.state = 0
 | 
					                update_list.append(tag)
 | 
				
			||||||
                    update_list.append(tag)
 | 
					                self.dataChanged.emit(tag_index, tag_index)
 | 
				
			||||||
                    self.dataChanged.emit(tag_index, tag_index)
 | 
					 | 
				
			||||||
            for t in tag_item.children:
 | 
					            for t in tag_item.children:
 | 
				
			||||||
                process_tag(t)
 | 
					                process_tag(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1503,13 +1539,11 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
        '''
 | 
					        '''
 | 
				
			||||||
        if not index.isValid(): return False
 | 
					        if not index.isValid(): return False
 | 
				
			||||||
        item = index.internalPointer()
 | 
					        item = index.internalPointer()
 | 
				
			||||||
        if item.type == TagTreeItem.TAG:
 | 
					        item.toggle(set_to=set_to)
 | 
				
			||||||
            item.toggle(set_to=set_to)
 | 
					        if exclusive:
 | 
				
			||||||
            if exclusive:
 | 
					            self.reset_all_states(except_=item.tag)
 | 
				
			||||||
                self.reset_all_states(except_=item.tag)
 | 
					        self.dataChanged.emit(index, index)
 | 
				
			||||||
            self.dataChanged.emit(index, index)
 | 
					        return True
 | 
				
			||||||
            return True
 | 
					 | 
				
			||||||
        return False
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def tokens(self):
 | 
					    def tokens(self):
 | 
				
			||||||
        ans = []
 | 
					        ans = []
 | 
				
			||||||
@ -1523,19 +1557,31 @@ class TagsModel(QAbstractItemModel): # {{{
 | 
				
			|||||||
        # into the search string only once. The nodes_seen set helps us do that
 | 
					        # into the search string only once. The nodes_seen set helps us do that
 | 
				
			||||||
        nodes_seen = set()
 | 
					        nodes_seen = set()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        node_searches = {TAG_SEARCH_STATES['mark_plus']       : 'true',
 | 
				
			||||||
 | 
					                         TAG_SEARCH_STATES['mark_plusplus']   : '.true',
 | 
				
			||||||
 | 
					                         TAG_SEARCH_STATES['mark_minus']      : 'false',
 | 
				
			||||||
 | 
					                         TAG_SEARCH_STATES['mark_minusminus'] : '.false'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for node in self.category_nodes:
 | 
					        for node in self.category_nodes:
 | 
				
			||||||
 | 
					            if node.tag.state:
 | 
				
			||||||
 | 
					                ans.append('%s:%s'%(node.category_key, node_searches[node.tag.state]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            key = node.category_key
 | 
					            key = node.category_key
 | 
				
			||||||
            for tag_item in node.child_tags():
 | 
					            for tag_item in node.child_tags():
 | 
				
			||||||
                tag = tag_item.tag
 | 
					                tag = tag_item.tag
 | 
				
			||||||
                if tag.state != TAG_SEARCH_STATES['clear']:
 | 
					                if tag.state != TAG_SEARCH_STATES['clear']:
 | 
				
			||||||
                    prefix = ' not ' if tag.state == TAG_SEARCH_STATES['mark_minus'] \
 | 
					                    if tag.state == TAG_SEARCH_STATES['mark_minus'] or \
 | 
				
			||||||
                                     else ''
 | 
					                            tag.state == TAG_SEARCH_STATES['mark_minusminus']:
 | 
				
			||||||
 | 
					                        prefix = ' not '
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        prefix = ''
 | 
				
			||||||
                    category = tag.category if key != 'news' else 'tag'
 | 
					                    category = tag.category if key != 'news' else 'tag'
 | 
				
			||||||
                    if tag.name and tag.name[0] == u'\u2605': # char is a star. Assume rating
 | 
					                    if tag.name and tag.name[0] == u'\u2605': # char is a star. Assume rating
 | 
				
			||||||
                        ans.append('%s%s:%s'%(prefix, category, len(tag.name)))
 | 
					                        ans.append('%s%s:%s'%(prefix, category, len(tag.name)))
 | 
				
			||||||
                    else:
 | 
					                    else:
 | 
				
			||||||
                        name = original_name(tag)
 | 
					                        name = original_name(tag)
 | 
				
			||||||
                        use_prefix = tag.is_hierarchical
 | 
					                        use_prefix = tag.state in [TAG_SEARCH_STATES['mark_plusplus'],
 | 
				
			||||||
 | 
					                                                   TAG_SEARCH_STATES['mark_minusminus']]
 | 
				
			||||||
                        if category == 'tags':
 | 
					                        if category == 'tags':
 | 
				
			||||||
                            if name in tags_seen:
 | 
					                            if name in tags_seen:
 | 
				
			||||||
                                continue
 | 
					                                continue
 | 
				
			||||||
 | 
				
			|||||||
@ -419,28 +419,23 @@ class ResultCache(SearchQueryParser): # {{{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def get_user_category_matches(self, location, query, candidates):
 | 
					    def get_user_category_matches(self, location, query, candidates):
 | 
				
			||||||
        res = set([])
 | 
					        res = set([])
 | 
				
			||||||
        if self.db_prefs is None:
 | 
					        if self.db_prefs is None or len(query) < 2:
 | 
				
			||||||
            return  res
 | 
					            return  res
 | 
				
			||||||
        user_cats = self.db_prefs.get('user_categories', [])
 | 
					        user_cats = self.db_prefs.get('user_categories', [])
 | 
				
			||||||
        c = set(candidates)
 | 
					        c = set(candidates)
 | 
				
			||||||
        l = location.rfind('.')
 | 
					
 | 
				
			||||||
        if l > 0:
 | 
					        if query.startswith('.'):
 | 
				
			||||||
            alt_loc = location[0:l]
 | 
					            check_subcats = True
 | 
				
			||||||
            alt_item = location[l+1:]
 | 
					            query = query[1:]
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            alt_loc = None
 | 
					            check_subcats = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for key in user_cats:
 | 
					        for key in user_cats:
 | 
				
			||||||
            if key == location or key.startswith(location + '.'):
 | 
					            if key == location or (check_subcats and key.startswith(location + '.')):
 | 
				
			||||||
                for (item, category, ign) in user_cats[key]:
 | 
					                for (item, category, ign) in user_cats[key]:
 | 
				
			||||||
                    s = self.get_matches(category, '=' + item, candidates=c)
 | 
					                    s = self.get_matches(category, '=' + item, candidates=c)
 | 
				
			||||||
                    c -= s
 | 
					                    c -= s
 | 
				
			||||||
                    res |= s
 | 
					                    res |= s
 | 
				
			||||||
            elif key == alt_loc:
 | 
					 | 
				
			||||||
                for (item, category, ign) in user_cats[key]:
 | 
					 | 
				
			||||||
                    if item == alt_item:
 | 
					 | 
				
			||||||
                        s = self.get_matches(category, '=' + item, candidates=c)
 | 
					 | 
				
			||||||
                        c -= s
 | 
					 | 
				
			||||||
                        res |= s
 | 
					 | 
				
			||||||
        if query == 'false':
 | 
					        if query == 'false':
 | 
				
			||||||
            return candidates - res
 | 
					            return candidates - res
 | 
				
			||||||
        return res
 | 
					        return res
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user