Fix #7415 (Option to restrict search to just Title/Author/Series/Tags fields)

This commit is contained in:
Kovid Goyal 2011-02-08 11:38:20 -07:00
commit 918dd9b5b9
17 changed files with 255 additions and 61 deletions

View File

@ -791,6 +791,17 @@ class Toolbar(PreferencesPlugin):
description = _('Customize the toolbars and context menus, changing which' description = _('Customize the toolbars and context menus, changing which'
' actions are available in each') ' actions are available in each')
class Search(PreferencesPlugin):
name = 'Search'
icon = I('search.png')
gui_name = _('Customize searching')
category = 'Interface'
gui_category = _('Interface')
category_order = 1
name_order = 5
config_widget = 'calibre.gui2.preferences.search'
description = _('Customize the way searching for books works in calibre')
class InputOptions(PreferencesPlugin): class InputOptions(PreferencesPlugin):
name = 'Input Options' name = 'Input Options'
icon = I('arrow-down.png') icon = I('arrow-down.png')
@ -941,7 +952,7 @@ class Misc(PreferencesPlugin):
config_widget = 'calibre.gui2.preferences.misc' config_widget = 'calibre.gui2.preferences.misc'
description = _('Miscellaneous advanced configuration') description = _('Miscellaneous advanced configuration')
plugins += [LookAndFeel, Behavior, Columns, Toolbar, InputOptions, plugins += [LookAndFeel, Behavior, Columns, Toolbar, Search, InputOptions,
CommonOptions, OutputOptions, Adding, Saving, Sending, Plugboard, CommonOptions, OutputOptions, Adding, Saving, Sending, Plugboard,
Email, Server, Plugins, Tweaks, Misc, TemplateFunctions] Email, Server, Plugins, Tweaks, Misc, TemplateFunctions]

View File

@ -106,9 +106,13 @@ def _config():
'clicked')) 'clicked'))
c.add_opt('asked_library_thing_password', default=False, c.add_opt('asked_library_thing_password', default=False,
help='Asked library thing password at least once.') help='Asked library thing password at least once.')
c.add_opt('search_as_you_type', default=True, c.add_opt('search_as_you_type', default=False,
help='Start searching as you type. If this is disabled then search will ' help=_('Start searching as you type. If this is disabled then search will '
'only take place when the Enter or Return key is pressed.') 'only take place when the Enter or Return key is pressed.'))
c.add_opt('highlight_search_matches', default=False,
help=_('When searching, show all books with search results '
'highlighted instead of showing only the matches. You can use the '
'N or F3 keys to go to the next match.'))
c.add_opt('save_to_disk_template_history', default=[], c.add_opt('save_to_disk_template_history', default=[],
help='Previously used Save to Disk templates') help='Previously used Save to Disk templates')
c.add_opt('send_to_device_template_history', default=[], c.add_opt('send_to_device_template_history', default=[],

View File

@ -28,21 +28,12 @@ class NextMatchAction(InterfaceAction):
self.gui.addAction(self.p_action) self.gui.addAction(self.p_action)
self.p_action.triggered.connect(self.move_backward) self.p_action.triggered.connect(self.move_backward)
def gui_layout_complete(self):
self.gui.search_highlight_only.setVisible(True)
def location_selected(self, loc): def location_selected(self, loc):
self.can_move = loc == 'library' self.can_move = loc == 'library'
try:
self.gui.search_highlight_only.setVisible(self.can_move)
except:
import traceback
traceback.print_exc()
def move_forward(self): def move_forward(self):
if self.can_move is None: if self.can_move is None:
self.can_move = self.gui.current_view() is self.gui.library_view self.can_move = self.gui.current_view() is self.gui.library_view
self.gui.search_highlight_only.setVisible(self.can_move)
if self.can_move: if self.can_move:
self.gui.current_view().move_highlighted_row(forward=True) self.gui.current_view().move_highlighted_row(forward=True)
@ -50,7 +41,6 @@ class NextMatchAction(InterfaceAction):
def move_backward(self): def move_backward(self):
if self.can_move is None: if self.can_move is None:
self.can_move = self.gui.current_view() is self.gui.library_view self.can_move = self.gui.current_view() is self.gui.library_view
self.gui.search_highlight_only.setVisible(self.can_move)
if self.can_move: if self.can_move:
self.gui.current_view().move_highlighted_row(forward=False) self.gui.current_view().move_highlighted_row(forward=False)

View File

@ -33,7 +33,8 @@ class PreferencesAction(InterfaceAction):
x.triggered.connect(self.do_config) x.triggered.connect(self.do_config)
def do_config(self, checked=False, initial_plugin=None): def do_config(self, checked=False, initial_plugin=None,
close_after_initial=False):
if self.gui.job_manager.has_jobs(): if self.gui.job_manager.has_jobs():
d = error_dialog(self.gui, _('Cannot configure'), d = error_dialog(self.gui, _('Cannot configure'),
_('Cannot configure while there are running jobs.')) _('Cannot configure while there are running jobs.'))
@ -44,7 +45,8 @@ class PreferencesAction(InterfaceAction):
_('Cannot configure before calibre is restarted.')) _('Cannot configure before calibre is restarted.'))
d.exec_() d.exec_()
return return
d = Preferences(self.gui, initial_plugin=initial_plugin) d = Preferences(self.gui, initial_plugin=initial_plugin,
close_after_initial=close_after_initial)
d.show() d.show()
d.run_wizard_requested.connect(self.gui.run_wizard, d.run_wizard_requested.connect(self.gui.run_wizard,
type=Qt.QueuedConnection) type=Qt.QueuedConnection)

