Commit before more extensive testing

This commit is contained in:
Charles Haley 2010-04-19 17:22:47 +01:00
parent 2b4e46c8fd
commit 27eca8fe72
9 changed files with 2874 additions and 213 deletions

2679
resources/images/drawer.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 278 KiB

View File

@ -96,7 +96,8 @@ def _config():
help=_('Overwrite author and title with new metadata')) help=_('Overwrite author and title with new metadata'))
c.add_opt('enforce_cpu_limit', default=True, c.add_opt('enforce_cpu_limit', default=True,
help=_('Limit max simultaneous jobs to number of CPUs')) 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) return ConfigProxy(c)

View File

@ -1,8 +1,12 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __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 PyQt4.Qt import QString
from calibre.gui2.dialogs.tag_categories_ui import Ui_TagCategories 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.gui2.dialogs.confirm_delete import confirm
from calibre.constants import islinux from calibre.constants import islinux
class TagCategories(QDialog, Ui_TagCategories): class Item:
category_names = [_('Authors'), _('Series'), _('Publishers'), _('Tags')] def __init__(self, name, label, index, icon, exists):
category_labels = ['author', 'series', 'publisher', 'tag'] 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): def __init__(self, window, db, index=None):
QDialog.__init__(self, window) QDialog.__init__(self, window)
@ -23,86 +35,110 @@ class TagCategories(QDialog, Ui_TagCategories):
self.db = db self.db = db
self.index = index self.index = index
self.tags = [] self.applied_items = []
self.all_items = {} category_icons = [None, QIcon(I('user_profile.svg')), QIcon(I('series.svg')),
self.all_items['tag'] = sorted(self.db.all_tags(), cmp=lambda x,y: cmp(x.lower(), y.lower())) QIcon(I('publisher.png')), QIcon(I('tags.svg'))]
self.all_items['author'] = sorted([i[1].replace('|', ',') for i in self.db.all_authors()], category_values = [None,
cmp=lambda x,y: cmp(x.lower(), y.lower())) lambda: [n for (id, n) in self.db.all_authors()],
self.all_items['publisher'] = sorted([i[1] for i in self.db.all_publishers()], lambda: [n for (id, n) in self.db.all_series()],
cmp=lambda x,y: cmp(x.lower(), y.lower())) lambda: [n for (id, n) in self.db.all_publishers()],
self.all_items['series'] = sorted([i[1] for i in self.db.all_series()], lambda: self.db.all_tags()
cmp=lambda x,y: cmp(x.lower(), y.lower())) ]
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_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.apply_button, SIGNAL('clicked()'), self.apply_tags)
self.connect(self.unapply_button, SIGNAL('clicked()'), self.unapply_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.add_category_button, SIGNAL('clicked()'), self.add_category)
self.connect(self.category_box, SIGNAL('currentIndexChanged(int)'), self.select_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) self.connect(self.delete_category_button, SIGNAL('clicked()'), self.del_category)
if islinux: if islinux:
self.available_tags.itemDoubleClicked.connect(self.apply_tags) self.available_items_box.itemDoubleClicked.connect(self.apply_tags)
else: else:
self.connect(self.available_tags, SIGNAL('itemActivated(QListWidgetItem*)'), self.apply_tags) self.connect(self.available_items_box, SIGNAL('itemActivated(QListWidgetItem*)'), self.apply_tags)
self.connect(self.applied_tags, SIGNAL('itemActivated(QListWidgetItem*)'), self.unapply_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.populate_category_list()
self.category_kind_box.clear() return
for i in range(len(self.category_names)):
self.category_kind_box.addItem(self.category_names[i])
self.select_category(0) self.select_category(0)
def apply_tags(self, item=None): def make_list_widget(self, item):
if self.current_cat_name[0] is None: 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 return
items = self.available_tags.selectedItems() if item is None else [item] nodes = self.available_items_box.selectedItems() if node is None else [node]
for item in items: for node in nodes:
tag = qstring_to_unicode(item.text()) index = self.all_items[node.data(Qt.UserRole).toPyObject()].index
if tag not in self.tags: if index not in self.applied_items:
self.tags.append(tag) self.applied_items.append(index)
self.available_tags.takeItem(self.available_tags.row(item)) self.applied_items.sort(cmp=lambda x, y:cmp(self.all_items[x].name.lower(), self.all_items[y].name.lower()))
self.tags.sort() self.display_filtered_categories(None)
self.applied_tags.clear()
for tag in self.tags: def unapply_tags(self, node=None):
self.applied_tags.addItem(tag) nodes = self.applied_items_box.selectedItems() if node is None else [node]
def unapply_tags(self, item=None): for node in nodes:
items = self.applied_tags.selectedItems() if item is None else [item] index = self.all_items[node.data(Qt.UserRole).toPyObject()].index
for item in items: self.applied_items.remove(index)
tag = qstring_to_unicode(item.text()) self.display_filtered_categories(None)
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()
def add_category(self): def add_category(self):
self.save_category() self.save_category()
cat_name = qstring_to_unicode(self.input_box.text()).strip() cat_name = qstring_to_unicode(self.input_box.text()).strip()
if cat_name == '': if cat_name == '':
return return False
cat_kind = unicode(self.category_kind_box.currentText()) if cat_name not in self.categories:
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]:
self.category_box.clear() self.category_box.clear()
self.category_kind_label.setText(cat_kind)
self.current_cat_name = cat_name self.current_cat_name = cat_name
self.current_cat_label = r_cat_kind self.categories[cat_name] = []
self.categories[r_cat_kind][cat_name] = [] self.applied_items = []
if len(self.tags):
self.clear_boxes(item_label=self.current_cat_label)
self.populate_category_list() self.populate_category_list()
self.category_box.setCurrentIndex(self.category_box.findText(cat_name)) self.category_box.setCurrentIndex(self.category_box.findText(cat_name))
else: else:
@ -116,8 +152,8 @@ class TagCategories(QDialog, Ui_TagCategories):
return return
if self.current_cat_name is not None: if self.current_cat_name is not None:
if self.current_cat_name == unicode(self.category_box.currentText()): if self.current_cat_name == unicode(self.category_box.currentText()):
del self.categories[self.current_cat_label][self.current_cat_name] del self.categories[self.current_cat_name]
self.current_category = [None, None] ## order here is important. RemoveItem will put it back self.current_category = None
self.category_box.removeItem(self.category_box.currentIndex()) self.category_box.removeItem(self.category_box.currentIndex())
def select_category(self, idx): def select_category(self, idx):
@ -125,47 +161,25 @@ class TagCategories(QDialog, Ui_TagCategories):
s = self.category_box.itemText(idx) s = self.category_box.itemText(idx)
if s: if s:
self.current_cat_name = unicode(s) self.current_cat_name = unicode(s)
self.current_cat_label = str(self.category_box.itemData(idx).toString())
else: else:
self.current_cat_name = None self.current_cat_name = None
self.current_cat_label = None if self.current_cat_name:
self.clear_boxes(item_label=False) self.applied_items = [tup[2] for tup in self.categories.get(self.current_cat_name, [])]
if self.current_cat_label: self.display_filtered_categories(None)
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)
def accept(self): def accept(self):
self.save_category() self.save_category()
config['tag_categories'] = self.categories config['user_categories'] = self.categories
QDialog.accept(self) QDialog.accept(self)
def save_category(self): def save_category(self):
if self.current_cat_name is not None: 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): def populate_category_list(self):
cat_list = {} for n in sorted(self.categories.keys(), cmp=lambda x,y: cmp(x.lower(), y.lower())):
for c in self.categories: self.category_box.addItem(n)
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])

