diff --git a/resources/images/drawer.svg b/resources/images/drawer.svg
new file mode 100644
index 0000000000..679bca53b2
--- /dev/null
+++ b/resources/images/drawer.svg
@@ -0,0 +1,2679 @@
+
+
+
+
diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py
index 12987caeb9..a78c71316f 100644
--- a/src/calibre/gui2/__init__.py
+++ b/src/calibre/gui2/__init__.py
@@ -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)
diff --git a/src/calibre/gui2/dialogs/tag_categories.py b/src/calibre/gui2/dialogs/tag_categories.py
index 5711f4794f..74afc67242 100644
--- a/src/calibre/gui2/dialogs/tag_categories.py
+++ b/src/calibre/gui2/dialogs/tag_categories.py
@@ -1,8 +1,12 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal '
-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])
\ No newline at end of file
+ for n in sorted(self.categories.keys(), cmp=lambda x,y: cmp(x.lower(), y.lower())):
+ self.category_box.addItem(n)
\ No newline at end of file
diff --git a/src/calibre/gui2/dialogs/tag_categories.ui b/src/calibre/gui2/dialogs/tag_categories.ui
index 9b3b58bc87..2904b2464e 100644
--- a/src/calibre/gui2/dialogs/tag_categories.ui
+++ b/src/calibre/gui2/dialogs/tag_categories.ui
@@ -25,10 +25,10 @@
-
- A&vailable values
+ A&vailable items
- available_tags
+ available_items_box
@@ -50,7 +50,7 @@
-
-
-
+
true
@@ -117,10 +117,10 @@
-
- A&pplied values
+ A&pplied items
- applied_tags
+ applied_items_box
@@ -140,7 +140,7 @@
-
-
+
true
@@ -311,48 +311,6 @@
- -
-
-
- Select the content kind of the new category
-
-
-
-
- Author
-
-
- -
-
- Series
-
-
- -
-
- Formats
-
-
- -
-
- Publishers
-
-
- -
-
- Tags
-
-
-
-
- -
-
-
- Category kind:
-
-
- category_kind_box
-
-
-
-
@@ -366,23 +324,23 @@
- -
-
-
- TextLabel
-
-
-
-
- Category kind:
+ Category filter:
Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+ -
+
+
+ Select the content kind of the new category
+
+
+
diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui
index b3ed89af93..12ce6fb2c9 100644
--- a/src/calibre/gui2/main.ui
+++ b/src/calibre/gui2/main.ui
@@ -355,10 +355,10 @@
-
- Manage tag categories
+ Manage user categories
- Create, edit, and delete tag categories
+ Create, edit, and delete user categories
diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py
index 7b24d9d2e0..2e964e8d8a 100644
--- a/src/calibre/gui2/tag_view.py
+++ b/src/calibre/gui2/tag_view.py
@@ -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
- 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 c in self.user_categories:
+ l = []
+ 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):
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index 1ce0843185..6794408ca0 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -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):
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index f704eb68a6..55aa7520f7 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -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'):
diff --git a/src/calibre/utils/date.py b/src/calibre/utils/date.py
index fb9d3e90b0..e48e10d90f 100644
--- a/src/calibre/utils/date.py
+++ b/src/calibre/utils/date.py
@@ -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)