View File

@ -64,6 +64,7 @@ class LibraryViewMixin(object): # {{{
view.verticalHeader().sectionDoubleClicked.connect(self.iactions['View'].view_specific_book) view.verticalHeader().sectionDoubleClicked.connect(self.iactions['View'].view_specific_book)
self.build_context_menus() self.build_context_menus()
self.library_view.model().set_highlight_only(config['highlight_search_matches'])
def build_context_menus(self): def build_context_menus(self):
lm = QMenu(self) lm = QMenu(self)

View File

@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
from functools import partial from functools import partial
from PyQt4.Qt import QIcon, Qt, QWidget, QToolBar, QSize, \ from PyQt4.Qt import QIcon, Qt, QWidget, QToolBar, QSize, \
pyqtSignal, QToolButton, QMenu, QCheckBox, \ pyqtSignal, QToolButton, QMenu, \
QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup
@ -156,7 +156,8 @@ class SearchBar(QWidget): # {{{
x = ComboBoxWithHelp(self) x = ComboBoxWithHelp(self)
x.setMaximumSize(QSize(150, 16777215)) x.setMaximumSize(QSize(150, 16777215))
x.setObjectName("search_restriction") x.setObjectName("search_restriction")
x.setToolTip(_("Books display will be restricted to those matching the selected saved search")) x.setToolTip(_('Books display will be restricted to those matching the '
'selected saved search'))
l.addWidget(x) l.addWidget(x)
parent.search_restriction = x parent.search_restriction = x
@ -175,7 +176,8 @@ class SearchBar(QWidget): # {{{
x = parent.search = SearchBox2(self) x = parent.search = SearchBox2(self)
x.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) x.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
x.setObjectName("search") x.setObjectName("search")
x.setToolTip(_("<p>Search the list of books by title, author, publisher, tags, comments, etc.<br><br>Words separated by spaces are ANDed")) x.setToolTip(_("<p>Search the list of books by title, author, publisher, "
"tags, comments, etc.<br><br>Words separated by spaces are ANDed"))
l.addWidget(x) l.addWidget(x)
self.search_button = QToolButton() self.search_button = QToolButton()
@ -194,13 +196,11 @@ class SearchBar(QWidget): # {{{
l.addWidget(x) l.addWidget(x)
x.setToolTip(_("Reset Quick Search")) x.setToolTip(_("Reset Quick Search"))
x = parent.search_highlight_only = QCheckBox() x = parent.search_options_button = QToolButton(self)
x.setText(_('&Highlight')) x.setIcon(QIcon(I('config.png')))
x.setToolTip('<p>'+_('When searching, highlight matched books, instead ' x.setObjectName("search_option_button")
'of restricting the book list to the matches.<p> You can use the '
'N or F3 keys to go to the next match.'))
l.addWidget(x) l.addWidget(x)
x.setVisible(False) x.setToolTip(_("Change the way searching for books works"))
x = parent.saved_search = SavedSearchBox(self) x = parent.saved_search = SavedSearchBox(self)
x.setMaximumSize(QSize(150, 16777215)) x.setMaximumSize(QSize(150, 16777215))
@ -227,7 +227,6 @@ class SearchBar(QWidget): # {{{
x.setToolTip(_("Delete current saved search")) x.setToolTip(_("Delete current saved search"))
# }}} # }}}
class Spacer(QWidget): # {{{ class Spacer(QWidget): # {{{

