Sync to trunk.
@ -44,8 +44,9 @@ class GlobeAndMail(BasicNewsRecipe):
|
|||||||
dict(name='div', attrs={'id':'blog-header'}),
|
dict(name='div', attrs={'id':'blog-header'}),
|
||||||
dict(name='div', attrs={'id':'right-rail'}),
|
dict(name='div', attrs={'id':'right-rail'}),
|
||||||
dict(name='div', attrs={'id':'group-footer-container'}),
|
dict(name='div', attrs={'id':'group-footer-container'}),
|
||||||
dict(name=['iframe'])
|
dict(name=['iframe', 'style'])
|
||||||
]
|
]
|
||||||
|
remove_attributes = ['style']
|
||||||
remove_tags_after = [{'id':['article-content']},
|
remove_tags_after = [{'id':['article-content']},
|
||||||
{'class':['pull','inline-img'] },
|
{'class':['pull','inline-img'] },
|
||||||
dict(name='img', attrs={'class':'inline-media-embed'}),
|
dict(name='img', attrs={'class':'inline-media-embed'}),
|
||||||
|
@ -2,7 +2,7 @@ from __future__ import with_statement
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import atexit, os, shutil, sys, tempfile, zipfile
|
import os, sys, zipfile
|
||||||
|
|
||||||
from calibre.constants import numeric_version
|
from calibre.constants import numeric_version
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
@ -226,7 +226,7 @@ class MetadataWriterPlugin(Plugin):
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class CatalogPlugin(Plugin):
|
class CatalogPlugin(Plugin):
|
||||||
'''
|
'''
|
||||||
A plugin that implements a catalog generator.
|
A plugin that implements a catalog generator.
|
||||||
@ -251,7 +251,7 @@ class CatalogPlugin(Plugin):
|
|||||||
#: '%default' + "'"))]
|
#: '%default' + "'"))]
|
||||||
|
|
||||||
cli_options = []
|
cli_options = []
|
||||||
|
|
||||||
|
|
||||||
def search_sort_db(self, db, opts):
|
def search_sort_db(self, db, opts):
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ class CatalogPlugin(Plugin):
|
|||||||
if opts.sort_by:
|
if opts.sort_by:
|
||||||
# 2nd arg = ascending
|
# 2nd arg = ascending
|
||||||
db.sort(opts.sort_by, True)
|
db.sort(opts.sort_by, True)
|
||||||
|
|
||||||
return db.get_data_as_dict(ids=opts.ids)
|
return db.get_data_as_dict(ids=opts.ids)
|
||||||
|
|
||||||
def get_output_fields(self, opts):
|
def get_output_fields(self, opts):
|
||||||
@ -289,29 +289,29 @@ class CatalogPlugin(Plugin):
|
|||||||
'''
|
'''
|
||||||
If plugin is not a built-in, copy the plugin's .ui and .py files from
|
If plugin is not a built-in, copy the plugin's .ui and .py files from
|
||||||
the zip file to $TMPDIR.
|
the zip file to $TMPDIR.
|
||||||
Tab will be dynamically generated and added to the Catalog Options dialog in
|
Tab will be dynamically generated and added to the Catalog Options dialog in
|
||||||
calibre.gui2.dialogs.catalog.py:Catalog
|
calibre.gui2.dialogs.catalog.py:Catalog
|
||||||
'''
|
'''
|
||||||
from calibre.customize.builtins import plugins as builtin_plugins
|
from calibre.customize.builtins import plugins as builtin_plugins
|
||||||
from calibre.customize.ui import config
|
from calibre.customize.ui import config
|
||||||
from calibre.ptempfile import PersistentTemporaryDirectory
|
from calibre.ptempfile import PersistentTemporaryDirectory
|
||||||
|
|
||||||
if not type(self) in builtin_plugins and \
|
if not type(self) in builtin_plugins and \
|
||||||
not self.name in config['disabled_plugins']:
|
not self.name in config['disabled_plugins']:
|
||||||
files_to_copy = ["%s.%s" % (self.name.lower(),ext) for ext in ["ui","py"]]
|
files_to_copy = ["%s.%s" % (self.name.lower(),ext) for ext in ["ui","py"]]
|
||||||
resources = zipfile.ZipFile(self.plugin_path,'r')
|
resources = zipfile.ZipFile(self.plugin_path,'r')
|
||||||
|
|
||||||
if self.resources_path is None:
|
if self.resources_path is None:
|
||||||
self.resources_path = PersistentTemporaryDirectory('_plugin_resources', prefix='')
|
self.resources_path = PersistentTemporaryDirectory('_plugin_resources', prefix='')
|
||||||
|
|
||||||
for file in files_to_copy:
|
for file in files_to_copy:
|
||||||
try:
|
try:
|
||||||
resources.extract(file, self.resources_path)
|
resources.extract(file, self.resources_path)
|
||||||
except:
|
except:
|
||||||
print " customize:__init__.initialize(): %s not found in %s" % (file, os.path.basename(self.plugin_path))
|
print " customize:__init__.initialize(): %s not found in %s" % (file, os.path.basename(self.plugin_path))
|
||||||
continue
|
continue
|
||||||
resources.close()
|
resources.close()
|
||||||
|
|
||||||
def run(self, path_to_output, opts, db, ids):
|
def run(self, path_to_output, opts, db, ids):
|
||||||
'''
|
'''
|
||||||
Run the plugin. Must be implemented in subclasses.
|
Run the plugin. Must be implemented in subclasses.
|
||||||
|
@ -119,7 +119,11 @@ class Stylizer(object):
|
|||||||
basename = os.path.basename(path)
|
basename = os.path.basename(path)
|
||||||
cssname = os.path.splitext(basename)[0] + '.css'
|
cssname = os.path.splitext(basename)[0] + '.css'
|
||||||
stylesheets = [HTML_CSS_STYLESHEET]
|
stylesheets = [HTML_CSS_STYLESHEET]
|
||||||
head = xpath(tree, '/h:html/h:head')[0]
|
head = xpath(tree, '/h:html/h:head')
|
||||||
|
if head:
|
||||||
|
head = head[0]
|
||||||
|
else:
|
||||||
|
head = []
|
||||||
parser = cssutils.CSSParser(fetcher=self._fetch_css_file,
|
parser = cssutils.CSSParser(fetcher=self._fetch_css_file,
|
||||||
log=logging.getLogger('calibre.css'))
|
log=logging.getLogger('calibre.css'))
|
||||||
self.font_face_rules = []
|
self.font_face_rules = []
|
||||||
|
@ -9,32 +9,44 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
from calibre.gui2 import gprefs
|
from calibre.gui2 import gprefs
|
||||||
from calibre.gui2.catalog.catalog_csv_xml_ui import Ui_Form
|
from calibre.gui2.catalog.catalog_csv_xml_ui import Ui_Form
|
||||||
from PyQt4.Qt import QDialog, QWidget, SIGNAL
|
from PyQt4.Qt import QWidget, QListWidgetItem
|
||||||
|
|
||||||
class PluginWidget(QWidget,Ui_Form):
|
class PluginWidget(QWidget, Ui_Form):
|
||||||
|
|
||||||
TITLE = _('CSV/XML Output')
|
TITLE = _('CSV/XML Options')
|
||||||
HELP = _('Options specific to')+' CSV/XML '+_('output')
|
HELP = _('Options specific to')+' CSV/XML '+_('output')
|
||||||
sync_enabled = False
|
sync_enabled = False
|
||||||
|
formats = set(['csv', 'xml'])
|
||||||
def initialize(self, name):
|
|
||||||
QWidget.__init__(self)
|
def __init__(self, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
from calibre.library.catalog import FIELDS
|
||||||
|
self.all_fields = []
|
||||||
|
for x in FIELDS:
|
||||||
|
if x != 'all':
|
||||||
|
self.all_fields.append(x)
|
||||||
|
QListWidgetItem(x, self.db_fields)
|
||||||
|
|
||||||
|
def initialize(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
fields = gprefs.get(name+'_db_fields', self.all_fields)
|
||||||
# Restore the activated fields from last use
|
# Restore the activated fields from last use
|
||||||
for x in range(self.db_fields.count()):
|
for x in range(self.db_fields.count()):
|
||||||
pref = '%s_db_fields_%s' % (self.name, self.db_fields.item(x).text())
|
item = self.db_fields.item(x)
|
||||||
activated = gprefs[pref] if pref in gprefs else False
|
item.setSelected(unicode(item.text()) in fields)
|
||||||
self.db_fields.item(x).setSelected(activated)
|
|
||||||
|
|
||||||
def options(self):
|
def options(self):
|
||||||
# Save the currently activated fields
|
# Save the currently activated fields
|
||||||
|
fields = []
|
||||||
for x in range(self.db_fields.count()):
|
for x in range(self.db_fields.count()):
|
||||||
pref = '%s_db_fields_%s' % (self.name, self.db_fields.item(x).text())
|
item = self.db_fields.item(x)
|
||||||
gprefs[pref] = self.db_fields.item(x).isSelected()
|
if item.isSelected():
|
||||||
|
fields.append(unicode(item.text()))
|
||||||
# Return a dictionary with current options for this widget
|
gprefs.set(self.name+'_db_fields', fields)
|
||||||
|
|
||||||
|
# Return a dictionary with current options for this widget
|
||||||
if len(self.db_fields.selectedItems()):
|
if len(self.db_fields.selectedItems()):
|
||||||
return {'fields':[str(item.text()) for item in self.db_fields.selectedItems()]}
|
return {'fields':[unicode(item.text()) for item in self.db_fields.selectedItems()]}
|
||||||
else:
|
else:
|
||||||
return {'fields':['all']}
|
return {'fields':['all']}
|
||||||
|
@ -34,91 +34,6 @@
|
|||||||
<property name="selectionMode">
|
<property name="selectionMode">
|
||||||
<enum>QAbstractItemView::MultiSelection</enum>
|
<enum>QAbstractItemView::MultiSelection</enum>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>author_sort</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>authors</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>comments</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>cover</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>formats</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>id</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>isbn</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>pubdate</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>publisher</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>rating</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>series_index</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>series</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>size</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>tags</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>timestamp</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>title</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>uuid</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QLabel" name="label_6">
|
<widget class="QLabel" name="label_6">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
|
||||||
from __future__ import with_statement
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
|
||||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
|
|
||||||
from <basename> import Ui_Form
|
|
||||||
from PyQt4.Qt import QDialog, QWidget
|
|
||||||
|
|
||||||
class PluginWidget(QWidget,Ui_Form):
|
|
||||||
|
|
||||||
TITLE = _('<formats> Output')
|
|
||||||
HELP = _('Options specific to')+' <formats> '+_('output')
|
|
||||||
# Indicates whether this plugin wants its output synced to the connected device
|
|
||||||
sync_enabled = False
|
|
||||||
|
|
||||||
def initialize(self):
|
|
||||||
QWidget.__init__(self)
|
|
||||||
self.setupUi(self)
|
|
||||||
|
|
||||||
def options(self):
|
|
||||||
# Return a dictionary with options for this Widget
|
|
||||||
return {}
|
|
@ -4,14 +4,12 @@ __license__ = 'GPL 3'
|
|||||||
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
from calibre.customize.conversion import OptionRecommendation, DummyReporter
|
from calibre.customize.conversion import OptionRecommendation, DummyReporter
|
||||||
from calibre.ebooks.conversion.plumber import Plumber
|
from calibre.ebooks.conversion.plumber import Plumber
|
||||||
from calibre.customize.ui import plugin_for_catalog_format
|
from calibre.customize.ui import plugin_for_catalog_format
|
||||||
from calibre.utils.logging import Log
|
from calibre.utils.logging import Log
|
||||||
from calibre.gui2 import choose_dir, Application
|
|
||||||
|
|
||||||
def gui_convert(input, output, recommendations, notification=DummyReporter(),
|
def gui_convert(input, output, recommendations, notification=DummyReporter(),
|
||||||
abort_after_input_dump=False, log=None):
|
abort_after_input_dump=False, log=None):
|
||||||
@ -36,7 +34,7 @@ def gui_catalog(fmt, title, dbspec, ids, out_file_name, fmt_options,
|
|||||||
db = LibraryDatabase2(dbpath)
|
db = LibraryDatabase2(dbpath)
|
||||||
else: # To be implemented in the future
|
else: # To be implemented in the future
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Create a minimal OptionParser that we can append to
|
# Create a minimal OptionParser that we can append to
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
args = []
|
args = []
|
||||||
@ -56,8 +54,8 @@ def gui_catalog(fmt, title, dbspec, ids, out_file_name, fmt_options,
|
|||||||
plugin = plugin_for_catalog_format(fmt)
|
plugin = plugin_for_catalog_format(fmt)
|
||||||
plugin.run(out_file_name, opts, db)
|
plugin.run(out_file_name, opts, db)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,28 +6,25 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os, shutil, sys, tempfile
|
import os, sys
|
||||||
|
|
||||||
from PyQt4.Qt import QDialog, QWidget
|
from PyQt4.Qt import QDialog
|
||||||
|
|
||||||
from calibre.customize.ui import config
|
from calibre.customize.ui import config
|
||||||
from calibre.gui2.dialogs.catalog_ui import Ui_Dialog
|
from calibre.gui2.dialogs.catalog_ui import Ui_Dialog
|
||||||
from calibre.gui2 import gprefs, dynamic
|
from calibre.gui2 import dynamic
|
||||||
from calibre.customize.ui import available_catalog_formats, catalog_plugins
|
from calibre.customize.ui import catalog_plugins
|
||||||
from calibre.gui2.catalog.catalog_csv_xml import PluginWidget
|
|
||||||
|
|
||||||
class Catalog(QDialog, Ui_Dialog):
|
class Catalog(QDialog, Ui_Dialog):
|
||||||
''' Catalog Dialog builder'''
|
''' Catalog Dialog builder'''
|
||||||
widgets = []
|
|
||||||
|
|
||||||
def __init__(self, parent, dbspec, ids):
|
def __init__(self, parent, dbspec, ids):
|
||||||
import re, cStringIO
|
import re, cStringIO
|
||||||
from calibre import prints as info
|
from calibre import prints as info
|
||||||
from calibre.gui2 import dynamic
|
|
||||||
from PyQt4.uic import compileUi
|
from PyQt4.uic import compileUi
|
||||||
|
|
||||||
QDialog.__init__(self, parent)
|
QDialog.__init__(self, parent)
|
||||||
|
|
||||||
# Run the dialog setup generated from catalog.ui
|
# Run the dialog setup generated from catalog.ui
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.dbspec, self.ids = dbspec, ids
|
self.dbspec, self.ids = dbspec, ids
|
||||||
@ -42,26 +39,25 @@ class Catalog(QDialog, Ui_Dialog):
|
|||||||
# GwR *** Add option tabs for built-in formats
|
# GwR *** Add option tabs for built-in formats
|
||||||
# This code models #69 in calibre/gui2/dialogs/config/__init__.py
|
# This code models #69 in calibre/gui2/dialogs/config/__init__.py
|
||||||
|
|
||||||
self.fmts = []
|
self.fmts, self.widgets = [], []
|
||||||
|
|
||||||
from calibre.customize.builtins import plugins as builtin_plugins
|
from calibre.customize.builtins import plugins as builtin_plugins
|
||||||
from calibre.customize import CatalogPlugin
|
|
||||||
|
|
||||||
for plugin in catalog_plugins():
|
for plugin in catalog_plugins():
|
||||||
if plugin.name in config['disabled_plugins']:
|
if plugin.name in config['disabled_plugins']:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
name = plugin.name.lower().replace(' ', '_')
|
name = plugin.name.lower().replace(' ', '_')
|
||||||
if type(plugin) in builtin_plugins:
|
if type(plugin) in builtin_plugins:
|
||||||
#info("Adding widget for builtin Catalog plugin %s" % plugin.name)
|
#info("Adding widget for builtin Catalog plugin %s" % plugin.name)
|
||||||
try:
|
try:
|
||||||
catalog_widget = __import__('calibre.gui2.catalog.'+name,
|
catalog_widget = __import__('calibre.gui2.catalog.'+name,
|
||||||
fromlist=[1])
|
fromlist=[1])
|
||||||
pw = catalog_widget.PluginWidget()
|
pw = catalog_widget.PluginWidget()
|
||||||
pw.initialize(name)
|
pw.initialize(name)
|
||||||
pw.ICON = I('forward.svg')
|
pw.ICON = I('forward.svg')
|
||||||
self.widgets.append(pw)
|
self.widgets.append(pw)
|
||||||
[self.fmts.append([file_type.upper(), pw.sync_enabled,pw]) for file_type in plugin.file_types]
|
[self.fmts.append([file_type.upper(), pw.sync_enabled,pw]) for file_type in plugin.file_types]
|
||||||
except ImportError:
|
except ImportError:
|
||||||
info("ImportError with %s" % name)
|
info("ImportError with %s" % name)
|
||||||
continue
|
continue
|
||||||
@ -73,38 +69,36 @@ class Catalog(QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
if os.path.exists(form) and os.path.exists(klass):
|
if os.path.exists(form) and os.path.exists(klass):
|
||||||
#info("Adding widget for user-installed Catalog plugin %s" % plugin.name)
|
#info("Adding widget for user-installed Catalog plugin %s" % plugin.name)
|
||||||
|
|
||||||
# Compile the .ui form provided in plugin.zip
|
# Compile the .ui form provided in plugin.zip
|
||||||
if not os.path.exists(compiled_form):
|
if not os.path.exists(compiled_form):
|
||||||
# info('\tCompiling form', form)
|
# info('\tCompiling form', form)
|
||||||
buf = cStringIO.StringIO()
|
buf = cStringIO.StringIO()
|
||||||
compileUi(form, buf)
|
compileUi(form, buf)
|
||||||
dat = buf.getvalue()
|
dat = buf.getvalue()
|
||||||
dat = re.compile(r'QtGui.QApplication.translate\(.+?,\s+"(.+?)(?<!\\)",.+?\)',
|
dat = re.compile(r'QtGui.QApplication.translate\(.+?,\s+"(.+?)(?<!\\)",.+?\)',
|
||||||
re.DOTALL).sub(r'_("\1")', dat)
|
re.DOTALL).sub(r'_("\1")', dat)
|
||||||
open(compiled_form, 'wb').write(dat)
|
open(compiled_form, 'wb').write(dat)
|
||||||
|
|
||||||
# Import the dynamic PluginWidget() from .py file provided in plugin.zip
|
# Import the dynamic PluginWidget() from .py file provided in plugin.zip
|
||||||
try:
|
try:
|
||||||
sys.path.insert(0, plugin.resources_path)
|
sys.path.insert(0, plugin.resources_path)
|
||||||
catalog_widget = __import__(name, fromlist=[1])
|
catalog_widget = __import__(name, fromlist=[1])
|
||||||
pw = catalog_widget.PluginWidget()
|
pw = catalog_widget.PluginWidget()
|
||||||
pw.initialize(name)
|
pw.initialize(name)
|
||||||
pw.ICON = I('forward.svg')
|
pw.ICON = I('forward.svg')
|
||||||
self.widgets.append(pw)
|
self.widgets.append(pw)
|
||||||
[self.fmts.append([file_type.upper(), pw.sync_enabled,pw]) for file_type in plugin.file_types]
|
[self.fmts.append([file_type.upper(), pw.sync_enabled,pw]) for file_type in plugin.file_types]
|
||||||
except ImportError:
|
except ImportError:
|
||||||
info("ImportError with %s" % name)
|
info("ImportError with %s" % name)
|
||||||
continue
|
continue
|
||||||
finally:
|
finally:
|
||||||
sys.path.remove(plugin.resources_path)
|
sys.path.remove(plugin.resources_path)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
info("No dynamic tab resources found for %s" % name)
|
info("No dynamic tab resources found for %s" % name)
|
||||||
|
|
||||||
self.widgets = sorted(self.widgets, key=lambda x:(x.TITLE, x.TITLE))
|
self.widgets = sorted(self.widgets, cmp=lambda x,y:cmp(x.TITLE, y.TITLE))
|
||||||
for pw in self.widgets:
|
|
||||||
page = self.tabs.addTab(pw,pw.TITLE)
|
|
||||||
|
|
||||||
# Generate a sorted list of installed catalog formats/sync_enabled pairs
|
# Generate a sorted list of installed catalog formats/sync_enabled pairs
|
||||||
fmts = sorted([x[0] for x in self.fmts])
|
fmts = sorted([x[0] for x in self.fmts])
|
||||||
@ -127,7 +121,20 @@ class Catalog(QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
if self.sync.isEnabled():
|
if self.sync.isEnabled():
|
||||||
self.sync.setChecked(dynamic.get('catalog_sync_to_device', True))
|
self.sync.setChecked(dynamic.get('catalog_sync_to_device', True))
|
||||||
|
|
||||||
|
self.format.currentIndexChanged.connect(self.format_changed)
|
||||||
|
self.show_plugin_tab(None)
|
||||||
|
|
||||||
|
|
||||||
|
def show_plugin_tab(self, idx):
|
||||||
|
cf = unicode(self.format.currentText()).lower()
|
||||||
|
while self.tabs.count() > 1:
|
||||||
|
self.tabs.remove(1)
|
||||||
|
for pw in self.widgets:
|
||||||
|
if cf in pw.formats:
|
||||||
|
self.tabs.addTab(pw, pw.TITLE)
|
||||||
|
break
|
||||||
|
|
||||||
def format_changed(self, idx):
|
def format_changed(self, idx):
|
||||||
cf = unicode(self.format.currentText())
|
cf = unicode(self.format.currentText())
|
||||||
if cf in self.sync_enabled_formats:
|
if cf in self.sync_enabled_formats:
|
||||||
@ -136,6 +143,14 @@ class Catalog(QDialog, Ui_Dialog):
|
|||||||
self.sync.setDisabled(True)
|
self.sync.setDisabled(True)
|
||||||
self.sync.setChecked(False)
|
self.sync.setChecked(False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fmt_options(self):
|
||||||
|
ans = {}
|
||||||
|
if self.tabs.count() > 1:
|
||||||
|
w = self.tabs.widget(1)
|
||||||
|
ans = w.options()
|
||||||
|
return ans
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
self.catalog_format = unicode(self.format.currentText())
|
self.catalog_format = unicode(self.format.currentText())
|
||||||
dynamic.set('catalog_preferred_format', self.catalog_format)
|
dynamic.set('catalog_preferred_format', self.catalog_format)
|
||||||
|
@ -238,7 +238,7 @@ def fetch_scheduled_recipe(arg):
|
|||||||
|
|
||||||
def generate_catalog(parent, dbspec, ids):
|
def generate_catalog(parent, dbspec, ids):
|
||||||
from calibre.gui2.dialogs.catalog import Catalog
|
from calibre.gui2.dialogs.catalog import Catalog
|
||||||
|
|
||||||
# Build the Catalog dialog in gui2.dialogs.catalog
|
# Build the Catalog dialog in gui2.dialogs.catalog
|
||||||
d = Catalog(parent, dbspec, ids)
|
d = Catalog(parent, dbspec, ids)
|
||||||
|
|
||||||
@ -248,22 +248,13 @@ def generate_catalog(parent, dbspec, ids):
|
|||||||
# Create the output file
|
# Create the output file
|
||||||
out = PersistentTemporaryFile(suffix='_catalog_out.'+d.catalog_format.lower())
|
out = PersistentTemporaryFile(suffix='_catalog_out.'+d.catalog_format.lower())
|
||||||
|
|
||||||
# Retrieve plugin options
|
|
||||||
fmt_options = {}
|
|
||||||
for x in range(d.tabs.count()):
|
|
||||||
if str(d.tabs.tabText(x)).find(str(d.catalog_format)) > -1:
|
|
||||||
for fmt in d.fmts:
|
|
||||||
if fmt[0] == d.catalog_format:
|
|
||||||
fmt_options = fmt[2].options()
|
|
||||||
# print "gui2.tools:generate_catalog(): options for %s: %s" % (fmt[0], fmt_options)
|
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
d.catalog_format,
|
d.catalog_format,
|
||||||
d.catalog_title,
|
d.catalog_title,
|
||||||
dbspec,
|
dbspec,
|
||||||
ids,
|
ids,
|
||||||
out.name,
|
out.name,
|
||||||
fmt_options
|
d.fmt_options
|
||||||
]
|
]
|
||||||
out.close()
|
out.close()
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
'''The main GUI'''
|
'''The main GUI'''
|
||||||
|
|
||||||
import atexit, os, shutil, sys, tempfile, textwrap, collections, time
|
import os, shutil, sys, textwrap, collections, time
|
||||||
from xml.parsers.expat import ExpatError
|
from xml.parsers.expat import ExpatError
|
||||||
from Queue import Queue, Empty
|
from Queue import Queue, Empty
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@ -1092,6 +1092,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.library_view.model().refresh_ids(ids)
|
self.library_view.model().refresh_ids(ids)
|
||||||
self.library_view.model().current_changed(self.library_view.currentIndex(),
|
self.library_view.model().current_changed(self.library_view.currentIndex(),
|
||||||
self.library_view.currentIndex())
|
self.library_view.currentIndex())
|
||||||
|
if ids:
|
||||||
|
self.tags_view.recount()
|
||||||
|
|
||||||
def delete_all_but_selected_formats(self, *args):
|
def delete_all_but_selected_formats(self, *args):
|
||||||
ids = self._get_selected_ids()
|
ids = self._get_selected_ids()
|
||||||
@ -1113,6 +1115,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.library_view.model().refresh_ids(ids)
|
self.library_view.model().refresh_ids(ids)
|
||||||
self.library_view.model().current_changed(self.library_view.currentIndex(),
|
self.library_view.model().current_changed(self.library_view.currentIndex(),
|
||||||
self.library_view.currentIndex())
|
self.library_view.currentIndex())
|
||||||
|
if ids:
|
||||||
|
self.tags_view.recount()
|
||||||
|
|
||||||
|
|
||||||
def delete_covers(self, *args):
|
def delete_covers(self, *args):
|
||||||
@ -1359,7 +1363,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
|
|
||||||
############################### Generate catalog ###########################
|
############################### Generate catalog ###########################
|
||||||
|
|
||||||
def generate_catalog(self):
|
def generate_catalog(self):
|
||||||
rows = self.library_view.selectionModel().selectedRows()
|
rows = self.library_view.selectionModel().selectedRows()
|
||||||
if not rows or len(rows) < 2:
|
if not rows or len(rows) < 2:
|
||||||
rows = xrange(self.library_view.model().rowCount(QModelIndex()))
|
rows = xrange(self.library_view.model().rowCount(QModelIndex()))
|
||||||
@ -1375,7 +1379,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
ret = generate_catalog(self, dbspec, ids)
|
ret = generate_catalog(self, dbspec, ids)
|
||||||
if ret is None:
|
if ret is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
func, args, desc, out, sync, title = ret
|
func, args, desc, out, sync, title = ret
|
||||||
|
|
||||||
fmt = os.path.splitext(out)[1][1:].upper()
|
fmt = os.path.splitext(out)[1][1:].upper()
|
||||||
@ -1384,7 +1388,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
description=desc)
|
description=desc)
|
||||||
job.catalog_file_path = out
|
job.catalog_file_path = out
|
||||||
job.fmt = fmt
|
job.fmt = fmt
|
||||||
job.catalog_sync, job.catalog_title = sync, title
|
job.catalog_sync, job.catalog_title = sync, title
|
||||||
self.status_bar.showMessage(_('Generating %s catalog...')%fmt)
|
self.status_bar.showMessage(_('Generating %s catalog...')%fmt)
|
||||||
|
|
||||||
def catalog_generated(self, job):
|
def catalog_generated(self, job):
|
||||||
@ -1399,12 +1403,12 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.status_bar.showMessage(_('Catalog generated.'), 3000)
|
self.status_bar.showMessage(_('Catalog generated.'), 3000)
|
||||||
self.sync_catalogs()
|
self.sync_catalogs()
|
||||||
if job.fmt in ['CSV','XML']:
|
if job.fmt in ['CSV','XML']:
|
||||||
export_dir = choose_dir(self, 'Export Catalog Directory',
|
export_dir = choose_dir(self, _('Export Catalog Directory'),
|
||||||
'Select destination for %s.%s' % (job.catalog_title, job.fmt.lower()))
|
_('Select destination for %s.%s') % (job.catalog_title, job.fmt.lower()))
|
||||||
if export_dir:
|
if export_dir:
|
||||||
destination = os.path.join(export_dir, '%s.%s' % (job.catalog_title, job.fmt.lower()))
|
destination = os.path.join(export_dir, '%s.%s' % (job.catalog_title, job.fmt.lower()))
|
||||||
shutil.copyfile(job.catalog_file_path, destination)
|
shutil.copyfile(job.catalog_file_path, destination)
|
||||||
|
|
||||||
############################### Fetch news #################################
|
############################### Fetch news #################################
|
||||||
|
|
||||||
def download_scheduled_recipe(self, arg):
|
def download_scheduled_recipe(self, arg):
|
||||||
|
@ -2,6 +2,11 @@ import os
|
|||||||
|
|
||||||
from calibre.customize import CatalogPlugin
|
from calibre.customize import CatalogPlugin
|
||||||
|
|
||||||
|
FIELDS = ['all', 'author_sort', 'authors', 'comments',
|
||||||
|
'cover', 'formats', 'id', 'isbn', 'pubdate', 'publisher', 'rating',
|
||||||
|
'series_index', 'series', 'size', 'tags', 'timestamp', 'title',
|
||||||
|
'uuid']
|
||||||
|
|
||||||
class CSV_XML(CatalogPlugin):
|
class CSV_XML(CatalogPlugin):
|
||||||
'CSV/XML catalog generator'
|
'CSV/XML catalog generator'
|
||||||
|
|
||||||
@ -22,11 +27,9 @@ class CSV_XML(CatalogPlugin):
|
|||||||
dest = 'fields',
|
dest = 'fields',
|
||||||
help = _('The fields to output when cataloging books in the '
|
help = _('The fields to output when cataloging books in the '
|
||||||
'database. Should be a comma-separated list of fields.\n'
|
'database. Should be a comma-separated list of fields.\n'
|
||||||
'Available fields: all, author_sort, authors, comments, '
|
'Available fields: %s.\n'
|
||||||
'cover, formats, id, isbn, pubdate, publisher, rating, '
|
"Default: '%%default'\n"
|
||||||
'series_index, series, size, tags, timestamp, title, uuid.\n'
|
"Applies to: CSV, XML output formats")%', '.join(FIELDS)),
|
||||||
"Default: '%default'\n"
|
|
||||||
"Applies to: CSV, XML output formats")),
|
|
||||||
|
|
||||||
Option('--sort-by',
|
Option('--sort-by',
|
||||||
default = 'id',
|
default = 'id',
|
||||||
@ -41,7 +44,7 @@ class CSV_XML(CatalogPlugin):
|
|||||||
|
|
||||||
log = Log()
|
log = Log()
|
||||||
self.fmt = path_to_output.rpartition('.')[2]
|
self.fmt = path_to_output.rpartition('.')[2]
|
||||||
|
|
||||||
if False and opts.verbose:
|
if False and opts.verbose:
|
||||||
log("%s:run" % self.name)
|
log("%s:run" % self.name)
|
||||||
log(" path_to_output: %s" % path_to_output)
|
log(" path_to_output: %s" % path_to_output)
|
||||||
@ -54,7 +57,7 @@ class CSV_XML(CatalogPlugin):
|
|||||||
log(" opts:")
|
log(" opts:")
|
||||||
for key in keys:
|
for key in keys:
|
||||||
log(" %s: %s" % (key, opts_dict[key]))
|
log(" %s: %s" % (key, opts_dict[key]))
|
||||||
|
|
||||||
# Get the sorted, filtered database as a dictionary
|
# Get the sorted, filtered database as a dictionary
|
||||||
data = self.search_sort_db(db, opts)
|
data = self.search_sort_db(db, opts)
|
||||||
|
|
||||||
@ -69,7 +72,7 @@ class CSV_XML(CatalogPlugin):
|
|||||||
outfile = open(path_to_output, 'w')
|
outfile = open(path_to_output, 'w')
|
||||||
|
|
||||||
# Output the field headers
|
# Output the field headers
|
||||||
outfile.write('%s\n' % ','.join(fields))
|
outfile.write(u'%s\n' % u','.join(fields))
|
||||||
|
|
||||||
# Output the entry fields
|
# Output the entry fields
|
||||||
for entry in data:
|
for entry in data:
|
||||||
@ -80,15 +83,15 @@ class CSV_XML(CatalogPlugin):
|
|||||||
item = ', '.join(item)
|
item = ', '.join(item)
|
||||||
if x < len(fields) - 1:
|
if x < len(fields) - 1:
|
||||||
if item is not None:
|
if item is not None:
|
||||||
outstr += '"%s",' % str(item).replace('"','""')
|
outstr += u'"%s",' % unicode(item).replace('"','""')
|
||||||
else:
|
else:
|
||||||
outstr += '"",'
|
outstr += '"",'
|
||||||
else:
|
else:
|
||||||
if item is not None:
|
if item is not None:
|
||||||
outstr += '"%s"\n' % str(item).replace('"','""')
|
outstr += u'"%s"\n' % unicode(item).replace('"','""')
|
||||||
else:
|
else:
|
||||||
outstr += '""\n'
|
outstr += '""\n'
|
||||||
outfile.write(outstr)
|
outfile.write(outstr.encode('utf-8'))
|
||||||
outfile.close()
|
outfile.close()
|
||||||
|
|
||||||
elif self.fmt == 'xml':
|
elif self.fmt == 'xml':
|
||||||
|
@ -673,7 +673,7 @@ def command_catalog(args, dbpath):
|
|||||||
if opts.verbose:
|
if opts.verbose:
|
||||||
log("library.cli:command_catalog dispatching to plugin %s" % plugin.name)
|
log("library.cli:command_catalog dispatching to plugin %s" % plugin.name)
|
||||||
if opts.ids:
|
if opts.ids:
|
||||||
opts.ids = [int(id) for id in opts.ids.split(',')]
|
opts.ids = [int(id) for id in opts.ids.split(',')]
|
||||||
|
|
||||||
with plugin:
|
with plugin:
|
||||||
plugin.run(args[1], opts, get_db(dbpath, opts))
|
plugin.run(args[1], opts, get_db(dbpath, opts))
|
||||||
|
@ -194,7 +194,7 @@ You can insert the following two lines of code to start an interactive python se
|
|||||||
When running from the command line, this will start an interactive python interpreter with access to all
|
When running from the command line, this will start an interactive python interpreter with access to all
|
||||||
locally defined variables (variables in the local scope). The interactive prompt even has TAB completion
|
locally defined variables (variables in the local scope). The interactive prompt even has TAB completion
|
||||||
for object properties and you can use the various python facilities for introspection, such as
|
for object properties and you can use the various python facilities for introspection, such as
|
||||||
:function:`dir`, :function:`type`, :function:`repr`, etc.
|
:func:`dir`, :func:`type`, :func:`repr`, etc.
|
||||||
|
|
||||||
Using print statements
|
Using print statements
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -204,4 +204,18 @@ terminal. For example, you can start the GUI from the terminal as::
|
|||||||
|
|
||||||
calibre-debug -g
|
calibre-debug -g
|
||||||
|
|
||||||
|
Executing arbitrary scripts in the calibre python environment
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The :command:`calibre-debug` command provides a couple of handy switches to execute your own
|
||||||
|
code, with access to the calibre modules::
|
||||||
|
|
||||||
|
calibre-debug -c "some python code"
|
||||||
|
|
||||||
|
is great for testing a little snippet of code on the command line. It works in the same way as the -c switch to the python interpreter::
|
||||||
|
|
||||||
|
calibre-debug -e myscript.py
|
||||||
|
|
||||||
|
can be used to execute your own python script. It works in the same way as passing the script to the python interpreter, except
|
||||||
|
that the calibre environment is fully initialized, so you can use all the calibre code in your script.
|
||||||
|
|
||||||
|
BIN
src/calibre/manual/images/bookmark.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/calibre/manual/images/font_size.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/calibre/manual/images/full_screen.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/calibre/manual/images/nav_pos.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/calibre/manual/images/pref_button.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/calibre/manual/images/prev_next.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
src/calibre/manual/images/ref_mode.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
src/calibre/manual/images/ref_mode_button.png
Normal file
After Width: | Height: | Size: 733 B |
BIN
src/calibre/manual/images/toc.png
Normal file
After Width: | Height: | Size: 628 B |
@ -29,6 +29,7 @@ Sections
|
|||||||
|
|
||||||
gui
|
gui
|
||||||
news
|
news
|
||||||
|
viewer
|
||||||
conversion
|
conversion
|
||||||
metadata
|
metadata
|
||||||
faq
|
faq
|
||||||
|
105
src/calibre/manual/viewer.rst
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
.. include:: global.rst
|
||||||
|
|
||||||
|
.. _gui:
|
||||||
|
|
||||||
|
The E-book Viewer
|
||||||
|
=============================
|
||||||
|
|
||||||
|
|app| includes a built-in E-book viewer that can view all the major e-book formats.
|
||||||
|
The viewer is highly customizable and has many advanced features.
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:depth: 1
|
||||||
|
:local:
|
||||||
|
|
||||||
|
Starting the viewer
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
You can view any of the books in your |app| library by selecting the book and pressing the View button. This
|
||||||
|
will open up the book in the e-book viewer. You can also launch the viewer by itself, from the Start menu in windows
|
||||||
|
or using the command :command:`ebook-viewer` in Linux and OS X (you have to install the command line tools on OS X
|
||||||
|
first by going to Preferences->Advanced).
|
||||||
|
|
||||||
|
Navigating around an e-book
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
.. |pni| image:: images/prev_next.png
|
||||||
|
|
||||||
|
.. |bookmi| image:: images/bookmark.png
|
||||||
|
|
||||||
|
.. |toci| image:: images/toc.png
|
||||||
|
|
||||||
|
.. |navposi| image:: images/nav_pos.png
|
||||||
|
|
||||||
|
.. |refmi| image:: images/ref_mode_button.png
|
||||||
|
|
||||||
|
|
||||||
|
You can "turn pages" in a book by using the :guilabel:`Page Next` and :guilabel:`Page Previous` buttons |pni|, or by pressing
|
||||||
|
the Page Down/Page Up keys. Unlike most e-book viewers, |app| does not force you to view books in paged mode. You can
|
||||||
|
scroll by amounts less than a page by using the scroll bar or various customizable keyboard shortcuts.
|
||||||
|
|
||||||
|
Bookmarks
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
When you are in the middle of a book and close the viewer, it will remember where you stopped reading and return there
|
||||||
|
the next time you open the book. You can also set bookmarks in the book by using the Bookmark button |bookmi|. When viewing EPUB format
|
||||||
|
books, these bookmarks are actually saved in the EPUB file itself, so you can add bookmarks, then send the file to a friend and
|
||||||
|
when they open the file, they will be able to see your bookmarks.
|
||||||
|
|
||||||
|
Table of Contents
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
If the book you are reading defines a Table of Contents, you can access it by pressing the Table of Contents button |toci|.
|
||||||
|
This will bring up a list of sections in the book and you can click on any of them to jump to that portion of the book.
|
||||||
|
|
||||||
|
Navigating by location
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
E-books, unlike paper books have no concept of pages. Instead,
|
||||||
|
as you read through the book, you will notice that your position in the book is displayed in the upper left corner in a box
|
||||||
|
like this |navposi|. This is both your current position and the total length of the book. These numbers are independent of the screen size and font
|
||||||
|
size you are viewing the boko at, and they play a similar role to page numbers in paper books.
|
||||||
|
You can enter any number you like to go to the corresponding location in the book.
|
||||||
|
|
||||||
|
|app| also has a very handy
|
||||||
|
reference mode. You can turn it on by clicking the Reference Mode button |refmi|. Once you do this, every time you move your
|
||||||
|
mouse over a paragraph, calibre will display a unique number made up of the section and paragraph numbers.
|
||||||
|
|
||||||
|
.. image:: images/ref_mode.png
|
||||||
|
|
||||||
|
You can use this number to unambiguously refer to parts of the books when discussing it with friends or referring to it
|
||||||
|
in other works. You can enter these numbers in the box marked Go to at the top of the window to go to a particular
|
||||||
|
reference location.
|
||||||
|
|
||||||
|
If you click on links inside the e-book to take you to different parts of the book, like an endnote, you can use the back and forward buttons
|
||||||
|
in the top left corner to return to where you were. These button behave just like those in a web browser.
|
||||||
|
|
||||||
|
Customizing the look and feel of your reading experience
|
||||||
|
------------------------------------------------------------
|
||||||
|
|
||||||
|
.. |fontsizei| image:: images/font_size.png
|
||||||
|
|
||||||
|
.. |fsi| image:: images/full_screen.png
|
||||||
|
|
||||||
|
.. |prefbi| image:: images/pref_button.png
|
||||||
|
|
||||||
|
You can change font sizes on the fly by using the font size buttons |fontsizei|. You can also make the viewer full screen
|
||||||
|
by pressing the Full Screen button |fsi|. By clicking the Preferences button |prefbi|, you can change the default fonts used
|
||||||
|
by the viewer to ones you like as well as the default font size when the viewer starts up.
|
||||||
|
|
||||||
|
More advanced customization can be achieved by the User Stylesheet setting. This is a stylesheet you can set that will be applied
|
||||||
|
to every book. Using it you can do things like have white text on a black background, change paragraph styles, text justification, etc.
|
||||||
|
For examples if custom stylesheets used by |app|'s users, see `the forums <http://www.mobileread.com/forums/showthread.php?t=51500>`_.
|
||||||
|
|
||||||
|
Dictionary lookup
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
You can lookup the meaning of words in the current book by right clicking on a word. |app| uses the publicly available dictionary
|
||||||
|
server at ``dict.org`` to lookup words. The definition is displayed in a small box at the bottom of the screen.
|
||||||
|
|
||||||
|
Copying text and images
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
You can select text and images by dragging the content with your mouse and then right click to copy to the clipboard.
|
||||||
|
The copied material can be pasted into another application as plain text and images.
|
||||||
|
|