]> git.lyx.org Git - features.git/commitdiff
Embedding: use a vector to store multiple ParConstIterator of multiple insets referri...
authorBo Peng <bpeng@lyx.org>
Sat, 8 Sep 2007 04:10:43 +0000 (04:10 +0000)
committerBo Peng <bpeng@lyx.org>
Sat, 8 Sep 2007 04:10:43 +0000 (04:10 +0000)
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@20137 a592a061-630c-0410-9148-cb99ea01b6c8

src/EmbeddedFiles.cpp
src/EmbeddedFiles.h
src/frontends/controllers/ControlEmbeddedFiles.cpp
src/frontends/controllers/ControlEmbeddedFiles.h
src/frontends/qt4/GuiEmbeddedFiles.cpp
src/frontends/qt4/GuiEmbeddedFiles.h

index e67d1511413e968bc56f925ffa7cf419efa27766..bad37e77898b446a8adbdb2a0cf9d30e29121e76 100644 (file)
@@ -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<char*>(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<char*>(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<char*>(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<char*>(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();
+       for (; it != it_end; ++it)
+               if (it->valid() && it->embedded())
+                       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();
-       // 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->updateFromExternalFile(buffer_))
+                               return false;
        return true;
 }
 
index de050c5622539c30d8fe5fbcea207f16a592643e..4d4ac014f11e54496ab5e20128ba0c6425eeb231 100644 (file)
@@ -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<ParConstIterator> par_it_;
 };
 
 
@@ -171,10 +175,16 @@ public:
 
        /// return buffer params embedded flag
        bool enabled() const;
-       /// set buffer params embedded flag
-       void enable(bool flag);
-
-       /// add a file item
+       /// 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. 
+       /* \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 &);
 
index 1ea646cf2a51dbe2650f76b3f02bbf3a1a4be5ae..707a4609c6155300203e613b512cb149cd4f6d46 100644 (file)
@@ -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<string>(item.parID());
-               kernel().lyxview().dispatch(FuncRequest(LFUN_PARAGRAPH_GOTO, tmp));
-       }
+       BOOST_ASSERT(idx < item.refCount());
+       string const tmp = convert<string>(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
index 426f42e82d411dd8a588c0a6754359a6158b2f11..d0f9eabd69765ebb458112b4b9566892cac5a889 100644 (file)
@@ -44,7 +44,7 @@ public:
        ///
        void dispatchParams() {};
        ///
-       void goTo(EmbeddedFile const & item);
+       void goTo(EmbeddedFile const & item, int idx);
        ///
        void view(EmbeddedFile const & item);
        ///
index 886ecde1ce62b26e1c7da4a0634e8b7c9ac6a414..9f48afc489e0a82610cd0cd185dbed6fc481d0b9 100644 (file)
 
 #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<QListWidgetItem*>::iterator it = selection.begin(); 
-       QList<QListWidgetItem*>::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<QListWidgetItem*>::iterator it = selection.begin(); 
+       QList<QListWidgetItem*>::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<QListWidgetItem *> 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<string>(k + 1)  + "/"
+                       + convert<string>(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<string>(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);
        }
 }
index 44cc425082950509c231c2808b5315bbfb6e707c..13bb84587526c1e8d6eed16305741f62b57af2b4 100644 (file)
@@ -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();
        ///