From b6df943eb3e1539d506d569472d9facbd64c5115 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 8 Nov 2010 15:34:14 +0000 Subject: [PATCH] Add box-based search interface tab to advanced search. --- src/calibre/gui2/dialogs/search.py | 94 +++++- src/calibre/gui2/dialogs/search.ui | 509 +++++++++++++++++++---------- src/calibre/gui2/layout.py | 1 + src/calibre/gui2/search_box.py | 7 +- 4 files changed, 431 insertions(+), 180 deletions(-) diff --git a/src/calibre/gui2/dialogs/search.py b/src/calibre/gui2/dialogs/search.py index 041e7ff1fc..bc04c38e73 100644 --- a/src/calibre/gui2/dialogs/search.py +++ b/src/calibre/gui2/dialogs/search.py @@ -1,17 +1,68 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' import re -from PyQt4.QtGui import QDialog +from PyQt4.QtGui import QDialog, QDialogButtonBox +from PyQt4 import QtCore from calibre.gui2.dialogs.search_ui import Ui_Dialog from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH class SearchDialog(QDialog, Ui_Dialog): - def __init__(self, *args): - QDialog.__init__(self, *args) + def __init__(self, parent, db, box_values, current_tab): + QDialog.__init__(self, parent) self.setupUi(self) self.mc = '' + searchables = sorted(db.field_metadata.searchable_fields(), + lambda x, y: cmp(x if x[0] != '#' else x[1:], + y if y[0] != '#' else y[1:])) + self.general_combo.addItems(searchables) + + if (box_values): + for k,v in box_values.items(): + if k == 'general_index': + continue + getattr(self, k).setText(v) + self.general_combo.setCurrentIndex( + self.general_combo.findText(box_values['general_index'])) + self.box_last_values = box_values + + self.buttonBox.accepted.connect(self.advanced_search_button_pushed) + self.tab_2_button_box.accepted.connect(self.box_search_accepted) + self.tab_2_button_box.rejected.connect(self.box_search_rejected) + self.clear_button.clicked.connect(self.clear_button_pushed) + self.adv_search_used = False + self.box_search_used = False + + self.tabWidget.setCurrentIndex(current_tab) + self.tabWidget.currentChanged[int].connect(self.tab_changed) + self.tab_changed(current_tab) + + def tab_changed(self, idx): + if idx == 1: + self.tab_2_button_box.button(QDialogButtonBox.Ok).setDefault(True) + else: + self.buttonBox.button(QDialogButtonBox.Ok).setDefault(True) + + def advanced_search_button_pushed(self): + self.adv_search_used = True + self.current_tab = 0 + QDialog.accept(self) + + def box_search_accepted(self): + self.box_search_used = True + self.current_tab = 1 + QDialog.accept(self) + + def box_search_rejected(self): + QDialog.reject(self) + + def clear_button_pushed(self): + self.title_box.setText('') + self.authors_box.setText('') + self.series_box.setText('') + self.tags_box.setText('') + self.general_box.setText('') def tokens(self, raw): phrases = re.findall(r'\s*".*?"\s*', raw) @@ -21,6 +72,12 @@ class SearchDialog(QDialog, Ui_Dialog): return ['"' + self.mc + t + '"' for t in phrases + [r.strip() for r in raw.split()]] def search_string(self): + if self.adv_search_used: + return self.adv_search_string() + else: + return self.box_search_string() + + def adv_search_string(self): mk = self.matchkind.currentIndex() if mk == CONTAINS_MATCH: self.mc = '' @@ -56,3 +113,34 @@ class SearchDialog(QDialog, Ui_Dialog): tok = '"%s"'%tok return tok + def box_search_string(self): + ans = [] + self.box_last_values = {} + title = unicode(self.title_box.text()).strip() + self.box_last_values['title_box'] = title + if title: + ans.append('title:"' + title + '"') + author = unicode(self.authors_box.text()).strip() + self.box_last_values['authors_box'] = author + if author: + ans.append('author:"' + author + '"') + series = unicode(self.series_box.text()).strip() + self.box_last_values['series_box'] = series + if series: + ans.append('series:"' + series + '"') + self.mc = '=' + tags = unicode(self.tags_box.text()) + self.box_last_values['tags_box'] = tags + tags = self.tokens(tags) + if tags: + tags = ['tags:' + t for t in tags] + ans.append('(' + ' or '.join(tags) + ')') + general = unicode(self.general_box.text()) + self.box_last_values['general_box'] = general + general_index = unicode(self.general_combo.currentText()) + self.box_last_values['general_index'] = general_index + if general: + ans.append(unicode(self.general_combo.currentText()) + ':"' + general + '"') + if ans: + return ' and '.join(ans) + return '' diff --git a/src/calibre/gui2/dialogs/search.ui b/src/calibre/gui2/dialogs/search.ui index 9e8817b1f4..a974d1b551 100644 --- a/src/calibre/gui2/dialogs/search.ui +++ b/src/calibre/gui2/dialogs/search.ui @@ -1,195 +1,352 @@ - + + Dialog - - + + 0 0 - 667 - 391 + 686 + 360 - + Advanced Search - - + + :/images/search.png:/images/search.png - + - - - Find entries that have... + + + 1 - - - - - - - &All these words: - - - all - - - - - - - - - - - - - - This exact &phrase: - - - all - - - - - - - - - - - - - - &One or more of these words: - - - all - - - - - - - - - - - - - - - But dont show entries that have... - - - - - - - - Any of these &unwanted words: - - - all - - - - - - - - - - - - - - - - 16777215 - 60 - - - - - - - What kind of match to use: - - - matchkind - - - - - - - - Contains: the word or phrase matches anywhere in the metadata + + + A&dvanced Search + + + + + + Find entries that have... - - - - Equals: the word or phrase must match an entire metadata field + + + + + + + &All these words: + + + all + + + + + + + + + + + + + + This exact &phrase: + + + all + + + + + + + + + + + + + + &One or more of these words: + + + all + + + + + + + + + + + + + + + But dont show entries that have... - - - - Regular expression: the expression must match anywhere in the metadata + + + + + + + Any of these &unwanted words: + + + all + + + + + + + + + + + + + 16777215 + 60 + + + + + + + What kind of match to use: + + + matchkind + + + + + + + + Contains: the word or phrase matches anywhere in the metadata + + + + + Equals: the word or phrase must match an entire metadata field + + + + + Regular expression: the expression must match anywhere in the metadata + + + + + + + + + 40 + 0 + + + + + + + matchkind + + + + + + + + + + + 16777215 + 30 + + + + See the <a href="http://calibre-ebook.com/user_manual/gui.html#the-search-interface">User Manual</a> for more help + + + true + + + + + + + + + + Qt::Horizontal - - - - - - - - 40 - 0 - - - - - - - matchkind - - - - - - - - - - - 16777215 - 30 - - - - See the <a href="http://calibre-ebook.com/user_manual/gui.html#the-search-interface">User Manual</a> for more help - - - true - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Titl&e/Author/Series ... + + + + + + &Title: + + + title_box + + + + + + + Enter the title. + + + + + + + &Author + + + authors_box + + + + + + + &Series + + + series_box + + + + + + + Ta&gs + + + tags_box + + + + + + + Enter an author's name. Only one author can be used. + + + + + + + Enter a series name, without an index. Only one series name can be used. + + + + + + + Enter tags separated by spaces + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + &Clear + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + all + phrase + any + none + matchkind + buttonBox + title_box + authors_box + series_box + tags_box + general_combo + general_box + clear_button + tab_2_button_box + tabWidget + - + @@ -198,11 +355,11 @@ Dialog accept() - + 248 254 - + 157 274 @@ -214,11 +371,11 @@ Dialog reject() - + 316 260 - + 286 274 diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index 885a9cc33f..07fac48b9d 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -167,6 +167,7 @@ class SearchBar(QWidget): # {{{ x.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) parent.advanced_search_button = x = QToolButton(self) + parent.advanced_search_button.setShortcut(_("Ctrl+s")) x.setIcon(QIcon(I('search.png'))) l.addWidget(x) x.setToolTip(_("Advanced search")) diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py index 0b85749370..9aa0c2c4dc 100644 --- a/src/calibre/gui2/search_box.py +++ b/src/calibre/gui2/search_box.py @@ -381,6 +381,8 @@ class SearchBoxMixin(object): unicode(self.search.toolTip()))) self.advanced_search_button.setStatusTip(self.advanced_search_button.toolTip()) self.clear_button.setStatusTip(self.clear_button.toolTip()) + self.search_last_values = None + self.search_current_tab = 0 def search_box_cleared(self): self.tags_view.clear() @@ -392,9 +394,12 @@ class SearchBoxMixin(object): self.tags_view.clear() def do_advanced_search(self, *args): - d = SearchDialog(self) + d = SearchDialog(self, self.library_view.model().db, + self.search_last_values, self.search_current_tab) if d.exec_() == QDialog.Accepted: self.search.set_search_string(d.search_string()) + self.search_last_values = d.box_last_values + self.search_current_tab = d.current_tab class SavedSearchBoxMixin(object):