View File

@ -25,10 +25,10 @@
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>A&amp;vailable values</string> <string>A&amp;vailable items</string>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>available_tags</cstring> <cstring>available_items_box</cstring>
</property> </property>
</widget> </widget>
</item> </item>
@ -50,7 +50,7 @@
<item> <item>
<layout class="QHBoxLayout"> <layout class="QHBoxLayout">
<item> <item>
<widget class="QListWidget" name="available_tags"> <widget class="QListWidget" name="available_items_box">
<property name="alternatingRowColors"> <property name="alternatingRowColors">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -117,10 +117,10 @@
<item> <item>
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
<string>A&amp;pplied values</string> <string>A&amp;pplied items</string>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>applied_tags</cstring> <cstring>applied_items_box</cstring>
</property> </property>
</widget> </widget>
</item> </item>
@ -140,7 +140,7 @@
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QListWidget" name="applied_tags"> <widget class="QListWidget" name="applied_items_box">
<property name="alternatingRowColors"> <property name="alternatingRowColors">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -311,48 +311,6 @@
</property> </property>
</widget> </widget>
</item> </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"> <item row="1" column="5">
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">
@ -366,23 +324,23 @@
</property> </property>
</spacer> </spacer>
</item> </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"> <item row="1" column="0">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
<string>Category kind: </string> <string>Category filter: </string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </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> </layout>
</item> </item>
</layout> </layout>

View File