View File

@ -238,8 +238,6 @@ class BooksModel(QAbstractTableModel): # {{{
def set_highlight_only(self, toWhat): def set_highlight_only(self, toWhat):
self.highlight_only = toWhat self.highlight_only = toWhat
if self.last_search:
self.research()
def get_current_highlighted_id(self): def get_current_highlighted_id(self):
if len(self.ids_to_highlight) == 0 or self.current_highlighted_idx is None: if len(self.ids_to_highlight) == 0 or self.current_highlighted_idx is None:

View File

@ -46,7 +46,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('disable_tray_notification', config) r('disable_tray_notification', config)
r('use_roman_numerals_for_series_number', config) r('use_roman_numerals_for_series_number', config)
r('separate_cover_flow', config, restart_required=True) r('separate_cover_flow', config, restart_required=True)
r('search_as_you_type', config)
r('show_child_bar', gprefs) r('show_child_bar', gprefs)
choices = [(_('Small'), 'small'), (_('Medium'), 'medium'), choices = [(_('Small'), 'small'), (_('Medium'), 'medium'),
@ -116,7 +115,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
def refresh_gui(self, gui): def refresh_gui(self, gui):
gui.search.search_as_you_type(config['search_as_you_type'])
self.update_font_display() self.update_font_display()
gui.tags_view.reread_collapse_parameters() gui.tags_view.reread_collapse_parameters()

View File

@ -124,23 +124,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="6" column="0" colspan="2">
<widget class="QCheckBox" name="opt_separate_cover_flow"> <widget class="QCheckBox" name="opt_separate_cover_flow">
<property name="text"> <property name="text">
<string>Show cover &amp;browser in a separate window (needs restart)</string> <string>Show cover &amp;browser in a separate window (needs restart)</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1">
<widget class="QCheckBox" name="opt_search_as_you_type">
<property name="text">
<string>Search as you type</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2"> <item row="7" column="0" colspan="2">
<layout class="QHBoxLayout"> <layout class="QHBoxLayout">
<item> <item>

View File

@ -157,11 +157,12 @@ class Preferences(QMainWindow):
run_wizard_requested = pyqtSignal() run_wizard_requested = pyqtSignal()
def __init__(self, gui, initial_plugin=None): def __init__(self, gui, initial_plugin=None, close_after_initial=False):
QMainWindow.__init__(self, gui) QMainWindow.__init__(self, gui)
self.gui = gui self.gui = gui
self.must_restart = False self.must_restart = False
self.committed = False self.committed = False
self.close_after_initial = close_after_initial
self.resize(900, 720) self.resize(900, 720)
nh, nw = min_available_height()-25, available_width()-10 nh, nw = min_available_height()-25, available_width()-10
@ -306,7 +307,7 @@ class Preferences(QMainWindow):
def esc(self, *args): def esc(self, *args):
if self.stack.currentIndex() == 1: if self.stack.currentIndex() == 1:
self.hide_plugin() self.cancel()
elif self.stack.currentIndex() == 0: elif self.stack.currentIndex() == 0:
self.close() self.close()
@ -331,11 +332,14 @@ class Preferences(QMainWindow):
show_copy_button=False) show_copy_button=False)
self.showing_widget.refresh_gui(self.gui) self.showing_widget.refresh_gui(self.gui)
self.hide_plugin() self.hide_plugin()
if must_restart and rc: if self.close_after_initial or (must_restart and rc):
self.close() self.close()
def cancel(self, *args): def cancel(self, *args):
if self.close_after_initial:
self.close()
else:
self.hide_plugin() self.hide_plugin()
def restore_defaults(self, *args): def restore_defaults(self, *args):

View File

