mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
37ddf7d075
@ -19,6 +19,74 @@
|
||||
# new recipes:
|
||||
# - title:
|
||||
|
||||
- version: 0.8.60
|
||||
date: 2012-07-13
|
||||
|
||||
new features:
|
||||
- title: "When searching, allow use of un-accented characters to match accented characters in all fields and all languages (not just authors and English as before)"
|
||||
description: "The rules for matching un-accented characters are done in a language dependent way. So if your calibre interface language is set to English, n will match both n and ñ, but if it is set to Spanish, it will match only n, as in Spanish ñ is a separate alphabet in Spanish. This makes searching a little slower, so if you have a very large library you can turn it off via Preferences->Searching."
|
||||
type: major
|
||||
|
||||
- title: "Content server: Show a best guess for the IP address the content server is currently listening at in the connect/share menu."
|
||||
tickets: [1024128]
|
||||
|
||||
- title: "E-book viewer: Add an option to show a clock in full screen mode."
|
||||
tickets: [1022086]
|
||||
|
||||
- title: "Drivers for Paquito Imaginarium and a few Android phones"
|
||||
tickets: [1024021,1023613,1023461,1022401]
|
||||
|
||||
- title: "HTMLZ Output: Add option to use the book title as the filename for the html file inside the archive"
|
||||
|
||||
- title: "Make the list of displayed fields in the book details panel a per library setting"
|
||||
|
||||
- title: "Have autocomplete on authors/series/tags/etc. ignore accented characters when finding matches (similar to the changes to search above)"
|
||||
|
||||
- title: "Support for retina displays in OS X (I hope)"
|
||||
tickets: [1022191]
|
||||
|
||||
- title: "Remove the dependency on the zip command line tool when developing plugins"
|
||||
|
||||
bug fixes:
|
||||
- title: "Kobo driver: Do not perform write operations on the Kobo database if its version is newer than the latest version the driver supports, for safety"
|
||||
|
||||
- title: "KF8 Input: Ignore encoding declarations inside the html markup, as they are sometimes incorrect."
|
||||
tickets: [1022933]
|
||||
|
||||
- title: "Force refresh of cached composite column values when values in the cache are changed"
|
||||
|
||||
- title: "Fix a regression that broke calibre --shutdown-running-calibre on windows."
|
||||
tickets: [1022504]
|
||||
|
||||
- title: "Possible workaround for Qt 4.8.2 open file dialog failing on some linux distros."
|
||||
tickets: [1022019]
|
||||
|
||||
- title: "Catalogs: Fix some epubcheck errors when generating catalogs in EPUB format"
|
||||
|
||||
- title: "Linux installer: When calling the xdg utilities use system libraries rather than the libraries bundled with calibre"
|
||||
|
||||
- title: "Fix numeric sort for composite custom columns that use custom separators"
|
||||
tickets: [1021814]
|
||||
|
||||
- title: "Tag browser: When grouping by first letter, handle languages that have 'letters' made of more than one character. This can be turned off via Preferences->Tweaks"
|
||||
|
||||
improved recipes:
|
||||
- Hola magazine
|
||||
- Adventure Gamers
|
||||
- Cosmopolitan UK
|
||||
- Onda Rock
|
||||
|
||||
new recipes:
|
||||
- title: Empire Magazine
|
||||
author: Dave Asbury
|
||||
|
||||
- title: NZZ Folio
|
||||
author: Bernd Leinfelder
|
||||
|
||||
- title: Warentest
|
||||
author: asdfdsfksd
|
||||
|
||||
|
||||
- version: 0.8.59
|
||||
date: 2012-07-06
|
||||
|
||||
|
@ -198,7 +198,7 @@ You can insert print statements anywhere in your plugin code, they will be outpu
|
||||
You can quickly test changes to your plugin by using the following command
|
||||
line::
|
||||
|
||||
calibre -s; calibre-customize -b /path/to/your/plugin/directory; calibre
|
||||
calibre-debug -s; calibre-customize -b /path/to/your/plugin/directory; calibre
|
||||
|
||||
This will shutdown a running calibre, wait for the shutdown to complete, then update your plugin in |app| and relaunch |app|.
|
||||
|
||||
|
@ -33,6 +33,7 @@ class FazNet(BasicNewsRecipe):
|
||||
('Technik & Motor', 'http://www.faz.net/aktuell/technik-motor/?rssview=1'),
|
||||
('Wissen', 'http://www.faz.net/aktuell/wissen/?rssview=1'),
|
||||
('Reise', 'http://www.faz.net/aktuell/reise/?rssview=1'),
|
||||
('Beruf & Chance', 'http://www.faz.net/aktuell/beruf-chance/?rssview=1')
|
||||
('Beruf & Chance', 'http://www.faz.net/aktuell/beruf-chance/?rssview=1'),
|
||||
('Rhein-Main', 'http://www.faz.net/aktuell/rhein-main/?rssview=1') # AGe add 2012-07-13
|
||||
]
|
||||
|
||||
|
@ -1,38 +1,73 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Brendan Sleight <bms.calibre at barwap.com>'
|
||||
__copyright__ = '30 June 2012, desUBIKado'
|
||||
__author__ = 'desUBIKado'
|
||||
__description__ = 'Diario de actualidad, moda y belleza'
|
||||
__version__ = 'v0.01'
|
||||
__date__ = '30, June 2012'
|
||||
'''
|
||||
hola.com
|
||||
http://www.hola.com/
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Hackaday(BasicNewsRecipe):
|
||||
title = u'Hola'
|
||||
__author__ = 'bmsleight'
|
||||
description = 'diario de actualidad, moda y belleza.'
|
||||
oldest_article = 10
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
class hola_es(BasicNewsRecipe):
|
||||
__author__ = 'desUBIKado'
|
||||
description = 'Diario de actualidad, moda y belleza'
|
||||
title = u'¡Hola!'
|
||||
publisher = 'Hola S.L.'
|
||||
category = 'Spanish celebrities, Entertainment News, Royalty, Daily Variety, Hollywood'
|
||||
language = 'es'
|
||||
|
||||
masthead_url = 'http://imagenes.hola.com/comunes/2008/logo-holacom.gif'
|
||||
timefmt = '[%a, %d %b, %Y]'
|
||||
oldest_article = 7
|
||||
delay = 1
|
||||
encoding = 'utf-8'
|
||||
max_articles_per_feed = 100
|
||||
use_embedded_content = False
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'id':'cuerpo'})
|
||||
]
|
||||
remove_empty_feeds = True
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
|
||||
feeds = [
|
||||
(u'Famosos' , u'http://www.hola.com/famosos/rss.xml' ),
|
||||
(u'Realeza' , u'http://www.hola.com/realeza/rss.xml' ),
|
||||
(u'Cine' , u'http://www.hola.com/cine/rss.xml' ),
|
||||
(u'Música' , u'http://www.hola.com/musica/rss.xml' ),
|
||||
(u'Moda y modelos' , u'http://www.hola.com/moda/portada/rss.xml' ),
|
||||
(u'Belleza y salud', u'http://www.hola.com/belleza/portada/rss.xml' ),
|
||||
(u'Niños' , u'http://www.hola.com/ninos/rss.xml' ),
|
||||
(u'Todas las noticias', u'http://int2.hola.com/app/feeds/rss_hola.php'),
|
||||
(u'Famosos' , u'http://www.hola.com/famosos/rss.xml' )
|
||||
,(u'Realeza' , u'http://www.hola.com/realeza/rss.xml' )
|
||||
,(u'Cine' , u'http://www.hola.com/cine/rss.xml' )
|
||||
,(u'M\xfasica' , u'http://www.hola.com/musica/rss.xml' )
|
||||
,(u'Moda y modelos' , u'http://www.hola.com/moda/portada/rss.xml' )
|
||||
,(u'Belleza y salud', u'http://www.hola.com/belleza/portada/rss.xml' )
|
||||
,(u'Ni\xf1os' , u'http://www.hola.com/ninos/rss.xml' )
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':['cuerpo','com']})]
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'id':['relacionadas','slide-enlaces-patrocinados','comentarios']}),
|
||||
dict(name='div', attrs={'class':['slide-enlaces-patricinados-tit','compartir']})
|
||||
]
|
||||
|
||||
remove_tags_after = dict(name='div' , attrs={'id':'comentarios'})
|
||||
|
||||
|
||||
# Recuperamos la portada de papel (la imagen 520 tiene mayor resolucion)
|
||||
|
||||
def get_cover_url(self):
|
||||
index = 'http://www.hola.com/abono/ediciondigital/'
|
||||
soup = self.index_to_soup(index)
|
||||
for image in soup.findAll('img',src=True):
|
||||
if image['src'].endswith('portada-revista-hola-520.jpg'):
|
||||
return 'http://www.hola.com/' + image['src']
|
||||
return None
|
||||
|
||||
|
||||
def get_article_url(self, article):
|
||||
url = article.get('guid', None)
|
||||
return url
|
||||
|
||||
extra_css = '''
|
||||
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:30px;}
|
||||
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal; font-style:italic; font-size:18px;}
|
||||
'''
|
||||
|
@ -8,7 +8,7 @@ class NatGeoMag(BasicNewsRecipe):
|
||||
oldest_article = 31
|
||||
max_articles_per_feed = 50
|
||||
category = 'geography, magazine'
|
||||
language = 'en_US'
|
||||
language = 'en'
|
||||
publication_type = 'magazine'
|
||||
cover_url = 'http://www.yourlogoresources.com/wp-content/uploads/2011/09/national-geographic-logo.jpg'
|
||||
use_embedded_content = False
|
||||
|
@ -522,6 +522,6 @@ default_tweak_format = None
|
||||
# consideration when partitioning by first letter.
|
||||
# Examples:
|
||||
# enable_multicharacters_in_tag_browser = True
|
||||
# enable_multicharacters_in_tag_browser = True
|
||||
# enable_multicharacters_in_tag_browser = False
|
||||
enable_multicharacters_in_tag_browser = True
|
||||
|
||||
|
@ -12,7 +12,7 @@ import sys, os, shutil, platform, subprocess, stat, py_compile, glob, \
|
||||
from setup import Command, modules, basenames, functions, __version__, \
|
||||
__appname__
|
||||
|
||||
SITE_PACKAGES = ['IPython', 'PIL', 'dateutil', 'dns', 'PyQt4', 'mechanize',
|
||||
SITE_PACKAGES = ['PIL', 'dateutil', 'dns', 'PyQt4', 'mechanize',
|
||||
'sip.so', 'BeautifulSoup.py', 'cssutils', 'encutils', 'lxml',
|
||||
'sipconfig.py', 'xdg', 'dbus', '_dbus_bindings.so', 'dbus_bindings.py',
|
||||
'_dbus_glib_bindings.so']
|
||||
|
@ -386,7 +386,7 @@ class Py2App(object):
|
||||
@flush
|
||||
def add_poppler(self):
|
||||
info('\nAdding poppler')
|
||||
for x in ('libpoppler.25.dylib',):
|
||||
for x in ('libpoppler.26.dylib',):
|
||||
self.install_dylib(os.path.join(SW, 'lib', x))
|
||||
for x in ('pdftohtml', 'pdftoppm', 'pdfinfo'):
|
||||
self.install_dylib(os.path.join(SW, 'bin', x), False)
|
||||
@ -483,10 +483,6 @@ class Py2App(object):
|
||||
shutil.rmtree(tdir)
|
||||
shutil.rmtree(os.path.join(self.site_packages, 'calibre', 'plugins'))
|
||||
self.remove_bytecode(join(self.resources_dir, 'Python', 'site-packages'))
|
||||
# Create dummy IPython README_STARTUP
|
||||
with open(join(self.site_packages,
|
||||
'IPython/config/profile/README_STARTUP'), 'wb') as f:
|
||||
f.write('\n')
|
||||
|
||||
@flush
|
||||
def add_modules_from_dir(self, src):
|
||||
|
@ -23,6 +23,7 @@ SW = r'C:\cygwin\home\kovid\sw'
|
||||
IMAGEMAGICK = os.path.join(SW, 'build', 'ImageMagick-6.7.6',
|
||||
'VisualMagick', 'bin')
|
||||
CRT = r'C:\Microsoft.VC90.CRT'
|
||||
SIGNTOOL = r'C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\signtool.exe'
|
||||
|
||||
VERSION = re.sub('[a-z]\d+', '', __version__)
|
||||
WINVER = VERSION+'.0'
|
||||
|
@ -30,7 +30,7 @@ If there are no windows binaries already compiled for the version of python you
|
||||
|
||||
Run the following command to install python dependencies::
|
||||
|
||||
easy_install --always-unzip -U ipython mechanize pyreadline python-dateutil dnspython cssutils clientform pycrypto
|
||||
easy_install --always-unzip -U mechanize pyreadline python-dateutil dnspython cssutils clientform pycrypto
|
||||
|
||||
Install BeautifulSoup 3.0.x manually into site-packages (3.1.x parses broken HTML very poorly)
|
||||
|
||||
|
@ -4,7 +4,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = u'calibre'
|
||||
numeric_version = (0, 8, 59)
|
||||
numeric_version = (0, 8, 60)
|
||||
__version__ = u'.'.join(map(unicode, numeric_version))
|
||||
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
|
@ -81,6 +81,9 @@ class DBPrefs(dict): # {{{
|
||||
def to_raw(self, val):
|
||||
return json.dumps(val, indent=2, default=to_json)
|
||||
|
||||
def has_setting(self, key):
|
||||
return key in self
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return dict.__getitem__(self, key)
|
||||
@ -102,6 +105,53 @@ class DBPrefs(dict): # {{{
|
||||
def set(self, key, val):
|
||||
self.__setitem__(key, val)
|
||||
|
||||
def get_namespaced(self, namespace, key, default=None):
|
||||
key = u'namespaced:%s:%s'%(namespace, key)
|
||||
try:
|
||||
return dict.__getitem__(self, key)
|
||||
except KeyError:
|
||||
return default
|
||||
|
||||
def set_namespaced(self, namespace, key, val):
|
||||
if u':' in key: raise KeyError('Colons are not allowed in keys')
|
||||
if u':' in namespace: raise KeyError('Colons are not allowed in'
|
||||
' the namespace')
|
||||
key = u'namespaced:%s:%s'%(namespace, key)
|
||||
self[key] = val
|
||||
|
||||
def write_serialized(self, library_path):
|
||||
try:
|
||||
to_filename = os.path.join(library_path, 'metadata_db_prefs_backup.json')
|
||||
with open(to_filename, "wb") as f:
|
||||
f.write(json.dumps(self, indent=2, default=to_json))
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
@classmethod
|
||||
def read_serialized(cls, library_path, recreate_prefs=False):
|
||||
try:
|
||||
from_filename = os.path.join(library_path,
|
||||
'metadata_db_prefs_backup.json')
|
||||
with open(from_filename, "rb") as f:
|
||||
d = json.load(f, object_hook=from_json)
|
||||
if not recreate_prefs:
|
||||
return d
|
||||
cls.clear()
|
||||
cls.db.conn.execute('DELETE FROM preferences')
|
||||
for k,v in d.iteritems():
|
||||
raw = cls.to_raw(v)
|
||||
cls.db.conn.execute(
|
||||
'INSERT INTO preferences (key,val) VALUES (?,?)', (k, raw))
|
||||
cls.db.conn.commit()
|
||||
cls.clear()
|
||||
cls.update(d)
|
||||
return d
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
raise
|
||||
return None
|
||||
# }}}
|
||||
|
||||
# Extra collators {{{
|
||||
@ -350,6 +400,23 @@ class DB(object):
|
||||
defs['gui_restriction'] = defs['cs_restriction'] = ''
|
||||
defs['categories_using_hierarchy'] = []
|
||||
defs['column_color_rules'] = []
|
||||
defs['grouped_search_make_user_categories'] = []
|
||||
defs['similar_authors_search_key'] = 'authors'
|
||||
defs['similar_authors_match_kind'] = 'match_any'
|
||||
defs['similar_publisher_search_key'] = 'publisher'
|
||||
defs['similar_publisher_match_kind'] = 'match_any'
|
||||
defs['similar_tags_search_key'] = 'tags'
|
||||
defs['similar_tags_match_kind'] = 'match_all'
|
||||
defs['similar_series_search_key'] = 'series'
|
||||
defs['similar_series_match_kind'] = 'match_any'
|
||||
defs['book_display_fields'] = [
|
||||
('title', False), ('authors', True), ('formats', True),
|
||||
('series', True), ('identifiers', True), ('tags', True),
|
||||
('path', True), ('publisher', False), ('rating', False),
|
||||
('author_sort', False), ('sort', False), ('timestamp', False),
|
||||
('uuid', False), ('comments', True), ('id', False), ('pubdate', False),
|
||||
('last_modified', False), ('size', False), ('languages', False),
|
||||
]
|
||||
|
||||
# Migrate the bool tristate tweak
|
||||
defs['bools_are_tristate'] = \
|
||||
|
@ -60,6 +60,11 @@ Run an embedded python interpreter.
|
||||
'editing tools, and then rebuilds the file from the edited HTML. '
|
||||
'Makes no additional changes to the HTML, unlike a full calibre '
|
||||
'conversion).')
|
||||
parser.add_option('-s', '--shutdown-running-calibre', default=False,
|
||||
action='store_true',
|
||||
help=_('Cause a running calibre instance, if any, to be'
|
||||
' shutdown. Note that if there are running jobs, they '
|
||||
'will be silently aborted, so use with care.'))
|
||||
|
||||
parser.add_option('--test-build', help='Test binary modules in build',
|
||||
action='store_true', default=False)
|
||||
@ -258,6 +263,9 @@ def main(args=sys.argv):
|
||||
elif opts.test_build:
|
||||
from calibre.test_build import test
|
||||
test()
|
||||
elif opts.shutdown_running_calibre:
|
||||
from calibre.gui2.main import shutdown_other
|
||||
shutdown_other()
|
||||
else:
|
||||
from calibre import ipython
|
||||
ipython()
|
||||
|
@ -41,6 +41,7 @@ class ANDROID(USBMS):
|
||||
0xca9 : HTC_BCDS,
|
||||
0xcac : HTC_BCDS,
|
||||
0xccf : HTC_BCDS,
|
||||
0xce5 : HTC_BCDS,
|
||||
0x2910 : HTC_BCDS,
|
||||
0xff9 : HTC_BCDS + [0x9999],
|
||||
},
|
||||
@ -210,7 +211,7 @@ class ANDROID(USBMS):
|
||||
'XT910', 'BOOK_A10', 'USB_2.0_DRIVER', 'I9100T', 'P999DW',
|
||||
'KTABLET_PC', 'INGENIC', 'GT-I9001_CARD', 'USB_2.0_DRIVER',
|
||||
'GT-S5830L_CARD', 'UNIVERSE', 'XT875', 'PRO', '.KOBO_VOX',
|
||||
'THINKPAD_TABLET', 'SGH-T989']
|
||||
'THINKPAD_TABLET', 'SGH-T989', 'YP-G70']
|
||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||
'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
|
||||
|
@ -606,11 +606,11 @@ class KOBO(USBMS):
|
||||
self.report_progress(1.0, _('Removing books from device...'))
|
||||
from calibre.devices.errors import UserFeedback
|
||||
raise UserFeedback(_("Kobo database version unsupported - See details"),
|
||||
_('Your Kobo is running an updated firmware/database version '
|
||||
_('Your Kobo is running an updated firmware/database version. '
|
||||
'As Calibre has not been updated, database editing is disabled. '
|
||||
'You can enable support for your Kobo in plugin preferences. '
|
||||
'Doing so may require you to perform a factory reset. '
|
||||
'before selecting the "Attempt to support newer firmware" option '
|
||||
'Before selecting the "Attempt to support newer firmware" option '
|
||||
'you should be familiar with restoring your Kobo to factory defaults.'),
|
||||
UserFeedback.WARN)
|
||||
|
||||
|
@ -19,9 +19,9 @@ class TECLAST_K3(USBMS):
|
||||
PRODUCT_ID = [0x3203]
|
||||
BCD = [0x0000, 0x0100]
|
||||
|
||||
VENDOR_NAME = ['TECLAST', 'IMAGIN']
|
||||
VENDOR_NAME = ['TECLAST', 'IMAGIN', 'RK28XX']
|
||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['DIGITAL_PLAYER', 'TL-K5',
|
||||
'EREADER']
|
||||
'EREADER', 'USB-MSC']
|
||||
|
||||
MAIN_MEMORY_VOLUME_LABEL = 'K3 Main Memory'
|
||||
STORAGE_CARD_VOLUME_LABEL = 'K3 Storage Card'
|
||||
|
@ -127,12 +127,13 @@ class Device(DeviceConfig, DevicePlugin):
|
||||
if not prefix:
|
||||
return 0, 0
|
||||
prefix = prefix[:-1]
|
||||
import win32file
|
||||
import win32file, winerror
|
||||
try:
|
||||
sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters = \
|
||||
win32file.GetDiskFreeSpace(prefix)
|
||||
except Exception as err:
|
||||
if getattr(err, 'args', [None])[0] == 21: # Disk not ready
|
||||
if getattr(err, 'args', [None])[0] == winerror.ERROR_NOT_READY:
|
||||
# Disk not ready
|
||||
time.sleep(3)
|
||||
sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters = \
|
||||
win32file.GetDiskFreeSpace(prefix)
|
||||
|
@ -10,10 +10,15 @@ __docformat__ = 'restructuredtext en'
|
||||
import re, codecs
|
||||
|
||||
ENCODING_PATS = [
|
||||
# XML declaration
|
||||
re.compile(r'<\?[^<>]+encoding\s*=\s*[\'"](.*?)[\'"][^<>]*>',
|
||||
re.IGNORECASE),
|
||||
# HTML 4 Pragma directive
|
||||
re.compile(r'''<meta\s+?[^<>]*?content\s*=\s*['"][^'"]*?charset=([-_a-z0-9]+)[^'"]*?['"][^<>]*>''',
|
||||
re.IGNORECASE),
|
||||
# HTML 5 charset
|
||||
re.compile(r'''<meta\s+charset=['"]([-_a-z0-9]+)['"][^<>]*>''',
|
||||
re.IGNORECASE),
|
||||
]
|
||||
ENTITY_PATTERN = re.compile(r'&(\S+?);')
|
||||
|
||||
|
@ -37,6 +37,11 @@ class HTMLZOutput(OutputFormatPlugin):
|
||||
'external: Use an external CSS file that is linked in the document.\n'
|
||||
'inline: Place the CSS in the head section of the document.'
|
||||
)),
|
||||
OptionRecommendation(name='htmlz_title_filename',
|
||||
recommended_value=False, level=OptionRecommendation.LOW,
|
||||
help=_('If set this option causes the file name of the html file'
|
||||
' inside the htmlz archive to be based on the book title.')
|
||||
),
|
||||
])
|
||||
|
||||
def convert(self, oeb_book, output_path, input_plugin, opts, log):
|
||||
@ -44,6 +49,7 @@ class HTMLZOutput(OutputFormatPlugin):
|
||||
from calibre.ebooks.oeb.base import OEB_IMAGES, SVG_MIME
|
||||
from calibre.ebooks.metadata.opf2 import OPF, metadata_to_opf
|
||||
from calibre.utils.zipfile import ZipFile
|
||||
from calibre.utils.filenames import ascii_filename
|
||||
|
||||
# HTML
|
||||
if opts.htmlz_css_type == 'inline':
|
||||
@ -59,7 +65,10 @@ class HTMLZOutput(OutputFormatPlugin):
|
||||
htmlizer = OEB2HTMLizer(log)
|
||||
html = htmlizer.oeb2html(oeb_book, opts)
|
||||
|
||||
with open(os.path.join(tdir, u'index.html'), 'wb') as tf:
|
||||
fname = u'index'
|
||||
if opts.htmlz_title_filename:
|
||||
fname = ascii_filename(unicode(oeb_book.metadata.title[0]))
|
||||
with open(os.path.join(tdir, fname+u'.html'), 'wb') as tf:
|
||||
tf.write(html)
|
||||
|
||||
# CSS
|
||||
|
@ -9,6 +9,8 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
import re, os
|
||||
|
||||
from calibre.ebooks.chardet import strip_encoding_declarations
|
||||
|
||||
def update_internal_links(mobi8_reader):
|
||||
# need to update all links that are internal which
|
||||
# are based on positions within the xhtml files **BEFORE**
|
||||
@ -324,6 +326,8 @@ def expand_mobi8_markup(mobi8_reader, resource_map, log):
|
||||
for i, part in enumerate(parts):
|
||||
pi = mobi8_reader.partinfo[i]
|
||||
with open(os.path.join(pi.type, pi.filename), 'wb') as f:
|
||||
part = strip_encoding_declarations(part)
|
||||
part = part.replace('<head>', '<head><meta charset="UTF-8"/>', 1)
|
||||
f.write(part.encode('utf-8'))
|
||||
spine.append(f.name)
|
||||
|
||||
|
@ -89,14 +89,6 @@ gprefs.defaults['tags_browser_partition_method'] = 'first letter'
|
||||
gprefs.defaults['tags_browser_collapse_at'] = 100
|
||||
gprefs.defaults['tag_browser_dont_collapse'] = []
|
||||
gprefs.defaults['edit_metadata_single_layout'] = 'default'
|
||||
gprefs.defaults['book_display_fields'] = [
|
||||
('title', False), ('authors', True), ('formats', True),
|
||||
('series', True), ('identifiers', True), ('tags', True),
|
||||
('path', True), ('publisher', False), ('rating', False),
|
||||
('author_sort', False), ('sort', False), ('timestamp', False),
|
||||
('uuid', False), ('comments', True), ('id', False), ('pubdate', False),
|
||||
('last_modified', False), ('size', False), ('languages', False),
|
||||
]
|
||||
gprefs.defaults['default_author_link'] = 'http://en.wikipedia.org/w/index.php?search={author}'
|
||||
gprefs.defaults['preserve_date_on_ctl'] = True
|
||||
gprefs.defaults['cb_fullscreen'] = False
|
||||
|
@ -74,9 +74,10 @@ class ShareConnMenu(QMenu): # {{{
|
||||
action=self.toggle_server_action, group=gr)
|
||||
|
||||
def server_state_changed(self, running):
|
||||
from calibre.utils.mdns import get_external_ip
|
||||
text = _('Start Content Server')
|
||||
if running:
|
||||
text = _('Stop Content Server')
|
||||
text = _('Stop Content Server') + ' [%s]'%get_external_ip()
|
||||
self.toggle_server_action.setText(text)
|
||||
|
||||
def build_email_entries(self, sync_menu):
|
||||
|
@ -84,7 +84,17 @@ def render_html(mi, css, vertical, widget, all_fields=False): # {{{
|
||||
return ans
|
||||
|
||||
def get_field_list(fm, use_defaults=False):
|
||||
src = gprefs.defaults if use_defaults else gprefs
|
||||
from calibre.gui2.ui import get_gui
|
||||
db = get_gui().current_db
|
||||
if use_defaults:
|
||||
src = db.prefs.defaults
|
||||
else:
|
||||
old_val = gprefs.get('book_display_fields', None)
|
||||
if old_val is not None and not db.prefs.has_setting(
|
||||
'book_display_fields'):
|
||||
src = gprefs
|
||||
else:
|
||||
src = db.prefs
|
||||
fieldlist = list(src['book_display_fields'])
|
||||
names = frozenset([x[0] for x in fieldlist])
|
||||
for field in fm.displayable_field_keys():
|
||||
|
@ -5,6 +5,10 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
WARNING: The code in this module is deprecated. Use complete2.py instead. This
|
||||
code remains here for legacy plugin support.
|
||||
'''
|
||||
|
||||
from PyQt4.Qt import (QLineEdit, QAbstractListModel, Qt,
|
||||
QApplication, QCompleter)
|
||||
|
@ -9,8 +9,9 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
import weakref
|
||||
|
||||
import sip
|
||||
from PyQt4.Qt import (QLineEdit, QAbstractListModel, Qt, pyqtSignal, QObject,
|
||||
QApplication, QListView, QPoint)
|
||||
QApplication, QListView, QPoint, QModelIndex)
|
||||
|
||||
from calibre.utils.icu import sort_key, primary_startswith
|
||||
from calibre.gui2 import NONE
|
||||
@ -82,9 +83,12 @@ class Completer(QListView): # {{{
|
||||
self.entered.connect(self.item_entered)
|
||||
self.activated.connect(self.item_chosen)
|
||||
self.pressed.connect(self.item_chosen)
|
||||
self.eat_focus_out = True
|
||||
self.installEventFilter(self)
|
||||
|
||||
def hide(self):
|
||||
self.setCurrentIndex(QModelIndex())
|
||||
QListView.hide(self)
|
||||
|
||||
def item_chosen(self, index):
|
||||
if not self.isVisible(): return
|
||||
self.hide()
|
||||
@ -109,7 +113,7 @@ class Completer(QListView): # {{{
|
||||
if c.isValid():
|
||||
r = c.row()
|
||||
else:
|
||||
r = 0
|
||||
r = self.model().rowCount() if previous else -1
|
||||
r = r + (-1 if previous else 1)
|
||||
index = self.model().index(r % self.model().rowCount())
|
||||
self.setCurrentIndex(index)
|
||||
@ -120,7 +124,7 @@ class Completer(QListView): # {{{
|
||||
if index is not None and index.isValid():
|
||||
self.setCurrentIndex(index)
|
||||
|
||||
def popup(self):
|
||||
def popup(self, select_first=True):
|
||||
p = self
|
||||
m = p.model()
|
||||
widget = self.completer_widget()
|
||||
@ -153,7 +157,8 @@ class Completer(QListView): # {{{
|
||||
|
||||
p.setGeometry(pos.x(), pos.y(), w, h)
|
||||
|
||||
if (not self.currentIndex().isValid() and self.model().rowCount() > 0):
|
||||
if (select_first and not self.currentIndex().isValid() and
|
||||
self.model().rowCount() > 0):
|
||||
self.setCurrentIndex(self.model().index(0))
|
||||
|
||||
if not p.isVisible():
|
||||
@ -162,12 +167,9 @@ class Completer(QListView): # {{{
|
||||
def eventFilter(self, obj, e):
|
||||
'Redirect key presses from the popup to the widget'
|
||||
widget = self.completer_widget()
|
||||
if widget is None:
|
||||
if widget is None or sip.isdeleted(widget):
|
||||
return False
|
||||
etype = e.type()
|
||||
if self.eat_focus_out and widget is obj and etype == e.FocusOut:
|
||||
if self.isVisible():
|
||||
return True
|
||||
if obj is not self:
|
||||
return QObject.eventFilter(self, obj, e)
|
||||
|
||||
@ -181,8 +183,14 @@ class Completer(QListView): # {{{
|
||||
self.hide()
|
||||
e.accept()
|
||||
return True
|
||||
if key in (Qt.Key_Enter, Qt.Key_Return):
|
||||
if not self.currentIndex().isValid():
|
||||
self.hide()
|
||||
e.accept()
|
||||
return True
|
||||
return False
|
||||
if key in (Qt.Key_End, Qt.Key_Home, Qt.Key_Up, Qt.Key_Down,
|
||||
Qt.Key_PageUp, Qt.Key_PageDown, Qt.Key_Enter, Qt.Key_Return):
|
||||
Qt.Key_PageUp, Qt.Key_PageDown):
|
||||
# Let the list view handle these keys
|
||||
return False
|
||||
if key in (Qt.Key_Tab, Qt.Key_Backtab):
|
||||
@ -190,9 +198,9 @@ class Completer(QListView): # {{{
|
||||
e.accept()
|
||||
return True
|
||||
# Send to widget
|
||||
self.eat_focus_out = False
|
||||
widget.eat_focus_out = False
|
||||
widget.keyPressEvent(e)
|
||||
self.eat_focus_out = True
|
||||
widget.eat_focus_out = True
|
||||
if not widget.hasFocus():
|
||||
# Widget lost focus hide the popup
|
||||
self.hide()
|
||||
@ -206,7 +214,6 @@ class Completer(QListView): # {{{
|
||||
return False
|
||||
# }}}
|
||||
|
||||
|
||||
class LineEdit(QLineEdit, LineEditECM):
|
||||
'''
|
||||
A line edit that completes on multiple items separated by a
|
||||
@ -233,7 +240,6 @@ class LineEdit(QLineEdit, LineEditECM):
|
||||
type=Qt.QueuedConnection)
|
||||
self.mcompleter.relayout_needed.connect(self.relayout)
|
||||
self.mcompleter.setFocusProxy(completer_widget)
|
||||
completer_widget.installEventFilter(self.mcompleter)
|
||||
self.textEdited.connect(self.text_edited)
|
||||
self.no_popup = False
|
||||
|
||||
@ -260,7 +266,7 @@ class LineEdit(QLineEdit, LineEditECM):
|
||||
|
||||
# }}}
|
||||
|
||||
def complete(self, show_all=False):
|
||||
def complete(self, show_all=False, select_first=True):
|
||||
orig = None
|
||||
if show_all:
|
||||
orig = self.mcompleter.model().current_prefix
|
||||
@ -268,7 +274,7 @@ class LineEdit(QLineEdit, LineEditECM):
|
||||
if not self.mcompleter.model().current_items:
|
||||
self.mcompleter.hide()
|
||||
return
|
||||
self.mcompleter.popup()
|
||||
self.mcompleter.popup(select_first=select_first)
|
||||
self.mcompleter.scroll_to(orig)
|
||||
|
||||
def relayout(self):
|
||||
@ -277,7 +283,8 @@ class LineEdit(QLineEdit, LineEditECM):
|
||||
def text_edited(self, *args):
|
||||
if self.no_popup: return
|
||||
self.update_completions()
|
||||
self.complete()
|
||||
select_first = len(self.mcompleter.model().current_prefix) > 0
|
||||
self.complete(select_first=select_first)
|
||||
|
||||
def update_completions(self):
|
||||
' Update the list of completions '
|
||||
@ -329,6 +336,8 @@ class EditWithComplete(EnComboBox):
|
||||
EnComboBox.__init__(self, *args)
|
||||
self.setLineEdit(LineEdit(self, completer_widget=self))
|
||||
self.setCompleter(None)
|
||||
self.eat_focus_out = True
|
||||
self.installEventFilter(self)
|
||||
|
||||
# Interface {{{
|
||||
def showPopup(self):
|
||||
@ -377,6 +386,21 @@ class EditWithComplete(EnComboBox):
|
||||
def textChanged(self):
|
||||
return self.lineEdit().textChanged
|
||||
|
||||
def clear(self):
|
||||
self.lineEdit().clear()
|
||||
EnComboBox.clear(self)
|
||||
|
||||
def eventFilter(self, obj, e):
|
||||
try:
|
||||
c = self.lineEdit().mcompleter
|
||||
except AttributeError:
|
||||
return False
|
||||
etype = e.type()
|
||||
if self.eat_focus_out and self is obj and etype == e.FocusOut:
|
||||
if c.isVisible():
|
||||
return True
|
||||
return EnComboBox.eventFilter(self, obj, e)
|
||||
|
||||
if __name__ == '__main__':
|
||||
from PyQt4.Qt import QDialog, QVBoxLayout
|
||||
app = QApplication([])
|
||||
|
@ -17,7 +17,8 @@ class PluginWidget(Widget, Ui_Form):
|
||||
ICON = I('mimetypes/html.png')
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, ['htmlz_css_type', 'htmlz_class_style'])
|
||||
Widget.__init__(self, parent, ['htmlz_css_type', 'htmlz_class_style',
|
||||
'htmlz_title_filename'])
|
||||
self.db, self.book_id = db, book_id
|
||||
for x in get_option('htmlz_css_type').option.choices:
|
||||
self.opt_htmlz_css_type.addItem(x)
|
||||
|
@ -14,7 +14,7 @@
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="0">
|
||||
<item row="3" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
@ -27,6 +27,9 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="opt_htmlz_class_style"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
@ -51,8 +54,12 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="opt_htmlz_class_style"/>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_htmlz_title_filename">
|
||||
<property name="text">
|
||||
<string>Use book &title as the filename for the HTML file inside the archive</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -313,21 +313,39 @@ def cant_start(msg=_('If you are sure it is not running')+', ',
|
||||
|
||||
raise SystemExit(1)
|
||||
|
||||
def communicate(opts, args):
|
||||
t = RC()
|
||||
def build_pipe(print_error=True):
|
||||
t = RC(print_error=print_error)
|
||||
t.start()
|
||||
time.sleep(3)
|
||||
if not t.done:
|
||||
t.join(3.0)
|
||||
if t.is_alive():
|
||||
if iswindows():
|
||||
cant_start()
|
||||
else:
|
||||
f = os.path.expanduser('~/.calibre_calibre GUI.lock')
|
||||
cant_start(what=_('try deleting the file')+': '+f)
|
||||
raise SystemExit(1)
|
||||
return t
|
||||
|
||||
if opts.shutdown_running_calibre:
|
||||
t.conn.send('shutdown:')
|
||||
def shutdown_other(rc=None):
|
||||
if rc is None:
|
||||
rc = build_pipe(print_error=False)
|
||||
if rc.conn is None:
|
||||
prints(_('No running calibre found'))
|
||||
return # No running instance found
|
||||
from calibre.utils.lock import singleinstance
|
||||
rc.conn.send('shutdown:')
|
||||
prints(_('Shutdown command sent, waiting for shutdown...'))
|
||||
while not singleinstance('calibre GUI'):
|
||||
for i in xrange(50):
|
||||
if singleinstance('calibre GUI'):
|
||||
return
|
||||
time.sleep(0.1)
|
||||
prints(_('Failed to shutdown running calibre instance'))
|
||||
raise SystemExit(1)
|
||||
|
||||
def communicate(opts, args):
|
||||
t = build_pipe()
|
||||
if opts.shutdown_running_calibre:
|
||||
shutdown_other(t)
|
||||
else:
|
||||
if len(args) > 1:
|
||||
args[1] = os.path.abspath(args[1])
|
||||
@ -335,7 +353,6 @@ def communicate(opts, args):
|
||||
t.conn.close()
|
||||
raise SystemExit(0)
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
gui_debug = None
|
||||
if args[0] == '__CALIBRE_GUI_DEBUG__':
|
||||
|
@ -75,7 +75,7 @@ class DisplayedFields(QAbstractListModel): # {{{
|
||||
|
||||
def commit(self):
|
||||
if self.changed:
|
||||
gprefs['book_display_fields'] = self.fields
|
||||
self.db.prefs['book_display_fields'] = self.fields
|
||||
|
||||
def move(self, idx, delta):
|
||||
row = idx.row() + delta
|
||||
|
@ -184,7 +184,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
self.opt_grouped_search_make_user_categories.update_items_cache(terms)
|
||||
self.gst_names.blockSignals(True)
|
||||
self.gst_names.clear()
|
||||
print (1111, self.gst_names)
|
||||
self.gst_names.addItem('', '')
|
||||
for t in terms:
|
||||
self.gst_names.addItem(t, t)
|
||||
|
@ -6,7 +6,6 @@ __license__ = 'GPL 3'
|
||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import random
|
||||
import urllib
|
||||
from contextlib import closing
|
||||
|
||||
|
@ -11,7 +11,6 @@ import cPickle, os
|
||||
|
||||
from PyQt4.Qt import QDialog, QProgressDialog, QString, QTimer
|
||||
|
||||
from calibre.constants import DEBUG
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.gui2 import warning_dialog, question_dialog
|
||||
from calibre.gui2.convert.single import NoSupportedInputFormats
|
||||
|
@ -51,6 +51,8 @@ def config(defaults=None):
|
||||
help=_('The amount by which to change the font size when clicking'
|
||||
' the font larger/smaller buttons. Should be a number between '
|
||||
'0 and 1.'))
|
||||
c.add_opt('fullscreen_clock', default=False, action='store_true',
|
||||
help=_('Show a clock in fullscreen mode.'))
|
||||
|
||||
fonts = c.add_group('FONTS', _('Font options'))
|
||||
fonts('serif_family', default='Times New Roman' if iswindows else 'Liberation Serif',
|
||||
@ -117,6 +119,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
self.hyphenate.setVisible(False)
|
||||
self.hyphenate_default_lang.setVisible(False)
|
||||
self.hyphenate_label.setVisible(False)
|
||||
self.opt_fullscreen_clock.setChecked(opts.fullscreen_clock)
|
||||
|
||||
def accept(self, *args):
|
||||
if self.shortcut_config.is_editing:
|
||||
@ -148,6 +151,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
str(self.hyphenate_default_lang.itemData(idx).toString()))
|
||||
c.set('line_scrolling_stops_on_pagebreaks',
|
||||
self.opt_line_scrolling_stops_on_pagebreaks.isChecked())
|
||||
c.set('fullscreen_clock', self.opt_fullscreen_clock.isChecked())
|
||||
return QDialog.accept(self, *args)
|
||||
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>479</width>
|
||||
<width>839</width>
|
||||
<height>630</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -167,20 +167,6 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_remember_window_size">
|
||||
<property name="text">
|
||||
<string>Remember last used &window size and layout</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_remember_current_page">
|
||||
<property name="text">
|
||||
<string>Remember the &current page when quitting</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="hyphenate">
|
||||
<property name="text">
|
||||
@ -205,13 +191,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_fit_images">
|
||||
<property name="text">
|
||||
<string>&Resize images larger than the viewer window (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
@ -247,13 +226,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_wheel_flips_pages">
|
||||
<property name="text">
|
||||
<string>Mouse &wheel flips pages</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="max_fs_width">
|
||||
<property name="toolTip">
|
||||
@ -301,13 +273,48 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0" colspan="2">
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="opt_fit_images">
|
||||
<property name="text">
|
||||
<string>&Resize images larger than the viewer window (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QCheckBox" name="opt_remember_window_size">
|
||||
<property name="text">
|
||||
<string>Remember last used &window size and layout</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QCheckBox" name="opt_wheel_flips_pages">
|
||||
<property name="text">
|
||||
<string>Mouse &wheel flips pages</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QCheckBox" name="opt_remember_current_page">
|
||||
<property name="text">
|
||||
<string>Remember the &current page when quitting</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QCheckBox" name="opt_line_scrolling_stops_on_pagebreaks">
|
||||
<property name="text">
|
||||
<string>Line &scrolling stops at page breaks</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="opt_fullscreen_clock">
|
||||
<property name="text">
|
||||
<string>Show &clock in full screen mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -134,6 +134,7 @@ class Document(QWebPage): # {{{
|
||||
screen_width = QApplication.desktop().screenGeometry().width()
|
||||
# Leave some space for the scrollbar and some border
|
||||
self.max_fs_width = min(opts.max_fs_width, screen_width-50)
|
||||
self.fullscreen_clock = opts.fullscreen_clock
|
||||
|
||||
def fit_images(self):
|
||||
if self.do_fit_images and not self.in_paged_mode:
|
||||
@ -193,6 +194,14 @@ class Document(QWebPage): # {{{
|
||||
self.read_anchor_positions(use_cache=False)
|
||||
self.first_load = False
|
||||
|
||||
def colors(self):
|
||||
self.javascript('''
|
||||
bs = getComputedStyle(document.body);
|
||||
py_bridge.value = [bs.backgroundColor, bs.color]
|
||||
''')
|
||||
ans = self.bridge_value
|
||||
return (ans if isinstance(ans, list) else ['white', 'black'])
|
||||
|
||||
def read_anchor_positions(self, use_cache=True):
|
||||
self.bridge_value = tuple(self.index_anchors)
|
||||
self.javascript(u'''
|
||||
|
@ -6,9 +6,10 @@ from functools import partial
|
||||
from threading import Thread
|
||||
|
||||
from PyQt4.Qt import (QApplication, Qt, QIcon, QTimer, QByteArray, QSize,
|
||||
QDoubleSpinBox, QLabel, QTextBrowser, QPropertyAnimation, QPainter,
|
||||
QBrush, QColor, pyqtSignal, QUrl, QRegExpValidator, QRegExp, QLineEdit,
|
||||
QToolButton, QMenu, QInputDialog, QAction, QKeySequence, QModelIndex)
|
||||
QTime, QDoubleSpinBox, QLabel, QTextBrowser, QPropertyAnimation,
|
||||
QPainter, QBrush, QColor, pyqtSignal, QUrl, QRegExpValidator, QRegExp,
|
||||
QLineEdit, QToolButton, QMenu, QInputDialog, QAction, QKeySequence,
|
||||
QModelIndex)
|
||||
|
||||
from calibre.gui2.viewer.main_ui import Ui_EbookViewer
|
||||
from calibre.gui2.viewer.printing import Printing
|
||||
@ -288,6 +289,23 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
self.addAction(self.toggle_toolbar_action)
|
||||
self.full_screen_label_anim = QPropertyAnimation(
|
||||
self.full_screen_label, 'size')
|
||||
self.clock_label = QLabel('99:99', self)
|
||||
self.clock_label.setVisible(False)
|
||||
self.clock_label.setFocusPolicy(Qt.NoFocus)
|
||||
self.clock_label_style = '''
|
||||
QLabel {
|
||||
text-align: right;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-radius: 8px;
|
||||
background-color: %s;
|
||||
color: %s;
|
||||
font-family: monospace;
|
||||
font-size: larger;
|
||||
padding: 5px;
|
||||
}'''
|
||||
self.clock_timer = QTimer(self)
|
||||
self.clock_timer.timeout.connect(self.update_clock)
|
||||
self.esc_full_screen_action = a = QAction(self)
|
||||
self.addAction(a)
|
||||
a.setShortcut(Qt.Key_Escape)
|
||||
@ -454,9 +472,29 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
a.start()
|
||||
QTimer.singleShot(2750, self.full_screen_label.hide)
|
||||
self.view.document.switch_to_fullscreen_mode()
|
||||
if self.view.document.fullscreen_clock:
|
||||
self.show_clock()
|
||||
|
||||
def show_clock(self):
|
||||
self.clock_label.setVisible(True)
|
||||
self.clock_label.setText('99:99 AA')
|
||||
self.clock_timer.start(1000)
|
||||
self.clock_label.setStyleSheet(self.clock_label_style%
|
||||
tuple(self.view.document.colors()))
|
||||
self.clock_label.resize(self.clock_label.sizeHint())
|
||||
sw = QApplication.desktop().screenGeometry(self.view)
|
||||
self.clock_label.move(sw.width() - self.vertical_scrollbar.width() - 15
|
||||
- self.clock_label.width(), sw.height() -
|
||||
self.clock_label.height()-10)
|
||||
self.update_clock()
|
||||
|
||||
def update_clock(self):
|
||||
self.clock_label.setText(QTime.currentTime().toString('h:mm a'))
|
||||
|
||||
def showNormal(self):
|
||||
self.view.document.page_position.save()
|
||||
self.clock_label.setVisible(False)
|
||||
self.clock_timer.stop()
|
||||
self.window_mode_changed = 'normal'
|
||||
self.esc_full_screen_action.setEnabled(False)
|
||||
self.tool_bar.setVisible(True)
|
||||
|
@ -251,6 +251,14 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
defs['similar_tags_match_kind'] = 'match_all'
|
||||
defs['similar_series_search_key'] = 'series'
|
||||
defs['similar_series_match_kind'] = 'match_any'
|
||||
defs['book_display_fields'] = [
|
||||
('title', False), ('authors', True), ('formats', True),
|
||||
('series', True), ('identifiers', True), ('tags', True),
|
||||
('path', True), ('publisher', False), ('rating', False),
|
||||
('author_sort', False), ('sort', False), ('timestamp', False),
|
||||
('uuid', False), ('comments', True), ('id', False), ('pubdate', False),
|
||||
('last_modified', False), ('size', False), ('languages', False),
|
||||
]
|
||||
|
||||
# Migrate the bool tristate tweak
|
||||
defs['bools_are_tristate'] = \
|
||||
|
@ -34,6 +34,9 @@ class DBPrefs(dict):
|
||||
def to_raw(self, val):
|
||||
return json.dumps(val, indent=2, default=to_json)
|
||||
|
||||
def has_setting(self, key):
|
||||
return key in self
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return dict.__getitem__(self, key)
|
||||
|
@ -76,15 +76,15 @@ def test_qt():
|
||||
print ('Qt OK!')
|
||||
|
||||
def test_imaging():
|
||||
from calibre.utils.magick.draw import create_canvas, Image
|
||||
im = create_canvas(20, 20, '#ffffff')
|
||||
jpg = im.export('jpg')
|
||||
Image().load(jpg)
|
||||
im.export('png')
|
||||
from calibre.ebooks import calibre_cover
|
||||
data = calibre_cover('test', 'ok')
|
||||
if len(data) > 1000:
|
||||
print ('ImageMagick OK!')
|
||||
else:
|
||||
raise RuntimeError('ImageMagick choked!')
|
||||
from PIL import Image
|
||||
i = Image.open(cStringIO.StringIO(jpg))
|
||||
if i.size != (20, 20):
|
||||
i = Image.open(cStringIO.StringIO(data))
|
||||
if i.size < (20, 20):
|
||||
raise RuntimeError('PIL choked!')
|
||||
print ('PIL OK!')
|
||||
|
||||
@ -94,6 +94,12 @@ def test_unrar():
|
||||
raise RuntimeError('Failed to load libunrar')
|
||||
print ('Unrar OK!')
|
||||
|
||||
def test_icu():
|
||||
from calibre.utils.icu import _icu_not_ok
|
||||
if _icu_not_ok:
|
||||
raise RuntimeError('ICU module not loaded/valid')
|
||||
print ('ICU OK!')
|
||||
|
||||
def test():
|
||||
test_plugins()
|
||||
test_lxml()
|
||||
@ -102,6 +108,7 @@ def test():
|
||||
test_qt()
|
||||
test_imaging()
|
||||
test_unrar()
|
||||
test_icu()
|
||||
if iswindows:
|
||||
test_win32()
|
||||
test_winutil()
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user