mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge branch 'kovidgoyal/master'
This commit is contained in:
commit
34aa6e68b2
@ -70,6 +70,10 @@ class DBPrefs(dict): # {{{
|
||||
self.db = db
|
||||
self.defaults = {}
|
||||
self.disable_setting = False
|
||||
self.load_from_db()
|
||||
|
||||
def load_from_db(self):
|
||||
self.clear()
|
||||
for key, val in self.db.conn.get('SELECT key,val FROM preferences'):
|
||||
try:
|
||||
val = self.raw_to_object(val)
|
||||
@ -136,28 +140,10 @@ class DBPrefs(dict): # {{{
|
||||
|
||||
@classmethod
|
||||
def read_serialized(cls, library_path, recreate_prefs=False):
|
||||
try:
|
||||
from_filename = os.path.join(library_path,
|
||||
'metadata_db_prefs_backup.json')
|
||||
with open(from_filename, "rb") as f:
|
||||
d = json.load(f, object_hook=from_json)
|
||||
if not recreate_prefs:
|
||||
return d
|
||||
cls.clear()
|
||||
cls.db.conn.execute('DELETE FROM preferences')
|
||||
for k,v in d.iteritems():
|
||||
raw = cls.to_raw(v)
|
||||
cls.db.conn.execute(
|
||||
'INSERT INTO preferences (key,val) VALUES (?,?)', (k, raw))
|
||||
cls.db.conn.commit()
|
||||
cls.clear()
|
||||
cls.update(d)
|
||||
return d
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
raise
|
||||
return None
|
||||
from_filename = os.path.join(library_path,
|
||||
'metadata_db_prefs_backup.json')
|
||||
with open(from_filename, "rb") as f:
|
||||
return json.load(f, object_hook=from_json)
|
||||
# }}}
|
||||
|
||||
# Extra collators {{{
|
||||
|
@ -89,7 +89,6 @@ class Cache(object):
|
||||
self.formatter_template_cache = {}
|
||||
self.dirtied_cache = {}
|
||||
self.dirtied_sequence = 0
|
||||
self._search_api = Search(self, 'saved_searches', self.field_metadata.get_search_terms())
|
||||
|
||||
# Implement locking for all simple read/write API methods
|
||||
# An unlocked version of the method is stored with the name starting
|
||||
@ -105,6 +104,7 @@ class Cache(object):
|
||||
lock = self.read_lock if ira else self.write_lock
|
||||
setattr(self, name, wrap_simple(lock, func))
|
||||
|
||||
self._search_api = Search(self, 'saved_searches', self.field_metadata.get_search_terms())
|
||||
self.initialize_dynamic()
|
||||
|
||||
@write_api
|
||||
@ -127,7 +127,7 @@ class Cache(object):
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
if len(self._search_api.get_saved_searches().names()):
|
||||
if len(self._search_api.saved_searches.names()) > 0:
|
||||
self.field_metadata.add_search_category(label='search', name=_('Searches'))
|
||||
|
||||
self.field_metadata.add_grouped_search_terms(
|
||||
@ -140,11 +140,6 @@ class Cache(object):
|
||||
if self.dirtied_cache:
|
||||
self.dirtied_sequence = max(self.dirtied_cache.itervalues())+1
|
||||
|
||||
@property
|
||||
def prefs(self):
|
||||
'For internal use only (used by SavedSearchQueries). For thread-safe access to the preferences, use the pref() and set_pref() methods.'
|
||||
return self.backend.prefs
|
||||
|
||||
@write_api
|
||||
def initialize_template_cache(self):
|
||||
self.formatter_template_cache = {}
|
||||
@ -161,6 +156,8 @@ class Cache(object):
|
||||
def reload_from_db(self, clear_caches=True):
|
||||
if clear_caches:
|
||||
self._clear_caches()
|
||||
self.backend.prefs.load_from_db()
|
||||
self._search_api.saved_searches.load_from_db()
|
||||
for field in self.fields.itervalues():
|
||||
if hasattr(field, 'table'):
|
||||
field.table.read(self.backend) # Reread data from metadata.db
|
||||
@ -1520,6 +1517,30 @@ class Cache(object):
|
||||
all_paths = {self._field_for('path', book_id).partition('/')[0] for book_id in self._all_book_ids()}
|
||||
self.backend.move_library_to(all_paths, newloc, progress=progress)
|
||||
|
||||
@read_api
|
||||
def saved_search_names(self):
|
||||
return self._search_api.saved_searches.names()
|
||||
|
||||
@read_api
|
||||
def saved_search_lookup(self, name):
|
||||
return self._search_api.saved_searches.lookup(name)
|
||||
|
||||
@write_api
|
||||
def saved_search_set_all(self, smap):
|
||||
self._search_api.saved_searches.set_all(smap)
|
||||
|
||||
@write_api
|
||||
def saved_search_delete(self, name):
|
||||
self._search_api.saved_searches.delete(name)
|
||||
|
||||
@write_api
|
||||
def saved_search_add(self, name, val):
|
||||
self._search_api.saved_searches.add(name, val)
|
||||
|
||||
@write_api
|
||||
def saved_search_rename(self, old_name, new_name):
|
||||
self._search_api.saved_searches.rename(old_name, new_name)
|
||||
|
||||
# }}}
|
||||
|
||||
class SortKey(object): # {{{
|
||||
|
@ -228,11 +228,10 @@ def get_categories(dbcache, sort='name', book_ids=None, icon_map=None):
|
||||
icon = None
|
||||
if icon_map and 'search' in icon_map:
|
||||
icon = icon_map['search']
|
||||
ss = dbcache._search_api.get_saved_searches()
|
||||
for srch in ss.names():
|
||||
items.append(Tag(srch, tooltip=ss.lookup(srch),
|
||||
sort=srch, icon=icon, category='search',
|
||||
is_editable=False))
|
||||
queries = dbcache._search_api.saved_searches.queries
|
||||
for srch in sorted(queries, key=sort_key):
|
||||
items.append(Tag(srch, tooltip=queries[srch], sort=srch, icon=icon,
|
||||
category='search', is_editable=False))
|
||||
if len(items):
|
||||
categories['search'] = items
|
||||
|
||||
|
@ -120,9 +120,6 @@ class LibraryDatabase(object):
|
||||
self.new_api.reload_from_db()
|
||||
self.last_update_check = utcnow()
|
||||
|
||||
def get_saved_searches(self):
|
||||
return self.new_api._search_api.get_saved_searches()
|
||||
|
||||
@property
|
||||
def custom_column_num_map(self):
|
||||
return self.backend.custom_column_num_map
|
||||
@ -887,6 +884,12 @@ for meth in ('get_next_series_num_for', 'has_book', 'author_sort_from_authors'):
|
||||
setattr(LibraryDatabase, meth, MT(getter(meth)))
|
||||
|
||||
LibraryDatabase.move_library_to = MT(lambda self, newloc, progress=None:self.new_api.move_library_to(newloc, progress=progress))
|
||||
LibraryDatabase.saved_search_names = MT(lambda self:self.new_api.saved_search_names())
|
||||
LibraryDatabase.saved_search_lookup = MT(lambda self, x:self.new_api.saved_search_lookup(x))
|
||||
LibraryDatabase.saved_search_set_all = MT(lambda self, smap:self.new_api.saved_search_set_all(smap))
|
||||
LibraryDatabase.saved_search_delete = MT(lambda self, x:self.new_api.saved_search_delete(x))
|
||||
LibraryDatabase.saved_search_add = MT(lambda self, x, y:self.new_api.saved_search_add(x, y))
|
||||
LibraryDatabase.saved_search_rename = MT(lambda self, x, y:self.new_api.saved_search_rename(x, y))
|
||||
# Cleaning is not required anymore
|
||||
LibraryDatabase.clean = LibraryDatabase.clean_custom = MT(lambda self:None)
|
||||
LibraryDatabase.clean_standard_field = MT(lambda self, field, commit=False:None)
|
||||
|
@ -7,15 +7,16 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import re
|
||||
import re, weakref
|
||||
from functools import partial
|
||||
from datetime import timedelta
|
||||
|
||||
from calibre.constants import preferred_encoding
|
||||
from calibre.utils.config_base import prefs
|
||||
from calibre.utils.date import parse_date, UNDEFINED_DATE, now
|
||||
from calibre.utils.icu import primary_find
|
||||
from calibre.utils.icu import primary_find, sort_key
|
||||
from calibre.utils.localization import lang_map, canonicalize_lang
|
||||
from calibre.utils.search_query_parser import SearchQueryParser, ParseException, SavedSearchQueries
|
||||
from calibre.utils.search_query_parser import SearchQueryParser, ParseException
|
||||
|
||||
CONTAINS_MATCH = 0
|
||||
EQUALS_MATCH = 1
|
||||
@ -388,11 +389,72 @@ class KeyPairSearch(object): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
class SavedSearchQueries(object): # {{{
|
||||
queries = {}
|
||||
opt_name = ''
|
||||
|
||||
def __init__(self, db, _opt_name):
|
||||
self.opt_name = _opt_name
|
||||
try:
|
||||
self._db = weakref.ref(db)
|
||||
except TypeError:
|
||||
# db could be None
|
||||
self._db = lambda : None
|
||||
self.load_from_db()
|
||||
|
||||
def load_from_db(self):
|
||||
db = self.db
|
||||
if db is not None:
|
||||
self.queries = db._pref(self.opt_name, default={})
|
||||
else:
|
||||
self.queries = {}
|
||||
|
||||
@property
|
||||
def db(self):
|
||||
return self._db()
|
||||
|
||||
def force_unicode(self, x):
|
||||
if not isinstance(x, unicode):
|
||||
x = x.decode(preferred_encoding, 'replace')
|
||||
return x
|
||||
|
||||
def add(self, name, value):
|
||||
db = self.db
|
||||
if db is not None:
|
||||
self.queries[self.force_unicode(name)] = self.force_unicode(value).strip()
|
||||
db._set_pref(self.opt_name, self.queries)
|
||||
|
||||
def lookup(self, name):
|
||||
return self.queries.get(self.force_unicode(name), None)
|
||||
|
||||
def delete(self, name):
|
||||
db = self.db
|
||||
if db is not None:
|
||||
self.queries.pop(self.force_unicode(name), False)
|
||||
db._set_pref(self.opt_name, self.queries)
|
||||
|
||||
def rename(self, old_name, new_name):
|
||||
db = self.db
|
||||
if db is not None:
|
||||
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)
|
||||
db._set_pref(self.opt_name, self.queries)
|
||||
|
||||
def set_all(self, smap):
|
||||
db = self.db
|
||||
if db is not None:
|
||||
self.queries = smap
|
||||
db._set_pref(self.opt_name, smap)
|
||||
|
||||
def names(self):
|
||||
return sorted(self.queries.iterkeys(), key=sort_key)
|
||||
# }}}
|
||||
|
||||
class Parser(SearchQueryParser):
|
||||
|
||||
def __init__(self, dbcache, all_book_ids, gst, date_search, num_search,
|
||||
bool_search, keypair_search, limit_search_columns, limit_search_columns_to,
|
||||
locations, virtual_fields, get_saved_searches):
|
||||
locations, virtual_fields, lookup_saved_search):
|
||||
self.dbcache, self.all_book_ids = dbcache, all_book_ids
|
||||
self.all_search_locations = frozenset(locations)
|
||||
self.grouped_search_terms = gst
|
||||
@ -403,7 +465,7 @@ class Parser(SearchQueryParser):
|
||||
self.virtual_fields = virtual_fields or {}
|
||||
if 'marked' not in self.virtual_fields:
|
||||
self.virtual_fields['marked'] = self
|
||||
super(Parser, self).__init__(locations, optimize=True, get_saved_searches=get_saved_searches)
|
||||
super(Parser, self).__init__(locations, optimize=True, lookup_saved_search=lookup_saved_search)
|
||||
|
||||
@property
|
||||
def field_metadata(self):
|
||||
@ -693,11 +755,11 @@ class Search(object):
|
||||
self.keypair_search,
|
||||
prefs['limit_search_columns'],
|
||||
prefs['limit_search_columns_to'], self.all_search_locations,
|
||||
virtual_fields, self.get_saved_searches)
|
||||
virtual_fields, self.saved_searches.lookup)
|
||||
|
||||
try:
|
||||
ret = sqp.parse(q)
|
||||
finally:
|
||||
sqp.dbcache = sqp.get_saved_searches = None
|
||||
sqp.dbcache = sqp.lookup_saved_search = None
|
||||
return ret
|
||||
|
||||
|
@ -751,3 +751,24 @@ class LegacyTest(BaseTest):
|
||||
db.close()
|
||||
|
||||
# }}}
|
||||
|
||||
def test_legacy_saved_search(self): # {{{
|
||||
' Test legacy saved search API '
|
||||
db, ndb = self.init_old(), self.init_legacy()
|
||||
run_funcs(self, db, ndb, (
|
||||
('saved_search_set_all', {'one':'a', 'two':'b'}),
|
||||
('saved_search_names',),
|
||||
('saved_search_lookup', 'one'),
|
||||
('saved_search_lookup', 'two'),
|
||||
('saved_search_lookup', 'xxx'),
|
||||
('saved_search_rename', 'one', '1'),
|
||||
('saved_search_names',),
|
||||
('saved_search_lookup', '1'),
|
||||
('saved_search_delete', '1'),
|
||||
('saved_search_names',),
|
||||
('saved_search_add', 'n', 'm'),
|
||||
('saved_search_names',),
|
||||
('saved_search_lookup', 'n'),
|
||||
))
|
||||
# }}}
|
||||
|
||||
|
@ -14,7 +14,8 @@ from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
class SavedSearchEditor(QDialog, Ui_SavedSearchEditor):
|
||||
|
||||
def __init__(self, parent, initial_search=None):
|
||||
from calibre.gui2.ui import saved_searches
|
||||
from calibre.gui2.ui import get_gui
|
||||
db = get_gui().current_db
|
||||
QDialog.__init__(self, parent)
|
||||
Ui_SavedSearchEditor.__init__(self)
|
||||
self.setupUi(self)
|
||||
@ -27,9 +28,9 @@ class SavedSearchEditor(QDialog, Ui_SavedSearchEditor):
|
||||
|
||||
self.current_search_name = None
|
||||
self.searches = {}
|
||||
for name in saved_searches().names():
|
||||
self.searches[name] = saved_searches().lookup(name)
|
||||
self.search_names = set([icu_lower(n) for n in saved_searches().names()])
|
||||
for name in db.saved_search_names():
|
||||
self.searches[name] = db.saved_search_lookup(name)
|
||||
self.search_names = set([icu_lower(n) for n in db.saved_search_names()])
|
||||
|
||||
self.populate_search_list()
|
||||
if initial_search is not None and initial_search in self.searches:
|
||||
@ -98,11 +99,10 @@ class SavedSearchEditor(QDialog, Ui_SavedSearchEditor):
|
||||
self.search_text.setPlainText('')
|
||||
|
||||
def accept(self):
|
||||
from calibre.gui2.ui import saved_searches
|
||||
from calibre.gui2.ui import get_gui
|
||||
db = get_gui().current_db
|
||||
if self.current_search_name:
|
||||
self.searches[self.current_search_name] = unicode(self.search_text.toPlainText())
|
||||
for name in saved_searches().names():
|
||||
saved_searches().delete(name)
|
||||
for name in self.searches:
|
||||
saved_searches().add(name, self.searches[name])
|
||||
ss = {name:self.searches[name] for name in self.searches}
|
||||
db.saved_search_set_all(ss)
|
||||
QDialog.accept(self)
|
||||
|
@ -308,28 +308,35 @@ class SavedSearchBox(QComboBox): # {{{
|
||||
self.saved_search_selected(self.currentText())
|
||||
|
||||
def saved_search_selected(self, qname):
|
||||
from calibre.gui2.ui import saved_searches
|
||||
from calibre.gui2.ui import get_gui
|
||||
db = get_gui().current_db
|
||||
qname = unicode(qname)
|
||||
if qname is None or not qname.strip():
|
||||
self.search_box.clear()
|
||||
return
|
||||
if not saved_searches().lookup(qname):
|
||||
if not db.saved_search_lookup(qname):
|
||||
self.search_box.clear()
|
||||
self.setEditText(qname)
|
||||
return
|
||||
self.search_box.set_search_string(u'search:"%s"' % qname, emit_changed=False)
|
||||
self.setEditText(qname)
|
||||
self.setToolTip(saved_searches().lookup(qname))
|
||||
self.setToolTip(db.saved_search_lookup(qname))
|
||||
|
||||
def initialize_saved_search_names(self):
|
||||
from calibre.gui2.ui import saved_searches
|
||||
qnames = saved_searches().names()
|
||||
self.addItems(qnames)
|
||||
from calibre.gui2.ui import get_gui
|
||||
gui = get_gui()
|
||||
try:
|
||||
names = gui.current_db.saved_search_names()
|
||||
except AttributeError:
|
||||
# Happens during gui initialization
|
||||
names = []
|
||||
self.addItems(names)
|
||||
self.setCurrentIndex(-1)
|
||||
|
||||
# SIGNALed from the main UI
|
||||
def save_search_button_clicked(self):
|
||||
from calibre.gui2.ui import saved_searches
|
||||
from calibre.gui2.ui import get_gui
|
||||
db = get_gui().current_db
|
||||
name = unicode(self.currentText())
|
||||
if not name.strip():
|
||||
name = unicode(self.search_box.text()).replace('"', '')
|
||||
@ -337,8 +344,8 @@ class SavedSearchBox(QComboBox): # {{{
|
||||
error_dialog(self, _('Create saved search'),
|
||||
_('There is no search to save'), show=True)
|
||||
return
|
||||
saved_searches().delete(name)
|
||||
saved_searches().add(name, unicode(self.search_box.text()))
|
||||
db.saved_search_delete(name)
|
||||
db.saved_search_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 +355,8 @@ class SavedSearchBox(QComboBox): # {{{
|
||||
self.changed.emit()
|
||||
|
||||
def delete_current_search(self):
|
||||
from calibre.gui2.ui import saved_searches
|
||||
from calibre.gui2.ui import get_gui
|
||||
db = get_gui().current_db
|
||||
idx = self.currentIndex()
|
||||
if idx <= 0:
|
||||
error_dialog(self, _('Delete current search'),
|
||||
@ -358,21 +366,22 @@ class SavedSearchBox(QComboBox): # {{{
|
||||
'<b>permanently deleted</b>. Are you sure?')
|
||||
+'</p>', 'saved_search_delete', self):
|
||||
return
|
||||
ss = saved_searches().lookup(unicode(self.currentText()))
|
||||
ss = db.saved_search_lookup(unicode(self.currentText()))
|
||||
if ss is None:
|
||||
return
|
||||
saved_searches().delete(unicode(self.currentText()))
|
||||
db.saved_search_delete(unicode(self.currentText()))
|
||||
self.clear()
|
||||
self.search_box.clear()
|
||||
self.changed.emit()
|
||||
|
||||
# SIGNALed from the main UI
|
||||
def copy_search_button_clicked(self):
|
||||
from calibre.gui2.ui import saved_searches
|
||||
from calibre.gui2.ui import get_gui
|
||||
db = get_gui().current_db
|
||||
idx = self.currentIndex()
|
||||
if idx < 0:
|
||||
return
|
||||
self.search_box.set_search_string(saved_searches().lookup(unicode(self.currentText())))
|
||||
self.search_box.set_search_string(db.saved_search_lookup(unicode(self.currentText())))
|
||||
|
||||
# }}}
|
||||
|
||||
|
@ -178,7 +178,7 @@ class CreateVirtualLibrary(QDialog): # {{{
|
||||
self.resize(self.sizeHint()+QSize(150, 25))
|
||||
|
||||
def search_text_changed(self, txt):
|
||||
from calibre.gui2.ui import saved_searches
|
||||
db = self.gui.current_db
|
||||
searches = [_('Saved searches recognized in the expression:')]
|
||||
txt = unicode(txt)
|
||||
while txt:
|
||||
@ -201,9 +201,9 @@ class CreateVirtualLibrary(QDialog): # {{{
|
||||
search_name = possible_search[0]
|
||||
if search_name.startswith('='):
|
||||
search_name = search_name[1:]
|
||||
if search_name in saved_searches().names():
|
||||
if search_name in db.saved_search_names():
|
||||
searches.append(search_name + '=' +
|
||||
saved_searches().lookup(search_name))
|
||||
db.saved_search_lookup(search_name))
|
||||
else:
|
||||
txt = ''
|
||||
else:
|
||||
@ -234,18 +234,17 @@ class CreateVirtualLibrary(QDialog): # {{{
|
||||
self.vl_text.setText(self.original_search)
|
||||
|
||||
def link_activated(self, url):
|
||||
from calibre.gui2.ui import saved_searches
|
||||
db = self.gui.current_db
|
||||
f, txt = unicode(url).partition('.')[0::2]
|
||||
if f == 'search':
|
||||
names = saved_searches().names()
|
||||
names = db.saved_search_names()
|
||||
else:
|
||||
names = getattr(db, 'all_%s_names'%f)()
|
||||
d = SelectNames(names, txt, parent=self)
|
||||
if d.exec_() == d.Accepted:
|
||||
prefix = f+'s' if f in {'tag', 'author'} else f
|
||||
if f == 'search':
|
||||
search = ['(%s)'%(saved_searches().lookup(x)) for x in d.names]
|
||||
search = ['(%s)'%(db.saved_search_lookup(x)) for x in d.names]
|
||||
else:
|
||||
search = ['%s:"=%s"'%(prefix, x.replace('"', '\\"')) for x in d.names]
|
||||
if search:
|
||||
@ -476,7 +475,7 @@ class SearchRestrictionMixin(object):
|
||||
return name[0:MAX_VIRTUAL_LIBRARY_NAME_LENGTH].strip()
|
||||
|
||||
def build_search_restriction_list(self):
|
||||
from calibre.gui2.ui import saved_searches
|
||||
from calibre.gui2.ui import get_gui
|
||||
m = self.ar_menu
|
||||
m.clear()
|
||||
|
||||
@ -508,7 +507,7 @@ class SearchRestrictionMixin(object):
|
||||
add_action(current_restriction_text, 2)
|
||||
dex += 1
|
||||
|
||||
for n in sorted(saved_searches().names(), key=sort_key):
|
||||
for n in sorted(get_gui().current_db.saved_search_names(), key=sort_key):
|
||||
add_action(n, dex)
|
||||
dex += 1
|
||||
|
||||
|
@ -878,7 +878,7 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
traceback.print_exc()
|
||||
self.db.data.change_search_locations(self.db.field_metadata.get_search_terms())
|
||||
|
||||
if len(self.db.get_saved_searches().names()):
|
||||
if len(self.db.saved_search_names()):
|
||||
tb_cats.add_search_category(label='search', name=_('Searches'))
|
||||
|
||||
if self.filter_categories_by:
|
||||
@ -1004,11 +1004,11 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
_('Author names cannot contain & characters.')).exec_()
|
||||
return False
|
||||
if key == 'search':
|
||||
if val in self.db.get_saved_searches().names():
|
||||
if val in self.db.saved_search_names():
|
||||
error_dialog(self.gui_parent, _('Duplicate search name'),
|
||||
_('The saved search name %s is already used.')%val).exec_()
|
||||
return False
|
||||
self.db.get_saved_searches().rename(unicode(item.data(role).toString()), val)
|
||||
self.db.saved_search_rename(unicode(item.data(role).toString()), val)
|
||||
item.tag.name = val
|
||||
self.search_item_renamed.emit() # Does a refresh
|
||||
else:
|
||||
|
@ -354,8 +354,7 @@ class TagsView(QTreeView): # {{{
|
||||
self.delete_user_category.emit(key)
|
||||
return
|
||||
if action == 'delete_search':
|
||||
from calibre.gui2.ui import saved_searches
|
||||
saved_searches().delete(key)
|
||||
self.model().db.saved_search_delete(key)
|
||||
self.rebuild_saved_searches.emit()
|
||||
return
|
||||
if action == 'delete_item_from_user_category':
|
||||
|
@ -98,16 +98,6 @@ _gui = None
|
||||
def get_gui():
|
||||
return _gui
|
||||
|
||||
def saved_searches():
|
||||
'Return the saved searches defined in the currently open library'
|
||||
try:
|
||||
return _gui.library_view.model().db.get_saved_searches()
|
||||
except AttributeError:
|
||||
# Happens during initialization of the gui
|
||||
from calibre.utils.search_query_parser import saved_searches
|
||||
return saved_searches()
|
||||
|
||||
|
||||
class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin,
|
||||
SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin,
|
||||
@ -312,10 +302,10 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
####################### Search boxes ########################
|
||||
SearchRestrictionMixin.__init__(self)
|
||||
SavedSearchBoxMixin.__init__(self)
|
||||
SearchBoxMixin.__init__(self)
|
||||
|
||||
####################### Library view ########################
|
||||
LibraryViewMixin.__init__(self, db)
|
||||
SearchBoxMixin.__init__(self) # Requires current_db
|
||||
|
||||
if show_gui:
|
||||
self.show()
|
||||
|
@ -1029,11 +1029,10 @@ def command_saved_searches(args, dbpath):
|
||||
prints(_('Error: You must specify an action (add|remove|list)'), file=sys.stderr)
|
||||
return 1
|
||||
db = get_db(dbpath, opts)
|
||||
ss = db.get_saved_searches()
|
||||
if args[0] == 'list':
|
||||
for name in ss.names():
|
||||
for name in db.saved_search_names():
|
||||
prints(_('Name:'), name)
|
||||
prints(_('Search string:'), ss.lookup(name))
|
||||
prints(_('Search string:'), db.saved_search_lookup(name))
|
||||
print
|
||||
elif args[0] == 'add':
|
||||
if len(args) < 3:
|
||||
@ -1041,7 +1040,7 @@ def command_saved_searches(args, dbpath):
|
||||
print
|
||||
prints(_('Error: You must specify a name and a search string'), file=sys.stderr)
|
||||
return 1
|
||||
ss.add(args[1], args[2])
|
||||
db.saved_search_add(args[1], args[2])
|
||||
prints(args[1], _('added'))
|
||||
elif args[0] == 'remove':
|
||||
if len(args) < 2:
|
||||
@ -1049,7 +1048,7 @@ def command_saved_searches(args, dbpath):
|
||||
print
|
||||
prints(_('Error: You must specify a name'), file=sys.stderr)
|
||||
return 1
|
||||
ss.delete(args[1])
|
||||
db.saved_search_delete(args[1])
|
||||
prints(args[1], _('removed'))
|
||||
else:
|
||||
parser.print_help()
|
||||
|
@ -538,8 +538,23 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
if self.user_version == 0:
|
||||
self.user_version = 1
|
||||
|
||||
def get_saved_searches(self):
|
||||
return saved_searches()
|
||||
def saved_search_names(self):
|
||||
return saved_searches().names()
|
||||
|
||||
def saved_search_rename(self, old_name, new_name):
|
||||
saved_searches().rename(old_name, new_name)
|
||||
|
||||
def saved_search_lookup(self, name):
|
||||
return saved_searches().lookup(name)
|
||||
|
||||
def saved_search_add(self, name, val):
|
||||
saved_searches().add(name, val)
|
||||
|
||||
def saved_search_delete(self, name):
|
||||
saved_searches().delete(name)
|
||||
|
||||
def saved_search_set_all(self, smap):
|
||||
saved_searches().set_all(smap)
|
||||
|
||||
def last_modified(self):
|
||||
''' Return last modified time as a UTC datetime object'''
|
||||
|
@ -209,7 +209,7 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
|
||||
if sr:
|
||||
if sr in virt_libs:
|
||||
sr = virt_libs[sr]
|
||||
elif sr not in self.db.get_saved_searches().names():
|
||||
elif sr not in self.db.saved_search_names():
|
||||
prints('WARNING: Content server: search restriction ',
|
||||
sr, ' does not exist')
|
||||
sr = ''
|
||||
|
@ -76,6 +76,11 @@ class SavedSearchQueries(object):
|
||||
self.queries.pop(self.force_unicode(old_name), False)
|
||||
db.prefs[self.opt_name] = self.queries
|
||||
|
||||
def set_all(self, smap):
|
||||
db = self.db
|
||||
if db is not None:
|
||||
self.queries = db.prefs[self.opt_name] = smap
|
||||
|
||||
def names(self):
|
||||
return sorted(self.queries.keys(),key=sort_key)
|
||||
|
||||
@ -93,6 +98,9 @@ def saved_searches():
|
||||
global ss
|
||||
return ss
|
||||
|
||||
def global_lookup_saved_search(name):
|
||||
return ss.lookup(name)
|
||||
|
||||
'''
|
||||
Parse a search expression into a series of potentially recursive operations.
|
||||
|
||||
@ -292,10 +300,10 @@ class SearchQueryParser(object):
|
||||
failed.append(test[0])
|
||||
return failed
|
||||
|
||||
def __init__(self, locations, test=False, optimize=False, get_saved_searches=None):
|
||||
def __init__(self, locations, test=False, optimize=False, lookup_saved_search=None):
|
||||
self.sqp_initialize(locations, test=test, optimize=optimize)
|
||||
self.parser = Parser()
|
||||
self.get_saved_searches = saved_searches if get_saved_searches is None else get_saved_searches
|
||||
self.lookup_saved_search = global_lookup_saved_search if lookup_saved_search is None else lookup_saved_search
|
||||
|
||||
def sqp_change_locations(self, locations):
|
||||
self.sqp_initialize(locations, optimize=self.optimize)
|
||||
@ -368,7 +376,7 @@ class SearchQueryParser(object):
|
||||
raise ParseException(_('Recursive saved search: {0}').format(query))
|
||||
if self.recurse_level > 5:
|
||||
self.searches_seen.add(query)
|
||||
return self._parse(self.get_saved_searches().lookup(query), candidates)
|
||||
return self._parse(self.lookup_saved_search(query), candidates)
|
||||
except ParseException as e:
|
||||
raise e
|
||||
except: # convert all exceptions (e.g., missing key) to a parse error
|
||||
|
Loading…
x
Reference in New Issue
Block a user