@ -355,10 +355,10 @@
<item> <item>
<widget class="QPushButton" name="edit_categories"> <widget class="QPushButton" name="edit_categories">
<property name="text"> <property name="text">
<string>Manage tag categories</string> <string>Manage user categories</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Create, edit, and delete tag categories</string> <string>Create, edit, and delete user categories</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -126,7 +126,8 @@ class TagTreeItem(object):
TAG = 1 TAG = 1
ROOT = 2 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.parent = parent
self.children = [] self.children = []
if self.parent is not None: if self.parent is not None:
@ -142,13 +143,14 @@ class TagTreeItem(object):
self.bold_font.setBold(True) self.bold_font.setBold(True)
self.bold_font = QVariant(self.bold_font) self.bold_font = QVariant(self.bold_font)
elif self.type == self.TAG: 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): def __str__(self):
if self.type == self.ROOT: if self.type == self.ROOT:
return 'ROOT' return 'ROOT'
if self.type == self.CATEGORY: 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 return 'TAG:'+self.tag.name
def row(self): def row(self):
@ -183,7 +185,7 @@ class TagTreeItem(object):
else: else:
return QVariant('[%d] %s'%(self.tag.count, self.tag.name)) return QVariant('[%d] %s'%(self.tag.count, self.tag.name))
if role == Qt.DecorationRole: 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: if role == Qt.ToolTipRole and self.tag.tooltip:
return QVariant(self.tag.tooltip) return QVariant(self.tag.tooltip)
return NONE return NONE
@ -196,16 +198,20 @@ class TagTreeItem(object):
class TagsModel(QAbstractItemModel): class TagsModel(QAbstractItemModel):
categories_orig = [_('Authors'), _('Series'), _('Formats'), _('Publishers'), _('News'), _('All tags')] categories_orig = [_('Authors'), _('Series'), _('Formats'), _('Publishers'), _('News'), _('All tags')]
row_map_orig = ['author', 'series', 'format', 'publisher', 'news', 'tag'] row_map_orig = ['author', 'series', 'format', 'publisher', 'news', 'tag']
fixed_categories= 5 tags_categories_start= 5
search_keys=['search', _('Searches')] search_keys=['search', _('Searches')]
def __init__(self, db, parent=None): def __init__(self, db, parent=None):
QAbstractItemModel.__init__(self, parent) 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('series.svg'), I('book.svg'), I('publisher.png'),
I('news.svg')])) I('news.svg'), I('tags.svg')]))
self.icon_map = [QIcon(), QIcon(I('plus.svg')), self.icon_state_map = [None, QIcon(I('plus.svg')), QIcon(I('minus.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.db = db
self.search_restriction = '' self.search_restriction = ''
self.user_categories = {} self.user_categories = {}
@ -214,9 +220,9 @@ class TagsModel(QAbstractItemModel):
self.root_item = TagTreeItem() self.root_item = TagTreeItem()
for i, r in enumerate(self.row_map): for i, r in enumerate(self.row_map):
c = TagTreeItem(parent=self.root_item, 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]: 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): def set_search_restriction(self, s):
self.search_restriction = s self.search_restriction = s
@ -224,65 +230,58 @@ class TagsModel(QAbstractItemModel):
def get_node_tree(self, sort): def get_node_tree(self, sort):
self.row_map = [] self.row_map = []
self.categories = [] self.categories = []
self.cmap = self.cmap_orig[:] 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['tag_categories']) self.user_categories = dict.copy(config['user_categories'])
column_map = config['column_map'] 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.row_map.append(self.row_map_orig[i])
self.categories.append(self.categories_orig[i]) self.categories.append(self.categories_orig[i])
if len(self.search_restriction): 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)) ids=self.db.search(self.search_restriction, return_matches=True))
else: 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 for c in data: # now the custom columns
if i not in self.row_map_orig and i in column_map: if c not in self.row_map_orig and c in column_map:
self.row_map.append(i) self.row_map.append(c)
self.categories.append(self.db.custom_column_label_map[i]['name']) self.categories.append(self.db.custom_column_label_map[c]['name'])
self.cmap.append(QIcon(I('column.svg'))) self.cat_icon_map.append(self.custcol_icon)
for i in self.row_map_orig: # Now do the user-defined categories. There is a time/space tradeoff here.
if i not in self.user_categories: # By converting the tags into a map, we can do the verification in the category
self.user_categories[i] = {} # loop much faster, at the cost of duplicating the categories lists.
config['tag_categories'] = self.user_categories 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 c in self.user_categories:
for i in data: l = []
taglist[i] = dict(map(lambda t:(t.name if i != 'author' else t.name.replace('|', ','), t), data[i])) for (name,label,ign) in self.user_categories[c]:
for k in self.row_map_orig: if name in taglist[label]: # use same node as the complete category
if k not in self.user_categories: l.append(taglist[label][name])
continue # else: do nothing, to eliminate nodes that have zero counts
for i in sorted(self.user_categories[k].keys()): # now the tag categories data[c+'*'] = sorted(l, cmp=(lambda x, y: cmp(x.name.lower(), y.name.lower())))
l = [] self.row_map.append(c+'*')
for t in self.user_categories[k][i]: self.categories.append(c)
if t in taglist[k]: # use same tag node as the complete category self.cat_icon_map.append(self.usercat_icon)
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)]))
# Now the rest of the normal tag categories # 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.row_map.append(self.row_map_orig[i])
self.categories.append(self.categories_orig[i]) self.categories.append(self.categories_orig[i])
self.cmap.append(QIcon(I('tags.svg'))) self.cat_icon_map.append(self.cat_icon_map_orig[i])
data['search'] = self.get_search_nodes() # Add the search category data['search'] = self.get_search_nodes(self.search_icon) # Add the search category
self.row_map.append(self.search_keys[0]) self.row_map.append(self.search_keys[0])
self.categories.append(self.search_keys[1]) self.categories.append(self.search_keys[1])
self.cmap.append(QIcon(I('search.svg'))) self.cat_icon_map.append(self.search_icon)
return data return data
def get_search_nodes(self): def get_search_nodes(self, icon):
l = [] l = []
for i in saved_searches.names(): 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 return l
def refresh(self): def refresh(self):
@ -302,7 +301,7 @@ class TagsModel(QAbstractItemModel):
self.beginInsertRows(category_index, 0, len(data[r])-1) self.beginInsertRows(category_index, 0, len(data[r])-1)
for tag in data[r]: for tag in data[r]:
tag.state = state_map.get(tag.name, 0) 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() self.endInsertRows()
def columnCount(self, parent): def columnCount(self, parent):

