Merge from trunk

This commit is contained in:
Charles Haley 2013-01-30 07:58:24 +01:00
commit 3156d2b3a7
16 changed files with 227 additions and 114 deletions

View File

@ -682,8 +682,11 @@ There are three possible things I know of, that can cause this:
* The Logitech SetPoint Settings application causes random crashes in * The Logitech SetPoint Settings application causes random crashes in
|app| when it is open. Close it before starting |app|. |app| when it is open. Close it before starting |app|.
* Constant Guard Protection by Xfinity causes crashes in |app|. You have to
manually allow |app| in it or uninstall Constant Guard Protection.
If none of the above apply to you, then there is some other program on your If none of the above apply to you, then there is some other program on your
computer that is interfering with |app|. First reboot your computer is safe computer that is interfering with |app|. First reboot your computer in safe
mode, to have as few running programs as possible, and see if the crashes still mode, to have as few running programs as possible, and see if the crashes still
happen. If they do not, then you know it is some program causing the problem. happen. If they do not, then you know it is some program causing the problem.
The most likely such culprit is a program that modifies other programs' The most likely such culprit is a program that modifies other programs'
@ -794,7 +797,7 @@ Why doesn't |app| have an automatic update?
For many reasons: For many reasons:
* *There is no need to update every week*. If you are happy with how |app| works turn off the update notification and be on your merry way. Check back to see if you want to update once a year or so. * *There is no need to update every week*. If you are happy with how |app| works turn off the update notification and be on your merry way. Check back to see if you want to update once a year or so.
* Pre downloading the updates for all users in the background would mean require about 80TB of bandwidth *every week*. That costs thousands of dollars a month. And |app| is currently growing at 300,000 new users every month. * Pre downloading the updates for all users in the background would require about 80TB of bandwidth *every week*. That costs thousands of dollars a month. And |app| is currently growing at 300,000 new users every month.
* If I implement a dialog that downloads the update and launches it, instead of going to the website as it does now, that would save the most ardent |app| updater, *at most five clicks a week*. There are far higher priority things to do in |app| development. * If I implement a dialog that downloads the update and launches it, instead of going to the website as it does now, that would save the most ardent |app| updater, *at most five clicks a week*. There are far higher priority things to do in |app| development.
* If you really, really hate downloading |app| every week but still want to be up to the latest, I encourage you to run from source, which makes updating trivial. Instructions are :ref:`available here <develop>`. * If you really, really hate downloading |app| every week but still want to be up to the latest, I encourage you to run from source, which makes updating trivial. Instructions are :ref:`available here <develop>`.

View File

