From 7c22f4ffa145e8bb3cd927f475725a676eb3e74c Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 26 Feb 2011 07:16:43 +0000 Subject: [PATCH] Fix #9169: not possible to hide user category with hierarchy Fix #9166: hierarchy indicator for no children Fix removes all hidden tag browser categories. They must be re-hidden. Fix adds clicking on category nodes to search, plus four-state searching. --- resources/images/minusminus.png | Bin 0 -> 1883 bytes resources/images/plusplus.png | Bin 0 -> 4249 bytes src/calibre/gui2/tag_view.py | 167 ++++++++++++++++++++------------ src/calibre/library/caches.py | 21 ++-- 4 files changed, 114 insertions(+), 74 deletions(-) create mode 100644 resources/images/minusminus.png create mode 100644 resources/images/plusplus.png diff --git a/resources/images/minusminus.png b/resources/images/minusminus.png new file mode 100644 index 0000000000000000000000000000000000000000..71225be8d7f6e5691e4efa5135c500c140141437 GIT binary patch literal 1883 zcmb`IdpOgJAIHCzd(u|SE!Wdw!#O13C~}#GVVId|4e>k9$R&nKu}LGHFe#>?OoX`| z*~(=jhbi~uWRy$vBg!S($|XNNzvuZq&+o7E$M5%ip3mp~Jg?{T`Q!c1=Y8GN1G!IL zT^;~{eJ;)j?_H&QZ&}dpbR&qEyCO~Y_P_u;-^J*D?=A80WI1Vf zYtsc`?{i`7BeMssxeeX+SBefYOI~B zhh9d5j|le^sO~xYR{oSi$>JX=^j9f^9l`lX+=v#9w0W&R-5tOYf(VY#1I0I&f0ks1Z1vxv~|A_Z(N~&Lt_n{-L zn3l$ObH`tEIM!?rsvEOq``(Mc0teWfYF{NJ>x@srnK<*xCGUu#%F`XcEi75|7oyfb zzZYa7@ol0!6Kxq8T$g|gg&wAyRc+C4Q*0GbB|U*uR^;*zZ@4G-fO*zctvIVplJ-S$ z-St#($(IG?0AeQEL;q)DNQKXKp?n|M#}ChFiL0u6q|_6#?sh6FQrdk6clV_;>Nze&1E(7rj^k=xPS!>%*zfvRIfXp< z@nc31j0TB{{IzK!=`G?PiQ~7XyHYs4HZrDg8w@D6S3a!zOxyrCgEjQk;3vB0e$BJb zN?~~nlG!lZ4ShOGixk?AK(>*ScQQ$49i9q~E{7Ehtw##_B3mxBTUz7?hu~gALL+60 z3V)bZV%av)IhAw?HVPk#nRc*3|z7dO+J#ch5w+z3qoRv!%Ko>)~Tt<*- z$8XPn9ey@DX&GhK^NG-6{P3yB6SU+quGn;RsXrYhPM4(7=if7m?{02?5N?f)RaD`c z4V8lmdd=I-jk2^~2^*tjNBs%xXdVW}HZlIw-~ZFhObMaJb^_LT57wA=@F8|o3%Qp( z^>S);8UdQu)LdmDRVG>!&(C!IbK{Z}MWWX>QG_zYGV(q5O5qKUV5=L#r@?{vJHS+@ zo(Yue>&xzQZ~x^n%jC)sH((=ZatjkUkJqcOEk~Am7Yn*SHi&x7){tJ}7u}yx%pN+}rsO2=rJ2I^uJAMVXNLbyH39d21VB+zUAaE45hO1R9RA>u zG0_m~>&q!R0Z;m(W2Ee*G3W!}$CCuq+!BKH@eRn`)Purv=Py3FBxB4^-a#jb$LWwC zv@GAQB8>l1$`Zu2!Z|m}Dh-1RYN{$LL9h%xC8v?NZ0|d+ZnFB4y#s64<#>;~trEJt zs^uEq35O;Z$7)|pj9@RVv=vePEHvN}X)#YeQ>hD`jkgApnar2tW3DJviJviO9`KI{ zS9wFMW=9+o$CRyQNF)Dz5E{R4!+F}KmBZ3KSCvnQZPYR6jA4aCTi3ex3>8e8l0Xq?7XBf77q@^q6G_ z6eS=Y7`ZfxY=~*FV4a{qlZz|JS*mb6@9q&hy;&bDisaI48f8|#|@j!uTG#o2&k4~xeQOD_O`^UjF@^Of_Q001sIZFLn>zmdPU zZ@UN%K0NHK`cN~6}UE z>ACaNsn-&tc|7;lx)DHX<2^eI$f!I9ZymSr#iwxSy}fn0c$AE&4$qNqYWm07bfwW@ zQBk7}+Q1@5>fX<2tD=Ze%38auv$Wuw6t7a-u=D&se7rF0Y;?jc_{}#yFe)OB_Ux)(P5)cSab<5NP)1%JNG#y6kM}wKElfAq7l|{bIn-440w3|$E{k!N2mxW>Djg|g}#vV51 z7jejnnK$29fvJ}#C-!$T6@B)W#?c;2hJk&CZuTfy@yof+xw+re!s|y{B1T&b-PSU! zH_y}eM@$uaLb$E3FzrtV1^!KqP*ygovhet*R+SK2mGH3Ge84oDffwC`_whq1D3Z@+ z#5;=E2g&cgI-Nd5B>}r$JSG%ND;QN@O*!y*@kglL_05|r5_1hWMUV)X>;#vseEl{+ z&5bM}fP5i@4Dt>PTpyY?UmSZ&FPJSyK>T(O&UZ9>&CY7>z5MjB_-L_y%r|xv^H%u7 zQDBlR0UuZbnx6;HL zFIW<|+#YH0N#@YFbDHT~PKQzjMg=QcjOx)^JD-txluysA+(_aycW`L)U*VvD=v=xB zWOBjqiF#Bx1CGE%ezaz`apl}^Jv=J-9p+$$`K>0z5CeXSXEGr?;(289T!3!LpbTWEI@O!&}et$rKt4pA+E*t>n*d z0$9L*>`Jy2lIt6J0HLM2bsfa0U_6s3Hk7SeABw`DF`=iR9(wT%_d@}UP)%?Me}Az7 zG1>`2Cp{|FJ=F^SBl4ur^b#{y-U5pNBdxS(9<^S$Z!1#ul9 zAlTSqjk~8zg-C@B`~hH026)}w!f6y55bz2VUN-`-YlpT%w{1h;@<|O;vpK6s)$T&L z5(dB6DC{r(bess#gaw~b2f$!;>gqi601-xbP9 z3wx0g?Tv9lXtbObt}!nC0>N%p&_pR|VmxT?K#lpF;9-MnK#e?w*tV9MDZkPG=Wa=S zM_hgP=bh*jb|%n(Ah^CAAVLN{|3^8hpp^&Ai+;}=LZrc4)$Tw)pj!HQSnW=eL-|dk zAi2(sFb9o|_4d|bztvkSV+Wt6I8%Z5_`lh@Qn1!jie{iNOTlUCH*F`|HfjdA3z~+beC9!U*Eh zrfvOMAYK`UNZuu3^)z zT_)&@6@0#mhNFB>4H}sU{kGh}EI4&&r-zK%@TYwDoVU{hJ%bgS9Q5m6F5_JtdsUAp z*F9G*3Tf1_&Dw~W>3}48@=^WhtKAcOMY0*oGb)vKfYuIu^NXq*+FVcrdnVO;2K{Hm z_I4_A4gU@)E zQ09}@34g3GOzD}|kv3nGSI@+|&J8b5q}tdn$Cz7Z0#sX6hQ(auvhhn+{;YLa^*c&T ztSRra{crM&eiX&1OMI6sybx){f}C)r!YTeJFpR;Jq; z_gl&fBLK*AAs|bOXW_c9b=OwXIji=QLm@hi>a2FGFrs)RCz&4x`;31mK$?pVsX*A+ z5n2`VBbUXu7HS>LgZF}D{MWyi9R7ZCOS)j-RG7st%6z|zBDY3kyXOll_p(y}Yk|uG zF{$19l@S1aaeV@wqlXL-xj-9RiMk*yp3G?;@SNXy9I@o@{-@?HT-MKjH8kk@PQNwx z#abF)(E%xp5o5+(*H22cdYJ^t_sZgemDm(hfr2;{FskJ#ShrrJ6ErYQ9rq@vHkQ z!TJl%kqc#dM!;5(8Sq(wZ+y7y;h>lM$LDR+{+?AXGJQ!M4)2F=A(Fb**X=zmed6S1 zJ^53CNoc-=HqR6lJ5_=@`I13Kni7WBfa&IZ82~2IOw&|1)X0aoKPR`&cSaV!TPoI5 z(9&&&3Ol?!Y{zQBmb*72Gt&K~E3+u8?Z12aY{&KnWRdjWKkEUE9QJVG62hCf_L^|g zBxzVmA$K_>O6l^80=s%C1-s_PC|zdQbfsfk^6v=|lf z0tp<}33dnC>*}r?&*GIkI z-?P*0mn{QJy=G7J&YjM7?@a*ov#QtGpK(raV2lF_fwQd)+*R-`kh7aCKI`@f;`si zT$xkelw7g21?s8ZX;6vfM3W#{^##{N2v)!9q|oZ1a{H~ENXNFF{652@sbvmmx#Q=& zqs0%(f-M{}eny2~Kp>aJi6>K>@%!6D@$|J1OM-Hb?45md%u@Fau)UGsA#qE;t88rv zOh!`o#K?@yk`z{TIDw?yE5sU6a)1)oW1}U(bO<7nGvKAOhwG{3%Bz$FxKwW)d%TpS5_CD6Q?f%H1SZ`c=13_OV<&5 z)`6itF_%vEOBj(~F>Z^1jT%83>OWRAAS9?j*A$j~*%rKrK;ah6#?161^`9#{*$%8y z>}9uS+lg@Q<+8maop!o{m#6;&XL;F6mvJbBiH#)pa@4qW{B5P``=393L083x4;ydU z;_Q|RMQ-VakyW*lA*29|y^8ik>CP9YCg_bw9M-iWTSt6=ma2hd8%O%l4L6c$$mW5n zCO?|-P3||9cVEig+lAhU!VyJfuSPk&7C`zT33do)v%GgV=K3heD5oJUXNm0W-%L3g z2;!*{5k#_ct>MPY8M8utkr~5NjL)2J;u@6Z9nK1*k#f18u~GoEAdHlR#24Ozn;Pj} zu*ez0wtJF`20UwIrk7xy2V%-mMP!w`xiCpxGUsdiG*1@VF$6?P7Vy_1BjJJ^ZuOxw zy^C;OY2clXL}0`-Q@f%Y1K}?s%Eqj+mp8^W@RMPH`+;%2-VD8P?z*^SOg9`Mki@r< z(<*IoV9Gu-30&7CT^-7#c4GTjZiM2n@95VoGoWaE&uO-4hWv9Wu8%%@-F2HQpZmrV zI{d3^xTIQUGjStz(Mdb4Lr4q-W5` zp5l304prO`?pb2BdF+FiakSv z(o~u()WmWiX0iZoZj z(hz|4X1bvs0`I^uFAXdc+Lm3pDzoJtxU*%W-?tT__8#lCcI zc+Gn2`et`$s8RpRb#2S0Aa6bcI%_Q=)G35YdHL;Pc zCy2$)o?vWlw$P=MxDHn9>+zbsDoa0^P|xcZ2r7^$6I!6P{QfH3j37g3pO@&yVxmvK)IK zofKl1Et1zh3Dg&p)=zd84N=KfCse!vYyBsVj*E3~%Gm1A{-!duOb@0VF3 z>D<`LT*jx5k2;pdN-L62+M?|UNxz837%k-de^2jyPTOK=$UuIi((%s%KwCp!y;v0= F@*kvZ)O-K{ literal 0 HcmV?d00001 diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 2693ba8ed6..034d88e02d 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -21,6 +21,7 @@ from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, QFont, QSize, \ from calibre.ebooks.metadata import title_sort from calibre.gui2 import config, NONE, gprefs 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.icu import sort_key, lower, strcmp 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): # {{{ @@ -127,13 +129,17 @@ class TagsView(QTreeView): # {{{ self.set_new_model(self._model.get_filter_categories_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 - if self.hidden_categories is None: - self.hidden_categories = config['tag_browser_hidden_categories'] - db.prefs.set('tag_browser_hidden_categories', list(self.hidden_categories)) - else: - self.hidden_categories = set(self.hidden_categories) + if hidden_cats is None: + hidden_cats = config['tag_browser_hidden_categories'] + # strip out any non-existence field keys + for cat in hidden_cats: + 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) if old is not None: @@ -370,14 +376,15 @@ class TagsView(QTreeView): # {{{ action='delete_user_category', key=key)) self.context_menu.addSeparator() # Hide/Show/Restore categories - if not key.startswith('@') or key.find('.') < 0: - self.context_menu.addAction(_('Hide category %s') % category, - partial(self.context_menu_handler, action='hide', - category=category)) +# if not key.startswith('@') or key.find('.') < 0: + self.context_menu.addAction(_('Hide category %s') % category, + partial(self.context_menu_handler, action='hide', + category=key)) if self.hidden_categories: m = self.context_menu.addMenu(_('Show category')) - for col in sorted(self.hidden_categories, key=sort_key): - m.addAction(col, + for col in sorted(self.hidden_categories, + 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)) # search by category @@ -540,6 +547,7 @@ class TagTreeItem(object): # {{{ self.id_set = set() self.is_gst = False self.boxed = False + self.icon_state_map = list(map(QVariant, icon_map)) if self.parent is not None: self.parent.append(self) if data is None: @@ -554,9 +562,11 @@ class TagTreeItem(object): # {{{ self.bold_font = QVariant(self.bold_font) self.category_key = category_key self.temporary = temporary + self.tag = Tag(data) + self.tag.is_hierarchical = category_key.startswith('@') elif self.type == self.TAG: icon_map[0] = data.icon - self.tag, self.icon_state_map = data, list(map(QVariant, icon_map)) + self.tag = data if tooltip: self.tooltip = tooltip + ' ' else: @@ -593,6 +603,8 @@ class TagTreeItem(object): # {{{ if role == Qt.EditRole: return QVariant(self.py_name) if role == Qt.DecorationRole: + if self.tag.state: + return self.icon_state_map[self.tag.state] return self.icon if role == Qt.FontRole: return self.bold_font @@ -642,11 +654,22 @@ class TagTreeItem(object): # {{{ ''' set_to: None => advance the state, otherwise a value from TAG_SEARCH_STATES ''' - if self.type == self.TAG: - if set_to is None: - self.tag.state = (self.tag.state + 1)%3 - else: - self.tag.state = set_to +# if self.type == self.TAG: + if set_to is None: + while True: + self.tag.state = (self.tag.state + 1)%5 + if self.tag.state == TAG_SEARCH_STATES['mark_plus'] or \ + 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): res = [] @@ -677,7 +700,8 @@ class TagsModel(QAbstractItemModel): # {{{ self.categories_with_ratings = ['authors', 'series', 'publisher', 'tags'] 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.tags_view = parent self.hidden_categories = hidden_categories @@ -691,26 +715,33 @@ class TagsModel(QAbstractItemModel): # {{{ data = self.get_node_tree(config['sort_tags_by']) gst = db.prefs.get('grouped_search_terms', {}) - self.root_item = TagTreeItem() + self.root_item = TagTreeItem(icon_map=self.icon_state_map) self.category_nodes = [] last_category_node = None category_node_map = {} self.category_node_tree = {} - for i, r in enumerate(self.row_map): - if self.hidden_categories and self.categories[i] in self.hidden_categories: - continue + for i, key in enumerate(self.row_map): + if self.hidden_categories: + 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 - if r.startswith('@') and r[1:] in gst: - tt = _(u'The grouped search term name is "{0}"').format(r[1:]) + if key.startswith('@') and key[1:] in gst: + tt = _(u'The grouped search term name is "{0}"').format(key[1:]) is_gst = True - elif r == 'news': + elif key == 'news': tt = '' else: - tt = _(u'The lookup/search name is "{0}"').format(r) + tt = _(u'The lookup/search name is "{0}"').format(key) - if r.startswith('@'): - path_parts = [p for p in r.split('.')] + if key.startswith('@'): + path_parts = [p for p in key.split('.')] path = '' last_category_node = self.root_item tree_root = self.category_node_tree @@ -719,9 +750,10 @@ class TagsModel(QAbstractItemModel): # {{{ if path not in category_node_map: node = TagTreeItem(parent=last_category_node, data=p[1:] if i == 0 else p, - category_icon=self.category_icon_map[r], - tooltip=tt if path == r else path, - category_key=path) + category_icon=self.category_icon_map[key], + tooltip=tt if path == key else path, + category_key=path, + icon_map=self.icon_state_map) last_category_node = node category_node_map[path] = node self.category_nodes.append(node) @@ -736,11 +768,12 @@ class TagsModel(QAbstractItemModel): # {{{ path += '.' else: node = TagTreeItem(parent=self.root_item, - data=self.categories[i], - category_icon=self.category_icon_map[r], - tooltip=tt, category_key=r) + data=self.categories[key], + category_icon=self.category_icon_map[key], + tooltip=tt, category_key=key, + icon_map=self.icon_state_map) node.is_gst = False - category_node_map[r] = node + category_node_map[key] = node last_category_node = node self.category_nodes.append(node) self.refresh(data=data) @@ -1015,7 +1048,7 @@ class TagsModel(QAbstractItemModel): # {{{ def get_node_tree(self, sort): old_row_map = self.row_map[:] self.row_map = [] - self.categories = [] + self.categories = {} # Get the categories if self.search_restriction: @@ -1062,7 +1095,7 @@ class TagsModel(QAbstractItemModel): # {{{ for category in tb_categories: if category in data: # The search category can come and go 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): # A category has been added or removed. We must force a rebuild of @@ -1163,7 +1196,8 @@ class TagsModel(QAbstractItemModel): # {{{ sub_cat = TagTreeItem(parent=category, data = name, tooltip = None, temporary=True, 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() else: # by 'first letter' cl = cl_list[idx] @@ -1173,7 +1207,8 @@ class TagsModel(QAbstractItemModel): # {{{ data = collapse_letter, category_icon = category_node.icon, 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 else: node_parent = category @@ -1477,16 +1512,16 @@ class TagsModel(QAbstractItemModel): # {{{ def reset_all_states(self, except_=None): update_list = [] def process_tag(tag_item): - if tag_item.type != TagTreeItem.CATEGORY: - tag = tag_item.tag - if tag is except_: - tag_index = self.createIndex(tag_item.row(), 0, tag_item) - self.dataChanged.emit(tag_index, tag_index) - elif tag.state != 0 or tag in update_list: - tag_index = self.createIndex(tag_item.row(), 0, tag_item) - tag.state = 0 - update_list.append(tag) - self.dataChanged.emit(tag_index, tag_index) +# if tag_item.type != TagTreeItem.CATEGORY: + tag = tag_item.tag + if tag is except_: + tag_index = self.createIndex(tag_item.row(), 0, tag_item) + self.dataChanged.emit(tag_index, tag_index) + elif tag.state != 0 or tag in update_list: + tag_index = self.createIndex(tag_item.row(), 0, tag_item) + tag.state = 0 + update_list.append(tag) + self.dataChanged.emit(tag_index, tag_index) for t in tag_item.children: process_tag(t) @@ -1503,13 +1538,11 @@ class TagsModel(QAbstractItemModel): # {{{ ''' if not index.isValid(): return False item = index.internalPointer() - if item.type == TagTreeItem.TAG: - item.toggle(set_to=set_to) - if exclusive: - self.reset_all_states(except_=item.tag) - self.dataChanged.emit(index, index) - return True - return False + item.toggle(set_to=set_to) + if exclusive: + self.reset_all_states(except_=item.tag) + self.dataChanged.emit(index, index) + return True def tokens(self): ans = [] @@ -1523,19 +1556,31 @@ class TagsModel(QAbstractItemModel): # {{{ # into the search string only once. The nodes_seen set helps us do that 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: + if node.tag.state: + ans.append('%s:%s'%(node.category_key, node_searches[node.tag.state])) + key = node.category_key for tag_item in node.child_tags(): tag = tag_item.tag if tag.state != TAG_SEARCH_STATES['clear']: - prefix = ' not ' if tag.state == TAG_SEARCH_STATES['mark_minus'] \ - else '' + if tag.state == TAG_SEARCH_STATES['mark_minus'] or \ + tag.state == TAG_SEARCH_STATES['mark_minusminus']: + prefix = ' not ' + else: + prefix = '' category = tag.category if key != 'news' else 'tag' 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))) else: 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 name in tags_seen: continue diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index e626d446d2..0335c1d280 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -419,28 +419,23 @@ class ResultCache(SearchQueryParser): # {{{ def get_user_category_matches(self, location, query, candidates): res = set([]) - if self.db_prefs is None: + if self.db_prefs is None or len(query) < 2: return res user_cats = self.db_prefs.get('user_categories', []) c = set(candidates) - l = location.rfind('.') - if l > 0: - alt_loc = location[0:l] - alt_item = location[l+1:] + + if query.startswith('.'): + check_subcats = True + query = query[1:] else: - alt_loc = None + check_subcats = False + 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]: s = self.get_matches(category, '=' + item, candidates=c) c -= 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': return candidates - res return res