mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 02:34:06 -04:00
1) Performance improvements when using hierarchical categories
2) ensure correct counts when using sub-categories 3) make drag & drop copy a node and its children 4) make tb searching work better with subcategories and hierarchies
This commit is contained in:
parent
9eda085a3b
commit
2b24a487fa
@ -537,6 +537,7 @@ class TagTreeItem(object): # {{{
|
||||
parent=None, tooltip=None, category_key=None):
|
||||
self.parent = parent
|
||||
self.children = []
|
||||
self.id_set = set()
|
||||
self.boxed = False
|
||||
if self.parent is not None:
|
||||
self.parent.append(self)
|
||||
@ -613,10 +614,12 @@ class TagTreeItem(object): # {{{
|
||||
name = tag.name
|
||||
tt_author = False
|
||||
if role == Qt.DisplayRole:
|
||||
if tag.count == 0:
|
||||
count = len(self.id_set)
|
||||
count = count if count > 0 else tag.count
|
||||
if count == 0:
|
||||
return QVariant('%s'%(name))
|
||||
else:
|
||||
return QVariant('[%d] %s'%(tag.count, name))
|
||||
return QVariant('[%d] %s'%(count, name))
|
||||
if role == Qt.EditRole:
|
||||
return QVariant(original_name(tag))
|
||||
if role == Qt.DecorationRole:
|
||||
@ -751,6 +754,7 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
if idx.isValid():
|
||||
# get some useful serializable data
|
||||
node = idx.internalPointer()
|
||||
path = self.path_for_index(idx)
|
||||
if node.type == TagTreeItem.CATEGORY:
|
||||
d = (node.type, node.py_name, node.category_key)
|
||||
else:
|
||||
@ -759,7 +763,7 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
while p.type != TagTreeItem.CATEGORY:
|
||||
p = p.parent
|
||||
d = (node.type, p.category_key, p.is_gst, original_name(t),
|
||||
t.category, t.id)
|
||||
t.category, path)
|
||||
data.append(d)
|
||||
else:
|
||||
data.append(None)
|
||||
@ -798,38 +802,30 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
'''
|
||||
src is a list of tuples representing items to copy. The tuple is
|
||||
(type, containing category key, category key is global search term,
|
||||
full name, category key, id)
|
||||
The 'id' member is ignored, and can be None.
|
||||
full name, category key, path to node)
|
||||
The type must be TagTreeItem.TAG
|
||||
dest is the TagTreeItem node to receive the items
|
||||
action is Qt.CopyAction or Qt.MoveAction
|
||||
'''
|
||||
##### TODO: must handle children of item being copied
|
||||
user_cats = self.db.prefs.get('user_categories', {})
|
||||
parent_node = None
|
||||
copied_node = None
|
||||
for s in src:
|
||||
src_parent, src_parent_is_gst, src_name, src_cat = s[1:5]
|
||||
parent_node = src_parent
|
||||
if src_parent.startswith('@'):
|
||||
is_uc = True
|
||||
src_parent = src_parent[1:]
|
||||
else:
|
||||
is_uc = False
|
||||
dest_key = dest.category_key[1:]
|
||||
if dest_key not in user_cats:
|
||||
continue
|
||||
new_cat = []
|
||||
def process_source_node(user_cats, src_parent, src_parent_is_gst,
|
||||
is_uc, dest_key, node):
|
||||
'''
|
||||
Copy/move an item and all its children to the destination
|
||||
'''
|
||||
copied = False
|
||||
src_name = original_name(node.tag)
|
||||
src_cat = node.tag.category
|
||||
# delete the item if the source is a user category and action is move
|
||||
if is_uc and not src_parent_is_gst and src_parent in user_cats and \
|
||||
action == Qt.MoveAction:
|
||||
new_cat = []
|
||||
for tup in user_cats[src_parent]:
|
||||
if src_name == tup[0] and src_cat == tup[1]:
|
||||
continue
|
||||
new_cat.append(list(tup))
|
||||
user_cats[src_parent] = new_cat
|
||||
else:
|
||||
copied_node = (src_parent, src_name)
|
||||
copied = True
|
||||
|
||||
# Now add the item to the destination user category
|
||||
add_it = True
|
||||
@ -841,19 +837,54 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
if add_it:
|
||||
user_cats[dest_key].append([src_name, src_cat, 0])
|
||||
|
||||
for c in node.children:
|
||||
copied = process_source_node(user_cats, src_parent, src_parent_is_gst,
|
||||
is_uc, dest_key, c)
|
||||
return copied
|
||||
|
||||
user_cats = self.db.prefs.get('user_categories', {})
|
||||
parent_node = None
|
||||
copied = False
|
||||
path = None
|
||||
for s in src:
|
||||
src_parent, src_parent_is_gst = s[1:3]
|
||||
path = s[5]
|
||||
parent_node = src_parent
|
||||
|
||||
if src_parent.startswith('@'):
|
||||
is_uc = True
|
||||
src_parent = src_parent[1:]
|
||||
else:
|
||||
is_uc = False
|
||||
dest_key = dest.category_key[1:]
|
||||
|
||||
if dest_key not in user_cats:
|
||||
continue
|
||||
|
||||
node = self.index_for_path(path)
|
||||
if node:
|
||||
copied = process_source_node(user_cats, src_parent, src_parent_is_gst,
|
||||
is_uc, dest_key, node.internalPointer())
|
||||
|
||||
self.db.prefs.set('user_categories', user_cats)
|
||||
self.tags_view.recount()
|
||||
|
||||
# Scroll to the item copied. If it was moved, scroll to the parent
|
||||
if parent_node is not None:
|
||||
self.clear_boxed()
|
||||
m = self.tags_view.model()
|
||||
if copied_node is not None:
|
||||
path = m.find_item_node(parent_node, copied_node[1], None,
|
||||
equals_match=True)
|
||||
else:
|
||||
path = m.find_category_node(parent_node)
|
||||
if not copied:
|
||||
p = path[-1]
|
||||
if p == 0:
|
||||
path = m.find_category_node(parent_node)
|
||||
else:
|
||||
path[-1] = p - 1
|
||||
idx = m.index_for_path(path)
|
||||
self.tags_view.setExpanded(idx, True)
|
||||
m.show_item_at_index(idx)
|
||||
if idx.internalPointer().type == TagTreeItem.TAG:
|
||||
m.show_item_at_index(idx, boxed=True)
|
||||
else:
|
||||
m.show_item_at_index(idx)
|
||||
return True
|
||||
|
||||
def do_drop_from_library(self, md, action, row, column, parent):
|
||||
@ -1131,6 +1162,8 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
self.beginInsertRows(category_index, 999999, 1)
|
||||
n = TagTreeItem(parent=node_parent, data=tag, tooltip=tt,
|
||||
icon_map=self.icon_state_map)
|
||||
if tag.id_set is not None:
|
||||
n.id_set |= tag.id_set
|
||||
category_child_map[tag.name, tag.category] = n
|
||||
self.endInsertRows()
|
||||
tag.is_editable = key != 'formats' and (key == 'news' or \
|
||||
@ -1146,8 +1179,6 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
if t.type != TagTreeItem.CATEGORY])
|
||||
if (comp,tag.category) in child_map:
|
||||
node_parent = child_map[(comp,tag.category)]
|
||||
if not in_uc:
|
||||
node_parent.tag.count += tag.count
|
||||
node_parent.tag.is_hierarchical = True
|
||||
else:
|
||||
if i < len(components)-1:
|
||||
@ -1166,6 +1197,8 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
tooltip=tt, icon_map=self.icon_state_map)
|
||||
child_map[(comp,tag.category)] = node_parent
|
||||
self.endInsertRows()
|
||||
# This id_set must not be None
|
||||
node_parent.id_set |= tag.id_set
|
||||
|
||||
return ((collapse_letter, collapse_letter_sk))
|
||||
|
||||
@ -1583,11 +1616,16 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
if tag_item.boxed:
|
||||
tag_item.boxed = False
|
||||
self.dataChanged.emit(tag_index, tag_index)
|
||||
for i,c in enumerate(tag_item.children):
|
||||
process_tag(self.index(i, 0, tag_index), c)
|
||||
|
||||
def process_level(category_index):
|
||||
for j in xrange(self.rowCount(category_index)):
|
||||
tag_index = self.index(j, 0, category_index)
|
||||
tag_item = tag_index.internalPointer()
|
||||
if tag_item.boxed:
|
||||
tag_item.boxed = False
|
||||
self.dataChanged.emit(tag_index, tag_index)
|
||||
if tag_item.type == TagTreeItem.CATEGORY:
|
||||
process_level(tag_index)
|
||||
else:
|
||||
|
@ -46,13 +46,14 @@ copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
|
||||
class Tag(object):
|
||||
|
||||
def __init__(self, name, id=None, count=0, state=0, avg=0, sort=None,
|
||||
tooltip=None, icon=None, category=None):
|
||||
tooltip=None, icon=None, category=None, id_set=None):
|
||||
self.name = name
|
||||
self.id = id
|
||||
self.count = count
|
||||
self.state = state
|
||||
self.is_hierarchical = False
|
||||
self.is_editable = True
|
||||
self.id_set = id_set
|
||||
self.avg_rating = avg/2.0 if avg is not None else 0
|
||||
self.sort = sort
|
||||
if self.avg_rating > 0:
|
||||
@ -1162,6 +1163,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
self.n = name
|
||||
self.s = sort
|
||||
self.c = 0
|
||||
self.id_set = set()
|
||||
self.rt = 0
|
||||
self.rc = 0
|
||||
self.id = None
|
||||
@ -1266,6 +1268,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
item = tag_class(val, sort_val)
|
||||
tcategories[cat][val] = item
|
||||
item.c += 1
|
||||
item.id_set.add(book[0])
|
||||
item.id = item_id
|
||||
if rating > 0:
|
||||
item.rt += rating
|
||||
@ -1283,6 +1286,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
item = tag_class(val, sort_val)
|
||||
tcategories[cat][val] = item
|
||||
item.c += 1
|
||||
item.id_set.add(book[0])
|
||||
item.id = item_id
|
||||
if rating > 0:
|
||||
item.rt += rating
|
||||
@ -1370,7 +1374,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
|
||||
categories[category] = [tag_class(formatter(r.n), count=r.c, id=r.id,
|
||||
avg=avgr(r), sort=r.s, icon=icon,
|
||||
tooltip=tooltip, category=category)
|
||||
tooltip=tooltip, category=category,
|
||||
id_set=r.id_set)
|
||||
for r in items]
|
||||
|
||||
#print 'end phase "tags list":', time.clock() - last, 'seconds'
|
||||
@ -1379,6 +1384,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
# Needed for legacy databases that have multiple ratings that
|
||||
# map to n stars
|
||||
for r in categories['rating']:
|
||||
r.id_set = None
|
||||
for x in categories['rating']:
|
||||
if r.name == x.name and r.id != x.id:
|
||||
r.count = r.count + x.count
|
||||
|
Loading…
x
Reference in New Issue
Block a user