@ -11,11 +11,11 @@ class HBR(BasicNewsRecipe):
timefmt = ' [%B %Y]' timefmt = ' [%B %Y]'
language = 'en' language = 'en'
no_stylesheets = True no_stylesheets = True
recipe_disabled = ('hbr.org has started requiring the use of javascript' # recipe_disabled = ('hbr.org has started requiring the use of javascript'
' to log into their website. This is unsupported in calibre, so' # ' to log into their website. This is unsupported in calibre, so'
' this recipe has been disabled. If you would like to see ' # ' this recipe has been disabled. If you would like to see '
' HBR supported in calibre, contact hbr.org and ask them' # ' HBR supported in calibre, contact hbr.org and ask them'
' to provide a javascript free login method.') # ' to provide a javascript free login method.')
LOGIN_URL = 'https://hbr.org/login?request_url=/' LOGIN_URL = 'https://hbr.org/login?request_url=/'
LOGOUT_URL = 'https://hbr.org/logout?request_url=/' LOGOUT_URL = 'https://hbr.org/logout?request_url=/'
@ -38,46 +38,37 @@ class HBR(BasicNewsRecipe):
#articleAuthors{font-family:Georgia,"Times New Roman",Times,serif; font-style:italic; color:#000000;font-size:x-small;} #articleAuthors{font-family:Georgia,"Times New Roman",Times,serif; font-style:italic; color:#000000;font-size:x-small;}
#summaryText{font-family:Georgia,"Times New Roman",Times,serif; font-weight:bold; font-size:x-small;} #summaryText{font-family:Georgia,"Times New Roman",Times,serif; font-weight:bold; font-size:x-small;}
''' '''
use_javascript_to_login = True
def get_browser(self): def javascript_login(self, br, username, password):
br = BasicNewsRecipe.get_browser(self) from calibre.web.jsbrowser.browser import Timeout
self.logout_url = None
#'''
br.open(self.LOGIN_URL)
br.select_form(name='signin-form')
br['signin-form:username'] = self.username
br['signin-form:password'] = self.password
raw = br.submit().read()
if '>Sign out<' not in raw:
raise Exception('Failed to login, are you sure your username and password are correct?')
try: try:
link = br.find_link(text='Sign out') br.visit('https://hbr.org/login?request_url=/', timeout=20)
if link: except Timeout:
self.logout_url = link.absolute_url pass
except: br.click('#accordion div[tabindex="0"]', wait_for_load=False)
self.logout_url = self.LOGOUT_URL f = br.select_form('#signin-form')
#''' f['signin-form:username'] = username
return br f['signin-form:password'] = password
br.submit(wait_for_load=False)
def cleanup(self): br.run_for_a_time(30)
if self.logout_url is not None:
self.browser.open(self.logout_url)
def map_url(self, url): def map_url(self, url):
if url.endswith('/ar/1'): if url.endswith('/ar/1'):
return url[:-1]+'pr' return url[:-1]+'pr'
def hbr_get_toc(self): def hbr_get_toc(self):
#return self.index_to_soup(open('/t/hbr.html').read()) # return self.index_to_soup(open('/t/toc.html').read())
today = date.today() today = date.today()
future = today + timedelta(days=30) future = today + timedelta(days=30)
for x in [x.strftime('%y%m') for x in (future, today)]: for x in [x.strftime('%y%m') for x in (future, today)]:
url = self.INDEX + x url = self.INDEX + x
soup = self.index_to_soup(url) soup = self.index_to_soup(url)
if not soup.find(text='Issue Not Found'): if (not soup.find(text='Issue Not Found') and not soup.find(
text="We're Sorry. There was an error processing your request")
and 'Exception: java.io.FileNotFoundException' not in
unicode(soup)):
return soup return soup
raise Exception('Could not find current issue') raise Exception('Could not find current issue')
@ -85,8 +76,9 @@ class HBR(BasicNewsRecipe):
feeds = [] feeds = []
current_section = None current_section = None
articles = [] articles = []
for x in soup.find(id='archiveToc').findAll(['h3', 'h4']): for x in soup.find(id='issueFeaturesContent').findAll(['li', 'h4']):
if x.name == 'h3': if x.name == 'h4':
if x.get('class', None) == 'basic':continue
if current_section is not None and articles: if current_section is not None and articles:
feeds.append((current_section, articles)) feeds.append((current_section, articles))
current_section = self.tag_to_string(x).capitalize() current_section = self.tag_to_string(x).capitalize()
@ -102,7 +94,7 @@ class HBR(BasicNewsRecipe):
if url.startswith('/'): if url.startswith('/'):
url = 'http://hbr.org' + url url = 'http://hbr.org' + url
url = self.map_url(url) url = self.map_url(url)
p = x.parent.find('p') p = x.find('p', attrs={'class':'author'})
desc = '' desc = ''
if p is not None: if p is not None:
desc = self.tag_to_string(p) desc = self.tag_to_string(p)
@ -114,10 +106,9 @@ class HBR(BasicNewsRecipe):
'date':''}) 'date':''})
return feeds return feeds
def parse_index(self): def parse_index(self):
soup = self.hbr_get_toc() soup = self.hbr_get_toc()
#open('/t/hbr.html', 'wb').write(unicode(soup).encode('utf-8')) # open('/t/hbr.html', 'wb').write(unicode(soup).encode('utf-8'))
feeds = self.hbr_parse_toc(soup) feeds = self.hbr_parse_toc(soup)
return feeds return feeds

Binary file not shown.

View File

@ -66,6 +66,7 @@ def find_categories(field_metadata):
def create_tag_class(category, fm, icon_map): def create_tag_class(category, fm, icon_map):
cat = fm[category] cat = fm[category]
dt = cat['datatype']
icon = None icon = None
label = fm.key_to_label(category) label = fm.key_to_label(category)
if icon_map: if icon_map:
@ -76,13 +77,13 @@ def create_tag_class(category, fm, icon_map):
icon = icon_map['custom:'] icon = icon_map['custom:']
icon_map[category] = icon icon_map[category] = icon
is_editable = category not in {'news', 'rating', 'languages', 'formats', is_editable = category not in {'news', 'rating', 'languages', 'formats',
'identifiers'} 'identifiers'} and dt != 'composite'
if (tweaks['categories_use_field_for_author_name'] == 'author_sort' and if (tweaks['categories_use_field_for_author_name'] == 'author_sort' and
(category == 'authors' or (category == 'authors' or
(cat['display'].get('is_names', False) and (cat['display'].get('is_names', False) and
cat['is_custom'] and cat['is_multiple'] and cat['is_custom'] and cat['is_multiple'] and
cat['datatype'] == 'text'))): dt == 'text'))):
use_sort_as_name = True use_sort_as_name = True
else: else:
use_sort_as_name = False use_sort_as_name = False

