mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
IGN:Complete migration to new config infrastructure
This commit is contained in:
parent
fab60df71d
commit
4d450f556f
2
setup.py
2
setup.py
@ -7,7 +7,7 @@ sys.path.append('src')
|
||||
iswindows = re.search('win(32|64)', sys.platform)
|
||||
isosx = 'darwin' in sys.platform
|
||||
islinux = not isosx and not iswindows
|
||||
src = open('src/calibre/__init__.py', 'rb').read()
|
||||
src = open('src/calibre/constants.py', 'rb').read()
|
||||
VERSION = re.search(r'__version__\s+=\s+[\'"]([^\'"]+)[\'"]', src).group(1)
|
||||
APPNAME = re.search(r'__appname__\s+=\s+[\'"]([^\'"]+)[\'"]', src).group(1)
|
||||
print 'Setup', APPNAME, 'version:', VERSION
|
||||
|
@ -1,123 +1,21 @@
|
||||
''' E-book management software'''
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__version__ = '0.4.83'
|
||||
__docformat__ = "epytext"
|
||||
__author__ = "Kovid Goyal <kovid at kovidgoyal.net>"
|
||||
__appname__ = 'calibre'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import sys, os, logging, mechanize, locale, copy, cStringIO, re, subprocess, \
|
||||
textwrap, atexit, cPickle, codecs, time
|
||||
from gettext import GNUTranslations
|
||||
import sys, os, re, logging, time, subprocess, mechanize, atexit
|
||||
from htmlentitydefs import name2codepoint
|
||||
from math import floor
|
||||
from optparse import OptionParser as _OptionParser
|
||||
from optparse import IndentedHelpFormatter
|
||||
from logging import Formatter
|
||||
|
||||
from PyQt4.QtCore import QSettings, QVariant, QUrl, QByteArray, QString
|
||||
from PyQt4.QtGui import QDesktopServices
|
||||
from PyQt4.QtCore import QUrl
|
||||
from PyQt4.QtGui import QDesktopServices
|
||||
from calibre.startup import plugins, winutil, winutilerror
|
||||
from calibre.constants import iswindows, isosx, islinux, isfrozen, \
|
||||
terminal_controller, preferred_encoding, \
|
||||
__appname__, __version__, __author__, \
|
||||
win32event, win32api, winerror, fcntl
|
||||
|
||||
from calibre.translations.msgfmt import make
|
||||
from calibre.ebooks.chardet import detect
|
||||
from calibre.utils.terminfo import TerminalController
|
||||
|
||||
terminal_controller = TerminalController(sys.stdout)
|
||||
iswindows = 'win32' in sys.platform.lower() or 'win64' in sys.platform.lower()
|
||||
isosx = 'darwin' in sys.platform.lower()
|
||||
islinux = not(iswindows or isosx)
|
||||
isfrozen = hasattr(sys, 'frozen')
|
||||
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
except:
|
||||
dl = locale.getdefaultlocale()
|
||||
try:
|
||||
if dl:
|
||||
locale.setlocale(dl[0])
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
preferred_encoding = locale.getpreferredencoding()
|
||||
codecs.lookup(preferred_encoding)
|
||||
except:
|
||||
preferred_encoding = 'utf-8'
|
||||
|
||||
if getattr(sys, 'frozen', False):
|
||||
if iswindows:
|
||||
plugin_path = os.path.join(os.path.dirname(sys.executable), 'plugins')
|
||||
elif isosx:
|
||||
plugin_path = os.path.join(getattr(sys, 'frameworks_dir'), 'plugins')
|
||||
elif islinux:
|
||||
plugin_path = os.path.join(getattr(sys, 'frozen_path'), 'plugins')
|
||||
sys.path.insert(0, plugin_path)
|
||||
else:
|
||||
import pkg_resources
|
||||
plugins = getattr(pkg_resources, 'resource_filename')(__appname__, 'plugins')
|
||||
sys.path.insert(0, plugins)
|
||||
|
||||
if iswindows and getattr(sys, 'frozen', False):
|
||||
sys.path.insert(1, os.path.dirname(sys.executable))
|
||||
|
||||
|
||||
plugins = {}
|
||||
for plugin in ['pictureflow', 'lzx', 'msdes'] + \
|
||||
(['winutil'] if iswindows else []) + \
|
||||
(['usbobserver'] if isosx else []):
|
||||
try:
|
||||
p, err = __import__(plugin), ''
|
||||
except Exception, err:
|
||||
p = None
|
||||
err = str(err)
|
||||
plugins[plugin] = (p, err)
|
||||
|
||||
if iswindows:
|
||||
winutil, winutilerror = plugins['winutil']
|
||||
if not winutil:
|
||||
raise RuntimeError('Failed to load the winutil plugin: %s'%winutilerror)
|
||||
if len(sys.argv) > 1:
|
||||
sys.argv[1:] = winutil.argv()[1-len(sys.argv):]
|
||||
win32event = __import__('win32event')
|
||||
winerror = __import__('winerror')
|
||||
win32api = __import__('win32api')
|
||||
else:
|
||||
import fcntl
|
||||
|
||||
_abspath = os.path.abspath
|
||||
def my_abspath(path, encoding=sys.getfilesystemencoding()):
|
||||
'''
|
||||
Work around for buggy os.path.abspath. This function accepts either byte strings,
|
||||
in which it calls os.path.abspath, or unicode string, in which case it first converts
|
||||
to byte strings using `encoding`, calls abspath and then decodes back to unicode.
|
||||
'''
|
||||
to_unicode = False
|
||||
if isinstance(path, unicode):
|
||||
path = path.encode(encoding)
|
||||
to_unicode = True
|
||||
res = _abspath(path)
|
||||
if to_unicode:
|
||||
res = res.decode(encoding)
|
||||
return res
|
||||
|
||||
os.path.abspath = my_abspath
|
||||
_join = os.path.join
|
||||
def my_join(a, *p):
|
||||
encoding=sys.getfilesystemencoding()
|
||||
p = [a] + list(p)
|
||||
_unicode = False
|
||||
for i in p:
|
||||
if isinstance(i, unicode):
|
||||
_unicode = True
|
||||
break
|
||||
p = [i.encode(encoding) if isinstance(i, unicode) else i for i in p]
|
||||
|
||||
res = _join(*p)
|
||||
if _unicode:
|
||||
res = res.decode(encoding)
|
||||
return res
|
||||
|
||||
os.path.join = my_join
|
||||
|
||||
def unicode_path(path, abs=False):
|
||||
if not isinstance(path, unicode):
|
||||
@ -135,10 +33,6 @@ def osx_version():
|
||||
return int(m.group(1)), int(m.group(2)), int(m.group(3))
|
||||
|
||||
|
||||
# Default translation is NOOP
|
||||
import __builtin__
|
||||
__builtin__.__dict__['_'] = lambda s: s
|
||||
|
||||
class CommandLineError(Exception):
|
||||
pass
|
||||
|
||||
@ -180,122 +74,6 @@ def setup_cli_handlers(logger, level):
|
||||
|
||||
logger.addHandler(handler)
|
||||
|
||||
class CustomHelpFormatter(IndentedHelpFormatter):
|
||||
|
||||
def format_usage(self, usage):
|
||||
return _("%sUsage%s: %s\n") % (terminal_controller.BLUE, terminal_controller.NORMAL, usage)
|
||||
|
||||
def format_heading(self, heading):
|
||||
return "%*s%s%s%s:\n" % (self.current_indent, terminal_controller.BLUE,
|
||||
"", heading, terminal_controller.NORMAL)
|
||||
|
||||
def format_option(self, option):
|
||||
result = []
|
||||
opts = self.option_strings[option]
|
||||
opt_width = self.help_position - self.current_indent - 2
|
||||
if len(opts) > opt_width:
|
||||
opts = "%*s%s\n" % (self.current_indent, "",
|
||||
terminal_controller.GREEN+opts+terminal_controller.NORMAL)
|
||||
indent_first = self.help_position
|
||||
else: # start help on same line as opts
|
||||
opts = "%*s%-*s " % (self.current_indent, "", opt_width + len(terminal_controller.GREEN + terminal_controller.NORMAL),
|
||||
terminal_controller.GREEN + opts + terminal_controller.NORMAL)
|
||||
indent_first = 0
|
||||
result.append(opts)
|
||||
if option.help:
|
||||
help_text = self.expand_default(option).split('\n')
|
||||
help_lines = []
|
||||
|
||||
for line in help_text:
|
||||
help_lines.extend(textwrap.wrap(line, self.help_width))
|
||||
result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
|
||||
result.extend(["%*s%s\n" % (self.help_position, "", line)
|
||||
for line in help_lines[1:]])
|
||||
elif opts[-1] != "\n":
|
||||
result.append("\n")
|
||||
return "".join(result)+'\n'
|
||||
|
||||
class OptionParser(_OptionParser):
|
||||
|
||||
def __init__(self,
|
||||
usage='%prog [options] filename',
|
||||
version='%%prog (%s %s)'%(__appname__, __version__),
|
||||
epilog=_('Created by ')+terminal_controller.RED+__author__+terminal_controller.NORMAL,
|
||||
gui_mode=False,
|
||||
conflict_handler='resolve',
|
||||
**kwds):
|
||||
usage += '''\n\nWhenever you pass arguments to %prog that have spaces in them, '''\
|
||||
'''enclose the arguments in quotation marks.'''
|
||||
_OptionParser.__init__(self, usage=usage, version=version, epilog=epilog,
|
||||
formatter=CustomHelpFormatter(),
|
||||
conflict_handler=conflict_handler, **kwds)
|
||||
self.gui_mode = gui_mode
|
||||
|
||||
def error(self, msg):
|
||||
if self.gui_mode:
|
||||
raise Exception(msg)
|
||||
_OptionParser.error(self, msg)
|
||||
|
||||
def merge(self, parser):
|
||||
'''
|
||||
Add options from parser to self. In case of conflicts, confilicting options from
|
||||
parser are skipped.
|
||||
'''
|
||||
opts = list(parser.option_list)
|
||||
groups = list(parser.option_groups)
|
||||
|
||||
def merge_options(options, container):
|
||||
for opt in copy.deepcopy(options):
|
||||
if not self.has_option(opt.get_opt_string()):
|
||||
container.add_option(opt)
|
||||
|
||||
merge_options(opts, self)
|
||||
|
||||
for group in groups:
|
||||
g = self.add_option_group(group.title)
|
||||
merge_options(group.option_list, g)
|
||||
|
||||
def subsume(self, group_name, msg=''):
|
||||
'''
|
||||
Move all existing options into a subgroup named
|
||||
C{group_name} with description C{msg}.
|
||||
'''
|
||||
opts = [opt for opt in self.options_iter() if opt.get_opt_string() not in ('--version', '--help')]
|
||||
self.option_groups = []
|
||||
subgroup = self.add_option_group(group_name, msg)
|
||||
for opt in opts:
|
||||
self.remove_option(opt.get_opt_string())
|
||||
subgroup.add_option(opt)
|
||||
|
||||
def options_iter(self):
|
||||
for opt in self.option_list:
|
||||
if str(opt).strip():
|
||||
yield opt
|
||||
for gr in self.option_groups:
|
||||
for opt in gr.option_list:
|
||||
if str(opt).strip():
|
||||
yield opt
|
||||
|
||||
def option_by_dest(self, dest):
|
||||
for opt in self.options_iter():
|
||||
if opt.dest == dest:
|
||||
return opt
|
||||
|
||||
def merge_options(self, lower, upper):
|
||||
'''
|
||||
Merge options in lower and upper option lists into upper.
|
||||
Default values in upper are overriden by
|
||||
non default values in lower.
|
||||
'''
|
||||
for dest in lower.__dict__.keys():
|
||||
if not upper.__dict__.has_key(dest):
|
||||
continue
|
||||
opt = self.option_by_dest(dest)
|
||||
if lower.__dict__[dest] != opt.default and \
|
||||
upper.__dict__[dest] == opt.default:
|
||||
upper.__dict__[dest] = lower.__dict__[dest]
|
||||
|
||||
|
||||
def load_library(name, cdll):
|
||||
if iswindows:
|
||||
return cdll.LoadLibrary(name)
|
||||
@ -392,40 +170,6 @@ def fit_image(width, height, pwidth, pheight):
|
||||
|
||||
return scaled, int(width), int(height)
|
||||
|
||||
def get_lang():
|
||||
lang = locale.getdefaultlocale()[0]
|
||||
if lang is None and os.environ.has_key('LANG'): # Needed for OS X
|
||||
try:
|
||||
lang = os.environ['LANG']
|
||||
except:
|
||||
pass
|
||||
if lang:
|
||||
match = re.match('[a-z]{2,3}', lang)
|
||||
if match:
|
||||
lang = match.group()
|
||||
return lang
|
||||
|
||||
def set_translator():
|
||||
# To test different translations invoke as
|
||||
# LC_ALL=de_DE.utf8 program
|
||||
try:
|
||||
from calibre.translations.compiled import translations
|
||||
except:
|
||||
return
|
||||
lang = get_lang()
|
||||
if lang:
|
||||
buf = None
|
||||
if os.access(lang+'.po', os.R_OK):
|
||||
buf = cStringIO.StringIO()
|
||||
make(lang+'.po', buf)
|
||||
buf = cStringIO.StringIO(buf.getvalue())
|
||||
elif translations.has_key(lang):
|
||||
buf = cStringIO.StringIO(translations[lang])
|
||||
if buf is not None:
|
||||
t = GNUTranslations(buf)
|
||||
t.install(unicode=True)
|
||||
|
||||
set_translator()
|
||||
|
||||
def sanitize_file_name(name):
|
||||
'''
|
||||
@ -510,120 +254,6 @@ def relpath(target, base=os.curdir):
|
||||
rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:]
|
||||
return os.path.join(*rel_list)
|
||||
|
||||
def _clean_lock_file(file):
|
||||
try:
|
||||
file.close()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
os.remove(file.name)
|
||||
except:
|
||||
pass
|
||||
|
||||
class LockError(Exception):
|
||||
pass
|
||||
class ExclusiveFile(object):
|
||||
|
||||
def __init__(self, path, timeout=10):
|
||||
self.path = path
|
||||
self.timeout = timeout
|
||||
|
||||
def __enter__(self):
|
||||
self.file = open(self.path, 'a+b')
|
||||
self.file.seek(0)
|
||||
timeout = self.timeout
|
||||
if iswindows:
|
||||
name = ('Local\\'+(__appname__+self.file.name).replace('\\', '_'))[:201]
|
||||
while self.timeout < 0 or timeout >= 0:
|
||||
self.mutex = win32event.CreateMutex(None, False, name)
|
||||
if win32api.GetLastError() != winerror.ERROR_ALREADY_EXISTS: break
|
||||
time.sleep(1)
|
||||
timeout -= 1
|
||||
else:
|
||||
while self.timeout < 0 or timeout >= 0:
|
||||
try:
|
||||
fcntl.lockf(self.file.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB)
|
||||
break
|
||||
except IOError:
|
||||
time.sleep(1)
|
||||
timeout -= 1
|
||||
if timeout < 0 and self.timeout >= 0:
|
||||
self.file.close()
|
||||
raise LockError
|
||||
return self.file
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.file.close()
|
||||
if iswindows:
|
||||
win32api.CloseHandle(self.mutex)
|
||||
|
||||
def singleinstance(name):
|
||||
'''
|
||||
Return True if no other instance of the application identified by name is running,
|
||||
False otherwise.
|
||||
@param name: The name to lock.
|
||||
@type name: string
|
||||
'''
|
||||
if iswindows:
|
||||
mutexname = 'mutexforsingleinstanceof'+__appname__+name
|
||||
mutex = win32event.CreateMutex(None, False, mutexname)
|
||||
if mutex:
|
||||
atexit.register(win32api.CloseHandle, mutex)
|
||||
return not win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS
|
||||
else:
|
||||
global _lock_file
|
||||
path = os.path.expanduser('~/.'+__appname__+'_'+name+'.lock')
|
||||
try:
|
||||
f = open(path, 'w')
|
||||
fcntl.lockf(f.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB)
|
||||
atexit.register(_clean_lock_file, f)
|
||||
return True
|
||||
except IOError:
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
class Settings(QSettings):
|
||||
|
||||
def __init__(self, name='calibre2'):
|
||||
QSettings.__init__(self, QSettings.IniFormat, QSettings.UserScope,
|
||||
'kovidgoyal.net', name)
|
||||
|
||||
def get(self, key, default=None):
|
||||
try:
|
||||
key = str(key)
|
||||
if not self.contains(key):
|
||||
return default
|
||||
val = str(self.value(key, QVariant()).toByteArray())
|
||||
if not val:
|
||||
return None
|
||||
return cPickle.loads(val)
|
||||
except:
|
||||
return default
|
||||
|
||||
def set(self, key, val):
|
||||
val = cPickle.dumps(val, -1)
|
||||
self.setValue(str(key), QVariant(QByteArray(val)))
|
||||
|
||||
_settings = Settings()
|
||||
|
||||
if not _settings.get('rationalized'):
|
||||
__settings = Settings(name='calibre')
|
||||
dbpath = os.path.join(os.path.expanduser('~'), 'library1.db').decode(sys.getfilesystemencoding())
|
||||
dbpath = unicode(__settings.value('database path',
|
||||
QVariant(QString.fromUtf8(dbpath.encode('utf-8')))).toString())
|
||||
cmdline = __settings.value('LRF conversion defaults', QVariant(QByteArray(''))).toByteArray().data()
|
||||
|
||||
if cmdline:
|
||||
cmdline = cPickle.loads(cmdline)
|
||||
_settings.set('LRF conversion defaults', cmdline)
|
||||
_settings.set('rationalized', True)
|
||||
try:
|
||||
os.unlink(unicode(__settings.fileName()))
|
||||
except:
|
||||
pass
|
||||
_settings.set('database path', dbpath)
|
||||
|
||||
_spat = re.compile(r'^the\s+|^a\s+|^an\s+', re.IGNORECASE)
|
||||
def english_sort(x, y):
|
||||
'''
|
||||
@ -671,11 +301,8 @@ def strftime(fmt, t=time.localtime()):
|
||||
A version of strtime that returns unicode strings.
|
||||
'''
|
||||
result = time.strftime(fmt, t)
|
||||
try:
|
||||
return unicode(result, locale.getpreferredencoding(), 'replace')
|
||||
except:
|
||||
return unicode(result, 'utf-8', 'replace')
|
||||
|
||||
return unicode(result, preferred_encoding, 'replace')
|
||||
|
||||
def entity_to_unicode(match, exceptions=[], encoding='cp1252'):
|
||||
'''
|
||||
@param match: A match object such that '&'+match.group(1)';' is the entity.
|
||||
@ -708,7 +335,7 @@ def entity_to_unicode(match, exceptions=[], encoding='cp1252'):
|
||||
return unichr(name2codepoint[ent])
|
||||
except KeyError:
|
||||
return '&'+ent+';'
|
||||
|
||||
|
||||
if isosx:
|
||||
fdir = os.path.expanduser('~/.fonts')
|
||||
if not os.path.exists(fdir):
|
||||
@ -716,6 +343,10 @@ if isosx:
|
||||
if not os.path.exists(os.path.join(fdir, 'LiberationSans_Regular.ttf')):
|
||||
from calibre.ebooks.lrf.fonts.liberation import __all__ as fonts
|
||||
for font in fonts:
|
||||
exec 'from calibre.ebooks.lrf.fonts.liberation.'+font+' import font_data'
|
||||
open(os.path.join(fdir, font+'.ttf'), 'wb').write(font_data)
|
||||
l = {}
|
||||
exec 'from calibre.ebooks.lrf.fonts.liberation.'+font+' import font_data' in l
|
||||
open(os.path.join(fdir, font+'.ttf'), 'wb').write(l['font_data'])
|
||||
|
||||
# Migrate from QSettings based config system
|
||||
from calibre.utils.config import migrate
|
||||
migrate()
|
30
src/calibre/constants.py
Normal file
30
src/calibre/constants.py
Normal file
@ -0,0 +1,30 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = 'calibre'
|
||||
__version__ = '0.4.83'
|
||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
'''
|
||||
Various run time constants.
|
||||
'''
|
||||
|
||||
import sys, locale, codecs
|
||||
from calibre.utils.terminfo import TerminalController
|
||||
|
||||
terminal_controller = TerminalController(sys.stdout)
|
||||
|
||||
iswindows = 'win32' in sys.platform.lower() or 'win64' in sys.platform.lower()
|
||||
isosx = 'darwin' in sys.platform.lower()
|
||||
islinux = not(iswindows or isosx)
|
||||
isfrozen = hasattr(sys, 'frozen')
|
||||
|
||||
try:
|
||||
preferred_encoding = locale.getpreferredencoding()
|
||||
codecs.lookup(preferred_encoding)
|
||||
except:
|
||||
preferred_encoding = 'utf-8'
|
||||
|
||||
win32event = __import__('win32event') if iswindows else None
|
||||
winerror = __import__('winerror') if iswindows else None
|
||||
win32api = __import__('win32api') if iswindows else None
|
||||
fcntl = None if iswindows else __import__('fcntl')
|
@ -7,7 +7,8 @@ Embedded console for debugging.
|
||||
'''
|
||||
|
||||
import sys, os, re
|
||||
from calibre import OptionParser, iswindows, isosx
|
||||
from calibre.utils.config import OptionParser
|
||||
from calibre.constants import iswindows, isosx
|
||||
from calibre.libunzip import update
|
||||
|
||||
def option_parser():
|
||||
|
@ -808,7 +808,7 @@ class LitReader(object):
|
||||
os.makedirs(dir)
|
||||
|
||||
def option_parser():
|
||||
from calibre import OptionParser
|
||||
from calibre.utils.config import OptionParser
|
||||
parser = OptionParser(usage=_('%prog [options] LITFILE'))
|
||||
parser.add_option(
|
||||
'-o', '--output-dir', default='.',
|
||||
|
@ -14,7 +14,8 @@ from calibre.ebooks.lrf.pylrs.pylrs import TextBlock, Header, PutObj, \
|
||||
Paragraph, TextStyle, BlockStyle
|
||||
from calibre.ebooks.lrf.fonts import FONT_FILE_MAP
|
||||
from calibre.ebooks import ConversionError
|
||||
from calibre import __appname__, __version__, __author__, iswindows, OptionParser
|
||||
from calibre import __appname__, __version__, __author__, iswindows
|
||||
from calibre.utils.config import OptionParser
|
||||
|
||||
__docformat__ = "epytext"
|
||||
|
||||
|
@ -2,7 +2,8 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import sys, logging, os
|
||||
|
||||
from calibre import setup_cli_handlers, OptionParser
|
||||
from calibre import setup_cli_handlers
|
||||
from calibre.utils.config import OptionParser
|
||||
from calibre.ebooks import ConversionError
|
||||
from calibre.ebooks.lrf.meta import get_metadata
|
||||
from calibre.ebooks.lrf.lrfparser import LRFDocument
|
||||
|
@ -4,7 +4,8 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import sys, array, os, re, codecs, logging
|
||||
|
||||
from calibre import OptionParser, setup_cli_handlers
|
||||
from calibre import setup_cli_handlers
|
||||
from calibre.utils.config import OptionParser
|
||||
from calibre.ebooks.lrf.meta import LRFMetaFile
|
||||
from calibre.ebooks.lrf.objects import get_object, PageTree, StyleObject, \
|
||||
Font, Text, TOCObject, BookAttr, ruby_tags
|
||||
|
@ -6,7 +6,8 @@ Compile a LRS file into a LRF file.
|
||||
|
||||
import sys, os, logging
|
||||
|
||||
from calibre import OptionParser, setup_cli_handlers
|
||||
from calibre import setup_cli_handlers
|
||||
from calibre.utils.config import OptionParser
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup, NavigableString, \
|
||||
CData, Tag
|
||||
from calibre.ebooks.lrf.pylrs.pylrs import Book, PageStyle, TextStyle, \
|
||||
|
@ -574,8 +574,8 @@ class LRFMetaFile(object):
|
||||
|
||||
|
||||
def option_parser():
|
||||
from optparse import OptionParser
|
||||
from calibre import __appname__, __version__
|
||||
from calibre.utils.config import OptionParser
|
||||
from calibre.constants import __appname__, __version__
|
||||
parser = OptionParser(usage = \
|
||||
_('''%prog [options] mybook.lrf
|
||||
|
||||
|
@ -7,7 +7,8 @@ Convert PDF to a reflowable format using pdftoxml.exe as the PDF parsing backend
|
||||
import sys, os, re, tempfile, subprocess, atexit, shutil, logging, xml.parsers.expat
|
||||
from xml.etree.ElementTree import parse
|
||||
|
||||
from calibre import isosx, OptionParser, setup_cli_handlers, __appname__
|
||||
from calibre import isosx, setup_cli_handlers, __appname__
|
||||
from calibre.utils.config import OptionParser
|
||||
from calibre.ebooks import ConversionError
|
||||
|
||||
PDFTOXML = 'pdftoxml.exe'
|
||||
|
@ -11,8 +11,9 @@ from urllib import unquote, quote
|
||||
from urlparse import urlparse
|
||||
|
||||
|
||||
from calibre import __version__ as VERSION, relpath
|
||||
from calibre import OptionParser
|
||||
from calibre.constants import __version__ as VERSION
|
||||
from calibre import relpath
|
||||
from calibre.utils.config import OptionParser
|
||||
|
||||
def get_parser(extension):
|
||||
''' Return an option parser with the basic metadata options already setup'''
|
||||
|
@ -7,7 +7,8 @@ Interface to isbndb.com. My key HLLXQX2A.
|
||||
import sys, logging, re, socket
|
||||
from urllib import urlopen, quote
|
||||
|
||||
from calibre import setup_cli_handlers, OptionParser
|
||||
from calibre import setup_cli_handlers
|
||||
from calibre.utils.config import OptionParser
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulStoneSoup
|
||||
|
||||
|
@ -6,7 +6,8 @@ Fetch cover from LibraryThing.com based on ISBN number.
|
||||
|
||||
import sys, socket, os, re, mechanize
|
||||
|
||||
from calibre import browser as _browser, OptionParser
|
||||
from calibre import browser as _browser
|
||||
from calibre.utils.config import OptionParser
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||
browser = None
|
||||
|
||||
|
@ -3,6 +3,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import os, re, collections
|
||||
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.ebooks.metadata.rtf import get_metadata as rtf_metadata
|
||||
from calibre.ebooks.metadata.fb2 import get_metadata as fb2_metadata
|
||||
from calibre.ebooks.lrf.meta import get_metadata as lrf_metadata
|
||||
@ -94,20 +95,11 @@ def set_metadata(stream, mi, stream_type='lrf'):
|
||||
elif stream_type == 'rtf':
|
||||
set_rtf_metadata(stream, mi)
|
||||
|
||||
_filename_pat = re.compile(ur'(?P<title>.+) - (?P<author>[^_]+)')
|
||||
|
||||
def get_filename_pat():
|
||||
return _filename_pat.pattern
|
||||
|
||||
def set_filename_pat(pat):
|
||||
global _filename_pat
|
||||
_filename_pat = re.compile(pat)
|
||||
|
||||
def metadata_from_filename(name, pat=None):
|
||||
name = os.path.splitext(name)[0]
|
||||
mi = MetaInformation(None, None)
|
||||
if pat is None:
|
||||
pat = _filename_pat
|
||||
pat = re.compile(prefs.get('filename_pattern'))
|
||||
match = pat.search(name)
|
||||
if match:
|
||||
try:
|
||||
|
@ -408,7 +408,7 @@ def get_metadata(stream):
|
||||
return mi
|
||||
|
||||
def option_parser():
|
||||
from calibre import OptionParser
|
||||
from calibre.utils.config import OptionParser
|
||||
parser = OptionParser(usage=_('%prog [options] myebook.mobi'))
|
||||
parser.add_option('-o', '--output-dir', default='.',
|
||||
help=_('Output directory. Defaults to current directory.'))
|
||||
|
@ -2,19 +2,49 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
""" The GUI """
|
||||
import sys, os, re, StringIO, traceback
|
||||
from PyQt4.QtCore import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \
|
||||
from PyQt4.QtCore import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, QSize, \
|
||||
QByteArray, QLocale, QUrl, QTranslator, QCoreApplication
|
||||
from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \
|
||||
QIcon, QTableView, QDialogButtonBox, QApplication
|
||||
|
||||
ORG_NAME = 'KovidsBrain'
|
||||
APP_UID = 'libprs500'
|
||||
from calibre import __author__, islinux, iswindows, Settings, isosx, get_lang
|
||||
from calibre import __author__, islinux, iswindows, isosx
|
||||
from calibre.startup import get_lang
|
||||
from calibre.utils.config import Config, ConfigProxy, dynamic
|
||||
import calibre.resources as resources
|
||||
|
||||
NONE = QVariant() #: Null value to return from the data function of item models
|
||||
|
||||
|
||||
def _config():
|
||||
c = Config('gui', 'preferences for the calibre GUI')
|
||||
c.add_opt('frequently_used_directories', default=[],
|
||||
help=_('Frequently used directories'))
|
||||
c.add_opt('send_to_device_by_default', default=True,
|
||||
help=_('Send downloaded periodical content to device automatically'))
|
||||
c.add_opt('save_to_disk_single_format', default='lrf',
|
||||
help=_('The format to use when saving single files to disk'))
|
||||
c.add_opt('confirm_delete', default=False,
|
||||
help=_('Confirm before deleting'))
|
||||
c.add_opt('toolbar_icon_size', default=QSize(48, 48),
|
||||
help=_('Toolbar icon size')) # value QVariant.toSize
|
||||
c.add_opt('show_text_in_toolbar', default=True,
|
||||
help=_('Show button labels in the toolbar'))
|
||||
c.add_opt('main_window_geometry', default=None,
|
||||
help=_('Main window geometry')) # value QVariant.toByteArray
|
||||
c.add_opt('new_version_notification', default=True,
|
||||
help=_('Notify when a new version is available'))
|
||||
c.add_opt('use_roman_numerals_for_series_number', default=True,
|
||||
help=_('Use Roman numerals for series number'))
|
||||
c.add_opt('cover_flow_queue_length', default=6,
|
||||
help=_('Number of covers to show in the cover browsing mode'))
|
||||
c.add_opt('LRF_conversion_defaults', default=[],
|
||||
help=_('Defaults for conversion to LRF'))
|
||||
c.add_opt('LRF_ebook_viewer_options', default=None,
|
||||
help=_('Options for the LRF ebook viewer'))
|
||||
return ConfigProxy(c)
|
||||
|
||||
config = _config()
|
||||
# Turn off DeprecationWarnings in windows GUI
|
||||
if iswindows:
|
||||
import warnings
|
||||
@ -105,14 +135,12 @@ class TableView(QTableView):
|
||||
QTableView.__init__(self, parent)
|
||||
self.read_settings()
|
||||
|
||||
|
||||
def read_settings(self):
|
||||
self.cw = Settings().get(self.__class__.__name__ + ' column widths')
|
||||
self.cw = dynamic[self.__class__.__name__+'column widths']
|
||||
|
||||
def write_settings(self):
|
||||
settings = Settings()
|
||||
settings.set(self.__class__.__name__ + ' column widths',
|
||||
tuple([int(self.columnWidth(i)) for i in range(self.model().columnCount(None))]))
|
||||
dynamic[self.__class__.__name__+'column widths'] = \
|
||||
tuple([int(self.columnWidth(i)) for i in range(self.model().columnCount(None))])
|
||||
|
||||
def restore_column_widths(self):
|
||||
if self.cw and len(self.cw):
|
||||
@ -125,10 +153,11 @@ class TableView(QTableView):
|
||||
@param cols: A list of booleans or None. If an entry is False the corresponding column
|
||||
is hidden, if True it is shown.
|
||||
'''
|
||||
key = self.__class__.__name__+'visible columns'
|
||||
if cols:
|
||||
Settings().set(self.__class__.__name__ + ' visible columns', cols)
|
||||
dynamic[key] = cols
|
||||
else:
|
||||
cols = Settings().get(self.__class__.__name__ + ' visible columns')
|
||||
cols = dynamic[key]
|
||||
if not cols:
|
||||
cols = [True for i in range(self.model().columnCount(self))]
|
||||
|
||||
@ -232,7 +261,7 @@ _sidebar_directories = []
|
||||
def set_sidebar_directories(dirs):
|
||||
global _sidebar_directories
|
||||
if dirs is None:
|
||||
dirs = Settings().get('frequently used directories', [])
|
||||
dirs = config['frequently_used_directories']
|
||||
_sidebar_directories = [QUrl.fromLocalFile(i) for i in dirs]
|
||||
|
||||
class FileDialog(QObject):
|
||||
@ -255,10 +284,10 @@ class FileDialog(QObject):
|
||||
if add_all_files_filter or not ftext:
|
||||
ftext += 'All files (*)'
|
||||
|
||||
settings = Settings()
|
||||
self.dialog_name = name if name else 'dialog_' + title
|
||||
self.selected_files = None
|
||||
self.fd = None
|
||||
|
||||
if islinux:
|
||||
self.fd = QFileDialog(parent)
|
||||
self.fd.setFileMode(mode)
|
||||
@ -266,15 +295,15 @@ class FileDialog(QObject):
|
||||
self.fd.setModal(modal)
|
||||
self.fd.setFilter(ftext)
|
||||
self.fd.setWindowTitle(title)
|
||||
state = settings.get(self.dialog_name, QByteArray())
|
||||
if not self.fd.restoreState(state):
|
||||
state = dynamic[self.dialog_name]
|
||||
if not state or not self.fd.restoreState(state):
|
||||
self.fd.setDirectory(os.path.expanduser('~'))
|
||||
osu = [i for i in self.fd.sidebarUrls()]
|
||||
self.fd.setSidebarUrls(osu + _sidebar_directories)
|
||||
QObject.connect(self.fd, SIGNAL('accepted()'), self.save_dir)
|
||||
self.accepted = self.fd.exec_() == QFileDialog.Accepted
|
||||
else:
|
||||
dir = settings.get(self.dialog_name, os.path.expanduser('~'))
|
||||
dir = dynamic.get(self.dialog_name, default=os.path.expanduser('~'))
|
||||
self.selected_files = []
|
||||
if mode == QFileDialog.AnyFile:
|
||||
f = qstring_to_unicode(
|
||||
@ -299,7 +328,7 @@ class FileDialog(QObject):
|
||||
self.selected_files.append(f)
|
||||
if self.selected_files:
|
||||
self.selected_files = [qstring_to_unicode(q) for q in self.selected_files]
|
||||
settings.set(self.dialog_name, os.path.dirname(self.selected_files[0]))
|
||||
dynamic[self.dialog_name] = os.path.dirname(self.selected_files[0])
|
||||
self.accepted = bool(self.selected_files)
|
||||
|
||||
|
||||
@ -313,8 +342,7 @@ class FileDialog(QObject):
|
||||
|
||||
def save_dir(self):
|
||||
if self.fd:
|
||||
settings = Settings()
|
||||
settings.set(self.dialog_name, self.fd.saveState())
|
||||
dynamic[self.dialog_name] = self.fd.saveState()
|
||||
|
||||
|
||||
def choose_dir(window, name, title):
|
||||
|
@ -12,7 +12,8 @@ import sys, os
|
||||
from PyQt4.QtGui import QImage, QSizePolicy
|
||||
from PyQt4.QtCore import Qt, QSize, SIGNAL, QObject
|
||||
|
||||
from calibre import Settings, plugins
|
||||
from calibre import plugins
|
||||
from calibre.gui2 import config
|
||||
pictureflow, pictureflowerror = plugins['pictureflow']
|
||||
|
||||
if pictureflow is not None:
|
||||
@ -70,7 +71,7 @@ if pictureflow is not None:
|
||||
|
||||
def __init__(self, height=300, parent=None):
|
||||
pictureflow.PictureFlow.__init__(self, parent,
|
||||
Settings().get('cover flow queue length', 6)+1)
|
||||
config['cover_flow_queue_length']+1)
|
||||
self.setSlideSize(QSize(int(2/3. * height), height))
|
||||
self.setMinimumSize(QSize(int(2.35*0.67*height), (5/3.)*height+25))
|
||||
self.setFocusPolicy(Qt.WheelFocus)
|
||||
|
@ -5,9 +5,10 @@ import os
|
||||
from PyQt4.QtGui import QDialog, QMessageBox, QListWidgetItem, QIcon
|
||||
from PyQt4.QtCore import SIGNAL, QTimer, Qt, QSize, QVariant
|
||||
|
||||
from calibre import islinux, Settings
|
||||
from calibre import islinux
|
||||
from calibre.gui2.dialogs.config_ui import Ui_Dialog
|
||||
from calibre.gui2 import qstring_to_unicode, choose_dir, error_dialog
|
||||
from calibre.gui2 import qstring_to_unicode, choose_dir, error_dialog, config
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.gui2.widgets import FilenamePattern
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
|
||||
@ -24,18 +25,17 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
self.item2 = QListWidgetItem(QIcon(':/images/view.svg'), _('Advanced'), self.category_list)
|
||||
self.db = db
|
||||
self.current_cols = columns
|
||||
settings = Settings()
|
||||
path = settings.get('database path')
|
||||
path = prefs['database_path']
|
||||
|
||||
self.location.setText(os.path.dirname(path))
|
||||
self.connect(self.browse_button, SIGNAL('clicked(bool)'), self.browse)
|
||||
self.connect(self.compact_button, SIGNAL('clicked(bool)'), self.compact)
|
||||
|
||||
dirs = settings.get('frequently used directories', [])
|
||||
rn = settings.get('use roman numerals for series number', True)
|
||||
self.timeout.setValue(settings.get('network timeout', 5))
|
||||
dirs = config['frequently_used_directories']
|
||||
rn = config['use_roman_numerals_for_series_number']
|
||||
self.timeout.setValue(prefs['network_timeout'])
|
||||
self.roman_numerals.setChecked(rn)
|
||||
self.new_version_notification.setChecked(settings.get('new version notification', True))
|
||||
self.new_version_notification.setChecked(config['new_version_notification'])
|
||||
self.directory_list.addItems(dirs)
|
||||
self.connect(self.add_button, SIGNAL('clicked(bool)'), self.add_dir)
|
||||
self.connect(self.remove_button, SIGNAL('clicked(bool)'), self.remove_dir)
|
||||
@ -57,17 +57,17 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
self.filename_pattern = FilenamePattern(self)
|
||||
self.metadata_box.layout().insertWidget(0, self.filename_pattern)
|
||||
|
||||
icons = settings.get('toolbar icon size', self.ICON_SIZES[0])
|
||||
icons = config['toolbar_icon_size']
|
||||
self.toolbar_button_size.setCurrentIndex(0 if icons == self.ICON_SIZES[0] else 1 if icons == self.ICON_SIZES[1] else 2)
|
||||
self.show_toolbar_text.setChecked(settings.get('show text in toolbar', True))
|
||||
self.show_toolbar_text.setChecked(config['show_text_in_toolbar'])
|
||||
|
||||
for ext in BOOK_EXTENSIONS:
|
||||
self.single_format.addItem(ext.upper(), QVariant(ext))
|
||||
|
||||
single_format = settings.get('save to disk single format', 'lrf')
|
||||
single_format = config['save_to_disk_single_format']
|
||||
self.single_format.setCurrentIndex(BOOK_EXTENSIONS.index(single_format))
|
||||
self.cover_browse.setValue(settings.get('cover flow queue length', 6))
|
||||
self.confirm_delete.setChecked(settings.get('confirm delete', False))
|
||||
self.cover_browse.setValue(config['cover_flow_queue_length'])
|
||||
self.confirm_delete.setChecked(config['confirm_delete'])
|
||||
|
||||
def compact(self, toggled):
|
||||
d = Vacuum(self, self.db)
|
||||
@ -89,19 +89,18 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
self.directory_list.takeItem(idx)
|
||||
|
||||
def accept(self):
|
||||
settings = Settings()
|
||||
settings.set('use roman numerals for series number', bool(self.roman_numerals.isChecked()))
|
||||
settings.set('new version notification', bool(self.new_version_notification.isChecked()))
|
||||
settings.set('network timeout', int(self.timeout.value()))
|
||||
config['use_roman_numerals_for_series_number'] = bool(self.roman_numerals.isChecked())
|
||||
config['new_version_notification'] = bool(self.new_version_notification.isChecked())
|
||||
prefs['network_timeout'] = int(self.timeout.value())
|
||||
path = qstring_to_unicode(self.location.text())
|
||||
self.final_columns = [self.columns.item(i).checkState() == Qt.Checked for i in range(self.columns.count())]
|
||||
settings.set('toolbar icon size', self.ICON_SIZES[self.toolbar_button_size.currentIndex()])
|
||||
settings.set('show text in toolbar', bool(self.show_toolbar_text.isChecked()))
|
||||
settings.set('confirm delete', bool(self.confirm_delete.isChecked()))
|
||||
config['toolbar_icon_size'] = self.ICON_SIZES[self.toolbar_button_size.currentIndex()]
|
||||
config['show_text_in_toolbar'] = bool(self.show_toolbar_text.isChecked())
|
||||
config['confirm_delete'] = bool(self.confirm_delete.isChecked())
|
||||
pattern = self.filename_pattern.commit()
|
||||
settings.set('filename pattern', pattern)
|
||||
settings.set('save to disk single format', BOOK_EXTENSIONS[self.single_format.currentIndex()])
|
||||
settings.set('cover flow queue length', self.cover_browse.value())
|
||||
config['filename_pattern'] = pattern
|
||||
config['save_to_disk_single_format'] = BOOK_EXTENSIONS[self.single_format.currentIndex()]
|
||||
config['cover_flow_queue_length'] = self.cover_browse.value()
|
||||
|
||||
if not path or not os.path.exists(path) or not os.path.isdir(path):
|
||||
d = error_dialog(self, _('Invalid database location'), _('Invalid database location ')+path+_('<br>Must be a directory.'))
|
||||
@ -112,7 +111,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
else:
|
||||
self.database_location = os.path.abspath(path)
|
||||
self.directories = [qstring_to_unicode(self.directory_list.item(i).text()) for i in range(self.directory_list.count())]
|
||||
settings.set('frequently used directories', self.directories)
|
||||
config['frequently_used_directories'] = self.directories
|
||||
QDialog.accept(self)
|
||||
|
||||
class Vacuum(QMessageBox):
|
||||
|
@ -13,7 +13,7 @@ from PyQt4.QtGui import QDialog, QItemSelectionModel
|
||||
from calibre.gui2.dialogs.fetch_metadata_ui import Ui_FetchMetadata
|
||||
from calibre.gui2 import error_dialog, NONE, info_dialog
|
||||
from calibre.ebooks.metadata.isbndb import create_books, option_parser, ISBNDBError
|
||||
from calibre import Settings
|
||||
from calibre.utils.config import prefs
|
||||
|
||||
class Matches(QAbstractTableModel):
|
||||
|
||||
@ -76,7 +76,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata):
|
||||
self.timeout = timeout
|
||||
QObject.connect(self.fetch, SIGNAL('clicked()'), self.fetch_metadata)
|
||||
|
||||
self.key.setText(Settings().get('isbndb.com key', ''))
|
||||
self.key.setText(prefs['isbndb_com_key'])
|
||||
|
||||
self.setWindowTitle(title if title else 'Unknown')
|
||||
self.tlabel.setText(self.tlabel.text().arg(title if title else 'Unknown'))
|
||||
@ -105,7 +105,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata):
|
||||
_('You must specify a valid access key for isbndb.com'))
|
||||
return
|
||||
else:
|
||||
Settings().set('isbndb.com key', key)
|
||||
prefs['isbndb_com_key'] = key
|
||||
|
||||
args = ['isbndb']
|
||||
if self.isbn:
|
||||
|
@ -9,11 +9,11 @@ from PyQt4.QtGui import QAbstractSpinBox, QLineEdit, QCheckBox, QDialog, \
|
||||
from calibre.gui2.dialogs.lrf_single_ui import Ui_LRFSingleDialog
|
||||
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
|
||||
from calibre.gui2 import qstring_to_unicode, error_dialog, \
|
||||
pixmap_to_data, choose_images
|
||||
pixmap_to_data, choose_images, config
|
||||
from calibre.gui2.widgets import FontFamilyModel
|
||||
from calibre.ebooks.lrf import option_parser
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre import __appname__, Settings
|
||||
from calibre.constants import __appname__
|
||||
|
||||
font_family_model = None
|
||||
|
||||
@ -109,7 +109,7 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog):
|
||||
|
||||
|
||||
def load_saved_global_defaults(self):
|
||||
cmdline = Settings().get('LRF conversion defaults')
|
||||
cmdline = config['LRF_conversion_defaults']
|
||||
if cmdline:
|
||||
self.set_options_from_cmdline(cmdline)
|
||||
|
||||
@ -163,7 +163,7 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog):
|
||||
|
||||
def select_cover(self, checked):
|
||||
files = choose_images(self, 'change cover dialog',
|
||||
u'Choose cover for ' + qstring_to_unicode(self.gui_title.text()))
|
||||
_('Choose cover for ') + qstring_to_unicode(self.gui_title.text()))
|
||||
if not files:
|
||||
return
|
||||
_file = files[0]
|
||||
@ -385,7 +385,7 @@ class LRFSingleDialog(QDialog, Ui_LRFSingleDialog):
|
||||
cmdline.extend([u'--cover', self.cover_file.name])
|
||||
self.cmdline = [unicode(i) for i in cmdline]
|
||||
else:
|
||||
Settings().set('LRF conversion defaults', cmdline)
|
||||
config.set('LRF_conversion_defaults', cmdline)
|
||||
QDialog.accept(self)
|
||||
|
||||
class LRFBulkDialog(LRFSingleDialog):
|
||||
|
@ -18,7 +18,8 @@ from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||
from calibre.gui2.dialogs.password import PasswordDialog
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
from calibre.ebooks.metadata.library_thing import login, cover_from_isbn, LibraryThingError
|
||||
from calibre import Settings, islinux
|
||||
from calibre import islinux
|
||||
from calibre.utils.config import prefs
|
||||
|
||||
class Format(QListWidgetItem):
|
||||
def __init__(self, parent, ext, size, path=None):
|
||||
@ -145,7 +146,7 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
|
||||
QObject.connect(self.remove_series_button, SIGNAL('clicked()'),
|
||||
self.remove_unused_series)
|
||||
self.connect(self.swap_button, SIGNAL('clicked()'), self.swap_title_author)
|
||||
self.timeout = float(Settings().get('network timeout', 5))
|
||||
self.timeout = float(prefs['network_timeout'])
|
||||
self.title.setText(db.title(row))
|
||||
isbn = db.isbn(self.id, index_is_id=True)
|
||||
if not isbn:
|
||||
|
@ -1,12 +1,11 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import re
|
||||
from PyQt4.QtGui import QDialog, QLineEdit
|
||||
from PyQt4.QtCore import QVariant, SIGNAL, Qt
|
||||
from PyQt4.QtCore import SIGNAL, Qt
|
||||
|
||||
from calibre.gui2.dialogs.password_ui import Ui_Dialog
|
||||
from calibre.gui2 import qstring_to_unicode
|
||||
from calibre import Settings
|
||||
from calibre.gui2 import qstring_to_unicode, dynamic
|
||||
|
||||
class PasswordDialog(QDialog, Ui_Dialog):
|
||||
|
||||
@ -14,10 +13,12 @@ class PasswordDialog(QDialog, Ui_Dialog):
|
||||
QDialog.__init__(self, window)
|
||||
Ui_Dialog.__init__(self)
|
||||
self.setupUi(self)
|
||||
self.cfg_key = re.sub(r'[^0-9a-zA-Z]', '_', name)
|
||||
|
||||
settings = Settings()
|
||||
un = settings.get(name+': un', u'')
|
||||
pw = settings.get(name+': pw', u'')
|
||||
un = dynamic[self.cfg_key+'__un']
|
||||
pw = dynamic[self.cfg_key+'__un']
|
||||
if not un: un = ''
|
||||
if not pw: pw = ''
|
||||
self.gui_username.setText(un)
|
||||
self.gui_password.setText(pw)
|
||||
self.sname = name
|
||||
@ -37,7 +38,6 @@ class PasswordDialog(QDialog, Ui_Dialog):
|
||||
return qstring_to_unicode(self.gui_password.text())
|
||||
|
||||
def accept(self):
|
||||
settings = Settings()
|
||||
settings.set(self.sname+': un', unicode(self.gui_username.text()))
|
||||
settings.set(self.sname+': pw', unicode(self.gui_password.text()))
|
||||
dynamic.set(self.cfg_key+'__un', unicode(self.gui_username.text()))
|
||||
dynamic.set(self.cfg_key+'__pw', unicode(self.gui_password.text()))
|
||||
QDialog.accept(self)
|
||||
|
@ -14,10 +14,10 @@ from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, QString, \
|
||||
QCoreApplication, SIGNAL, QObject, QSize, QModelIndex, \
|
||||
QTimer
|
||||
|
||||
from calibre import Settings, preferred_encoding
|
||||
from calibre import preferred_encoding
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.library.database import LibraryDatabase, text_to_tokens
|
||||
from calibre.gui2 import NONE, TableView, qstring_to_unicode
|
||||
from calibre.gui2 import NONE, TableView, qstring_to_unicode, config
|
||||
|
||||
class LibraryDelegate(QItemDelegate):
|
||||
COLOR = QColor("blue")
|
||||
@ -117,7 +117,7 @@ class BooksModel(QAbstractTableModel):
|
||||
self.load_queue = deque()
|
||||
|
||||
def read_config(self):
|
||||
self.use_roman_numbers = Settings().get('use roman numerals for series number', True)
|
||||
self.use_roman_numbers = config['use_roman_numerals_for_series_number']
|
||||
|
||||
|
||||
def set_database(self, db):
|
||||
|
@ -6,10 +6,11 @@ import sys, logging, os, traceback, time
|
||||
from PyQt4.QtGui import QKeySequence, QPainter, QDialog, QSpinBox, QSlider, QIcon
|
||||
from PyQt4.QtCore import Qt, QObject, SIGNAL, QCoreApplication, QThread
|
||||
|
||||
from calibre import __appname__, setup_cli_handlers, islinux, Settings
|
||||
from calibre import __appname__, setup_cli_handlers, islinux
|
||||
from calibre.ebooks.lrf.lrfparser import LRFDocument
|
||||
|
||||
from calibre.gui2 import ORG_NAME, APP_UID, error_dialog, choose_files, Application
|
||||
from calibre.gui2 import ORG_NAME, APP_UID, error_dialog, \
|
||||
config, choose_files, Application
|
||||
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
|
||||
from calibre.gui2.lrf_renderer.main_ui import Ui_MainWindow
|
||||
from calibre.gui2.lrf_renderer.config_ui import Ui_ViewerConfig
|
||||
@ -102,13 +103,15 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
|
||||
|
||||
def configure(self, triggered):
|
||||
opts = Settings().get('LRF ebook viewer options', self.opts)
|
||||
opts = config['LRF_ebook_viewer_options']
|
||||
if not opts:
|
||||
opts = self.opts
|
||||
d = Config(self, opts)
|
||||
d.exec_()
|
||||
if d.result() == QDialog.Accepted:
|
||||
opts.white_background = bool(d.white_background.isChecked())
|
||||
opts.hyphenate = bool(d.hyphenate.isChecked())
|
||||
Settings().set('LRF ebook viewer options', opts)
|
||||
config['LRF_ebook_viewer_options'] = opts
|
||||
|
||||
def set_ebook(self, stream):
|
||||
self.progress_bar.setMinimum(0)
|
||||
@ -281,7 +284,9 @@ Read the LRF ebook book.lrf
|
||||
return parser
|
||||
|
||||
def normalize_settings(parser, opts):
|
||||
saved_opts = Settings().get('LRF ebook viewer options', opts)
|
||||
saved_opts = config['LRF_ebook_viewer_options']
|
||||
if not saved_opts:
|
||||
saved_opts = opts
|
||||
for opt in parser.option_list:
|
||||
if not opt.dest:
|
||||
continue
|
||||
|
@ -3,23 +3,24 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import os, sys, textwrap, collections, traceback, shutil, time
|
||||
from xml.parsers.expat import ExpatError
|
||||
from functools import partial
|
||||
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \
|
||||
QVariant, QUrl, QSize
|
||||
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, QUrl
|
||||
from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \
|
||||
QToolButton, QDialog, QDesktopServices
|
||||
from PyQt4.QtSvg import QSvgRenderer
|
||||
|
||||
from calibre import __version__, __appname__, islinux, sanitize_file_name, \
|
||||
Settings, iswindows, isosx, preferred_encoding
|
||||
iswindows, isosx, preferred_encoding
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.ebooks.metadata.meta import get_metadata, get_filename_pat, set_filename_pat
|
||||
from calibre.ebooks.metadata.meta import get_metadata
|
||||
from calibre.devices.errors import FreeSpaceError
|
||||
from calibre.devices.interface import Device
|
||||
from calibre.utils.config import prefs, dynamic
|
||||
from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \
|
||||
initialize_file_icon_provider, question_dialog,\
|
||||
pixmap_to_data, choose_dir, ORG_NAME, \
|
||||
set_sidebar_directories, Dispatcher, \
|
||||
SingleApplication, Application, available_height, max_available_height
|
||||
SingleApplication, Application, available_height, \
|
||||
max_available_height, config
|
||||
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
|
||||
from calibre.library.database import LibraryDatabase
|
||||
from calibre.gui2.update import CheckForUpdates
|
||||
@ -120,12 +121,12 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
sm.addAction(_('Send to storage card by default'))
|
||||
sm.actions()[-1].setCheckable(True)
|
||||
def default_sync(checked):
|
||||
Settings().set('send to device by default', bool(checked))
|
||||
config.set('send_to_device_by_default', bool(checked))
|
||||
QObject.disconnect(self.action_sync, SIGNAL("triggered(bool)"), self.sync_to_main_memory)
|
||||
QObject.disconnect(self.action_sync, SIGNAL("triggered(bool)"), self.sync_to_card)
|
||||
QObject.connect(self.action_sync, SIGNAL("triggered(bool)"), self.sync_to_card if checked else self.sync_to_main_memory)
|
||||
QObject.connect(sm.actions()[-1], SIGNAL('toggled(bool)'), default_sync)
|
||||
sm.actions()[-1].setChecked(Settings().get('send to device by default', False))
|
||||
sm.actions()[-1].setChecked(config.get('send_to_device_by_default'))
|
||||
default_sync(sm.actions()[-1].isChecked())
|
||||
self.sync_menu = sm # Needed
|
||||
md = QMenu()
|
||||
@ -152,7 +153,7 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.save_menu = QMenu()
|
||||
self.save_menu.addAction(_('Save to disk'))
|
||||
self.save_menu.addAction(_('Save to disk in a single directory'))
|
||||
self.save_menu.addAction(_('Save only %s format to disk')%Settings().get('save to disk single format', 'lrf').upper())
|
||||
self.save_menu.addAction(_('Save only %s format to disk')%config.get('save_to_disk_single_format').upper())
|
||||
|
||||
self.view_menu = QMenu()
|
||||
self.view_menu.addAction(_('View'))
|
||||
@ -529,7 +530,7 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
rows = view.selectionModel().selectedRows()
|
||||
if not rows or len(rows) == 0:
|
||||
return
|
||||
if Settings().get('confirm delete', False):
|
||||
if config['confirm_delete']:
|
||||
d = question_dialog(self, _('Confirm delete'),
|
||||
_('Are you sure you want to delete these %d books?')%len(rows))
|
||||
if d.exec_() != QMessageBox.Yes:
|
||||
@ -680,7 +681,7 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
|
||||
############################## Save to disk ################################
|
||||
def save_single_format_to_disk(self, checked):
|
||||
self.save_to_disk(checked, True, Settings().get('save to disk single format', 'lrf'))
|
||||
self.save_to_disk(checked, True, config['save_to_disk_single_format'])
|
||||
|
||||
def save_to_single_dir(self, checked):
|
||||
self.save_to_disk(checked, True)
|
||||
@ -1067,9 +1068,8 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
d.exec_()
|
||||
if d.result() == d.Accepted:
|
||||
self.library_view.set_visible_columns(d.final_columns)
|
||||
settings = Settings()
|
||||
self.tool_bar.setIconSize(settings.value('toolbar icon size', QVariant(QSize(48, 48))).toSize())
|
||||
self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon if settings.get('show text in toolbar', True) else Qt.ToolButtonIconOnly)
|
||||
self.tool_bar.setIconSize(config['toolbar_icon_size'])
|
||||
self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon if config['show_text_in_toolbar'] else Qt.ToolButtonIconOnly)
|
||||
|
||||
if os.path.dirname(self.database_path) != d.database_location:
|
||||
try:
|
||||
@ -1100,8 +1100,7 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
d.exec_()
|
||||
newloc = self.database_path
|
||||
self.database_path = newloc
|
||||
settings = Settings()
|
||||
settings.set('database path', self.database_path)
|
||||
prefs().set('database_path', self.database_path)
|
||||
except Exception, err:
|
||||
traceback.print_exc()
|
||||
d = error_dialog(self, _('Could not move database'), unicode(err))
|
||||
@ -1200,12 +1199,10 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
|
||||
|
||||
def read_settings(self):
|
||||
settings = Settings()
|
||||
settings.beginGroup('Main Window')
|
||||
geometry = settings.value('main window geometry', QVariant()).toByteArray()
|
||||
self.restoreGeometry(geometry)
|
||||
settings.endGroup()
|
||||
self.database_path = settings.get('database path')
|
||||
geometry = config['main_window_geometry']
|
||||
if geometry is not None:
|
||||
self.restoreGeometry(geometry)
|
||||
self.database_path = prefs['database_path']
|
||||
if not os.access(os.path.dirname(self.database_path), os.W_OK):
|
||||
error_dialog(self, _('Database does not exist'), _('The directory in which the database should be: %s no longer exists. Please choose a new database location.')%self.database_path).exec_()
|
||||
self.database_path = choose_dir(self, 'database path dialog', 'Choose new location for database')
|
||||
@ -1214,24 +1211,18 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
if not os.path.exists(self.database_path):
|
||||
os.makedirs(self.database_path)
|
||||
self.database_path = os.path.join(self.database_path, 'library1.db')
|
||||
settings.set('database path', self.database_path)
|
||||
prefs.set('database_path', self.database_path)
|
||||
set_sidebar_directories(None)
|
||||
set_filename_pat(settings.get('filename pattern', get_filename_pat()))
|
||||
self.tool_bar.setIconSize(settings.get('toolbar icon size', QSize(48, 48)))
|
||||
self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon if settings.get('show text in toolbar', True) else Qt.ToolButtonIconOnly)
|
||||
self.tool_bar.setIconSize(config['toolbar_icon_size'])
|
||||
self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon if config['show_text_in_toolbar'] else Qt.ToolButtonIconOnly)
|
||||
|
||||
|
||||
def write_settings(self):
|
||||
settings = Settings()
|
||||
settings.beginGroup("Main Window")
|
||||
settings.setValue("main window geometry", QVariant(self.saveGeometry()))
|
||||
settings.endGroup()
|
||||
settings.beginGroup('Book Views')
|
||||
config.set('main_window_geometry', self.saveGeometry())
|
||||
self.library_view.write_settings()
|
||||
if self.device_connected:
|
||||
self.memory_view.write_settings()
|
||||
settings.endGroup()
|
||||
|
||||
|
||||
def closeEvent(self, e):
|
||||
msg = 'There are active jobs. Are you sure you want to quit?'
|
||||
if self.job_manager.has_device_jobs():
|
||||
@ -1261,17 +1252,16 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.vanity.setText(self.vanity_template%(dict(version=self.latest_version,
|
||||
device=self.device_info)))
|
||||
self.vanity.update()
|
||||
s = Settings()
|
||||
if s.get('new version notification', True) and s.get('update to version %s'%version, True):
|
||||
if config.get('new_version_notification') and dynamic.get('update to version %s'%version, True):
|
||||
d = question_dialog(self, _('Update available'), _('%s has been updated to version %s. See the <a href="http://calibre.kovidgoyal.net/wiki/Changelog">new features</a>. Visit the download page?')%(__appname__, version))
|
||||
if d.exec_() == QMessageBox.Yes:
|
||||
url = 'http://calibre.kovidgoyal.net/download_'+('windows' if iswindows else 'osx' if isosx else 'linux')
|
||||
QDesktopServices.openUrl(QUrl(url))
|
||||
s.set('update to version %s'%version, False)
|
||||
dynamic.set('update to version %s'%version, False)
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
from calibre import singleinstance
|
||||
from calibre.utils.lock import singleinstance
|
||||
|
||||
pid = os.fork() if False and islinux else -1
|
||||
if pid <= 0:
|
||||
|
@ -5,7 +5,7 @@ import StringIO, traceback, sys
|
||||
|
||||
from PyQt4.Qt import QMainWindow, QString, Qt, QFont, QCoreApplication, SIGNAL
|
||||
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
|
||||
from calibre import OptionParser
|
||||
from calibre.utils.config import OptionParser
|
||||
|
||||
def option_parser(usage='''\
|
||||
Usage: %prog [options]
|
||||
|
@ -9,15 +9,16 @@ from PyQt4.QtGui import QListView, QIcon, QFont, QLabel, QListWidget, \
|
||||
QSyntaxHighlighter, QCursor, QColor, QWidget, \
|
||||
QAbstractItemDelegate, QPixmap, QStyle, QFontMetrics
|
||||
from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, SIGNAL, \
|
||||
QObject, QRegExp, QString
|
||||
QObject, QRegExp, QString, QSettings
|
||||
|
||||
from calibre.gui2.jobs2 import DetailView
|
||||
from calibre.gui2 import human_readable, NONE, TableView, qstring_to_unicode, error_dialog
|
||||
from calibre.gui2 import human_readable, NONE, TableView, \
|
||||
qstring_to_unicode, error_dialog
|
||||
from calibre.gui2.filename_pattern_ui import Ui_Form
|
||||
from calibre import fit_image, Settings
|
||||
from calibre import fit_image
|
||||
from calibre.utils.fontconfig import find_font_families
|
||||
from calibre.ebooks.metadata.meta import get_filename_pat, metadata_from_filename, \
|
||||
set_filename_pat
|
||||
from calibre.ebooks.metadata.meta import metadata_from_filename
|
||||
from calibre.utils.config import prefs
|
||||
|
||||
|
||||
|
||||
@ -29,7 +30,7 @@ class FilenamePattern(QWidget, Ui_Form):
|
||||
|
||||
self.connect(self.test_button, SIGNAL('clicked()'), self.do_test)
|
||||
self.connect(self.re, SIGNAL('returnPressed()'), self.do_test)
|
||||
self.re.setText(get_filename_pat())
|
||||
self.re.setText(prefs['filename_pattern'])
|
||||
|
||||
def do_test(self):
|
||||
try:
|
||||
@ -66,9 +67,9 @@ class FilenamePattern(QWidget, Ui_Form):
|
||||
return re.compile(pat)
|
||||
|
||||
def commit(self):
|
||||
pat = self.pattern()
|
||||
set_filename_pat(pat)
|
||||
return pat.pattern
|
||||
pat = self.pattern().pattern
|
||||
prefs['filename_pattern'] = pat
|
||||
return pat
|
||||
|
||||
|
||||
|
||||
@ -367,13 +368,13 @@ class PythonHighlighter(QSyntaxHighlighter):
|
||||
@classmethod
|
||||
def loadConfig(cls):
|
||||
Config = cls.Config
|
||||
settings = QSettings()
|
||||
def setDefaultString(name, default):
|
||||
value = settings.value(name).toString()
|
||||
if value.isEmpty():
|
||||
value = default
|
||||
Config[name] = value
|
||||
|
||||
settings = Settings()
|
||||
for name in ("window", "shell"):
|
||||
Config["%swidth" % name] = settings.value("%swidth" % name,
|
||||
QVariant(QApplication.desktop() \
|
||||
|
@ -10,7 +10,8 @@ Command line interface to the calibre database.
|
||||
import sys, os
|
||||
from textwrap import TextWrapper
|
||||
|
||||
from calibre import OptionParser, Settings, terminal_controller, preferred_encoding
|
||||
from calibre import terminal_controller, preferred_encoding
|
||||
from calibre.utils.config import OptionParser, prefs
|
||||
try:
|
||||
from calibre.utils.single_qt_application import send_message
|
||||
except:
|
||||
@ -24,7 +25,8 @@ FIELDS = set(['title', 'authors', 'publisher', 'rating', 'timestamp', 'size', 't
|
||||
def get_parser(usage):
|
||||
parser = OptionParser(usage)
|
||||
go = parser.add_option_group('GLOBAL OPTIONS')
|
||||
go.add_option('--database', default=None, help=_('Path to the calibre database. Default is to use the path stored in the settings.'))
|
||||
go.add_option('--database', default=None,
|
||||
help=_('Path to the calibre database. Default is to use the path stored in the settings.'))
|
||||
return parser
|
||||
|
||||
def get_db(dbpath, options):
|
||||
@ -422,7 +424,7 @@ For help on an individual command: %%prog command --help
|
||||
return 1
|
||||
|
||||
command = eval('command_'+args[1])
|
||||
dbpath = Settings().get('database path')
|
||||
dbpath = prefs.get('database_path')
|
||||
|
||||
return command(args[2:], dbpath)
|
||||
|
||||
|
@ -244,6 +244,7 @@ def do_postinstall(destdir):
|
||||
os.chdir(destdir)
|
||||
os.environ['LD_LIBRARY_PATH'] = destdir+':'+os.environ.get('LD_LIBRARY_PATH', '')
|
||||
os.environ['PYTHONPATH'] = destdir
|
||||
os.environ['PYTHONSTARTUP'] = ''
|
||||
subprocess.call((os.path.join(destdir, 'calibre_postinstall'), '--save-manifest-to', t.name))
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
149
src/calibre/startup.py
Normal file
149
src/calibre/startup.py
Normal file
@ -0,0 +1,149 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
Perform various initialization tasks.
|
||||
'''
|
||||
|
||||
import locale, sys, os, re, cStringIO
|
||||
from gettext import GNUTranslations
|
||||
|
||||
# Default translation is NOOP
|
||||
import __builtin__
|
||||
__builtin__.__dict__['_'] = lambda s: s
|
||||
|
||||
from calibre.constants import iswindows, isosx, islinux, isfrozen
|
||||
from calibre.translations.msgfmt import make
|
||||
|
||||
_run_once = False
|
||||
if not _run_once:
|
||||
_run_once = True
|
||||
################################################################################
|
||||
# Setup translations
|
||||
|
||||
def get_lang():
|
||||
lang = locale.getdefaultlocale()[0]
|
||||
if lang is None and os.environ.has_key('LANG'): # Needed for OS X
|
||||
try:
|
||||
lang = os.environ['LANG']
|
||||
except:
|
||||
pass
|
||||
if lang:
|
||||
match = re.match('[a-z]{2,3}', lang)
|
||||
if match:
|
||||
lang = match.group()
|
||||
return lang
|
||||
|
||||
def set_translator():
|
||||
# To test different translations invoke as
|
||||
# LC_ALL=de_DE.utf8 program
|
||||
try:
|
||||
from calibre.translations.compiled import translations
|
||||
except:
|
||||
return
|
||||
lang = get_lang()
|
||||
if lang:
|
||||
buf = None
|
||||
if os.access(lang+'.po', os.R_OK):
|
||||
buf = cStringIO.StringIO()
|
||||
make(lang+'.po', buf)
|
||||
buf = cStringIO.StringIO(buf.getvalue())
|
||||
elif translations.has_key(lang):
|
||||
buf = cStringIO.StringIO(translations[lang])
|
||||
if buf is not None:
|
||||
t = GNUTranslations(buf)
|
||||
t.install(unicode=True)
|
||||
|
||||
set_translator()
|
||||
|
||||
################################################################################
|
||||
# Initialize locale
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
except:
|
||||
dl = locale.getdefaultlocale()
|
||||
try:
|
||||
if dl:
|
||||
locale.setlocale(dl[0])
|
||||
except:
|
||||
pass
|
||||
|
||||
################################################################################
|
||||
# Load plugins
|
||||
if isfrozen:
|
||||
if iswindows:
|
||||
plugin_path = os.path.join(os.path.dirname(sys.executable), 'plugins')
|
||||
sys.path.insert(1, os.path.dirname(sys.executable))
|
||||
elif isosx:
|
||||
plugin_path = os.path.join(getattr(sys, 'frameworks_dir'), 'plugins')
|
||||
elif islinux:
|
||||
plugin_path = os.path.join(getattr(sys, 'frozen_path'), 'plugins')
|
||||
sys.path.insert(0, plugin_path)
|
||||
else:
|
||||
import pkg_resources
|
||||
plugins = getattr(pkg_resources, 'resource_filename')('calibre', 'plugins')
|
||||
sys.path.insert(0, plugins)
|
||||
|
||||
plugins = {}
|
||||
for plugin in ['pictureflow', 'lzx', 'msdes'] + \
|
||||
(['winutil'] if iswindows else []) + \
|
||||
(['usbobserver'] if isosx else []):
|
||||
try:
|
||||
p, err = __import__(plugin), ''
|
||||
except Exception, err:
|
||||
p = None
|
||||
err = str(err)
|
||||
plugins[plugin] = (p, err)
|
||||
|
||||
################################################################################
|
||||
# Improve builtin path functions to handle unicode sensibly
|
||||
|
||||
_abspath = os.path.abspath
|
||||
def my_abspath(path, encoding=sys.getfilesystemencoding()):
|
||||
'''
|
||||
Work around for buggy os.path.abspath. This function accepts either byte strings,
|
||||
in which it calls os.path.abspath, or unicode string, in which case it first converts
|
||||
to byte strings using `encoding`, calls abspath and then decodes back to unicode.
|
||||
'''
|
||||
to_unicode = False
|
||||
if isinstance(path, unicode):
|
||||
path = path.encode(encoding)
|
||||
to_unicode = True
|
||||
res = _abspath(path)
|
||||
if to_unicode:
|
||||
res = res.decode(encoding)
|
||||
return res
|
||||
|
||||
os.path.abspath = my_abspath
|
||||
_join = os.path.join
|
||||
def my_join(a, *p):
|
||||
encoding=sys.getfilesystemencoding()
|
||||
p = [a] + list(p)
|
||||
_unicode = False
|
||||
for i in p:
|
||||
if isinstance(i, unicode):
|
||||
_unicode = True
|
||||
break
|
||||
p = [i.encode(encoding) if isinstance(i, unicode) else i for i in p]
|
||||
|
||||
res = _join(*p)
|
||||
if _unicode:
|
||||
res = res.decode(encoding)
|
||||
return res
|
||||
|
||||
os.path.join = my_join
|
||||
|
||||
|
||||
################################################################################
|
||||
# Platform specific modules
|
||||
winutil = winutilerror = None
|
||||
if iswindows:
|
||||
winutil, winutilerror = plugins['winutil']
|
||||
if not winutil:
|
||||
raise RuntimeError('Failed to load the winutil plugin: %s'%winutilerror)
|
||||
if len(sys.argv) > 1:
|
||||
sys.argv[1:] = winutil.argv()[1-len(sys.argv):]
|
||||
|
||||
################################################################################
|
||||
|
@ -164,8 +164,8 @@ DEFAULTKEYWORDS = ', '.join(default_keywords)
|
||||
|
||||
EMPTYSTRING = ''
|
||||
|
||||
from calibre import __appname__
|
||||
from calibre import __version__ as version
|
||||
from calibre.constants import __appname__
|
||||
from calibre.constants import __version__ as version
|
||||
|
||||
# The normal pot-file header. msgmerge and Emacs's po-mode work better if it's
|
||||
# there.
|
||||
@ -641,4 +641,4 @@ def main(outfile, args=sys.argv[1:]):
|
||||
eater.write(outfile)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.stdout)
|
||||
main(sys.stdout)
|
||||
|
@ -6,10 +6,14 @@ __docformat__ = 'restructuredtext en'
|
||||
'''
|
||||
Manage application-wide preferences.
|
||||
'''
|
||||
import os, re, cPickle
|
||||
import os, re, cPickle, textwrap
|
||||
from copy import deepcopy
|
||||
from optparse import OptionParser as _OptionParser
|
||||
from optparse import IndentedHelpFormatter
|
||||
from PyQt4.QtCore import QString
|
||||
from calibre import iswindows, isosx, OptionParser, ExclusiveFile, LockError
|
||||
from calibre.constants import terminal_controller, iswindows, isosx, \
|
||||
__appname__, __version__, __author__
|
||||
from calibre.utils.lock import LockError, ExclusiveFile
|
||||
from collections import defaultdict
|
||||
|
||||
if iswindows:
|
||||
@ -26,7 +30,125 @@ else:
|
||||
|
||||
if not os.path.exists(config_dir):
|
||||
os.makedirs(config_dir, mode=448) # 0700 == 448
|
||||
|
||||
class CustomHelpFormatter(IndentedHelpFormatter):
|
||||
|
||||
def format_usage(self, usage):
|
||||
return _("%sUsage%s: %s\n") % (terminal_controller.BLUE, terminal_controller.NORMAL, usage)
|
||||
|
||||
def format_heading(self, heading):
|
||||
return "%*s%s%s%s:\n" % (self.current_indent, terminal_controller.BLUE,
|
||||
"", heading, terminal_controller.NORMAL)
|
||||
|
||||
def format_option(self, option):
|
||||
result = []
|
||||
opts = self.option_strings[option]
|
||||
opt_width = self.help_position - self.current_indent - 2
|
||||
if len(opts) > opt_width:
|
||||
opts = "%*s%s\n" % (self.current_indent, "",
|
||||
terminal_controller.GREEN+opts+terminal_controller.NORMAL)
|
||||
indent_first = self.help_position
|
||||
else: # start help on same line as opts
|
||||
opts = "%*s%-*s " % (self.current_indent, "", opt_width + len(terminal_controller.GREEN + terminal_controller.NORMAL),
|
||||
terminal_controller.GREEN + opts + terminal_controller.NORMAL)
|
||||
indent_first = 0
|
||||
result.append(opts)
|
||||
if option.help:
|
||||
help_text = self.expand_default(option).split('\n')
|
||||
help_lines = []
|
||||
|
||||
for line in help_text:
|
||||
help_lines.extend(textwrap.wrap(line, self.help_width))
|
||||
result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
|
||||
result.extend(["%*s%s\n" % (self.help_position, "", line)
|
||||
for line in help_lines[1:]])
|
||||
elif opts[-1] != "\n":
|
||||
result.append("\n")
|
||||
return "".join(result)+'\n'
|
||||
|
||||
|
||||
class OptionParser(_OptionParser):
|
||||
|
||||
def __init__(self,
|
||||
usage='%prog [options] filename',
|
||||
version='%%prog (%s %s)'%(__appname__, __version__),
|
||||
epilog=_('Created by ')+terminal_controller.RED+__author__+terminal_controller.NORMAL,
|
||||
gui_mode=False,
|
||||
conflict_handler='resolve',
|
||||
**kwds):
|
||||
usage += '''\n\nWhenever you pass arguments to %prog that have spaces in them, '''\
|
||||
'''enclose the arguments in quotation marks.'''
|
||||
_OptionParser.__init__(self, usage=usage, version=version, epilog=epilog,
|
||||
formatter=CustomHelpFormatter(),
|
||||
conflict_handler=conflict_handler, **kwds)
|
||||
self.gui_mode = gui_mode
|
||||
|
||||
def error(self, msg):
|
||||
if self.gui_mode:
|
||||
raise Exception(msg)
|
||||
_OptionParser.error(self, msg)
|
||||
|
||||
def merge(self, parser):
|
||||
'''
|
||||
Add options from parser to self. In case of conflicts, confilicting options from
|
||||
parser are skipped.
|
||||
'''
|
||||
opts = list(parser.option_list)
|
||||
groups = list(parser.option_groups)
|
||||
|
||||
def merge_options(options, container):
|
||||
for opt in deepcopy(options):
|
||||
if not self.has_option(opt.get_opt_string()):
|
||||
container.add_option(opt)
|
||||
|
||||
merge_options(opts, self)
|
||||
|
||||
for group in groups:
|
||||
g = self.add_option_group(group.title)
|
||||
merge_options(group.option_list, g)
|
||||
|
||||
def subsume(self, group_name, msg=''):
|
||||
'''
|
||||
Move all existing options into a subgroup named
|
||||
C{group_name} with description C{msg}.
|
||||
'''
|
||||
opts = [opt for opt in self.options_iter() if opt.get_opt_string() not in ('--version', '--help')]
|
||||
self.option_groups = []
|
||||
subgroup = self.add_option_group(group_name, msg)
|
||||
for opt in opts:
|
||||
self.remove_option(opt.get_opt_string())
|
||||
subgroup.add_option(opt)
|
||||
|
||||
def options_iter(self):
|
||||
for opt in self.option_list:
|
||||
if str(opt).strip():
|
||||
yield opt
|
||||
for gr in self.option_groups:
|
||||
for opt in gr.option_list:
|
||||
if str(opt).strip():
|
||||
yield opt
|
||||
|
||||
def option_by_dest(self, dest):
|
||||
for opt in self.options_iter():
|
||||
if opt.dest == dest:
|
||||
return opt
|
||||
|
||||
def merge_options(self, lower, upper):
|
||||
'''
|
||||
Merge options in lower and upper option lists into upper.
|
||||
Default values in upper are overridden by
|
||||
non default values in lower.
|
||||
'''
|
||||
for dest in lower.__dict__.keys():
|
||||
if not upper.__dict__.has_key(dest):
|
||||
continue
|
||||
opt = self.option_by_dest(dest)
|
||||
if lower.__dict__[dest] != opt.default and \
|
||||
upper.__dict__[dest] == opt.default:
|
||||
upper.__dict__[dest] = lower.__dict__[dest]
|
||||
|
||||
|
||||
|
||||
class Option(object):
|
||||
|
||||
def __init__(self, name, switches=[], help='', type=None, choices=None,
|
||||
@ -250,8 +372,154 @@ class StringConfig(object):
|
||||
setattr(opts, name, val)
|
||||
footer = self.option_set.get_override_section(self.src)
|
||||
self.src = self.option_set.serialize(opts)+ '\n\n' + footer + '\n'
|
||||
|
||||
class ConfigProxy(object):
|
||||
'''
|
||||
A Proxy to minimize file reads for widely used config settings
|
||||
'''
|
||||
|
||||
def __init__(self, config):
|
||||
self.__config = config
|
||||
self.__opts = None
|
||||
|
||||
def refresh(self):
|
||||
self.__opts = self.__config.parse()
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.get(key)
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
return self.set(key, val)
|
||||
|
||||
def get(self, key):
|
||||
if self.__opts is None:
|
||||
self.refresh()
|
||||
return getattr(self.__opts, key)
|
||||
|
||||
def set(self, key, val):
|
||||
if self.__opts is None:
|
||||
self.refresh()
|
||||
setattr(self.__opts, key, val)
|
||||
return self.__config.set(key, val)
|
||||
|
||||
class DynamicConfig(dict):
|
||||
'''
|
||||
A replacement for QSettings that supports dynamic config keys.
|
||||
Returns `None` if a config key is not found. Note that the config
|
||||
data is stored in a non human readable pickle file, so only use this
|
||||
class for preferences that you don't intend to have the users edit directly.
|
||||
'''
|
||||
def __init__(self, name='dynamic'):
|
||||
self.name = name
|
||||
self.file_path = os.path.join(config_dir, name+'.pickle')
|
||||
with ExclusiveFile(self.file_path) as f:
|
||||
raw = f.read()
|
||||
d = cPickle.loads(raw) if raw.strip() else {}
|
||||
dict.__init__(self, d)
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return dict.__getitem__(self, key)
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
dict.__setitem__(self, key, val)
|
||||
self.commit()
|
||||
|
||||
def set(self, key, val):
|
||||
self.__setitem__(key, val)
|
||||
|
||||
def commit(self):
|
||||
if hasattr(self, 'file_path') and self.file_path:
|
||||
with ExclusiveFile(self.file_path) as f:
|
||||
raw = cPickle.dumps(self, -1)
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
f.write(raw)
|
||||
|
||||
dynamic = DynamicConfig()
|
||||
|
||||
def _prefs():
|
||||
c = Config('global', 'calibre wide preferences')
|
||||
c.add_opt('database_path',
|
||||
default=os.path.expanduser('~/library1.db'),
|
||||
help=_('Path to the database in which books are stored'))
|
||||
c.add_opt('filename_pattern', default=ur'(?P<title>.+) - (?P<author>[^_]+)',
|
||||
help=_('Pattern to guess metadata from filenames'))
|
||||
c.add_opt('isbndb_com_key', default='',
|
||||
help=_('Access key for isbndb.com'))
|
||||
c.add_opt('network_timeout', default=5,
|
||||
help=_('Default timeout for network operations (seconds)'))
|
||||
|
||||
c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.')
|
||||
return c
|
||||
|
||||
prefs = ConfigProxy(_prefs())
|
||||
|
||||
def migrate():
|
||||
p = prefs
|
||||
if p.get('migrated'):
|
||||
return
|
||||
|
||||
from PyQt4.QtCore import QSettings, QVariant
|
||||
class Settings(QSettings):
|
||||
|
||||
def __init__(self, name='calibre2'):
|
||||
QSettings.__init__(self, QSettings.IniFormat, QSettings.UserScope,
|
||||
'kovidgoyal.net', name)
|
||||
|
||||
def get(self, key, default=None):
|
||||
try:
|
||||
key = str(key)
|
||||
if not self.contains(key):
|
||||
return default
|
||||
val = str(self.value(key, QVariant()).toByteArray())
|
||||
if not val:
|
||||
return None
|
||||
return cPickle.loads(val)
|
||||
except:
|
||||
return default
|
||||
|
||||
s, migrated = Settings(), set([])
|
||||
all_keys = set(map(unicode, s.allKeys()))
|
||||
from calibre.gui2 import config, dynamic
|
||||
def _migrate(key, safe=None, from_qvariant=None, p=config):
|
||||
try:
|
||||
if key not in all_keys:
|
||||
return
|
||||
if safe is None:
|
||||
safe = re.sub(r'[^0-9a-zA-Z]', '_', key)
|
||||
val = s.get(key)
|
||||
if from_qvariant is not None:
|
||||
val = getattr(s.value(key), from_qvariant)()
|
||||
p.set(safe, val)
|
||||
except:
|
||||
pass
|
||||
finally:
|
||||
migrated.add(key)
|
||||
|
||||
|
||||
_migrate('database path', p=prefs)
|
||||
_migrate('filename pattern', p=prefs)
|
||||
_migrate('network timeout', p=prefs)
|
||||
_migrate('isbndb.com key', p=prefs)
|
||||
|
||||
_migrate('frequently used directories')
|
||||
_migrate('send to device by default')
|
||||
_migrate('save to disk single format')
|
||||
_migrate('confirm delete')
|
||||
_migrate('show text in toolbar')
|
||||
_migrate('new version notification')
|
||||
_migrate('use roman numerals for series number')
|
||||
_migrate('cover flow queue length')
|
||||
_migrate('LRF conversion defaults')
|
||||
_migrate('LRF ebook viewer options')
|
||||
|
||||
for key in all_keys - migrated:
|
||||
if key.endswith(': un') or key.endswith(': pw'):
|
||||
_migrate(key, p=dynamic)
|
||||
p.set('migrated', True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
86
src/calibre/utils/lock.py
Normal file
86
src/calibre/utils/lock.py
Normal file
@ -0,0 +1,86 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
Secure access to locked files from multiple processes.
|
||||
'''
|
||||
|
||||
from calibre.constants import iswindows, __appname__, \
|
||||
win32api, win32event, winerror, fcntl
|
||||
import time, atexit, os
|
||||
|
||||
class LockError(Exception):
|
||||
pass
|
||||
|
||||
class ExclusiveFile(object):
|
||||
|
||||
def __init__(self, path, timeout=10):
|
||||
self.path = path
|
||||
self.timeout = timeout
|
||||
|
||||
def __enter__(self):
|
||||
self.file = open(self.path, 'a+b')
|
||||
self.file.seek(0)
|
||||
timeout = self.timeout
|
||||
if iswindows:
|
||||
name = ('Local\\'+(__appname__+self.file.name).replace('\\', '_'))[:201]
|
||||
while self.timeout < 0 or timeout >= 0:
|
||||
self.mutex = win32event.CreateMutex(None, False, name)
|
||||
if win32api.GetLastError() != winerror.ERROR_ALREADY_EXISTS: break
|
||||
time.sleep(1)
|
||||
timeout -= 1
|
||||
else:
|
||||
while self.timeout < 0 or timeout >= 0:
|
||||
try:
|
||||
fcntl.lockf(self.file.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB)
|
||||
break
|
||||
except IOError:
|
||||
time.sleep(1)
|
||||
timeout -= 1
|
||||
if timeout < 0 and self.timeout >= 0:
|
||||
self.file.close()
|
||||
raise LockError
|
||||
return self.file
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.file.close()
|
||||
if iswindows:
|
||||
win32api.CloseHandle(self.mutex)
|
||||
|
||||
|
||||
def _clean_lock_file(file):
|
||||
try:
|
||||
file.close()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
os.remove(file.name)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def singleinstance(name):
|
||||
'''
|
||||
Return True if no other instance of the application identified by name is running,
|
||||
False otherwise.
|
||||
@param name: The name to lock.
|
||||
@type name: string
|
||||
'''
|
||||
if iswindows:
|
||||
mutexname = 'mutexforsingleinstanceof'+__appname__+name
|
||||
mutex = win32event.CreateMutex(None, False, mutexname)
|
||||
if mutex:
|
||||
atexit.register(win32api.CloseHandle, mutex)
|
||||
return not win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS
|
||||
else:
|
||||
path = os.path.expanduser('~/.'+__appname__+'_'+name+'.lock')
|
||||
try:
|
||||
f = open(path, 'w')
|
||||
fcntl.lockf(f.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB)
|
||||
atexit.register(_clean_lock_file, f)
|
||||
return True
|
||||
except IOError:
|
||||
return False
|
||||
|
||||
return False
|
@ -12,9 +12,10 @@ from urllib import url2pathname
|
||||
from httplib import responses
|
||||
|
||||
from calibre import setup_cli_handlers, browser, sanitize_file_name, \
|
||||
OptionParser, relpath, LoggingInterface
|
||||
relpath, LoggingInterface
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag
|
||||
from calibre.ebooks.chardet import xml_to_unicode
|
||||
from calibre.utils.config import OptionParser
|
||||
|
||||
class FetchError(Exception):
|
||||
pass
|
||||
|
Loading…
x
Reference in New Issue
Block a user