Refactor the advanced search dialog to use modern UI building techniques, getting rid of the .ui file

Implement the Clear button for all tabs and also add placeholder text
where appropriate.
This commit is contained in:
Kovid Goyal 2017-05-21 07:54:07 +05:30
parent 355a8d417b
commit bb6158f57e
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 239 additions and 739 deletions

View File

@ -4,12 +4,16 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import re, copy
from datetime import date
from PyQt5.Qt import QDialog, QDialogButtonBox
from PyQt5.Qt import (
QDialog, QDialogButtonBox, QFrame, QLabel, QComboBox, QIcon, QVBoxLayout,
QSize, QHBoxLayout, QTabWidget, QLineEdit, QWidget, QGroupBox, QFormLayout,
QSpinBox, QRadioButton
)
from calibre import strftime
from calibre.gui2.dialogs.search_ui import Ui_Dialog
from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH
from calibre.gui2 import gprefs
from calibre.gui2.complete2 import EditWithComplete
from calibre.utils.icu import sort_key
from calibre.utils.config import tweaks
from calibre.utils.date import now
@ -19,6 +23,7 @@ box_values = {}
last_matchkind = CONTAINS_MATCH
# UI {{{
def init_dateop(cb):
for op, desc in [
('=', _('equal to')),
@ -34,75 +39,225 @@ def current_dateop(cb):
return unicode(cb.itemData(cb.currentIndex()) or '')
class SearchDialog(QDialog, Ui_Dialog):
def create_msg_label(self):
self.frame = f = QFrame(self)
f.setFrameShape(QFrame.StyledPanel)
f.setFrameShadow(QFrame.Raised)
f.l = l = QVBoxLayout(f)
f.um_label = la = QLabel(_(
"<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=\"%s\">The Search Interface</a> for more information.'
) % localize_user_manual_link('https://manual.calibre-ebook.com/gui.html#the-search-interface'))
la.setMinimumSize(QSize(150, 0))
la.setWordWrap(True)
la.setOpenExternalLinks(True)
l.addWidget(la)
return f
def create_match_kind(self):
self.cmk_label = la = QLabel(_("What &kind of match to use:"))
self.matchkind = m = QComboBox(self)
la.setBuddy(m)
m.addItems([
_("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"),
])
l = QHBoxLayout()
l.addWidget(la), l.addWidget(m)
return l
def create_button_box(self):
self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.clear_button = bb.addButton(_('Clear'), QDialogButtonBox.ResetRole)
self.clear_button.clicked.connect(self.clear_button_pushed)
bb.accepted.connect(self.accept)
bb.rejected.connect(self.reject)
return bb
def create_adv_tab(self):
self.adv_tab = w = QWidget(self.tab_widget)
self.tab_widget.addTab(w, _("A&dvanced Search"))
w.g1 = QGroupBox(_("Find entries that have..."), w)
w.g2 = QGroupBox(("But don't show entries that have..."), w)
w.l = l = QVBoxLayout(w)
l.addWidget(w.g1), l.addWidget(w.g2), l.addStretch(10)
w.g1.l = l = QFormLayout(w.g1)
l.setFieldGrowthPolicy(l.AllNonFixedFieldsGrow)
for key, text in (
('all', _("A&ll these words:")),
('phrase', _("&This exact phrase:")),
('any', _("O&ne or more of these words:")),
):
le = QLineEdit(w)
setattr(self, key, le)
l.addRow(text, le)
w.g2.l = l = QFormLayout(w.g2)
self.none = le = QLineEdit(w)
l.addRow(_("Any of these &unwanted words:"), le)
def create_simple_tab(self, db):
self.simple_tab = w = QWidget(self.tab_widget)
self.tab_widget.addTab(w, ("Titl&e/author/series..."))
w.l = l = QFormLayout(w)
l.setFieldGrowthPolicy(l.AllNonFixedFieldsGrow)
self.title_box = le = QLineEdit(w)
le.setPlaceholderText(_('The title to search for'))
l.addRow(_('&Title:'), le)
self.authors_box = le = EditWithComplete(self)
le.lineEdit().setPlaceholderText(_('The author to search for'))
le.setEditText('')
le.set_separator('&')
le.set_space_before_sep(True)
le.set_add_separator(tweaks['authors_completer_append_separator'])
le.update_items_cache(db.all_author_names())
l.addRow(_('&Author:'), le)
self.series_box = le = EditWithComplete(self)
le.lineEdit().setPlaceholderText(_('The series to search for'))
all_series = sorted((x[1] for x in db.all_series()), key=sort_key)
le.set_separator(None)
le.update_items_cache(all_series)
le.show_initial_value('')
l.addRow(_('&Series:'), le)
self.tags_box = le = EditWithComplete(self)
le.lineEdit().setPlaceholderText(_('The tags to search for'))
self.tags_box.update_items_cache(db.all_tags())
l.addRow(_('Ta&gs:'), le)
searchables = sorted(db.field_metadata.searchable_fields(),
key=lambda x: sort_key(x if x[0] != '#' else x[1:]))
self.general_combo = QComboBox(w)
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.general_box = le = QLineEdit(self)
l.addRow(self.general_combo, le)
def create_date_tab(self, db):
self.date_tab = w = QWidget(self.tab_widget)
self.tab_widget.addTab(w, ("&Date searches"))
w.l = l = QVBoxLayout(w)
def a(w):
h.addWidget(w)
return w
def add(text, w):
w.la = la = QLabel(text)
h.addWidget(la), h.addWidget(w)
la.setBuddy(w)
return w
w.h1 = h = QHBoxLayout()
l.addLayout(h)
self.date_field = df = add(_("&Search the"), QComboBox(w))
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)):
df.addItem(v, k)
h.addWidget(df)
self.dateop_date = dd = add(_("date column for books whose &date is "), QComboBox(w))
init_dateop(dd)
w.la3 = la = QLabel('...')
h.addWidget(la)
h.addStretch(10)
w.h2 = h = QHBoxLayout()
l.addLayout(h)
self.sel_date = a(QRadioButton(_('&year'), w))
self.date_year = dy = a(QSpinBox(w))
dy.setRange(102, 10000)
dy.setValue(now().year)
self.date_month = dm = add(_('mo&nth'), QComboBox(w))
for val, text in [(0, '')] + [(i, strftime('%B', date(2010, i, 1).timetuple())) for i in xrange(1, 13)]:
dm.addItem(text, val)
self.date_day = dd = add(_('&day'), QSpinBox(w))
dd.setRange(0, 31)
dd.setSpecialValueText(u' \xa0')
h.addStretch(10)
w.h3 = h = QHBoxLayout()
l.addLayout(h)
self.sel_daysago = a(QRadioButton('', w))
self.date_daysago = da = a(QSpinBox(w))
da.setRange(0, 9999999)
self.date_ago_type = dt = a(QComboBox(w))
dt.addItems([_('days'), _('weeks'), _('months'), _('years')])
w.la4 = a(QLabel(' ' + _('ago')))
h.addStretch(10)
w.h4 = h = QHBoxLayout()
l.addLayout(h)
self.sel_human = a(QRadioButton('', w))
self.date_human = dh = a(QComboBox(w))
for val, text in [('today', _('Today')), ('yesterday', _('Yesterday')), ('thismonth', _('This month'))]:
dh.addItem(text, val)
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))
self.sel_date.setChecked(True)
h.addStretch(10)
l.addStretch(10)
def setup_ui(self, db):
self.setWindowTitle(_("Advanced Search"))
self.setWindowIcon(QIcon(I('search.png')))
self.l = l = QVBoxLayout(self)
self.h = h = QHBoxLayout()
self.v = v = QVBoxLayout()
l.addLayout(h)
h.addLayout(v)
h.addWidget(create_msg_label(self))
l.addWidget(create_button_box(self))
v.addLayout(create_match_kind(self))
self.tab_widget = tw = QTabWidget(self)
v.addWidget(tw)
create_adv_tab(self)
create_simple_tab(self, db)
create_date_tab(self, db)
# }}}
class SearchDialog(QDialog):
mc = ''
def __init__(self, parent, db):
QDialog.__init__(self, parent)
self.setupUi(self)
self.um_label.setText(self.um_label.text() % localize_user_manual_link('https://manual.calibre-ebook.com/gui.html#the-search-interface'))
for val, text in [(0, '')] + [(i, strftime('%B', date(2010, i, 1).timetuple())) 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)
self.date_day.setSpecialValueText(u' \xa0')
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_ago_type.addItems([_('days'), _('weeks'), _('months'), _('years')])
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:]))
self.general_combo.addItems(searchables)
all_authors = db.all_authors()
all_authors.sort(key=lambda x : sort_key(x[1]))
self.authors_box.setEditText('')
self.authors_box.set_separator('&')
self.authors_box.set_space_before_sep(True)
self.authors_box.set_add_separator(tweaks['authors_completer_append_separator'])
self.authors_box.update_items_cache(db.all_author_names())
all_series = db.all_series()
all_series.sort(key=lambda x : sort_key(x[1]))
self.series_box.set_separator(None)
self.series_box.update_items_cache([x[1] for x in all_series])
self.series_box.show_initial_value('')
all_tags = db.all_tags()
self.tags_box.update_items_cache(all_tags)
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.clear_button.clicked.connect(self.clear_button_pushed)
setup_ui(self, db)
current_tab = gprefs.get('advanced search dialog current tab', 0)
self.tabWidget.setCurrentIndex(current_tab)
self.tab_widget.setCurrentIndex(current_tab)
if current_tab == 1:
self.matchkind.setCurrentIndex(last_matchkind)
self.tabWidget.currentChanged[int].connect(self.tab_changed)
self.tab_changed(current_tab)
self.resize(self.sizeHint())
def save_state(self):
gprefs['advanced search dialog current tab'] = \
self.tabWidget.currentIndex()
self.tab_widget.currentIndex()
def accept(self):
self.save_state()
@ -112,16 +267,20 @@ class SearchDialog(QDialog, Ui_Dialog):
self.save_state()
return QDialog.reject(self)
def tab_changed(self, idx):
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('')
self.authors_box.setText('')
self.series_box.setText('')
self.tags_box.setText('')
self.general_box.setText('')
w = self.tab_widget.currentWidget()
if w is self.date_tab:
for c in w.findChildren(QComboBox):
c.setCurrentIndex(0)
for c in w.findChildren(QSpinBox):
c.setValue(c.minimum())
self.sel_date.setChecked(True)
self.date_year.setValue(now().year)
else:
for c in w.findChildren(QLineEdit):
c.setText('')
for c in w.findChildren(EditWithComplete):
c.setText('')
def tokens(self, raw):
phrases = re.findall(r'\s*".*?"\s*', raw)
@ -131,7 +290,7 @@ class SearchDialog(QDialog, Ui_Dialog):
return ['"' + self.mc + t + '"' for t in phrases + [r.strip() for r in raw.split()]]
def search_string(self):
i = self.tabWidget.currentIndex()
i = self.tab_widget.currentIndex()
return (self.adv_search_string, self.box_search_string, self.date_search_string)[i]()
def date_search_string(self):
@ -236,3 +395,13 @@ class SearchDialog(QDialog, Ui_Dialog):
if ans:
return ' and '.join(ans)
return ''
if __name__ == '__main__':
from calibre.library import db
db = db()
from calibre.gui2 import Application
app = Application([])
d = SearchDialog(None, db)
d.exec_()
print(d.search_string())

