Fix #7616 (Add "similar books" searches to the search history)

This commit is contained in:
Kovid Goyal 2010-12-01 12:02:42 -07:00
parent 8cc1c798f5
commit 039d4653b0
3 changed files with 64 additions and 39 deletions

View File

@ -58,6 +58,7 @@ class SimilarBooksAction(InterfaceAction):
for a in authors.split(',')] for a in authors.split(',')]
join = ' or ' join = ' or '
if search: if search:
self.gui.search.set_search_string(join.join(search)) self.gui.search.set_search_string(join.join(search),
store_in_history=True)

View File

@ -18,7 +18,7 @@ from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor
from calibre.gui2.dialogs.search import SearchDialog from calibre.gui2.dialogs.search import SearchDialog
from calibre.utils.search_query_parser import saved_searches from calibre.utils.search_query_parser import saved_searches
class SearchLineEdit(QLineEdit): class SearchLineEdit(QLineEdit): # {{{
key_pressed = pyqtSignal(object) key_pressed = pyqtSignal(object)
def keyPressEvent(self, event): def keyPressEvent(self, event):
@ -37,17 +37,23 @@ class SearchLineEdit(QLineEdit):
def paste(self, *args): def paste(self, *args):
self.parent().normalize_state() self.parent().normalize_state()
return QLineEdit.paste(self) return QLineEdit.paste(self)
# }}}
class SearchBox2(QComboBox): class SearchBox2(QComboBox): # {{{
''' '''
To use this class: To use this class:
* Call initialize() * Call initialize()
* Connect to the search() and cleared() signals from this widget. * Connect to the search() and cleared() signals from this widget.
* Connect to the cleared() signal to know when the box content changes * Connect to the changed() signal to know when the box content changes
* Connect to focus_to_library signal to be told to manually change focus * Connect to focus_to_library() signal to be told to manually change focus
* Call search_done() after every search is complete * Call search_done() after every search is complete
* Call set_search_string() to perform a search programmatically
* You can use the current_text property to get the current search text
Be aware that if you are using it in a slow connected to the
changed() signal, if the connection is not queued it will not be
accurate.
''' '''
INTERVAL = 1500 #: Time to wait before emitting search signal INTERVAL = 1500 #: Time to wait before emitting search signal
@ -166,12 +172,13 @@ class SearchBox2(QComboBox):
self.changed.emit() self.changed.emit()
self.do_search() self.do_search()
def do_search(self, *args): def _do_search(self, store_in_history=True):
text = unicode(self.currentText()).strip() text = unicode(self.currentText()).strip()
if not text: if not text:
return self.clear() return self.clear()
self.search.emit(text) self.search.emit(text)
if store_in_history:
idx = self.findText(text, Qt.MatchFixedString) idx = self.findText(text, Qt.MatchFixedString)
self.block_signals(True) self.block_signals(True)
if idx < 0: if idx < 0:
@ -185,30 +192,23 @@ class SearchBox2(QComboBox):
config[self.opt_name] = [unicode(self.itemText(i)) for i in config[self.opt_name] = [unicode(self.itemText(i)) for i in
range(self.count())] range(self.count())]
def do_search(self, *args):
self._do_search()
def block_signals(self, yes): def block_signals(self, yes):
self.blockSignals(yes) self.blockSignals(yes)
self.line_edit.blockSignals(yes) self.line_edit.blockSignals(yes)
def search_from_tokens(self, tokens, all): def set_search_string(self, txt, store_in_history=False):
ans = u' '.join([u'%s:%s'%x for x in tokens])
if not all:
ans = '[' + ans + ']'
self.set_search_string(ans)
def search_from_tags(self, tags, all):
joiner = ' and ' if all else ' or '
self.set_search_string(joiner.join(tags))
def set_search_string(self, txt):
self.setFocus(Qt.OtherFocusReason) self.setFocus(Qt.OtherFocusReason)
if not txt: if not txt:
self.clear() self.clear()
else: else:
self.normalize_state() self.normalize_state()
self.setEditText(txt) self.setEditText(txt)
self.search.emit(txt)
self.line_edit.end(False) self.line_edit.end(False)
self.initial_state = False self.changed.emit()
self._do_search(store_in_history=store_in_history)
self.focus_to_library.emit() self.focus_to_library.emit()
def search_as_you_type(self, enabled): def search_as_you_type(self, enabled):
@ -217,7 +217,13 @@ class SearchBox2(QComboBox):
def in_a_search(self): def in_a_search(self):
return self._in_a_search return self._in_a_search
class SavedSearchBox(QComboBox): @property
def current_text(self):
return unicode(self.lineEdit().text())
# }}}
class SavedSearchBox(QComboBox): # {{{
''' '''
To use this class: To use this class:
@ -329,13 +335,17 @@ class SavedSearchBox(QComboBox):
return return
self.search_box.set_search_string(saved_searches().lookup(unicode(self.currentText()))) self.search_box.set_search_string(saved_searches().lookup(unicode(self.currentText())))
class SearchBoxMixin(object): # }}}
class SearchBoxMixin(object): # {{{
def __init__(self): def __init__(self):
self.search.initialize('main_search_history', colorize=True, self.search.initialize('main_search_history', colorize=True,
help_text=_('Search (For Advanced Search click the button to the left)')) help_text=_('Search (For Advanced Search click the button to the left)'))
self.search.cleared.connect(self.search_box_cleared) self.search.cleared.connect(self.search_box_cleared)
self.search.changed.connect(self.search_box_changed) # Queued so that search.current_text will be correct
self.search.changed.connect(self.search_box_changed,
type=Qt.QueuedConnection)
self.search.focus_to_library.connect(self.focus_to_library) self.search.focus_to_library.connect(self.focus_to_library)
self.clear_button.clicked.connect(self.search.clear_clicked) self.clear_button.clicked.connect(self.search.clear_clicked)
self.advanced_search_button.clicked[bool].connect(self.do_advanced_search) self.advanced_search_button.clicked[bool].connect(self.do_advanced_search)
@ -364,7 +374,7 @@ class SearchBoxMixin(object):
def search_box_changed(self): def search_box_changed(self):
self.saved_search.clear() self.saved_search.clear()
self.tags_view.clear() self.tags_view.conditional_clear(self.search.current_text)
def do_advanced_search(self, *args): def do_advanced_search(self, *args):
d = SearchDialog(self, self.library_view.model().db) d = SearchDialog(self, self.library_view.model().db)
@ -378,7 +388,9 @@ class SearchBoxMixin(object):
def focus_to_library(self): def focus_to_library(self):
self.current_view().setFocus(Qt.OtherFocusReason) self.current_view().setFocus(Qt.OtherFocusReason)
class SavedSearchBoxMixin(object): # }}}
class SavedSearchBoxMixin(object): # {{{
def __init__(self): def __init__(self):
self.saved_search.changed.connect(self.saved_searches_changed) self.saved_search.changed.connect(self.saved_searches_changed)
@ -417,3 +429,6 @@ class SavedSearchBoxMixin(object):
if d.result() == d.Accepted: if d.result() == d.Accepted:
self.saved_searches_changed() self.saved_searches_changed()
self.saved_search.clear() self.saved_search.clear()
# }}}