@ -0,0 +1,38 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import QApplication
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, \
CommaSeparatedList
from calibre.gui2.preferences.search_ui import Ui_Form
from calibre.gui2 import config
from calibre.utils.config import prefs
class ConfigWidget(ConfigWidgetBase, Ui_Form):
def genesis(self, gui):
self.gui = gui
r = self.register
r('search_as_you_type', config)
r('highlight_search_matches', config)
r('limit_search_columns', prefs)
r('limit_search_columns_to', prefs, setting=CommaSeparatedList)
fl = gui.library_view.model().db.field_metadata.get_search_terms()
self.opt_limit_search_columns_to.update_items_cache(fl)
def refresh_gui(self, gui):
gui.search.search_as_you_type(config['search_as_you_type'])
gui.library_view.model().set_highlight_only(config['highlight_search_matches'])
gui.search.do_search()
if __name__ == '__main__':
app = QApplication([])
test_widget('Interface', 'Search')

View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>670</width>
<height>392</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="opt_search_as_you_type">
<property name="text">
<string>Search as you &amp;type</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="opt_highlight_search_matches">
<property name="text">
<string>&amp;Highlight search results instead of restricting the book list to the results</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>What to search by default</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>When you enter a search term without a prefix, by default calibre will search all metadata for matches. For example, entering, &quot;asimov&quot; will search not just authors but title/tags/series/comments/etc. Use these options if you would like to change this behavior.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="opt_limit_search_columns">
<property name="text">
<string>&amp;Limit the searched metadata</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Columns that non-prefixed searches are limited to:</string>
</property>
<property name="buddy">
<cstring>opt_limit_search_columns_to</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="MultiCompleteLineEdit" name="opt_limit_search_columns_to"/>
</item>
<item row="5" column="0" colspan="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Note that this option affects all searches, including saved searches and restrictions. Therefore, if you use this option, it is best to ensure that you always use prefixes in your saved searches. For example, use &quot;series:Foundation&quot; rather than just &quot;Foundation&quot; in a saved search</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<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>
<item row="4" column="0" colspan="2">
<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>
</layout>
</widget>
</item>
<item row="3" column="0">
<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>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>MultiCompleteLineEdit</class>
<extends>QLineEdit</extends>
<header>calibre/gui2.complete.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -16,7 +16,6 @@ from calibre.gui2 import config
from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor
from calibre.gui2.dialogs.search import SearchDialog from calibre.gui2.dialogs.search import SearchDialog
from calibre.utils.config import dynamic
from calibre.utils.search_query_parser import saved_searches from calibre.utils.search_query_parser import saved_searches
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
@ -376,9 +375,7 @@ class SearchBoxMixin(object): # {{{
unicode(self.search.toolTip()))) unicode(self.search.toolTip())))
self.advanced_search_button.setStatusTip(self.advanced_search_button.toolTip()) self.advanced_search_button.setStatusTip(self.advanced_search_button.toolTip())
self.clear_button.setStatusTip(self.clear_button.toolTip()) self.clear_button.setStatusTip(self.clear_button.toolTip())
self.search_highlight_only.stateChanged.connect(self.highlight_only_changed) self.search_options_button.clicked.connect(self.search_options_button_clicked)
self.search_highlight_only.setChecked(
dynamic.get('search_highlight_only', False))
def focus_search_box(self, *args): def focus_search_box(self, *args):
self.search.setFocus(Qt.OtherFocusReason) self.search.setFocus(Qt.OtherFocusReason)
@ -402,14 +399,13 @@ class SearchBoxMixin(object): # {{{
self.search.do_search() self.search.do_search()
self.focus_to_library() self.focus_to_library()
def search_options_button_clicked(self):
self.iactions['Preferences'].do_config(initial_plugin=('Interface',
'Search'), close_after_initial=True)
def focus_to_library(self): def focus_to_library(self):
self.current_view().setFocus(Qt.OtherFocusReason) 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()
# }}} # }}}
class SavedSearchBoxMixin(object): # {{{ class SavedSearchBoxMixin(object): # {{{

View File

@ -1214,7 +1214,7 @@ class TagBrowserMixin(object): # {{{
db.field_metadata.remove_user_categories() db.field_metadata.remove_user_categories()
for k in d.categories: for k in d.categories:
db.field_metadata.add_user_category('@' + k, k) 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.set_new_model()
self.tags_view.recount() self.tags_view.recount()

View File