View File

@ -99,6 +99,18 @@ class PDFOutput(OutputFormatPlugin):
recommended_value=False, help=_( recommended_value=False, help=_(
'Generate an uncompressed PDF, useful for debugging, ' 'Generate an uncompressed PDF, useful for debugging, '
'only works with the new PDF engine.')), 'only works with the new PDF engine.')),
OptionRecommendation(name='pdf_page_numbers', recommended_value=False,
help=_('Add page numbers to the bottom of every page in the generated PDF file. If you '
'specify a footer template, it will take precedence '
'over this option.')),
OptionRecommendation(name='pdf_footer_template', recommended_value=None,
help=_('An HTML template used to generate footers on every page.'
' The string _PAGENUM_ will be replaced by the current page'
' number.')),
OptionRecommendation(name='pdf_header_template', recommended_value=None,
help=_('An HTML template used to generate headers on every page.'
' The string _PAGENUM_ will be replaced by the current page'
' number.')),
]) ])
def convert(self, oeb_book, output_path, input_plugin, opts, log): def convert(self, oeb_book, output_path, input_plugin, opts, log):

View File

@ -29,6 +29,10 @@ class PagedDisplay
this.current_page_height = null this.current_page_height = null
this.document_margins = null this.document_margins = null
this.use_document_margins = false this.use_document_margins = false
this.footer_template = null
this.header_template = null
this.header = null
this.footer = null
read_document_margins: () -> read_document_margins: () ->
# Read page margins from the document. First checks for an @page rule. # Read page margins from the document. First checks for an @page rule.
@ -102,6 +106,7 @@ class PagedDisplay
# than max_col_width # than max_col_width
sm += Math.ceil( (col_width - this.max_col_width) / 2*n ) sm += Math.ceil( (col_width - this.max_col_width) / 2*n )
col_width = Math.max(100, ((ww - adjust)/n) - 2*sm) col_width = Math.max(100, ((ww - adjust)/n) - 2*sm)
this.col_width = col_width
this.page_width = col_width + 2*sm this.page_width = col_width + 2*sm
this.screen_width = this.page_width * this.cols_per_screen this.screen_width = this.page_width * this.cols_per_screen
this.current_page_height = window.innerHeight - this.margin_top - this.margin_bottom this.current_page_height = window.innerHeight - this.margin_top - this.margin_bottom
@ -171,6 +176,30 @@ class PagedDisplay
# log('Time to layout:', new Date().getTime() - start_time) # log('Time to layout:', new Date().getTime() - start_time)
return sm return sm
create_header_footer: () ->
if this.header_template != null
this.header = document.createElement('div')
this.header.setAttribute('style', "overflow:hidden; display:block; position:absolute; left:#{ this.side_margin }px; top: 0px; height: #{ this.margin_top }px; width: #{ this.col_width }px; margin: 0; padding: 0")
document.body.appendChild(this.header)
if this.footer_template != null
this.footer = document.createElement('div')
this.footer.setAttribute('style', "overflow:hidden; display:block; position:absolute; left:#{ this.side_margin }px; top: #{ window.innerHeight - this.margin_bottom }px; height: #{ this.margin_bottom }px; width: #{ this.col_width }px; margin: 0; padding: 0")
document.body.appendChild(this.footer)
this.update_header_footer(1)
position_header_footer: () ->
[left, top] = calibre_utils.viewport_to_document(0, 0, document.body.ownerDocument)
if this.header != null
this.header.style.setProperty('left', left+'px')
if this.footer != null
this.footer.style.setProperty('left', left+'px')
update_header_footer: (pagenum) ->
if this.header != null
this.header.innerHTML = this.header_template.replace(/_PAGENUM_/g, pagenum+"")
if this.footer != null
this.footer.innerHTML = this.footer_template.replace(/_PAGENUM_/g, pagenum+"")
fit_images: () -> fit_images: () ->
# Ensure no images are wider than the available width in a column. Note # Ensure no images are wider than the available width in a column. Note
# that this method use getBoundingClientRect() which means it will # that this method use getBoundingClientRect() which means it will

View File

