merge from main branch2

This commit is contained in:
ldolse 2010-08-26 17:57:25 +08:00
commit b9e526fe22
27 changed files with 991 additions and 66 deletions

View File

@ -28,3 +28,4 @@ nbproject/
*.userprefs *.userprefs
.project .project
.pydevproject .pydevproject
.settings/

View File

@ -19,7 +19,7 @@ class NRO(BasicNewsRecipe):
encoding = 'utf-8' encoding = 'utf-8'
use_embedded_content = True use_embedded_content = True
remove_javascript = True remove_javascript = True
conversion_options = { conversion_options = {
'comment' : description 'comment' : description
@ -34,17 +34,18 @@ 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')
]
]

View File

@ -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',

View File

@ -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

View File

@ -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:

View File

@ -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]
#}}}

View File

@ -20,7 +20,7 @@ class Book(MetaInformation):
'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, thumbnail_name, other=None): def __init__(self, prefix, lpath, title, authors, mime, date, ContentType, thumbnail_name, other=None):

View File

@ -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"

View File

@ -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
@ -73,7 +72,7 @@ def line_length(format, raw, percent):
''' '''
raw = raw.replace('&nbsp;', ' ') raw = raw.replace('&nbsp;', ' ')
if format == 'html': if format == 'html':
linere = re.compile('(?<=<p).*?(?=</p>)', re.DOTALL) linere = re.compile('(?<=<p).*?(?=</p>)', re.DOTALL)
elif format == 'pdf': elif format == 'pdf':
linere = re.compile('(?<=<br>).*?(?=<br>)', re.DOTALL) linere = re.compile('(?<=<br>).*?(?=<br>)', re.DOTALL)
lines = linere.findall(raw) lines = linere.findall(raw)
@ -205,9 +204,6 @@ 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|Dedication)\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>(<(i|b)>(<(i|b)>)?)?(.?Chapter|Epilogue|Prologue|Book|Part|Dedication)\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),
@ -254,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 = []
@ -299,9 +302,35 @@ class HTMLPreProcessor(object):
(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(

View File

@ -86,10 +86,12 @@ 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'<p>\s*</p>', '', text)
return text return text
@ -149,6 +151,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:

View File

@ -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):

View File

@ -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

View File

@ -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)

View File

@ -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/>

View File

@ -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))

View File

@ -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)

View File

@ -219,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)
@ -265,7 +265,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

View File

@ -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)

View File

@ -5,7 +5,8 @@ __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
from calibre.customize.ui import preferences_plugins from calibre.customize.ui import preferences_plugins
@ -22,6 +23,94 @@ class ConfigWidgetInterface(object):
def commit(self): def commit(self):
pass pass
class Setting(object):
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): class ConfigWidgetBase(QWidget, ConfigWidgetInterface):
@ -31,6 +120,30 @@ class ConfigWidgetBase(QWidget, ConfigWidgetInterface):
QWidget.__init__(self, parent) QWidget.__init__(self, parent)
if hasattr(self, 'setupUi'): if hasattr(self, 'setupUi'):
self.setupUi(self) 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): def get_plugin(category, name):
for plugin in preferences_plugins(): for plugin in preferences_plugins():
@ -54,18 +167,19 @@ def test_widget(category, name, gui=None): # {{{
bb.button(bb.Apply).setEnabled(False) bb.button(bb.Apply).setEnabled(False)
w.changed_signal.connect(lambda : bb.button(bb.Apply).setEnable(True)) w.changed_signal.connect(lambda : bb.button(bb.Apply).setEnable(True))
l = QVBoxLayout() l = QVBoxLayout()
pl.setLayout(l) d.setLayout(l)
l.addWidget(w) l.addWidget(w)
l.addWidget(bb)
if gui is None: if gui is None:
from calibre.gui2.ui import Main from calibre.gui2.ui import Main
from calibre.gui2.main import option_parser from calibre.gui2.main import option_parser
from calibre.library.db import db from calibre.library import db
parser = option_parser() parser = option_parser()
opts, args = parser.parse_args([]) opts, args = parser.parse_args([])
actions = tuple(Main.create_application_menubar()) actions = tuple(Main.create_application_menubar())
db = db() db = db()
gui = Main(opts) gui = Main(opts)
gui.initialize(db.library_path, db, None, actions) gui.initialize(db.library_path, db, None, actions, show_gui=False)
w.genesis(gui) w.genesis(gui)
if d.exec_() == QDialog.Accepted: if d.exec_() == QDialog.Accepted:
w.commit() w.commit()

View 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>&amp;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 &amp;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 &amp;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 &amp;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>&amp;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 &amp;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 &amp;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 &amp;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 &amp;confirmation dialogs</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Preferred &amp;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 &amp;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>

View 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 &amp;custom column</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../../../resources/images.qrc"/>
</resources>
<connections/>
</ui>

View 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')

View 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 &amp;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>&amp;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 &amp;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 &amp;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 &amp;animations</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="opt_systray_icon">
<property name="text">
<string>Enable system &amp;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 &amp;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 &amp;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 &amp;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 &amp;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>&amp;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>&amp;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 &amp;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>

View File

@ -110,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
@ -203,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()

View File

@ -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)

View File

@ -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

View File

@ -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,