From 1d8e98122c743cd697c9d580b97d47eed408954a Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Mon, 7 Feb 2011 11:54:07 +0000
Subject: [PATCH 1/8] Add a 'search limit' feature where the user can specify a
set of fields (columns) to search when non-prefixed terms are used
---
src/calibre/gui2/layout.py | 9 ++++++++
src/calibre/gui2/preferences/look_feel.py | 3 +++
src/calibre/gui2/preferences/look_feel.ui | 20 ++++++++++++++++
src/calibre/gui2/search_box.py | 15 ++++++++++--
src/calibre/gui2/tag_view.py | 2 +-
src/calibre/gui2/ui.py | 2 ++
src/calibre/library/caches.py | 28 +++++++++++++++++++----
src/calibre/utils/config.py | 5 ++++
8 files changed, 77 insertions(+), 7 deletions(-)
diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py
index c1d9498075..d3d51066a1 100644
--- a/src/calibre/gui2/layout.py
+++ b/src/calibre/gui2/layout.py
@@ -202,6 +202,15 @@ class SearchBar(QWidget): # {{{
l.addWidget(x)
x.setVisible(False)
+ x = parent.search_limit_to = QCheckBox()
+ x.setText(_('&Limit'))
+ x.setToolTip('
'+_('When searching for text without using lookup '
+ 'prefixes, as for example someword instead of title:someword, '
+ 'limit the columns searched to those named in the option '
+ 'Preferences -> Look and Feel -> Limit non-prefixed searches to columns.'))
+ x.setVisible(False)
+ l.addWidget(x)
+
x = parent.saved_search = SavedSearchBox(self)
x.setMaximumSize(QSize(150, 16777215))
x.setMinimumContentsLength(15)
diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py
index 37ed90cc61..e7bc172dfd 100644
--- a/src/calibre/gui2/preferences/look_feel.py
+++ b/src/calibre/gui2/preferences/look_feel.py
@@ -62,6 +62,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('tags_browser_partition_method', gprefs, choices=choices)
r('tags_browser_collapse_at', gprefs)
+ r('search_box_limit_to', prefs)
+
self.current_font = None
self.change_font_button.clicked.connect(self.change_font)
@@ -119,6 +121,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
gui.search.search_as_you_type(config['search_as_you_type'])
self.update_font_display()
gui.tags_view.reread_collapse_parameters()
+ gui.search_limit_to.setEnabled(bool(prefs['search_box_limit_to']))
if __name__ == '__main__':
app = QApplication([])
diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui
index 2223167068..248941515c 100644
--- a/src/calibre/gui2/preferences/look_feel.ui
+++ b/src/calibre/gui2/preferences/look_feel.ui
@@ -200,6 +200,26 @@ up into sub-categories. If the partition method is set to disable, this value is
+ -
+
+
+ Limit non-&prefixed searches to columns:
+
+
+ opt_search_box_limit_to
+
+
+
+ -
+
+
+ Choose columns to be searched when not using prefixes, as for
+example when searching for someword instead of title:someword.
+Enter a list of search/lookup names separated by commas. You
+must check the 'Limit' box on the GUI for this option to take effect.
+
+
+
-
diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py
index e4073a01c9..1c94038125 100644
--- a/src/calibre/gui2/search_box.py
+++ b/src/calibre/gui2/search_box.py
@@ -16,7 +16,7 @@ from calibre.gui2 import config
from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor
from calibre.gui2.dialogs.search import SearchDialog
-from calibre.utils.config import dynamic
+from calibre.utils.config import dynamic, prefs
from calibre.utils.search_query_parser import saved_searches
from calibre.utils.icu import sort_key
@@ -271,7 +271,7 @@ class SavedSearchBox(QComboBox): # {{{
def initialize(self, _search_box, colorize=False, help_text=_('Search')):
self.search_box = _search_box
try:
- self.line_edit.setPlaceholderText(help_text)
+ self.line_edit.setPlaceholderText(help_text)
except:
# Using Qt < 4.7
pass
@@ -379,6 +379,12 @@ class SearchBoxMixin(object): # {{{
self.search_highlight_only.stateChanged.connect(self.highlight_only_changed)
self.search_highlight_only.setChecked(
dynamic.get('search_highlight_only', False))
+ self.search_limit_to.stateChanged.connect(self.search_limit_to_changed)
+ self.search_limit_to.setVisible(True)
+ chk = dynamic.get('use_search_box_limit', False)
+ self.search_limit_to.setChecked(chk)
+ prefs['use_search_box_limit'] = chk
+ self.search_limit_to.setEnabled(bool(prefs['search_box_limit_to']))
def focus_search_box(self, *args):
self.search.setFocus(Qt.OtherFocusReason)
@@ -410,6 +416,11 @@ class SearchBoxMixin(object): # {{{
self.current_view().model().set_highlight_only(toWhat)
self.focus_to_library()
+ def search_limit_to_changed(self, toWhat):
+ dynamic.set('use_search_box_limit', toWhat)
+ prefs['use_search_box_limit'] = toWhat
+ self.search.do_search()
+
# }}}
class SavedSearchBoxMixin(object): # {{{
diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py
index fd3530d333..79199c6881 100644
--- a/src/calibre/gui2/tag_view.py
+++ b/src/calibre/gui2/tag_view.py
@@ -1214,7 +1214,7 @@ class TagBrowserMixin(object): # {{{
db.field_metadata.remove_user_categories()
for k in d.categories:
db.field_metadata.add_user_category('@' + k, k)
- db.data.sqp_change_locations(db.field_metadata.get_search_terms())
+ db.data.change_search_locations(db.field_metadata.get_search_terms())
self.tags_view.set_new_model()
self.tags_view.recount()
diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py
index 907dd577b8..70d0d387a5 100644
--- a/src/calibre/gui2/ui.py
+++ b/src/calibre/gui2/ui.py
@@ -482,8 +482,10 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
for action in self.iactions.values():
action.location_selected(location)
if location == 'library':
+ self.search_limit_to.setVisible(True)
self.search_restriction.setEnabled(True)
else:
+ self.search_limit_to.setVisible(False)
self.search_restriction.setEnabled(False)
# Reset the view in case something changed while it was invisible
self.current_view().reset()
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index e818e6a3c0..fb68a0164a 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -11,7 +11,7 @@ from itertools import repeat
from datetime import timedelta
from threading import Thread
-from calibre.utils.config import tweaks
+from calibre.utils.config import tweaks, prefs
from calibre.utils.date import parse_date, now, UNDEFINED_DATE
from calibre.utils.search_query_parser import SearchQueryParser
from calibre.utils.pyparsing import ParseException
@@ -182,15 +182,16 @@ class ResultCache(SearchQueryParser): # {{{
self.first_sort = True
self.search_restriction = ''
self.field_metadata = field_metadata
- all_search_locations = field_metadata.get_search_terms()
- SearchQueryParser.__init__(self, all_search_locations, optimize=True)
+ self.all_search_locations = field_metadata.get_search_terms()
+ SearchQueryParser.__init__(self, self.all_search_locations, optimize=True)
self.build_date_relop_dict()
self.build_numeric_relop_dict()
def break_cycles(self):
self._data = self.field_metadata = self.FIELD_MAP = \
self.numeric_search_relops = self.date_search_relops = \
- self.db_prefs = None
+ self.db_prefs = self.all_search_locations = None
+ self.sqp_change_locations([])
def __getitem__(self, row):
@@ -218,6 +219,10 @@ class ResultCache(SearchQueryParser): # {{{
def universal_set(self):
return set([i[0] for i in self._data if i is not None])
+ def change_search_locations(self, locations):
+ self.sqp_change_locations(locations)
+ self.all_search_locations = locations
+
def build_date_relop_dict(self):
'''
Because the database dates have time in them, we can't use direct
@@ -432,6 +437,7 @@ class ResultCache(SearchQueryParser): # {{{
# get metadata key associated with the search term. Eliminates
# dealing with plurals and other aliases
location = self.field_metadata.search_term_to_field_key(icu_lower(location.strip()))
+ # grouped search terms
if isinstance(location, list):
if allow_recursion:
for loc in location:
@@ -440,6 +446,20 @@ class ResultCache(SearchQueryParser): # {{{
return matches
raise ParseException(query, len(query), 'Recursive query group detected', self)
+ # apply the limit if appropriate
+ if location == 'all' and prefs['use_search_box_limit'] and \
+ prefs['search_box_limit_to']:
+ for l in prefs['search_box_limit_to'].split(','):
+ l = icu_lower(l.strip())
+ if not l or l == 'all':
+ continue
+ if l not in self.all_search_locations:
+ raise ParseException(l, len(l),
+ 'Unknown field "%s" in search column limit'%l, self)
+ matches |= self.get_matches(l, query,
+ candidates=candidates, allow_recursion=allow_recursion)
+ return matches
+
if location in self.field_metadata:
fm = self.field_metadata[location]
# take care of dates special case
diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py
index 11c58f7769..976864ebd3 100644
--- a/src/calibre/utils/config.py
+++ b/src/calibre/utils/config.py
@@ -729,6 +729,11 @@ def _prefs():
c.add_opt('manage_device_metadata', default='manual',
help=_('How and when calibre updates metadata on the device.'))
+ c.add_opt('search_box_limit_to', default='',
+ help=_('Comma-separated list of fields to search when no prefix'))
+ c.add_opt('use_search_box_limit', default=False,
+ help=_('Set to true to apply the search box limit'))
+
c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.')
return c
From 5d14ffaec595a9b66c28117c3d44e8d4d5722f03 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Mon, 7 Feb 2011 17:28:31 +0000
Subject: [PATCH 2/8] Harmonize QString vs unicode
---
src/calibre/gui2/dialogs/tag_list_editor.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/calibre/gui2/dialogs/tag_list_editor.py b/src/calibre/gui2/dialogs/tag_list_editor.py
index 5e35a236e4..ef279e78c7 100644
--- a/src/calibre/gui2/dialogs/tag_list_editor.py
+++ b/src/calibre/gui2/dialogs/tag_list_editor.py
@@ -1,8 +1,8 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal '
-from PyQt4.QtCore import SIGNAL, Qt
-from PyQt4.QtGui import QDialog, QListWidgetItem, QListWidget
+from PyQt4.QtCore import Qt, QString
+from PyQt4.QtGui import QDialog, QListWidgetItem
from calibre.gui2.dialogs.tag_list_editor_ui import Ui_TagListEditor
from calibre.gui2 import question_dialog, error_dialog
@@ -11,9 +11,9 @@ class ListWidgetItem(QListWidgetItem):
def __init__(self, txt):
QListWidgetItem.__init__(self, txt)
- self.initial_value = txt
- self.current_value = txt
- self.previous_value = txt
+ self.initial_value = QString(txt)
+ self.current_value = QString(txt)
+ self.previous_value = QString(txt)
def data(self, role):
if role == Qt.DisplayRole:
@@ -86,7 +86,7 @@ class TagListEditor(QDialog, Ui_TagListEditor):
return
if item.text() != item.initial_text():
id_ = item.data(Qt.UserRole).toInt()[0]
- self.to_rename[id_] = item.text()
+ self.to_rename[id_] = unicode(item.text())
def rename_tag(self):
item = self.available_tags.currentItem()
From 0a89f2e69a2b1b096dfffa02314e9cd2c18ad162 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Mon, 7 Feb 2011 19:02:37 +0000
Subject: [PATCH 3/8] A few changes to the author_sort faq
---
src/calibre/manual/faq.rst | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst
index 9c02ace0e8..cdae20ea3b 100644
--- a/src/calibre/manual/faq.rst
+++ b/src/calibre/manual/faq.rst
@@ -327,12 +327,14 @@ Now coming to author name sorting:
* Authors in the Tag Browser are sorted by the sort value for the **authors**. Remember that this is different from the Author sort field for a book.
* By default, this sort algorithm assumes that the author name is in ``First name Last name`` format and generates a ``Last name, First name`` sort value.
* You can change this algorithm by going to Preferences->Tweaks and setting the :guilabel:`author_sort_copy_method` tweak.
- * You can force |app| to recalculate the author sort values for every author by right clicking on any author and selecting :guilabel:`Manage authors`
- * You can force |app| to recalculate the author sort values for all books by using the bulk metadata edit dialog (select all books and click edit metadata)
- * When recalculating the author sort values for books, |app| uses the author sort values for each individual author.
+ * You can force |app| to recalculate the author sort values for every author by right clicking on any author and selecting :guilabel:`Manage authors`, then pushing the `Recalculate all author sort values` button. Do this after you have set the author_sort_copy_method tweak to what you want.
+ * You can force |app| to recalculate the author sort values for all books by using the bulk metadata edit dialog (select all books and click edit metadata, check the `Automatically set author sort` checkbox, then press OK.)
+ * When recalculating the author sort values for books, |app| uses the author sort values for each individual author. Therefore, ensure that the individual author sort values are correct before recalculating the books' author sort values.
* You can control whether the Tag Browser display authors using their names or their sort values by setting the :guilabel:`categories_use_field_for_author_name` tweak in Preferences->Tweaks
-With all this flexibility, it is possible to have |app| manage your author names however you like. For example, one common request is to have |app| display author names LN, FN. To do this first set the ``author_sort_copy_method`` to ``copy``. Then change all author names to LN, FN via the Manage authors dialog. Then have |app| recalculate author sort values as described above.
+With all this flexibility, it is possible to have |app| manage your author names however you like. For example, one common request is to have |app| display author names LN, FN. To do this first set the ``author_sort_copy_method`` to ``copy``. Then change all author names to LN, FN via the Manage authors dialog. Then have |app| recalculate author sort values for both authors and books as described above.
+
+Note that you can set an individual author's sort value to whatever you want using :guilabel:`Manage authors`. This is useful when dealing with names that |app| will not get right, such as complex multi-part names like Miguel de Cervantes Saavedra or when dealing with Asian names like Sun Tzu.
Why doesn't |app| let me store books in my own directory structure?
From 708b1a1769323d85da41b7666fa71a1520cc9f25 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Mon, 7 Feb 2011 20:41:20 +0000
Subject: [PATCH 4/8] Add completion to the search limit entry box
---
src/calibre/gui2/preferences/look_feel.py | 3 +++
src/calibre/gui2/preferences/look_feel.ui | 9 ++++++++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py
index e7bc172dfd..86d450567c 100644
--- a/src/calibre/gui2/preferences/look_feel.py
+++ b/src/calibre/gui2/preferences/look_feel.py
@@ -63,6 +63,9 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('tags_browser_collapse_at', gprefs)
r('search_box_limit_to', prefs)
+ self.opt_search_box_limit_to.set_separator(',')
+ self.opt_search_box_limit_to.update_items_cache(
+ self.gui.library_view.model().db.field_metadata.get_search_terms())
self.current_font = None
self.change_font_button.clicked.connect(self.change_font)
diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui
index 248941515c..4bd514101b 100644
--- a/src/calibre/gui2/preferences/look_feel.ui
+++ b/src/calibre/gui2/preferences/look_feel.ui
@@ -211,7 +211,7 @@ up into sub-categories. If the partition method is set to disable, this value is
-
-
+
Choose columns to be searched when not using prefixes, as for
example when searching for someword instead of title:someword.
@@ -305,6 +305,13 @@ must check the 'Limit' box on the GUI for this option to take effect.
+
+
+ MultiCompleteLineEdit
+ QLineEdit
+
+
+
From 8818bccf1d201addecf3a9a596a35260435bae5e Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Tue, 8 Feb 2011 12:03:03 +0000
Subject: [PATCH 5/8] Intermediate commit before cleaning up after adding
search options box
---
src/calibre/gui2/actions/next_match.py | 8 +-
src/calibre/gui2/layout.py | 108 ++++++++++++++++++----
src/calibre/gui2/library/models.py | 2 -
src/calibre/gui2/preferences/look_feel.py | 6 --
src/calibre/gui2/preferences/look_feel.ui | 20 ----
src/calibre/gui2/search_box.py | 50 ++++++++--
src/calibre/gui2/ui.py | 2 -
src/calibre/utils/config.py | 2 +-
8 files changed, 134 insertions(+), 64 deletions(-)
diff --git a/src/calibre/gui2/actions/next_match.py b/src/calibre/gui2/actions/next_match.py
index 79de6a2d9b..b88aa0dd59 100644
--- a/src/calibre/gui2/actions/next_match.py
+++ b/src/calibre/gui2/actions/next_match.py
@@ -29,12 +29,12 @@ class NextMatchAction(InterfaceAction):
self.p_action.triggered.connect(self.move_backward)
def gui_layout_complete(self):
- self.gui.search_highlight_only.setVisible(True)
+ self.gui.search_options_button.setVisible(True)
def location_selected(self, loc):
self.can_move = loc == 'library'
try:
- self.gui.search_highlight_only.setVisible(self.can_move)
+ self.gui.search_options_button.setVisible(self.can_move)
except:
import traceback
traceback.print_exc()
@@ -42,7 +42,7 @@ class NextMatchAction(InterfaceAction):
def move_forward(self):
if self.can_move is None:
self.can_move = self.gui.current_view() is self.gui.library_view
- self.gui.search_highlight_only.setVisible(self.can_move)
+ self.gui.search_options_button.setVisible(self.can_move)
if self.can_move:
self.gui.current_view().move_highlighted_row(forward=True)
@@ -50,7 +50,7 @@ class NextMatchAction(InterfaceAction):
def move_backward(self):
if self.can_move is None:
self.can_move = self.gui.current_view() is self.gui.library_view
- self.gui.search_highlight_only.setVisible(self.can_move)
+ self.gui.search_options_button.setVisible(self.can_move)
if self.can_move:
self.gui.current_view().move_highlighted_row(forward=False)
diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py
index d3d51066a1..9ef8a546eb 100644
--- a/src/calibre/gui2/layout.py
+++ b/src/calibre/gui2/layout.py
@@ -7,8 +7,8 @@ __docformat__ = 'restructuredtext en'
from functools import partial
-from PyQt4.Qt import QIcon, Qt, QWidget, QToolBar, QSize, \
- pyqtSignal, QToolButton, QMenu, QCheckBox, \
+from PyQt4.Qt import QIcon, Qt, QWidget, QToolBar, QSize, QDialogButtonBox, \
+ pyqtSignal, QToolButton, QMenu, QCheckBox, QDialog, QGridLayout, \
QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup
@@ -17,7 +17,9 @@ from calibre.gui2.search_box import SearchBox2, SavedSearchBox
from calibre.gui2.throbber import ThrobbingButton
from calibre.gui2 import gprefs
from calibre.gui2.widgets import ComboBoxWithHelp
+from calibre.gui2.complete import MultiCompleteLineEdit
from calibre import human_readable
+from calibre.utils.config import prefs
class LocationManager(QObject): # {{{
@@ -149,6 +151,8 @@ class SearchBar(QWidget): # {{{
def __init__(self, parent):
QWidget.__init__(self, parent)
+ self.parent = parent
+
self._layout = l = QHBoxLayout()
self.setLayout(self._layout)
self._layout.setContentsMargins(0,5,0,0)
@@ -156,9 +160,10 @@ class SearchBar(QWidget): # {{{
x = ComboBoxWithHelp(self)
x.setMaximumSize(QSize(150, 16777215))
x.setObjectName("search_restriction")
- x.setToolTip(_("Books display will be restricted to those matching the selected saved search"))
- l.addWidget(x)
+ x.setToolTip(_('Books display will be restricted to those matching the '
+ 'selected saved search'))
parent.search_restriction = x
+ l.addWidget(x)
x = QLabel(self)
x.setObjectName("search_count")
@@ -175,7 +180,8 @@ class SearchBar(QWidget): # {{{
x = parent.search = SearchBox2(self)
x.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
x.setObjectName("search")
- x.setToolTip(_("
Search the list of books by title, author, publisher, tags, comments, etc.
Words separated by spaces are ANDed"))
+ x.setToolTip(_("
Search the list of books by title, author, publisher, "
+ "tags, comments, etc.
Words separated by spaces are ANDed"))
l.addWidget(x)
self.search_button = QToolButton()
@@ -194,23 +200,13 @@ class SearchBar(QWidget): # {{{
l.addWidget(x)
x.setToolTip(_("Reset Quick Search"))
- x = parent.search_highlight_only = QCheckBox()
- x.setText(_('&Highlight'))
- x.setToolTip('
'+_('When searching, highlight matched books, instead '
- 'of restricting the book list to the matches.
You can use the '
- 'N or F3 keys to go to the next match.'))
+ x = parent.search_options_button = QToolButton(self)
+ x.setIcon(QIcon(I('config.png')))
+ x.setObjectName("search_option_button")
l.addWidget(x)
+ x.setToolTip(_("Change search highlighting and field limit options"))
x.setVisible(False)
- x = parent.search_limit_to = QCheckBox()
- x.setText(_('&Limit'))
- x.setToolTip('
'+_('When searching for text without using lookup '
- 'prefixes, as for example someword instead of title:someword, '
- 'limit the columns searched to those named in the option '
- 'Preferences -> Look and Feel -> Limit non-prefixed searches to columns.'))
- x.setVisible(False)
- l.addWidget(x)
-
x = parent.saved_search = SavedSearchBox(self)
x.setMaximumSize(QSize(150, 16777215))
x.setMinimumContentsLength(15)
@@ -236,6 +232,80 @@ class SearchBar(QWidget): # {{{
x.setToolTip(_("Delete current saved search"))
+class SearchOptions(QDialog):
+
+ def __init__(self, parent, limit_to_fields, limit_field_list,
+ limit_cbox, highlight_cbox):
+ QDialog.__init__(self, parent=parent)
+# self.search_limit_possible_fields = []
+# self.search_limit_cbox_value = False
+# self.search_highlight_cbox_value = False
+# self.search_limit_list = ''
+# self = self.search_popup = QDialog(self.parent)
+ self.setWindowTitle(_('Search options'))
+ l = QGridLayout()
+ self.setLayout(l)
+
+ x = QLabel(_(' '), parent=self)
+ x.setBuddy(parent.search_restriction)
+ l.addWidget(x, 1, 0, 1, 1)
+
+ x = self.search_highlight_only = QCheckBox(self)
+ x.setToolTip('
'+_('When searching, highlight matched books, instead '
+ 'of restricting the book list to the matches.
You can use the '
+ 'N or F3 keys to go to the next match.'))
+ x.setChecked(highlight_cbox)
+ l.addWidget(x, 2, 1, 1, 1)
+ x = QLabel(_('Check this box if you want to see all books with search '
+ 'results &highlighted'), parent=self)
+ x.setBuddy(self.search_highlight_only)
+ l.addWidget(x, 2, 0, 1, 1)
+
+ x = self.search_limit_checkbox = QCheckBox(self)
+ x.setToolTip('
'+_('When searching for text without using lookup '
+ 'prefixes, as for example someword instead of title:someword, '
+ 'limit the columns searched to those named in the option '
+ 'Preferences -> Look and Feel -> Limit non-prefixed searches to columns.'))
+ x.setChecked(limit_cbox)
+ l.addWidget(x, 3, 1, 1, 1)
+ x = QLabel(_('Check this box if you want non-prefixed searches to be '
+ '&limited to certain fields/lookup names'), parent=self)
+ x.setBuddy(self.search_limit_checkbox)
+ l.addWidget(x, 3, 0, 1, 1)
+
+ x = self.search_box_limit_to = MultiCompleteLineEdit(parent=self)
+ x.setToolTip(_('Choose columns to be searched when not using prefixes, '
+ 'as for example when searching for someword instead of '
+ 'title:someword. Enter a list of search/lookup names '
+ 'separated by commas. You must check the Limit box '
+ 'above for this option to take effect.'))
+ x.setMinimumWidth(200)
+ x.set_separator(',')
+ x.update_items_cache(limit_field_list)
+ x.setText(limit_to_fields)
+ l.addWidget(x, 4, 1, 1, 1)
+ x = QLabel(_('Enter the list of fields that non-prefixed searches '
+ 'are &limited to'), parent=self)
+ x.setBuddy(self.search_box_limit_to)
+ l.addWidget(x, 4, 0, 1, 1)
+
+ buttons = QDialogButtonBox()
+ buttons.addButton(QDialogButtonBox.Ok)
+ buttons.addButton(QDialogButtonBox.Cancel)
+ l.addWidget(buttons, 5, 0, 1, 1)
+ buttons.accepted.connect(self.search_options_accepted)
+ buttons.rejected.connect(self.search_options_rejected)
+
+ def search_options_accepted(self):
+ QDialog.accept(self)
+
+ def search_options_rejected(self):
+ QDialog.reject(self)
+
+ def values(self):
+ return (unicode(self.search_box_limit_to.text()),
+ bool(self.search_limit_checkbox.checkState()),
+ bool(self.search_highlight_only.checkState()))
# }}}
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index 2f8a747c39..48668d3376 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -238,8 +238,6 @@ class BooksModel(QAbstractTableModel): # {{{
def set_highlight_only(self, toWhat):
self.highlight_only = toWhat
- if self.last_search:
- self.research()
def get_current_highlighted_id(self):
if len(self.ids_to_highlight) == 0 or self.current_highlighted_idx is None:
diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py
index 86d450567c..37ed90cc61 100644
--- a/src/calibre/gui2/preferences/look_feel.py
+++ b/src/calibre/gui2/preferences/look_feel.py
@@ -62,11 +62,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('tags_browser_partition_method', gprefs, choices=choices)
r('tags_browser_collapse_at', gprefs)
- r('search_box_limit_to', prefs)
- self.opt_search_box_limit_to.set_separator(',')
- self.opt_search_box_limit_to.update_items_cache(
- self.gui.library_view.model().db.field_metadata.get_search_terms())
-
self.current_font = None
self.change_font_button.clicked.connect(self.change_font)
@@ -124,7 +119,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
gui.search.search_as_you_type(config['search_as_you_type'])
self.update_font_display()
gui.tags_view.reread_collapse_parameters()
- gui.search_limit_to.setEnabled(bool(prefs['search_box_limit_to']))
if __name__ == '__main__':
app = QApplication([])
diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui
index 4bd514101b..2c9c2cc089 100644
--- a/src/calibre/gui2/preferences/look_feel.ui
+++ b/src/calibre/gui2/preferences/look_feel.ui
@@ -200,26 +200,6 @@ up into sub-categories. If the partition method is set to disable, this value is
- -
-
-
- Limit non-&prefixed searches to columns:
-
-
- opt_search_box_limit_to
-
-
-
- -
-
-
- Choose columns to be searched when not using prefixes, as for
-example when searching for someword instead of title:someword.
-Enter a list of search/lookup names separated by commas. You
-must check the 'Limit' box on the GUI for this option to take effect.
-
-
-
-
diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py
index 1c94038125..827b549afb 100644
--- a/src/calibre/gui2/search_box.py
+++ b/src/calibre/gui2/search_box.py
@@ -376,15 +376,10 @@ 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_highlight_only.stateChanged.connect(self.highlight_only_changed)
- self.search_highlight_only.setChecked(
- dynamic.get('search_highlight_only', False))
- self.search_limit_to.stateChanged.connect(self.search_limit_to_changed)
- self.search_limit_to.setVisible(True)
- chk = dynamic.get('use_search_box_limit', False)
- self.search_limit_to.setChecked(chk)
- prefs['use_search_box_limit'] = chk
- self.search_limit_to.setEnabled(bool(prefs['search_box_limit_to']))
+
+ self.search_options_button.clicked.connect(self.search_options_button_clicked)
+ prefs['use_search_box_limit'] = dynamic.get('use_search_box_limit', False)
+ highlight_cbox=dynamic.get('search_highlight_only', False)
def focus_search_box(self, *args):
self.search.setFocus(Qt.OtherFocusReason)
@@ -408,6 +403,40 @@ class SearchBoxMixin(object): # {{{
self.search.do_search()
self.focus_to_library()
+ def search_options_button_clicked(self):
+ fm = self.library_view.model().db.field_metadata
+ ll = fm.get_search_terms()
+ ll = [l for l in ll if not l.startswith('@') and l not in fm.search_items]
+ print ll
+
+ from calibre.gui2.layout import SearchOptions
+ options_box = SearchOptions(self,
+ limit_to_fields=prefs['search_box_limit_to'],
+ limit_field_list=ll,
+ limit_cbox=dynamic.get('use_search_box_limit', False),
+ highlight_cbox=dynamic.get('search_highlight_only', False))
+ r = options_box.exec_()
+ if r:
+ limit_list, limit_cb, highlight_cb = options_box.values()
+ print limit_list, limit_cb, highlight_cb
+ prefs['search_box_limit_to'] = limit_list
+ dynamic.set('use_search_box_limit', limit_cb)
+ prefs['use_search_box_limit'] = limit_cb
+ dynamic.set('search_highlight_only', highlight_cb)
+ self.current_view().model().set_highlight_only(highlight_cb)
+ self.search.do_search()
+
+# self.search_highlight_only.stateChanged.connect(self.highlight_only_changed)
+# self.search_highlight_only.setChecked(
+# dynamic.get('search_highlight_only', False))
+# self.search_limit_checkbox.stateChanged.connect(self.search_limit_checkbox_changed)
+# self.search_limit_checkbox.setVisible(True)
+# chk = dynamic.get('use_search_box_limit', False)
+# self.search_limit_checkbox.setChecked(chk)
+# prefs['use_search_box_limit'] = chk
+# self.search_limit_checkbox.setEnabled(bool(prefs['search_box_limit_to']))
+
+
def focus_to_library(self):
self.current_view().setFocus(Qt.OtherFocusReason)
@@ -416,7 +445,8 @@ class SearchBoxMixin(object): # {{{
self.current_view().model().set_highlight_only(toWhat)
self.focus_to_library()
- def search_limit_to_changed(self, toWhat):
+ def search_limit_checkbox_changed(self, toWhat):
+ toWhat = bool(toWhat)
dynamic.set('use_search_box_limit', toWhat)
prefs['use_search_box_limit'] = toWhat
self.search.do_search()
diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py
index 70d0d387a5..907dd577b8 100644
--- a/src/calibre/gui2/ui.py
+++ b/src/calibre/gui2/ui.py
@@ -482,10 +482,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
for action in self.iactions.values():
action.location_selected(location)
if location == 'library':
- self.search_limit_to.setVisible(True)
self.search_restriction.setEnabled(True)
else:
- self.search_limit_to.setVisible(False)
self.search_restriction.setEnabled(False)
# Reset the view in case something changed while it was invisible
self.current_view().reset()
diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py
index 976864ebd3..0ccc949260 100644
--- a/src/calibre/utils/config.py
+++ b/src/calibre/utils/config.py
@@ -729,7 +729,7 @@ def _prefs():
c.add_opt('manage_device_metadata', default='manual',
help=_('How and when calibre updates metadata on the device.'))
- c.add_opt('search_box_limit_to', default='',
+ c.add_opt('search_box_limit_to', default='title, authors, series',
help=_('Comma-separated list of fields to search when no prefix'))
c.add_opt('use_search_box_limit', default=False,
help=_('Set to true to apply the search box limit'))
From cc181a9d0bb5adde566d28f9f891cf654d48acca Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Tue, 8 Feb 2011 12:44:52 +0000
Subject: [PATCH 6/8] Move some initialization to the appropriate mixin and
clean up the use of the preference
---
src/calibre/gui2/init.py | 3 ++
src/calibre/gui2/layout.py | 40 +++++++++---------
src/calibre/gui2/preferences/look_feel.ui | 7 ----
src/calibre/gui2/search_box.py | 50 +++++------------------
4 files changed, 35 insertions(+), 65 deletions(-)
diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py
index ebd670c8fa..bfa009b2da 100644
--- a/src/calibre/gui2/init.py
+++ b/src/calibre/gui2/init.py
@@ -19,6 +19,7 @@ from calibre.gui2.widgets import Splitter
from calibre.gui2.tag_view import TagBrowserWidget
from calibre.gui2.book_details import BookDetails
from calibre.gui2.notify import get_notifier
+from calibre.utils.config import dynamic
_keep_refs = []
@@ -64,6 +65,8 @@ class LibraryViewMixin(object): # {{{
view.verticalHeader().sectionDoubleClicked.connect(self.iactions['View'].view_specific_book)
self.build_context_menus()
+ highlight_cbox=dynamic.get('search_highlight_only', False)
+ self.library_view.model().set_highlight_only(highlight_cbox)
def build_context_menus(self):
lm = QMenu(self)
diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py
index 9ef8a546eb..e6f796da06 100644
--- a/src/calibre/gui2/layout.py
+++ b/src/calibre/gui2/layout.py
@@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
from functools import partial
from PyQt4.Qt import QIcon, Qt, QWidget, QToolBar, QSize, QDialogButtonBox, \
- pyqtSignal, QToolButton, QMenu, QCheckBox, QDialog, QGridLayout, \
+ pyqtSignal, QToolButton, QMenu, QCheckBox, QDialog, QGridLayout, QFrame, \
QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup
@@ -19,7 +19,6 @@ from calibre.gui2 import gprefs
from calibre.gui2.widgets import ComboBoxWithHelp
from calibre.gui2.complete import MultiCompleteLineEdit
from calibre import human_readable
-from calibre.utils.config import prefs
class LocationManager(QObject): # {{{
@@ -151,8 +150,6 @@ class SearchBar(QWidget): # {{{
def __init__(self, parent):
QWidget.__init__(self, parent)
- self.parent = parent
-
self._layout = l = QHBoxLayout()
self.setLayout(self._layout)
self._layout.setContentsMargins(0,5,0,0)
@@ -162,8 +159,8 @@ class SearchBar(QWidget): # {{{
x.setObjectName("search_restriction")
x.setToolTip(_('Books display will be restricted to those matching the '
'selected saved search'))
- parent.search_restriction = x
l.addWidget(x)
+ parent.search_restriction = x
x = QLabel(self)
x.setObjectName("search_count")
@@ -237,21 +234,26 @@ class SearchOptions(QDialog):
def __init__(self, parent, limit_to_fields, limit_field_list,
limit_cbox, highlight_cbox):
QDialog.__init__(self, parent=parent)
-# self.search_limit_possible_fields = []
-# self.search_limit_cbox_value = False
-# self.search_highlight_cbox_value = False
-# self.search_limit_list = ''
-# self = self.search_popup = QDialog(self.parent)
self.setWindowTitle(_('Search options'))
l = QGridLayout()
self.setLayout(l)
- x = QLabel(_(' '), parent=self)
- x.setBuddy(parent.search_restriction)
- l.addWidget(x, 1, 0, 1, 1)
+ x = QLabel(_('Use this box to change search options related to how '
+ 'results are displayed and which fields are searched. '
+ 'Changes will be remembered across calibre restarts. '
+ 'When you press OK, the last search will be redone using '
+ 'the new option values.'),
+ parent=self)
+ x.setWordWrap(True)
+ l.addWidget(x, 0, 0, 1, 2)
+
+ line = QFrame(self)
+ line.setFrameShape(QFrame.HLine)
+ line.setFrameShadow(QFrame.Sunken)
+ l.addWidget(line, 1, 0, 1, 2)
x = self.search_highlight_only = QCheckBox(self)
- x.setToolTip('
'+_('When searching, highlight matched books, instead '
+ x.setToolTip('
'+_('When searching, highlight matched books instead '
'of restricting the book list to the matches.
You can use the '
'N or F3 keys to go to the next match.'))
x.setChecked(highlight_cbox)
@@ -268,8 +270,8 @@ class SearchOptions(QDialog):
'Preferences -> Look and Feel -> Limit non-prefixed searches to columns.'))
x.setChecked(limit_cbox)
l.addWidget(x, 3, 1, 1, 1)
- x = QLabel(_('Check this box if you want non-prefixed searches to be '
- '&limited to certain fields/lookup names'), parent=self)
+ x = QLabel(_('Check this box if you want non-&prefixed searches to be '
+ 'limited to certain fields/lookup names'), parent=self)
x.setBuddy(self.search_limit_checkbox)
l.addWidget(x, 3, 0, 1, 1)
@@ -284,15 +286,15 @@ class SearchOptions(QDialog):
x.update_items_cache(limit_field_list)
x.setText(limit_to_fields)
l.addWidget(x, 4, 1, 1, 1)
- x = QLabel(_('Enter the list of fields that non-prefixed searches '
- 'are &limited to'), parent=self)
+ x = QLabel(_('Enter the list of &columns that non-prefixed searches '
+ 'are limited to'), parent=self)
x.setBuddy(self.search_box_limit_to)
l.addWidget(x, 4, 0, 1, 1)
buttons = QDialogButtonBox()
buttons.addButton(QDialogButtonBox.Ok)
buttons.addButton(QDialogButtonBox.Cancel)
- l.addWidget(buttons, 5, 0, 1, 1)
+ l.addWidget(buttons, 5, 0, 1, 2)
buttons.accepted.connect(self.search_options_accepted)
buttons.rejected.connect(self.search_options_rejected)
diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui
index 2c9c2cc089..2223167068 100644
--- a/src/calibre/gui2/preferences/look_feel.ui
+++ b/src/calibre/gui2/preferences/look_feel.ui
@@ -285,13 +285,6 @@ up into sub-categories. If the partition method is set to disable, this value is
-
-
- MultiCompleteLineEdit
- QLineEdit
-
-
-
diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py
index 827b549afb..b9344a7782 100644
--- a/src/calibre/gui2/search_box.py
+++ b/src/calibre/gui2/search_box.py
@@ -376,10 +376,7 @@ 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_options_button.clicked.connect(self.search_options_button_clicked)
- prefs['use_search_box_limit'] = dynamic.get('use_search_box_limit', False)
- highlight_cbox=dynamic.get('search_highlight_only', False)
def focus_search_box(self, *args):
self.search.setFocus(Qt.OtherFocusReason)
@@ -404,53 +401,28 @@ class SearchBoxMixin(object): # {{{
self.focus_to_library()
def search_options_button_clicked(self):
+ from calibre.gui2.layout import SearchOptions
+
fm = self.library_view.model().db.field_metadata
ll = fm.get_search_terms()
ll = [l for l in ll if not l.startswith('@') and l not in fm.search_items]
- print ll
-
- from calibre.gui2.layout import SearchOptions
- options_box = SearchOptions(self,
- limit_to_fields=prefs['search_box_limit_to'],
- limit_field_list=ll,
- limit_cbox=dynamic.get('use_search_box_limit', False),
- highlight_cbox=dynamic.get('search_highlight_only', False))
+ options_box = SearchOptions(parent=self,
+ limit_to_fields=prefs['search_box_limit_to'],
+ limit_field_list=ll,
+ limit_cbox=prefs['use_search_box_limit'],
+ highlight_cbox=dynamic.get('search_highlight_only', False))
r = options_box.exec_()
if r:
- limit_list, limit_cb, highlight_cb = options_box.values()
- print limit_list, limit_cb, highlight_cb
+ limit_list, limit_cbox, highlight_cbox = options_box.values()
prefs['search_box_limit_to'] = limit_list
- dynamic.set('use_search_box_limit', limit_cb)
- prefs['use_search_box_limit'] = limit_cb
- dynamic.set('search_highlight_only', highlight_cb)
- self.current_view().model().set_highlight_only(highlight_cb)
+ prefs['use_search_box_limit'] = limit_cbox
+ dynamic.set('search_highlight_only', highlight_cbox)
+ self.current_view().model().set_highlight_only(highlight_cbox)
self.search.do_search()
-# self.search_highlight_only.stateChanged.connect(self.highlight_only_changed)
-# self.search_highlight_only.setChecked(
-# dynamic.get('search_highlight_only', False))
-# self.search_limit_checkbox.stateChanged.connect(self.search_limit_checkbox_changed)
-# self.search_limit_checkbox.setVisible(True)
-# chk = dynamic.get('use_search_box_limit', False)
-# self.search_limit_checkbox.setChecked(chk)
-# prefs['use_search_box_limit'] = chk
-# self.search_limit_checkbox.setEnabled(bool(prefs['search_box_limit_to']))
-
-
def focus_to_library(self):
self.current_view().setFocus(Qt.OtherFocusReason)
- def highlight_only_changed(self, toWhat):
- dynamic.set('search_highlight_only', toWhat)
- self.current_view().model().set_highlight_only(toWhat)
- self.focus_to_library()
-
- def search_limit_checkbox_changed(self, toWhat):
- toWhat = bool(toWhat)
- dynamic.set('use_search_box_limit', toWhat)
- prefs['use_search_box_limit'] = toWhat
- self.search.do_search()
-
# }}}
class SavedSearchBoxMixin(object): # {{{
From 900ff9b93ac0e0673b8a4728e5629a831610564d Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Tue, 8 Feb 2011 12:51:00 +0000
Subject: [PATCH 7/8] Minor changes to tooltips, etc
---
src/calibre/gui2/layout.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py
index e6f796da06..efac3e1232 100644
--- a/src/calibre/gui2/layout.py
+++ b/src/calibre/gui2/layout.py
@@ -201,7 +201,7 @@ class SearchBar(QWidget): # {{{
x.setIcon(QIcon(I('config.png')))
x.setObjectName("search_option_button")
l.addWidget(x)
- x.setToolTip(_("Change search highlighting and field limit options"))
+ x.setToolTip(_("Change search highlighting and column limit options"))
x.setVisible(False)
x = parent.saved_search = SavedSearchBox(self)
@@ -239,7 +239,7 @@ class SearchOptions(QDialog):
self.setLayout(l)
x = QLabel(_('Use this box to change search options related to how '
- 'results are displayed and which fields are searched. '
+ 'results are displayed and which columns are searched. '
'Changes will be remembered across calibre restarts. '
'When you press OK, the last search will be redone using '
'the new option values.'),
@@ -253,25 +253,25 @@ class SearchOptions(QDialog):
l.addWidget(line, 1, 0, 1, 2)
x = self.search_highlight_only = QCheckBox(self)
- x.setToolTip('
'+_('When searching, highlight matched books instead '
- 'of restricting the book list to the matches.
You can use the '
+ x.setToolTip('
'+_('When searching, show all books with search results '
+ 'highlight instead of showing only the matches.
You can use the '
'N or F3 keys to go to the next match.'))
x.setChecked(highlight_cbox)
l.addWidget(x, 2, 1, 1, 1)
x = QLabel(_('Check this box if you want to see all books with search '
- 'results &highlighted'), parent=self)
+ 'results &highlighted instead of only the matched books'),
+ parent=self)
x.setBuddy(self.search_highlight_only)
l.addWidget(x, 2, 0, 1, 1)
x = self.search_limit_checkbox = QCheckBox(self)
x.setToolTip('
'+_('When searching for text without using lookup '
'prefixes, as for example someword instead of title:someword, '
- 'limit the columns searched to those named in the option '
- 'Preferences -> Look and Feel -> Limit non-prefixed searches to columns.'))
+ 'limit the columns searched to those named in the text box below.'))
x.setChecked(limit_cbox)
l.addWidget(x, 3, 1, 1, 1)
x = QLabel(_('Check this box if you want non-&prefixed searches to be '
- 'limited to certain fields/lookup names'), parent=self)
+ 'limited to certain columns/lookup names'), parent=self)
x.setBuddy(self.search_limit_checkbox)
l.addWidget(x, 3, 0, 1, 1)
From 9b9eeb5265a4ac4c8da1c1bd5649d1f93e85753f Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Tue, 8 Feb 2011 13:07:35 +0000
Subject: [PATCH 8/8] Add some text about limits interacting with saved
searches
---
src/calibre/gui2/layout.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py
index efac3e1232..6ad5551de7 100644
--- a/src/calibre/gui2/layout.py
+++ b/src/calibre/gui2/layout.py
@@ -238,11 +238,15 @@ class SearchOptions(QDialog):
l = QGridLayout()
self.setLayout(l)
- x = QLabel(_('Use this box to change search options related to how '
+ x = QLabel('
'+_('Use this box to change search options related to how '
'results are displayed and which columns are searched. '
'Changes will be remembered across calibre restarts. '
'When you press OK, the last search will be redone using '
- 'the new option values.'),
+ 'the new option values.')+'
'+_('Note: the limit option '
+ 'below affects all searches, including saved searches '
+ 'and, by extension, search restrictions. For this reason '
+ 'it is usually better to use prefixes in saved searches, '
+ 'for example series:someword instead of simply someword.'),
parent=self)
x.setWordWrap(True)
l.addWidget(x, 0, 0, 1, 2)