@ -161,6 +161,17 @@ class PDFWriter(QObject):
debug=self.log.debug, compress=not debug=self.log.debug, compress=not
opts.uncompressed_pdf, opts.uncompressed_pdf,
mark_links=opts.pdf_mark_links) mark_links=opts.pdf_mark_links)
self.footer = opts.pdf_footer_template
if self.footer is None and opts.pdf_page_numbers:
self.footer = '<p style="text-align:center; text-indent: 0">_PAGENUM_</p>'
self.header = opts.pdf_header_template
min_margin = 36
if self.footer and opts.margin_bottom < min_margin:
self.log.warn('Bottom margin is too small for footer, increasing it.')
opts.margin_bottom = min_margin
if self.header and opts.margin_top < min_margin:
self.log.warn('Top margin is too small for header, increasing it.')
opts.margin_top = min_margin
self.page.setViewportSize(QSize(self.doc.width(), self.doc.height())) self.page.setViewportSize(QSize(self.doc.width(), self.doc.height()))
self.render_queue = items self.render_queue = items
@ -264,6 +275,15 @@ class PDFWriter(QObject):
py_bridge.value = book_indexing.all_links_and_anchors(); py_bridge.value = book_indexing.all_links_and_anchors();
'''%(self.margin_top, 0, self.margin_bottom)) '''%(self.margin_top, 0, self.margin_bottom))
if self.header:
self.bridge_value = self.header
evaljs('paged_display.header_template = py_bridge.value')
if self.footer:
self.bridge_value = self.footer
evaljs('paged_display.footer_template = py_bridge.value')
if self.header or self.footer:
evaljs('paged_display.create_header_footer();')
amap = self.bridge_value amap = self.bridge_value
if not isinstance(amap, dict): if not isinstance(amap, dict):
amap = {'links':[], 'anchors':{}} # Some javascript error occurred amap = {'links':[], 'anchors':{}} # Some javascript error occurred
@ -272,6 +292,8 @@ class PDFWriter(QObject):
mf = self.view.page().mainFrame() mf = self.view.page().mainFrame()
while True: while True:
self.doc.init_page() self.doc.init_page()
if self.header or self.footer:
evaljs('paged_display.update_header_footer(%d)'%self.current_page_num)
self.painter.save() self.painter.save()
mf.render(self.painter) mf.render(self.painter)
self.painter.restore() self.painter.restore()
@ -279,7 +301,7 @@ class PDFWriter(QObject):
self.doc.end_page() self.doc.end_page()
if not nsl[1] or nsl[0] <= 0: if not nsl[1] or nsl[0] <= 0:
break break
evaljs('window.scrollTo(%d, 0)'%nsl[0]) evaljs('window.scrollTo(%d, 0); paged_display.position_header_footer();'%nsl[0])
if self.doc.errors_occurred: if self.doc.errors_occurred:
break break

View File

