From 50c80a8505c395c43904d0139dbaf908628c7298 Mon Sep 17 00:00:00 2001 From: John Schember Date: Tue, 24 May 2011 19:34:04 -0400 Subject: [PATCH] Store: Fix some bugs. Add basic store chooser which give information and allows enabling and disabling store plugins in a user friendly manner. --- src/calibre/customize/builtins.py | 4 +- src/calibre/gui2/actions/store.py | 7 + src/calibre/gui2/store/config/chooser.py | 18 + .../gui2/store/config/chooser/__init__.py | 0 .../config/chooser/adv_search_builder.py | 131 ++++++ .../config/chooser/adv_search_builder.ui | 416 ++++++++++++++++++ .../store/config/chooser/chooser_dialog.py | 28 ++ .../store/config/chooser/chooser_widget.py | 35 ++ .../store/config/chooser/chooser_widget.ui | 87 ++++ .../gui2/store/config/chooser/models.py | 244 ++++++++++ .../gui2/store/config/chooser/results_view.py | 31 ++ .../gui2/store/config/search/__init__.py | 0 .../config/{ => search}/search_widget.py | 2 +- .../config/{ => search}/search_widget.ui | 0 src/calibre/gui2/store/config/store.py | 2 +- src/calibre/gui2/store/mobileread/models.py | 1 + .../gui2/store/search/adv_search_builder.py | 2 +- src/calibre/gui2/store/search/search.py | 2 +- src/calibre/gui2/store/search/search.ui | 7 +- 19 files changed, 1009 insertions(+), 8 deletions(-) create mode 100644 src/calibre/gui2/store/config/chooser.py create mode 100644 src/calibre/gui2/store/config/chooser/__init__.py create mode 100644 src/calibre/gui2/store/config/chooser/adv_search_builder.py create mode 100644 src/calibre/gui2/store/config/chooser/adv_search_builder.ui create mode 100644 src/calibre/gui2/store/config/chooser/chooser_dialog.py create mode 100644 src/calibre/gui2/store/config/chooser/chooser_widget.py create mode 100644 src/calibre/gui2/store/config/chooser/chooser_widget.ui create mode 100644 src/calibre/gui2/store/config/chooser/models.py create mode 100644 src/calibre/gui2/store/config/chooser/results_view.py create mode 100644 src/calibre/gui2/store/config/search/__init__.py rename src/calibre/gui2/store/config/{ => search}/search_widget.py (96%) rename src/calibre/gui2/store/config/{ => search}/search_widget.ui (100%) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index d9e8be00b5..5c90e5699b 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1316,7 +1316,7 @@ class StoreOpenLibraryStore(StoreBase): actual_plugin = 'calibre.gui2.store.open_library_plugin:OpenLibraryStore' drm_free_only = True - headquarters = ['US'] + headquarters = 'US' formats = ['DAISY', 'DJVU', 'EPUB', 'MOBI', 'PDF', 'TXT'] class StoreOReillyStore(StoreBase): @@ -1381,7 +1381,7 @@ class StoreWoblinkStore(StoreBase): actual_plugin = 'calibre.gui2.store.woblink_plugin:WoblinkStore' drm_free_only = False - location = 'PL' + headquarters = 'PL' formats = ['EPUB'] plugins += [ diff --git a/src/calibre/gui2/actions/store.py b/src/calibre/gui2/actions/store.py index c8507e851c..effe470359 100644 --- a/src/calibre/gui2/actions/store.py +++ b/src/calibre/gui2/actions/store.py @@ -34,6 +34,8 @@ class StoreAction(InterfaceAction): self.store_list_menu = self.store_menu.addMenu(_('Stores')) for n, p in sorted(self.gui.istores.items(), key=lambda x: x[0].lower()): self.store_list_menu.addAction(n, partial(self.open_store, p)) + self.store_menu.addSeparator() + self.store_menu.addAction(_('Choose stores'), self.choose) self.qaction.setMenu(self.store_menu) def do_search(self): @@ -107,6 +109,11 @@ class StoreAction(InterfaceAction): query = 'author:"%s" title:"%s"' % (self._get_author(row), self._get_title(row)) self.search(query) + def choose(self): + from calibre.gui2.store.config.chooser.chooser_dialog import StoreChooserDialog + d = StoreChooserDialog(self.gui) + d.exec_() + def open_store(self, store_plugin): self.show_disclaimer() store_plugin.open(self.gui) diff --git a/src/calibre/gui2/store/config/chooser.py b/src/calibre/gui2/store/config/chooser.py new file mode 100644 index 0000000000..f5c40a18ae --- /dev/null +++ b/src/calibre/gui2/store/config/chooser.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__docformat__ = 'restructuredtext en' + +''' +Config widget access functions for enabling and disabling stores. +''' + +def config_widget(): + from calibre.gui2.store.config.chooser.chooser_widget import StoreChooserWidget + return StoreChooserWidget() + +def save_settings(config_widget): + pass diff --git a/src/calibre/gui2/store/config/chooser/__init__.py b/src/calibre/gui2/store/config/chooser/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/calibre/gui2/store/config/chooser/adv_search_builder.py b/src/calibre/gui2/store/config/chooser/adv_search_builder.py new file mode 100644 index 0000000000..7b519abcd1 --- /dev/null +++ b/src/calibre/gui2/store/config/chooser/adv_search_builder.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__docformat__ = 'restructuredtext en' + +import re + +from PyQt4.Qt import (QDialog, QDialogButtonBox) + +from calibre.gui2.store.config.chooser.adv_search_builder_ui import Ui_Dialog +from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH + +class AdvSearchBuilderDialog(QDialog, Ui_Dialog): + + def __init__(self, parent): + QDialog.__init__(self, parent) + self.setupUi(self) + + 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 + self.mc = '' + + self.tabWidget.setCurrentIndex(0) + self.tabWidget.currentChanged[int].connect(self.tab_changed) + self.tab_changed(0) + + 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.name_box.setText('') + self.description_box.setText('') + self.headquarters_box.setText('') + self.format_box.setText('') + self.enabled_combo.setIndex(0) + self.drm_combo.setIndex(0) + + def tokens(self, raw): + phrases = re.findall(r'\s*".*?"\s*', raw) + for f in phrases: + raw = raw.replace(f, ' ') + phrases = [t.strip('" ') for t in phrases] + 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 = '' + elif mk == EQUALS_MATCH: + self.mc = '=' + else: + self.mc = '~' + all, any, phrase, none = map(lambda x: unicode(x.text()), + (self.all, self.any, self.phrase, self.none)) + all, any, none = map(self.tokens, (all, any, none)) + phrase = phrase.strip() + all = ' and '.join(all) + any = ' or '.join(any) + none = ' and not '.join(none) + ans = '' + if phrase: + ans += '"%s"'%phrase + if all: + ans += (' and ' if ans else '') + all + if none: + ans += (' and not ' if ans else 'not ') + none + if any: + ans += (' or ' if ans else '') + any + return ans + + def token(self): + txt = unicode(self.text.text()).strip() + if txt: + if self.negate.isChecked(): + txt = '!'+txt + tok = self.FIELDS[unicode(self.field.currentText())]+txt + if re.search(r'\s', tok): + tok = '"%s"'%tok + return tok + + def box_search_string(self): + mk = self.matchkind.currentIndex() + if mk == CONTAINS_MATCH: + self.mc = '' + elif mk == EQUALS_MATCH: + self.mc = '=' + else: + self.mc = '~' + + ans = [] + self.box_last_values = {} + name = unicode(self.name_box.text()).strip() + if name: + ans.append('name:"' + self.mc + name + '"') + description = unicode(self.description_box.text()).strip() + if description: + ans.append('description:"' + self.mc + description + '"') + headquarters = unicode(self.headquarters_box.text()).strip() + if headquarters: + ans.append('headquarters:"' + self.mc + headquarters + '"') + format = unicode(self.format_box.text()).strip() + if format: + ans.append('format:"' + self.mc + format + '"') + enabled = unicode(self.enabled_combo.currentText()).strip() + if enabled: + ans.append('enabled:' + enabled) + drm = unicode(self.drm_combo.currentText()).strip() + if drm: + ans.append('drm:' + drm) + if ans: + return ' and '.join(ans) + return '' diff --git a/src/calibre/gui2/store/config/chooser/adv_search_builder.ui b/src/calibre/gui2/store/config/chooser/adv_search_builder.ui new file mode 100644 index 0000000000..7d57321c72 --- /dev/null +++ b/src/calibre/gui2/store/config/chooser/adv_search_builder.ui @@ -0,0 +1,416 @@ + + + Dialog + + + + 0 + 0 + 752 + 472 + + + + Advanced Search + + + + :/images/search.png:/images/search.png + + + + + + &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 + + + + + + + + 0 + + + + A&dvanced Search + + + + + + Find entries that have... + + + + + + + + &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 + 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 + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + Nam&e/Description ... + + + + + + &Name: + + + name_box + + + + + + + Enter the title. + + + + + + + &Description: + + + description_box + + + + + + + &Headquarters: + + + headquarters_box + + + + + + + + + &Clear + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Search only in specific fields: + + + + + + + + + + + + + &Format: + + + format_box + + + + + + + + + + Enabled: + + + + + + + DRM: + + + + + + + + + + + + + true + + + + + false + + + + + + + + + + + + + + true + + + + + false + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + EnLineEdit + QLineEdit +
widgets.h
+
+
+ + all + phrase + any + none + buttonBox + name_box + description_box + headquarters_box + format_box + clear_button + tab_2_button_box + tabWidget + matchkind + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/src/calibre/gui2/store/config/chooser/chooser_dialog.py b/src/calibre/gui2/store/config/chooser/chooser_dialog.py new file mode 100644 index 0000000000..c94796dc11 --- /dev/null +++ b/src/calibre/gui2/store/config/chooser/chooser_dialog.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__docformat__ = 'restructuredtext en' + +from PyQt4.Qt import (QDialog, QDialogButtonBox, QVBoxLayout) + +from calibre.gui2.store.config.chooser.chooser_widget import StoreChooserWidget + +class StoreChooserDialog(QDialog): + + def __init__(self, parent): + QDialog.__init__(self, parent) + + self.setWindowTitle(_('Choose stores')) + + button_box = QDialogButtonBox(QDialogButtonBox.Close) + button_box.accepted.connect(self.accept) + button_box.rejected.connect(self.reject) + v = QVBoxLayout(self) + self.config_widget = StoreChooserWidget() + v.addWidget(self.config_widget) + v.addWidget(button_box) + + self.resize(800, 600) diff --git a/src/calibre/gui2/store/config/chooser/chooser_widget.py b/src/calibre/gui2/store/config/chooser/chooser_widget.py new file mode 100644 index 0000000000..93630d69a7 --- /dev/null +++ b/src/calibre/gui2/store/config/chooser/chooser_widget.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__docformat__ = 'restructuredtext en' + +from PyQt4.Qt import (QWidget, QIcon, QDialog) + +from calibre.gui2.store.config.chooser.adv_search_builder import AdvSearchBuilderDialog +from calibre.gui2.store.config.chooser.chooser_widget_ui import Ui_Form + +class StoreChooserWidget(QWidget, Ui_Form): + + def __init__(self): + QWidget.__init__(self) + self.setupUi(self) + + self.adv_search_builder.setIcon(QIcon(I('search.png'))) + + self.search.clicked.connect(self.do_search) + self.adv_search_builder.clicked.connect(self.build_adv_search) + self.results_view.activated.connect(self.toggle_plugin) + + def do_search(self): + self.results_view.model().search(unicode(self.query.text())) + + def toggle_plugin(self, index): + self.results_view.model().toggle_plugin(index) + + def build_adv_search(self): + adv = AdvSearchBuilderDialog(self) + if adv.exec_() == QDialog.Accepted: + self.query.setText(adv.search_string()) diff --git a/src/calibre/gui2/store/config/chooser/chooser_widget.ui b/src/calibre/gui2/store/config/chooser/chooser_widget.ui new file mode 100644 index 0000000000..69117406b1 --- /dev/null +++ b/src/calibre/gui2/store/config/chooser/chooser_widget.ui @@ -0,0 +1,87 @@ + + + Form + + + + 0 + 0 + 610 + 553 + + + + Form + + + + + + + + Query: + + + + + + + ... + + + + + + + + + + Search + + + + + + + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + true + + + false + + + true + + + false + + + false + + + + + + + + ResultsView + QTreeView +
results_view.h
+
+
+ + +
diff --git a/src/calibre/gui2/store/config/chooser/models.py b/src/calibre/gui2/store/config/chooser/models.py new file mode 100644 index 0000000000..460b698878 --- /dev/null +++ b/src/calibre/gui2/store/config/chooser/models.py @@ -0,0 +1,244 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__docformat__ = 'restructuredtext en' + +from PyQt4.Qt import (Qt, QAbstractItemModel, QIcon, QVariant, QModelIndex) + +from calibre.gui2 import NONE +from calibre.customize.ui import is_disabled, disable_plugin, enable_plugin +from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \ + REGEXP_MATCH +from calibre.utils.icu import sort_key +from calibre.utils.search_query_parser import SearchQueryParser + + +class Matches(QAbstractItemModel): + + HEADERS = [_('Enabled'), _('Name'), _('No DRM'), _('Headquarters'), _('Formats')] + HTML_COLS = [1] + + def __init__(self, plugins): + QAbstractItemModel.__init__(self) + + self.NO_DRM_ICON = QIcon(I('ok.png')) + + self.all_matches = plugins + self.matches = plugins + self.filter = '' + self.search_filter = SearchFilter(self.all_matches) + + self.sort_col = 1 + self.sort_order = Qt.AscendingOrder + + def get_plugin(self, index): + row = index.row() + if row < len(self.matches): + return self.matches[row] + else: + return None + + def search(self, filter): + self.filter = filter.strip() + if not self.filter: + self.matches = self.all_matches + else: + try: + self.matches = list(self.search_filter.parse(self.filter)) + except: + self.matches = self.all_matches + self.layoutChanged.emit() + self.sort(self.sort_col, self.sort_order) + + def toggle_plugin(self, index): + new_index = self.createIndex(index.row(), 0) + data = QVariant(is_disabled(self.get_plugin(index))) + self.setData(new_index, data, Qt.CheckStateRole) + + def index(self, row, column, parent=QModelIndex()): + return self.createIndex(row, column) + + def parent(self, index): + if not index.isValid() or index.internalId() == 0: + return QModelIndex() + return self.createIndex(0, 0) + + def rowCount(self, *args): + return len(self.matches) + + def columnCount(self, *args): + return len(self.HEADERS) + + def headerData(self, section, orientation, role): + if role != Qt.DisplayRole: + return NONE + text = '' + if orientation == Qt.Horizontal: + if section < len(self.HEADERS): + text = self.HEADERS[section] + return QVariant(text) + else: + return QVariant(section+1) + + def data(self, index, role): + row, col = index.row(), index.column() + result = self.matches[row] + if role in (Qt.DisplayRole, Qt.EditRole): + if col == 1: + return QVariant('%s
%s' % (result.name, result.description)) + elif col == 3: + return QVariant(result.headquarters) + elif col == 4: + return QVariant(', '.join(result.formats).upper()) + elif role == Qt.DecorationRole: + if col == 2: + if result.drm_free_only: + return QVariant(self.NO_DRM_ICON) + elif role == Qt.CheckStateRole: + if col == 0: + if is_disabled(result): + return Qt.Unchecked + return Qt.Checked + elif role == Qt.ToolTipRole: + return QVariant('

%s

' % result.description) + return NONE + + def setData(self, index, data, role): + if not index.isValid(): + return False + row, col = index.row(), index.column() + if col == 0: + if data.toBool(): + enable_plugin(self.get_plugin(index)) + else: + disable_plugin(self.get_plugin(index)) + self.dataChanged.emit(self.index(index.row(), 0), self.index(index.row(), self.columnCount() - 1)) + return True + + def flags(self, index): + if index.column() == 0: + return QAbstractItemModel.flags(self, index) | Qt.ItemIsUserCheckable + return QAbstractItemModel.flags(self, index) + + def data_as_text(self, match, col): + text = '' + if col == 0: + text = 'b' if is_disabled(match) else 'a' + elif col == 1: + text = match.name + elif col == 2: + text = 'b' if match.drm else 'a' + elif col == 3: + text = match.headquarteres + return text + + def sort(self, col, order, reset=True): + self.sort_col = col + self.sort_order = order + if not self.matches: + return + descending = order == Qt.DescendingOrder + self.matches.sort(None, + lambda x: sort_key(unicode(self.data_as_text(x, col))), + descending) + if reset: + self.reset() + + +class SearchFilter(SearchQueryParser): + + USABLE_LOCATIONS = [ + 'all', + 'description', + 'drm', + 'enabled', + 'format', + 'formats', + 'headquarters', + 'name', + ] + + def __init__(self, all_plugins=[]): + SearchQueryParser.__init__(self, locations=self.USABLE_LOCATIONS) + self.srs = set(all_plugins) + + def universal_set(self): + return self.srs + + def get_matches(self, location, query): + location = location.lower().strip() + if location == 'formats': + location = 'format' + + matchkind = CONTAINS_MATCH + if len(query) > 1: + if query.startswith('\\'): + query = query[1:] + elif query.startswith('='): + matchkind = EQUALS_MATCH + query = query[1:] + elif query.startswith('~'): + matchkind = REGEXP_MATCH + query = query[1:] + if matchkind != REGEXP_MATCH: ### leave case in regexps because it can be significant e.g. \S \W \D + query = query.lower() + + if location not in self.USABLE_LOCATIONS: + return set([]) + matches = set([]) + all_locs = set(self.USABLE_LOCATIONS) - set(['all']) + locations = all_locs if location == 'all' else [location] + q = { + 'description': lambda x: x.description.lower(), + 'drm': lambda x: not x.drm_free_only, + 'enabled': lambda x: not is_disabled(x), + 'format': lambda x: ','.join(x.formats).lower(), + 'headquarters': lambda x: x.headquarters.lower(), + 'name': lambda x : x.name.lower(), + } + q['formats'] = q['format'] + for sr in self.srs: + for locvalue in locations: + accessor = q[locvalue] + if query == 'true': + if locvalue in ('drm', 'enabled'): + if accessor(sr) == True: + matches.add(sr) + elif accessor(sr) is not None: + matches.add(sr) + continue + if query == 'false': + if locvalue in ('drm', 'enabled'): + if accessor(sr) == False: + matches.add(sr) + elif accessor(sr) is None: + matches.add(sr) + continue + # this is bool, so can't match below + if locvalue in ('drm', 'enabled'): + continue + try: + ### Can't separate authors because comma is used for name sep and author sep + ### Exact match might not get what you want. For that reason, turn author + ### exactmatch searches into contains searches. + if locvalue == 'name' and matchkind == EQUALS_MATCH: + m = CONTAINS_MATCH + else: + m = matchkind + + if locvalue == 'format': + vals = accessor(sr).split(',') + else: + vals = [accessor(sr)] + if _match(query, vals, m): + matches.add(sr) + break + except ValueError: # Unicode errors + import traceback + traceback.print_exc() + return matches + + \ No newline at end of file diff --git a/src/calibre/gui2/store/config/chooser/results_view.py b/src/calibre/gui2/store/config/chooser/results_view.py new file mode 100644 index 0000000000..52d7696e4f --- /dev/null +++ b/src/calibre/gui2/store/config/chooser/results_view.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__docformat__ = 'restructuredtext en' + +from PyQt4.Qt import (QTreeView, QSize) + +from calibre.customize.ui import store_plugins +from calibre.gui2.metadata.single_download import RichTextDelegate +from calibre.gui2.store.config.chooser.models import Matches + +class ResultsView(QTreeView): + + def __init__(self, *args): + QTreeView.__init__(self,*args) + + self._model = Matches([p for p in store_plugins()]) + self.setModel(self._model) + + self.setIconSize(QSize(24, 24)) + + self.rt_delegate = RichTextDelegate(self) + + for i in self._model.HTML_COLS: + self.setItemDelegateForColumn(i, self.rt_delegate) + + for i in xrange(self._model.columnCount()): + self.resizeColumnToContents(i) diff --git a/src/calibre/gui2/store/config/search/__init__.py b/src/calibre/gui2/store/config/search/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/calibre/gui2/store/config/search_widget.py b/src/calibre/gui2/store/config/search/search_widget.py similarity index 96% rename from src/calibre/gui2/store/config/search_widget.py rename to src/calibre/gui2/store/config/search/search_widget.py index 43e911a432..b2e55d2ad1 100644 --- a/src/calibre/gui2/store/config/search_widget.py +++ b/src/calibre/gui2/store/config/search/search_widget.py @@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en' from PyQt4.Qt import QWidget from calibre.gui2 import JSONConfig -from calibre.gui2.store.config.search_widget_ui import Ui_Form +from calibre.gui2.store.config.search.search_widget_ui import Ui_Form class StoreConfigWidget(QWidget, Ui_Form): diff --git a/src/calibre/gui2/store/config/search_widget.ui b/src/calibre/gui2/store/config/search/search_widget.ui similarity index 100% rename from src/calibre/gui2/store/config/search_widget.ui rename to src/calibre/gui2/store/config/search/search_widget.ui diff --git a/src/calibre/gui2/store/config/store.py b/src/calibre/gui2/store/config/store.py index ddc24870bd..852f602d08 100644 --- a/src/calibre/gui2/store/config/store.py +++ b/src/calibre/gui2/store/config/store.py @@ -11,7 +11,7 @@ Config widget access functions for configuring the store action. ''' def config_widget(): - from calibre.gui2.store.config.search_widget import StoreConfigWidget + from calibre.gui2.store.config.search.search_widget import StoreConfigWidget return StoreConfigWidget() def save_settings(config_widget): diff --git a/src/calibre/gui2/store/mobileread/models.py b/src/calibre/gui2/store/mobileread/models.py index a080affb51..297707e248 100644 --- a/src/calibre/gui2/store/mobileread/models.py +++ b/src/calibre/gui2/store/mobileread/models.py @@ -47,6 +47,7 @@ class BooksModel(QAbstractItemModel): self.books = list(self.search_filter.parse(self.filter)) except: self.books = self.all_books + self.layoutChanged.emit() self.sort(self.sort_col, self.sort_order) self.total_changed.emit(self.rowCount()) diff --git a/src/calibre/gui2/store/search/adv_search_builder.py b/src/calibre/gui2/store/search/adv_search_builder.py index 50d4d3f3f4..745e709f90 100644 --- a/src/calibre/gui2/store/search/adv_search_builder.py +++ b/src/calibre/gui2/store/search/adv_search_builder.py @@ -116,7 +116,7 @@ class AdvSearchBuilderDialog(QDialog, Ui_Dialog): if price: ans.append('price:"' + self.mc + price + '"') format = unicode(self.format_box.text()).strip() - if author: + if format: ans.append('format:"' + self.mc + format + '"') if ans: return ' and '.join(ans) diff --git a/src/calibre/gui2/store/search/search.py b/src/calibre/gui2/store/search/search.py index c7c252034d..ffc6ec097e 100644 --- a/src/calibre/gui2/store/search/search.py +++ b/src/calibre/gui2/store/search/search.py @@ -14,7 +14,7 @@ from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QTimer, QCheckBox, from calibre.gui2 import JSONConfig, info_dialog from calibre.gui2.progress_indicator import ProgressIndicator -from calibre.gui2.store.config.search_widget import StoreConfigWidget +from calibre.gui2.store.config.search.search_widget import StoreConfigWidget from calibre.gui2.store.search.adv_search_builder import AdvSearchBuilderDialog from calibre.gui2.store.search.download_thread import SearchThreadPool, \ CacheUpdateThreadPool diff --git a/src/calibre/gui2/store/search/search.ui b/src/calibre/gui2/store/search/search.ui index 0360fa5f98..1451aa09f1 100644 --- a/src/calibre/gui2/store/search/search.ui +++ b/src/calibre/gui2/store/search/search.ui @@ -82,8 +82,8 @@ 0 0 - 102 - 129 + 125 + 127 @@ -159,6 +159,9 @@ false + + false +