View File

@ -1,669 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>872</width>
<height>411</height>
</rect>
</property>
<property name="windowTitle">
<string>Advanced Search</string>
</property>
<property name="windowIcon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/search.png</normaloff>:/images/search.png</iconset>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="2" rowspan="3">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="um_label">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>&lt;p&gt;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 &lt;a href=&quot;%s&quot;&gt;The Search Interface&lt;/a&gt; for more information.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>What &amp;kind of match to use:</string>
</property>
<property name="buddy">
<cstring>matchkind</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="matchkind">
<item>
<property name="text">
<string>Contains: the word or phrase matches anywhere in the metadata field</string>
</property>
</item>
<item>
<property name="text">
<string>Equals: the word or phrase must match the entire metadata field</string>
</property>
</item>
<item>
<property name="text">
<string>Regular expression: the expression must match anywhere in the metadata field</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>A&amp;dvanced Search</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="5" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Find entries that have...</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>A&amp;ll these words:</string>
</property>
<property name="buddy">
<cstring>all</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="all"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;This exact phrase:</string>
</property>
<property name="buddy">
<cstring>all</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="phrase"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>O&amp;ne or more of these words:</string>
</property>
<property name="buddy">
<cstring>all</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="any"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>But don&apos;t show entries that have...</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Any of these &amp;unwanted words:</string>
</property>
<property name="buddy">
<cstring>all</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="none"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Titl&amp;e/author/series...</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>&amp;Title:</string>
</property>
<property name="buddy">
<cstring>title_box</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="EnLineEdit" name="title_box">
<property name="toolTip">
<string>Enter the title.</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>&amp;Author:</string>
</property>
<property name="buddy">
<cstring>authors_box</cstring>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>&amp;Series:</string>
</property>
<property name="buddy">
<cstring>series_box</cstring>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Ta&amp;gs:</string>
</property>
<property name="buddy">
<cstring>tags_box</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="EditWithComplete" name="authors_box">
<property name="toolTip">
<string>Enter an author's name. Only one author can be used.</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="EditWithComplete" name="series_box">
<property name="toolTip">
<string>Enter a series name, without an index. Only one series name can be used.</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="EditWithComplete" name="tags_box">
<property name="toolTip">
<string>Enter tags separated by spaces</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="general_box"/>
</item>
<item row="6" column="0">
<widget class="QComboBox" name="general_combo"/>
</item>
<item row="8" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QPushButton" name="clear_button">
<property name="text">
<string>&amp;Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="tab_2_button_box">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Search only in specific fields:</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>&amp;Date searches</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>&amp;Search the</string>
</property>
<property name="buddy">
<cstring>date_field</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="date_field"/>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="text">
<string>date column for books whose date is </string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="dateop_date"/>
</item>
<item>
<widget class="QLabel" name="label_17">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QRadioButton" name="sel_date">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_14">
<property name="text">
<string>&amp;year</string>
</property>
<property name="buddy">
<cstring>date_year</cstring>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="date_year">
<property name="minimum">
<number>102</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_15">
<property name="text">
<string>mo&amp;nth</string>
</property>
<property name="buddy">
<cstring>date_month</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="date_month"/>
</item>
<item>
<widget class="QLabel" name="label_16">
<property name="text">
<string>&amp;day</string>
</property>
<property name="buddy">
<cstring>date_day</cstring>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="date_day">
<property name="maximum">
<number>31</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QRadioButton" name="sel_daysago">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="date_daysago">
<property name="maximum">
<number>999999</number>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="date_ago_type"/>
</item>
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string> ago</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QRadioButton" name="sel_human">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="date_human"/>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="tab_3_button_box">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>EnLineEdit</class>
<extends>QLineEdit</extends>
<header>calibre/gui2/widgets.h</header>
</customwidget>
<customwidget>
<class>EditWithComplete</class>
<extends>QComboBox</extends>
<header>calibre/gui2/complete2.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>all</tabstop>
<tabstop>phrase</tabstop>
<tabstop>any</tabstop>
<tabstop>none</tabstop>
<tabstop>buttonBox</tabstop>
<tabstop>title_box</tabstop>
<tabstop>authors_box</tabstop>
<tabstop>series_box</tabstop>
<tabstop>tags_box</tabstop>
<tabstop>general_combo</tabstop>
<tabstop>general_box</tabstop>
<tabstop>clear_button</tabstop>
<tabstop>tab_2_button_box</tabstop>
</tabstops>
<resources>
<include location="../../../../resources/images.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>256</x>
<y>396</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>324</x>
<y>396</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>tab_3_button_box</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>101</x>
<y>370</y>
</hint>
<hint type="destinationlabel">
<x>697</x>
<y>0</y>
</hint>
</hints>
</connection>
<connection>
<sender>tab_3_button_box</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>101</x>
<y>370</y>
</hint>
<hint type="destinationlabel">
<x>696</x>
<y>36</y>
</hint>
</hints>
</connection>
<connection>
<sender>tab_2_button_box</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>508</x>
<y>378</y>
</hint>
<hint type="destinationlabel">
<x>697</x>
<y>353</y>
</hint>
</hints>
</connection>
<connection>
<sender>tab_2_button_box</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>350</x>
<y>376</y>
</hint>
<hint type="destinationlabel">
<x>697</x>
<y>269</y>
</hint>
</hints>
</connection>
</connections>
</ui>