@ -22,91 +22,94 @@ from calibre.utils.date import UNDEFINED_DATE
# Setup gprefs {{{ # Setup gprefs {{{
gprefs = JSONConfig('gui') gprefs = JSONConfig('gui')
defs = gprefs.defaults
if isosx: if isosx:
gprefs.defaults['action-layout-menubar'] = ( defs['action-layout-menubar'] = (
'Add Books', 'Edit Metadata', 'Convert Books', 'Add Books', 'Edit Metadata', 'Convert Books',
'Choose Library', 'Save To Disk', 'Preferences', 'Choose Library', 'Save To Disk', 'Preferences',
'Help', 'Help',
) )
gprefs.defaults['action-layout-menubar-device'] = ( defs['action-layout-menubar-device'] = (
'Add Books', 'Edit Metadata', 'Convert Books', 'Add Books', 'Edit Metadata', 'Convert Books',
'Location Manager', 'Send To Device', 'Location Manager', 'Send To Device',
'Save To Disk', 'Preferences', 'Help', 'Save To Disk', 'Preferences', 'Help',
) )
gprefs.defaults['action-layout-toolbar'] = ( defs['action-layout-toolbar'] = (
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None, 'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None,
'Choose Library', 'Donate', None, 'Fetch News', 'Store', 'Save To Disk', 'Choose Library', 'Donate', None, 'Fetch News', 'Store', 'Save To Disk',
'Connect Share', None, 'Remove Books', 'Connect Share', None, 'Remove Books',
) )
gprefs.defaults['action-layout-toolbar-device'] = ( defs['action-layout-toolbar-device'] = (
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', 'Add Books', 'Edit Metadata', None, 'Convert Books', 'View',
'Send To Device', None, None, 'Location Manager', None, None, 'Send To Device', None, None, 'Location Manager', None, None,
'Fetch News', 'Store', 'Save To Disk', 'Connect Share', None, 'Fetch News', 'Store', 'Save To Disk', 'Connect Share', None,
'Remove Books', 'Remove Books',
) )
else: else:
gprefs.defaults['action-layout-menubar'] = () defs['action-layout-menubar'] = ()
gprefs.defaults['action-layout-menubar-device'] = () defs['action-layout-menubar-device'] = ()
gprefs.defaults['action-layout-toolbar'] = ( defs['action-layout-toolbar'] = (
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None, 'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None,
'Store', 'Donate', 'Fetch News', 'Help', None, 'Store', 'Donate', 'Fetch News', 'Help', None,
'Remove Books', 'Choose Library', 'Save To Disk', 'Remove Books', 'Choose Library', 'Save To Disk',
'Connect Share', 'Preferences', 'Connect Share', 'Preferences',
) )
gprefs.defaults['action-layout-toolbar-device'] = ( defs['action-layout-toolbar-device'] = (
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', 'Add Books', 'Edit Metadata', None, 'Convert Books', 'View',
'Send To Device', None, None, 'Location Manager', None, None, 'Send To Device', None, None, 'Location Manager', None, None,
'Fetch News', 'Save To Disk', 'Store', 'Connect Share', None, 'Fetch News', 'Save To Disk', 'Store', 'Connect Share', None,
'Remove Books', None, 'Help', 'Preferences', 'Remove Books', None, 'Help', 'Preferences',
) )
gprefs.defaults['action-layout-toolbar-child'] = () defs['action-layout-toolbar-child'] = ()
gprefs.defaults['action-layout-context-menu'] = ( defs['action-layout-context-menu'] = (
'Edit Metadata', 'Send To Device', 'Save To Disk', 'Edit Metadata', 'Send To Device', 'Save To Disk',
'Connect Share', 'Copy To Library', None, 'Connect Share', 'Copy To Library', None,
'Convert Books', 'View', 'Open Folder', 'Show Book Details', 'Convert Books', 'View', 'Open Folder', 'Show Book Details',
'Similar Books', 'Tweak ePub', None, 'Remove Books', 'Similar Books', 'Tweak ePub', None, 'Remove Books',
) )
gprefs.defaults['action-layout-context-menu-device'] = ( defs['action-layout-context-menu-device'] = (
'View', 'Save To Disk', None, 'Remove Books', None, 'View', 'Save To Disk', None, 'Remove Books', None,
'Add To Library', 'Edit Collections', 'Add To Library', 'Edit Collections',
) )
gprefs.defaults['action-layout-context-menu-cover-browser'] = ( defs['action-layout-context-menu-cover-browser'] = (
'Edit Metadata', 'Send To Device', 'Save To Disk', 'Edit Metadata', 'Send To Device', 'Save To Disk',
'Connect Share', 'Copy To Library', None, 'Connect Share', 'Copy To Library', None,
'Convert Books', 'View', 'Open Folder', 'Show Book Details', 'Convert Books', 'View', 'Open Folder', 'Show Book Details',
'Similar Books', 'Tweak ePub', None, 'Remove Books', 'Similar Books', 'Tweak ePub', None, 'Remove Books',
) )
gprefs.defaults['show_splash_screen'] = True defs['show_splash_screen'] = True
gprefs.defaults['toolbar_icon_size'] = 'medium' defs['toolbar_icon_size'] = 'medium'
gprefs.defaults['automerge'] = 'ignore' defs['automerge'] = 'ignore'
gprefs.defaults['toolbar_text'] = 'always' defs['toolbar_text'] = 'always'
gprefs.defaults['font'] = None defs['font'] = None
gprefs.defaults['tags_browser_partition_method'] = 'first letter' defs['tags_browser_partition_method'] = 'first letter'
gprefs.defaults['tags_browser_collapse_at'] = 100 defs['tags_browser_collapse_at'] = 100
gprefs.defaults['tag_browser_dont_collapse'] = [] defs['tag_browser_dont_collapse'] = []
gprefs.defaults['edit_metadata_single_layout'] = 'default' defs['edit_metadata_single_layout'] = 'default'
gprefs.defaults['default_author_link'] = 'http://en.wikipedia.org/w/index.php?search={author}' defs['default_author_link'] = 'http://en.wikipedia.org/w/index.php?search={author}'
gprefs.defaults['preserve_date_on_ctl'] = True defs['preserve_date_on_ctl'] = True
gprefs.defaults['manual_add_auto_convert'] = False defs['manual_add_auto_convert'] = False
gprefs.defaults['cb_fullscreen'] = False defs['cb_fullscreen'] = False
gprefs.defaults['worker_max_time'] = 0 defs['worker_max_time'] = 0
gprefs.defaults['show_files_after_save'] = True defs['show_files_after_save'] = True
gprefs.defaults['auto_add_path'] = None defs['auto_add_path'] = None
gprefs.defaults['auto_add_check_for_duplicates'] = False defs['auto_add_check_for_duplicates'] = False
gprefs.defaults['blocked_auto_formats'] = [] defs['blocked_auto_formats'] = []
gprefs.defaults['auto_add_auto_convert'] = True defs['auto_add_auto_convert'] = True
gprefs.defaults['ui_style'] = 'calibre' if iswindows or isosx else 'system' defs['ui_style'] = 'calibre' if iswindows or isosx else 'system'
gprefs.defaults['tag_browser_old_look'] = False defs['tag_browser_old_look'] = False
gprefs.defaults['book_list_tooltips'] = True defs['book_list_tooltips'] = True
gprefs.defaults['bd_show_cover'] = True defs['bd_show_cover'] = True
gprefs.defaults['bd_overlay_cover_size'] = False defs['bd_overlay_cover_size'] = False
gprefs.defaults['tags_browser_category_icons'] = {} defs['tags_browser_category_icons'] = {}
defs['cover_browser_reflections'] = True
del defs
# }}} # }}}
NONE = QVariant() #: Null value to return from the data function of item models NONE = QVariant() #: Null value to return from the data function of item models

