Allow user customization of static resources

This commit is contained in:
Kovid Goyal 2010-07-21 23:03:57 -06:00
parent 241b2a4841
commit 6fa16aa3fa
5 changed files with 88 additions and 40 deletions

View File

@ -2,6 +2,7 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import sys, os, re, logging, time, mimetypes, \
__builtin__, warnings, multiprocessing
from urllib import getproxies
@ -13,12 +14,13 @@ from functools import partial
warnings.simplefilter('ignore', DeprecationWarning)
from calibre.startup import plugins, winutil, winutilerror
from calibre.constants import iswindows, isosx, islinux, isfreebsd, isfrozen, \
terminal_controller, preferred_encoding, \
__appname__, __version__, __author__, \
win32event, win32api, winerror, fcntl, \
filesystem_encoding
filesystem_encoding, plugins, config_dir
from calibre.startup import winutil, winutilerror
import mechanize
if False:
@ -486,7 +488,6 @@ def ipython(user_ns=None):
sys.argv = ['ipython']
if user_ns is None:
user_ns = locals()
from calibre.utils.config import config_dir
ipydir = os.path.join(config_dir, ('_' if iswindows else '.')+'ipython')
os.environ['IPYTHONDIR'] = ipydir
if not os.path.exists(ipydir):

View File