View File

@ -60,7 +60,7 @@ class TagDelegate(QItemDelegate): # {{{
class TagsView(QTreeView): # {{{ class TagsView(QTreeView): # {{{
refresh_required = pyqtSignal() refresh_required = pyqtSignal()
tags_marked = pyqtSignal(object, object) tags_marked = pyqtSignal(object)
user_category_edit = pyqtSignal(object) user_category_edit = pyqtSignal(object)
tag_list_edit = pyqtSignal(object, object) tag_list_edit = pyqtSignal(object, object)
saved_search_edit = pyqtSignal(object) saved_search_edit = pyqtSignal(object)
@ -135,11 +135,21 @@ class TagsView(QTreeView): # {{{
# swallow these to avoid toggling and editing at the same time # swallow these to avoid toggling and editing at the same time
pass pass
@property
def search_string(self):
tokens = self._model.tokens()
joiner = ' and ' if self.match_all else ' or '
return joiner.join(tokens)
def toggle(self, index): def toggle(self, index):
modifiers = int(QApplication.keyboardModifiers()) modifiers = int(QApplication.keyboardModifiers())
exclusive = modifiers not in (Qt.CTRL, Qt.SHIFT) exclusive = modifiers not in (Qt.CTRL, Qt.SHIFT)
if self._model.toggle(index, exclusive): if self._model.toggle(index, exclusive):
self.tags_marked.emit(self._model.tokens(), self.match_all) self.tags_marked.emit(self.search_string)
def conditional_clear(self, search_string):
if search_string != self.search_string:
self.clear()
def context_menu_handler(self, action=None, category=None, def context_menu_handler(self, action=None, category=None,
key=None, index=None): key=None, index=None):
@ -842,8 +852,7 @@ class TagBrowserMixin(object): # {{{
self.library_view.model().count_changed_signal.connect(self.tags_view.recount) self.library_view.model().count_changed_signal.connect(self.tags_view.recount)
self.tags_view.set_database(self.library_view.model().db, self.tags_view.set_database(self.library_view.model().db,
self.tag_match, self.sort_by) self.tag_match, self.sort_by)
self.tags_view.tags_marked.connect(self.search.search_from_tags) self.tags_view.tags_marked.connect(self.search.set_search_string)
self.tags_view.tags_marked.connect(self.saved_search.clear)
self.tags_view.tag_list_edit.connect(self.do_tags_list_edit) self.tags_view.tag_list_edit.connect(self.do_tags_list_edit)
self.tags_view.user_category_edit.connect(self.do_user_categories_edit) self.tags_view.user_category_edit.connect(self.do_user_categories_edit)
self.tags_view.saved_search_edit.connect(self.do_saved_search_edit) self.tags_view.saved_search_edit.connect(self.do_saved_search_edit)