diff --git a/src/calibre/gui2/dialogs/search.py b/src/calibre/gui2/dialogs/search.py index 041e7ff1fc..ba09a34a68 100644 --- a/src/calibre/gui2/dialogs/search.py +++ b/src/calibre/gui2/dialogs/search.py @@ -1,17 +1,75 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -import re -from PyQt4.QtGui import QDialog + +import re, copy + +from PyQt4.QtGui import QDialog, QDialogButtonBox from calibre.gui2.dialogs.search_ui import Ui_Dialog from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH +from calibre.gui2 import gprefs + +box_values = {} class SearchDialog(QDialog, Ui_Dialog): - def __init__(self, *args): - QDialog.__init__(self, *args) + def __init__(self, parent, db): + 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) + + self.box_last_values = copy.deepcopy(box_values) + if self.box_last_values: + for k,v in self.box_last_values.items(): + if k == 'general_index': + continue + getattr(self, k).setText(v) + self.general_combo.setCurrentIndex( + self.general_combo.findText(self.box_last_values['general_index'])) + + self.buttonBox.accepted.connect(self.advanced_search_button_pushed) + self.tab_2_button_box.accepted.connect(self.accept) + self.tab_2_button_box.rejected.connect(self.reject) + self.clear_button.clicked.connect(self.clear_button_pushed) + self.adv_search_used = False + + current_tab = gprefs.get('advanced search dialog current tab', 0) + self.tabWidget.setCurrentIndex(current_tab) + self.tabWidget.currentChanged[int].connect(self.tab_changed) + self.tab_changed(current_tab) + + def save_state(self): + gprefs['advanced search dialog current tab'] = \ + self.tabWidget.currentIndex() + + def accept(self): + self.save_state() + return QDialog.accept(self) + + def reject(self): + self.save_state() + return QDialog.reject(self) + + 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.accept() + + 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 +79,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 +120,36 @@ 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 + global box_values + box_values = copy.deepcopy(self.box_last_values) + 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..41de898b56 100644 --- a/src/calibre/gui2/dialogs/search.ui +++ b/src/calibre/gui2/dialogs/search.ui @@ -1,195 +1,359 @@ - + + Dialog - - + + 0 0 - 667 - 391 + 731 + 384 - + Advanced Search - - + + :/images/search.png:/images/search.png - - - - - Find entries that have... + + + + + 0 - - - - - - - &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::Vertical - - - - - - - - 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 - + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + 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 + + + + + + + + + + + + + + + &Clear + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Search only in specific fields: + + + + + + + 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 +362,11 @@ Dialog accept() - + 248 254 - + 157 274 @@ -214,11 +378,11 @@ Dialog reject() - + 316 260 - + 286 274 diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index 885a9cc33f..c6177ed882 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(_("Shift+Ctrl+F")) 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..b37d74f51f 100644 --- a/src/calibre/gui2/search_box.py +++ b/src/calibre/gui2/search_box.py @@ -392,7 +392,7 @@ class SearchBoxMixin(object): self.tags_view.clear() def do_advanced_search(self, *args): - d = SearchDialog(self) + d = SearchDialog(self, self.library_view.model().db) if d.exec_() == QDialog.Accepted: self.search.set_search_string(d.search_string()) diff --git a/src/calibre/manual/gui.rst b/src/calibre/manual/gui.rst index 4d75400b7d..c81688ba8c 100644 --- a/src/calibre/manual/gui.rst +++ b/src/calibre/manual/gui.rst @@ -380,6 +380,8 @@ Calibre has several keyboard shortcuts to save you time and mouse movement. Thes - Show books in the same series as current book * - :kbd:`/, Ctrl+F` - Focus the search bar + * - :kbd:`Shift+Ctrl+F` + - Open the advanced search dialog * - :kbd:`Ctrl+D` - Download metadata and shortcuts * - :kbd:`Ctrl+R`