mirror of
git://github.com/kovidgoyal/calibre.git
synced 2026-02-11 04:54:47 +01:00
merge from trunk
This commit is contained in:
commit
870929d1c7
27 changed files with 278 additions and 76 deletions
|
|
@ -4,6 +4,7 @@
|
|||
economictimes.indiatimes.com
|
||||
'''
|
||||
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class TheEconomicTimes(BasicNewsRecipe):
|
||||
|
|
@ -32,18 +33,17 @@ class TheEconomicTimes(BasicNewsRecipe):
|
|||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(attrs={'class':'printdiv'})]
|
||||
remove_tags = [dict(name=['object','link','embed','iframe','base','table','meta'])]
|
||||
remove_attributes = ['name']
|
||||
|
||||
remove_tags_before = dict(name='h1')
|
||||
feeds = [(u'All articles', u'http://economictimes.indiatimes.com/rssfeedsdefault.cms')]
|
||||
|
||||
def print_version(self, url):
|
||||
rest, sep, art = url.rpartition('/articleshow/')
|
||||
return 'http://m.economictimes.com/PDAET/articleshow/' + art
|
||||
return 'http://economictimes.indiatimes.com/articleshow/' + art + '?prtpage=1'
|
||||
|
||||
def get_article_url(self, article):
|
||||
rurl = article.get('link', None)
|
||||
rurl = article.get('guid', None)
|
||||
if (rurl.find('/quickieslist/') > 0) or (rurl.find('/quickiearticleshow/') > 0):
|
||||
return None
|
||||
return rurl
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ class TimesOfIndia(BasicNewsRecipe):
|
|||
max_articles_per_feed = 25
|
||||
|
||||
no_stylesheets = True
|
||||
keep_only_tags = [dict(attrs={'class':'prttabl'})]
|
||||
keep_only_tags = [dict(attrs={'class':'maintable12'})]
|
||||
remove_tags = [
|
||||
dict(style=lambda x: x and 'float' in x)
|
||||
dict(style=lambda x: x and 'float' in x),
|
||||
dict(attrs={'class':'prvnxtbg'}),
|
||||
]
|
||||
|
||||
feeds = [
|
||||
|
|
|
|||
|
|
@ -80,6 +80,34 @@ def initialize(self):
|
|||
'''
|
||||
pass
|
||||
|
||||
def load_resources(self, names):
|
||||
'''
|
||||
If this plugin comes in a ZIP file (user added plugin), this method
|
||||
will allow you to load resources from the ZIP file.
|
||||
|
||||
For example to load an image::
|
||||
|
||||
pixmap = QPixmap()
|
||||
pixmap.loadFromData(self.load_resources(['images/icon.png']).itervalues().next())
|
||||
icon = QIcon(pixmap)
|
||||
|
||||
:param names: List of paths to resources in the zip file using / as separator
|
||||
|
||||
:return: A dictionary of the form ``{name : file_contents}``. Any names
|
||||
that were not found in the zip file will not be present in the
|
||||
dictionary.
|
||||
|
||||
'''
|
||||
if self.plugin_path is None:
|
||||
raise ValueError('This plugin was not loaded from a ZIP file')
|
||||
ans = {}
|
||||
with zipfile.ZipFile(self.plugin_path, 'r') as zf:
|
||||
for candidate in zf.namelist():
|
||||
if candidate in names:
|
||||
ans[candidate] = zf.read(candidate)
|
||||
return ans
|
||||
|
||||
|
||||
def customization_help(self, gui=False):
|
||||
'''
|
||||
Return a string giving help on how to customize this plugin.
|
||||
|
|
|
|||
|
|
@ -101,6 +101,13 @@ class EPUBOutput(OutputFormatPlugin):
|
|||
)
|
||||
),
|
||||
|
||||
OptionRecommendation(name='epub_flatten', recommended_value=False,
|
||||
help=_('This option is needed only if you intend to use the EPUB'
|
||||
' with FBReaderJ. It will flatten the file system inside the'
|
||||
' EPUB, putting all files into the top level.')
|
||||
),
|
||||
|
||||
|
||||
])
|
||||
|
||||
recommendations = set([('pretty_print', True, OptionRecommendation.HIGH)])
|
||||
|
|
@ -142,8 +149,12 @@ def upshift_markup(self): # {{{
|
|||
def convert(self, oeb, output_path, input_plugin, opts, log):
|
||||
self.log, self.opts, self.oeb = log, opts, oeb
|
||||
|
||||
#from calibre.ebooks.oeb.transforms.filenames import UniqueFilenames
|
||||
#UniqueFilenames()(oeb, opts)
|
||||
if self.opts.epub_flatten:
|
||||
from calibre.ebooks.oeb.transforms.filenames import FlatFilenames
|
||||
FlatFilenames()(oeb, opts)
|
||||
else:
|
||||
from calibre.ebooks.oeb.transforms.filenames import UniqueFilenames
|
||||
UniqueFilenames()(oeb, opts)
|
||||
|
||||
self.workaround_ade_quirks()
|
||||
self.workaround_webkit_quirks()
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ def config_widget(self):
|
|||
setattr(w, '_'+x, cb)
|
||||
cb.setChecked(c.get(x, True))
|
||||
w._layout.addWidget(cb)
|
||||
|
||||
|
||||
if self.has_html_comments:
|
||||
cb = QCheckBox(_('Convert comments downloaded from %s to plain text')%(self.name))
|
||||
setattr(w, '_textcomments', cb)
|
||||
|
|
@ -276,12 +276,13 @@ def result_index(source, result):
|
|||
return -1
|
||||
|
||||
def merge_results(one, two):
|
||||
for x in two:
|
||||
idx = result_index(one, x)
|
||||
if idx < 0:
|
||||
one.append(x)
|
||||
else:
|
||||
one[idx].smart_update(x)
|
||||
if two is not None and one is not None:
|
||||
for x in two:
|
||||
idx = result_index(one, x)
|
||||
if idx < 0:
|
||||
one.append(x)
|
||||
else:
|
||||
one[idx].smart_update(x)
|
||||
|
||||
class MetadataSources(object):
|
||||
|
||||
|
|
@ -337,7 +338,7 @@ def search(title=None, author=None, publisher=None, isbn=None, isbndb_key=None,
|
|||
manager(title, author, publisher, isbn, verbose)
|
||||
manager.join()
|
||||
|
||||
results = list(fetchers[0].results)
|
||||
results = list(fetchers[0].results) if fetchers else []
|
||||
for fetcher in fetchers[1:]:
|
||||
merge_results(results, fetcher.results)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@
|
|||
from calibre.ebooks.metadata.opf2 import OPFCreator, OPF
|
||||
from calibre.ebooks.metadata.toc import TOC
|
||||
|
||||
class TopazError(ValueError):
|
||||
pass
|
||||
|
||||
class EXTHHeader(object):
|
||||
|
||||
def __init__(self, raw, codec, title):
|
||||
|
|
@ -239,7 +242,7 @@ def __init__(self, filename_or_stream, log, user_encoding=None, debug=None,
|
|||
self.base_css_rules = textwrap.dedent('''
|
||||
blockquote { margin: 0em 0em 0em 2em; text-align: justify }
|
||||
|
||||
p { margin: 0em; text-align: justify }
|
||||
p { margin: 0em; text-align: justify; text-indent: 1.5em }
|
||||
|
||||
.bold { font-weight: bold }
|
||||
|
||||
|
|
@ -259,7 +262,7 @@ def __init__(self, filename_or_stream, log, user_encoding=None, debug=None,
|
|||
|
||||
raw = stream.read()
|
||||
if raw.startswith('TPZ'):
|
||||
raise ValueError(_('This is an Amazon Topaz book. It cannot be processed.'))
|
||||
raise TopazError(_('This is an Amazon Topaz book. It cannot be processed.'))
|
||||
|
||||
self.header = raw[0:72]
|
||||
self.name = self.header[:32].replace('\x00', '')
|
||||
|
|
@ -832,6 +835,15 @@ def extract_images(self, processed_records, output_dir):
|
|||
im.save(open(path, 'wb'), format='JPEG')
|
||||
|
||||
def get_metadata(stream):
|
||||
stream.seek(0)
|
||||
try:
|
||||
raw = stream.read(3)
|
||||
except:
|
||||
raw = ''
|
||||
stream.seek(0)
|
||||
if raw == 'TPZ':
|
||||
from calibre.ebooks.metadata.topaz import get_metadata
|
||||
return get_metadata(stream)
|
||||
from calibre.utils.logging import Log
|
||||
log = Log()
|
||||
mi = MetaInformation(os.path.basename(stream.name), [_('Unknown')])
|
||||
|
|
@ -861,7 +873,10 @@ def get_metadata(stream):
|
|||
cover_index = mh.first_image_index + mh.exth.cover_offset
|
||||
data = mh.section_data(int(cover_index))
|
||||
else:
|
||||
data = mh.section_data(mh.first_image_index)
|
||||
try:
|
||||
data = mh.section_data(mh.first_image_index)
|
||||
except:
|
||||
data = ''
|
||||
buf = cStringIO.StringIO(data)
|
||||
try:
|
||||
im = PILImage.open(buf)
|
||||
|
|
|
|||
|
|
@ -13,15 +13,16 @@
|
|||
|
||||
from calibre.ebooks.oeb.base import rewrite_links, urlnormalize
|
||||
|
||||
class RenameFiles(object):
|
||||
class RenameFiles(object): # {{{
|
||||
|
||||
'''
|
||||
Rename files and adjust all links pointing to them. Note that the spine
|
||||
and manifest are not touched by this transform.
|
||||
'''
|
||||
|
||||
def __init__(self, rename_map):
|
||||
def __init__(self, rename_map, renamed_items_map = None):
|
||||
self.rename_map = rename_map
|
||||
self.renamed_items_map = renamed_items_map
|
||||
|
||||
def __call__(self, oeb, opts):
|
||||
self.log = oeb.logger
|
||||
|
|
@ -49,7 +50,6 @@ def __call__(self, oeb, opts):
|
|||
if self.oeb.toc:
|
||||
self.fix_toc_entry(self.oeb.toc)
|
||||
|
||||
|
||||
def fix_toc_entry(self, toc):
|
||||
if toc.href:
|
||||
href = urlnormalize(toc.href)
|
||||
|
|
@ -66,18 +66,22 @@ def fix_toc_entry(self, toc):
|
|||
self.fix_toc_entry(x)
|
||||
|
||||
def url_replacer(self, orig_url):
|
||||
url = urlnormalize(orig_url)
|
||||
path, frag = urldefrag(url)
|
||||
href = self.current_item.abshref(path)
|
||||
replacement = self.rename_map.get(href, None)
|
||||
if replacement is None:
|
||||
return orig_url
|
||||
replacement = self.current_item.relhref(replacement)
|
||||
if frag:
|
||||
replacement += '#' + frag
|
||||
return replacement
|
||||
url = urlnormalize(orig_url)
|
||||
path, frag = urldefrag(url)
|
||||
if self.renamed_items_map:
|
||||
orig_item = self.renamed_items_map.get(self.current_item.href, self.current_item)
|
||||
else:
|
||||
orig_item = self.current_item
|
||||
|
||||
class UniqueFilenames(object):
|
||||
href = orig_item.abshref(path)
|
||||
replacement = self.current_item.relhref(self.rename_map.get(href, href))
|
||||
if frag:
|
||||
replacement += '#' + frag
|
||||
return replacement
|
||||
|
||||
# }}}
|
||||
|
||||
class UniqueFilenames(object): # {{{
|
||||
|
||||
'Ensure that every item in the manifest has a unique filename'
|
||||
|
||||
|
|
@ -127,4 +131,48 @@ def unique_suffix(self, fname):
|
|||
candidate = base + suffix + ext
|
||||
if candidate not in self.seen_filenames:
|
||||
return suffix
|
||||
# }}}
|
||||
|
||||
class FlatFilenames(object): # {{{
|
||||
|
||||
'Ensure that every item in the manifest has a unique filename without subdirectories.'
|
||||
|
||||
def __call__(self, oeb, opts):
|
||||
self.log = oeb.logger
|
||||
self.opts = opts
|
||||
self.oeb = oeb
|
||||
|
||||
self.rename_map = {}
|
||||
self.renamed_items_map = {}
|
||||
|
||||
for item in list(oeb.manifest.items):
|
||||
# Flatten URL by removing directories.
|
||||
# Example: a/b/c/index.html -> a_b_c_index.html
|
||||
nhref = item.href.replace("/", "_")
|
||||
|
||||
if item.href == nhref:
|
||||
# URL hasn't changed, skip item.
|
||||
continue
|
||||
|
||||
data = item.data
|
||||
nhref = oeb.manifest.generate(href=nhref)[1]
|
||||
nitem = oeb.manifest.add(item.id, nhref, item.media_type, data=data,
|
||||
fallback=item.fallback)
|
||||
self.rename_map[item.href] = nhref
|
||||
self.renamed_items_map[nhref] = item
|
||||
if item.spine_position is not None:
|
||||
oeb.spine.insert(item.spine_position, nitem, item.linear)
|
||||
oeb.spine.remove(item)
|
||||
oeb.manifest.remove(item)
|
||||
|
||||
if self.rename_map:
|
||||
self.log('Found non-flat filenames, renaming to support broken'
|
||||
' EPUB readers like FBReader...')
|
||||
from pprint import pformat
|
||||
self.log.debug(pformat(self.rename_map))
|
||||
self.log.debug(pformat(self.renamed_items_map))
|
||||
|
||||
renamer = RenameFiles(self.rename_map, self.renamed_items_map)
|
||||
renamer(oeb, opts)
|
||||
# }}}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from functools import partial
|
||||
from zipfile import ZipFile
|
||||
|
||||
from PyQt4.Qt import QToolButton, QAction, QIcon, QObject
|
||||
|
||||
|
|
@ -108,6 +109,34 @@ def create_action(self, spec=None, attr='qaction'):
|
|||
setattr(self, attr, action)
|
||||
return action
|
||||
|
||||
def load_resources(self, names):
|
||||
'''
|
||||
If this plugin comes in a ZIP file (user added plugin), this method
|
||||
will allow you to load resources from the ZIP file.
|
||||
|
||||
For example to load an image::
|
||||
|
||||
pixmap = QPixmap()
|
||||
pixmap.loadFromData(self.load_resources(['images/icon.png']).itervalues().next())
|
||||
icon = QIcon(pixmap)
|
||||
|
||||
:param names: List of paths to resources in the zip file using / as separator
|
||||
|
||||
:return: A dictionary of the form ``{name : file_contents}``. Any names
|
||||
that were not found in the zip file will not be present in the
|
||||
dictionary.
|
||||
|
||||
'''
|
||||
if self.plugin_path is None:
|
||||
raise ValueError('This plugin was not loaded from a ZIP file')
|
||||
ans = {}
|
||||
with ZipFile(self.plugin_path, 'r') as zf:
|
||||
for candidate in zf.namelist():
|
||||
if candidate in names:
|
||||
ans[candidate] = zf.read(candidate)
|
||||
return ans
|
||||
|
||||
|
||||
def genesis(self):
|
||||
'''
|
||||
Setup this plugin. Only called once during initialization. self.gui is
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ def files_dropped_on_book(self, event, paths):
|
|||
if self.gui.current_view() is not self.gui.library_view:
|
||||
return
|
||||
db = self.gui.library_view.model().db
|
||||
cover_changed = False
|
||||
current_idx = self.gui.library_view.currentIndex()
|
||||
if not current_idx.isValid(): return
|
||||
cid = db.id(current_idx.row())
|
||||
|
|
@ -133,12 +134,16 @@ def files_dropped_on_book(self, event, paths):
|
|||
if not pmap.isNull():
|
||||
accept = True
|
||||
db.set_cover(cid, pmap)
|
||||
cover_changed = True
|
||||
elif ext in BOOK_EXTENSIONS:
|
||||
db.add_format_with_hooks(cid, ext, path, index_is_id=True)
|
||||
accept = True
|
||||
if accept:
|
||||
event.accept()
|
||||
self.gui.library_view.model().current_changed(current_idx, current_idx)
|
||||
if cover_changed:
|
||||
if self.gui.cover_flow:
|
||||
self.gui.cover_flow.dataChanged()
|
||||
|
||||
def __add_filesystem_book(self, paths, allow_device=True):
|
||||
if isinstance(paths, basestring):
|
||||
|
|
|
|||
|
|
@ -160,15 +160,17 @@ def genesis(self):
|
|||
self.action_choose.triggered.connect(self.choose_library,
|
||||
type=Qt.QueuedConnection)
|
||||
self.choose_menu = QMenu(self.gui)
|
||||
self.choose_menu.addAction(self.action_choose)
|
||||
self.qaction.setMenu(self.choose_menu)
|
||||
|
||||
self.quick_menu = QMenu(_('Quick switch'))
|
||||
self.quick_menu_action = self.choose_menu.addMenu(self.quick_menu)
|
||||
self.rename_menu = QMenu(_('Rename library'))
|
||||
self.rename_menu_action = self.choose_menu.addMenu(self.rename_menu)
|
||||
self.delete_menu = QMenu(_('Delete library'))
|
||||
self.delete_menu_action = self.choose_menu.addMenu(self.delete_menu)
|
||||
if not os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
|
||||
self.choose_menu.addAction(self.action_choose)
|
||||
|
||||
self.quick_menu = QMenu(_('Quick switch'))
|
||||
self.quick_menu_action = self.choose_menu.addMenu(self.quick_menu)
|
||||
self.rename_menu = QMenu(_('Rename library'))
|
||||
self.rename_menu_action = self.choose_menu.addMenu(self.rename_menu)
|
||||
self.delete_menu = QMenu(_('Delete library'))
|
||||
self.delete_menu_action = self.choose_menu.addMenu(self.delete_menu)
|
||||
|
||||
self.rename_separator = self.choose_menu.addSeparator()
|
||||
|
||||
|
|
@ -223,6 +225,8 @@ def initialization_complete(self):
|
|||
self.library_changed(self.gui.library_view.model().db)
|
||||
|
||||
def build_menus(self):
|
||||
if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
|
||||
return
|
||||
db = self.gui.library_view.model().db
|
||||
locations = list(self.stats.locations(db))
|
||||
for ac in self.switch_actions:
|
||||
|
|
@ -387,6 +391,11 @@ def choose_library(self, *args):
|
|||
c.exec_()
|
||||
|
||||
def change_library_allowed(self):
|
||||
if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
|
||||
warning_dialog(self.gui, _('Not allowed'),
|
||||
_('You cannot change libraries while using the environment'
|
||||
' variable CALIBRE_OVERRIDE_DATABASE_PATH.'), show=True)
|
||||
return False
|
||||
if self.gui.job_manager.has_jobs():
|
||||
warning_dialog(self.gui, _('Not allowed'),
|
||||
_('You cannot change libraries while jobs'
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
from PyQt4.Qt import QMenu, QToolButton
|
||||
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
from calibre.gui2 import error_dialog, Dispatcher
|
||||
from calibre.gui2 import error_dialog, Dispatcher, warning_dialog
|
||||
from calibre.gui2.dialogs.progress import ProgressDialog
|
||||
from calibre.utils.config import prefs, tweaks
|
||||
|
||||
|
|
@ -106,6 +106,9 @@ def location_selected(self, loc):
|
|||
|
||||
def build_menus(self):
|
||||
self.menu.clear()
|
||||
if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
|
||||
self.menu.addAction('disabled', self.cannot_do_dialog)
|
||||
return
|
||||
db = self.gui.library_view.model().db
|
||||
locations = list(self.stats.locations(db))
|
||||
for name, loc in locations:
|
||||
|
|
@ -160,5 +163,9 @@ def progress(idx, title):
|
|||
self.gui.iactions['Remove Books'].library_ids_deleted(
|
||||
self.worker.processed, row)
|
||||
|
||||
def cannot_do_dialog(self):
|
||||
warning_dialog(self.gui, _('Not allowed'),
|
||||
_('You cannot use other libraries while using the environment'
|
||||
' variable CALIBRE_OVERRIDE_DATABASE_PATH.'), show=True)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
|||
Widget.__init__(self, parent,
|
||||
['dont_split_on_page_breaks', 'flow_size',
|
||||
'no_default_epub_cover', 'no_svg_cover',
|
||||
'preserve_cover_aspect_ratio',]
|
||||
'preserve_cover_aspect_ratio', 'epub_flatten']
|
||||
)
|
||||
for i in range(2):
|
||||
self.opt_no_svg_cover.toggle()
|
||||
|
|
|
|||
|
|
@ -81,6 +81,13 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="opt_epub_flatten">
|
||||
<property name="text">
|
||||
<string>&Flatten EPUB file structure</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
|
|
|||
|
|
@ -166,7 +166,9 @@ def do_connect(self, connected_devices, device_kind):
|
|||
report_progress=self.report_progress)
|
||||
dev.open()
|
||||
except OpenFeedback, e:
|
||||
self.open_feedback_msg(dev.get_gui_name(), e.feedback_msg)
|
||||
if dev not in self.ejected_devices:
|
||||
self.open_feedback_msg(dev.get_gui_name(), e.feedback_msg)
|
||||
self.ejected_devices.add(dev)
|
||||
continue
|
||||
except:
|
||||
tb = traceback.format_exc()
|
||||
|
|
|
|||
|
|
@ -399,14 +399,11 @@ Future conversion of these books will use the default settings.</string>
|
|||
<property name="title">
|
||||
<string>Change &cover</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="cover_no_change">
|
||||
<widget class="QRadioButton" name="cover_generate">
|
||||
<property name="text">
|
||||
<string>&No change</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
<string>&Generate default cover</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -417,13 +414,6 @@ Future conversion of these books will use the default settings.</string>
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="cover_generate">
|
||||
<property name="text">
|
||||
<string>&Generate default cover</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
|||
|
|
@ -105,7 +105,8 @@ def __init__(self, parent, modelcls=BooksModel):
|
|||
hv.setCursor(Qt.PointingHandCursor)
|
||||
self.selected_ids = []
|
||||
self._model.about_to_be_sorted.connect(self.about_to_be_sorted)
|
||||
self._model.sorting_done.connect(self.sorting_done)
|
||||
self._model.sorting_done.connect(self.sorting_done,
|
||||
type=Qt.QueuedConnection)
|
||||
|
||||
# Column Header Context Menu {{{
|
||||
def column_header_context_handler(self, action=None, column=None):
|
||||
|
|
@ -227,6 +228,7 @@ def sorting_done(self, indexc):
|
|||
sm = self.selectionModel()
|
||||
for idx in indices:
|
||||
sm.select(idx, sm.Select|sm.Rows)
|
||||
self.scroll_to_row(indices[0].row())
|
||||
self.selected_ids = []
|
||||
# }}}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ def update_font_display(self):
|
|||
name = unicode(fi.family())
|
||||
|
||||
self.font_display.setFont(font)
|
||||
self.font_display.setText(_('Current font:') + ' ' + name +
|
||||
self.font_display.setText(name +
|
||||
' [%dpt]'%fi.pointSize())
|
||||
|
||||
def change_font(self, *args):
|
||||
|
|
|
|||
|
|
@ -183,6 +183,34 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Interface font:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>font_display</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="font_display">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QPushButton" name="change_font_button">
|
||||
<property name="text">
|
||||
<string>Change &font (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
|
|
@ -196,20 +224,6 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLineEdit" name="font_display">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QPushButton" name="change_font_button">
|
||||
<property name="text">
|
||||
<string>Change &font (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
|
|
|||
|
|
@ -236,6 +236,10 @@ def fetch_scheduled_recipe(arg):
|
|||
recs.append(('header', True, OptionRecommendation.HIGH))
|
||||
recs.append(('header_format', '%t', OptionRecommendation.HIGH))
|
||||
|
||||
epub = load_defaults('epub_output')
|
||||
if epub.get('epub_flatten', False):
|
||||
recs.append(('epub_flatten', True, OptionRecommendation.HIGH))
|
||||
|
||||
args = [arg['recipe'], pt.name, recs]
|
||||
if arg['username'] is not None:
|
||||
recs.append(('username', arg['username'], OptionRecommendation.HIGH))
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ def __init__(self, opts, parent=None):
|
|||
acmap = OrderedDict()
|
||||
for action in interface_actions():
|
||||
ac = action.load_actual_plugin(self)
|
||||
ac.plugin_path = action.plugin_path
|
||||
if ac.name in acmap:
|
||||
if ac.priority >= acmap[ac.name].priority:
|
||||
acmap[ac.name] = ac
|
||||
|
|
|
|||
|
|
@ -171,6 +171,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_remember_current_page">
|
||||
<property name="text">
|
||||
<string>Remember the &current page when quitting</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="max_view_width">
|
||||
<property name="suffix">
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ def config(defaults=None):
|
|||
c.add_opt('hyphenate', default=False, help=_('Hyphenate text'))
|
||||
c.add_opt('hyphenate_default_lang', default='en',
|
||||
help=_('Default language for hyphenation rules'))
|
||||
c.add_opt('remember_current_page', default=True,
|
||||
help=_('Save the current position in the document, when quitting'))
|
||||
|
||||
fonts = c.add_group('FONTS', _('Font options'))
|
||||
fonts('serif_family', default='Times New Roman' if iswindows else 'Liberation Serif',
|
||||
|
|
@ -72,6 +74,7 @@ def __init__(self, shortcuts, parent=None):
|
|||
|
||||
opts = config().parse()
|
||||
self.opt_remember_window_size.setChecked(opts.remember_window_size)
|
||||
self.opt_remember_current_page.setChecked(opts.remember_current_page)
|
||||
self.serif_family.setCurrentFont(QFont(opts.serif_family))
|
||||
self.sans_family.setCurrentFont(QFont(opts.sans_family))
|
||||
self.mono_family.setCurrentFont(QFont(opts.mono_family))
|
||||
|
|
@ -118,6 +121,7 @@ def accept(self, *args):
|
|||
c.set('fit_images', self.opt_fit_images.isChecked())
|
||||
c.set('max_view_width', int(self.max_view_width.value()))
|
||||
c.set('hyphenate', self.hyphenate.isChecked())
|
||||
c.set('remember_current_page', self.opt_remember_current_page.isChecked())
|
||||
idx = self.hyphenate_default_lang.currentIndex()
|
||||
c.set('hyphenate_default_lang',
|
||||
str(self.hyphenate_default_lang.itemData(idx).toString()))
|
||||
|
|
|
|||
|
|
@ -328,6 +328,11 @@ def set_max_width(self):
|
|||
c = config().parse()
|
||||
self.frame.setMaximumWidth(c.max_view_width)
|
||||
|
||||
def get_remember_current_page_opt(self):
|
||||
from calibre.gui2.viewer.documentview import config
|
||||
c = config().parse()
|
||||
return c.remember_current_page
|
||||
|
||||
def print_book(self, preview):
|
||||
Printing(self.iterator.spine, preview)
|
||||
|
||||
|
|
@ -578,7 +583,8 @@ def set_bookmarks(self, bookmarks):
|
|||
current_page = None
|
||||
self.existing_bookmarks = []
|
||||
for bm in bookmarks:
|
||||
if bm[0] == 'calibre_current_page_bookmark':
|
||||
if bm[0] == 'calibre_current_page_bookmark' and \
|
||||
self.get_remember_current_page_opt():
|
||||
current_page = bm
|
||||
else:
|
||||
self.existing_bookmarks.append(bm[0])
|
||||
|
|
@ -598,6 +604,8 @@ def manage_bookmarks(self):
|
|||
self.set_bookmarks(bookmarks)
|
||||
|
||||
def save_current_position(self):
|
||||
if not self.get_remember_current_page_opt():
|
||||
return
|
||||
try:
|
||||
pos = self.view.bookmark()
|
||||
bookmark = '%d#%s'%(self.current_index, pos)
|
||||
|
|
|
|||
|
|
@ -1153,6 +1153,7 @@ def get_categories(self, sort='name', ids=None, icon_map=None):
|
|||
else:
|
||||
vals = book[dex].split(mult)
|
||||
for val in vals:
|
||||
if not val: continue
|
||||
try:
|
||||
(item_id, sort_val) = tids[cat][val] # let exceptions fly
|
||||
item = tcategories[cat].get(val, None)
|
||||
|
|
|
|||
|
|
@ -425,3 +425,7 @@ def has_cover(path):
|
|||
ids = [(x[0],) for x in data if has_cover(x[1])]
|
||||
self.conn.executemany('UPDATE books SET has_cover=1 WHERE id=?', ids)
|
||||
|
||||
def upgrade_version_15(self):
|
||||
'Remove commas from tags'
|
||||
self.conn.execute("UPDATE tags SET name=REPLACE(name, ',', ';')")
|
||||
|
||||
|
|
|
|||
|
|
@ -552,16 +552,18 @@ def browse_matches(self, category=None, cid=None, list_sort=None):
|
|||
ids = self.search_cache('search:"%s"'%which)
|
||||
except:
|
||||
raise cherrypy.HTTPError(404, 'Search: %r not understood'%which)
|
||||
elif category == 'newest':
|
||||
ids = self.search_cache('')
|
||||
all_ids = self.search_cache('')
|
||||
if category == 'newest':
|
||||
ids = all_ids
|
||||
hide_sort = 'true'
|
||||
elif category == 'allbooks':
|
||||
ids = self.search_cache('')
|
||||
ids = all_ids
|
||||
else:
|
||||
q = category
|
||||
if q == 'news':
|
||||
q = 'tags'
|
||||
ids = self.db.get_books_for_category(q, cid)
|
||||
ids = [x for x in ids if x in all_ids]
|
||||
|
||||
items = [self.db.data._data[x] for x in ids]
|
||||
if category == 'newest':
|
||||
|
|
|
|||
|
|
@ -72,3 +72,5 @@ Precautions
|
|||
--------------
|
||||
|
||||
Portable media can occasionally fail so you should make periodic backups of you Calibre library. This can be done by making a copy of the CalibreLibrary folder and all its contents. There are many freely available tools around that can optimise such back processes, well known ones being RoboCopy and RichCopy. However you can simply use a Windows copy facility if you cannot be bothered to use a specialised tools.
|
||||
|
||||
Using the environment variable CALIBRE_OVERRIDE_DATABASE_PATH disables multiple-library support in |app|. Avoid setting this variable in calibre-portable.bat unless you really need it.
|
||||
Loading…
Reference in a new issue