diff --git a/src/calibre/gui2/dialogs/search.py b/src/calibre/gui2/dialogs/search.py index 86d18e6145..337c7674f0 100644 --- a/src/calibre/gui2/dialogs/search.py +++ b/src/calibre/gui2/dialogs/search.py @@ -2,6 +2,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' import re, copy +from datetime import date from PyQt4.Qt import QDialog, QDialogButtonBox @@ -10,15 +11,45 @@ from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH from calibre.gui2 import gprefs from calibre.utils.icu import sort_key from calibre.utils.config import tweaks +from calibre.utils.date import now box_values = {} last_matchkind = CONTAINS_MATCH +def init_dateop(cb): + for op, desc in [ + ('=', _('equal to')), + ('<', _('before')), + ('>', _('after')), + ('<=', _('before or equal to')), + ('>=', _('after or equal to')), + ]: + cb.addItem(desc, op) + +def current_dateop(cb): + return unicode(cb.itemData(cb.currentIndex()).toString()) + class SearchDialog(QDialog, Ui_Dialog): def __init__(self, parent, db): QDialog.__init__(self, parent) self.setupUi(self) + for val, text in [(0, '')] + [(i, date(2010, i, 1).strftime('%B')) for i in xrange(1, 13)]: + self.date_month.addItem(text, val) + for val, text in [('today', _('Today')), ('yesterday', _('Yesterday')), ('thismonth', _('This month'))]: + self.date_human.addItem(text, val) + self.date_year.setValue(now().year) + vals = [((v['search_terms'] or [k])[0], v['name'] or k) for k, v in db.field_metadata.iteritems() if v.get('datatype', None) == 'datetime'] + for k, v in sorted(vals, key=lambda (k, v): sort_key(v)): + self.date_field.addItem(v, k) + + self.date_year.valueChanged.connect(lambda : self.sel_date.setChecked(True)) + self.date_month.currentIndexChanged.connect(lambda : self.sel_date.setChecked(True)) + self.date_day.valueChanged.connect(lambda : self.sel_date.setChecked(True)) + self.date_daysago.valueChanged.connect(lambda : self.sel_daysago.setChecked(True)) + self.date_human.currentIndexChanged.connect(lambda : self.sel_human.setChecked(True)) + init_dateop(self.dateop_date) + self.sel_date.setChecked(True) self.mc = '' searchables = sorted(db.field_metadata.searchable_fields(), key=lambda x: sort_key(x if x[0] != '#' else x[1:])) @@ -50,11 +81,7 @@ class SearchDialog(QDialog, Ui_Dialog): 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) @@ -77,14 +104,8 @@ class SearchDialog(QDialog, Ui_Dialog): 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() + bb = (self.buttonBox, self.tab_2_button_box, self.tab_3_button_box)[idx] + bb.button(QDialogButtonBox.Ok).setDefault(True) def clear_button_pushed(self): self.title_box.setText('') @@ -101,10 +122,25 @@ 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() + i = self.tabWidget.currentIndex() + return (self.adv_search_string, self.box_search_string, self.date_search_string)[i]() + + def date_search_string(self): + field = unicode(self.date_field.itemData(self.date_field.currentIndex()).toString()) + op = current_dateop(self.dateop_date) + prefix = '%s:%s' % (field, op) + if self.sel_date.isChecked(): + ans = '%s%s' % (prefix, self.date_year.value()) + m = self.date_month.itemData(self.date_month.currentIndex()).toPyObject() + if m > 0: + ans += '-%s' % m + d = self.date_day.value() + if d > 0: + ans += '-%s' % d + return ans + if self.sel_daysago.isChecked(): + return '%s%sdaysago' % (prefix, self.date_daysago.value()) + return '%s%s' % (prefix, unicode(self.date_human.itemData(self.date_human.currentIndex()).toString())) def adv_search_string(self): mk = self.matchkind.currentIndex() diff --git a/src/calibre/gui2/dialogs/search.ui b/src/calibre/gui2/dialogs/search.ui index f9966fd12c..c1a90e0e00 100644 --- a/src/calibre/gui2/dialogs/search.ui +++ b/src/calibre/gui2/dialogs/search.ui @@ -6,7 +6,7 @@ 0 0 - 731 + 872 411 @@ -36,7 +36,7 @@ - <p>You can also perform other kinds of advanced searches, for example searching date columns for dates after a particular date, or checking for books that have no covers and so on. See the <a href="http://manual.calibre-ebook.com/gui.html#the-search-interface">The Search Interface</a> for more information. + <p>You can also perform other kinds of advanced searches, for example checking for books that have no covers, combining multiple search expression using Boolean operators and so on. See the <a href="http://manual.calibre-ebook.com/gui.html#the-search-interface">The Search Interface</a> for more information. true @@ -49,6 +49,35 @@ + + + + &What kind of match to use: + + + matchkind + + + + + + + + Contains: the word or phrase matches anywhere in the metadata field + + + + + Equals: the word or phrase must match the entire metadata field + + + + + Regular expression: the expression must match anywhere in the metadata field + + + + @@ -289,35 +318,222 @@ - - - - - - &What kind of match to use: - - - matchkind - - - - - - - - Contains: the word or phrase matches anywhere in the metadata field - - - - - Equals: the word or phrase must match the entire metadata field - - - - - Regular expression: the expression must match anywhere in the metadata field - - + + + &Date searches + + + + + + + + &Search the + + + date_field + + + + + + + + + + date column for books whose date is + + + + + + + + + + ... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + + &year + + + date_year + + + + + + + 102 + + + 10000 + + + + + + + &month + + + date_month + + + + + + + + + + &day + + + date_day + + + + + + +   + + + 31 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + + 999999 + + + + + + + days ago + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + @@ -360,8 +576,8 @@ accept() - 248 - 254 + 256 + 396 157 @@ -376,8 +592,8 @@ reject() - 316 - 260 + 324 + 396 286 @@ -385,5 +601,69 @@ + + tab_3_button_box + accepted() + Dialog + accept() + + + 101 + 370 + + + 697 + 0 + + + + + tab_3_button_box + rejected() + Dialog + reject() + + + 101 + 370 + + + 696 + 36 + + + + + tab_2_button_box + accepted() + Dialog + accept() + + + 508 + 378 + + + 697 + 353 + + + + + tab_2_button_box + rejected() + Dialog + reject() + + + 350 + 376 + + + 697 + 269 + + +