From c9a7e8c4169e7f45b9bfa6859fe6765adb0bc3a7 Mon Sep 17 00:00:00 2001 From: Bo Peng Date: Mon, 7 Jan 2008 03:34:21 +0000 Subject: [PATCH] Embedding: After another round of EmbeddedFiles changes, embedded figures should more or less work now. * src/insets/InsetExternal.cpp: fix compiling. (Does not work yet) * src/insets/InsetGraphics.cpp: use latexFilename to produce output. * src/insets/InsetGraphicsParams.cpp: use availableFile to show preview. * src/EmbeddedFiles.h|cpp: More things are moved from EmbeddedFiles to EmbeddedFile * src/frontends/qt4/GuiGraphics.cpp: remove embeddable-related operations. * src/support/FileName.h: make isReadableFile virtual git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22410 a592a061-630c-0410-9148-cb99ea01b6c8 --- src/EmbeddedFiles.cpp | 241 ++++++++++++++--------------- src/EmbeddedFiles.h | 48 +++--- src/frontends/qt4/GuiGraphics.cpp | 5 - src/insets/InsetExternal.cpp | 17 +- src/insets/InsetGraphics.cpp | 40 ++--- src/insets/InsetGraphicsParams.cpp | 2 +- src/support/FileName.h | 2 +- 7 files changed, 155 insertions(+), 200 deletions(-) diff --git a/src/EmbeddedFiles.cpp b/src/EmbeddedFiles.cpp index dac3796b17..23bebc3756 100644 --- a/src/EmbeddedFiles.cpp +++ b/src/EmbeddedFiles.cpp @@ -48,29 +48,35 @@ namespace lyx { Under the lyx temp directory, content.lyx and its embedded files are usually saved as -$temp/file.lyx -$temp/figure1.png for ./figure1.png) -$temp/sub/figure2.png for ./sub/figure2.png) +$temp/$embDirName/file.lyx +$temp/$embDirName/figure1.png for ./figure1.png) +$temp/$embDirName/sub/figure2.png for ./sub/figure2.png) This works fine for embedded files that are in the current or deeper directory of the document directory, but not for files such as ../figures/figure.png. A unique name $upDirName is chosen to represent .. in such filenames so that 'up' directories can be stored 'down' the directory tree: -$temp/$upDirName/figures/figure.png for ../figures/figure.png -$temp/$upDirName/$upDirName/figure.png for ../../figure.png +$temp/$embDirName/$upDirName/figures/figure.png for ../figures/figure.png +$temp/$embDirName/$upDirName/$upDirName/figure.png for ../../figure.png This name has to be fixed because it is used in lyx bundled .zip file. -Note that absolute files are not embeddable because there is no easy -way to put them under $temp. +Using a similar trick, we use $absDirName for absolute path so that +an absolute filename can be saved as + +$temp/$embDirName/$absDirName/a/absolute/path for /a/absolute/path + */ +const std::string embDirName = "LyX.Embedded.Files"; const std::string upDirName = "LyX.Embed.Dir.Up"; +const std::string absDirName = "LyX.Embed.Dir.Abs"; namespace Alert = frontend::Alert; EmbeddedFile::EmbeddedFile(string const & file, std::string const & buffer_path) - : DocFileName("", false), inzip_name_(""), embedded_(false), inset_list_() + : DocFileName("", false), inzip_name_(""), embedded_(false), inset_list_(), + temp_path_("") { set(file, buffer_path); } @@ -84,30 +90,39 @@ void EmbeddedFile::set(std::string const & filename, std::string const & buffer_ inzip_name_ = to_utf8(makeRelPath(from_utf8(absFilename()), from_utf8(buffer_path))); - // if inzip_name_ is an absolute path, this file is not embeddable + if (FileName(inzip_name_).isAbsolute()) - inzip_name_ = ""; + inzip_name_ = absDirName + '/' + inzip_name_; + // replace .. by upDirName if (prefixIs(inzip_name_, ".")) inzip_name_ = subst(inzip_name_, "..", upDirName); - LYXERR(Debug::FILES, "Create embedded file " << filename << - " with in zip name " << inzip_name_ << endl); + + // to avoid name conflict between $docu_path/file and $temp_path/file + // embedded files are in a subdirectory of $temp_path. + inzip_name_ = embDirName + '/' + inzip_name_; +} + + +string EmbeddedFile::embeddedFile() const +{ + BOOST_ASSERT(enabled()); + return temp_path_ + inzip_name_; } -string EmbeddedFile::embeddedFile(Buffer const * buf) const +FileName EmbeddedFile::availableFile() const { - BOOST_ASSERT(embeddable()); - string temp = buf->temppath(); - if (!suffixIs(temp, '/')) - temp += '/'; - return temp + inzip_name_; + if (enabled() && embedded()) + return FileName(embeddedFile()); + else + return *this; } -string EmbeddedFile::availableFile(Buffer const * buf) const +string EmbeddedFile::latexFilename(std::string const & buffer_path) const { - return embedded() ? embeddedFile(buf) : absFilename(); + return (enabled() && embedded()) ? inzip_name_ : relFilename(buffer_path); } @@ -118,41 +133,49 @@ void EmbeddedFile::addInset(Inset const * inset) } -Inset const * EmbeddedFile::inset(int idx) const +void EmbeddedFile::setEmbed(bool embed) { - BOOST_ASSERT(idx < refCount()); - // some embedded file do not have a valid par iterator - return inset_list_[idx]; + embedded_ = embed; } -void EmbeddedFile::setEmbed(bool embed) +void EmbeddedFile::enable(bool flag, Buffer const * buf) { - if (!embeddable() && embed) { - Alert::error(_("Embedding failed."), bformat( - _("Cannot embed file %1$s because its path is not relative to document path."), - from_utf8(absFilename()))); + if (enabled() == flag) return; + + if (flag) { + temp_path_ = buf->temppath(); + if (!suffixIs(temp_path_, '/')) + temp_path_ += '/'; + if (embedded()) + updateFromExternalFile(); + } else { + extract(); + temp_path_ = ""; } - embedded_ = embed; } -bool EmbeddedFile::extract(Buffer const * buf) const +bool EmbeddedFile::extract() const { - BOOST_ASSERT(embeddable()); + BOOST_ASSERT(enabled()); string ext_file = absFilename(); - string emb_file = embeddedFile(buf); + string emb_file = embeddedFile(); FileName emb(emb_file); FileName ext(ext_file); - if (!emb.exists()) - throw ExceptionMessage(ErrorException, _("Failed to extract file"), - bformat(_("Cannot extract file '%1$s'.\n" - "Source file %2$s does not exist"), - from_utf8(outputFilename()), from_utf8(emb_file))); + if (!emb.exists()) { + if (ext.exists()) + return true; + else + throw ExceptionMessage(ErrorException, _("Failed to extract file"), + bformat(_("Cannot extract file '%1$s'.\n" + "Source file %2$s does not exist"), + from_utf8(outputFilename()), from_utf8(emb_file))); + } // if external file already exists ... if (ext.exists()) { @@ -194,12 +217,12 @@ bool EmbeddedFile::extract(Buffer const * buf) const } -bool EmbeddedFile::updateFromExternalFile(Buffer const * buf) const +bool EmbeddedFile::updateFromExternalFile() const { - BOOST_ASSERT(embeddable()); + BOOST_ASSERT(enabled()); string ext_file = absFilename(); - string emb_file = embeddedFile(buf); + string emb_file = embeddedFile(); FileName emb(emb_file); FileName ext(ext_file); @@ -257,6 +280,12 @@ void EmbeddedFile::updateInsets(Buffer const * buf) const } +bool EmbeddedFile::isReadableFile() const +{ + return availableFile().isReadableFile(); +} + + bool operator==(EmbeddedFile const & lhs, EmbeddedFile const & rhs) { return lhs.absFilename() == rhs.absFilename() @@ -279,27 +308,50 @@ bool EmbeddedFiles::enabled() const void EmbeddedFiles::enable(bool flag) { - if (enabled() != flag) { - // update embedded file list - update(); - // An exception may be thrown. - if (flag) - // if enable, copy all files to temppath() - updateFromExternalFile(); + if (enabled() == flag) + return; + + // update embedded file list + update(); + + int count_embedded = 0; + int count_external = 0; + EmbeddedFileList::iterator it = file_list_.begin(); + EmbeddedFileList::iterator it_end = file_list_.end(); + // an exception may be thrown + for (; it != it_end; ++it) { + it->enable(flag, buffer_); + if (it->embedded()) + count_embedded ++; else - // if disable, extract all files - extractAll(); - // if operation is successful (no exception is thrown) - buffer_->markDirty(); - buffer_->params().embedded = flag; - if (flag) - updateInsets(); + count_external ++; + } + // if operation is successful (no exception is thrown) + buffer_->markDirty(); + buffer_->params().embedded = flag; + + // if the operation is successful, update insets + for (it = file_list_.begin(); it != it_end; ++it) + it->updateInsets(buffer_); + + // show result + if (flag) { + docstring const msg = bformat(_("%1$d external files are ignored.\n" + "%2$d embeddable files are embedded.\n"), count_external, count_embedded); + Alert::information(_("Packing all files"), msg); + } else { + docstring const msg = bformat(_("%1$d external files are ignored.\n" + "%2$d embedded files are extracted.\n"), count_external, count_embedded); + Alert::information(_("Unpacking all files"), msg); } } -EmbeddedFile & EmbeddedFiles::registerFile(EmbeddedFile const & file, Inset const * inset) +void EmbeddedFiles::registerFile(EmbeddedFile const & file, Inset const * inset) { + BOOST_ASSERT(!enabled() || file.availableFile().exists()); + BOOST_ASSERT(!enabled() || file.enabled()); + // try to find this file from the list EmbeddedFileList::iterator it = file_list_.begin(); EmbeddedFileList::iterator it_end = file_list_.end(); @@ -311,14 +363,15 @@ EmbeddedFile & EmbeddedFiles::registerFile(EmbeddedFile const & file, Inset cons "but with different embedding status. Assuming embedding status."), from_utf8(it->outputFilename()))); it->setEmbed(true); + // update the inset with this embedding status. + const_cast(inset)->updateEmbeddedFile(*buffer_, *it); } it->addInset(inset); - return *it; + return; } // file_list_.push_back(file); file_list_.back().addInset(inset); - return file_list_.back(); } @@ -341,11 +394,12 @@ bool EmbeddedFiles::writeFile(DocFileName const & filename) // add content.lyx to filenames filenames.push_back(make_pair(content, "content.lyx")); // prepare list of embedded file + update(); EmbeddedFileList::iterator it = file_list_.begin(); EmbeddedFileList::iterator it_end = file_list_.end(); for (; it != it_end; ++it) { if (it->embedded()) { - string file = it->embeddedFile(buffer_); + string file = it->embeddedFile(); if (!FileName(file).exists()) throw ExceptionMessage(ErrorException, _("Failed to write file"), bformat(_("Embedded file %1$s does not exist. Did you tamper lyx temporary directory?"), @@ -373,73 +427,4 @@ bool EmbeddedFiles::writeFile(DocFileName const & filename) return true; } - -EmbeddedFiles::EmbeddedFileList::const_iterator -EmbeddedFiles::find(string filename) const -{ - EmbeddedFileList::const_iterator it = file_list_.begin(); - EmbeddedFileList::const_iterator it_end = file_list_.end(); - for (; it != it_end; ++it) - if (it->absFilename() == filename || it->embeddedFile(buffer_) == filename) - return it; - return file_list_.end(); -} - - -bool EmbeddedFiles::extractAll() const -{ - EmbeddedFileList::const_iterator it = file_list_.begin(); - EmbeddedFileList::const_iterator it_end = file_list_.end(); - int count_extracted = 0; - int count_external = 0; - for (; it != it_end; ++it) - if (it->embedded()) { - if(!it->extract(buffer_)) { - throw ExceptionMessage(ErrorException, - _("Failed to extract file"), - bformat(_("Error: can not extract file %1$s.\n"), it->displayName())); - } else - count_extracted += 1; - } else - count_external += 1; - docstring const msg = bformat(_("%1$d external or non-embeddable files are ignored.\n" - "%2$d embedded files are extracted.\n"), count_external, count_extracted); - Alert::information(_("Unpacking all files"), msg); - return true; -} - - -bool EmbeddedFiles::updateFromExternalFile() const -{ - EmbeddedFileList::const_iterator it = file_list_.begin(); - EmbeddedFileList::const_iterator it_end = file_list_.end(); - int count_embedded = 0; - int count_external = 0; - for (; it != it_end; ++it) - if (it->embedded()) { - if (!it->updateFromExternalFile(buffer_)) { - throw ExceptionMessage(ErrorException, - _("Failed to embed file"), - bformat(_("Error: can not embed file %1$s.\n"), it->displayName())); - return false; - } else - count_embedded += 1; - } else - count_external += 1; - docstring const msg = bformat(_("%1$d external or non-embeddable files are ignored.\n" - "%2$d embeddable files are embedded.\n"), count_external, count_embedded); - Alert::information(_("Packing all files"), msg); - return true; -} - - -void EmbeddedFiles::updateInsets() const -{ - EmbeddedFiles::EmbeddedFileList::const_iterator it = begin(); - EmbeddedFiles::EmbeddedFileList::const_iterator it_end = end(); - for (; it != it_end; ++it) - if (it->refCount() > 0) - it->updateInsets(buffer_); -} - } diff --git a/src/EmbeddedFiles.h b/src/EmbeddedFiles.h index 3cb95be7ca..76cb9b8fdb 100644 --- a/src/EmbeddedFiles.h +++ b/src/EmbeddedFiles.h @@ -103,22 +103,20 @@ public: /// set filename and inzipName. void set(std::string const & filename, std::string const & buffer_path); - + /// filename in the zip file, which is the relative path std::string inzipName() const { return inzip_name_; } /// embedded file, equals to temppath()/inzipName() - std::string embeddedFile(Buffer const * buf) const; + std::string embeddedFile() const; /// embeddedFile() or absFilename() depending on embedding status - std::string availableFile(Buffer const * buf) const; + /// and whether or not embedding is enabled. + FileName availableFile() const; + /// + std::string latexFilename(std::string const & buffer_path) const; /// add an inset that refers to this file void addInset(Inset const * inset); - Inset const * inset(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 inset_list_.size(); } /// embedding status of this file @@ -126,13 +124,16 @@ public: /// set embedding status. updateFromExternal() should be called before this /// to copy or sync the embedded file with external one. void setEmbed(bool embed); - /// Only files in or under current document path is embeddable - bool embeddable() const { return inzip_name_ != ""; } + + /// whether or not embedding is enabled in the current buffer + bool enabled() const { return temp_path_ != ""; } + /// enable embedding of this file + void enable(bool flag, Buffer const * buf); /// extract file, does not change embedding status - bool extract(Buffer const * buf) const; + bool extract() const; /// update embedded file from external file, does not change embedding status - bool updateFromExternalFile(Buffer const * buf) const; + bool updateFromExternalFile() const; /// /// After the embedding status is changed, update all insets related /// to this file item. For example, a graphic inset may need to monitor @@ -141,6 +142,9 @@ public: /// document between EmbeddedFiles::update() and this function. void updateInsets(Buffer const * buf) const; + /// Check readability of availableFile + bool isReadableFile() const; + private: /// filename in zip file std::string inzip_name_; @@ -149,6 +153,11 @@ private: /// Insets that contains this file item. Because a /// file item can be referred by several Insets, a vector is used. std::vector inset_list_; + /// Embedded file needs to know whether enbedding is enabled, + /// and where is the lyx temporary directory. Such information can + /// be retrived from a buffer, but a buffer is not always available when + /// an EmbeddedFile is used. + std::string temp_path_; }; @@ -175,7 +184,7 @@ public: /* \param file Embedded file to add * \param inset Inset pointer */ - EmbeddedFile & registerFile(EmbeddedFile const & file, Inset const * inset = 0); + void registerFile(EmbeddedFile const & file, Inset const * inset = 0); /// scan the buffer and get a list of EmbeddedFile void update(); @@ -185,23 +194,12 @@ public: void clear() { file_list_.clear(); } - /// - EmbeddedFile & operator[](size_t idx) { return *(file_list_.begin() + idx); } - EmbeddedFile const & operator[](size_t idx) const { return *(file_list_.begin() + idx); } /// EmbeddedFileList::iterator begin() { return file_list_.begin(); } EmbeddedFileList::iterator end() { return file_list_.end(); } EmbeddedFileList::const_iterator begin() const { return file_list_.begin(); } 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; - /// extract all file items, used when disable embedding - bool extractAll() const; - /// update all files from external, used when enable embedding - bool updateFromExternalFile() const; - /// - /// update all insets to use embedded files when embedding status is changed - void updateInsets() const; + private: /// list of embedded files EmbeddedFileList file_list_; diff --git a/src/frontends/qt4/GuiGraphics.cpp b/src/frontends/qt4/GuiGraphics.cpp index 789d38db2f..f886fe829f 100644 --- a/src/frontends/qt4/GuiGraphics.cpp +++ b/src/frontends/qt4/GuiGraphics.cpp @@ -300,10 +300,6 @@ void GuiGraphics::on_filename_textChanged(const QString & filename) { editPB->setDisabled(filename.isEmpty()); EmbeddedFile file = EmbeddedFile(fromqstr(filename), bufferFilepath()); - if (!file.embeddable()) { - embedCB->setCheckState(Qt::Unchecked); - embedCB->setDisabled(true); - } } @@ -459,7 +455,6 @@ void GuiGraphics::updateContents() string const name = igp.filename.outputFilename(bufferFilepath()); filename->setText(toqstr(name)); - embedCB->setEnabled(igp.filename.embeddable()); embedCB->setCheckState(igp.filename.embedded() ? Qt::Checked : Qt::Unchecked); // set the bounding box values diff --git a/src/insets/InsetExternal.cpp b/src/insets/InsetExternal.cpp index bb86ea0264..5e04597b3a 100644 --- a/src/insets/InsetExternal.cpp +++ b/src/insets/InsetExternal.cpp @@ -182,15 +182,8 @@ void InsetExternalParams::write(Buffer const & buffer, ostream & os) const os << "External\n" << "\ttemplate " << templatename() << '\n'; - if (!filename.empty()) { - // when we save, we still use the original filename - EmbeddedFiles::EmbeddedFileList::const_iterator it = - buffer.embeddedFiles().find(filename.toFilesystemEncoding()); - if (it != buffer.embeddedFiles().end()) - os << "\tfilename " << DocFileName(it->absFilename()).outputFilename(buffer.filePath()) << '\n'; - else - os << "\tfilename " << filename.outputFilename(buffer.filePath()) << '\n'; - } + if (!filename.empty()) + os << "\tfilename " << filename.outputFilename(buffer.filePath()) << '\n'; if (display != defaultDisplayType) os << "\tdisplay " @@ -297,12 +290,6 @@ bool InsetExternalParams::read(Buffer const & buffer, Lexer & lex) lex.eatLine(); string const name = lex.getString(); filename.set(name, buffer.filePath()); - // maybe this file is embedded - EmbeddedFiles::EmbeddedFileList::const_iterator it = buffer.embeddedFiles().find(filename.toFilesystemEncoding()); - if (it != buffer.embeddedFiles().end()) - // using available file, embedded or external, depending on file availability and - // embedding status. - filename = DocFileName(it->availableFile(&buffer)); break; } diff --git a/src/insets/InsetGraphics.cpp b/src/insets/InsetGraphics.cpp index 4368ffb6f9..36ad1b7f40 100644 --- a/src/insets/InsetGraphics.cpp +++ b/src/insets/InsetGraphics.cpp @@ -171,13 +171,9 @@ void InsetGraphics::doDispatch(Cursor & cur, FuncRequest & cmd) Buffer const & buffer = cur.buffer(); InsetGraphicsParams p; InsetGraphicsMailer::string2params(to_utf8(cmd.argument()), buffer, p); - // when embedding is enabled, change of embedding status leads to actions - if (!p.filename.empty() && buffer.embeddedFiles().enabled() ) { + if (!p.filename.empty()) { try { - if (p.filename.embedded()) - p.filename.updateFromExternalFile(&buffer); - else - p.filename.extract(&buffer); + updateEmbeddedFile(buffer, p.filename); } catch (ExceptionMessage const & message) { Alert::error(message.title_, message.details_); // do not set parameter if an error happens @@ -231,11 +227,16 @@ void InsetGraphics::registerEmbeddedFiles(Buffer const &, void InsetGraphics::updateEmbeddedFile(Buffer const & buf, EmbeddedFile const & file) { - BOOST_ASSERT(buf.embeddedFiles().enabled()); - params_.filename = file; + // when embedding is enabled, change of embedding status leads to actions + EmbeddedFile temp = file; + temp.enable(buf.embeddedFiles().enabled(), &buf); + // this will not be set if an exception is thorwn in enable() + params_.filename = temp; + LYXERR(Debug::FILES, "Update InsetGraphic with File " << params_.filename.toFilesystemEncoding() - << ", embedding status: " << params_.filename.embedded()); + << ", embedding status: " << params_.filename.embedded() + << ", enabled: " << params_.filename.enabled()); } @@ -279,16 +280,7 @@ void InsetGraphics::read(Buffer const & buf, Lexer & lex) else LYXERR(Debug::GRAPHICS, "Not a Graphics inset!"); - // InsetGraphics is read, with filename in params_. We do not know if this file actually - // exists or is embedded so we need to get the 'availableFile' from buf.embeddedFiles() - if (buf.embeddedFiles().enabled()) { - EmbeddedFiles::EmbeddedFileList::const_iterator it = - buf.embeddedFiles().find(params_.filename.toFilesystemEncoding()); - if (it != buf.embeddedFiles().end()) - // using available file, embedded or external, depending on file availability and - // embedding status. - params_.filename = *it; - } + params_.filename.enable(buf.embeddedFiles().enabled(), &buf); graphic_->update(params().as_grfxParams()); } @@ -577,7 +569,8 @@ string const InsetGraphics::prepareFile(Buffer const & buf, if (params().filename.empty()) return string(); - string const orig_file = params().filename.absFilename(); + string const orig_file = params().filename.availableFile().absFilename(); + // this is for dryrun and display purposes, do not use latexFilename string const rel_file = params().filename.relFilename(buf.filePath()); // previewing source code, no file copying or file format conversion @@ -586,7 +579,7 @@ string const InsetGraphics::prepareFile(Buffer const & buf, // temp_file will contain the file for LaTeX to act on if, for example, // we move it to a temp dir or uncompress it. - FileName temp_file = params().filename; + FileName temp_file = params().filename.availableFile(); // The master buffer. This is useful when there are multiple levels // of include files @@ -606,7 +599,7 @@ string const InsetGraphics::prepareFile(Buffer const & buf, GraphicsCopyStatus status; boost::tie(status, temp_file) = - copyToDirIfNeeded(params().filename, temp_path); + copyToDirIfNeeded(params().filename.availableFile(), temp_path); if (status == FAILURE) return orig_file; @@ -763,9 +756,6 @@ int InsetGraphics::latex(Buffer const & buf, odocstream & os, LYXERR(Debug::GRAPHICS, "insetgraphics::latex: Filename = " << params().filename.absFilename()); - string const relative_file = - params().filename.relFilename(buf.filePath()); - bool const file_exists = !params().filename.empty() && params().filename.isReadableFile(); string const message = file_exists ? diff --git a/src/insets/InsetGraphicsParams.cpp b/src/insets/InsetGraphicsParams.cpp index 0afe0c38b6..5df541bf7d 100644 --- a/src/insets/InsetGraphicsParams.cpp +++ b/src/insets/InsetGraphicsParams.cpp @@ -273,7 +273,7 @@ bool InsetGraphicsParams::Read(Lexer & lex, string const & token, string const & graphics::Params InsetGraphicsParams::as_grfxParams() const { graphics::Params pars; - pars.filename = filename; + pars.filename = filename.availableFile(); pars.scale = lyxscale; pars.angle = convert(rotateAngle); diff --git a/src/support/FileName.h b/src/support/FileName.h index 66ea2edf24..97edfd7b7c 100644 --- a/src/support/FileName.h +++ b/src/support/FileName.h @@ -83,7 +83,7 @@ public: /// return true when file/directory is readable bool isReadableDirectory() const; /// return true when it is a file and readable - bool isReadableFile() const; + virtual bool isReadableFile() const; /// return true when file/directory is writable bool isWritable() const; /// return true when file/directory is writable (write test file) -- 2.39.5