@ -483,8 +483,10 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
action.location_selected(location) action.location_selected(location)
if location == 'library': if location == 'library':
self.search_restriction.setEnabled(True) self.search_restriction.setEnabled(True)
self.search_options_button.setEnabled(True)
else: else:
self.search_restriction.setEnabled(False) self.search_restriction.setEnabled(False)
self.search_options_button.setEnabled(False)
# Reset the view in case something changed while it was invisible # Reset the view in case something changed while it was invisible
self.current_view().reset() self.current_view().reset()
self.set_number_of_books_shown() self.set_number_of_books_shown()

View File

@ -11,7 +11,7 @@ from itertools import repeat
from datetime import timedelta from datetime import timedelta
from threading import Thread 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.date import parse_date, now, UNDEFINED_DATE
from calibre.utils.search_query_parser import SearchQueryParser from calibre.utils.search_query_parser import SearchQueryParser
from calibre.utils.pyparsing import ParseException from calibre.utils.pyparsing import ParseException
@ -182,15 +182,16 @@ class ResultCache(SearchQueryParser): # {{{
self.first_sort = True self.first_sort = True
self.search_restriction = '' self.search_restriction = ''
self.field_metadata = field_metadata self.field_metadata = field_metadata
all_search_locations = field_metadata.get_search_terms() self.all_search_locations = field_metadata.get_search_terms()
SearchQueryParser.__init__(self, all_search_locations, optimize=True) SearchQueryParser.__init__(self, self.all_search_locations, optimize=True)
self.build_date_relop_dict() self.build_date_relop_dict()
self.build_numeric_relop_dict() self.build_numeric_relop_dict()
def break_cycles(self): def break_cycles(self):
self._data = self.field_metadata = self.FIELD_MAP = \ self._data = self.field_metadata = self.FIELD_MAP = \
self.numeric_search_relops = self.date_search_relops = \ 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): def __getitem__(self, row):
@ -218,6 +219,10 @@ class ResultCache(SearchQueryParser): # {{{
def universal_set(self): def universal_set(self):
return set([i[0] for i in self._data if i is not None]) 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): def build_date_relop_dict(self):
''' '''
Because the database dates have time in them, we can't use direct 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 # get metadata key associated with the search term. Eliminates
# dealing with plurals and other aliases # dealing with plurals and other aliases
location = self.field_metadata.search_term_to_field_key(icu_lower(location.strip())) location = self.field_metadata.search_term_to_field_key(icu_lower(location.strip()))
# grouped search terms
if isinstance(location, list): if isinstance(location, list):
if allow_recursion: if allow_recursion:
for loc in location: for loc in location:
@ -440,6 +446,20 @@ class ResultCache(SearchQueryParser): # {{{
return matches return matches
raise ParseException(query, len(query), 'Recursive query group detected', self) raise ParseException(query, len(query), 'Recursive query group detected', self)
# apply the limit if appropriate
if location == 'all' and prefs['limit_search_columns'] and \
prefs['limit_search_columns_to']:
terms = set([])
for l in prefs['limit_search_columns_to']:
l = icu_lower(l.strip())
if l and l != 'all' and l in self.all_search_locations:
terms.add(l)
if terms:
for l in terms:
matches |= self.get_matches(l, query,
candidates=candidates, allow_recursion=allow_recursion)
return matches
if location in self.field_metadata: if location in self.field_metadata:
fm = self.field_metadata[location] fm = self.field_metadata[location]
# take care of dates special case # take care of dates special case

View File

@ -728,6 +728,17 @@ def _prefs():
c.add_opt('user_categories', default={}, help=_('User-created tag browser categories')) c.add_opt('user_categories', default={}, help=_('User-created tag browser categories'))
c.add_opt('manage_device_metadata', default='manual', c.add_opt('manage_device_metadata', default='manual',
help=_('How and when calibre updates metadata on the device.')) help=_('How and when calibre updates metadata on the device.'))
c.add_opt('limit_search_columns', default=False,
help=_('When searching for text without using lookup '
'prefixes, as for example, Red instead of title:Red, '
'limit the columns searched to those named below.'))
c.add_opt('limit_search_columns_to',
default=['title', 'authors', 'tags', 'series'],
help=_('Choose columns to be searched when not using prefixes, '
'as for example, when searching for Redd instead of '
'title:Red. Enter a list of search/lookup names '
'separated by commas. Only takes effect if you set the option '
'to limit search columns above.'))
c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.') c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.')
return c return c