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
8045c42af3
@ -28,3 +28,4 @@ nbproject/
|
|||||||
*.userprefs
|
*.userprefs
|
||||||
.project
|
.project
|
||||||
.pydevproject
|
.pydevproject
|
||||||
|
.settings/
|
||||||
|
@ -35,16 +35,17 @@ class NRO(BasicNewsRecipe):
|
|||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
|
|
||||||
(u'National Review', u'http://www.nationalreview.com/index.xml'),
|
(u'National Review', u'http://www.nationalreview.com/articles/feed'),
|
||||||
(u'The Corner', u'http://corner.nationalreview.com/corner.xml'),
|
(u'The Corner', u'http://www.nationalreview.com/corner/feed'),
|
||||||
(u'The Agenda', u'http://agenda.nationalreview.com/agenda.xml'),
|
(u'The Agenda', u'http://www.nationalreview.com/agenda/feed'),
|
||||||
(u'Bench Memos', u'http://bench.nationalreview.com/bench.xml'),
|
(u'Bench Memos', u'http://www.nationalreview.com/bench-memos/feed'),
|
||||||
(u'Campaign Spot', u'http://campaignspot.nationalreview.com/campaignspot.xml'),
|
(u'Campaign Spot', u'http://www.nationalreview.com/campaign-spot/feed'),
|
||||||
(u'Critical Care', u'http://healthcare.nationalreview.com/healthcare.xml'),
|
(u'Battle 10', u'http://www.nationalreview.com/battle10/feed'),
|
||||||
(u'Doctor, Doctor', u'http://www.nationalreview.com/doctor/doctor.xml'),
|
(u'Critical Care', u'http://www.nationalreview.com/critical-condition/feed'),
|
||||||
(u"Kudlow's Money Politic$", u'http://kudlow.nationalreview.com/kudlow.xml'),
|
(u"Kudlow's Money Politic$", u'http://www.nationalreview.com/kudlows-money-politics/feed'),
|
||||||
(u'Media Blog', u'http://media.nationalreview.com/media.xml'),
|
(u'Media Blog', u'http://www.nationalreview.com/media-blog/feed'),
|
||||||
(u'Phi Beta Cons', u'http://phibetacons.nationalreview.com/phibetacons.xml'),
|
(u'Exchequer', u'http://www.nationalreview.com/exchequer/feed'),
|
||||||
(u'Planet Gore', u'http://planetgore.nationalreview.com/planetgore.xml')
|
(u'Phi Beta Cons', u'http://www.nationalreview.com/phi-beta-cons/feed'),
|
||||||
|
(u'Planet Gore', u'http://www.nationalreview.com/planet-gore/feed')
|
||||||
|
|
||||||
]
|
]
|
67
resources/recipes/tmz.recipe
Normal file
67
resources/recipes/tmz.recipe
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class AdvancedUserRecipe1282101454(BasicNewsRecipe):
|
||||||
|
title = 'The TMZ'
|
||||||
|
__author__ = 'Tony Stegall'
|
||||||
|
description = 'Celeb Gossip and News'
|
||||||
|
language = 'en'
|
||||||
|
publisher = 'The TMZ'
|
||||||
|
category = 'news, celebrity, USA'
|
||||||
|
oldest_article = 1
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_javascript = True
|
||||||
|
masthead_url = 'http://t0.gstatic.com/images?q=tbn:t43QkABe_BmaWM:http://www.thetreymoore.com/logos/TMZ%20logo%20(crop).JPG'
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
|
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||||
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
|
'''
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='div' , attrs={'id':['sidebar','print-actions'] })
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
('TOP 20', 'http://www.tmz.com/rss.xml'),
|
||||||
|
('Exclusives', 'http://www.tmz.com/category/exclusives/rss.xml'),
|
||||||
|
('Celeb Justice', 'http://www.tmz.com/category/celebrity-justice/rss.xml'),
|
||||||
|
('Celeb Feuds', 'http://www.tmz.com/category/celebrity-feuds/rss.xml'),
|
||||||
|
('Politix', 'http://www.tmz.com/category/politix/rss.xml'),
|
||||||
|
('Music', 'http://www.tmz.com/category/music/rss.xml'),
|
||||||
|
('Movies', 'http://www.tmz.com/category/movies/rss.xml'),
|
||||||
|
('TV', 'http://www.tmz.com/category/tv/rss.xml'),
|
||||||
|
('Sports', 'http://www.tmz.com/category/TMZsports/rss.xml'),
|
||||||
|
('Hook-Ups', 'http://www.tmz.com/category/hook-ups/rss.xml'),
|
||||||
|
('Beauty', 'http://www.tmz.com/category/beauty/rss.xml'),
|
||||||
|
('Fashion', 'http://www.tmz.com/category/fashion/rss.xml'),
|
||||||
|
('Gossip & Rumor', 'http://www.tmz.com/category/gossip-rumors/rss.xml'),
|
||||||
|
('Hot Mama', 'http://www.tmz.com/category/hot-mamas/rss.xml'),
|
||||||
|
('Party All The Time', 'http://www.tmz.com/category/party-all-the-time/rss.xml'),
|
||||||
|
('Ride Me!', 'http://www.tmz.com/category/ride-me/rss.xml'),
|
||||||
|
('Stars in Heat', 'http://www.tmz.com/category/stars-in-heat/rss.xml'),
|
||||||
|
('Vegas', 'http://www.tmz.com/category/hot-vegas/rss.xml')
|
||||||
|
]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
print_url = url +'print'
|
||||||
|
return print_url
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -48,7 +48,7 @@ class LinuxFreeze(Command):
|
|||||||
'/usr/lib/libsqlite3.so.0',
|
'/usr/lib/libsqlite3.so.0',
|
||||||
'/usr/lib/libsqlite3.so.0',
|
'/usr/lib/libsqlite3.so.0',
|
||||||
'/usr/lib/libmng.so.1',
|
'/usr/lib/libmng.so.1',
|
||||||
'/usr/lib/libpodofo.so.0.8.1',
|
'/usr/lib/libpodofo.so.0.8.2',
|
||||||
'/lib/libz.so.1',
|
'/lib/libz.so.1',
|
||||||
'/lib/libuuid.so.1',
|
'/lib/libuuid.so.1',
|
||||||
'/usr/lib/libtiff.so.5',
|
'/usr/lib/libtiff.so.5',
|
||||||
|
@ -401,7 +401,7 @@ class Py2App(object):
|
|||||||
@flush
|
@flush
|
||||||
def add_podofo(self):
|
def add_podofo(self):
|
||||||
info('\nAdding PoDoFo')
|
info('\nAdding PoDoFo')
|
||||||
pdf = join(SW, 'lib', 'libpodofo.0.8.1.dylib')
|
pdf = join(SW, 'lib', 'libpodofo.0.8.2.dylib')
|
||||||
self.install_dylib(pdf)
|
self.install_dylib(pdf)
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
|
@ -230,14 +230,14 @@ SET(WANT_LIB64 FALSE)
|
|||||||
SET(PODOFO_BUILD_SHARED TRUE)
|
SET(PODOFO_BUILD_SHARED TRUE)
|
||||||
SET(PODOFO_BUILD_STATIC FALSE)
|
SET(PODOFO_BUILD_STATIC FALSE)
|
||||||
|
|
||||||
cp build/podofo/build/src/Release/podofo.dll bin/
|
cp build/podofo-*/build/src/Release/podofo.dll bin/
|
||||||
cp build/podofo/build/src/Release/podofo.lib lib/
|
cp build/podofo-*/build/src/Release/podofo.lib lib/
|
||||||
cp build/podofo/build/src/Release/podofo.exp lib/
|
cp build/podofo-*/build/src/Release/podofo.exp lib/
|
||||||
|
|
||||||
cp build/podofo/build/podofo_config.h include/podofo/
|
cp build/podofo-*/build/podofo_config.h include/podofo/
|
||||||
cp -r build/podofo/src/* include/podofo/
|
cp -r build/podofo-*/src/* include/podofo/
|
||||||
|
|
||||||
You have to use >0.8.1 (>= revision 1269)
|
You have to use >=0.8.2
|
||||||
|
|
||||||
The following patch (against -r1269) was required to get it to compile:
|
The following patch (against -r1269) was required to get it to compile:
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from calibre.constants import numeric_version
|
|||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
|
|
||||||
|
|
||||||
class Plugin(object):
|
class Plugin(object): # {{{
|
||||||
'''
|
'''
|
||||||
A calibre plugin. Useful members include:
|
A calibre plugin. Useful members include:
|
||||||
|
|
||||||
@ -147,9 +147,9 @@ class Plugin(object):
|
|||||||
if hasattr(it, '__exit__'):
|
if hasattr(it, '__exit__'):
|
||||||
it.__exit__(*args)
|
it.__exit__(*args)
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class FileTypePlugin(Plugin): # {{{
|
||||||
class FileTypePlugin(Plugin):
|
|
||||||
'''
|
'''
|
||||||
A plugin that is associated with a particular set of file types.
|
A plugin that is associated with a particular set of file types.
|
||||||
'''
|
'''
|
||||||
@ -191,7 +191,9 @@ class FileTypePlugin(Plugin):
|
|||||||
# Default implementation does nothing
|
# Default implementation does nothing
|
||||||
return path_to_ebook
|
return path_to_ebook
|
||||||
|
|
||||||
class MetadataReaderPlugin(Plugin):
|
# }}}
|
||||||
|
|
||||||
|
class MetadataReaderPlugin(Plugin): # {{{
|
||||||
'''
|
'''
|
||||||
A plugin that implements reading metadata from a set of file types.
|
A plugin that implements reading metadata from a set of file types.
|
||||||
'''
|
'''
|
||||||
@ -219,8 +221,9 @@ class MetadataReaderPlugin(Plugin):
|
|||||||
:return: A :class:`calibre.ebooks.metadata.MetaInformation` object
|
:return: A :class:`calibre.ebooks.metadata.MetaInformation` object
|
||||||
'''
|
'''
|
||||||
return None
|
return None
|
||||||
|
# }}}
|
||||||
|
|
||||||
class MetadataWriterPlugin(Plugin):
|
class MetadataWriterPlugin(Plugin): # {{{
|
||||||
'''
|
'''
|
||||||
A plugin that implements reading metadata from a set of file types.
|
A plugin that implements reading metadata from a set of file types.
|
||||||
'''
|
'''
|
||||||
@ -249,7 +252,9 @@ class MetadataWriterPlugin(Plugin):
|
|||||||
'''
|
'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class CatalogPlugin(Plugin):
|
# }}}
|
||||||
|
|
||||||
|
class CatalogPlugin(Plugin): # {{{
|
||||||
'''
|
'''
|
||||||
A plugin that implements a catalog generator.
|
A plugin that implements a catalog generator.
|
||||||
'''
|
'''
|
||||||
@ -352,7 +357,9 @@ class CatalogPlugin(Plugin):
|
|||||||
raise NotImplementedError('CatalogPlugin.generate_catalog() default '
|
raise NotImplementedError('CatalogPlugin.generate_catalog() default '
|
||||||
'method, should be overridden in subclass')
|
'method, should be overridden in subclass')
|
||||||
|
|
||||||
class InterfaceActionBase(Plugin):
|
# }}}
|
||||||
|
|
||||||
|
class InterfaceActionBase(Plugin): # {{{
|
||||||
|
|
||||||
supported_platforms = ['windows', 'osx', 'linux']
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
author = 'Kovid Goyal'
|
author = 'Kovid Goyal'
|
||||||
@ -360,3 +367,44 @@ class InterfaceActionBase(Plugin):
|
|||||||
can_be_disabled = False
|
can_be_disabled = False
|
||||||
|
|
||||||
actual_plugin = None
|
actual_plugin = None
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class PreferencesPlugin(Plugin): # {{{
|
||||||
|
|
||||||
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
|
author = 'Kovid Goyal'
|
||||||
|
type = _('Preferences')
|
||||||
|
can_be_disabled = False
|
||||||
|
|
||||||
|
#: Import path to module that contains a class named ConfigWidget
|
||||||
|
#: which implements the ConfigWidgetInterface. Used by
|
||||||
|
#: :meth:`create_widget`.
|
||||||
|
config_widget = None
|
||||||
|
|
||||||
|
#: Where in the list of categories the :attr:`category` of this plugin should be.
|
||||||
|
category_order = 100
|
||||||
|
|
||||||
|
#: Where in the list of names in a category, the :attr:`gui_name` of this
|
||||||
|
#: plugin should be
|
||||||
|
name_order = 100
|
||||||
|
|
||||||
|
#: The category this plugin should be in
|
||||||
|
category = None
|
||||||
|
|
||||||
|
#: The name displayed to the user for this plugin
|
||||||
|
gui_name = None
|
||||||
|
|
||||||
|
def create_widget(self, parent=None):
|
||||||
|
'''
|
||||||
|
Create and return the actual Qt widget used for setting this group of
|
||||||
|
preferences. The widget must implement the ConfigWidgetInterface.
|
||||||
|
|
||||||
|
The default implementation uses :attr:`config_widget` to instantiate
|
||||||
|
the widget.
|
||||||
|
'''
|
||||||
|
base = __import__(self.config_widget, fromlist=[1])
|
||||||
|
widget = base.ConfigWidget(parent)
|
||||||
|
return widget
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
@ -5,7 +5,8 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
import textwrap
|
import textwrap
|
||||||
import os
|
import os
|
||||||
import glob
|
import glob
|
||||||
from calibre.customize import FileTypePlugin, MetadataReaderPlugin, MetadataWriterPlugin
|
from calibre.customize import FileTypePlugin, MetadataReaderPlugin, \
|
||||||
|
MetadataWriterPlugin, PreferencesPlugin, InterfaceActionBase
|
||||||
from calibre.constants import numeric_version
|
from calibre.constants import numeric_version
|
||||||
from calibre.ebooks.metadata.archive import ArchiveExtract, get_cbz_metadata
|
from calibre.ebooks.metadata.archive import ArchiveExtract, get_cbz_metadata
|
||||||
|
|
||||||
@ -577,7 +578,7 @@ plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
|||||||
x.__name__.endswith('MetadataWriter')]
|
x.__name__.endswith('MetadataWriter')]
|
||||||
plugins += input_profiles + output_profiles
|
plugins += input_profiles + output_profiles
|
||||||
|
|
||||||
from calibre.customize import InterfaceActionBase
|
# Interface Actions {{{
|
||||||
|
|
||||||
class ActionAdd(InterfaceActionBase):
|
class ActionAdd(InterfaceActionBase):
|
||||||
name = 'Add Books'
|
name = 'Add Books'
|
||||||
@ -670,3 +671,20 @@ plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog,
|
|||||||
ActionSendToDevice, ActionHelp, ActionPreferences, ActionSimilarBooks,
|
ActionSendToDevice, ActionHelp, ActionPreferences, ActionSimilarBooks,
|
||||||
ActionAddToLibrary, ActionEditCollections, ActionChooseLibrary,
|
ActionAddToLibrary, ActionEditCollections, ActionChooseLibrary,
|
||||||
ActionCopyToLibrary]
|
ActionCopyToLibrary]
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Preferences Plugins {{{
|
||||||
|
|
||||||
|
class LookAndFeel(PreferencesPlugin):
|
||||||
|
name = 'Look & Feel'
|
||||||
|
gui_name = _('Look and Feel')
|
||||||
|
category = _('Interface')
|
||||||
|
category_order = 1
|
||||||
|
name_order = 1
|
||||||
|
config_widget = 'calibre.gui2.preferences.look_feel'
|
||||||
|
|
||||||
|
plugins += [LookAndFeel]
|
||||||
|
|
||||||
|
#}}}
|
||||||
|
|
||||||
|
@ -7,7 +7,8 @@ from contextlib import closing
|
|||||||
|
|
||||||
from calibre.customize import Plugin, CatalogPlugin, FileTypePlugin, \
|
from calibre.customize import Plugin, CatalogPlugin, FileTypePlugin, \
|
||||||
MetadataReaderPlugin, MetadataWriterPlugin, \
|
MetadataReaderPlugin, MetadataWriterPlugin, \
|
||||||
InterfaceActionBase as InterfaceAction
|
InterfaceActionBase as InterfaceAction, \
|
||||||
|
PreferencesPlugin
|
||||||
from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin
|
from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin
|
||||||
from calibre.customize.profiles import InputProfile, OutputProfile
|
from calibre.customize.profiles import InputProfile, OutputProfile
|
||||||
from calibre.customize.builtins import plugins as builtin_plugins
|
from calibre.customize.builtins import plugins as builtin_plugins
|
||||||
@ -257,6 +258,17 @@ def interface_actions():
|
|||||||
yield plugin
|
yield plugin
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
# Preferences Plugins # {{{
|
||||||
|
|
||||||
|
def preferences_plugins():
|
||||||
|
customization = config['plugin_customization']
|
||||||
|
for plugin in _initialized_plugins:
|
||||||
|
if isinstance(plugin, PreferencesPlugin):
|
||||||
|
if not is_disabled(plugin):
|
||||||
|
plugin.site_customization = customization.get(plugin.name, '')
|
||||||
|
yield plugin
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Metadata read/write {{{
|
# Metadata read/write {{{
|
||||||
_metadata_readers = {}
|
_metadata_readers = {}
|
||||||
_metadata_writers = {}
|
_metadata_writers = {}
|
||||||
|
@ -165,11 +165,11 @@ class KINDLE(USBMS):
|
|||||||
|
|
||||||
class KINDLE2(KINDLE):
|
class KINDLE2(KINDLE):
|
||||||
|
|
||||||
name = 'Kindle 2 Device Interface'
|
name = 'Kindle 2/3 Device Interface'
|
||||||
description = _('Communicate with the Kindle 2 eBook reader.')
|
description = _('Communicate with the Kindle 2/3 eBook reader.')
|
||||||
|
|
||||||
FORMATS = KINDLE.FORMATS + ['pdf']
|
FORMATS = KINDLE.FORMATS + ['pdf']
|
||||||
PRODUCT_ID = [0x0002]
|
PRODUCT_ID = [0x0002, 0x0004]
|
||||||
BCD = [0x0100]
|
BCD = [0x0100]
|
||||||
|
|
||||||
def books(self, oncard=None, end_session=True):
|
def books(self, oncard=None, end_session=True):
|
||||||
|
@ -20,7 +20,7 @@ class Book(Metadata):
|
|||||||
'title_sort', 'comments', 'category', 'publisher', 'series',
|
'title_sort', 'comments', 'category', 'publisher', 'series',
|
||||||
'series_index', 'rating', 'isbn', 'language', 'application_id',
|
'series_index', 'rating', 'isbn', 'language', 'application_id',
|
||||||
'book_producer', 'lccn', 'lcc', 'ddc', 'rights', 'publication_type',
|
'book_producer', 'lccn', 'lcc', 'ddc', 'rights', 'publication_type',
|
||||||
'uuid',
|
'uuid', 'device_collections',
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, prefix, lpath, title, authors, mime, date, ContentType,
|
def __init__(self, prefix, lpath, title, authors, mime, date, ContentType,
|
||||||
|
@ -72,7 +72,7 @@ class KOBO(USBMS):
|
|||||||
for idx,b in enumerate(bl):
|
for idx,b in enumerate(bl):
|
||||||
bl_cache[b.lpath] = idx
|
bl_cache[b.lpath] = idx
|
||||||
|
|
||||||
def update_booklist(prefix, path, title, authors, mime, date, ContentType, ImageID):
|
def update_booklist(prefix, path, title, authors, mime, date, ContentType, ImageID, readstatus):
|
||||||
changed = False
|
changed = False
|
||||||
# if path_to_ext(path) in self.FORMATS:
|
# if path_to_ext(path) in self.FORMATS:
|
||||||
try:
|
try:
|
||||||
@ -82,6 +82,13 @@ class KOBO(USBMS):
|
|||||||
lpath = lpath.replace('\\', '/')
|
lpath = lpath.replace('\\', '/')
|
||||||
# print "LPATH: " + lpath
|
# print "LPATH: " + lpath
|
||||||
|
|
||||||
|
playlist_map = {}
|
||||||
|
|
||||||
|
if readstatus == 1:
|
||||||
|
if lpath not in playlist_map:
|
||||||
|
playlist_map[lpath] = []
|
||||||
|
playlist_map[lpath].append("I\'m Reading")
|
||||||
|
|
||||||
path = self.normalize_path(path)
|
path = self.normalize_path(path)
|
||||||
# print "Normalized FileName: " + path
|
# print "Normalized FileName: " + path
|
||||||
|
|
||||||
@ -97,11 +104,13 @@ class KOBO(USBMS):
|
|||||||
if self.update_metadata_item(bl[idx]):
|
if self.update_metadata_item(bl[idx]):
|
||||||
# print 'update_metadata_item returned true'
|
# print 'update_metadata_item returned true'
|
||||||
changed = True
|
changed = True
|
||||||
|
bl[idx].device_collections = playlist_map.get(lpath, [])
|
||||||
else:
|
else:
|
||||||
book = Book(prefix, lpath, title, authors, mime, date, ContentType, ImageID)
|
book = Book(prefix, lpath, title, authors, mime, date, ContentType, ImageID)
|
||||||
# print 'Update booklist'
|
# print 'Update booklist'
|
||||||
if bl.add_book(book, replace_metadata=False):
|
if bl.add_book(book, replace_metadata=False):
|
||||||
changed = True
|
changed = True
|
||||||
|
book.device_collections = playlist_map.get(book.lpath, [])
|
||||||
except: # Probably a path encoding error
|
except: # Probably a path encoding error
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
@ -117,7 +126,7 @@ class KOBO(USBMS):
|
|||||||
#cursor.close()
|
#cursor.close()
|
||||||
|
|
||||||
query= 'select Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ' \
|
query= 'select Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ' \
|
||||||
'ImageID from content where BookID is Null'
|
'ImageID, ReadStatus from content where BookID is Null'
|
||||||
|
|
||||||
cursor.execute (query)
|
cursor.execute (query)
|
||||||
|
|
||||||
@ -129,10 +138,10 @@ class KOBO(USBMS):
|
|||||||
mime = mime_type_ext(path_to_ext(row[3]))
|
mime = mime_type_ext(path_to_ext(row[3]))
|
||||||
|
|
||||||
if oncard != 'carda' and oncard != 'cardb' and not row[3].startswith("file:///mnt/sd/"):
|
if oncard != 'carda' and oncard != 'cardb' and not row[3].startswith("file:///mnt/sd/"):
|
||||||
changed = update_booklist(self._main_prefix, path, row[0], row[1], mime, row[2], row[5], row[6])
|
changed = update_booklist(self._main_prefix, path, row[0], row[1], mime, row[2], row[5], row[6], row[7])
|
||||||
# print "shortbook: " + path
|
# print "shortbook: " + path
|
||||||
elif oncard == 'carda' and row[3].startswith("file:///mnt/sd/"):
|
elif oncard == 'carda' and row[3].startswith("file:///mnt/sd/"):
|
||||||
changed = update_booklist(self._card_a_prefix, path, row[0], row[1], mime, row[2], row[5], row[6])
|
changed = update_booklist(self._card_a_prefix, path, row[0], row[1], mime, row[2], row[5], row[6], row[7])
|
||||||
|
|
||||||
if changed:
|
if changed:
|
||||||
need_sync = True
|
need_sync = True
|
||||||
@ -193,7 +202,7 @@ class KOBO(USBMS):
|
|||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
cursor.close()
|
cursor.close()
|
||||||
if ImageID != None:
|
if ImageID == None:
|
||||||
print "Error condition ImageID was not found"
|
print "Error condition ImageID was not found"
|
||||||
print "You likely tried to delete a book that the kobo has not yet added to the database"
|
print "You likely tried to delete a book that the kobo has not yet added to the database"
|
||||||
|
|
||||||
|
@ -5,8 +5,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import functools
|
import functools, re
|
||||||
import re
|
|
||||||
|
|
||||||
from calibre import entity_to_unicode
|
from calibre import entity_to_unicode
|
||||||
|
|
||||||
@ -54,7 +53,7 @@ def chap_head(match):
|
|||||||
if not title:
|
if not title:
|
||||||
return '<h1>'+chap+'</h1><br/>\n'
|
return '<h1>'+chap+'</h1><br/>\n'
|
||||||
else:
|
else:
|
||||||
return '<h1>'+chap+'<br/>\n'+title+'</h1><br/>\n'
|
return '<h1>'+chap+'</h1>\n<h3>'+title+'</h3>\n'
|
||||||
|
|
||||||
def wrap_lines(match):
|
def wrap_lines(match):
|
||||||
ital = match.group('ital')
|
ital = match.group('ital')
|
||||||
@ -63,7 +62,7 @@ def wrap_lines(match):
|
|||||||
else:
|
else:
|
||||||
return ital+' '
|
return ital+' '
|
||||||
|
|
||||||
def line_length(raw, percent):
|
def line_length(format, raw, percent):
|
||||||
'''
|
'''
|
||||||
raw is the raw text to find the line length to use for wrapping.
|
raw is the raw text to find the line length to use for wrapping.
|
||||||
percentage is a decimal number, 0 - 1 which is used to determine
|
percentage is a decimal number, 0 - 1 which is used to determine
|
||||||
@ -72,7 +71,10 @@ def line_length(raw, percent):
|
|||||||
median value.
|
median value.
|
||||||
'''
|
'''
|
||||||
raw = raw.replace(' ', ' ')
|
raw = raw.replace(' ', ' ')
|
||||||
linere = re.compile('(?<=<br>).*?(?=<br>)', re.DOTALL)
|
if format == 'html':
|
||||||
|
linere = re.compile('(?<=<p).*?(?=</p>)', re.DOTALL)
|
||||||
|
elif format == 'pdf':
|
||||||
|
linere = re.compile('(?<=<br>).*?(?=<br>)', re.DOTALL)
|
||||||
lines = linere.findall(raw)
|
lines = linere.findall(raw)
|
||||||
|
|
||||||
lengths = []
|
lengths = []
|
||||||
@ -202,11 +204,8 @@ class HTMLPreProcessor(object):
|
|||||||
# Remove gray background
|
# Remove gray background
|
||||||
(re.compile(r'<BODY[^<>]+>'), lambda match : '<BODY>'),
|
(re.compile(r'<BODY[^<>]+>'), lambda match : '<BODY>'),
|
||||||
|
|
||||||
# Remove non breaking spaces
|
|
||||||
(re.compile(ur'\u00a0'), lambda match : ' '),
|
|
||||||
|
|
||||||
# Detect Chapters to match default XPATH in GUI
|
# Detect Chapters to match default XPATH in GUI
|
||||||
(re.compile(r'(?=<(/?br|p))(<(/?br|p)[^>]*)?>\s*(?P<chap>(<i><b>|<i>|<b>)?(Chapter|Epilogue|Prologue|Book|Part)\s*([\d\w-]+(\s\w+)?)?(</i></b>|</i>|</b>)?)(</?p[^>]*>|<br[^>]*>)\n?((?=(<i>)?\s*\w+(\s+\w+)?(</i>)?(<br[^>]*>|</?p[^>]*>))((?P<title>(<i>)?\s*\w+(\s+\w+)?(</i>)?)(<br[^>]*>|</?p[^>]*>)))?', re.IGNORECASE), chap_head),
|
(re.compile(r'(?=<(/?br|p))(<(/?br|p)[^>]*)?>\s*(?P<chap>(<(i|b)>(<(i|b)>)?)?(.?Chapter|Epilogue|Prologue|Book|Part)\s*([\d\w-]+(\s\w+)?)?(</(i|b)>(</(i|b)>)?)?)</?(br|p)[^>]*>\s*(?P<title>(<(i|b)>)?\s*\w+(\s*\w+)?\s*(</(i|b)>)?\s*(</?(br|p)[^>]*>))?', re.IGNORECASE), chap_head),
|
||||||
(re.compile(r'(?=<(/?br|p))(<(/?br|p)[^>]*)?>\s*(?P<chap>([A-Z \'"!]{5,})\s*(\d+|\w+)?)(</?p[^>]*>|<br[^>]*>)\n?((?=(<i>)?\s*\w+(\s+\w+)?(</i>)?(<br[^>]*>|</?p[^>]*>))((?P<title>.*)(<br[^>]*>|</?p[^>]*>)))?'), chap_head),
|
(re.compile(r'(?=<(/?br|p))(<(/?br|p)[^>]*)?>\s*(?P<chap>([A-Z \'"!]{5,})\s*(\d+|\w+)?)(</?p[^>]*>|<br[^>]*>)\n?((?=(<i>)?\s*\w+(\s+\w+)?(</i>)?(<br[^>]*>|</?p[^>]*>))((?P<title>.*)(<br[^>]*>|</?p[^>]*>)))?'), chap_head),
|
||||||
|
|
||||||
# Have paragraphs show better
|
# Have paragraphs show better
|
||||||
@ -251,20 +250,27 @@ class HTMLPreProcessor(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 __call__(self, html, remove_special_chars=None):
|
def __call__(self, html, remove_special_chars=None,
|
||||||
|
get_preprocess_html=False):
|
||||||
if remove_special_chars is not None:
|
if remove_special_chars is not None:
|
||||||
html = remove_special_chars.sub('', html)
|
html = remove_special_chars.sub('', html)
|
||||||
html = html.replace('\0', '')
|
html = html.replace('\0', '')
|
||||||
|
is_pdftohtml = self.is_pdftohtml(html)
|
||||||
if self.is_baen(html):
|
if self.is_baen(html):
|
||||||
rules = []
|
rules = []
|
||||||
elif self.is_book_designer(html):
|
elif self.is_book_designer(html):
|
||||||
rules = self.BOOK_DESIGNER
|
rules = self.BOOK_DESIGNER
|
||||||
elif self.is_pdftohtml(html):
|
elif is_pdftohtml:
|
||||||
rules = self.PDFTOHTML
|
rules = self.PDFTOHTML
|
||||||
else:
|
else:
|
||||||
rules = []
|
rules = []
|
||||||
|
|
||||||
if not self.extra_opts.keep_ligatures:
|
start_rules = []
|
||||||
|
if is_pdftohtml:
|
||||||
|
# Remove non breaking spaces
|
||||||
|
start_rules.append((re.compile(ur'\u00a0'), lambda match : ' '))
|
||||||
|
|
||||||
|
if not getattr(self.extra_opts, 'keep_ligatures', False):
|
||||||
html = _ligpat.sub(lambda m:LIGATURES[m.group()], html)
|
html = _ligpat.sub(lambda m:LIGATURES[m.group()], html)
|
||||||
|
|
||||||
end_rules = []
|
end_rules = []
|
||||||
@ -289,16 +295,42 @@ class HTMLPreProcessor(object):
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
if getattr(self.extra_opts, 'unwrap_factor', 0.0) > 0.01:
|
if getattr(self.extra_opts, 'unwrap_factor', 0.0) > 0.01:
|
||||||
length = line_length(html, getattr(self.extra_opts, 'unwrap_factor'))
|
length = line_length('pdf', html, getattr(self.extra_opts, 'unwrap_factor'))
|
||||||
if length:
|
if length:
|
||||||
end_rules.append(
|
end_rules.append(
|
||||||
# Un wrap using punctuation
|
# Un wrap using punctuation
|
||||||
(re.compile(r'(?<=.{%i}[a-z\.,;:)\-IA])\s*(?P<ital></(i|b|u)>)?\s*(<p.*?>)\s*(?=(<(i|b|u)>)?\s*[\w\d(])' % length, re.UNICODE), wrap_lines),
|
(re.compile(r'(?<=.{%i}[a-z\.,;:)\-IA])\s*(?P<ital></(i|b|u)>)?\s*(<p.*?>)\s*(?=(<(i|b|u)>)?\s*[\w\d(])' % length, re.UNICODE), wrap_lines),
|
||||||
)
|
)
|
||||||
|
|
||||||
for rule in self.PREPROCESS + rules + end_rules:
|
for rule in self.PREPROCESS + start_rules:
|
||||||
html = rule[0].sub(rule[1], html)
|
html = rule[0].sub(rule[1], html)
|
||||||
|
|
||||||
|
if get_preprocess_html:
|
||||||
|
return html
|
||||||
|
|
||||||
|
def dump(raw, where):
|
||||||
|
import os
|
||||||
|
dp = getattr(self.extra_opts, 'debug_pipeline', None)
|
||||||
|
if dp and os.path.exists(dp):
|
||||||
|
odir = os.path.join(dp, 'input')
|
||||||
|
if os.path.exists(odir):
|
||||||
|
odir = os.path.join(odir, where)
|
||||||
|
if not os.path.exists(odir):
|
||||||
|
os.makedirs(odir)
|
||||||
|
name, i = None, 0
|
||||||
|
while not name or os.path.exists(os.path.join(odir, name)):
|
||||||
|
i += 1
|
||||||
|
name = '%04d.html'%i
|
||||||
|
with open(os.path.join(odir, name), 'wb') as f:
|
||||||
|
f.write(raw.encode('utf-8'))
|
||||||
|
|
||||||
|
#dump(html, 'pre-preprocess')
|
||||||
|
|
||||||
|
for rule in rules + end_rules:
|
||||||
|
html = rule[0].sub(rule[1], html)
|
||||||
|
|
||||||
|
#dump(html, 'post-preprocess')
|
||||||
|
|
||||||
# Handle broken XHTML w/ SVG (ugh)
|
# Handle broken XHTML w/ SVG (ugh)
|
||||||
if 'svg:' in html and SVG_NS not in html:
|
if 'svg:' in html and SVG_NS not in html:
|
||||||
html = html.replace(
|
html = html.replace(
|
||||||
|
@ -86,11 +86,14 @@ class FB2MLizer(object):
|
|||||||
output.append(self.fb2_footer())
|
output.append(self.fb2_footer())
|
||||||
output = ''.join(output).replace(u'ghji87yhjko0Caliblre-toc-placeholder-for-insertion-later8ujko0987yjk', self.get_toc())
|
output = ''.join(output).replace(u'ghji87yhjko0Caliblre-toc-placeholder-for-insertion-later8ujko0987yjk', self.get_toc())
|
||||||
output = self.clean_text(output)
|
output = self.clean_text(output)
|
||||||
|
if self.opts.sectionize_chapters:
|
||||||
|
output = self.sectionize_chapters(output)
|
||||||
return u'<?xml version="1.0" encoding="UTF-8"?>\n%s' % etree.tostring(etree.fromstring(output), encoding=unicode, pretty_print=True)
|
return u'<?xml version="1.0" encoding="UTF-8"?>\n%s' % etree.tostring(etree.fromstring(output), encoding=unicode, pretty_print=True)
|
||||||
|
|
||||||
def clean_text(self, text):
|
def clean_text(self, text):
|
||||||
text = re.sub('<p>[ ]*</p>', '', text)
|
text = re.sub(r'(?miu)<p>\s*</p>', '', text)
|
||||||
|
text = re.sub(r'(?miu)\s+</p>', '</p>', text)
|
||||||
|
text = re.sub(r'(?miu)</p><p>', '</p>\n\n<p>', text)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def fb2_header(self):
|
def fb2_header(self):
|
||||||
@ -149,6 +152,11 @@ class FB2MLizer(object):
|
|||||||
self.oeb.warn('Ignoring toc item: %s not found in document.' % item)
|
self.oeb.warn('Ignoring toc item: %s not found in document.' % item)
|
||||||
return ''.join(toc)
|
return ''.join(toc)
|
||||||
|
|
||||||
|
def sectionize_chapters(self, text):
|
||||||
|
text = re.sub(r'(?imsu)(?P<anchor><a\s+id="calibre_link-\d+"\s*/>)\s*(?P<strong>(<p>)*\s*<strong>.+?</strong>\s*(</p>)*)', lambda mo: '</section><section>%s<title>%s</title>' % (mo.group('anchor'), mo.group('strong')), text)
|
||||||
|
text = re.sub(r'(?imsu)<p>\s*(?P<anchor><a\s+id="calibre_link-\d+"\s*/>)\s*</p>\s*(?P<strong>(<p>)*\s*<strong>.+?</strong>\s*(</p>)*)', lambda mo: '</section><section>%s<title>%s</title>' % (mo.group('anchor'), mo.group('strong')), text)
|
||||||
|
return text
|
||||||
|
|
||||||
def get_text(self):
|
def get_text(self):
|
||||||
text = []
|
text = []
|
||||||
for item in self.oeb_book.spine:
|
for item in self.oeb_book.spine:
|
||||||
|
@ -19,6 +19,12 @@ class FB2Output(OutputFormatPlugin):
|
|||||||
OptionRecommendation(name='inline_toc',
|
OptionRecommendation(name='inline_toc',
|
||||||
recommended_value=False, level=OptionRecommendation.LOW,
|
recommended_value=False, level=OptionRecommendation.LOW,
|
||||||
help=_('Add Table of Contents to beginning of the book.')),
|
help=_('Add Table of Contents to beginning of the book.')),
|
||||||
|
OptionRecommendation(name='sectionize_chapters',
|
||||||
|
recommended_value=False, level=OptionRecommendation.LOW,
|
||||||
|
help=_('Try to turn chapters into individual sections. ' \
|
||||||
|
'WARNING: ' \
|
||||||
|
'This option is experimental. It can cause conversion ' \
|
||||||
|
'to fail. It can also produce unexpected output.')),
|
||||||
])
|
])
|
||||||
|
|
||||||
def convert(self, oeb_book, output_path, input_plugin, opts, log):
|
def convert(self, oeb_book, output_path, input_plugin, opts, log):
|
||||||
|
@ -24,6 +24,7 @@ from calibre.constants import islinux, isfreebsd, iswindows
|
|||||||
from calibre import unicode_path
|
from calibre import unicode_path
|
||||||
from calibre.utils.localization import get_lang
|
from calibre.utils.localization import get_lang
|
||||||
from calibre.utils.filenames import ascii_filename
|
from calibre.utils.filenames import ascii_filename
|
||||||
|
from calibre.ebooks.conversion.preprocess import line_length
|
||||||
|
|
||||||
class Link(object):
|
class Link(object):
|
||||||
'''
|
'''
|
||||||
@ -489,5 +490,18 @@ class HTMLInput(InputFormatPlugin):
|
|||||||
return (None, None)
|
return (None, None)
|
||||||
return (None, raw)
|
return (None, raw)
|
||||||
|
|
||||||
|
def preprocess_html(self, html):
|
||||||
|
self.log("********* Preprocessing HTML *********")
|
||||||
|
# Detect Chapters to match the xpath in the GUI
|
||||||
|
chapdetect = re.compile(r'(?=</?(br|p|span))(</?(br|p|span)[^>]*>)?\s*(?P<chap>(<(i|b)><(i|b)>|<(i|b)>)?(.?Chapter|Epilogue|Prologue|Book|Part|Dedication)\s*([\d\w-]+(\s\w+)?)?(</(i|b)></(i|b)>|</(i|b)>)?)(</?(p|br|span)[^>]*>)', re.IGNORECASE)
|
||||||
|
html = chapdetect.sub('<h2>'+'\g<chap>'+'</h2>\n', html)
|
||||||
|
# Unwrap lines using punctation if the median length of all lines is less than 150
|
||||||
|
#
|
||||||
|
# Insert extra line feeds so the line length regex functions properly
|
||||||
|
html = re.sub(r"</p>", "</p>\n", html)
|
||||||
|
length = line_length('html', html, 0.4)
|
||||||
|
self.log.debug("*** Median length is " + str(length) + " ***")
|
||||||
|
unwrap = re.compile(r"(?<=.{%i}[a-z,;:\IA])\s*</(span|p|div)>\s*(</(p|span|div)>)?\s*(?P<up2threeblanks><(p|span|div)[^>]*>\s*(<(p|span|div)[^>]*>\s*</(span|p|div)>\s*)</(span|p|div)>\s*){0,3}\s*<(span|div|p)[^>]*>\s*(<(span|div|p)[^>]*>)?\s*" % length, re.UNICODE)
|
||||||
|
if length < 150:
|
||||||
|
html = unwrap.sub(' ', html)
|
||||||
|
return html
|
||||||
|
@ -9,6 +9,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from calibre.customize.conversion import InputFormatPlugin
|
from calibre.customize.conversion import InputFormatPlugin
|
||||||
|
from calibre.ebooks.conversion.preprocess import line_length
|
||||||
|
|
||||||
class LITInput(InputFormatPlugin):
|
class LITInput(InputFormatPlugin):
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ class LITInput(InputFormatPlugin):
|
|||||||
accelerators):
|
accelerators):
|
||||||
from calibre.ebooks.lit.reader import LitReader
|
from calibre.ebooks.lit.reader import LitReader
|
||||||
from calibre.ebooks.conversion.plumber import create_oebbook
|
from calibre.ebooks.conversion.plumber import create_oebbook
|
||||||
|
self.log = log
|
||||||
return create_oebbook(log, stream, options, self, reader=LitReader)
|
return create_oebbook(log, stream, options, self, reader=LitReader)
|
||||||
|
|
||||||
def postprocess_book(self, oeb, opts, log):
|
def postprocess_book(self, oeb, opts, log):
|
||||||
@ -53,8 +55,18 @@ class LITInput(InputFormatPlugin):
|
|||||||
|
|
||||||
|
|
||||||
def preprocess_html(self, html):
|
def preprocess_html(self, html):
|
||||||
|
self.log("********* Preprocessing HTML *********")
|
||||||
|
# Detect Chapters to match the xpath in the GUI
|
||||||
chapdetect = re.compile(r'(?=</?(br|p|span))(</?(br|p|span)[^>]*>)?\s*(?P<chap>(<(i|b)><(i|b)>|<(i|b)>)?(.?Chapter|Epilogue|Prologue|Book|Part|Dedication)\s*([\d\w-]+(\s\w+)?)?(</(i|b)></(i|b)>|</(i|b)>)?)(</?(p|br|span)[^>]*>)', re.IGNORECASE)
|
chapdetect = re.compile(r'(?=</?(br|p|span))(</?(br|p|span)[^>]*>)?\s*(?P<chap>(<(i|b)><(i|b)>|<(i|b)>)?(.?Chapter|Epilogue|Prologue|Book|Part|Dedication)\s*([\d\w-]+(\s\w+)?)?(</(i|b)></(i|b)>|</(i|b)>)?)(</?(p|br|span)[^>]*>)', re.IGNORECASE)
|
||||||
html = chapdetect.sub('<h2>'+'\g<chap>'+'</h2>\n', html)
|
html = chapdetect.sub('<h2>'+'\g<chap>'+'</h2>\n', html)
|
||||||
html = re.sub(r"(?<=.{65}[a-z,\IA])\s*</(span|p|div)>\s*(</(p|span|div)>\s*<p[^>]*>(\s*<(p|span|div)>\s*</(p|span|div)[^>]*>)?\s*(</(p|span|div)>\s*<p[^>]*>)?)?\s*<(span|div|p)[^>]*>", " ", html)
|
# Unwrap lines using punctation if the median length of all lines is less than 150
|
||||||
return html
|
#
|
||||||
|
# Insert extra line feeds so the line length regex functions properly
|
||||||
|
html = re.sub(r"</p>", "</p>\n", html)
|
||||||
|
length = line_length('html', html, 0.4)
|
||||||
|
self.log("*** Median length is " + str(length) + " ***")
|
||||||
|
unwrap = re.compile(r"(?<=.{%i}[a-z,;:\IA])\s*</(span|p|div)>\s*(</(p|span|div)>)?\s*(?P<up2threeblanks><(p|span|div)[^>]*>\s*(<(p|span|div)[^>]*>\s*</(span|p|div)>\s*)</(span|p|div)>\s*){0,3}\s*<(span|div|p)[^>]*>\s*(<(span|div|p)[^>]*>)?\s*" % length, re.UNICODE)
|
||||||
|
if length < 150:
|
||||||
|
html = unwrap.sub(' ', html)
|
||||||
|
return html
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import os, glob, re, textwrap
|
|||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from calibre.customize.conversion import InputFormatPlugin
|
from calibre.customize.conversion import InputFormatPlugin
|
||||||
|
from calibre.ebooks.conversion.preprocess import line_length
|
||||||
|
|
||||||
class InlineClass(etree.XSLTExtension):
|
class InlineClass(etree.XSLTExtension):
|
||||||
|
|
||||||
@ -184,6 +185,7 @@ class RTFInput(InputFormatPlugin):
|
|||||||
from calibre.ebooks.metadata.meta import get_metadata
|
from calibre.ebooks.metadata.meta import get_metadata
|
||||||
from calibre.ebooks.metadata.opf2 import OPFCreator
|
from calibre.ebooks.metadata.opf2 import OPFCreator
|
||||||
from calibre.ebooks.rtf2xml.ParseRtf import RtfInvalidCodeException
|
from calibre.ebooks.rtf2xml.ParseRtf import RtfInvalidCodeException
|
||||||
|
self.options = options
|
||||||
self.log = log
|
self.log = log
|
||||||
self.log('Converting RTF to XML...')
|
self.log('Converting RTF to XML...')
|
||||||
#Name of the preprocesssed RTF file
|
#Name of the preprocesssed RTF file
|
||||||
@ -226,6 +228,17 @@ class RTFInput(InputFormatPlugin):
|
|||||||
with open(html, 'wb') as f:
|
with open(html, 'wb') as f:
|
||||||
res = transform.tostring(result)
|
res = transform.tostring(result)
|
||||||
res = res[:100].replace('xmlns:html', 'xmlns') + res[100:]
|
res = res[:100].replace('xmlns:html', 'xmlns') + res[100:]
|
||||||
|
if self.options.preprocess_html:
|
||||||
|
self.log("********* Preprocessing HTML *********")
|
||||||
|
# Detect Chapters to match the xpath in the GUI
|
||||||
|
chapdetect = re.compile(r'<p[^>]*>\s*<span[^>]*>\s*(?P<chap>(<(i|b)><(i|b)>|<(i|b)>)?(.?Chapter|Epilogue|Prologue|Book|Part|Dedication)\s*([\d\w-]+(\s\w+)?)?(</(i|b)></(i|b)>|</(i|b)>)?)\s*</span>\s*</p>', re.IGNORECASE)
|
||||||
|
res = chapdetect.sub('<h2>'+'\g<chap>'+'</h2>\n', res)
|
||||||
|
# Unwrap lines using punctation if the median length of all lines is less than 150
|
||||||
|
length = line_length('html', res, 0.4)
|
||||||
|
self.log("*** Median length is " + str(length) + " ***")
|
||||||
|
unwrap = re.compile(r"(?<=.{%i}[a-z,;:\IA])\s*</span>\s*(</p>)?\s*(?P<up2threeblanks><p[^>]*>\s*(<span[^>]*>\s*</span>\s*)</p>\s*){0,3}\s*<p[^>]*>\s*(<span[^>]*>)?\s*" % length, re.UNICODE)
|
||||||
|
if length < 150:
|
||||||
|
res = unwrap.sub(' ', res)
|
||||||
f.write(res)
|
f.write(res)
|
||||||
self.write_inline_css(inline_class)
|
self.write_inline_css(inline_class)
|
||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
|
@ -46,6 +46,11 @@ gprefs.defaults['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['show_splash_screen'] = True
|
||||||
|
gprefs.defaults['toolbar_icon_size'] = 'medium'
|
||||||
|
gprefs.defaults['toolbar_text'] = 'auto'
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
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
|
||||||
|
@ -16,6 +16,6 @@ class PluginWidget(Widget, Ui_Form):
|
|||||||
COMMIT_NAME = 'fb2_output'
|
COMMIT_NAME = 'fb2_output'
|
||||||
|
|
||||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||||
Widget.__init__(self, parent, ['inline_toc'])
|
Widget.__init__(self, parent, ['inline_toc', 'sectionize_chapters'])
|
||||||
self.db, self.book_id = db, book_id
|
self.db, self.book_id = db, book_id
|
||||||
self.initialize_options(get_option, get_help, db, book_id)
|
self.initialize_options(get_option, get_help, db, book_id)
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="1" column="0">
|
<item row="2" column="0">
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
@ -34,6 +34,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_sectionize_chapters">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sectionize Chapters (Use with care!)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
@ -14,7 +14,7 @@ from calibre.gui2.convert.regex_builder_ui import Ui_RegexBuilder
|
|||||||
from calibre.gui2.convert.xexp_edit_ui import Ui_Form as Ui_Edit
|
from calibre.gui2.convert.xexp_edit_ui import Ui_Form as Ui_Edit
|
||||||
from calibre.gui2 import error_dialog, choose_files
|
from calibre.gui2 import error_dialog, choose_files
|
||||||
from calibre.ebooks.oeb.iterator import EbookIterator
|
from calibre.ebooks.oeb.iterator import EbookIterator
|
||||||
from calibre.ebooks.conversion.preprocess import convert_entities
|
from calibre.ebooks.conversion.preprocess import HTMLPreProcessor
|
||||||
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
|
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
|
||||||
|
|
||||||
class RegexBuilder(QDialog, Ui_RegexBuilder):
|
class RegexBuilder(QDialog, Ui_RegexBuilder):
|
||||||
@ -91,10 +91,10 @@ class RegexBuilder(QDialog, Ui_RegexBuilder):
|
|||||||
self.iterator = EbookIterator(pathtoebook)
|
self.iterator = EbookIterator(pathtoebook)
|
||||||
self.iterator.__enter__(only_input_plugin=True)
|
self.iterator.__enter__(only_input_plugin=True)
|
||||||
text = [u'']
|
text = [u'']
|
||||||
ent_pat = re.compile(r'&(\S+?);')
|
preprocessor = HTMLPreProcessor(None, False)
|
||||||
for path in self.iterator.spine:
|
for path in self.iterator.spine:
|
||||||
html = open(path, 'rb').read().decode('utf-8', 'replace')
|
html = open(path, 'rb').read().decode('utf-8', 'replace')
|
||||||
html = ent_pat.sub(convert_entities, html)
|
html = preprocessor(html, get_preprocess_html=True)
|
||||||
text.append(html)
|
text.append(html)
|
||||||
self.preview.setPlainText('\n---\n'.join(text))
|
self.preview.setPlainText('\n---\n'.join(text))
|
||||||
|
|
||||||
|
@ -357,7 +357,7 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
|||||||
|
|
||||||
input_map = prefs['input_format_order']
|
input_map = prefs['input_format_order']
|
||||||
all_formats = set()
|
all_formats = set()
|
||||||
for fmt in all_input_formats():
|
for fmt in all_input_formats().union(set(['ZIP', 'RAR'])):
|
||||||
all_formats.add(fmt.upper())
|
all_formats.add(fmt.upper())
|
||||||
for format in input_map + list(all_formats.difference(input_map)):
|
for format in input_map + list(all_formats.difference(input_map)):
|
||||||
item = QListWidgetItem(format, self.input_order)
|
item = QListWidgetItem(format, self.input_order)
|
||||||
|
@ -97,6 +97,7 @@ class LocationManager(QObject): # {{{
|
|||||||
self.free[2] = fs[2] if fs[2] is not None and cpb is not None else -1
|
self.free[2] = fs[2] if fs[2] is not None and cpb is not None else -1
|
||||||
self.update_tooltips()
|
self.update_tooltips()
|
||||||
if self.has_device != had_device:
|
if self.has_device != had_device:
|
||||||
|
self.location_library.setChecked(True)
|
||||||
self.locations_changed.emit()
|
self.locations_changed.emit()
|
||||||
if not self.has_device:
|
if not self.has_device:
|
||||||
self.location_library.trigger()
|
self.location_library.trigger()
|
||||||
@ -218,11 +219,11 @@ class ToolBar(QToolBar): # {{{
|
|||||||
self.preferred_width = self.sizeHint().width()
|
self.preferred_width = self.sizeHint().width()
|
||||||
|
|
||||||
def apply_settings(self):
|
def apply_settings(self):
|
||||||
sz = gprefs.get('toolbar_icon_size', 'medium')
|
sz = gprefs['toolbar_icon_size']
|
||||||
sz = {'small':24, 'medium':48, 'large':64}[sz]
|
sz = {'small':24, 'medium':48, 'large':64}[sz]
|
||||||
self.setIconSize(QSize(sz, sz))
|
self.setIconSize(QSize(sz, sz))
|
||||||
style = Qt.ToolButtonTextUnderIcon
|
style = Qt.ToolButtonTextUnderIcon
|
||||||
if gprefs.get('toolbar_text', 'auto') == 'never':
|
if gprefs['toolbar_text'] == 'never':
|
||||||
style = Qt.ToolButtonIconOnly
|
style = Qt.ToolButtonIconOnly
|
||||||
self.setToolButtonStyle(style)
|
self.setToolButtonStyle(style)
|
||||||
self.donate_button.set_normal_icon_size(sz, sz)
|
self.donate_button.set_normal_icon_size(sz, sz)
|
||||||
@ -231,6 +232,7 @@ class ToolBar(QToolBar): # {{{
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def build_bar(self):
|
def build_bar(self):
|
||||||
|
self.showing_donate = False
|
||||||
showing_device = self.location_manager.has_device
|
showing_device = self.location_manager.has_device
|
||||||
actions = '-device' if showing_device else ''
|
actions = '-device' if showing_device else ''
|
||||||
actions = gprefs['action-layout-toolbar'+actions]
|
actions = gprefs['action-layout-toolbar'+actions]
|
||||||
@ -249,6 +251,7 @@ class ToolBar(QToolBar): # {{{
|
|||||||
self.d_widget.setLayout(QVBoxLayout())
|
self.d_widget.setLayout(QVBoxLayout())
|
||||||
self.d_widget.layout().addWidget(self.donate_button)
|
self.d_widget.layout().addWidget(self.donate_button)
|
||||||
self.addWidget(self.d_widget)
|
self.addWidget(self.d_widget)
|
||||||
|
self.showing_donate = True
|
||||||
elif what in self.gui.iactions:
|
elif what in self.gui.iactions:
|
||||||
action = self.gui.iactions[what]
|
action = self.gui.iactions[what]
|
||||||
self.addAction(action.qaction)
|
self.addAction(action.qaction)
|
||||||
@ -264,7 +267,7 @@ class ToolBar(QToolBar): # {{{
|
|||||||
def resizeEvent(self, ev):
|
def resizeEvent(self, ev):
|
||||||
QToolBar.resizeEvent(self, ev)
|
QToolBar.resizeEvent(self, ev)
|
||||||
style = Qt.ToolButtonTextUnderIcon
|
style = Qt.ToolButtonTextUnderIcon
|
||||||
p = gprefs.get('toolbar_text', 'auto')
|
p = gprefs['toolbar_text']
|
||||||
if p == 'never':
|
if p == 'never':
|
||||||
style = Qt.ToolButtonIconOnly
|
style = Qt.ToolButtonIconOnly
|
||||||
|
|
||||||
@ -291,7 +294,7 @@ class MainWindowMixin(object): # {{{
|
|||||||
self._central_widget_layout = QVBoxLayout()
|
self._central_widget_layout = QVBoxLayout()
|
||||||
self.centralwidget.setLayout(self._central_widget_layout)
|
self.centralwidget.setLayout(self._central_widget_layout)
|
||||||
self.resize(1012, 740)
|
self.resize(1012, 740)
|
||||||
self.donate_button = ThrobbingButton(self.centralwidget)
|
self.donate_button = ThrobbingButton()
|
||||||
self.location_manager = LocationManager(self)
|
self.location_manager = LocationManager(self)
|
||||||
|
|
||||||
self.iactions['Fetch News'].init_scheduler(db)
|
self.iactions['Fetch News'].init_scheduler(db)
|
||||||
|
@ -241,7 +241,7 @@ class GuiRunner(QObject):
|
|||||||
QApplication.instance().processEvents()
|
QApplication.instance().processEvents()
|
||||||
|
|
||||||
def initialize(self, *args):
|
def initialize(self, *args):
|
||||||
if gprefs.get('show_splash_screen', True):
|
if gprefs['show_splash_screen']:
|
||||||
self.show_splash_screen()
|
self.show_splash_screen()
|
||||||
|
|
||||||
self.library_path = get_library_path(parent=self.splash_screen)
|
self.library_path = get_library_path(parent=self.splash_screen)
|
||||||
|
@ -5,30 +5,183 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
from PyQt4.Qt import QWidget, pyqtSignal
|
from PyQt4.Qt import QWidget, pyqtSignal, QCheckBox, QAbstractSpinBox, \
|
||||||
|
QLineEdit, QComboBox, QVariant
|
||||||
|
|
||||||
class PreferenceWidget(QWidget):
|
from calibre.customize.ui import preferences_plugins
|
||||||
|
|
||||||
category = None
|
class ConfigWidgetInterface(object):
|
||||||
name = None
|
|
||||||
|
|
||||||
changed_signal = pyqtSignal()
|
changed_signal = None
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
QWidget.__init__(self, parent)
|
|
||||||
|
|
||||||
self.has_changed = False
|
|
||||||
self.changed.connect(lambda : setattr(self, 'has_changed', True))
|
|
||||||
self.setupUi(self)
|
|
||||||
|
|
||||||
def genesis(self, gui):
|
def genesis(self, gui):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def reset_to_defaults(self):
|
def restore_defaults(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def add_boolean(self, widget_name, preference_interface, pref_name):
|
class Setting(object):
|
||||||
pass
|
|
||||||
|
def __init__(self, name, config_obj, widget, gui_name=None,
|
||||||
|
empty_string_is_None=True, choices=None):
|
||||||
|
self.name, self.gui_name = name, gui_name
|
||||||
|
self.empty_string_is_None = empty_string_is_None
|
||||||
|
self.choices = choices
|
||||||
|
if gui_name is None:
|
||||||
|
self.gui_name = 'opt_'+name
|
||||||
|
self.config_obj = config_obj
|
||||||
|
self.gui_obj = getattr(widget, self.gui_name)
|
||||||
|
|
||||||
|
if isinstance(self.gui_obj, QCheckBox):
|
||||||
|
self.datatype = 'bool'
|
||||||
|
self.gui_obj.stateChanged.connect(lambda x:
|
||||||
|
widget.changed_signal.emit())
|
||||||
|
elif isinstance(self.gui_obj, QAbstractSpinBox):
|
||||||
|
self.datatype = 'number'
|
||||||
|
self.gui_obj.valueChanged.connect(lambda x:
|
||||||
|
widget.changed_signal.emit())
|
||||||
|
elif isinstance(self.gui_obj, QLineEdit):
|
||||||
|
self.datatype = 'string'
|
||||||
|
self.gui_obj.textChanged.connect(lambda x:
|
||||||
|
widget.changed_signal.emit())
|
||||||
|
elif isinstance(self.gui_obj, QComboBox):
|
||||||
|
self.datatype = 'choice'
|
||||||
|
self.gui_obj.editTextChanged.connect(lambda x:
|
||||||
|
widget.changed_signal.emit())
|
||||||
|
self.gui_obj.currentIndexChanged.connect(lambda x:
|
||||||
|
widget.changed_signal.emit())
|
||||||
|
else:
|
||||||
|
raise ValueError('Unknown data type')
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
self.gui_obj.blockSignals(True)
|
||||||
|
if self.datatype == 'choices':
|
||||||
|
self.gui_obj.clear()
|
||||||
|
for x in self.choices:
|
||||||
|
if isinstance(x, basestring):
|
||||||
|
x = (x, x)
|
||||||
|
self.gui_obj.addItem(x[0], QVariant(x[1]))
|
||||||
|
self.set_gui_val(self.get_config_val(default=False))
|
||||||
|
self.gui_obj.blockSignals(False)
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
self.set_config_val(self.get_gui_val())
|
||||||
|
|
||||||
|
def restore_defaults(self):
|
||||||
|
self.set_gui_val(self.get_config_val(default=True))
|
||||||
|
|
||||||
|
def get_config_val(self, default=False):
|
||||||
|
if default:
|
||||||
|
val = self.config_obj.defaults[self.name]
|
||||||
|
else:
|
||||||
|
val = self.config_obj[self.name]
|
||||||
|
return val
|
||||||
|
|
||||||
|
def set_config_val(self, val):
|
||||||
|
self.config_obj[self.name] = val
|
||||||
|
|
||||||
|
def set_gui_val(self, val):
|
||||||
|
if self.datatype == 'bool':
|
||||||
|
self.gui_obj.setChecked(bool(val))
|
||||||
|
elif self.datatype == 'number':
|
||||||
|
self.gui_obj.setValue(val)
|
||||||
|
elif self.datatype == 'string':
|
||||||
|
self.gui_obj.setText(val if val else '')
|
||||||
|
elif self.datatype == 'choices':
|
||||||
|
idx = self.gui_obj.findData(QVariant(val))
|
||||||
|
if idx == -1:
|
||||||
|
idx = 0
|
||||||
|
self.gui_obj.setCurrentIndex(idx)
|
||||||
|
|
||||||
|
def get_gui_val(self):
|
||||||
|
if self.datatype == 'bool':
|
||||||
|
val = bool(self.gui_obj.isChecked())
|
||||||
|
elif self.datatype == 'number':
|
||||||
|
val = self.gui_obj.value(val)
|
||||||
|
elif self.datatype == 'string':
|
||||||
|
val = unicode(self.gui_name.text()).strip()
|
||||||
|
if self.empty_string_is_None and not val:
|
||||||
|
val = None
|
||||||
|
elif self.datatype == 'choices':
|
||||||
|
idx = self.gui_obj.currentIndex()
|
||||||
|
if idx < 0: idx = 0
|
||||||
|
val = unicode(self.gui_obj.itemData(idx).toString())
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigWidgetBase(QWidget, ConfigWidgetInterface):
|
||||||
|
|
||||||
|
changed_signal = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
if hasattr(self, 'setupUi'):
|
||||||
|
self.setupUi(self)
|
||||||
|
self.settings = {}
|
||||||
|
|
||||||
|
def register(self, name, config_obj, gui_name=None, choices=None, setting=Setting):
|
||||||
|
setting = setting(name, config_obj, self, gui_name=gui_name,
|
||||||
|
choices=choices)
|
||||||
|
self.register_setting(setting)
|
||||||
|
|
||||||
|
def register_setting(self, setting):
|
||||||
|
self.settings[setting.name] = setting
|
||||||
|
return setting
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
for setting in self.settings.values():
|
||||||
|
setting.initialize()
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
for setting in self.settings.values():
|
||||||
|
setting.commit()
|
||||||
|
|
||||||
|
def restore_defaults(self, *args):
|
||||||
|
for setting in self.settings.values():
|
||||||
|
setting.restore_defaults()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugin(category, name):
|
||||||
|
for plugin in preferences_plugins():
|
||||||
|
if plugin.category == category and plugin.name == name:
|
||||||
|
return plugin
|
||||||
|
raise ValueError(
|
||||||
|
'No Preferences PLugin with category: %s and name: %s found' %
|
||||||
|
(category, name))
|
||||||
|
|
||||||
|
def test_widget(category, name, gui=None): # {{{
|
||||||
|
from PyQt4.Qt import QDialog, QVBoxLayout, QDialogButtonBox
|
||||||
|
pl = get_plugin(category, name)
|
||||||
|
d = QDialog()
|
||||||
|
d.resize(750, 550)
|
||||||
|
bb = QDialogButtonBox(d)
|
||||||
|
bb.setStandardButtons(bb.Apply|bb.Cancel|bb.RestoreDefaults)
|
||||||
|
bb.accepted.connect(d.accept)
|
||||||
|
bb.rejected.connect(d.reject)
|
||||||
|
w = pl.create_widget(d)
|
||||||
|
bb.button(bb.RestoreDefaults).clicked.connect(w.restore_defaults)
|
||||||
|
bb.button(bb.Apply).setEnabled(False)
|
||||||
|
w.changed_signal.connect(lambda : bb.button(bb.Apply).setEnable(True))
|
||||||
|
l = QVBoxLayout()
|
||||||
|
d.setLayout(l)
|
||||||
|
l.addWidget(w)
|
||||||
|
l.addWidget(bb)
|
||||||
|
if gui is None:
|
||||||
|
from calibre.gui2.ui import Main
|
||||||
|
from calibre.gui2.main import option_parser
|
||||||
|
from calibre.library import db
|
||||||
|
parser = option_parser()
|
||||||
|
opts, args = parser.parse_args([])
|
||||||
|
actions = tuple(Main.create_application_menubar())
|
||||||
|
db = db()
|
||||||
|
gui = Main(opts)
|
||||||
|
gui.initialize(db.library_path, db, None, actions, show_gui=False)
|
||||||
|
w.genesis(gui)
|
||||||
|
if d.exec_() == QDialog.Accepted:
|
||||||
|
w.commit()
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
263
src/calibre/gui2/preferences/behavior.ui
Normal file
263
src/calibre/gui2/preferences/behavior.ui
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
<?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>672</width>
|
||||||
|
<height>563</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_overwrite_author_title_metadata">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Overwrite author and title by default when fetching metadata</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_get_social_metadata">
|
||||||
|
<property name="text">
|
||||||
|
<string>Download &social metadata (tags/ratings/etc.) by default</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="new_version_notification">
|
||||||
|
<property name="text">
|
||||||
|
<string>Show notification when &new version is available</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="sync_news">
|
||||||
|
<property name="text">
|
||||||
|
<string>Automatically send downloaded &news to ebook reader</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="delete_news">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Delete news from library when it is automatically sent to reader</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0" colspan="2">
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Default network &timeout:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>timeout</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QSpinBox" name="timeout">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information)</string>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string> seconds</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>120</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QComboBox" name="priority">
|
||||||
|
<property name="sizeAdjustPolicy">
|
||||||
|
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimumContentsLength">
|
||||||
|
<number>20</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Normal</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>High</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Low</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="priority_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Job &priority:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>priority</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_23">
|
||||||
|
<property name="text">
|
||||||
|
<string>Preferred &output format:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>output_format</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="output_format">
|
||||||
|
<property name="sizeAdjustPolicy">
|
||||||
|
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimumContentsLength">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_170">
|
||||||
|
<property name="text">
|
||||||
|
<string>Restriction to apply when the current library is opened:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_gui_restriction</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QComboBox" name="opt_gui_restriction">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>250</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Apply this restriction on calibre startup if the current library is being used. Also applied when switching to this library. Note that this setting is per library. </string>
|
||||||
|
</property>
|
||||||
|
<property name="sizeAdjustPolicy">
|
||||||
|
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimumContentsLength">
|
||||||
|
<number>15</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0" colspan="2">
|
||||||
|
<widget class="QPushButton" name="reset_confirmation_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Reset all disabled &confirmation dialogs</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QGroupBox" name="groupBox_5">
|
||||||
|
<property name="title">
|
||||||
|
<string>Preferred &input format order:</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_11">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_10">
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="input_order">
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_10">
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="input_up">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_5">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="input_down">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QGroupBox" name="groupBox_3">
|
||||||
|
<property name="title">
|
||||||
|
<string>Use internal &viewer for:</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_4">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QListWidget" name="viewer">
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::NoSelection</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../../../../resources/images.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
170
src/calibre/gui2/preferences/custom_columns.ui
Normal file
170
src/calibre/gui2/preferences/custom_columns.ui
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<?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>504</width>
|
||||||
|
<height>399</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Here you can re-arrange the layout of the columns in the calibre library book list. You can hide columns by unchecking them. You can also create your own, custom columns.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QListWidget" name="columns">
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="column_up">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="del_custcol_button">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Remove a user-defined column</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/minus.svg</normaloff>:/images/minus.svg</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="add_custcol_button">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Add a user-defined column</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/plus.svg</normaloff>:/images/plus.svg</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="edit_custcol_button">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Edit settings of a user-defined column</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/edit_input.svg</normaloff>:/images/edit_input.svg</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_5">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="column_down">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QPushButton" name="pushButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Add &custom column</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../../../../resources/images.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
62
src/calibre/gui2/preferences/look_feel.py
Normal file
62
src/calibre/gui2/preferences/look_feel.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#!/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 calibre.gui2.preferences import ConfigWidgetBase, test_widget
|
||||||
|
from calibre.gui2.preferences.look_feel_ui import Ui_Form
|
||||||
|
from calibre.gui2 import config, gprefs
|
||||||
|
from calibre.utils.localization import available_translations, \
|
||||||
|
get_language, get_lang
|
||||||
|
from calibre.utils.config import prefs
|
||||||
|
|
||||||
|
class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||||
|
|
||||||
|
def genesis(self, gui):
|
||||||
|
self.gui = gui
|
||||||
|
|
||||||
|
r = self.register
|
||||||
|
|
||||||
|
r('gui_layout', config, choices=
|
||||||
|
[(_('Wide'), 'wide'), (_('Narrow'), 'narrow')])
|
||||||
|
|
||||||
|
r('cover_flow_queue_length', config)
|
||||||
|
|
||||||
|
lang = get_lang()
|
||||||
|
if lang is None or lang not in available_translations():
|
||||||
|
lang = 'en'
|
||||||
|
items = [(l, get_language(l)) for l in available_translations() \
|
||||||
|
if l != lang]
|
||||||
|
if lang != 'en':
|
||||||
|
items.append(('en', get_language('en')))
|
||||||
|
items.sort(cmp=lambda x, y: cmp(x[1], y[1]))
|
||||||
|
choices = [(y, x) for x, y in items]
|
||||||
|
# Default language is the autodetected one
|
||||||
|
choices = [get_language(lang), lang] + choices
|
||||||
|
r('language', prefs, choices=choices)
|
||||||
|
|
||||||
|
r('show_avg_rating', config)
|
||||||
|
r('disable_animations', config)
|
||||||
|
r('systray_icon', config)
|
||||||
|
r('show_splash_screen', gprefs)
|
||||||
|
r('disable_tray_notification', config)
|
||||||
|
r('use_roman_numerals_for_series_number', config)
|
||||||
|
r('separate_cover_flow', config)
|
||||||
|
r('search_as_you_type', config)
|
||||||
|
|
||||||
|
choices = [(_('Small'), 'small'), (_('Medium'), 'medium'),
|
||||||
|
(_('Large'), 'large')]
|
||||||
|
r('toolbar_icon_size', gprefs, choices=choices)
|
||||||
|
|
||||||
|
choices = [(_('Automatic'), 'auto'), (_('Always'), 'always'),
|
||||||
|
(_('Never'), 'never')]
|
||||||
|
r('toolbar_text', gprefs, choices=choices)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from PyQt4.Qt import QApplication
|
||||||
|
app = QApplication([])
|
||||||
|
test_widget('Interface', 'Look & Feel')
|
||||||
|
|
196
src/calibre/gui2/preferences/look_feel.ui
Normal file
196
src/calibre/gui2/preferences/look_feel.ui
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
<?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>385</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_17">
|
||||||
|
<property name="text">
|
||||||
|
<string>User Interface &layout (needs restart):</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_gui_layout</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="opt_gui_layout">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>250</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="sizeAdjustPolicy">
|
||||||
|
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimumContentsLength">
|
||||||
|
<number>20</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Number of covers to show in browse mode (needs restart):</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_cover_flow_queue_length</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QSpinBox" name="opt_cover_flow_queue_length"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Choose &language (requires restart):</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_language</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QComboBox" name="opt_language">
|
||||||
|
<property name="sizeAdjustPolicy">
|
||||||
|
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimumContentsLength">
|
||||||
|
<number>20</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_show_avg_rating">
|
||||||
|
<property name="text">
|
||||||
|
<string>Show &average ratings in the tags browser</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QCheckBox" name="opt_disable_animations">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Disable all animations. Useful if you have a slow/old computer.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Disable &animations</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_systray_icon">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable system &tray icon (needs restart)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QCheckBox" name="opt_show_splash_screen">
|
||||||
|
<property name="text">
|
||||||
|
<string>Show &splash screen at startup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_disable_tray_notification">
|
||||||
|
<property name="text">
|
||||||
|
<string>Disable &notifications in system tray</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QCheckBox" name="opt_use_roman_numerals_for_series_number">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use &Roman numerals for series</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_separate_cover_flow">
|
||||||
|
<property name="text">
|
||||||
|
<string>Show cover &browser in a separate window (needs restart)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</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">
|
||||||
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
|
<property name="title">
|
||||||
|
<string>&Toolbar</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="opt_toolbar_icon_size"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Icon size:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_toolbar_icon_size</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="opt_toolbar_text"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Show &text under icons:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_toolbar_text</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0" colspan="2">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
@ -50,6 +50,8 @@ class Listener(Thread): # {{{
|
|||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
if self.listener is None:
|
||||||
|
return
|
||||||
while self._run:
|
while self._run:
|
||||||
try:
|
try:
|
||||||
conn = self.listener.accept()
|
conn = self.listener.accept()
|
||||||
@ -108,7 +110,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
|
|||||||
|
|
||||||
self.iactions = acmap
|
self.iactions = acmap
|
||||||
|
|
||||||
def initialize(self, library_path, db, listener, actions):
|
def initialize(self, library_path, db, listener, actions, show_gui=True):
|
||||||
opts = self.opts
|
opts = self.opts
|
||||||
self.preferences_action, self.quit_action = actions
|
self.preferences_action, self.quit_action = actions
|
||||||
self.library_path = library_path
|
self.library_path = library_path
|
||||||
@ -201,7 +203,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
|
|||||||
####################### Library view ########################
|
####################### Library view ########################
|
||||||
LibraryViewMixin.__init__(self, db)
|
LibraryViewMixin.__init__(self, db)
|
||||||
|
|
||||||
self.show()
|
if show_gui:
|
||||||
|
self.show()
|
||||||
|
|
||||||
if self.system_tray_icon.isVisible() and opts.start_in_tray:
|
if self.system_tray_icon.isVisible() and opts.start_in_tray:
|
||||||
self.hide_windows()
|
self.hide_windows()
|
||||||
@ -249,7 +252,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
|
|||||||
|
|
||||||
self.read_settings()
|
self.read_settings()
|
||||||
self.finalize_layout()
|
self.finalize_layout()
|
||||||
self.donate_button.start_animation()
|
if self.tool_bar.showing_donate:
|
||||||
|
self.donate_button.start_animation()
|
||||||
self.set_window_title()
|
self.set_window_title()
|
||||||
|
|
||||||
for ac in self.iactions.values():
|
for ac in self.iactions.values():
|
||||||
|
@ -15,6 +15,7 @@ class DBPrefs(dict):
|
|||||||
def __init__(self, db):
|
def __init__(self, db):
|
||||||
dict.__init__(self)
|
dict.__init__(self)
|
||||||
self.db = db
|
self.db = db
|
||||||
|
self.defaults = {}
|
||||||
for key, val in self.db.conn.get('SELECT key,val FROM preferences'):
|
for key, val in self.db.conn.get('SELECT key,val FROM preferences'):
|
||||||
val = self.raw_to_object(val)
|
val = self.raw_to_object(val)
|
||||||
dict.__setitem__(self, key, val)
|
dict.__setitem__(self, key, val)
|
||||||
@ -28,7 +29,10 @@ class DBPrefs(dict):
|
|||||||
return json.dumps(val, indent=2, default=to_json)
|
return json.dumps(val, indent=2, default=to_json)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
return dict.__getitem__(self, key)
|
try:
|
||||||
|
return dict.__getitem__(self, key)
|
||||||
|
except KeyError:
|
||||||
|
return self.defaults[key]
|
||||||
|
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
dict.__delitem__(self, key)
|
dict.__delitem__(self, key)
|
||||||
|
@ -81,7 +81,7 @@ Device Integration
|
|||||||
|
|
||||||
What devices does |app| support?
|
What devices does |app| support?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
At the moment |app| has full support for the SONY PRS 300/500/505/600/700/900, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Entourage Edge, Longshine ShineBook, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Italica, eClicto, Iriver Story, Airis dBook, Hanvon N515, Binatone Readme, Teclast K3, SpringDesign Alex, Kobo Reader, various Android phones and the iPhone/iPad. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
|
At the moment |app| has full support for the SONY PRS 300/500/505/600/700/900, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle 1/2/3/DX/DXG, Entourage Edge, Longshine ShineBook, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Italica, eClicto, Iriver Story, Airis dBook, Hanvon N515, Binatone Readme, Teclast K3, SpringDesign Alex, Kobo Reader, various Android phones and the iPhone/iPad. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
|
||||||
|
|
||||||
How can I help get my device supported in |app|?
|
How can I help get my device supported in |app|?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -338,6 +338,16 @@ Why does |app| show only some of my fonts on OS X?
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
There can be several causes for this:
|
There can be several causes for this:
|
||||||
|
|
||||||
|
* If you get an error about calibre not being able to open a file because it is in use by another program, do the following:
|
||||||
|
|
||||||
|
* Uninstall calibre
|
||||||
|
* Reboot your computer
|
||||||
|
* Re-install calibre. But do not start calibre from the installation wizard.
|
||||||
|
* Temporarily disable your antivirus program (disconnect from the internet before doing so, to be safe)
|
||||||
|
* Look inside the folder you chose for your calibre library. If you see a file named metadata.db, delete it.
|
||||||
|
* Start calibre
|
||||||
|
* From now on you should be able to start calibre normally.
|
||||||
|
|
||||||
* If you get an error about a Python function terminating unexpectedly after upgrading calibre, first uninstall calibre, then delete the folders (if they exists)
|
* If you get an error about a Python function terminating unexpectedly after upgrading calibre, first uninstall calibre, then delete the folders (if they exists)
|
||||||
:file:`C:\\Program Files\\Calibre` and :file:`C:\\Program Files\\Calibre2`. Now re-install and you should be fine.
|
:file:`C:\\Program Files\\Calibre` and :file:`C:\\Program Files\\Calibre2`. Now re-install and you should be fine.
|
||||||
* If you get an error in the welcome wizard on an initial run of calibre, try choosing a folder like :file:`C:\\library` as the calibre library (calibre sometimes
|
* If you get an error in the welcome wizard on an initial run of calibre, try choosing a folder like :file:`C:\\library` as the calibre library (calibre sometimes
|
||||||
@ -394,4 +404,8 @@ Can I include |app| on a CD to be distributed with my product/magazine?
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|app| is licensed under the GNU General Public License v3 (an open source license). This means that you are free to redistribute |app| as long as you make the source code available. So if you want to put |app| on a CD with your product, you must also put the |app| source code on the CD. The source code is available for download `from googlecode <http://code.google.com/p/calibre-ebook/downloads/list>`_.
|
|app| is licensed under the GNU General Public License v3 (an open source license). This means that you are free to redistribute |app| as long as you make the source code available. So if you want to put |app| on a CD with your product, you must also put the |app| source code on the CD. The source code is available for download `from googlecode <http://code.google.com/p/calibre-ebook/downloads/list>`_.
|
||||||
|
|
||||||
|
How do I run calibre from my USB stick?
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A portable version of calibre is available at: `portableapps.com <http://portableapps.com/node/20518>`_. However, this is usually out of date. You can also setup your own portable calibre install by following :ref:`these instructions <portablecalibre>`.
|
||||||
|
|
||||||
|
74
src/calibre/manual/portable.rst
Normal file
74
src/calibre/manual/portable.rst
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
|
||||||
|
.. include:: global.rst
|
||||||
|
|
||||||
|
.. _portablecalibre:
|
||||||
|
|
||||||
|
Creating your own portable/customized calibre install
|
||||||
|
=======================================================
|
||||||
|
|
||||||
|
You can "install" calibre onto a USB stick that you can take with you and use on any computer. The magic is in a .bat file called calibre-portable.bat found in the resources folder in your calibre install. Typical uses of this files are:
|
||||||
|
|
||||||
|
* Run a Mobile Calibre installation with both the Calibre binaries and your ebook library resident on a USB disk or other portable media. In particular it is not necessary to have Calibre installed on the Windows PC that is to run Calibre. This batch file also does not care what drive letter is assigned when you plug in the USB device. It also will not affect any settings on the host machine being a completely self-contained Calibre installation.
|
||||||
|
* Run a networked Calibre installation optimised for performance when the ebook files are located on a networked share.
|
||||||
|
|
||||||
|
|
||||||
|
This calibre-portable.bat file is intended for use on Windows based systems, but the principles are easily adapted for use on Linux or OS X based systems. Note that calibre requires the Microsoft Visual C++ 2008 runtimes to run. Most windows computers have them installed already, but it may be a good idea to have the installer for installing them on your USB stick. The installer is available from `Microsoft <http://www.microsoft.com/downloads/details.aspx?FamilyID=9b2da534-3e03-4391-8a4d-074b9f2bc1bf&displaylang=en>`_.
|
||||||
|
|
||||||
|
Assumptions
|
||||||
|
------------
|
||||||
|
|
||||||
|
The calibre-portable.bat file makes the following assumptions about the folder layout that is being used::
|
||||||
|
|
||||||
|
Calibre_Root_Folder
|
||||||
|
calibre-portable.bat The batch file used to start Calibre
|
||||||
|
Calibre2 The Calibre binaries
|
||||||
|
CalibreLibrary The Calibre Library
|
||||||
|
CalibreConfig The Calibre user preferences
|
||||||
|
CalibreSource The calibre source (optional) if running a development environment.
|
||||||
|
|
||||||
|
If you want to use a different folder layout then the calibre-portable.bat file will need editing appropriately. This file can be edited using any appropriate text editor.
|
||||||
|
|
||||||
|
Preparation
|
||||||
|
------------
|
||||||
|
|
||||||
|
The steps required to prepare the USB stick are as follows:
|
||||||
|
|
||||||
|
* Decide what folder will be used to hold all the Calibre related files. If the portable media is to be dedicated to Calibre use then this can be the root folder, but if not is suggested that a folder called Calibre should be created – this will then be the Calibre_Root_Folder mentioned above and in the following steps.
|
||||||
|
* Copy the calibre-portable.bat file into the Calibre_Root_Folder.
|
||||||
|
* Create the Calibre2 folder inside the Calibre_Root_Folder to hold the Calibre binaries. There are 2 ways of populating the contents of this folder:
|
||||||
|
|
||||||
|
* The easiest is to simply copy an existing Calibre installation. Typically this would involve copying the contents of the C:\Program Files\Calibre2 folder
|
||||||
|
* Run the Calibre Windows installer:
|
||||||
|
|
||||||
|
* Tick the box to accept the GNU GPL license
|
||||||
|
* Select the Advanced option
|
||||||
|
* Change the install location to be the Calibre2 folder on the USB drive
|
||||||
|
* Deselect the options for creating Menu shortcuts; creating a calibre shortcut on the desktop; and adding Calibre to the path
|
||||||
|
|
||||||
|
* Create the CalibreLibrary folder inside the Calibre_Root_Folder. If you have an existing Calibre library copy it and all its contents to the CalibreLibrary folder. If you do not already have a library do not worry as a new one will be created at this location when Calibre is started.
|
||||||
|
* Create the CalibreConfig folder inside the Calibre_Root_Folder. This will hold your personal Calibre configuration settings. If you have an existing Calibre installation and want to copy the current settings then copy the contents of your current configuration folder to the CalibreConfig folder. You can find the location of your current configuration folder by going to Preferences->Advanced and clicking the “Open calibre configuration Directory” button.
|
||||||
|
* When you have started Calibre, go into Preferences->General and check that you have set the Job Priority to ‘Low’. This setting keeps single-processor Windows systems responsive without affecting Calibre performance to any noticeable degree. On multi-processor or multi-core systems this setting does not matter as much, but setting it will do no harm.
|
||||||
|
|
||||||
|
Using calibre-portable.bat
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Once you have got the USB stick set up then the steps to use Calibre are:
|
||||||
|
|
||||||
|
* Plug the USB stick into the host machine
|
||||||
|
* Use Windows Explorer to navigate to the location of the calibre-portable.bat file on the USB stick
|
||||||
|
* Start Calibre by double-clicking the calibre-portable.bat file
|
||||||
|
* A Command Window will be opened showing the settings that are about to be used. If you are not happy with these setting use CTRL-C to abandon the batch file without starting Calibre. If you are happy then press any other key to launch Calibre with the specified settings. Once you are happy with your setup you may wish to edit the calibre-portable.bat file to eliminate this pause (add REM to the start of the line) but it a useful check that you are running with the expected settings.
|
||||||
|
|
||||||
|
Networked Installations
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
The performance of Calibre can be severely degraded if running with the Calibre library on a network share. This is primarily due to the fact that the access to the metadata.db file is slow across a network. The calibre-portable.bat file is designed to help in such scenarios. To use the calibre-portable.bat file in such a scenario the following deviations from those detailed above for the Mobile Calibre installation are needed:
|
||||||
|
|
||||||
|
* Edit the calibre-portable.bat file to specify the location of your Calibre library on the network.
|
||||||
|
* Create a CalibreMetadata folder in the Calibre_Root_Folder location. If you have an existing Calibre library then copy the metadata.db files from there to the CalibreMetadata folder.
|
||||||
|
* You can now run Calibre using the calibre-portable.bat file as specified in the previous section. One thing you should remember is to periodically copy the metadata.db file from the CalibreMetadatqa folder back to your Calibre library located on the network share.
|
||||||
|
|
||||||
|
Precautions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Portable media can occasionally fail so you should make periodic backups of you Calibre library. This can be done by making a copy of the CalibreLibrary folder and all its contents. There are many freely available tools around that can optimise such back processes, well known ones being RoboCopy and RichCopy. However you can simply use a Windows copy facility if you cannot be bothered to use a specialised tools.
|
@ -5,18 +5,36 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
Provides platform independent temporary files that persist even after
|
Provides platform independent temporary files that persist even after
|
||||||
being closed.
|
being closed.
|
||||||
"""
|
"""
|
||||||
import tempfile, os, atexit, shutil
|
import tempfile, os, atexit
|
||||||
|
|
||||||
from calibre import __version__, __appname__
|
from calibre import __version__, __appname__
|
||||||
|
|
||||||
def cleanup(path):
|
def cleanup(path):
|
||||||
try:
|
try:
|
||||||
import os
|
import os as oss
|
||||||
if os.path.exists(path):
|
if oss.path.exists(path):
|
||||||
os.remove(path)
|
oss.remove(path)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
_base_dir = None
|
||||||
|
|
||||||
|
def remove_dir(x):
|
||||||
|
try:
|
||||||
|
import shutil
|
||||||
|
shutil.rmtree(x, ignore_errors=True)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def base_dir():
|
||||||
|
global _base_dir
|
||||||
|
if _base_dir is None:
|
||||||
|
_base_dir = tempfile.mkdtemp(prefix='%s_%s_tmp_'%(__appname__,
|
||||||
|
__version__))
|
||||||
|
atexit.register(remove_dir, _base_dir)
|
||||||
|
return _base_dir
|
||||||
|
|
||||||
class PersistentTemporaryFile(object):
|
class PersistentTemporaryFile(object):
|
||||||
"""
|
"""
|
||||||
A file-like object that is a temporary file that is available even after being closed on
|
A file-like object that is a temporary file that is available even after being closed on
|
||||||
@ -27,6 +45,8 @@ class PersistentTemporaryFile(object):
|
|||||||
def __init__(self, suffix="", prefix="", dir=None, mode='w+b'):
|
def __init__(self, suffix="", prefix="", dir=None, mode='w+b'):
|
||||||
if prefix == None:
|
if prefix == None:
|
||||||
prefix = ""
|
prefix = ""
|
||||||
|
if dir is None:
|
||||||
|
dir = base_dir()
|
||||||
fd, name = tempfile.mkstemp(suffix, __appname__+"_"+ __version__+"_" + prefix,
|
fd, name = tempfile.mkstemp(suffix, __appname__+"_"+ __version__+"_" + prefix,
|
||||||
dir=dir)
|
dir=dir)
|
||||||
self._file = os.fdopen(fd, mode)
|
self._file = os.fdopen(fd, mode)
|
||||||
@ -56,8 +76,10 @@ def PersistentTemporaryDirectory(suffix='', prefix='', dir=None):
|
|||||||
Return the path to a newly created temporary directory that will
|
Return the path to a newly created temporary directory that will
|
||||||
be automatically deleted on application exit.
|
be automatically deleted on application exit.
|
||||||
'''
|
'''
|
||||||
|
if dir is None:
|
||||||
|
dir = base_dir()
|
||||||
tdir = tempfile.mkdtemp(suffix, __appname__+"_"+ __version__+"_" +prefix, dir)
|
tdir = tempfile.mkdtemp(suffix, __appname__+"_"+ __version__+"_" +prefix, dir)
|
||||||
atexit.register(shutil.rmtree, tdir, True)
|
atexit.register(remove_dir, tdir)
|
||||||
return tdir
|
return tdir
|
||||||
|
|
||||||
class TemporaryDirectory(object):
|
class TemporaryDirectory(object):
|
||||||
@ -67,6 +89,8 @@ class TemporaryDirectory(object):
|
|||||||
def __init__(self, suffix='', prefix='', dir=None, keep=False):
|
def __init__(self, suffix='', prefix='', dir=None, keep=False):
|
||||||
self.suffix = suffix
|
self.suffix = suffix
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
|
if dir is None:
|
||||||
|
dir = base_dir()
|
||||||
self.dir = dir
|
self.dir = dir
|
||||||
self.keep = keep
|
self.keep = keep
|
||||||
|
|
||||||
@ -76,7 +100,7 @@ class TemporaryDirectory(object):
|
|||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
if not self.keep and os.path.exists(self.tdir):
|
if not self.keep and os.path.exists(self.tdir):
|
||||||
shutil.rmtree(self.tdir, ignore_errors=True)
|
remove_dir(self.tdir)
|
||||||
|
|
||||||
class TemporaryFile(object):
|
class TemporaryFile(object):
|
||||||
|
|
||||||
@ -85,6 +109,8 @@ class TemporaryFile(object):
|
|||||||
prefix = ''
|
prefix = ''
|
||||||
if suffix is None:
|
if suffix is None:
|
||||||
suffix = ''
|
suffix = ''
|
||||||
|
if dir is None:
|
||||||
|
dir = base_dir()
|
||||||
self.prefix, self.suffix, self.dir, self.mode = prefix, suffix, dir, mode
|
self.prefix, self.suffix, self.dir, self.mode = prefix, suffix, dir, mode
|
||||||
self._file = None
|
self._file = None
|
||||||
|
|
||||||
|
@ -194,6 +194,7 @@ class OptionSet(object):
|
|||||||
|
|
||||||
def __init__(self, description=''):
|
def __init__(self, description=''):
|
||||||
self.description = description
|
self.description = description
|
||||||
|
self.defaults = {}
|
||||||
self.preferences = []
|
self.preferences = []
|
||||||
self.group_list = []
|
self.group_list = []
|
||||||
self.groups = {}
|
self.groups = {}
|
||||||
@ -274,6 +275,7 @@ class OptionSet(object):
|
|||||||
if pref in self.preferences:
|
if pref in self.preferences:
|
||||||
raise ValueError('An option with the name %s already exists in this set.'%name)
|
raise ValueError('An option with the name %s already exists in this set.'%name)
|
||||||
self.preferences.append(pref)
|
self.preferences.append(pref)
|
||||||
|
self.defaults[name] = default
|
||||||
|
|
||||||
def option_parser(self, user_defaults=None, usage='', gui_mode=False):
|
def option_parser(self, user_defaults=None, usage='', gui_mode=False):
|
||||||
parser = OptionParser(usage, gui_mode=gui_mode)
|
parser = OptionParser(usage, gui_mode=gui_mode)
|
||||||
@ -466,6 +468,10 @@ class ConfigProxy(object):
|
|||||||
self.__config = config
|
self.__config = config
|
||||||
self.__opts = None
|
self.__opts = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def defaults(self):
|
||||||
|
return self.__config.option_set.defaults
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
self.__opts = self.__config.parse()
|
self.__opts = self.__config.parse()
|
||||||
|
|
||||||
@ -701,7 +707,7 @@ def _prefs():
|
|||||||
c.add_opt('output_format', default='EPUB',
|
c.add_opt('output_format', default='EPUB',
|
||||||
help=_('The default output format for ebook conversions.'))
|
help=_('The default output format for ebook conversions.'))
|
||||||
c.add_opt('input_format_order', default=['EPUB', 'MOBI', 'LIT', 'PRC',
|
c.add_opt('input_format_order', default=['EPUB', 'MOBI', 'LIT', 'PRC',
|
||||||
'FB2', 'HTML', 'HTM', 'XHTM', 'SHTML', 'XHTML', 'ODT', 'RTF', 'PDF',
|
'FB2', 'HTML', 'HTM', 'XHTM', 'SHTML', 'XHTML', 'ZIP', 'ODT', 'RTF', 'PDF',
|
||||||
'TXT'],
|
'TXT'],
|
||||||
help=_('Ordered list of formats to prefer for input.'))
|
help=_('Ordered list of formats to prefer for input.'))
|
||||||
c.add_opt('read_file_metadata', default=True,
|
c.add_opt('read_file_metadata', default=True,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user