View File

@ -22,7 +22,7 @@ class PluginWidget(Widget, Ui_Form):
'override_profile_size', 'paper_size', 'custom_size', 'override_profile_size', 'paper_size', 'custom_size',
'preserve_cover_aspect_ratio', 'pdf_serif_family', 'unit', 'preserve_cover_aspect_ratio', 'pdf_serif_family', 'unit',
'pdf_sans_family', 'pdf_mono_family', 'pdf_standard_font', 'pdf_sans_family', 'pdf_mono_family', 'pdf_standard_font',
'pdf_default_font_size', 'pdf_mono_font_size']) 'pdf_default_font_size', 'pdf_mono_font_size', 'pdf_page_numbers'])
self.db, self.book_id = db, book_id self.db, self.book_id = db, book_id
for x in get_option('paper_size').option.choices: for x in get_option('paper_size').option.choices:

View File

@ -84,7 +84,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="6" column="0">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>Se&amp;rif family:</string> <string>Se&amp;rif family:</string>
@ -94,10 +94,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="6" column="1">
<widget class="QFontComboBox" name="opt_pdf_serif_family"/> <widget class="QFontComboBox" name="opt_pdf_serif_family"/>
</item> </item>
<item row="6" column="0"> <item row="7" column="0">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
<string>&amp;Sans family:</string> <string>&amp;Sans family:</string>
@ -107,10 +107,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="7" column="1">
<widget class="QFontComboBox" name="opt_pdf_sans_family"/> <widget class="QFontComboBox" name="opt_pdf_sans_family"/>
</item> </item>
<item row="7" column="0"> <item row="8" column="0">
<widget class="QLabel" name="label_6"> <widget class="QLabel" name="label_6">
<property name="text"> <property name="text">
<string>&amp;Monospace family:</string> <string>&amp;Monospace family:</string>
@ -120,10 +120,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="8" column="1">
<widget class="QFontComboBox" name="opt_pdf_mono_family"/> <widget class="QFontComboBox" name="opt_pdf_mono_family"/>
</item> </item>
<item row="8" column="0"> <item row="9" column="0">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_7">
<property name="text"> <property name="text">
<string>S&amp;tandard font:</string> <string>S&amp;tandard font:</string>
@ -133,10 +133,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="1"> <item row="9" column="1">
<widget class="QComboBox" name="opt_pdf_standard_font"/> <widget class="QComboBox" name="opt_pdf_standard_font"/>
</item> </item>
<item row="9" column="0"> <item row="10" column="0">
<widget class="QLabel" name="label_8"> <widget class="QLabel" name="label_8">
<property name="text"> <property name="text">
<string>Default font si&amp;ze:</string> <string>Default font si&amp;ze:</string>
@ -146,14 +146,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="1"> <item row="10" column="1">
<widget class="QSpinBox" name="opt_pdf_default_font_size"> <widget class="QSpinBox" name="opt_pdf_default_font_size">
<property name="suffix"> <property name="suffix">
<string> px</string> <string> px</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="0"> <item row="11" column="0">
<widget class="QLabel" name="label_9"> <widget class="QLabel" name="label_9">
<property name="text"> <property name="text">
<string>Monospace &amp;font size:</string> <string>Monospace &amp;font size:</string>
@ -163,14 +163,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="1"> <item row="11" column="1">
<widget class="QSpinBox" name="opt_pdf_mono_font_size"> <widget class="QSpinBox" name="opt_pdf_mono_font_size">
<property name="suffix"> <property name="suffix">
<string> px</string> <string> px</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="0"> <item row="12" column="0">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -183,6 +183,13 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="opt_pdf_page_numbers">
<property name="text">
<string>Add page &amp;numbers to the bottom of every page</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -106,6 +106,8 @@ if pictureflow is not None:
self.setContextMenuPolicy(Qt.DefaultContextMenu) self.setContextMenuPolicy(Qt.DefaultContextMenu)
if hasattr(self, 'setSubtitleFont'): if hasattr(self, 'setSubtitleFont'):
self.setSubtitleFont(QFont(rating_font())) self.setSubtitleFont(QFont(rating_font()))
if not gprefs['cover_browser_reflections']:
self.setShowReflections(False)
def set_context_menu(self, cm): def set_context_menu(self, cm):
self.context_menu = cm self.context_menu = cm

