mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
5cb9505ee1
@ -32,9 +32,10 @@ series_index_auto_increment = 'next'
|
|||||||
|
|
||||||
# Should the completion separator be append
|
# Should the completion separator be append
|
||||||
# to the end of the completed text to
|
# to the end of the completed text to
|
||||||
# automatically begin a new completion operation.
|
# automatically begin a new completion operation
|
||||||
|
# for authors.
|
||||||
# Can be either True or False
|
# Can be either True or False
|
||||||
completer_append_separator = False
|
authors_completer_append_separator = False
|
||||||
|
|
||||||
|
|
||||||
# The algorithm used to copy author to author_sort
|
# The algorithm used to copy author to author_sort
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Lionel Bergeret <lbergeret at gmail.com>'
|
__copyright__ = '2008-2011, Lionel Bergeret <lbergeret at gmail.com>'
|
||||||
'''
|
'''
|
||||||
cinebel.be
|
cinebel.be
|
||||||
'''
|
'''
|
||||||
@ -14,14 +14,14 @@ class Cinebel(BasicNewsRecipe):
|
|||||||
description = u'Cinema news from Belgium in French'
|
description = u'Cinema news from Belgium in French'
|
||||||
publisher = u'cinebel.be'
|
publisher = u'cinebel.be'
|
||||||
category = 'news, cinema, movie, Belgium'
|
category = 'news, cinema, movie, Belgium'
|
||||||
oldest_article = 3
|
oldest_article = 15
|
||||||
encoding = 'utf8'
|
language = 'fr'
|
||||||
language = 'fr_BE'
|
|
||||||
|
|
||||||
max_articles_per_feed = 20
|
max_articles_per_feed = 20
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
timefmt = ' [%d %b %Y]'
|
timefmt = ' [%d %b %Y]'
|
||||||
|
filterDuplicates = True
|
||||||
|
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
dict(name = 'span', attrs = {'class': 'movieMainTitle'})
|
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' )
|
,(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):
|
def get_cover_url(self):
|
||||||
cover_url = 'http://www.cinebel.be/portal/resources/common/logo_index.gif'
|
cover_url = 'http://www.cinebel.be/portal/resources/common/logo_index.gif'
|
||||||
return cover_url
|
return cover_url
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Lionel Bergeret <lbergeret at gmail.com>'
|
__copyright__ = '2008-2011, Lionel Bergeret <lbergeret at gmail.com>'
|
||||||
'''
|
'''
|
||||||
dhnet.be
|
dhnet.be
|
||||||
'''
|
'''
|
||||||
@ -16,7 +16,8 @@ class DHNetBe(BasicNewsRecipe):
|
|||||||
publisher = u'dhnet.be'
|
publisher = u'dhnet.be'
|
||||||
category = 'news, Belgium'
|
category = 'news, Belgium'
|
||||||
oldest_article = 3
|
oldest_article = 3
|
||||||
language = 'fr_BE'
|
language = 'fr'
|
||||||
|
masthead_url = 'http://www.dhnet.be/images/homepage_logo_dh.gif'
|
||||||
|
|
||||||
max_articles_per_feed = 20
|
max_articles_per_feed = 20
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
@ -34,6 +35,13 @@ class DHNetBe(BasicNewsRecipe):
|
|||||||
,(u'La Une Info' , u'http://www.dhnet.be/rss/dhinfos/' )
|
,(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):
|
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')
|
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
|
return cover_url
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Lionel Bergeret <lbergeret at gmail.com>'
|
__copyright__ = '2008-2011, Lionel Bergeret <lbergeret at gmail.com>'
|
||||||
'''
|
'''
|
||||||
lalibre.be
|
lalibre.be
|
||||||
'''
|
'''
|
||||||
@ -16,18 +16,18 @@ class LaLibre(BasicNewsRecipe):
|
|||||||
publisher = u'lalibre.be'
|
publisher = u'lalibre.be'
|
||||||
category = 'news, Belgium'
|
category = 'news, Belgium'
|
||||||
oldest_article = 3
|
oldest_article = 3
|
||||||
language = 'fr_BE'
|
language = 'fr'
|
||||||
|
masthead_url = 'http://www.lalibre.be/img/logoLaLibre.gif'
|
||||||
|
|
||||||
max_articles_per_feed = 20
|
max_articles_per_feed = 20
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
timefmt = ' [%d %b %Y]'
|
timefmt = ' [%d %b %Y]'
|
||||||
|
|
||||||
keep_only_tags = [
|
remove_tags_before = dict(name = 'div', attrs = {'class': 'extraMainContent'})
|
||||||
dict(name = 'div', attrs = {'id': 'articleHat'})
|
remove_tags_after = dict(name = 'div', attrs = {'id': 'articleText'})
|
||||||
,dict(name = 'p', attrs = {'id': 'publicationDate'})
|
|
||||||
,dict(name = 'div', attrs = {'id': 'articleText'})
|
remove_tags = [dict(name = 'div', attrs = {'id': 'strongArticleLinks'})]
|
||||||
]
|
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'L\'actu' , u'http://www.lalibre.be/rss/?section=10' )
|
(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' )
|
,(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):
|
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')
|
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
|
return cover_url
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Lionel Bergeret <lbergeret at gmail.com>'
|
__copyright__ = '2008-2011, Lionel Bergeret <lbergeret at gmail.com>'
|
||||||
'''
|
'''
|
||||||
lameuse.be
|
lameuse.be
|
||||||
'''
|
'''
|
||||||
@ -16,8 +16,8 @@ class LaMeuse(BasicNewsRecipe):
|
|||||||
publisher = u'lameuse.be'
|
publisher = u'lameuse.be'
|
||||||
category = 'news, Belgium'
|
category = 'news, Belgium'
|
||||||
oldest_article = 3
|
oldest_article = 3
|
||||||
encoding = 'utf8'
|
language = 'fr'
|
||||||
language = 'fr_BE'
|
masthead_url = 'http://www.lameuse.be/images/SPV3/logo_header_LM.gif'
|
||||||
|
|
||||||
max_articles_per_feed = 20
|
max_articles_per_feed = 20
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
@ -32,6 +32,11 @@ class LaMeuse(BasicNewsRecipe):
|
|||||||
dict(name = 'div', attrs = {'class': 'sb-group'})
|
dict(name = 'div', attrs = {'class': 'sb-group'})
|
||||||
,dict(name = 'div', attrs = {'id': 'share'})
|
,dict(name = 'div', attrs = {'id': 'share'})
|
||||||
,dict(name = 'div', attrs = {'id': 'commentaires'})
|
,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 = [
|
feeds = [
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Lionel Bergeret <lbergeret at gmail.com>'
|
__copyright__ = '2008-2011, Lionel Bergeret <lbergeret at gmail.com>'
|
||||||
'''
|
'''
|
||||||
lavenir.net
|
lavenir.net
|
||||||
'''
|
'''
|
||||||
@ -15,8 +15,7 @@ class LAvenir(BasicNewsRecipe):
|
|||||||
publisher = u'lavenir.net'
|
publisher = u'lavenir.net'
|
||||||
category = 'news, Belgium'
|
category = 'news, Belgium'
|
||||||
oldest_article = 3
|
oldest_article = 3
|
||||||
encoding = 'utf8'
|
language = 'fr'
|
||||||
language = 'fr_BE'
|
|
||||||
|
|
||||||
max_articles_per_feed = 20
|
max_articles_per_feed = 20
|
||||||
no_stylesheets = True
|
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' )
|
,(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):
|
def get_cover_url(self):
|
||||||
cover_url = 'http://www.lavenir.net/extra/Static/journal/Pdf/1/UNE_Nationale.PDF'
|
cover_url = 'http://www.lavenir.net/extra/Static/journal/Pdf/1/UNE_Nationale.PDF'
|
||||||
return cover_url
|
return cover_url
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Lionel Bergeret <lbergeret at gmail.com>'
|
__copyright__ = '2008-2011, Lionel Bergeret <lbergeret at gmail.com>'
|
||||||
'''
|
'''
|
||||||
lesoir.be
|
lesoir.be
|
||||||
'''
|
'''
|
||||||
@ -16,7 +16,8 @@ class LeSoirBe(BasicNewsRecipe):
|
|||||||
publisher = u'lesoir.be'
|
publisher = u'lesoir.be'
|
||||||
category = 'news, Belgium'
|
category = 'news, Belgium'
|
||||||
oldest_article = 3
|
oldest_article = 3
|
||||||
language = 'fr_BE'
|
language = 'fr'
|
||||||
|
masthead_url = 'http://pdf.lesoir.be/pdf/images/SOIR//logo.gif'
|
||||||
|
|
||||||
max_articles_per_feed = 20
|
max_articles_per_feed = 20
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
@ -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]
|
||||||
|
|
||||||
|
@ -62,6 +62,9 @@ class ANDROID(USBMS):
|
|||||||
# Archos
|
# Archos
|
||||||
0x0e79 : { 0x1419: [0x0216], 0x1420 : [0x0216], 0x1422 : [0x0216]},
|
0x0e79 : { 0x1419: [0x0216], 0x1420 : [0x0216], 0x1422 : [0x0216]},
|
||||||
|
|
||||||
|
# Huawei
|
||||||
|
0x45e : { 0x00e1 : [0x007], },
|
||||||
|
|
||||||
}
|
}
|
||||||
EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books']
|
EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books']
|
||||||
EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to '
|
EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to '
|
||||||
@ -71,12 +74,13 @@ class ANDROID(USBMS):
|
|||||||
|
|
||||||
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER',
|
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER',
|
||||||
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE', 'ARCHOS',
|
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE', 'ARCHOS',
|
||||||
'TELECHIP']
|
'TELECHIP', 'HUAWEI', ]
|
||||||
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
||||||
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID',
|
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID',
|
||||||
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE',
|
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE',
|
||||||
'SGH-T849', '_MB300', 'A70S', 'S_ANDROID', 'A101IT', 'A70H']
|
'SGH-T849', '_MB300', 'A70S', 'S_ANDROID', 'A101IT', 'A70H',
|
||||||
|
'IDEOS_TABLET']
|
||||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||||
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||||
'A70S', 'A101IT']
|
'A70S', 'A101IT']
|
||||||
|
@ -11,6 +11,7 @@ from calibre.ebooks.conversion.preprocess import DocAnalysis, Dehyphenator
|
|||||||
from calibre.utils.logging import default_log
|
from calibre.utils.logging import default_log
|
||||||
from calibre.utils.wordcount import get_wordcount_obj
|
from calibre.utils.wordcount import get_wordcount_obj
|
||||||
|
|
||||||
|
|
||||||
class HeuristicProcessor(object):
|
class HeuristicProcessor(object):
|
||||||
|
|
||||||
def __init__(self, extra_opts=None, log=None):
|
def __init__(self, extra_opts=None, log=None):
|
||||||
@ -40,6 +41,9 @@ class HeuristicProcessor(object):
|
|||||||
def is_pdftohtml(self, src):
|
def is_pdftohtml(self, src):
|
||||||
return '<!-- created by calibre\'s pdftohtml -->' in src[:1000]
|
return '<!-- created by calibre\'s pdftohtml -->' in src[:1000]
|
||||||
|
|
||||||
|
def is_abbyy(self, src):
|
||||||
|
return '<meta name="generator" content="ABBYY FineReader' in src[:1000]
|
||||||
|
|
||||||
def chapter_head(self, match):
|
def chapter_head(self, match):
|
||||||
from calibre.utils.html2text import html2text
|
from calibre.utils.html2text import html2text
|
||||||
chap = match.group('chap')
|
chap = match.group('chap')
|
||||||
@ -77,22 +81,23 @@ class HeuristicProcessor(object):
|
|||||||
|
|
||||||
def insert_indent(self, match):
|
def insert_indent(self, match):
|
||||||
pstyle = match.group('formatting')
|
pstyle = match.group('formatting')
|
||||||
|
tag = match.group('tagtype')
|
||||||
span = match.group('span')
|
span = match.group('span')
|
||||||
self.found_indents = self.found_indents + 1
|
self.found_indents = self.found_indents + 1
|
||||||
if pstyle:
|
if pstyle:
|
||||||
if pstyle.lower().find('style'):
|
if pstyle.lower().find('style') != -1:
|
||||||
pstyle = re.sub(r'"$', '; text-indent:3%"', pstyle)
|
pstyle = re.sub(r'"$', '; text-indent:3%"', pstyle)
|
||||||
else:
|
else:
|
||||||
pstyle = pstyle+' style="text-indent:3%"'
|
pstyle = pstyle+' style="text-indent:3%"'
|
||||||
if not span:
|
if not span:
|
||||||
return '<p '+pstyle+'>'
|
return '<'+tag+' '+pstyle+'>'
|
||||||
else:
|
else:
|
||||||
return '<p '+pstyle+'>'+span
|
return '<'+tag+' '+pstyle+'>'+span
|
||||||
else:
|
else:
|
||||||
if not span:
|
if not span:
|
||||||
return '<p style="text-indent:3%">'
|
return '<'+tag+' style="text-indent:3%">'
|
||||||
else:
|
else:
|
||||||
return '<p style="text-indent:3%">'+span
|
return '<'+tag+' style="text-indent:3%">'+span
|
||||||
|
|
||||||
def no_markup(self, raw, percent):
|
def no_markup(self, raw, percent):
|
||||||
'''
|
'''
|
||||||
@ -365,7 +370,7 @@ class HeuristicProcessor(object):
|
|||||||
return html
|
return html
|
||||||
|
|
||||||
def fix_nbsp_indents(self, html):
|
def fix_nbsp_indents(self, html):
|
||||||
txtindent = re.compile(ur'<p(?P<formatting>[^>]*)>\s*(?P<span>(<span[^>]*>\s*)+)?\s*(\u00a0){2,}', re.IGNORECASE)
|
txtindent = re.compile(ur'<(?P<tagtype>p|div)(?P<formatting>[^>]*)>\s*(?P<span>(<span[^>]*>\s*)+)?\s*(\u00a0){2,}', re.IGNORECASE)
|
||||||
html = txtindent.sub(self.insert_indent, html)
|
html = txtindent.sub(self.insert_indent, html)
|
||||||
if self.found_indents > 1:
|
if self.found_indents > 1:
|
||||||
self.log.debug("replaced "+unicode(self.found_indents)+ " nbsp indents with inline styles")
|
self.log.debug("replaced "+unicode(self.found_indents)+ " nbsp indents with inline styles")
|
||||||
@ -518,6 +523,111 @@ class HeuristicProcessor(object):
|
|||||||
|
|
||||||
return scene_break
|
return scene_break
|
||||||
|
|
||||||
|
def abbyy_processor(self, html):
|
||||||
|
abbyy_line = re.compile('((?P<linestart><p\sstyle="(?P<styles>[^\"]*?);?">)(?P<content>.*?)(?P<lineend></p>)|(?P<image><img[^>]*>))', re.IGNORECASE)
|
||||||
|
empty_paragraph = '\n<p> </p>\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<blockquote>\n'
|
||||||
|
blockquote_close = '</blockquote>\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+'<p style="'+text_indent+text_align+
|
||||||
|
'">'+content+'</p>'+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+'<p style="'+text_indent+text_align+'">'+content+'</p>'+paragraph_after
|
||||||
|
|
||||||
|
html = abbyy_line.sub(convert_styles, html)
|
||||||
|
return html
|
||||||
|
|
||||||
|
|
||||||
def __call__(self, html):
|
def __call__(self, html):
|
||||||
self.log.debug("********* Heuristic processing HTML *********")
|
self.log.debug("********* Heuristic processing HTML *********")
|
||||||
@ -532,6 +642,10 @@ class HeuristicProcessor(object):
|
|||||||
self.log.warn("flow is too short, not running heuristics")
|
self.log.warn("flow is too short, not running heuristics")
|
||||||
return html
|
return html
|
||||||
|
|
||||||
|
is_abbyy = self.is_abbyy(html)
|
||||||
|
if is_abbyy:
|
||||||
|
html = self.abbyy_processor(html)
|
||||||
|
|
||||||
# Arrange line feeds and </p> tags so the line_length and no_markup functions work correctly
|
# Arrange line feeds and </p> tags so the line_length and no_markup functions work correctly
|
||||||
html = self.arrange_htm_line_endings(html)
|
html = self.arrange_htm_line_endings(html)
|
||||||
#self.dump(html, 'after_arrange_line_endings')
|
#self.dump(html, 'after_arrange_line_endings')
|
||||||
|
@ -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=[],
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -9,7 +9,6 @@ __docformat__ = 'restructuredtext en'
|
|||||||
from PyQt4.Qt import QLineEdit, QAbstractListModel, Qt, \
|
from PyQt4.Qt import QLineEdit, QAbstractListModel, Qt, \
|
||||||
QApplication, QCompleter
|
QApplication, QCompleter
|
||||||
|
|
||||||
from calibre.utils.config import tweaks
|
|
||||||
from calibre.utils.icu import sort_key, lower
|
from calibre.utils.icu import sort_key, lower
|
||||||
from calibre.gui2 import NONE
|
from calibre.gui2 import NONE
|
||||||
from calibre.gui2.widgets import EnComboBox
|
from calibre.gui2.widgets import EnComboBox
|
||||||
@ -55,6 +54,8 @@ class MultiCompleteLineEdit(QLineEdit):
|
|||||||
|
|
||||||
self.sep = ','
|
self.sep = ','
|
||||||
self.space_before_sep = False
|
self.space_before_sep = False
|
||||||
|
self.add_separator = True
|
||||||
|
self.original_cursor_pos = None
|
||||||
|
|
||||||
self._model = CompleteModel(parent=self)
|
self._model = CompleteModel(parent=self)
|
||||||
self._completer = c = QCompleter(self._model, self)
|
self._completer = c = QCompleter(self._model, self)
|
||||||
@ -82,6 +83,9 @@ class MultiCompleteLineEdit(QLineEdit):
|
|||||||
def set_space_before_sep(self, space_before):
|
def set_space_before_sep(self, space_before):
|
||||||
self.space_before_sep = space_before
|
self.space_before_sep = space_before
|
||||||
|
|
||||||
|
def set_add_separator(self, what):
|
||||||
|
self.add_separator = bool(what)
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def item_entered(self, idx):
|
def item_entered(self, idx):
|
||||||
@ -93,7 +97,7 @@ class MultiCompleteLineEdit(QLineEdit):
|
|||||||
|
|
||||||
def update_completions(self):
|
def update_completions(self):
|
||||||
' Update the list of completions '
|
' Update the list of completions '
|
||||||
cpos = self.cursorPosition()
|
self.original_cursor_pos = cpos = self.cursorPosition()
|
||||||
text = unicode(self.text())
|
text = unicode(self.text())
|
||||||
prefix = text[:cpos]
|
prefix = text[:cpos]
|
||||||
self.current_prefix = prefix
|
self.current_prefix = prefix
|
||||||
@ -103,38 +107,38 @@ class MultiCompleteLineEdit(QLineEdit):
|
|||||||
self._completer.setCompletionPrefix(complete_prefix)
|
self._completer.setCompletionPrefix(complete_prefix)
|
||||||
|
|
||||||
def get_completed_text(self, text):
|
def get_completed_text(self, text):
|
||||||
'''
|
'Get completed text in before and after parts'
|
||||||
Get completed text from current cursor position and the completion
|
|
||||||
text
|
|
||||||
'''
|
|
||||||
if self.sep is None:
|
if self.sep is None:
|
||||||
return -1, text
|
return text, ''
|
||||||
else:
|
else:
|
||||||
cursor_pos = self.cursorPosition()
|
cursor_pos = self.original_cursor_pos
|
||||||
before_text = unicode(self.text())[:cursor_pos]
|
if cursor_pos is None:
|
||||||
after_text = unicode(self.text())[cursor_pos:]
|
cursor_pos = self.cursorPosition()
|
||||||
prefix_len = len(before_text.split(self.sep)[-1].lstrip())
|
self.original_cursor_pos = None
|
||||||
if tweaks['completer_append_separator']:
|
# Split text
|
||||||
prefix_len = len(before_text.split(self.sep)[-1].lstrip())
|
curtext = unicode(self.text())
|
||||||
completed_text = before_text[:cursor_pos - prefix_len] + text + self.sep + ' ' + after_text
|
before_text = curtext[:cursor_pos]
|
||||||
prefix_len = prefix_len - len(self.sep) - 1
|
after_text = curtext[cursor_pos:].rstrip()
|
||||||
if prefix_len < 0:
|
# Remove the completion prefix from the before text
|
||||||
prefix_len = 0
|
before_text = self.sep.join(before_text.split(self.sep)[:-1]).rstrip()
|
||||||
|
if before_text:
|
||||||
|
# Add the separator to the end of before_text
|
||||||
|
if self.space_before_sep:
|
||||||
|
before_text += ' '
|
||||||
|
before_text += self.sep + ' '
|
||||||
|
if self.add_separator or after_text:
|
||||||
|
# Add separator to the end of completed text
|
||||||
|
if self.space_before_sep:
|
||||||
|
text = text.rstrip() + ' '
|
||||||
|
completed_text = text + self.sep + ' '
|
||||||
else:
|
else:
|
||||||
prefix_len = len(before_text.split(self.sep)[-1].lstrip())
|
completed_text = text
|
||||||
completed_text = before_text[:cursor_pos - prefix_len] + text + after_text
|
return before_text + completed_text, after_text
|
||||||
return prefix_len, completed_text
|
|
||||||
|
|
||||||
|
|
||||||
def completion_selected(self, text):
|
def completion_selected(self, text):
|
||||||
prefix_len, ctext = self.get_completed_text(unicode(text))
|
before_text, after_text = self.get_completed_text(unicode(text))
|
||||||
if self.sep is None:
|
self.setText(before_text + after_text)
|
||||||
self.setText(ctext)
|
self.setCursorPosition(len(before_text))
|
||||||
self.setCursorPosition(len(ctext))
|
|
||||||
else:
|
|
||||||
cursor_pos = self.cursorPosition()
|
|
||||||
self.setText(ctext)
|
|
||||||
self.setCursorPosition(cursor_pos - prefix_len + len(text))
|
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def all_items(self):
|
def all_items(self):
|
||||||
@ -164,6 +168,9 @@ class MultiCompleteComboBox(EnComboBox):
|
|||||||
def set_space_before_sep(self, space_before):
|
def set_space_before_sep(self, space_before):
|
||||||
self.lineEdit().set_space_before_sep(space_before)
|
self.lineEdit().set_space_before_sep(space_before)
|
||||||
|
|
||||||
|
def set_add_separator(self, what):
|
||||||
|
self.lineEdit().set_add_separator(what)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -19,6 +19,7 @@ from calibre.ptempfile import PersistentTemporaryFile
|
|||||||
from calibre.gui2.convert import Widget
|
from calibre.gui2.convert import Widget
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.library.comments import comments_to_html
|
from calibre.library.comments import comments_to_html
|
||||||
|
from calibre.utils.config import tweaks
|
||||||
|
|
||||||
def create_opf_file(db, book_id):
|
def create_opf_file(db, book_id):
|
||||||
mi = db.get_metadata(book_id, index_is_id=True)
|
mi = db.get_metadata(book_id, index_is_id=True)
|
||||||
@ -108,6 +109,7 @@ class MetadataWidget(Widget, Ui_Form):
|
|||||||
all_authors.sort(key=lambda x : sort_key(x[1]))
|
all_authors.sort(key=lambda x : sort_key(x[1]))
|
||||||
self.author.set_separator('&')
|
self.author.set_separator('&')
|
||||||
self.author.set_space_before_sep(True)
|
self.author.set_space_before_sep(True)
|
||||||
|
self.author.set_add_separator(tweaks['authors_completer_append_separator'])
|
||||||
self.author.update_items_cache(self.db.all_author_names())
|
self.author.update_items_cache(self.db.all_author_names())
|
||||||
|
|
||||||
for i in all_authors:
|
for i in all_authors:
|
||||||
|
@ -9,6 +9,7 @@ from PyQt4.Qt import QDialog, QGridLayout, QLabel, QDialogButtonBox, \
|
|||||||
from calibre.ebooks.metadata import authors_to_string, string_to_authors
|
from calibre.ebooks.metadata import authors_to_string, string_to_authors
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.gui2.complete import MultiCompleteComboBox
|
from calibre.gui2.complete import MultiCompleteComboBox
|
||||||
|
from calibre.utils.config import tweaks
|
||||||
|
|
||||||
class AddEmptyBookDialog(QDialog):
|
class AddEmptyBookDialog(QDialog):
|
||||||
|
|
||||||
@ -69,6 +70,7 @@ class AddEmptyBookDialog(QDialog):
|
|||||||
|
|
||||||
self.authors_combo.set_separator('&')
|
self.authors_combo.set_separator('&')
|
||||||
self.authors_combo.set_space_before_sep(True)
|
self.authors_combo.set_space_before_sep(True)
|
||||||
|
self.authors_combo.set_add_separator(tweaks['authors_completer_append_separator'])
|
||||||
self.authors_combo.update_items_cache(db.all_author_names())
|
self.authors_combo.update_items_cache(db.all_author_names())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -781,6 +781,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
|
|
||||||
self.authors.set_separator('&')
|
self.authors.set_separator('&')
|
||||||
self.authors.set_space_before_sep(True)
|
self.authors.set_space_before_sep(True)
|
||||||
|
self.authors.set_add_separator(tweaks['authors_completer_append_separator'])
|
||||||
self.authors.update_items_cache(self.db.all_author_names())
|
self.authors.update_items_cache(self.db.all_author_names())
|
||||||
|
|
||||||
def initialize_series(self):
|
def initialize_series(self):
|
||||||
|
@ -735,6 +735,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
|
|
||||||
self.authors.set_separator('&')
|
self.authors.set_separator('&')
|
||||||
self.authors.set_space_before_sep(True)
|
self.authors.set_space_before_sep(True)
|
||||||
|
self.authors.set_add_separator(tweaks['authors_completer_append_separator'])
|
||||||
self.authors.update_items_cache(self.db.all_author_names())
|
self.authors.update_items_cache(self.db.all_author_names())
|
||||||
|
|
||||||
def initialize_series(self):
|
def initialize_series(self):
|
||||||
|
@ -9,6 +9,7 @@ from calibre.gui2.dialogs.search_ui import Ui_Dialog
|
|||||||
from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH
|
from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH
|
||||||
from calibre.gui2 import gprefs
|
from calibre.gui2 import gprefs
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
|
from calibre.utils.config import tweaks
|
||||||
|
|
||||||
box_values = {}
|
box_values = {}
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ class SearchDialog(QDialog, Ui_Dialog):
|
|||||||
self.authors_box.setEditText('')
|
self.authors_box.setEditText('')
|
||||||
self.authors_box.set_separator('&')
|
self.authors_box.set_separator('&')
|
||||||
self.authors_box.set_space_before_sep(True)
|
self.authors_box.set_space_before_sep(True)
|
||||||
|
self.authors_box.set_add_separator(tweaks['authors_completer_append_separator'])
|
||||||
self.authors_box.update_items_cache(db.all_author_names())
|
self.authors_box.update_items_cache(db.all_author_names())
|
||||||
|
|
||||||
all_series = db.all_series()
|
all_series = db.all_series()
|
||||||
|
@ -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)
|
||||||
|
@ -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): # {{{
|
||||||
|
@ -177,6 +177,8 @@ class CompleteDelegate(QStyledItemDelegate): # {{{
|
|||||||
editor = MultiCompleteLineEdit(parent)
|
editor = MultiCompleteLineEdit(parent)
|
||||||
editor.set_separator(self.sep)
|
editor.set_separator(self.sep)
|
||||||
editor.set_space_before_sep(self.space_before_sep)
|
editor.set_space_before_sep(self.space_before_sep)
|
||||||
|
if self.sep == '&':
|
||||||
|
editor.set_add_separator(tweaks['authors_completer_append_separator'])
|
||||||
if not index.model().is_custom_column(col):
|
if not index.model().is_custom_column(col):
|
||||||
all_items = getattr(self.db, self.items_func_name)()
|
all_items = getattr(self.db, self.items_func_name)()
|
||||||
else:
|
else:
|
||||||
|
@ -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:
|
||||||
|
@ -177,6 +177,7 @@ class AuthorsEdit(MultiCompleteComboBox):
|
|||||||
|
|
||||||
self.set_separator('&')
|
self.set_separator('&')
|
||||||
self.set_space_before_sep(True)
|
self.set_space_before_sep(True)
|
||||||
|
self.set_add_separator(tweaks['authors_completer_append_separator'])
|
||||||
self.update_items_cache(db.all_author_names())
|
self.update_items_cache(db.all_author_names())
|
||||||
|
|
||||||
au = db.authors(id_, index_is_id=True)
|
au = db.authors(id_, index_is_id=True)
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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 &browser in a separate window (needs restart)</string>
|
<string>Show cover &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>
|
||||||
@ -177,7 +167,7 @@ if you never want subcategories</string>
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QSpinBox" name="opt_tags_browser_collapse_at">
|
<widget class="QSpinBox" name="opt_tags_browser_collapse_at">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>If a Tag Browser category has more than this number of items, it is divided
|
<string>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.</string>
|
up into sub-categories. If the partition method is set to disable, this value is ignored.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
|
@ -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,12 +332,15 @@ 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):
|
||||||
self.hide_plugin()
|
if self.close_after_initial:
|
||||||
|
self.close()
|
||||||
|
else:
|
||||||
|
self.hide_plugin()
|
||||||
|
|
||||||
def restore_defaults(self, *args):
|
def restore_defaults(self, *args):
|
||||||
self.showing_widget.restore_defaults()
|
self.showing_widget.restore_defaults()
|
||||||
|
38
src/calibre/gui2/preferences/search.py
Normal file
38
src/calibre/gui2/preferences/search.py
Normal 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')
|
||||||
|
|
104
src/calibre/gui2/preferences/search.ui
Normal file
104
src/calibre/gui2/preferences/search.ui
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<?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 &type</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_highlight_search_matches">
|
||||||
|
<property name="text">
|
||||||
|
<string>&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, "asimov" 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="1" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_limit_search_columns">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Limit the searched metadata</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>&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="2" 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 "series:Foundation" rather than just "Foundation" in a saved search</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</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>0</width>
|
||||||
|
<height>0</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>
|
@ -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
|
||||||
|
|
||||||
@ -271,7 +270,7 @@ class SavedSearchBox(QComboBox): # {{{
|
|||||||
def initialize(self, _search_box, colorize=False, help_text=_('Search')):
|
def initialize(self, _search_box, colorize=False, help_text=_('Search')):
|
||||||
self.search_box = _search_box
|
self.search_box = _search_box
|
||||||
try:
|
try:
|
||||||
self.line_edit.setPlaceholderText(help_text)
|
self.line_edit.setPlaceholderText(help_text)
|
||||||
except:
|
except:
|
||||||
# Using Qt < 4.7
|
# Using Qt < 4.7
|
||||||
pass
|
pass
|
||||||
@ -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): # {{{
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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', '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.')
|
c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.')
|
||||||
return c
|
return c
|
||||||
|
Loading…
x
Reference in New Issue
Block a user