@ -14,7 +14,7 @@ numeric_version = tuple(_ver)
Various run time constants.
'''
import sys, locale, codecs
import sys, locale, codecs, os
from calibre.utils.terminfo import TerminalController
terminal_controller = TerminalController(sys.stdout)
@ -47,7 +47,7 @@ def debug():
global DEBUG
DEBUG = True
################################################################################
# plugins {{{
plugins = None
if plugins is None:
# Load plugins
@ -80,3 +80,22 @@ if plugins is None:
return plugins
plugins = load_plugins()
# }}}
# config_dir {{{
if os.environ.has_key('CALIBRE_CONFIG_DIRECTORY'):
config_dir = os.path.abspath(os.environ['CALIBRE_CONFIG_DIRECTORY'])
elif iswindows:
if plugins['winutil'][0] is None:
raise Exception(plugins['winutil'][1])
config_dir = plugins['winutil'][0].special_folder_path(plugins['winutil'][0].CSIDL_APPDATA)
if not os.access(config_dir, os.W_OK|os.X_OK):
config_dir = os.path.expanduser('~')
config_dir = os.path.join(config_dir, 'calibre')
elif isosx:
config_dir = os.path.expanduser('~/Library/Preferences/calibre')
else:
bdir = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME', '~/.config')))
config_dir = os.path.join(bdir, 'calibre')
# }}}

View File

@ -9,7 +9,8 @@ Customizing |app|
|app| has a highly modular design. Various parts of it can be customized. You can learn how to create
*recipes* to add new sources of online content to |app| in the Section :ref:`news`. Here, you will learn,
first, how to use environment variables and *tweaks* to customize |app|'s behavior and then how to
first, how to use environment variables and *tweaks* to customize |app|'s behavior, and then how to
specify your own static resources like icons and templates to override the defaults and finally how to
use *plugins* to add funtionality to |app|.
.. contents::
@ -35,6 +36,20 @@ The default tweaks.py file is reproduced below
.. literalinclude:: ../../../resources/default_tweaks.py
Overriding icons, templates, etcetera
----------------------------------------
|app| allows you to override the static resources, like icons, templates, javascript, etc. with customized versions that you like.
All static resources are stored in the resources sub-folder of the calibre install location. On Windows, this is usually
:file:`C:\Program Files\Calibre2\resources`. On OS X, :file:`/Applications/calibre.app/Contents/Resources/resources/`. On linux, if you are using the binary installer
from the calibre website it will be :file:`/opt/calibre/resources`. These paths can change depending on where you choose to install |app|.
You should not change the files in this resources folder, as your changes will get overwritten the next time you update |app|. Instead, go to
:guilabel:`Preferences->Advanced` and click :guilabel:`Open calibre configuration directory`. In this configuration directory, create a sub-folder called resources and place the files you want to override in it. |app| will automatically use your custom file in preference to the builtin one the next time it is started.
For example, if you wanted to change the icon for the :guilabel:`Remove books` action, you would first look in the builtin resources folder and see that the relevant file is
:file:`resources/images/trash.svg`. Assuming you have an alternate icon in svg format called :file:`mytrash.svg` you would save it in the configuration directory as :file:`resources/images/trash.svg`. All the icons used by the calibre user interface are in :file:`resources/images` and its sub-folders.
A Hello World plugin
------------------------

View File

@ -13,25 +13,10 @@ from optparse import OptionParser as _OptionParser
from optparse import IndentedHelpFormatter
from collections import defaultdict
from calibre.constants import terminal_controller, iswindows, isosx, \
__appname__, __version__, __author__, plugins
from calibre.constants import terminal_controller, config_dir, \
__appname__, __version__, __author__
from calibre.utils.lock import LockError, ExclusiveFile
if os.environ.has_key('CALIBRE_CONFIG_DIRECTORY'):
config_dir = os.path.abspath(os.environ['CALIBRE_CONFIG_DIRECTORY'])
elif iswindows:
if plugins['winutil'][0] is None:
raise Exception(plugins['winutil'][1])
config_dir = plugins['winutil'][0].special_folder_path(plugins['winutil'][0].CSIDL_APPDATA)
if not os.access(config_dir, os.W_OK|os.X_OK):
config_dir = os.path.expanduser('~')
config_dir = os.path.join(config_dir, 'calibre')
elif isosx:
config_dir = os.path.expanduser('~/Library/Preferences/calibre')
else:
bdir = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME', '~/.config')))
config_dir = os.path.join(bdir, 'calibre')
plugin_dir = os.path.join(config_dir, 'plugins')
CONFIG_DIR_MODE = 0700

View File

@ -9,26 +9,54 @@ __docformat__ = 'restructuredtext en'
import __builtin__, sys, os
_dev_path = os.environ.get('CALIBRE_DEVELOP_FROM', None)
if _dev_path is not None:
_dev_path = os.path.join(os.path.abspath(os.path.dirname(_dev_path)), 'resources')
if not os.path.exists(_dev_path):
_dev_path = None
from calibre import config_dir
_path_cache = {}
class PathResolver(object):
def __init__(self):
self.locations = [sys.resources_location]
self.cache = {}
def suitable(path):
try:
return os.path.exists(path) and os.path.isdir(path) and \
os.listdir(path)
except:
pass
return False
dev_path = os.environ.get('CALIBRE_DEVELOP_FROM', None)
if dev_path is not None:
dev_path = os.path.join(os.path.abspath(
os.path.dirname(dev_path)), 'resources')
if suitable(dev_path):
self.locations.insert(0, dev_path)
user_path = os.path.join(config_dir, 'resources')
if suitable(user_path):
self.locations.insert(0, user_path)
def __call__(self, path):
path = path.replace(os.sep, '/')
ans = self.cache.get(path, None)
if ans is None:
for base in self.locations:
fpath = os.path.join(base, *path.split('/'))
if os.path.exists(fpath):
ans = fpath
break
if ans is None:
ans = os.path.join(self.location[0], *path.split('/'))
self.cache[path] = ans
return ans
_resolver = PathResolver()
def get_path(path, data=False):
global _dev_path
path = path.replace(os.sep, '/')
base = sys.resources_location
if _dev_path is not None:
if path in _path_cache:
return _path_cache[path]
if os.path.exists(os.path.join(_dev_path, *path.split('/'))):
base = _dev_path
fpath = os.path.join(base, *path.split('/'))
if _dev_path is not None:
_path_cache[path] = fpath
fpath = _resolver(path)
if data:
return open(fpath, 'rb').read()
return fpath