diff --git a/src/calibre/gui2/dialogs/saved_search_editor.py b/src/calibre/gui2/dialogs/saved_search_editor.py index 6a8b790625..3f9f7ad437 100644 --- a/src/calibre/gui2/dialogs/saved_search_editor.py +++ b/src/calibre/gui2/dialogs/saved_search_editor.py @@ -25,8 +25,8 @@ class SavedSearchEditor(QDialog, Ui_SavedSearchEditor): self.current_search_name = None self.searches = {} self.searches_to_delete = [] - for name in saved_searches.names(): - self.searches[name] = saved_searches.lookup(name) + for name in saved_searches().names(): + self.searches[name] = saved_searches().lookup(name) self.populate_search_list() if initial_search is not None and initial_search in self.searches: @@ -78,7 +78,7 @@ class SavedSearchEditor(QDialog, Ui_SavedSearchEditor): if self.current_search_name: self.searches[self.current_search_name] = unicode(self.search_text.toPlainText()) for name in self.searches_to_delete: - saved_searches.delete(name) + saved_searches().delete(name) for name in self.searches: - saved_searches.add(name, self.searches[name]) + saved_searches().add(name, self.searches[name]) QDialog.accept(self) diff --git a/src/calibre/gui2/dialogs/tag_categories.py b/src/calibre/gui2/dialogs/tag_categories.py index b7d64226ab..90d34796c4 100644 --- a/src/calibre/gui2/dialogs/tag_categories.py +++ b/src/calibre/gui2/dialogs/tag_categories.py @@ -7,7 +7,6 @@ from PyQt4.QtCore import SIGNAL, Qt from PyQt4.QtGui import QDialog, QIcon, QListWidgetItem from calibre.gui2.dialogs.tag_categories_ui import Ui_TagCategories -from calibre.utils.config import prefs from calibre.gui2.dialogs.confirm_delete import confirm from calibre.constants import islinux @@ -63,7 +62,7 @@ class TagCategories(QDialog, Ui_TagCategories): self.all_items.append(t) self.all_items_dict[label+':'+n] = t - self.categories = dict.copy(prefs['user_categories']) + self.categories = dict.copy(db.prefs['user_categories']) if self.categories is None: self.categories = {} for cat in self.categories: @@ -182,7 +181,7 @@ class TagCategories(QDialog, Ui_TagCategories): def accept(self): self.save_category() - prefs['user_categories'] = self.categories + self.db.prefs['user_categories'] = self.categories QDialog.accept(self) def save_category(self): diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py index dd7d0a63a0..5fb6efc2a8 100644 --- a/src/calibre/gui2/search_box.py +++ b/src/calibre/gui2/search_box.py @@ -259,8 +259,7 @@ class SavedSearchBox(QComboBox): self.setMinimumContentsLength(10) self.tool_tip_text = self.toolTip() - def initialize(self, _saved_searches, _search_box, colorize=False, help_text=_('Search')): - self.saved_searches = _saved_searches + def initialize(self, _search_box, colorize=False, help_text=_('Search')): self.search_box = _search_box self.help_text = help_text self.colorize = colorize @@ -302,11 +301,11 @@ class SavedSearchBox(QComboBox): self.normalize_state() self.search_box.set_search_string(u'search:"%s"' % qname) self.setEditText(qname) - self.setToolTip(self.saved_searches.lookup(qname)) + self.setToolTip(saved_searches().lookup(qname)) def initialize_saved_search_names(self): self.clear() - qnames = self.saved_searches.names() + qnames = saved_searches().names() self.addItems(qnames) self.setCurrentIndex(-1) @@ -319,10 +318,10 @@ class SavedSearchBox(QComboBox): idx = self.currentIndex if idx < 0: return - ss = self.saved_searches.lookup(unicode(self.currentText())) + ss = saved_searches().lookup(unicode(self.currentText())) if ss is None: return - self.saved_searches.delete(unicode(self.currentText())) + saved_searches().delete(unicode(self.currentText())) self.clear_to_help() self.search_box.clear_to_help() self.emit(SIGNAL('changed()')) @@ -332,8 +331,8 @@ class SavedSearchBox(QComboBox): name = unicode(self.currentText()) if self.help_state or not name.strip(): name = unicode(self.search_box.text()).replace('"', '') - self.saved_searches.delete(name) - self.saved_searches.add(name, unicode(self.search_box.text())) + saved_searches().delete(name) + saved_searches().add(name, unicode(self.search_box.text())) # now go through an initialization cycle to ensure that the combobox has # the new search in it, that it is selected, and that the search box # references the new search instead of the text in the search. @@ -348,7 +347,7 @@ class SavedSearchBox(QComboBox): idx = self.currentIndex(); if idx < 0: return - self.search_box.set_search_string(self.saved_searches.lookup(unicode(self.currentText()))) + self.search_box.set_search_string(saved_searches().lookup(unicode(self.currentText()))) class SearchBoxMixin(object): @@ -390,11 +389,12 @@ class SearchBoxMixin(object): class SavedSearchBoxMixin(object): - def __init__(self): + def __init__(self, db): + self.db = db self.connect(self.saved_search, SIGNAL('changed()'), self.saved_searches_changed) self.saved_searches_changed() self.connect(self.clear_button, SIGNAL('clicked()'), self.saved_search.clear_to_help) - self.saved_search.initialize(saved_searches, self.search, colorize=True, + self.saved_search.initialize(self.search, colorize=True, help_text=_('Saved Searches')) self.connect(self.save_search_button, SIGNAL('clicked()'), self.saved_search.save_search_button_clicked) @@ -409,9 +409,12 @@ class SavedSearchBoxMixin(object): b = getattr(self, x+'_search_button') b.setStatusTip(b.toolTip()) + def set_database(self, db): + self.db = db + self.saved_searches_changed() def saved_searches_changed(self): - p = prefs['saved_searches'].keys() + p = saved_searches().names() p.sort() t = unicode(self.search_restriction.currentText()) self.search_restriction.clear() # rebuild the restrictions combobox using current saved searches diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 2a9fb129ac..d4c7032abe 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -224,7 +224,7 @@ class TagsView(QTreeView): # {{{ # Always show the user categories editor self.context_menu.addSeparator() - if category in prefs['user_categories'].keys(): + if category in self.db.prefs['user_categories'].keys(): self.context_menu.addAction(_('Manage User Categories'), partial(self.context_menu_handler, action='manage_categories', category=category)) @@ -426,10 +426,10 @@ class TagsModel(QAbstractItemModel): # {{{ for k in tb_cats.keys(): if tb_cats[k]['kind'] in ['user', 'search']: del tb_cats[k] - for user_cat in sorted(prefs['user_categories'].keys()): + for user_cat in sorted(self.db.prefs['user_categories'].keys()): cat_name = user_cat+':' # add the ':' to avoid name collision tb_cats.add_user_category(label=cat_name, name=user_cat) - if len(saved_searches.names()): + if len(saved_searches().names()): tb_cats.add_search_category(label='search', name=_('Searches')) # Now get the categories @@ -507,11 +507,11 @@ class TagsModel(QAbstractItemModel): # {{{ if key not in self.db.field_metadata: return if key == 'search': - if val in saved_searches.names(): + if val in saved_searches().names(): error_dialog(self.tags_view, _('Duplicate search name'), _('The saved search name %s is already used.')%val).exec_() return False - saved_searches.rename(unicode(item.data(role).toString()), val) + saved_searches().rename(unicode(item.data(role).toString()), val) self.tags_view.search_item_renamed.emit() else: if key == 'series': diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 72f7202504..6bcf564cc3 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -199,7 +199,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ UpdateMixin.__init__(self, opts) ####################### Search boxes ######################## - SavedSearchBoxMixin.__init__(self) + SavedSearchBoxMixin.__init__(self, db) SearchBoxMixin.__init__(self) ####################### Library view ######################## @@ -392,6 +392,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ self.library_view.model().set_book_on_device_func(self.book_on_device) self.status_bar.clear_message() self.search.clear_to_help() + self.saved_search.clear_to_help() self.book_details.reset_info() self.library_view.model().count_changed() self.scheduler.database_changed(db) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 1cf88d3750..c26a4b96f3 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -30,7 +30,7 @@ from calibre.customize.ui import run_plugins_on_import from calibre.utils.filenames import ascii_filename from calibre.utils.date import utcnow, now as nowf, utcfromtimestamp from calibre.utils.config import prefs, tweaks -from calibre.utils.search_query_parser import saved_searches +from calibre.utils.search_query_parser import saved_searches, set_saved_searches from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format from calibre.utils.magick_draw import save_cover_data_to @@ -142,6 +142,24 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): def initialize_dynamic(self): self.prefs = DBPrefs(self) + + # Migrate saved search and user categories to db preference scheme + def migrate_preference(name): + ans = self.prefs.get(name, None) + if ans is None: + ans = prefs[name] + try: + del prefs[name] + except: + pass + if ans is not None: + self.prefs[name] = ans + + migrate_preference('user_categories') + migrate_preference('saved_searches') + + set_saved_searches(self, 'saved_searches') + self.conn.executescript(''' DROP TRIGGER IF EXISTS author_insert_trg; CREATE TEMP TRIGGER author_insert_trg @@ -270,10 +288,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): for k in tb_cats.keys(): if tb_cats[k]['kind'] in ['user', 'search']: del tb_cats[k] - for user_cat in sorted(prefs['user_categories'].keys()): + for user_cat in sorted(self.prefs['user_categories'].keys()): cat_name = user_cat+':' # add the ':' to avoid name collision tb_cats.add_user_category(label=cat_name, name=user_cat) - if len(saved_searches.names()): + if len(saved_searches().names()): tb_cats.add_search_category(label='search', name=_('Searches')) self.book_on_device_func = None @@ -845,7 +863,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): categories['formats'].sort(key = lambda x:x.name) #### Now do the user-defined categories. #### - user_categories = prefs['user_categories'] + user_categories = self.prefs['user_categories'] # We want to use same node in the user category as in the source # category. To do that, we need to find the original Tag node. There is @@ -882,8 +900,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): icon = None if icon_map and 'search' in icon_map: icon = icon_map['search'] - for srch in saved_searches.names(): - items.append(Tag(srch, tooltip=saved_searches.lookup(srch), icon=icon)) + for srch in saved_searches().names(): + items.append(Tag(srch, tooltip=saved_searches().lookup(srch), icon=icon)) if len(items): if icon_map is not None: icon_map['search'] = icon_map['search'] diff --git a/src/calibre/library/schema_upgrades.py b/src/calibre/library/schema_upgrades.py index 4d1b966107..b08161abf2 100644 --- a/src/calibre/library/schema_upgrades.py +++ b/src/calibre/library/schema_upgrades.py @@ -397,4 +397,3 @@ class SchemaUpgrade(object): UNIQUE(key)); ''' self.conn.executescript(script) - diff --git a/src/calibre/utils/search_query_parser.py b/src/calibre/utils/search_query_parser.py index d6bf932b76..32c686221e 100644 --- a/src/calibre/utils/search_query_parser.py +++ b/src/calibre/utils/search_query_parser.py @@ -21,7 +21,6 @@ import sys, string, operator from calibre.utils.pyparsing import Keyword, Group, Forward, CharsNotIn, Suppress, \ OneOrMore, oneOf, CaselessLiteral, Optional, NoMatch, ParseException from calibre.constants import preferred_encoding -from calibre.utils.config import prefs ''' This class manages access to the preference holding the saved search queries. @@ -32,9 +31,13 @@ class SavedSearchQueries(object): queries = {} opt_name = '' - def __init__(self, _opt_name): + def __init__(self, db, _opt_name): self.opt_name = _opt_name; - self.queries = prefs[self.opt_name] + self.db = db + if db is not None: + self.queries = db.prefs[self.opt_name] + else: + self.queries = {} def force_unicode(self, x): if not isinstance(x, unicode): @@ -43,20 +46,20 @@ class SavedSearchQueries(object): def add(self, name, value): self.queries[self.force_unicode(name)] = self.force_unicode(value).strip() - prefs[self.opt_name] = self.queries + self.db.prefs[self.opt_name] = self.queries def lookup(self, name): return self.queries.get(self.force_unicode(name), None) def delete(self, name): self.queries.pop(self.force_unicode(name), False) - prefs[self.opt_name] = self.queries + self.db.prefs[self.opt_name] = self.queries def rename(self, old_name, new_name): self.queries[self.force_unicode(new_name)] = \ self.queries.get(self.force_unicode(old_name), None) self.queries.pop(self.force_unicode(old_name), False) - prefs[self.opt_name] = self.queries + self.db.prefs[self.opt_name] = self.queries def names(self): return sorted(self.queries.keys(), @@ -66,8 +69,15 @@ class SavedSearchQueries(object): Create a global instance of the saved searches. It is global so that the searches are common across all instances of the parser (devices, library, etc). ''' -saved_searches = SavedSearchQueries('saved_searches') +ss = SavedSearchQueries(None, None) +def set_saved_searches(db, opt_name): + global ss + ss = SavedSearchQueries(db, opt_name) + +def saved_searches(): + global ss + return ss class SearchQueryParser(object): ''' @@ -209,7 +219,7 @@ class SearchQueryParser(object): raise ParseException(query, len(query), 'undefined saved search', self) if self.recurse_level > 5: self.searches_seen.add(query) - return self._parse(saved_searches.lookup(query)) + return self._parse(saved_searches().lookup(query)) except: # convert all exceptions (e.g., missing key) to a parse error raise ParseException(query, len(query), 'undefined saved search', self) return self.get_matches(location, query)