mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Commit before more extensive testing
This commit is contained in:
parent
2b4e46c8fd
commit
27eca8fe72
2679
resources/images/drawer.svg
Normal file
2679
resources/images/drawer.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 278 KiB |
@ -96,7 +96,8 @@ def _config():
|
||||
help=_('Overwrite author and title with new metadata'))
|
||||
c.add_opt('enforce_cpu_limit', default=True,
|
||||
help=_('Limit max simultaneous jobs to number of CPUs'))
|
||||
c.add_opt('tag_categories', default={}, help=_('User-created tag categories'))
|
||||
c.add_opt('user_categories', default={},
|
||||
help=_('User-created tag browser categories'))
|
||||
|
||||
return ConfigProxy(c)
|
||||
|
||||
|
@ -1,8 +1,12 @@
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
from PyQt4.QtCore import SIGNAL, Qt
|
||||
from PyQt4.QtGui import QDialog, QDialogButtonBox, QLineEdit, QComboBox
|
||||
|
||||
from copy import copy
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, Qt, QVariant
|
||||
from PyQt4.QtGui import QDialog, QDialogButtonBox, QLineEdit, QComboBox, \
|
||||
QIcon, QListWidgetItem
|
||||
from PyQt4.Qt import QString
|
||||
|
||||
from calibre.gui2.dialogs.tag_categories_ui import Ui_TagCategories
|
||||
@ -11,10 +15,18 @@ from calibre.gui2 import question_dialog, error_dialog
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
from calibre.constants import islinux
|
||||
|
||||
class TagCategories(QDialog, Ui_TagCategories):
|
||||
category_names = [_('Authors'), _('Series'), _('Publishers'), _('Tags')]
|
||||
category_labels = ['author', 'series', 'publisher', 'tag']
|
||||
class Item:
|
||||
def __init__(self, name, label, index, icon, exists):
|
||||
self.name = name
|
||||
self.label = label
|
||||
self.index = index
|
||||
self.icon = icon
|
||||
self.exists = exists
|
||||
def __str__(self):
|
||||
return 'name=%s, label=%s, index=%s, exists='%(self.name, self.label, self.index, self.exists)
|
||||
|
||||
class TagCategories(QDialog, Ui_TagCategories):
|
||||
category_labels = ['', 'author', 'series', 'publisher', 'tag']
|
||||
|
||||
def __init__(self, window, db, index=None):
|
||||
QDialog.__init__(self, window)
|
||||
@ -23,86 +35,110 @@ class TagCategories(QDialog, Ui_TagCategories):
|
||||
|
||||
self.db = db
|
||||
self.index = index
|
||||
self.tags = []
|
||||
self.applied_items = []
|
||||
|
||||
self.all_items = {}
|
||||
self.all_items['tag'] = sorted(self.db.all_tags(), cmp=lambda x,y: cmp(x.lower(), y.lower()))
|
||||
self.all_items['author'] = sorted([i[1].replace('|', ',') for i in self.db.all_authors()],
|
||||
cmp=lambda x,y: cmp(x.lower(), y.lower()))
|
||||
self.all_items['publisher'] = sorted([i[1] for i in self.db.all_publishers()],
|
||||
cmp=lambda x,y: cmp(x.lower(), y.lower()))
|
||||
self.all_items['series'] = sorted([i[1] for i in self.db.all_series()],
|
||||
cmp=lambda x,y: cmp(x.lower(), y.lower()))
|
||||
category_icons = [None, QIcon(I('user_profile.svg')), QIcon(I('series.svg')),
|
||||
QIcon(I('publisher.png')), QIcon(I('tags.svg'))]
|
||||
category_values = [None,
|
||||
lambda: [n for (id, n) in self.db.all_authors()],
|
||||
lambda: [n for (id, n) in self.db.all_series()],
|
||||
lambda: [n for (id, n) in self.db.all_publishers()],
|
||||
lambda: self.db.all_tags()
|
||||
]
|
||||
category_names = ['', _('Authors'), _('Series'), _('Publishers'), _('Tags')]
|
||||
|
||||
self.all_items = []
|
||||
self.all_items_dict = {}
|
||||
for idx,label in enumerate(self.category_labels):
|
||||
if idx == 0:
|
||||
continue
|
||||
for n in category_values[idx]():
|
||||
t = Item(name=n, label=label, index=len(self.all_items),icon=category_icons[idx], exists=True)
|
||||
self.all_items.append(t)
|
||||
self.all_items_dict[label+':'+n] = t
|
||||
|
||||
self.categories = dict.copy(config['user_categories'])
|
||||
if self.categories is None:
|
||||
self.categories = {}
|
||||
for cat in self.categories:
|
||||
for item,l in enumerate(self.categories[cat]):
|
||||
key = ':'.join([l[1], l[0]])
|
||||
t = self.all_items_dict.get(key, None)
|
||||
if t is None:
|
||||
t = Item(name=l[0], label=l[1], index=len(self.all_items),
|
||||
icon=category_icons[self.category_labels.index(l[1])], exists=False)
|
||||
self.all_items.append(t)
|
||||
self.all_items_dict[key] = t
|
||||
l[2] = t.index
|
||||
|
||||
self.all_items_sorted = sorted(self.all_items, cmp=lambda x,y: cmp(x.name.lower(), y.name.lower()))
|
||||
self.display_filtered_categories(0)
|
||||
|
||||
for v in category_names:
|
||||
self.category_filter_box.addItem(v)
|
||||
self.current_cat_name = None
|
||||
self.current_cat_label= None
|
||||
self.category_label_to_name = {}
|
||||
self.category_name_to_label = {}
|
||||
for i in range(len(self.category_labels)):
|
||||
self.category_label_to_name[self.category_labels[i]] = self.category_names[i]
|
||||
self.category_name_to_label[self.category_names[i]] = self.category_labels[i]
|
||||
|
||||
self.connect(self.apply_button, SIGNAL('clicked()'), self.apply_tags)
|
||||
self.connect(self.unapply_button, SIGNAL('clicked()'), self.unapply_tags)
|
||||
self.connect(self.add_category_button, SIGNAL('clicked()'), self.add_category)
|
||||
self.connect(self.category_box, SIGNAL('currentIndexChanged(int)'), self.select_category)
|
||||
self.connect(self.category_filter_box, SIGNAL('currentIndexChanged(int)'), self.display_filtered_categories)
|
||||
self.connect(self.delete_category_button, SIGNAL('clicked()'), self.del_category)
|
||||
if islinux:
|
||||
self.available_tags.itemDoubleClicked.connect(self.apply_tags)
|
||||
self.available_items_box.itemDoubleClicked.connect(self.apply_tags)
|
||||
else:
|
||||
self.connect(self.available_tags, SIGNAL('itemActivated(QListWidgetItem*)'), self.apply_tags)
|
||||
self.connect(self.applied_tags, SIGNAL('itemActivated(QListWidgetItem*)'), self.unapply_tags)
|
||||
self.connect(self.available_items_box, SIGNAL('itemActivated(QListWidgetItem*)'), self.apply_tags)
|
||||
self.connect(self.applied_items_box, SIGNAL('itemActivated(QListWidgetItem*)'), self.unapply_tags)
|
||||
|
||||
self.categories = dict.copy(config['tag_categories'])
|
||||
if self.categories is None:
|
||||
self.categories = {}
|
||||
self.populate_category_list()
|
||||
self.category_kind_box.clear()
|
||||
for i in range(len(self.category_names)):
|
||||
self.category_kind_box.addItem(self.category_names[i])
|
||||
return
|
||||
self.select_category(0)
|
||||
|
||||
def apply_tags(self, item=None):
|
||||
if self.current_cat_name[0] is None:
|
||||
def make_list_widget(self, item):
|
||||
n = item.name if item.exists else item.name + _(' (not on any book)')
|
||||
w = QListWidgetItem(item.icon, n)
|
||||
w.setData(Qt.UserRole, item.index)
|
||||
return w
|
||||
|
||||
def display_filtered_categories(self, idx):
|
||||
idx = idx if idx is not None else self.category_filter_box.currentIndex()
|
||||
self.available_items_box.clear()
|
||||
self.applied_items_box.clear()
|
||||
for item in self.all_items_sorted:
|
||||
if idx == 0 or item.label == self.category_labels[idx]:
|
||||
if item.index not in self.applied_items and item.exists:
|
||||
self.available_items_box.addItem(self.make_list_widget(item))
|
||||
for index in self.applied_items:
|
||||
self.applied_items_box.addItem(self.make_list_widget(self.all_items[index]))
|
||||
|
||||
def apply_tags(self, node=None):
|
||||
if self.current_cat_name is None:
|
||||
return
|
||||
items = self.available_tags.selectedItems() if item is None else [item]
|
||||
for item in items:
|
||||
tag = qstring_to_unicode(item.text())
|
||||
if tag not in self.tags:
|
||||
self.tags.append(tag)
|
||||
self.available_tags.takeItem(self.available_tags.row(item))
|
||||
self.tags.sort()
|
||||
self.applied_tags.clear()
|
||||
for tag in self.tags:
|
||||
self.applied_tags.addItem(tag)
|
||||
def unapply_tags(self, item=None):
|
||||
items = self.applied_tags.selectedItems() if item is None else [item]
|
||||
for item in items:
|
||||
tag = qstring_to_unicode(item.text())
|
||||
self.tags.remove(tag)
|
||||
self.available_tags.addItem(tag)
|
||||
self.tags.sort()
|
||||
self.applied_tags.clear()
|
||||
for tag in self.tags:
|
||||
self.applied_tags.addItem(tag)
|
||||
self.available_tags.sortItems()
|
||||
nodes = self.available_items_box.selectedItems() if node is None else [node]
|
||||
for node in nodes:
|
||||
index = self.all_items[node.data(Qt.UserRole).toPyObject()].index
|
||||
if index not in self.applied_items:
|
||||
self.applied_items.append(index)
|
||||
self.applied_items.sort(cmp=lambda x, y:cmp(self.all_items[x].name.lower(), self.all_items[y].name.lower()))
|
||||
self.display_filtered_categories(None)
|
||||
|
||||
def unapply_tags(self, node=None):
|
||||
nodes = self.applied_items_box.selectedItems() if node is None else [node]
|
||||
for node in nodes:
|
||||
index = self.all_items[node.data(Qt.UserRole).toPyObject()].index
|
||||
self.applied_items.remove(index)
|
||||
self.display_filtered_categories(None)
|
||||
|
||||
def add_category(self):
|
||||
self.save_category()
|
||||
cat_name = qstring_to_unicode(self.input_box.text()).strip()
|
||||
if cat_name == '':
|
||||
return
|
||||
cat_kind = unicode(self.category_kind_box.currentText())
|
||||
r_cat_kind = self.category_name_to_label[cat_kind]
|
||||
if r_cat_kind not in self.categories:
|
||||
self.categories[r_cat_kind] = {}
|
||||
if cat_name not in self.categories[r_cat_kind]:
|
||||
return False
|
||||
if cat_name not in self.categories:
|
||||
self.category_box.clear()
|
||||
self.category_kind_label.setText(cat_kind)
|
||||
self.current_cat_name = cat_name
|
||||
self.current_cat_label = r_cat_kind
|
||||
self.categories[r_cat_kind][cat_name] = []
|
||||
if len(self.tags):
|
||||
self.clear_boxes(item_label=self.current_cat_label)
|
||||
self.categories[cat_name] = []
|
||||
self.applied_items = []
|
||||
self.populate_category_list()
|
||||
self.category_box.setCurrentIndex(self.category_box.findText(cat_name))
|
||||
else:
|
||||
@ -116,8 +152,8 @@ class TagCategories(QDialog, Ui_TagCategories):
|
||||
return
|
||||
if self.current_cat_name is not None:
|
||||
if self.current_cat_name == unicode(self.category_box.currentText()):
|
||||
del self.categories[self.current_cat_label][self.current_cat_name]
|
||||
self.current_category = [None, None] ## order here is important. RemoveItem will put it back
|
||||
del self.categories[self.current_cat_name]
|
||||
self.current_category = None
|
||||
self.category_box.removeItem(self.category_box.currentIndex())
|
||||
|
||||
def select_category(self, idx):
|
||||
@ -125,47 +161,25 @@ class TagCategories(QDialog, Ui_TagCategories):
|
||||
s = self.category_box.itemText(idx)
|
||||
if s:
|
||||
self.current_cat_name = unicode(s)
|
||||
self.current_cat_label = str(self.category_box.itemData(idx).toString())
|
||||
else:
|
||||
self.current_cat_name = None
|
||||
self.current_cat_label = None
|
||||
self.clear_boxes(item_label=False)
|
||||
if self.current_cat_label:
|
||||
self.category_kind_label.setText(self.category_label_to_name[self.current_cat_label])
|
||||
self.tags = self.categories[self.current_cat_label].get(self.current_cat_name, [])
|
||||
# Must do two loops because obsolete values can be saved
|
||||
# We need to show these to the user so they can be deleted if desired
|
||||
for t in self.tags:
|
||||
self.applied_tags.addItem(t)
|
||||
for t in self.all_items[self.current_cat_label]:
|
||||
if t not in self.tags:
|
||||
self.available_tags.addItem(t)
|
||||
else:
|
||||
self.category_kind_label.setText('')
|
||||
|
||||
|
||||
def clear_boxes(self, item_label = None):
|
||||
self.tags = []
|
||||
self.applied_tags.clear()
|
||||
self.available_tags.clear()
|
||||
if item_label:
|
||||
for item in self.all_items[item_label]:
|
||||
self.available_tags.addItem(item)
|
||||
if self.current_cat_name:
|
||||
self.applied_items = [tup[2] for tup in self.categories.get(self.current_cat_name, [])]
|
||||
self.display_filtered_categories(None)
|
||||
|
||||
def accept(self):
|
||||
self.save_category()
|
||||
config['tag_categories'] = self.categories
|
||||
config['user_categories'] = self.categories
|
||||
QDialog.accept(self)
|
||||
|
||||
def save_category(self):
|
||||
if self.current_cat_name is not None:
|
||||
self.categories[self.current_cat_label][self.current_cat_name] = self.tags
|
||||
l = []
|
||||
for index in self.applied_items:
|
||||
item = self.all_items[index]
|
||||
l.append([item.name, item.label, item.index])
|
||||
self.categories[self.current_cat_name] = l
|
||||
|
||||
def populate_category_list(self):
|
||||
cat_list = {}
|
||||
for c in self.categories:
|
||||
for n in self.categories[c]:
|
||||
if n.strip():
|
||||
cat_list[n] = c
|
||||
for n in sorted(cat_list.keys(), cmp=lambda x,y: cmp(x.lower(), y.lower())):
|
||||
self.category_box.addItem(n, cat_list[n])
|
||||
for n in sorted(self.categories.keys(), cmp=lambda x,y: cmp(x.lower(), y.lower())):
|
||||
self.category_box.addItem(n)
|
@ -25,10 +25,10 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>A&vailable values</string>
|
||||
<string>A&vailable items</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>available_tags</cstring>
|
||||
<cstring>available_items_box</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -50,7 +50,7 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
<widget class="QListWidget" name="available_tags">
|
||||
<widget class="QListWidget" name="available_items_box">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -117,10 +117,10 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>A&pplied values</string>
|
||||
<string>A&pplied items</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>applied_tags</cstring>
|
||||
<cstring>applied_items_box</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -140,7 +140,7 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="applied_tags">
|
||||
<widget class="QListWidget" name="applied_items_box">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -311,48 +311,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QComboBox" name="category_kind_box">
|
||||
<property name="toolTip">
|
||||
<string>Select the content kind of the new category</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Author</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Series</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Formats</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Publishers</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Tags</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Category kind:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>category_kind_box</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="5">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@ -366,23 +324,23 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="category_kind_label">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Category kind: </string>
|
||||
<string>Category filter: </string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="category_filter_box">
|
||||
<property name="toolTip">
|
||||
<string>Select the content kind of the new category</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -355,10 +355,10 @@
|
||||
<item>
|
||||
<widget class="QPushButton" name="edit_categories">
|
||||
<property name="text">
|
||||
<string>Manage tag categories</string>
|
||||
<string>Manage user categories</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Create, edit, and delete tag categories</string>
|
||||
<string>Create, edit, and delete user categories</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -126,7 +126,8 @@ class TagTreeItem(object):
|
||||
TAG = 1
|
||||
ROOT = 2
|
||||
|
||||
def __init__(self, data=None, tag=None, category_icon=None, icon_map=None, parent=None):
|
||||
# def __init__(self, data=None, tag=None, category_icon=None, icon_map=None, parent=None):
|
||||
def __init__(self, data=None, category_icon=None, icon_map=None, parent=None):
|
||||
self.parent = parent
|
||||
self.children = []
|
||||
if self.parent is not None:
|
||||
@ -142,13 +143,14 @@ class TagTreeItem(object):
|
||||
self.bold_font.setBold(True)
|
||||
self.bold_font = QVariant(self.bold_font)
|
||||
elif self.type == self.TAG:
|
||||
self.tag, self.icon_map = data, list(map(QVariant, icon_map))
|
||||
icon_map[0] = data.icon
|
||||
self.tag, self.icon_state_map = data, list(map(QVariant, icon_map))
|
||||
|
||||
def __str__(self):
|
||||
if self.type == self.ROOT:
|
||||
return 'ROOT'
|
||||
if self.type == self.CATEGORY:
|
||||
return 'CATEGORY:'+self.name+':%d'%len(self.children)
|
||||
return 'CATEGORY:'+str(QVariant.toString(self.name))+':%d'%len(self.children)
|
||||
return 'TAG:'+self.tag.name
|
||||
|
||||
def row(self):
|
||||
@ -183,7 +185,7 @@ class TagTreeItem(object):
|
||||
else:
|
||||
return QVariant('[%d] %s'%(self.tag.count, self.tag.name))
|
||||
if role == Qt.DecorationRole:
|
||||
return self.icon_map[self.tag.state]
|
||||
return self.icon_state_map[self.tag.state]
|
||||
if role == Qt.ToolTipRole and self.tag.tooltip:
|
||||
return QVariant(self.tag.tooltip)
|
||||
return NONE
|
||||
@ -196,16 +198,20 @@ class TagTreeItem(object):
|
||||
class TagsModel(QAbstractItemModel):
|
||||
categories_orig = [_('Authors'), _('Series'), _('Formats'), _('Publishers'), _('News'), _('All tags')]
|
||||
row_map_orig = ['author', 'series', 'format', 'publisher', 'news', 'tag']
|
||||
fixed_categories= 5
|
||||
tags_categories_start= 5
|
||||
search_keys=['search', _('Searches')]
|
||||
|
||||
def __init__(self, db, parent=None):
|
||||
QAbstractItemModel.__init__(self, parent)
|
||||
self.cmap_orig = list(map(QIcon, [I('user_profile.svg'),
|
||||
self.cat_icon_map_orig = list(map(QIcon, [I('user_profile.svg'),
|
||||
I('series.svg'), I('book.svg'), I('publisher.png'),
|
||||
I('news.svg')]))
|
||||
self.icon_map = [QIcon(), QIcon(I('plus.svg')),
|
||||
QIcon(I('minus.svg'))]
|
||||
I('news.svg'), I('tags.svg')]))
|
||||
self.icon_state_map = [None, QIcon(I('plus.svg')), QIcon(I('minus.svg'))]
|
||||
self.custcol_icon = QIcon(I('column.svg'))
|
||||
self.search_icon = QIcon(I('search.svg'))
|
||||
self.usercat_icon = QIcon(I('drawer.svg'))
|
||||
self.label_to_icon_map = dict(map(None, self.row_map_orig, self.cat_icon_map_orig))
|
||||
self.label_to_icon_map['*custom'] = self.custcol_icon
|
||||
self.db = db
|
||||
self.search_restriction = ''
|
||||
self.user_categories = {}
|
||||
@ -214,9 +220,9 @@ class TagsModel(QAbstractItemModel):
|
||||
self.root_item = TagTreeItem()
|
||||
for i, r in enumerate(self.row_map):
|
||||
c = TagTreeItem(parent=self.root_item,
|
||||
data=self.categories[i], category_icon=self.cmap[i])
|
||||
data=self.categories[i], category_icon=self.cat_icon_map[i])
|
||||
for tag in data[r]:
|
||||
TagTreeItem(parent=c, data=tag, icon_map=self.icon_map)
|
||||
TagTreeItem(parent=c, data=tag, icon_map=self.icon_state_map)
|
||||
|
||||
def set_search_restriction(self, s):
|
||||
self.search_restriction = s
|
||||
@ -224,65 +230,58 @@ class TagsModel(QAbstractItemModel):
|
||||
def get_node_tree(self, sort):
|
||||
self.row_map = []
|
||||
self.categories = []
|
||||
self.cmap = self.cmap_orig[:]
|
||||
self.user_categories = dict.copy(config['tag_categories'])
|
||||
self.cat_icon_map = self.cat_icon_map_orig[:-1] # strip the tags icon. We will put it back later
|
||||
self.user_categories = dict.copy(config['user_categories'])
|
||||
column_map = config['column_map']
|
||||
|
||||
for i in range(0, self.fixed_categories): # First the standard categories
|
||||
for i in range(0, self.tags_categories_start): # First the standard categories
|
||||
self.row_map.append(self.row_map_orig[i])
|
||||
self.categories.append(self.categories_orig[i])
|
||||
if len(self.search_restriction):
|
||||
data = self.db.get_categories(sort_on_count=sort,
|
||||
data = self.db.get_categories(sort_on_count=sort, icon_map=self.label_to_icon_map,
|
||||
ids=self.db.search(self.search_restriction, return_matches=True))
|
||||
else:
|
||||
data = self.db.get_categories(sort_on_count=sort)
|
||||
data = self.db.get_categories(sort_on_count=sort, icon_map=self.label_to_icon_map)
|
||||
|
||||
for i in data: # now the custom columns
|
||||
if i not in self.row_map_orig and i in column_map:
|
||||
self.row_map.append(i)
|
||||
self.categories.append(self.db.custom_column_label_map[i]['name'])
|
||||
self.cmap.append(QIcon(I('column.svg')))
|
||||
for c in data: # now the custom columns
|
||||
if c not in self.row_map_orig and c in column_map:
|
||||
self.row_map.append(c)
|
||||
self.categories.append(self.db.custom_column_label_map[c]['name'])
|
||||
self.cat_icon_map.append(self.custcol_icon)
|
||||
|
||||
for i in self.row_map_orig:
|
||||
if i not in self.user_categories:
|
||||
self.user_categories[i] = {}
|
||||
config['tag_categories'] = self.user_categories
|
||||
# Now do the user-defined categories. There is a time/space tradeoff here.
|
||||
# By converting the tags into a map, we can do the verification in the category
|
||||
# loop much faster, at the cost of duplicating the categories lists.
|
||||
taglist = {}
|
||||
for c in self.row_map_orig:
|
||||
taglist[c] = dict(map(lambda t:(t.name if c != 'author' else t.name.replace('|', ','), t), data[c]))
|
||||
|
||||
taglist = {} # Now the user-defined categories
|
||||
for i in data:
|
||||
taglist[i] = dict(map(lambda t:(t.name if i != 'author' else t.name.replace('|', ','), t), data[i]))
|
||||
for k in self.row_map_orig:
|
||||
if k not in self.user_categories:
|
||||
continue
|
||||
for i in sorted(self.user_categories[k].keys()): # now the tag categories
|
||||
for c in self.user_categories:
|
||||
l = []
|
||||
for t in self.user_categories[k][i]:
|
||||
if t in taglist[k]: # use same tag node as the complete category
|
||||
l.append(taglist[k][t])
|
||||
# else: eliminate nodes that have zero counts
|
||||
data[i+'*'] = l
|
||||
self.row_map.append(i+'*')
|
||||
self.categories.append(i)
|
||||
if k == 'tag': # choose the icon
|
||||
self.cmap.append(QIcon(I('tags.svg')))
|
||||
else:
|
||||
self.cmap.append(QIcon(self.cmap[self.row_map_orig.index(k)]))
|
||||
for (name,label,ign) in self.user_categories[c]:
|
||||
if name in taglist[label]: # use same node as the complete category
|
||||
l.append(taglist[label][name])
|
||||
# else: do nothing, to eliminate nodes that have zero counts
|
||||
data[c+'*'] = sorted(l, cmp=(lambda x, y: cmp(x.name.lower(), y.name.lower())))
|
||||
self.row_map.append(c+'*')
|
||||
self.categories.append(c)
|
||||
self.cat_icon_map.append(self.usercat_icon)
|
||||
|
||||
# Now the rest of the normal tag categories
|
||||
for i in range(self.fixed_categories, len(self.row_map_orig)):
|
||||
for i in range(self.tags_categories_start, len(self.row_map_orig)):
|
||||
self.row_map.append(self.row_map_orig[i])
|
||||
self.categories.append(self.categories_orig[i])
|
||||
self.cmap.append(QIcon(I('tags.svg')))
|
||||
data['search'] = self.get_search_nodes() # Add the search category
|
||||
self.cat_icon_map.append(self.cat_icon_map_orig[i])
|
||||
data['search'] = self.get_search_nodes(self.search_icon) # Add the search category
|
||||
self.row_map.append(self.search_keys[0])
|
||||
self.categories.append(self.search_keys[1])
|
||||
self.cmap.append(QIcon(I('search.svg')))
|
||||
self.cat_icon_map.append(self.search_icon)
|
||||
return data
|
||||
|
||||
def get_search_nodes(self):
|
||||
def get_search_nodes(self, icon):
|
||||
l = []
|
||||
for i in saved_searches.names():
|
||||
l.append(Tag(i, tooltip=saved_searches.lookup(i)))
|
||||
l.append(Tag(i, tooltip=saved_searches.lookup(i), icon=icon))
|
||||
return l
|
||||
|
||||
def refresh(self):
|
||||
@ -302,7 +301,7 @@ class TagsModel(QAbstractItemModel):
|
||||
self.beginInsertRows(category_index, 0, len(data[r])-1)
|
||||
for tag in data[r]:
|
||||
tag.state = state_map.get(tag.name, 0)
|
||||
t = TagTreeItem(parent=category, data=tag, icon_map=self.icon_map)
|
||||
t = TagTreeItem(parent=category, data=tag, icon_map=self.icon_state_map)
|
||||
self.endInsertRows()
|
||||
|
||||
def columnCount(self, parent):
|
||||
|
@ -15,6 +15,7 @@ from PyQt4.QtGui import QImage
|
||||
from calibre.utils.config import tweaks, prefs
|
||||
from calibre.utils.date import parse_date
|
||||
from calibre.utils.search_query_parser import SearchQueryParser
|
||||
from calibre.utils.pyparsing import ParseException
|
||||
|
||||
class CoverCache(QThread):
|
||||
|
||||
|
@ -58,12 +58,13 @@ copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
|
||||
|
||||
class Tag(object):
|
||||
|
||||
def __init__(self, name, id=None, count=0, state=0, tooltip=None):
|
||||
def __init__(self, name, id=None, count=0, state=0, tooltip=None, icon=None):
|
||||
self.name = name
|
||||
self.id = id
|
||||
self.count = count
|
||||
self.state = state
|
||||
self.tooltip = tooltip
|
||||
self.icon = icon
|
||||
|
||||
def __unicode__(self):
|
||||
return u'%s:%s:%s:%s:%s'%(self.name, self.count, self.id, self.state, self.tooltip)
|
||||
@ -574,7 +575,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
def get_recipe(self, id):
|
||||
return self.conn.get('SELECT script FROM feeds WHERE id=?', (id,), all=False)
|
||||
|
||||
def get_categories(self, sort_on_count=False, ids=None):
|
||||
def get_categories(self, sort_on_count=False, ids=None, icon_map=None):
|
||||
|
||||
orig_category_columns = {'tags': ['tag', 'name'],
|
||||
'series': ['series', 'name'],
|
||||
@ -647,11 +648,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
query += ' ORDER BY {0} ASC'.format(cn[1])
|
||||
data = self.conn.get(query)
|
||||
category = cn[0]
|
||||
icon = icon_map[category] if category in icon_map else icon_map['*custom']
|
||||
if ids is None: # no filtering
|
||||
categories[category] = [Tag(r[1], count=r[2], id=r[0])
|
||||
categories[category] = [Tag(r[1], count=r[2], id=r[0], icon=icon)
|
||||
for r in data]
|
||||
else: # filter out zero-count tags
|
||||
categories[category] = [Tag(r[1], count=r[2], id=r[0])
|
||||
categories[category] = [Tag(r[1], count=r[2], id=r[0], icon=icon)
|
||||
for r in data if r[2] > 0]
|
||||
categories['format'] = []
|
||||
for fmt in self.conn.get('SELECT DISTINCT format FROM data'):
|
||||
|
@ -24,6 +24,13 @@ class SafeLocalTimeZone(tzlocal):
|
||||
pass
|
||||
return False
|
||||
|
||||
def compute_locale_info_for_parse_date():
|
||||
dt = datetime.strptime('1/5/2000', "%x")
|
||||
if dt.month == 5:
|
||||
return True
|
||||
return False
|
||||
|
||||
parse_date_day_first = compute_locale_info_for_parse_date()
|
||||
utc_tz = _utc_tz = tzutc()
|
||||
local_tz = _local_tz = SafeLocalTimeZone()
|
||||
|
||||
@ -44,7 +51,7 @@ def parse_date(date_string, assume_utc=False, as_utc=True, default=None):
|
||||
func = datetime.utcnow if assume_utc else datetime.now
|
||||
default = func().replace(hour=0, minute=0, second=0, microsecond=0,
|
||||
tzinfo=_utc_tz if assume_utc else _local_tz)
|
||||
dt = parse(date_string, default=default)
|
||||
dt = parse(date_string, default=default, dayfirst=parse_date_day_first)
|
||||
if dt.tzinfo is None:
|
||||
dt = dt.replace(tzinfo=_utc_tz if assume_utc else _local_tz)
|
||||
return dt.astimezone(_utc_tz if as_utc else _local_tz)
|
||||
|
Loading…
x
Reference in New Issue
Block a user