View File

@ -340,6 +340,9 @@ public:
int currentSlide() const; int currentSlide() const;
void setCurrentSlide(int index); void setCurrentSlide(int index);
bool showReflections() const;
void setShowReflections(bool show);
int getTarget() const; int getTarget() const;
void showPrevious(); void showPrevious();
@ -378,6 +381,7 @@ private:
int slideHeight; int slideHeight;
int fontSize; int fontSize;
int queueLength; int queueLength;
bool doReflections;
int centerIndex; int centerIndex;
SlideInfo centerSlide; SlideInfo centerSlide;
@ -416,6 +420,7 @@ PictureFlowPrivate::PictureFlowPrivate(PictureFlow* w, int queueLength_)
slideWidth = 200; slideWidth = 200;
slideHeight = 200; slideHeight = 200;
fontSize = 10; fontSize = 10;
doReflections = true;
centerIndex = 0; centerIndex = 0;
queueLength = queueLength_; queueLength = queueLength_;
@ -494,6 +499,15 @@ void PictureFlowPrivate::setCurrentSlide(int index)
widget->emitcurrentChanged(centerIndex); widget->emitcurrentChanged(centerIndex);
} }
bool PictureFlowPrivate::showReflections() const {
return doReflections;
}
void PictureFlowPrivate::setShowReflections(bool show) {
doReflections = show;
triggerRender();
}
void PictureFlowPrivate::showPrevious() void PictureFlowPrivate::showPrevious()
{ {
if(step >= 0) if(step >= 0)
@ -584,7 +598,7 @@ void PictureFlowPrivate::resetSlides()
} }
} }
static QImage prepareSurface(QImage img, int w, int h) static QImage prepareSurface(QImage img, int w, int h, bool doReflections)
{ {
img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
@ -602,19 +616,21 @@ static QImage prepareSurface(QImage img, int w, int h)
for(int y = 0; y < h; y++) for(int y = 0; y < h; y++)
result.setPixel(y, x, img.pixel(x, y)); result.setPixel(y, x, img.pixel(x, y));
// create the reflection if (doReflections) {
int ht = hs - h; // create the reflection
for(int x = 0; x < w; x++) int ht = hs - h;
for(int y = 0; y < ht; y++) for(int x = 0; x < w; x++)
{ for(int y = 0; y < ht; y++)
QRgb color = img.pixel(x, img.height()-y-1); {
//QRgb565 color = img.scanLine(img.height()-y-1) + x*sizeof(QRgb565); //img.pixel(x, img.height()-y-1); QRgb color = img.pixel(x, img.height()-y-1);
int a = qAlpha(color); //QRgb565 color = img.scanLine(img.height()-y-1) + x*sizeof(QRgb565); //img.pixel(x, img.height()-y-1);
int r = qRed(color) * a / 256 * (ht - y) / ht * 3/5; int a = qAlpha(color);
int g = qGreen(color) * a / 256 * (ht - y) / ht * 3/5; int r = qRed(color) * a / 256 * (ht - y) / ht * 3/5;
int b = qBlue(color) * a / 256 * (ht - y) / ht * 3/5; int g = qGreen(color) * a / 256 * (ht - y) / ht * 3/5;
result.setPixel(h+y, x, qRgb(r, g, b)); int b = qBlue(color) * a / 256 * (ht - y) / ht * 3/5;
} result.setPixel(h+y, x, qRgb(r, g, b));
}
}
return result; return result;
} }
@ -652,12 +668,12 @@ QImage* PictureFlowPrivate::surface(int slideIndex)
painter.setBrush(QBrush()); painter.setBrush(QBrush());
painter.drawRect(2, 2, slideWidth-3, slideHeight-3); painter.drawRect(2, 2, slideWidth-3, slideHeight-3);
painter.end(); painter.end();
blankSurface = prepareSurface(blankSurface, slideWidth, slideHeight); blankSurface = prepareSurface(blankSurface, slideWidth, slideHeight, doReflections);
} }
return &blankSurface; return &blankSurface;
} }
surfaceCache.insert(slideIndex, new QImage(prepareSurface(img, slideWidth, slideHeight))); surfaceCache.insert(slideIndex, new QImage(prepareSurface(img, slideWidth, slideHeight, doReflections)));
return surfaceCache[slideIndex]; return surfaceCache[slideIndex];
} }
@ -1196,6 +1212,13 @@ QImage PictureFlow::slide(int index) const
return d->slide(index); return d->slide(index);
} }
bool PictureFlow::showReflections() const {
return d->showReflections();
}
void PictureFlow::setShowReflections(bool show) {
d->setShowReflections(show);
}
void PictureFlow::setImages(FlowImages *images) void PictureFlow::setImages(FlowImages *images)
{ {

View File

@ -121,6 +121,12 @@ public:
*/ */
void setSlideSize(QSize size); void setSlideSize(QSize size);
/*!
Turn the reflections on/off.
*/
void setShowReflections(bool show);
bool showReflections() const;
/*! /*!
Returns the font used to render subtitles Returns the font used to render subtitles
*/ */

View File

@ -51,6 +51,10 @@ public :
int currentSlide() const; int currentSlide() const;
bool showReflections() const;
void setShowReflections(bool show);
public slots: public slots:
void setCurrentSlide(int index); void setCurrentSlide(int index);

View File

@ -110,6 +110,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('bd_overlay_cover_size', gprefs) r('bd_overlay_cover_size', gprefs)
r('cover_flow_queue_length', config, restart_required=True) r('cover_flow_queue_length', config, restart_required=True)
r('cover_browser_reflections', gprefs)
def get_esc_lang(l): def get_esc_lang(l):
if l == 'en': if l == 'en':
@ -289,6 +290,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.update_font_display() self.update_font_display()
gui.tags_view.reread_collapse_parameters() gui.tags_view.reread_collapse_parameters()
gui.library_view.refresh_book_details() gui.library_view.refresh_book_details()
if gui.cover_flow is not None:
gui.cover_flow.setShowReflections(gprefs['cover_browser_reflections'])
if __name__ == '__main__': if __name__ == '__main__':
from calibre.gui2 import Application from calibre.gui2 import Application

View File

@ -496,10 +496,7 @@ a few top-level elements.</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="5" column="0" colspan="2">
<widget class="QSpinBox" name="opt_cover_flow_queue_length"/>
</item>
<item row="4" column="0" colspan="2">
<spacer name="verticalSpacer_4"> <spacer name="verticalSpacer_4">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -512,14 +509,17 @@ a few top-level elements.</string>
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="2" column="0" colspan="2"> <item row="1" column="1">
<widget class="QSpinBox" name="opt_cover_flow_queue_length"/>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="opt_cb_fullscreen"> <widget class="QCheckBox" name="opt_cb_fullscreen">
<property name="text"> <property name="text">
<string>When showing cover browser in separate window, show it &amp;fullscreen</string> <string>When showing cover browser in separate window, show it &amp;fullscreen</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0" colspan="2"> <item row="4" column="0" colspan="2">
<widget class="QLabel" name="fs_help_msg"> <widget class="QLabel" name="fs_help_msg">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">margin-left: 1.5em</string> <string notr="true">margin-left: 1.5em</string>
@ -532,6 +532,13 @@ a few top-level elements.</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QCheckBox" name="opt_cover_browser_reflections">
<property name="text">
<string>Show &amp;reflections in the cover browser</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>