mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement replacing of files
This commit is contained in:
parent
f885896c31
commit
7e4da66e09
@ -7,9 +7,10 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import codecs
|
import codecs, shutil
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
from calibre import sanitize_file_name_unicode
|
||||||
from calibre.ebooks.chardet import strip_encoding_declarations
|
from calibre.ebooks.chardet import strip_encoding_declarations
|
||||||
|
|
||||||
class LinkReplacer(object):
|
class LinkReplacer(object):
|
||||||
@ -122,3 +123,25 @@ def rename_files(container, file_map):
|
|||||||
link_map[current_name] = new_name
|
link_map[current_name] = new_name
|
||||||
replace_links(container, link_map, replace_in_opf=True)
|
replace_links(container, link_map, replace_in_opf=True)
|
||||||
|
|
||||||
|
def replace_file(container, name, path, basename, force_mt=None):
|
||||||
|
dirname, base = name.rpartition('/')[0::2]
|
||||||
|
nname = sanitize_file_name_unicode(basename)
|
||||||
|
if dirname:
|
||||||
|
nname = dirname + '/' + nname
|
||||||
|
with open(path, 'rb') as src:
|
||||||
|
if name != nname:
|
||||||
|
count = 0
|
||||||
|
b, e = nname.rpartition('.')[0::2]
|
||||||
|
while container.exists(nname):
|
||||||
|
count += 1
|
||||||
|
nname = b + ('_%d.%s' % (count, e))
|
||||||
|
rename_files(container, {name:nname})
|
||||||
|
mt = force_mt or container.guess_type(nname)
|
||||||
|
for itemid, q in container.manifest_id_map.iteritems():
|
||||||
|
if q == nname:
|
||||||
|
for item in container.opf_xpath('//opf:manifest/opf:item[@href and @id="%s"]' % itemid):
|
||||||
|
item.set('media-type', mt)
|
||||||
|
container.dirty(container.opf_name)
|
||||||
|
with container.open(nname, 'wb') as dest:
|
||||||
|
shutil.copyfileobj(src, dest)
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ from calibre.ebooks.oeb.polish.main import SUPPORTED, tweak_polish
|
|||||||
from calibre.ebooks.oeb.polish.container import get_container as _gc, clone_container, guess_type
|
from calibre.ebooks.oeb.polish.container import get_container as _gc, clone_container, guess_type
|
||||||
from calibre.ebooks.oeb.polish.cover import mark_as_cover, mark_as_titlepage
|
from calibre.ebooks.oeb.polish.cover import mark_as_cover, mark_as_titlepage
|
||||||
from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all
|
from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all
|
||||||
from calibre.ebooks.oeb.polish.replace import rename_files
|
from calibre.ebooks.oeb.polish.replace import rename_files, replace_file
|
||||||
from calibre.ebooks.oeb.polish.split import split, merge, AbortError
|
from calibre.ebooks.oeb.polish.split import split, merge, AbortError
|
||||||
from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog, choose_save_file
|
from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog, choose_save_file
|
||||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||||
@ -75,6 +75,7 @@ class Boss(QObject):
|
|||||||
fl.merge_requested.connect(self.merge_requested)
|
fl.merge_requested.connect(self.merge_requested)
|
||||||
fl.mark_requested.connect(self.mark_requested)
|
fl.mark_requested.connect(self.mark_requested)
|
||||||
fl.export_requested.connect(self.export_requested)
|
fl.export_requested.connect(self.export_requested)
|
||||||
|
fl.replace_requested.connect(self.replace_requested)
|
||||||
self.gui.central.current_editor_changed.connect(self.apply_current_editor_state)
|
self.gui.central.current_editor_changed.connect(self.apply_current_editor_state)
|
||||||
self.gui.central.close_requested.connect(self.editor_close_requested)
|
self.gui.central.close_requested.connect(self.editor_close_requested)
|
||||||
self.gui.central.search_panel.search_triggered.connect(self.search)
|
self.gui.central.search_panel.search_triggered.connect(self.search)
|
||||||
@ -682,6 +683,13 @@ class Boss(QObject):
|
|||||||
with current_container().open(name, 'rb') as src, open(path, 'wb') as dest:
|
with current_container().open(name, 'rb') as src, open(path, 'wb') as dest:
|
||||||
shutil.copyfileobj(src, dest)
|
shutil.copyfileobj(src, dest)
|
||||||
|
|
||||||
|
@in_thread_job
|
||||||
|
def replace_requested(self, name, path, basename, force_mt):
|
||||||
|
self.commit_all_editors_to_container()
|
||||||
|
self.add_savepoint(_('Replace %s') % name)
|
||||||
|
replace_file(current_container(), name, path, basename, force_mt)
|
||||||
|
self.apply_container_update_to_gui()
|
||||||
|
|
||||||
def sync_editor_to_preview(self, name, lnum):
|
def sync_editor_to_preview(self, name, lnum):
|
||||||
editor = self.edit_file(name, 'html')
|
editor = self.edit_file(name, 'html')
|
||||||
self.ignore_preview_to_editor_sync = True
|
self.ignore_preview_to_editor_sync = True
|
||||||
|
@ -86,6 +86,7 @@ class FileList(QTreeWidget):
|
|||||||
merge_requested = pyqtSignal(object, object, object)
|
merge_requested = pyqtSignal(object, object, object)
|
||||||
mark_requested = pyqtSignal(object, object)
|
mark_requested = pyqtSignal(object, object)
|
||||||
export_requested = pyqtSignal(object, object)
|
export_requested = pyqtSignal(object, object)
|
||||||
|
replace_requested = pyqtSignal(object, object, object, object)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QTreeWidget.__init__(self, parent)
|
QTreeWidget.__init__(self, parent)
|
||||||
@ -304,6 +305,7 @@ class FileList(QTreeWidget):
|
|||||||
m = QMenu(self)
|
m = QMenu(self)
|
||||||
sel = self.selectedItems()
|
sel = self.selectedItems()
|
||||||
num = len(sel)
|
num = len(sel)
|
||||||
|
container = current_container()
|
||||||
if num > 0:
|
if num > 0:
|
||||||
m.addAction(QIcon(I('trash.png')), _('&Delete selected files'), self.request_delete)
|
m.addAction(QIcon(I('trash.png')), _('&Delete selected files'), self.request_delete)
|
||||||
m.addSeparator()
|
m.addSeparator()
|
||||||
@ -312,13 +314,16 @@ class FileList(QTreeWidget):
|
|||||||
cn = unicode(ci.data(0, NAME_ROLE).toString())
|
cn = unicode(ci.data(0, NAME_ROLE).toString())
|
||||||
mt = unicode(ci.data(0, MIME_ROLE).toString())
|
mt = unicode(ci.data(0, MIME_ROLE).toString())
|
||||||
cat = unicode(ci.data(0, CATEGORY_ROLE).toString())
|
cat = unicode(ci.data(0, CATEGORY_ROLE).toString())
|
||||||
m.addAction(QIcon(I('modified.png')), _('&Rename %s') % (elided_text(cn)), self.edit_current_item)
|
n = elided_text(cn.rpartition('/')[-1])
|
||||||
|
m.addAction(QIcon(I('modified.png')), _('&Rename %s') % n, self.edit_current_item)
|
||||||
if is_raster_image(mt):
|
if is_raster_image(mt):
|
||||||
m.addAction(QIcon(I('default_cover.png')), _('Mark %s as cover image') % elided_text(cn), partial(self.mark_as_cover, cn))
|
m.addAction(QIcon(I('default_cover.png')), _('Mark %s as cover image') % n, partial(self.mark_as_cover, cn))
|
||||||
elif current_container().SUPPORTS_TITLEPAGES and mt in OEB_DOCS and cat == 'text':
|
elif current_container().SUPPORTS_TITLEPAGES and mt in OEB_DOCS and cat == 'text':
|
||||||
m.addAction(QIcon(I('default_cover.png')), _('Mark %s as title/cover page') % elided_text(cn), partial(self.mark_as_titlepage, cn))
|
m.addAction(QIcon(I('default_cover.png')), _('Mark %s as cover page') % n, partial(self.mark_as_titlepage, cn))
|
||||||
m.addSeparator()
|
m.addSeparator()
|
||||||
m.addAction(QIcon(I('save.png')), _('Export %s') % elided_text(cn), partial(self.export, cn))
|
m.addAction(QIcon(I('save.png')), _('Export %s') % n, partial(self.export, cn))
|
||||||
|
if cn not in container.names_that_must_not_be_changed and cn not in container.names_that_must_not_be_removed and mt not in OEB_FONTS:
|
||||||
|
m.addAction(_('Replace %s with file...') % n, partial(self.replace, cn))
|
||||||
m.addSeparator()
|
m.addSeparator()
|
||||||
|
|
||||||
selected_map = defaultdict(list)
|
selected_map = defaultdict(list)
|
||||||
@ -453,6 +458,32 @@ class FileList(QTreeWidget):
|
|||||||
if path:
|
if path:
|
||||||
self.export_requested.emit(name, path)
|
self.export_requested.emit(name, path)
|
||||||
|
|
||||||
|
def replace(self, name):
|
||||||
|
c = current_container()
|
||||||
|
mt = c.mime_map[name]
|
||||||
|
oext = name.rpartition('.')[-1].lower()
|
||||||
|
filters = [oext]
|
||||||
|
fname = _('Files')
|
||||||
|
if mt in OEB_DOCS:
|
||||||
|
fname = _('HTML Files')
|
||||||
|
filters = 'html htm xhtm xhtml shtml'.split()
|
||||||
|
elif is_raster_image(mt):
|
||||||
|
fname = _('Images')
|
||||||
|
filters = 'jpeg jpg gif png'.split()
|
||||||
|
path = choose_files(self, 'tweak_book_import_file', _('Choose file'), filters=[(fname, filters)], select_only_single_file=True)
|
||||||
|
if not path:
|
||||||
|
return
|
||||||
|
path = path[0]
|
||||||
|
ext = path.rpartition('.')[-1].lower()
|
||||||
|
force_mt = None
|
||||||
|
if mt in OEB_DOCS:
|
||||||
|
force_mt = c.guess_type('a.html')
|
||||||
|
nname = os.path.basename(path)
|
||||||
|
nname, ext = nname.rpartition('.')[0::2]
|
||||||
|
nname = nname + '.' + ext.lower()
|
||||||
|
self.replace_requested.emit(name, path, nname, force_mt)
|
||||||
|
|
||||||
|
|
||||||
class NewFileDialog(QDialog): # {{{
|
class NewFileDialog(QDialog): # {{{
|
||||||
|
|
||||||
def __init__(self, initial_choice='html', parent=None):
|
def __init__(self, initial_choice='html', parent=None):
|
||||||
@ -578,6 +609,7 @@ class FileListWidget(QWidget):
|
|||||||
merge_requested = pyqtSignal(object, object, object)
|
merge_requested = pyqtSignal(object, object, object)
|
||||||
mark_requested = pyqtSignal(object, object)
|
mark_requested = pyqtSignal(object, object)
|
||||||
export_requested = pyqtSignal(object, object)
|
export_requested = pyqtSignal(object, object)
|
||||||
|
replace_requested = pyqtSignal(object, object, object, object)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
@ -585,7 +617,9 @@ class FileListWidget(QWidget):
|
|||||||
self.file_list = FileList(self)
|
self.file_list = FileList(self)
|
||||||
self.layout().addWidget(self.file_list)
|
self.layout().addWidget(self.file_list)
|
||||||
self.layout().setContentsMargins(0, 0, 0, 0)
|
self.layout().setContentsMargins(0, 0, 0, 0)
|
||||||
for x in ('delete_requested', 'reorder_spine', 'rename_requested', 'edit_file', 'merge_requested', 'mark_requested', 'export_requested'):
|
for x in ('delete_requested', 'reorder_spine', 'rename_requested',
|
||||||
|
'edit_file', 'merge_requested', 'mark_requested',
|
||||||
|
'export_requested', 'replace_requested'):
|
||||||
getattr(self.file_list, x).connect(getattr(self, x))
|
getattr(self.file_list, x).connect(getattr(self, x))
|
||||||
for x in ('delete_done', 'select_name'):
|
for x in ('delete_done', 'select_name'):
|
||||||
setattr(self, x, getattr(self.file_list, x))
|
setattr(self, x, getattr(self.file_list, x))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user