diff --git a/resources/recipes/cinebel_be.recipe b/resources/recipes/cinebel_be.recipe
index ec76bfc894..024050eb67 100644
--- a/resources/recipes/cinebel_be.recipe
+++ b/resources/recipes/cinebel_be.recipe
@@ -1,7 +1,7 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
-__copyright__ = '2008, Lionel Bergeret '
+__copyright__ = '2008-2011, Lionel Bergeret '
'''
cinebel.be
'''
@@ -14,14 +14,14 @@ class Cinebel(BasicNewsRecipe):
description = u'Cinema news from Belgium in French'
publisher = u'cinebel.be'
category = 'news, cinema, movie, Belgium'
- oldest_article = 3
- encoding = 'utf8'
- language = 'fr_BE'
+ oldest_article = 15
+ language = 'fr'
max_articles_per_feed = 20
no_stylesheets = True
use_embedded_content = False
timefmt = ' [%d %b %Y]'
+ filterDuplicates = True
keep_only_tags = [
dict(name = 'span', attrs = {'class': 'movieMainTitle'})
@@ -35,6 +35,13 @@ class Cinebel(BasicNewsRecipe):
,(u'Top 10' , u'http://www.cinebel.be/Servlets/RssServlet?languageCode=fr&rssType=2' )
]
+ def preprocess_html(self, soup):
+ for alink in soup.findAll('a'):
+ if alink.has_key('href'):
+ tstr = "Site officiel: " + alink['href']
+ alink.replaceWith(tstr)
+ return soup
+
def get_cover_url(self):
cover_url = 'http://www.cinebel.be/portal/resources/common/logo_index.gif'
return cover_url
diff --git a/resources/recipes/dhnet_be.recipe b/resources/recipes/dhnet_be.recipe
index ef4d1736e3..d55470a765 100644
--- a/resources/recipes/dhnet_be.recipe
+++ b/resources/recipes/dhnet_be.recipe
@@ -1,7 +1,7 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
-__copyright__ = '2008, Lionel Bergeret '
+__copyright__ = '2008-2011, Lionel Bergeret '
'''
dhnet.be
'''
@@ -16,7 +16,8 @@ class DHNetBe(BasicNewsRecipe):
publisher = u'dhnet.be'
category = 'news, Belgium'
oldest_article = 3
- language = 'fr_BE'
+ language = 'fr'
+ masthead_url = 'http://www.dhnet.be/images/homepage_logo_dh.gif'
max_articles_per_feed = 20
no_stylesheets = True
@@ -34,6 +35,13 @@ class DHNetBe(BasicNewsRecipe):
,(u'La Une Info' , u'http://www.dhnet.be/rss/dhinfos/' )
]
+ def preprocess_html(self, soup):
+ for alink in soup.findAll('a'):
+ if alink.string is not None:
+ tstr = alink.string
+ alink.replaceWith(tstr)
+ return soup
+
def get_cover_url(self):
cover_url = strftime('http://pdf-online.dhnet.be/pdfonline/image/%Y%m%d/dh_%Y%m%d_nam_infoge_001.pdf.L.jpg')
return cover_url
diff --git a/resources/recipes/lalibre_be.recipe b/resources/recipes/lalibre_be.recipe
index 53e346bf12..a6356be828 100644
--- a/resources/recipes/lalibre_be.recipe
+++ b/resources/recipes/lalibre_be.recipe
@@ -1,7 +1,7 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
-__copyright__ = '2008, Lionel Bergeret '
+__copyright__ = '2008-2011, Lionel Bergeret '
'''
lalibre.be
'''
@@ -16,18 +16,18 @@ class LaLibre(BasicNewsRecipe):
publisher = u'lalibre.be'
category = 'news, Belgium'
oldest_article = 3
- language = 'fr_BE'
+ language = 'fr'
+ masthead_url = 'http://www.lalibre.be/img/logoLaLibre.gif'
max_articles_per_feed = 20
no_stylesheets = True
use_embedded_content = False
timefmt = ' [%d %b %Y]'
- keep_only_tags = [
- dict(name = 'div', attrs = {'id': 'articleHat'})
- ,dict(name = 'p', attrs = {'id': 'publicationDate'})
- ,dict(name = 'div', attrs = {'id': 'articleText'})
- ]
+ remove_tags_before = dict(name = 'div', attrs = {'class': 'extraMainContent'})
+ remove_tags_after = dict(name = 'div', attrs = {'id': 'articleText'})
+
+ remove_tags = [dict(name = 'div', attrs = {'id': 'strongArticleLinks'})]
feeds = [
(u'L\'actu' , u'http://www.lalibre.be/rss/?section=10' )
@@ -38,6 +38,13 @@ class LaLibre(BasicNewsRecipe):
,(u'Societe' , u'http://www.lalibre.be/rss/?section=12' )
]
+ def preprocess_html(self, soup):
+ for alink in soup.findAll('a'):
+ if alink.string is not None:
+ tstr = alink.string
+ alink.replaceWith(tstr)
+ return soup
+
def get_cover_url(self):
cover_url = strftime('http://pdf-online.lalibre.be/pdfonline/image/%Y%m%d/llb_%Y%m%d_nam_libre_001.pdf.L.jpg')
return cover_url
diff --git a/resources/recipes/lameuse_be.recipe b/resources/recipes/lameuse_be.recipe
index 03b7f84a5f..7166d01103 100644
--- a/resources/recipes/lameuse_be.recipe
+++ b/resources/recipes/lameuse_be.recipe
@@ -1,7 +1,7 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
-__copyright__ = '2008, Lionel Bergeret '
+__copyright__ = '2008-2011, Lionel Bergeret '
'''
lameuse.be
'''
@@ -16,8 +16,8 @@ class LaMeuse(BasicNewsRecipe):
publisher = u'lameuse.be'
category = 'news, Belgium'
oldest_article = 3
- encoding = 'utf8'
- language = 'fr_BE'
+ language = 'fr'
+ masthead_url = 'http://www.lameuse.be/images/SPV3/logo_header_LM.gif'
max_articles_per_feed = 20
no_stylesheets = True
@@ -32,6 +32,11 @@ class LaMeuse(BasicNewsRecipe):
dict(name = 'div', attrs = {'class': 'sb-group'})
,dict(name = 'div', attrs = {'id': 'share'})
,dict(name = 'div', attrs = {'id': 'commentaires'})
+ ,dict(name = 'ul', attrs = {'class': 'right liensutiles'})
+ ,dict(name = 'ul', attrs = {'class': 'bas liensutiles'})
+ ,dict(name = 'p', attrs = {'class': 'ariane'})
+ ,dict(name = 'div', attrs = {'class': 'inner-bloc'})
+ ,dict(name = 'div', attrs = {'class': 'block-01'})
]
feeds = [
diff --git a/resources/recipes/lavenir_be.recipe b/resources/recipes/lavenir_be.recipe
index 68be449ae5..4c2c8a00a2 100644
--- a/resources/recipes/lavenir_be.recipe
+++ b/resources/recipes/lavenir_be.recipe
@@ -1,7 +1,7 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
-__copyright__ = '2008, Lionel Bergeret '
+__copyright__ = '2008-2011, Lionel Bergeret '
'''
lavenir.net
'''
@@ -15,8 +15,7 @@ class LAvenir(BasicNewsRecipe):
publisher = u'lavenir.net'
category = 'news, Belgium'
oldest_article = 3
- encoding = 'utf8'
- language = 'fr_BE'
+ language = 'fr'
max_articles_per_feed = 20
no_stylesheets = True
@@ -35,6 +34,13 @@ class LAvenir(BasicNewsRecipe):
,(u'Societe' , u'http://www.lavenir.net/rss.aspx?foto=1&intro=1§ion=info&info=12e1a2f4-7e03-4cf1-afec-016869072317' )
]
+ def preprocess_html(self, soup):
+ for alink in soup.findAll('a'):
+ if alink.string is not None:
+ tstr = alink.string
+ alink.replaceWith(tstr)
+ return soup
+
def get_cover_url(self):
cover_url = 'http://www.lavenir.net/extra/Static/journal/Pdf/1/UNE_Nationale.PDF'
return cover_url
diff --git a/resources/recipes/lesoir_be.recipe b/resources/recipes/lesoir_be.recipe
index 6b6891c3b8..64fd2fa65c 100644
--- a/resources/recipes/lesoir_be.recipe
+++ b/resources/recipes/lesoir_be.recipe
@@ -1,7 +1,7 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
-__copyright__ = '2008, Lionel Bergeret '
+__copyright__ = '2008-2011, Lionel Bergeret '
'''
lesoir.be
'''
@@ -16,7 +16,8 @@ class LeSoirBe(BasicNewsRecipe):
publisher = u'lesoir.be'
category = 'news, Belgium'
oldest_article = 3
- language = 'fr_BE'
+ language = 'fr'
+ masthead_url = 'http://pdf.lesoir.be/pdf/images/SOIR//logo.gif'
max_articles_per_feed = 20
no_stylesheets = True
diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py
index 32c512fe39..bd8e07fb58 100644
--- a/src/calibre/customize/builtins.py
+++ b/src/calibre/customize/builtins.py
@@ -791,6 +791,17 @@ class Toolbar(PreferencesPlugin):
description = _('Customize the toolbars and context menus, changing which'
' 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):
name = 'Input Options'
icon = I('arrow-down.png')
@@ -941,7 +952,7 @@ class Misc(PreferencesPlugin):
config_widget = 'calibre.gui2.preferences.misc'
description = _('Miscellaneous advanced configuration')
-plugins += [LookAndFeel, Behavior, Columns, Toolbar, InputOptions,
+plugins += [LookAndFeel, Behavior, Columns, Toolbar, Search, InputOptions,
CommonOptions, OutputOptions, Adding, Saving, Sending, Plugboard,
Email, Server, Plugins, Tweaks, Misc, TemplateFunctions]
diff --git a/src/calibre/ebooks/conversion/utils.py b/src/calibre/ebooks/conversion/utils.py
index d075390e8e..c0612a7b9c 100644
--- a/src/calibre/ebooks/conversion/utils.py
+++ b/src/calibre/ebooks/conversion/utils.py
@@ -11,6 +11,7 @@ from calibre.ebooks.conversion.preprocess import DocAnalysis, Dehyphenator
from calibre.utils.logging import default_log
from calibre.utils.wordcount import get_wordcount_obj
+
class HeuristicProcessor(object):
def __init__(self, extra_opts=None, log=None):
@@ -40,6 +41,9 @@ class HeuristicProcessor(object):
def is_pdftohtml(self, src):
return '' in src[:1000]
+ def is_abbyy(self, src):
+ return '[^\"]*?);?">)(?P.*?)(?P
)|(?P
]*>))', re.IGNORECASE)
+ empty_paragraph = '\n
\n'
+ self.in_blockquote = False
+ self.previous_was_paragraph = False
+ html = re.sub('?a[^>]*>', '', html)
+
+ def check_paragraph(content):
+ content = re.sub('\s*?span[^>]*>\s*', '', content)
+ if re.match('.*[\"\'.!?:]$', content):
+ #print "detected this as a paragraph"
+ return True
+ else:
+ return False
+
+ def convert_styles(match):
+ #print "raw styles are: "+match.group('styles')
+ content = match.group('content')
+ #print "raw content is: "+match.group('content')
+ image = match.group('image')
+
+ is_paragraph = False
+ text_align = ''
+ text_indent = ''
+ paragraph_before = ''
+ paragraph_after = ''
+ blockquote_open = '\n\n'
+ blockquote_close = '
\n'
+ indented_text = 'text-indent:3%;'
+ blockquote_open_loop = ''
+ blockquote_close_loop = ''
+ debugabby = False
+
+ if image:
+ debugabby = True
+ if self.in_blockquote:
+ self.in_blockquote = False
+ blockquote_close_loop = blockquote_close
+ self.previous_was_paragraph = False
+ return blockquote_close_loop+'\n'+image+'\n'
+ else:
+ styles = match.group('styles').split(';')
+ is_paragraph = check_paragraph(content)
+ #print "styles for this line are: "+str(styles)
+ split_styles = []
+ for style in styles:
+ #print "style is: "+str(style)
+ newstyle = style.split(':')
+ #print "newstyle is: "+str(newstyle)
+ split_styles.append(newstyle)
+ styles = split_styles
+ for style, setting in styles:
+ if style == 'text-align' and setting != 'left':
+ text_align = style+':'+setting+';'
+ if style == 'text-indent':
+ setting = int(re.sub('\s*pt\s*', '', setting))
+ if 9 < setting < 14:
+ text_indent = indented_text
+ else:
+ text_indent = style+':'+str(setting)+'pt;'
+ if style == 'padding':
+ setting = re.sub('pt', '', setting).split(' ')
+ if int(setting[1]) < 16 and int(setting[3]) < 16:
+ if self.in_blockquote:
+ debugabby = True
+ if is_paragraph:
+ self.in_blockquote = False
+ blockquote_close_loop = blockquote_close
+ if int(setting[3]) > 8 and text_indent == '':
+ text_indent = indented_text
+ if int(setting[0]) > 5:
+ paragraph_before = empty_paragraph
+ if int(setting[2]) > 5:
+ paragraph_after = empty_paragraph
+ elif not self.in_blockquote and self.previous_was_paragraph:
+ debugabby = True
+ self.in_blockquote = True
+ blockquote_open_loop = blockquote_open
+ if debugabby:
+ self.log.debug('\n\n******\n')
+ self.log.debug('padding top is: '+str(setting[0]))
+ self.log.debug('padding right is:'
+ +str(setting[1]))
+ self.log.debug('padding bottom is: ' +
+ str(setting[2]))
+ self.log.debug('padding left is: '
+ +str(setting[3]))
+
+ #print "text-align is: "+str(text_align)
+ #print "\n***\nline is:\n "+str(match.group(0))+'\n'
+ if debugabby:
+ #print "this line is a paragraph = "+str(is_paragraph)+", previous line was "+str(self.previous_was_paragraph)
+ self.log.debug("styles for this line were:", styles)
+ self.log.debug('newline is:')
+ self.log.debug(blockquote_open_loop+blockquote_close_loop+
+ paragraph_before+''+content+'
'+paragraph_after+'\n\n\n\n\n')
+ #print "is_paragraph is "+str(is_paragraph)+", previous_was_paragraph is "+str(self.previous_was_paragraph)
+ self.previous_was_paragraph = is_paragraph
+ #print "previous_was_paragraph is now set to "+str(self.previous_was_paragraph)+"\n\n\n"
+ return blockquote_open_loop+blockquote_close_loop+paragraph_before+''+content+'
'+paragraph_after
+
+ html = abbyy_line.sub(convert_styles, html)
+ return html
+
def __call__(self, html):
self.log.debug("********* Heuristic processing HTML *********")
@@ -532,6 +641,10 @@ class HeuristicProcessor(object):
self.log.warn("flow is too short, not running heuristics")
return html
+ is_abbyy = self.is_abbyy(html)
+ if is_abbyy:
+ html = self.abbyy_processor(html)
+
# Arrange line feeds and tags so the line_length and no_markup functions work correctly
html = self.arrange_htm_line_endings(html)
#self.dump(html, 'after_arrange_line_endings')
diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py
index 92a68fa840..b33166dd33 100644
--- a/src/calibre/gui2/__init__.py
+++ b/src/calibre/gui2/__init__.py
@@ -106,9 +106,13 @@ def _config():
'clicked'))
c.add_opt('asked_library_thing_password', default=False,
help='Asked library thing password at least once.')
- c.add_opt('search_as_you_type', default=True,
- help='Start searching as you type. If this is disabled then search will '
- 'only take place when the Enter or Return key is pressed.')
+ c.add_opt('search_as_you_type', default=False,
+ help=_('Start searching as you type. If this is disabled then search will '
+ '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=[],
help='Previously used Save to Disk templates')
c.add_opt('send_to_device_template_history', default=[],
diff --git a/src/calibre/gui2/actions/next_match.py b/src/calibre/gui2/actions/next_match.py
index b88aa0dd59..1c74719674 100644
--- a/src/calibre/gui2/actions/next_match.py
+++ b/src/calibre/gui2/actions/next_match.py
@@ -28,21 +28,12 @@ class NextMatchAction(InterfaceAction):
self.gui.addAction(self.p_action)
self.p_action.triggered.connect(self.move_backward)
- def gui_layout_complete(self):
- self.gui.search_options_button.setVisible(True)
-
def location_selected(self, loc):
self.can_move = loc == 'library'
- try:
- self.gui.search_options_button.setVisible(self.can_move)
- except:
- import traceback
- traceback.print_exc()
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_options_button.setVisible(self.can_move)
if self.can_move:
self.gui.current_view().move_highlighted_row(forward=True)
@@ -50,7 +41,6 @@ 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_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/actions/preferences.py b/src/calibre/gui2/actions/preferences.py
index f1fe06c43d..ee52f06aac 100644
--- a/src/calibre/gui2/actions/preferences.py
+++ b/src/calibre/gui2/actions/preferences.py
@@ -33,7 +33,8 @@ class PreferencesAction(InterfaceAction):
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():
d = error_dialog(self.gui, _('Cannot configure'),
_('Cannot configure while there are running jobs.'))
@@ -44,7 +45,8 @@ class PreferencesAction(InterfaceAction):
_('Cannot configure before calibre is restarted.'))
d.exec_()
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.run_wizard_requested.connect(self.gui.run_wizard,
type=Qt.QueuedConnection)
diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py
index bfa009b2da..0ca58582b6 100644
--- a/src/calibre/gui2/init.py
+++ b/src/calibre/gui2/init.py
@@ -19,7 +19,6 @@ 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 = []
@@ -65,8 +64,7 @@ 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)
+ self.library_view.model().set_highlight_only(config['highlight_search_matches'])
def build_context_menus(self):
lm = QMenu(self)
diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py
index 6ad5551de7..e8a4e79384 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, QDialogButtonBox, \
- pyqtSignal, QToolButton, QMenu, QCheckBox, QDialog, QGridLayout, QFrame, \
+from PyQt4.Qt import QIcon, Qt, QWidget, QToolBar, QSize, \
+ pyqtSignal, QToolButton, QMenu, \
QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup
@@ -17,7 +17,6 @@ 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
class LocationManager(QObject): # {{{
@@ -201,8 +200,7 @@ class SearchBar(QWidget): # {{{
x.setIcon(QIcon(I('config.png')))
x.setObjectName("search_option_button")
l.addWidget(x)
- x.setToolTip(_("Change search highlighting and column limit options"))
- x.setVisible(False)
+ x.setToolTip(_("Change the way searching for books works"))
x = parent.saved_search = SavedSearchBox(self)
x.setMaximumSize(QSize(150, 16777215))
@@ -229,90 +227,6 @@ 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.setWindowTitle(_('Search options'))
- l = QGridLayout()
- self.setLayout(l)
-
- 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.')+'
'+_('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)
-
- 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, 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 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 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 columns/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 &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, 2)
- 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()))
-
# }}}
class Spacer(QWidget): # {{{
diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py
index 37ed90cc61..196ef16b08 100644
--- a/src/calibre/gui2/preferences/look_feel.py
+++ b/src/calibre/gui2/preferences/look_feel.py
@@ -46,7 +46,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('disable_tray_notification', config)
r('use_roman_numerals_for_series_number', config)
r('separate_cover_flow', config, restart_required=True)
- r('search_as_you_type', config)
r('show_child_bar', gprefs)
choices = [(_('Small'), 'small'), (_('Medium'), 'medium'),
@@ -116,7 +115,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
def refresh_gui(self, gui):
- gui.search.search_as_you_type(config['search_as_you_type'])
self.update_font_display()
gui.tags_view.reread_collapse_parameters()
diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui
index 2223167068..3f2bb3e145 100644
--- a/src/calibre/gui2/preferences/look_feel.ui
+++ b/src/calibre/gui2/preferences/look_feel.ui
@@ -124,23 +124,13 @@
- -
+
-
Show cover &browser in a separate window (needs restart)
- -
-
-
- Search as you type
-
-
- true
-
-
-
-
-
@@ -177,7 +167,7 @@ if you never want subcategories
-
- If a Tag Browser category has more than this number of items, it is divided
+ If a Tag Browser category has more than this number of items, it is divided
up into sub-categories. If the partition method is set to disable, this value is ignored.
diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py
index f7d49427c8..f25cc85dce 100644
--- a/src/calibre/gui2/preferences/main.py
+++ b/src/calibre/gui2/preferences/main.py
@@ -157,11 +157,12 @@ class Preferences(QMainWindow):
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)
self.gui = gui
self.must_restart = False
self.committed = False
+ self.close_after_initial = close_after_initial
self.resize(900, 720)
nh, nw = min_available_height()-25, available_width()-10
@@ -306,7 +307,7 @@ class Preferences(QMainWindow):
def esc(self, *args):
if self.stack.currentIndex() == 1:
- self.hide_plugin()
+ self.cancel()
elif self.stack.currentIndex() == 0:
self.close()
@@ -331,12 +332,15 @@ class Preferences(QMainWindow):
show_copy_button=False)
self.showing_widget.refresh_gui(self.gui)
self.hide_plugin()
- if must_restart and rc:
+ if self.close_after_initial or (must_restart and rc):
self.close()
def cancel(self, *args):
- self.hide_plugin()
+ if self.close_after_initial:
+ self.close()
+ else:
+ self.hide_plugin()
def restore_defaults(self, *args):
self.showing_widget.restore_defaults()
diff --git a/src/calibre/gui2/preferences/search.py b/src/calibre/gui2/preferences/search.py
new file mode 100644
index 0000000000..81bc603df4
--- /dev/null
+++ b/src/calibre/gui2/preferences/search.py
@@ -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 '
+__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')
+
diff --git a/src/calibre/gui2/preferences/search.ui b/src/calibre/gui2/preferences/search.ui
new file mode 100644
index 0000000000..69426a3728
--- /dev/null
+++ b/src/calibre/gui2/preferences/search.ui
@@ -0,0 +1,130 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 670
+ 392
+
+
+
+ Form
+
+
+
-
+
+
+ Search as you &type
+
+
+
+ -
+
+
+ &Highlight search results instead of restricting the book list to the results
+
+
+
+ -
+
+
+ What to search by default
+
+
+
-
+
+
+ When you enter a search term without a prefix, by default calibre will search all metadata for matches. For example, entering, "asimov" will search not just authors but title/tags/series/comments/etc. Use these options if you would like to change this behavior.
+
+
+ true
+
+
+
+ -
+
+
+ &Limit the searched metadata
+
+
+
+ -
+
+
+ &Columns that non-prefixed searches are limited to:
+
+
+ opt_limit_search_columns_to
+
+
+
+ -
+
+
+ -
+
+
+ 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 "series:Foundation" rather than just "Foundation" in a saved search
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ MultiCompleteLineEdit
+ QLineEdit
+
+
+
+
+
+
diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py
index b9344a7782..900c882adc 100644
--- a/src/calibre/gui2/search_box.py
+++ b/src/calibre/gui2/search_box.py
@@ -16,7 +16,6 @@ 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, prefs
from calibre.utils.search_query_parser import saved_searches
from calibre.utils.icu import sort_key
@@ -401,24 +400,8 @@ 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]
- 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_cbox, highlight_cbox = options_box.values()
- prefs['search_box_limit_to'] = limit_list
- 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.iactions['Preferences'].do_config(initial_plugin=('Interface',
+ 'Search'), close_after_initial=True)
def focus_to_library(self):
self.current_view().setFocus(Qt.OtherFocusReason)
diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py
index 907dd577b8..5ac7e6a45d 100644
--- a/src/calibre/gui2/ui.py
+++ b/src/calibre/gui2/ui.py
@@ -483,8 +483,10 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
action.location_selected(location)
if location == 'library':
self.search_restriction.setEnabled(True)
+ self.search_options_button.setEnabled(True)
else:
self.search_restriction.setEnabled(False)
+ self.search_options_button.setEnabled(False)
# Reset the view in case something changed while it was invisible
self.current_view().reset()
self.set_number_of_books_shown()
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index fb68a0164a..1330d10e59 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -447,18 +447,18 @@ class ResultCache(SearchQueryParser): # {{{
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(','):
+ 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 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 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:
fm = self.field_metadata[location]
diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py
index 0ccc949260..a2ceaced68 100644
--- a/src/calibre/utils/config.py
+++ b/src/calibre/utils/config.py
@@ -728,11 +728,17 @@ def _prefs():
c.add_opt('user_categories', default={}, help=_('User-created tag browser categories'))
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='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'))
+ 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', 'publisher'],
+ 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.')
return c