diff --git a/src/calibre/ebooks/oeb/polish/container.py b/src/calibre/ebooks/oeb/polish/container.py index 163759431e..9a58f0ef6b 100644 --- a/src/calibre/ebooks/oeb/polish/container.py +++ b/src/calibre/ebooks/oeb/polish/container.py @@ -985,9 +985,13 @@ class EpubContainer(Container): f.write(guess_type('a.epub')) zip_rebuilder(self.root, outpath) - @property + @dynamic_property def path_to_ebook(self): - return self.pathtoepub + def fget(self): + return self.pathtoepub + def fset(self, val): + self.pathtoepub = val + return property(fget=fget, fset=fset) # }}} @@ -1097,9 +1101,13 @@ class AZW3Container(Container): outpath = self.pathtoazw3 opf_to_azw3(self.name_path_map[self.opf_name], outpath, self.log) - @property + @dynamic_property def path_to_ebook(self): - return self.pathtoazw3 + def fget(self): + return self.pathtoazw3 + def fset(self, val): + self.pathtoazw3 = val + return property(fget=fget, fset=fset) @property def names_that_must_not_be_changed(self): diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 3b7ddc309d..5e3989f8af 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -722,7 +722,7 @@ def choose_files(window, name, title, return fd.get_files() return None -def choose_save_file(window, name, title, filters=[], all_files=True): +def choose_save_file(window, name, title, filters=[], all_files=True, initial_dir=None): ''' Ask user to choose a file to save to. Can be a non-existent file. :param filters: list of allowable extensions. Each element of the list @@ -731,9 +731,12 @@ def choose_save_file(window, name, title, filters=[], all_files=True): of extensions. :param all_files: If True add All files to filters. ''' - mode = QFileDialog.AnyFile - fd = FileDialog(title=title, name=name, filters=filters, - parent=window, add_all_files_filter=all_files, mode=mode) + kwargs = dict(title=title, name=name, filters=filters, + parent=window, add_all_files_filter=all_files, mode=QFileDialog.AnyFile) + if initial_dir is not None: + kwargs['no_save_dir'] = True + kwargs['default_dir'] = initial_dir + fd = FileDialog(**kwargs) fd.setParent(None) ans = None if fd.accepted: diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 2a90573db4..92b224b43b 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -31,7 +31,7 @@ from calibre.gui2.dialogs.message_box import MessageBox from calibre.gui2.tweak_book import set_current_container, current_container, tprefs, actions, editors from calibre.gui2.tweak_book.undo import GlobalUndoHistory from calibre.gui2.tweak_book.file_list import NewFileDialog -from calibre.gui2.tweak_book.save import SaveManager, save_container +from calibre.gui2.tweak_book.save import SaveManager, save_container, find_first_existing_ancestor from calibre.gui2.tweak_book.preview import parse_worker, font_cache from calibre.gui2.tweak_book.toc import TOCEditor from calibre.gui2.tweak_book.editor import editor_from_syntax, syntax_from_mime @@ -829,6 +829,22 @@ class Boss(QObject): if ed.is_modified or not ed.is_synced_to_container: self.commit_editor_to_container(name, c) ed.is_modified = False + destdir = os.path.dirname(c.path_to_ebook) + if not os.path.exists(destdir): + info_dialog(self.gui, _('Path does not exist'), _( + 'The file you are editing (%s) no longer exists. You have to choose a new save location.') % c.path_to_ebook, + show_copy_button=False, show=True) + fmt = c.path_to_ebook.rpartition('.')[-1].lower() + start_dir = find_first_existing_ancestor(c.path_to_ebook) + path = choose_save_file(self.gui, 'choose-new-save-location', _('Choose file location'), initial_dir=start_dir, + filters=[(fmt.upper(), (fmt,))], all_files=False) + if path is not None: + if not path.lower().endswith('.' + fmt): + path = path + '.' + fmt + c.path_to_ebook = path + self.global_undo.update_path_to_ebook(path) + else: + return self.gui.action_save.setEnabled(False) tdir = self.mkdtemp(prefix='save-') container = clone_container(c, tdir) @@ -868,7 +884,8 @@ class Boss(QObject): return error_dialog(self.gui, _('Could not save'), _('Saving of the book failed. Click "Show Details"' - ' for more information.'), det_msg=tb, show=True) + ' for more information. You can try to save a copy' + ' to a different location, via File->Save a Copy'), det_msg=tb, show=True) def go_to_line_number(self): ed = self.gui.central.current_editor diff --git a/src/calibre/gui2/tweak_book/save.py b/src/calibre/gui2/tweak_book/save.py index 34f070c388..0627c27241 100644 --- a/src/calibre/gui2/tweak_book/save.py +++ b/src/calibre/gui2/tweak_book/save.py @@ -40,6 +40,14 @@ def send_message(msg=''): t.conn.send('bookedited:'+msg) t.conn.close() +def find_first_existing_ancestor(path): + while path and not os.path.exists(path): + npath = os.path.dirname(path) + if npath == path: + break + path = npath + return path + class SaveWidget(QWidget): def __init__(self, parent=None): diff --git a/src/calibre/gui2/tweak_book/undo.py b/src/calibre/gui2/tweak_book/undo.py index f67fedc735..5a81aa0e57 100644 --- a/src/calibre/gui2/tweak_book/undo.py +++ b/src/calibre/gui2/tweak_book/undo.py @@ -167,6 +167,10 @@ class GlobalUndoHistory(QAbstractListModel): return '' return self.states[self.pos + 1].message or _('[Unnamed state]') + def update_path_to_ebook(self, path): + for state in self.states: + state.container.path_to_ebook = path + class SpacedDelegate(QStyledItemDelegate): def sizeHint(self, *args):