From cf8443950265ae598cda9171f82a717cf25a2ca8 Mon Sep 17 00:00:00 2001 From: Bo Peng Date: Sat, 8 Sep 2007 04:10:43 +0000 Subject: [PATCH] Embedding: use a vector to store multiple ParConstIterator of multiple insets referring to the same embedded file, polish the internal logics of EmbeddedFile(s) git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@20137 a592a061-630c-0410-9148-cb99ea01b6c8 --- src/EmbeddedFiles.cpp | 187 +++++++++++------- src/EmbeddedFiles.h | 42 ++-- .../controllers/ControlEmbeddedFiles.cpp | 23 ++- .../controllers/ControlEmbeddedFiles.h | 2 +- src/frontends/qt4/GuiEmbeddedFiles.cpp | 52 +++-- src/frontends/qt4/GuiEmbeddedFiles.h | 3 +- 6 files changed, 198 insertions(+), 111 deletions(-) diff --git a/src/EmbeddedFiles.cpp b/src/EmbeddedFiles.cpp index e67d151141..bad37e7789 100644 --- a/src/EmbeddedFiles.cpp +++ b/src/EmbeddedFiles.cpp @@ -69,8 +69,11 @@ using support::makedir; EmbeddedFile::EmbeddedFile(string const & file, string const & inzip_name, bool embed, ParConstIterator const & pit) : DocFileName(file, true), inzip_name_(inzip_name), embedded_(embed), - valid_(true), par_it_(pit) -{} + valid_(true), par_it_() +{ + if (pit != ParConstIterator()) + par_it_.push_back(pit); +} string EmbeddedFile::embeddedFile(Buffer const * buf) const @@ -79,98 +82,119 @@ string EmbeddedFile::embeddedFile(Buffer const * buf) const } -int const EmbeddedFile::parID() const +void EmbeddedFile::addParIter(ParConstIterator const & pit) { - // some embedded file do not have a valid par iterator - return par_it_ == ParConstIterator() ? 0 : par_it_->id(); + par_it_.push_back(pit); } -void EmbeddedFile::setParIter(ParConstIterator const & pit) +int EmbeddedFile::parID(int idx) const { - par_it_ = pit; + BOOST_ASSERT(idx < refCount()); + // some embedded file do not have a valid par iterator + return par_it_[idx]->id(); } string EmbeddedFile::availableFile(Buffer const * buf) const { - if (embedded_) + if (embedded()) return embeddedFile(buf); else return absFilename(); } +void EmbeddedFile::invalidate() +{ + // Clear par_it_ because they will be registered again. + par_it_.clear(); + valid_ = false; +} + + bool EmbeddedFile::extract(Buffer const * buf) const { - if (!embedded_) - return true; string ext_file = absFilename(); string emb_file = embeddedFile(buf); - bool copyFile = false; - // both files exist, are different - if (fs::exists(ext_file) && fs::exists(emb_file) - && sum(*this) != sum(FileName(emb_file))) { - int const ret = Alert::prompt( + + if (!fs::exists(emb_file)) + return false; + + // if external file already exists ... + if (fs::exists(ext_file)) { + // no need to copy if the files are the same + if (sum(*this) == sum(FileName(emb_file))) + return true; + // otherwise, ask if overwrite + int ret = Alert::prompt( _("Overwrite external file?"), bformat(_("External file %1$s already exists, do you want to overwrite it"), from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel")); - copyFile = ret == 0; - } - // copy file in the previous case, and a new case - if (copyFile || (!fs::exists(ext_file) && fs::exists(emb_file))) { - try { - // need to make directory? - string path = onlyPath(ext_file); - if (!fs::is_directory(path)) - makedir(const_cast(path.c_str()), 0755); - fs::copy_file(emb_file, ext_file, false); + if (ret != 0) + // if the user does not want to overwrite, we still consider it + // a successful operation. return true; - } catch (fs::filesystem_error const & fe) { - Alert::error(_("Copy file failure"), - bformat(_("Cannot copy file %1$s to %2$s.\n" - "Please check whether the directory exists and is writeable."), - from_utf8(emb_file), from_utf8(ext_file))); - LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl; - } + } + // copy file + try { + // need to make directory? + string path = onlyPath(ext_file); + if (!fs::is_directory(path)) + makedir(const_cast(path.c_str()), 0755); + fs::copy_file(emb_file, ext_file, false); + return true; + } catch (fs::filesystem_error const & fe) { + Alert::error(_("Copy file failure"), + bformat(_("Cannot copy file %1$s to %2$s.\n" + "Please check whether the directory exists and is writeable."), + from_utf8(emb_file), from_utf8(ext_file))); + LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl; } return false; } -bool EmbeddedFile::update(Buffer const * buf) const +bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const { string ext_file = absFilename(); string emb_file = embeddedFile(buf); - bool copyFile = false; - // both files exist, are different - if (fs::exists(ext_file) && fs::exists(emb_file) - && sum(*this) != sum(FileName(emb_file))) { + + if (!fs::exists(ext_file)) + return false; + + // if embedded file already exists ... + if (fs::exists(emb_file)) { + // no need to copy if the files are the same + if (sum(*this) == sum(FileName(emb_file))) + return true; + // other wise, ask if overwrite int const ret = Alert::prompt( _("Update embedded file?"), bformat(_("Embeddedl file %1$s already exists, do you want to overwrite it"), from_utf8(ext_file)), 1, 1, _("&Overwrite"), _("&Cancel")); - copyFile = ret == 0; + if (ret != 0) + // if the user does not want to overwrite, we still consider it + // a successful operation. + return true; } - // copy file in the previous case, and a new case - if (copyFile || (fs::exists(ext_file) && !fs::exists(emb_file))) { - try { - // need to make directory? - string path = onlyPath(emb_file); - if (!fs::is_directory(path)) - makedir(const_cast(path.c_str()), 0755); - fs::copy_file(ext_file, emb_file, false); - } catch (fs::filesystem_error const & fe) { - Alert::error(_("Copy file failure"), - bformat(_("Cannot copy file %1$s to %2$s.\n" - "Please check whether the directory exists and is writeable."), - from_utf8(ext_file), from_utf8(emb_file))); - LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl; - return false; - } + // copy file + try { + // need to make directory? + string path = onlyPath(emb_file); + if (!fs::is_directory(path)) + makedir(const_cast(path.c_str()), 0755); + fs::copy_file(ext_file, emb_file, false); + return true; + } catch (fs::filesystem_error const & fe) { + Alert::error(_("Copy file failure"), + bformat(_("Cannot copy file %1$s to %2$s.\n" + "Please check whether the directory exists and is writeable."), + from_utf8(ext_file), from_utf8(emb_file))); + LYXERR(Debug::DEBUG) << "Fs error: " << fe.what() << endl; } - return true; + return false; } @@ -180,15 +204,16 @@ bool EmbeddedFiles::enabled() const } -void EmbeddedFiles::enable(bool flag) +bool EmbeddedFiles::enable(bool flag) { if (enabled() != flag) { - // if disable embedding, first extract all embedded files - if (flag || (!flag && extractAll())) { - // file will be changed - buffer_->markDirty(); - buffer_->params().embedded = flag; - } + // if enable, copy all files to temppath() + // if disable, extract all files + if ((flag && !updateFromExternalFile()) || (!flag && !extract())) + return false; + // if operation is successful + buffer_->markDirty(); + buffer_->params().embedded = flag; } } @@ -196,6 +221,7 @@ void EmbeddedFiles::enable(bool flag) void EmbeddedFiles::registerFile(string const & filename, bool embed, ParConstIterator const & pit) { + // filename can be relative or absolute, translate to absolute filename string abs_filename = makeAbsPath(filename, buffer_->filePath()).absFilename(); // try to find this file from the list EmbeddedFileList::iterator it = file_list_.begin(); @@ -203,16 +229,21 @@ void EmbeddedFiles::registerFile(string const & filename, for (; it != it_end; ++it) if (it->absFilename() == abs_filename || it->embeddedFile(buffer_) == abs_filename) break; - // find this filename + // find this filename, keep the original embedding status if (it != file_list_.end()) { - it->setParIter(pit); - it->update(buffer_); - it->setEmbed(true); + it->addParIter(pit); + // if the file is embedded, the embedded file should have exist + // check for this to ensure that our logic is correct + if (it->embedded()) + BOOST_ASSERT(fs::exists(it->embeddedFile(buffer_))); it->validate(); return; } + // try to be more careful file_list_.push_back(EmbeddedFile(abs_filename, getInzipName(abs_filename), embed, pit)); + // validate if things are OK + BOOST_ASSERT(fs::exists(file_list_.back().availableFile(buffer_))); } @@ -225,7 +256,9 @@ void EmbeddedFiles::update() EmbeddedFileList::iterator it = file_list_.begin(); EmbeddedFileList::iterator it_end = file_list_.end(); for (; it != it_end; ++it) - it->invalidate(); + // we do not update items that are manually inserted + if (it->refCount() > 0) + it->invalidate(); ParIterator pit = buffer_->par_iterator_begin(); ParIterator pit_end = buffer_->par_iterator_end(); @@ -306,16 +339,26 @@ EmbeddedFiles::EmbeddedFileList::const_iterator EmbeddedFiles::find(std::string } -bool EmbeddedFiles::extractAll() const +bool EmbeddedFiles::extract() const { EmbeddedFileList::const_iterator it = file_list_.begin(); EmbeddedFileList::const_iterator it_end = file_list_.end(); - // FIXME: the logic here is hard to decide, we should allow cancel for - // 'do not overwrite' this file, and cancel for 'cancel extract all files'. - // I am not sure how to do this now. for (; it != it_end; ++it) if (it->valid() && it->embedded()) - it->extract(buffer_); + if(!it->extract(buffer_)) + return false; + return true; +} + + +bool EmbeddedFiles::updateFromExternalFile() const +{ + EmbeddedFileList::const_iterator it = file_list_.begin(); + EmbeddedFileList::const_iterator it_end = file_list_.end(); + for (; it != it_end; ++it) + if (it->valid() && it->embedded()) + if (!it->updateFromExternalFile(buffer_)) + return false; return true; } diff --git a/src/EmbeddedFiles.h b/src/EmbeddedFiles.h index de050c5622..4d4ac014f1 100644 --- a/src/EmbeddedFiles.h +++ b/src/EmbeddedFiles.h @@ -122,13 +122,18 @@ public: std::string availableFile(Buffer const * buf) const; /// paragraph id - void setParIter(ParConstIterator const & pit); - int const parID() const; + void addParIter(ParConstIterator const & pit); + int parID(int idx) const; + /// Number of Insets this file item is referred + /// If refCount() == 0, this file must be manually inserted. + /// This fact is used by the update() function to skip updating + /// such items. + int refCount() const { return par_it_.size(); } /// embedding status of this file bool embedded() const { return embedded_; } - /// set embedding status. update() should be called before this - /// to sync the embedded file with external one. + /// set embedding status. updateFromExternal() should be called before this + /// to copy or sync the embedded file with external one. bool setEmbed(bool embed) { embedded_ = embed; } // A flag indicating whether or not this filename is valid. @@ -139,12 +144,12 @@ public: // status setting untouched. bool valid() const { return valid_; } void validate() { valid_ = true; } - void invalidate() { valid_ = false; } + void invalidate(); /// extract file, does not change embedding status bool extract(Buffer const * buf) const; /// update embedded file from external file, does not change embedding status - bool update(Buffer const * buf) const; + bool updateFromExternalFile(Buffer const * buf) const; private: /// filename in zip file @@ -153,10 +158,9 @@ private: bool embedded_; /// bool valid_; - /// Current position of the item, used to locate the files - /// A figure may be referred by several items. In this case - /// only the last location is recorded. - ParConstIterator par_it_; + /// Current position of the item, used to locate the files. Because one + /// file item can be referred by several Insets, a vector is used. + std::vector par_it_; }; @@ -171,10 +175,16 @@ public: /// return buffer params embedded flag bool enabled() const; - /// set buffer params embedded flag - void enable(bool flag); + /// set buffer params embedded flag. Files will be updated or extracted + /// if such an operation fails, enable will fail. + bool enable(bool flag); - /// add a file item + /// add a file item. + /* \param filename filename to add + * \param embed embedding status. For a new file item, this is always true. + * If the file already exists, this parameter is ignored. + * \param pit paragraph id. + */ void registerFile(std::string const & filename, bool embed = false, ParConstIterator const & pit = ParConstIterator()); @@ -196,8 +206,10 @@ public: EmbeddedFileList::const_iterator end() const { return file_list_.end(); } // try to locate filename, using either absFilename() or embeddedFile() EmbeddedFileList::const_iterator find(std::string filename) const; - /// - bool extractAll() const; + /// extract all file items, used when disable embedding + bool extract() const; + /// update all files from external, used when enable embedding + bool updateFromExternalFile() const; /// friend std::istream & operator>> (std::istream & is, EmbeddedFiles &); diff --git a/src/frontends/controllers/ControlEmbeddedFiles.cpp b/src/frontends/controllers/ControlEmbeddedFiles.cpp index 1ea646cf2a..707a4609c6 100644 --- a/src/frontends/controllers/ControlEmbeddedFiles.cpp +++ b/src/frontends/controllers/ControlEmbeddedFiles.cpp @@ -67,13 +67,11 @@ void ControlEmbeddedFiles::dispatchMessage(string const & msg) } -void ControlEmbeddedFiles::goTo(EmbeddedFile const & item) +void ControlEmbeddedFiles::goTo(EmbeddedFile const & item, int idx) { - int id = item.parID(); - if (id != 0) { - string const tmp = convert(item.parID()); - kernel().lyxview().dispatch(FuncRequest(LFUN_PARAGRAPH_GOTO, tmp)); - } + BOOST_ASSERT(idx < item.refCount()); + string const tmp = convert(item.parID(idx)); + kernel().lyxview().dispatch(FuncRequest(LFUN_PARAGRAPH_GOTO, tmp)); } @@ -85,8 +83,9 @@ void ControlEmbeddedFiles::view(EmbeddedFile const & item) void ControlEmbeddedFiles::setEmbed(EmbeddedFile & item, bool embed) { + // FIXME: updateFromExternalFile() or extract() may fail... if (embed) - item.update(&kernel().buffer()); + item.updateFromExternalFile(&kernel().buffer()); else item.extract(&kernel().buffer()); item.setEmbed(embed); @@ -106,13 +105,19 @@ docstring const ControlEmbeddedFiles::browseFile() bool ControlEmbeddedFiles::extract(EmbeddedFile const & item) { - return item.extract(&kernel().buffer()); + if (item.embedded()) + return item.extract(&kernel().buffer()); + else + return false; } bool ControlEmbeddedFiles::update(EmbeddedFile const & item) { - return item.update(&kernel().buffer()); + if (item.embedded()) + return item.updateFromExternalFile(&kernel().buffer()); + else + return false; } } // namespace frontend diff --git a/src/frontends/controllers/ControlEmbeddedFiles.h b/src/frontends/controllers/ControlEmbeddedFiles.h index 426f42e82d..d0f9eabd69 100644 --- a/src/frontends/controllers/ControlEmbeddedFiles.h +++ b/src/frontends/controllers/ControlEmbeddedFiles.h @@ -44,7 +44,7 @@ public: /// void dispatchParams() {}; /// - void goTo(EmbeddedFile const & item); + void goTo(EmbeddedFile const & item, int idx); /// void view(EmbeddedFile const & item); /// diff --git a/src/frontends/qt4/GuiEmbeddedFiles.cpp b/src/frontends/qt4/GuiEmbeddedFiles.cpp index 886ecde1ce..9f48afc489 100644 --- a/src/frontends/qt4/GuiEmbeddedFiles.cpp +++ b/src/frontends/qt4/GuiEmbeddedFiles.cpp @@ -12,9 +12,12 @@ #include "GuiEmbeddedFiles.h" #include "debug.h" +#include "support/convert.h" +using std::string; namespace lyx { + namespace frontend { @@ -44,9 +47,13 @@ void GuiEmbeddedFilesDialog::on_filesLW_itemChanged(QListWidgetItem* item) { EmbeddedFiles & files = controller().embeddedFiles(); if (item->checkState() == Qt::Checked) { + if (files[filesLW->row(item)].embedded()) + return; controller().setEmbed(files[filesLW->row(item)], true); controller().dispatchMessage("Embed file " + fromqstr(item->text())); } else { + if (!files[filesLW->row(item)].embedded()) + return; controller().setEmbed(files[filesLW->row(item)], false); controller().dispatchMessage("Stop embedding file " + fromqstr(item->text())); } @@ -71,17 +78,10 @@ void GuiEmbeddedFilesDialog::on_filesLW_itemSelectionChanged() fullpathLE->setEnabled(selection.size() == 1); // try to find a common embedding status - QList::iterator it = selection.begin(); - QList::iterator it_end = selection.end(); - // if the selection is not empty - if (it != it_end) { - int idx = filesLW->row(*it); - fullpathLE->setText(toqstr(files[idx].absFilename())); - controller().goTo(files[idx]); - } - // are the items all selected or unselected? bool hasSelected = false; bool hasUnselected = false; + QList::iterator it = selection.begin(); + QList::iterator it_end = selection.end(); for (; it != it_end; ++it) { if ((*it)->checkState() == Qt::Checked) hasSelected = true; @@ -93,11 +93,32 @@ void GuiEmbeddedFilesDialog::on_filesLW_itemSelectionChanged() } -void GuiEmbeddedFilesDialog::on_filesLW_itemDoubleClicked() +void GuiEmbeddedFilesDialog::on_filesLW_itemClicked(QListWidgetItem* item) { EmbeddedFiles & files = controller().embeddedFiles(); - QList selection = filesLW->selectedItems(); - controller().view(files[filesLW->row(*selection.begin())]); + int idx = filesLW->row(item); + fullpathLE->setText(toqstr(files[idx].absFilename())); + if (files[idx].refCount() > 1) { + // if multiple insets are referred, click again will move + // to another inset + int k = item->data(Qt::UserRole).toInt(); + controller().goTo(files[idx], k); + k = (k + 1) % files[idx].refCount(); + item->setData(Qt::UserRole, k); + // update label + string label = files[idx].inzipName() + " (" + + convert(k + 1) + "/" + + convert(files[idx].refCount()) + ")"; + item->setText(toqstr(label)); + } else + controller().goTo(files[idx], 0); +} + + +void GuiEmbeddedFilesDialog::on_filesLW_itemDoubleClicked(QListWidgetItem* item) +{ + EmbeddedFiles & files = controller().embeddedFiles(); + controller().view(files[filesLW->row(item)]); } @@ -109,7 +130,10 @@ void GuiEmbeddedFilesDialog::updateView() EmbeddedFiles::EmbeddedFileList::const_iterator it = files.begin(); EmbeddedFiles::EmbeddedFileList::const_iterator it_end = files.end(); for (; it != it_end; ++it) { - QListWidgetItem * item = new QListWidgetItem(toqstr(it->inzipName())); + string label = it->inzipName(); + if (it->refCount() > 1) + label += " (1/" + convert(it->refCount()) + ")"; + QListWidgetItem * item = new QListWidgetItem(toqstr(label)); Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsSelectable; if (it->valid()) flag |= Qt::ItemIsEnabled; @@ -118,6 +142,8 @@ void GuiEmbeddedFilesDialog::updateView() item->setCheckState(Qt::Checked); else item->setCheckState(Qt::Unchecked); + // index of the currently used ParConstIterator + item->setData(Qt::UserRole, 0); filesLW->addItem(item); } } diff --git a/src/frontends/qt4/GuiEmbeddedFiles.h b/src/frontends/qt4/GuiEmbeddedFiles.h index 44cc425082..13bb845875 100644 --- a/src/frontends/qt4/GuiEmbeddedFiles.h +++ b/src/frontends/qt4/GuiEmbeddedFiles.h @@ -31,7 +31,8 @@ public Q_SLOTS: /// void on_filesLW_itemChanged(QListWidgetItem* item); void on_filesLW_itemSelectionChanged(); - void on_filesLW_itemDoubleClicked(); + void on_filesLW_itemClicked(QListWidgetItem* item); + void on_filesLW_itemDoubleClicked(QListWidgetItem* item); /// void updateView(); ///