From 7a9dc00e0ff62d24428607b935781f4729f2e0b5 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Thu, 2 Sep 2010 09:29:14 +0100
Subject: [PATCH 1/3] Tweak to automatically add a tag to newly added books.
---
resources/default_tweaks.py | 7 ++++++-
src/calibre/library/database2.py | 11 +++++++++++
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py
index e03b0680be..43edd1c690 100644
--- a/resources/default_tweaks.py
+++ b/resources/default_tweaks.py
@@ -90,4 +90,9 @@ save_template_title_series_sorting = 'library_order'
# Examples:
# auto_connect_to_folder = 'C:\\Users\\someone\\Desktop\\testlib'
# auto_connect_to_folder = '/home/dropbox/My Dropbox/someone/library'
-auto_connect_to_folder = ''
\ No newline at end of file
+auto_connect_to_folder = ''
+
+
+# Specify a tag to be automatically applied when a book is added to the library.
+# Example: add_tag_to_new_books='ToDo'
+add_tag_to_new_books = ''
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index ba4095fbc5..e406fb8766 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -1681,7 +1681,16 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
path = path_or_stream
return run_plugins_on_import(path, format)
+ def _add_newbook_tag(self, mi):
+ tag = tweaks['add_tag_to_new_books']
+ if tag:
+ if mi.tags is None:
+ mi.tags = [tag]
+ else:
+ mi.tags.append(tag)
+
def create_book_entry(self, mi, cover=None, add_duplicates=True):
+ self._add_newbook_tag(mi)
if not add_duplicates and self.has_book(mi):
return None
series_index = 1.0 if mi.series_index is None else mi.series_index
@@ -1720,6 +1729,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
ids = []
for path in paths:
mi = metadata.next()
+ self._add_newbook_tag(mi)
format = formats.next()
if not add_duplicates and self.has_book(mi):
duplicates.append((path, format, mi))
@@ -1760,6 +1770,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
def import_book(self, mi, formats, notify=True, import_hooks=True):
series_index = 1.0 if mi.series_index is None else mi.series_index
+ self._add_newbook_tag(mi)
if not mi.title:
mi.title = _('Unknown')
if not mi.authors:
From 7fa99ffb5769e9fca78cadfcc2ea6547b88aa259 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Thu, 2 Sep 2010 11:12:40 +0100
Subject: [PATCH 2/3] 1) Add tweak to make user-defined search terms for
searching across multiple fields. 2) Make parse exceptions visible as the
tooltip in the GUI
---
resources/default_tweaks.py | 14 ++++++++++++++
src/calibre/gui2/library/models.py | 4 ++--
src/calibre/gui2/search_box.py | 6 ++++++
src/calibre/library/caches.py | 8 +++++++-
src/calibre/library/database2.py | 7 +++++++
src/calibre/library/field_metadata.py | 7 ++++---
6 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py
index 43edd1c690..fb4fff9dc7 100644
--- a/resources/default_tweaks.py
+++ b/resources/default_tweaks.py
@@ -96,3 +96,17 @@ auto_connect_to_folder = ''
# Specify a tag to be automatically applied when a book is added to the library.
# Example: add_tag_to_new_books='ToDo'
add_tag_to_new_books = ''
+
+
+# Create search terms to apply a query across several built-in search terms.
+# Syntax: {'new term':['existing term 1', 'term 2', ...], 'new':['old'...] ...}
+# Example: create the term 'myseries' that when used as myseries:foo would
+# search all of the search categories 'series', '#myseries', and '#myseries2':
+# grouped_search_terms={'myseries':['series','#myseries', '#myseries2']}
+# Example: two search terms 'a' and 'b' both that search 'tags' and '#mytags':
+# grouped_search_terms={'a':['tags','#mytags'], 'b':['tags','#mytags']}
+# Note: You cannot create a search term that is a duplicate of an existing term.
+# Such duplicates will be silently ignored. Also note that search terms ignore
+# case. 'MySearch' and 'mysearch' are the same term.
+grouped_search_terms = {}
+
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index 89008735fe..e442007c9a 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -236,8 +236,8 @@ class BooksModel(QAbstractTableModel): # {{{
def search(self, text, reset=True):
try:
self.db.search(text)
- except ParseException:
- self.searched.emit(False)
+ except ParseException as e:
+ self.searched.emit(e.msg)
return
self.last_search = text
if reset:
diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py
index 8986346dbc..d8f59b8db7 100644
--- a/src/calibre/gui2/search_box.py
+++ b/src/calibre/gui2/search_box.py
@@ -90,6 +90,7 @@ class SearchBox2(QComboBox):
self.setSizeAdjustPolicy(self.AdjustToMinimumContentsLengthWithIcon)
self.setMinimumContentsLength(25)
self._in_a_search = False
+ self.tool_tip_text = self.toolTip()
def initialize(self, opt_name, colorize=False, help_text=_('Search')):
self.as_you_type = config['search_as_you_type']
@@ -100,6 +101,7 @@ class SearchBox2(QComboBox):
self.clear_to_help()
def normalize_state(self):
+ self.setToolTip(self.tool_tip_text)
if self.help_state:
self.setEditText('')
self.line_edit.setStyleSheet(
@@ -112,6 +114,7 @@ class SearchBox2(QComboBox):
self.normal_background)
def clear_to_help(self):
+ self.setToolTip(self.tool_tip_text)
if self.help_state:
return
self.help_state = True
@@ -131,6 +134,9 @@ class SearchBox2(QComboBox):
self.clear_to_help()
def search_done(self, ok):
+ if isinstance(ok, basestring):
+ self.setToolTip(ok)
+ ok = False
if not unicode(self.currentText()).strip():
return self.clear_to_help()
self._in_a_search = ok
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index ca66d28ddb..b9c1211c7f 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -319,12 +319,18 @@ class ResultCache(SearchQueryParser):
matches.add(item[0])
return matches
- def get_matches(self, location, query):
+ def get_matches(self, location, query, allow_recursion=True):
matches = set([])
if query and query.strip():
# get metadata key associated with the search term. Eliminates
# dealing with plurals and other aliases
location = self.field_metadata.search_term_to_key(location.lower().strip())
+ if isinstance(location, list):
+ if allow_recursion:
+ for loc in location:
+ matches |= self.get_matches(loc, query, allow_recursion=False)
+ return matches
+ raise ParseException(query, len(query), 'Recursive query group detected', self)
# take care of dates special case
if location in self.field_metadata and \
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 02141b8f91..91eafbaff0 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -297,6 +297,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
if len(saved_searches().names()):
tb_cats.add_search_category(label='search', name=_('Searches'))
+ gst = tweaks['grouped_search_terms']
+ for t in gst:
+ try:
+ self.field_metadata._add_search_terms_to_map(gst[t], [t])
+ except ValueError:
+ traceback.print_exc()
+
self.book_on_device_func = None
self.data = ResultCache(self.FIELD_MAP, self.field_metadata)
self.search = self.data.search
diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py
index 2a79c3a75b..e28b6d422a 100644
--- a/src/calibre/library/field_metadata.py
+++ b/src/calibre/library/field_metadata.py
@@ -475,9 +475,7 @@ class FieldMetadata(dict):
# ])
def get_search_terms(self):
- s_keys = []
- for v in self._tb_cats.itervalues():
- map((lambda x:s_keys.append(x)), v['search_terms'])
+ s_keys = sorted(self._search_term_map.keys())
for v in self.search_items:
s_keys.append(v)
# if set(s_keys) != self.DEFAULT_LOCATIONS:
@@ -488,6 +486,9 @@ class FieldMetadata(dict):
def _add_search_terms_to_map(self, key, terms):
if terms is not None:
for t in terms:
+ t = t.lower()
+ if t in self._search_term_map:
+ raise ValueError('Attempt to add duplicate search term "%s"'%t)
self._search_term_map[t] = key
def search_term_to_key(self, term):
From e957291d37dc783afda42d7e37119dff60ae6053 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Thu, 2 Sep 2010 14:41:08 +0100
Subject: [PATCH 3/3] Put new_book_tags into the GUI, remove from tweaks.
---
resources/default_tweaks.py | 5 ----
src/calibre/gui2/dialogs/config/__init__.py | 2 ++
src/calibre/gui2/dialogs/config/config.ui | 33 ++++++++++++++++-----
src/calibre/library/database2.py | 13 ++++----
src/calibre/utils/config.py | 1 +
5 files changed, 35 insertions(+), 19 deletions(-)
diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py
index fb4fff9dc7..8d853deaa9 100644
--- a/resources/default_tweaks.py
+++ b/resources/default_tweaks.py
@@ -93,11 +93,6 @@ save_template_title_series_sorting = 'library_order'
auto_connect_to_folder = ''
-# Specify a tag to be automatically applied when a book is added to the library.
-# Example: add_tag_to_new_books='ToDo'
-add_tag_to_new_books = ''
-
-
# Create search terms to apply a query across several built-in search terms.
# Syntax: {'new term':['existing term 1', 'term 2', ...], 'new':['old'...] ...}
# Example: create the term 'myseries' that when used as myseries:foo would
diff --git a/src/calibre/gui2/dialogs/config/__init__.py b/src/calibre/gui2/dialogs/config/__init__.py
index 1f04c50e19..1dca3e28ff 100644
--- a/src/calibre/gui2/dialogs/config/__init__.py
+++ b/src/calibre/gui2/dialogs/config/__init__.py
@@ -457,6 +457,7 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
self.priority.setCurrentIndex(p)
self.priority.setVisible(iswindows)
self.priority_label.setVisible(iswindows)
+ self.new_book_tags.setText(prefs['new_book_tags'])
self._plugin_model = PluginModel()
self.plugin_view.setModel(self._plugin_model)
self.plugin_view.setStyleSheet(
@@ -906,6 +907,7 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
config['disable_tray_notification'] = not self.systray_notifications.isChecked()
p = {0:'normal', 1:'high', 2:'low'}[self.priority.currentIndex()]
prefs['worker_process_priority'] = p
+ prefs['new_book_tags'] = unicode(self.new_book_tags.text()).strip()
prefs['output_format'] = unicode(self.output_format.currentText()).upper()
config['cover_flow_queue_length'] = self.cover_browse.value()
prefs['language'] = str(self.language.itemData(self.language.currentIndex()).toString())
diff --git a/src/calibre/gui2/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui
index 988960615d..5a2bf805da 100644
--- a/src/calibre/gui2/dialogs/config/config.ui
+++ b/src/calibre/gui2/dialogs/config/config.ui
@@ -136,7 +136,7 @@
-
-
-
+
-
Default network &timeout:
@@ -146,7 +146,7 @@
- -
+
-
Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information)
@@ -165,10 +165,10 @@
- -
+
-
- -
+
-
Choose &language (requires restart):
@@ -178,7 +178,7 @@
- -
+
-
-
@@ -197,7 +197,7 @@
- -
+
-
Job &priority:
@@ -207,7 +207,7 @@
- -
+
-
Preferred &output format:
@@ -217,9 +217,26 @@
- -
+
-
+ -
+
+
+ Tags to apply when adding a book:
+
+
+ new_book_tags
+
+
+
+ -
+
+
+ A comma-separated list of tags that will be applied to books added to the library
+
+
+
-
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 91eafbaff0..76801a9893 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -1726,12 +1726,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
return run_plugins_on_import(path, format)
def _add_newbook_tag(self, mi):
- tag = tweaks['add_tag_to_new_books']
- if tag:
- if mi.tags is None:
- mi.tags = [tag]
- else:
- mi.tags.append(tag)
+ tags = prefs['new_book_tags'].strip()
+ if tags:
+ for tag in [t.strip() for t in tags.split(',')]:
+ if mi.tags is None:
+ mi.tags = [tag]
+ else:
+ mi.tags.append(tag)
def create_book_entry(self, mi, cover=None, add_duplicates=True):
self._add_newbook_tag(mi)
diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py
index a890d653b6..5682cab5e1 100644
--- a/src/calibre/utils/config.py
+++ b/src/calibre/utils/config.py
@@ -717,6 +717,7 @@ def _prefs():
c.add_opt('add_formats_to_existing', default=False,
help=_('Add new formats to existing book records'))
c.add_opt('installation_uuid', default=None, help='Installation UUID')
+ c.add_opt('new_book_tags', default='', help=_('Tags to apply to books added to the library'))
# these are here instead of the gui preferences because calibredb and
# calibre server can execute searches