View File

@ -15,6 +15,7 @@ from PyQt4.QtGui import QImage
from calibre.utils.config import tweaks, prefs from calibre.utils.config import tweaks, prefs
from calibre.utils.date import parse_date from calibre.utils.date import parse_date
from calibre.utils.search_query_parser import SearchQueryParser from calibre.utils.search_query_parser import SearchQueryParser
from calibre.utils.pyparsing import ParseException
class CoverCache(QThread): class CoverCache(QThread):

View File

@ -58,12 +58,13 @@ copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
class Tag(object): 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.name = name
self.id = id self.id = id
self.count = count self.count = count
self.state = state self.state = state
self.tooltip = tooltip self.tooltip = tooltip
self.icon = icon
def __unicode__(self): def __unicode__(self):
return u'%s:%s:%s:%s:%s'%(self.name, self.count, self.id, self.state, self.tooltip) 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): def get_recipe(self, id):
return self.conn.get('SELECT script FROM feeds WHERE id=?', (id,), all=False) 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'], orig_category_columns = {'tags': ['tag', 'name'],
'series': ['series', 'name'], 'series': ['series', 'name'],
@ -647,11 +648,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
query += ' ORDER BY {0} ASC'.format(cn[1]) query += ' ORDER BY {0} ASC'.format(cn[1])
data = self.conn.get(query) data = self.conn.get(query)
category = cn[0] category = cn[0]
icon = icon_map[category] if category in icon_map else icon_map['*custom']
if ids is None: # no filtering 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] for r in data]
else: # filter out zero-count tags 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] for r in data if r[2] > 0]
categories['format'] = [] categories['format'] = []
for fmt in self.conn.get('SELECT DISTINCT format FROM data'): for fmt in self.conn.get('SELECT DISTINCT format FROM data'):

View File

@ -24,6 +24,13 @@ class SafeLocalTimeZone(tzlocal):
pass pass
return False 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() utc_tz = _utc_tz = tzutc()
local_tz = _local_tz = SafeLocalTimeZone() 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 func = datetime.utcnow if assume_utc else datetime.now
default = func().replace(hour=0, minute=0, second=0, microsecond=0, default = func().replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=_utc_tz if assume_utc else _local_tz) 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: if dt.tzinfo is None:
dt = dt.replace(tzinfo=_utc_tz if assume_utc else _local_tz) dt = dt.replace(tzinfo=_utc_tz if assume_utc else _local_tz)
return dt.astimezone(_utc_tz if as_utc else _local_tz) return dt.astimezone(_utc_tz if as_utc else _local_tz)