mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
569cfefe16
@ -80,6 +80,34 @@ class Plugin(object): # {{{
|
|||||||
'''
|
'''
|
||||||
pass
|
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):
|
def customization_help(self, gui=False):
|
||||||
'''
|
'''
|
||||||
Return a string giving help on how to customize this plugin.
|
Return a string giving help on how to customize this plugin.
|
||||||
|
@ -540,6 +540,7 @@ def choose_dir(window, name, title, default_dir='~'):
|
|||||||
parent=window, name=name, mode=QFileDialog.Directory,
|
parent=window, name=name, mode=QFileDialog.Directory,
|
||||||
default_dir=default_dir)
|
default_dir=default_dir)
|
||||||
dir = fd.get_files()
|
dir = fd.get_files()
|
||||||
|
fd.setParent(None)
|
||||||
if dir:
|
if dir:
|
||||||
return dir[0]
|
return dir[0]
|
||||||
|
|
||||||
@ -560,6 +561,7 @@ def choose_files(window, name, title,
|
|||||||
fd = FileDialog(title=title, name=name, filters=filters,
|
fd = FileDialog(title=title, name=name, filters=filters,
|
||||||
parent=window, add_all_files_filter=all_files, mode=mode,
|
parent=window, add_all_files_filter=all_files, mode=mode,
|
||||||
)
|
)
|
||||||
|
fd.setParent(None)
|
||||||
if fd.accepted:
|
if fd.accepted:
|
||||||
return fd.get_files()
|
return fd.get_files()
|
||||||
return None
|
return None
|
||||||
@ -570,6 +572,7 @@ def choose_images(window, name, title, select_only_single_file=True):
|
|||||||
filters=[('Images', ['png', 'gif', 'jpeg', 'jpg', 'svg'])],
|
filters=[('Images', ['png', 'gif', 'jpeg', 'jpg', 'svg'])],
|
||||||
parent=window, add_all_files_filter=False, mode=mode,
|
parent=window, add_all_files_filter=False, mode=mode,
|
||||||
)
|
)
|
||||||
|
fd.setParent(None)
|
||||||
if fd.accepted:
|
if fd.accepted:
|
||||||
return fd.get_files()
|
return fd.get_files()
|
||||||
return None
|
return None
|
||||||
|
@ -6,6 +6,7 @@ __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from zipfile import ZipFile
|
||||||
|
|
||||||
from PyQt4.Qt import QToolButton, QAction, QIcon, QObject
|
from PyQt4.Qt import QToolButton, QAction, QIcon, QObject
|
||||||
|
|
||||||
@ -108,6 +109,34 @@ class InterfaceAction(QObject):
|
|||||||
setattr(self, attr, action)
|
setattr(self, attr, action)
|
||||||
return 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):
|
def genesis(self):
|
||||||
'''
|
'''
|
||||||
Setup this plugin. Only called once during initialization. self.gui is
|
Setup this plugin. Only called once during initialization. self.gui is
|
||||||
|
@ -243,7 +243,9 @@ class AddAction(InterfaceAction):
|
|||||||
|
|
||||||
if hasattr(self._adder, 'cleanup'):
|
if hasattr(self._adder, 'cleanup'):
|
||||||
self._adder.cleanup()
|
self._adder.cleanup()
|
||||||
self._adder = None
|
self._adder.setParent(None)
|
||||||
|
del self._adder
|
||||||
|
self._adder = None
|
||||||
|
|
||||||
def _add_from_device_adder(self, paths=[], names=[], infos=[],
|
def _add_from_device_adder(self, paths=[], names=[], infos=[],
|
||||||
on_card=None, model=None):
|
on_card=None, model=None):
|
||||||
|
@ -160,15 +160,17 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
self.action_choose.triggered.connect(self.choose_library,
|
self.action_choose.triggered.connect(self.choose_library,
|
||||||
type=Qt.QueuedConnection)
|
type=Qt.QueuedConnection)
|
||||||
self.choose_menu = QMenu(self.gui)
|
self.choose_menu = QMenu(self.gui)
|
||||||
self.choose_menu.addAction(self.action_choose)
|
|
||||||
self.qaction.setMenu(self.choose_menu)
|
self.qaction.setMenu(self.choose_menu)
|
||||||
|
|
||||||
self.quick_menu = QMenu(_('Quick switch'))
|
if not os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
|
||||||
self.quick_menu_action = self.choose_menu.addMenu(self.quick_menu)
|
self.choose_menu.addAction(self.action_choose)
|
||||||
self.rename_menu = QMenu(_('Rename library'))
|
|
||||||
self.rename_menu_action = self.choose_menu.addMenu(self.rename_menu)
|
self.quick_menu = QMenu(_('Quick switch'))
|
||||||
self.delete_menu = QMenu(_('Delete library'))
|
self.quick_menu_action = self.choose_menu.addMenu(self.quick_menu)
|
||||||
self.delete_menu_action = self.choose_menu.addMenu(self.delete_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()
|
self.rename_separator = self.choose_menu.addSeparator()
|
||||||
|
|
||||||
@ -223,6 +225,8 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
self.library_changed(self.gui.library_view.model().db)
|
self.library_changed(self.gui.library_view.model().db)
|
||||||
|
|
||||||
def build_menus(self):
|
def build_menus(self):
|
||||||
|
if os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
|
||||||
|
return
|
||||||
db = self.gui.library_view.model().db
|
db = self.gui.library_view.model().db
|
||||||
locations = list(self.stats.locations(db))
|
locations = list(self.stats.locations(db))
|
||||||
for ac in self.switch_actions:
|
for ac in self.switch_actions:
|
||||||
@ -387,6 +391,11 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
c.exec_()
|
c.exec_()
|
||||||
|
|
||||||
def change_library_allowed(self):
|
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():
|
if self.gui.job_manager.has_jobs():
|
||||||
warning_dialog(self.gui, _('Not allowed'),
|
warning_dialog(self.gui, _('Not allowed'),
|
||||||
_('You cannot change libraries while jobs'
|
_('You cannot change libraries while jobs'
|
||||||
|
@ -12,7 +12,7 @@ from threading import Thread
|
|||||||
from PyQt4.Qt import QMenu, QToolButton
|
from PyQt4.Qt import QMenu, QToolButton
|
||||||
|
|
||||||
from calibre.gui2.actions import InterfaceAction
|
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.gui2.dialogs.progress import ProgressDialog
|
||||||
from calibre.utils.config import prefs, tweaks
|
from calibre.utils.config import prefs, tweaks
|
||||||
|
|
||||||
@ -106,6 +106,9 @@ class CopyToLibraryAction(InterfaceAction):
|
|||||||
|
|
||||||
def build_menus(self):
|
def build_menus(self):
|
||||||
self.menu.clear()
|
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
|
db = self.gui.library_view.model().db
|
||||||
locations = list(self.stats.locations(db))
|
locations = list(self.stats.locations(db))
|
||||||
for name, loc in locations:
|
for name, loc in locations:
|
||||||
@ -160,5 +163,9 @@ class CopyToLibraryAction(InterfaceAction):
|
|||||||
self.gui.iactions['Remove Books'].library_ids_deleted(
|
self.gui.iactions['Remove Books'].library_ids_deleted(
|
||||||
self.worker.processed, row)
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -368,6 +368,15 @@ class Adder(QObject): # {{{
|
|||||||
shutil.rmtree(self.worker.tdir)
|
shutil.rmtree(self.worker.tdir)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
self._parent = None
|
||||||
|
self.pd.setParent(None)
|
||||||
|
del self.pd
|
||||||
|
self.pd = None
|
||||||
|
if hasattr(self, 'db_adder'):
|
||||||
|
self.db_adder.setParent(None)
|
||||||
|
del self.db_adder
|
||||||
|
self.db_adder = None
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def number_of_books_added(self):
|
def number_of_books_added(self):
|
||||||
|
@ -166,7 +166,9 @@ class DeviceManager(Thread): # {{{
|
|||||||
report_progress=self.report_progress)
|
report_progress=self.report_progress)
|
||||||
dev.open()
|
dev.open()
|
||||||
except OpenFeedback, e:
|
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
|
continue
|
||||||
except:
|
except:
|
||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
|
@ -103,6 +103,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
acmap = OrderedDict()
|
acmap = OrderedDict()
|
||||||
for action in interface_actions():
|
for action in interface_actions():
|
||||||
ac = action.load_actual_plugin(self)
|
ac = action.load_actual_plugin(self)
|
||||||
|
ac.plugin_path = action.plugin_path
|
||||||
if ac.name in acmap:
|
if ac.name in acmap:
|
||||||
if ac.priority >= acmap[ac.name].priority:
|
if ac.priority >= acmap[ac.name].priority:
|
||||||
acmap[ac.name] = ac
|
acmap[ac.name] = ac
|
||||||
|
@ -171,6 +171,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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">
|
<item row="3" column="1">
|
||||||
<widget class="QSpinBox" name="max_view_width">
|
<widget class="QSpinBox" name="max_view_width">
|
||||||
<property name="suffix">
|
<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=False, help=_('Hyphenate text'))
|
||||||
c.add_opt('hyphenate_default_lang', default='en',
|
c.add_opt('hyphenate_default_lang', default='en',
|
||||||
help=_('Default language for hyphenation rules'))
|
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 = c.add_group('FONTS', _('Font options'))
|
||||||
fonts('serif_family', default='Times New Roman' if iswindows else 'Liberation Serif',
|
fonts('serif_family', default='Times New Roman' if iswindows else 'Liberation Serif',
|
||||||
@ -72,6 +74,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
opts = config().parse()
|
opts = config().parse()
|
||||||
self.opt_remember_window_size.setChecked(opts.remember_window_size)
|
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.serif_family.setCurrentFont(QFont(opts.serif_family))
|
||||||
self.sans_family.setCurrentFont(QFont(opts.sans_family))
|
self.sans_family.setCurrentFont(QFont(opts.sans_family))
|
||||||
self.mono_family.setCurrentFont(QFont(opts.mono_family))
|
self.mono_family.setCurrentFont(QFont(opts.mono_family))
|
||||||
@ -118,6 +121,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
c.set('fit_images', self.opt_fit_images.isChecked())
|
c.set('fit_images', self.opt_fit_images.isChecked())
|
||||||
c.set('max_view_width', int(self.max_view_width.value()))
|
c.set('max_view_width', int(self.max_view_width.value()))
|
||||||
c.set('hyphenate', self.hyphenate.isChecked())
|
c.set('hyphenate', self.hyphenate.isChecked())
|
||||||
|
c.set('remember_current_page', self.opt_remember_current_page.isChecked())
|
||||||
idx = self.hyphenate_default_lang.currentIndex()
|
idx = self.hyphenate_default_lang.currentIndex()
|
||||||
c.set('hyphenate_default_lang',
|
c.set('hyphenate_default_lang',
|
||||||
str(self.hyphenate_default_lang.itemData(idx).toString()))
|
str(self.hyphenate_default_lang.itemData(idx).toString()))
|
||||||
|
@ -328,6 +328,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
c = config().parse()
|
c = config().parse()
|
||||||
self.frame.setMaximumWidth(c.max_view_width)
|
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):
|
def print_book(self, preview):
|
||||||
Printing(self.iterator.spine, preview)
|
Printing(self.iterator.spine, preview)
|
||||||
|
|
||||||
@ -578,7 +583,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
current_page = None
|
current_page = None
|
||||||
self.existing_bookmarks = []
|
self.existing_bookmarks = []
|
||||||
for bm in 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
|
current_page = bm
|
||||||
else:
|
else:
|
||||||
self.existing_bookmarks.append(bm[0])
|
self.existing_bookmarks.append(bm[0])
|
||||||
@ -598,6 +604,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
self.set_bookmarks(bookmarks)
|
self.set_bookmarks(bookmarks)
|
||||||
|
|
||||||
def save_current_position(self):
|
def save_current_position(self):
|
||||||
|
if not self.get_remember_current_page_opt():
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
pos = self.view.bookmark()
|
pos = self.view.bookmark()
|
||||||
bookmark = '%d#%s'%(self.current_index, pos)
|
bookmark = '%d#%s'%(self.current_index, pos)
|
||||||
|
@ -425,3 +425,7 @@ class SchemaUpgrade(object):
|
|||||||
ids = [(x[0],) for x in data if has_cover(x[1])]
|
ids = [(x[0],) for x in data if has_cover(x[1])]
|
||||||
self.conn.executemany('UPDATE books SET has_cover=1 WHERE id=?', ids)
|
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, ',', ';')")
|
||||||
|
|
||||||
|
@ -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.
|
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…
x
Reference in New Issue
Block a user