From f52b73747f32ac5ca563bbabc148ced2e6157517 Mon Sep 17 00:00:00 2001 From: Bo Peng Date: Thu, 10 Jan 2008 23:39:58 +0000 Subject: [PATCH] Embedding: saving inzip name to .lyx file so that embedded files can always be found under different operating systems (even lyx versions). yet to be tested under windows git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22486 a592a061-630c-0410-9148-cb99ea01b6c8 --- src/EmbeddedFiles.cpp | 145 +++++++++++++++++++++-------- src/EmbeddedFiles.h | 27 ++++++ src/frontends/qt4/GuiBibtex.cpp | 10 +- src/frontends/qt4/GuiInclude.cpp | 2 +- src/insets/InsetBibtex.cpp | 43 ++++++++- src/insets/InsetExternal.cpp | 6 +- src/insets/InsetGraphicsParams.cpp | 6 +- src/insets/InsetInclude.cpp | 13 ++- 8 files changed, 191 insertions(+), 61 deletions(-) diff --git a/src/EmbeddedFiles.cpp b/src/EmbeddedFiles.cpp index e416ef9079..cc6174ec0d 100644 --- a/src/EmbeddedFiles.cpp +++ b/src/EmbeddedFiles.cpp @@ -43,35 +43,6 @@ using namespace lyx::support; namespace lyx { -/** Dir name used for ".." in the bundled file. - -Under the lyx temp directory, content.lyx and its embedded files are usually -saved as - -$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/$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. - -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) @@ -88,19 +59,19 @@ void EmbeddedFile::set(std::string const & filename, std::string const & buffer_ if (filename.empty()) return; - inzip_name_ = to_utf8(makeRelPath(from_utf8(absFilename()), - from_utf8(buffer_path))); - - if (FileName(inzip_name_).isAbsolute()) - inzip_name_ = absDirName + '/' + inzip_name_; + inzip_name_ = calcInzipName(buffer_path); +} - // replace .. by upDirName - if (prefixIs(inzip_name_, ".")) - inzip_name_ = subst(inzip_name_, "..", upDirName); - // 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_; +void EmbeddedFile::setInzipName(std::string const & name) +{ + if (name.empty() || name == inzip_name_) + return; + + // an enabled EmbeededFile should have this problem handled + BOOST_ASSERT(!enabled()); + // file will be synced when it is enabled + inzip_name_ = name; } @@ -148,8 +119,11 @@ void EmbeddedFile::enable(bool flag, Buffer const * buf) temp_path_ = buf->temppath(); if (!suffixIs(temp_path_, '/')) temp_path_ += '/'; - if (embedded()) + if (embedded()) { + if (inzip_name_ != calcInzipName(buf->filePath())) + syncInzipFile(buf->filePath()); updateFromExternalFile(); + } } else { extract(); temp_path_ = ""; @@ -291,6 +265,95 @@ unsigned long EmbeddedFile::checksum() const return availableFile().checksum(); } +/** +Under the lyx temp directory, content.lyx and its embedded files are usually +saved as + +$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/$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. + +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"; +const std::string driveName = "LyX.Embed.Drive"; +const std::string spaceName = "LyX.Embed.Space"; + +std::string EmbeddedFile::calcInzipName(std::string const & buffer_path) +{ + string inzipName = to_utf8(makeRelPath(from_utf8(absFilename()), + from_utf8(buffer_path))); + + if (FileName(inzipName).isAbsolute()) + inzipName = absDirName + '/' + inzipName; + + // replace .. by upDirName + if (prefixIs(inzipName, ".")) + inzipName = subst(inzipName, "..", upDirName); + // replace special characters by their value + inzipName = subst(inzipName, ":", driveName); + inzipName = subst(inzipName, " ", spaceName); + + // to avoid name conflict between $docu_path/file and $temp_path/file + // embedded files are in a subdirectory of $temp_path. + inzipName = embDirName + '/' + inzipName; + return inzipName; +} + + +void EmbeddedFile::syncInzipFile(std::string const & buffer_path) +{ + BOOST_ASSERT(enabled()); + string old_emb_file = temp_path_ + '/' + inzip_name_; + FileName old_emb(old_emb_file); + + LYXERR(Debug::FILES, " OLD ZIP " << old_emb_file << + " NEW ZIP " << calcInzipName(buffer_path)); + + //BOOST_ASSERT(old_emb.exists()); + + string new_inzip_name = calcInzipName(buffer_path); + string new_emb_file = temp_path_ + '/' + new_inzip_name; + FileName new_emb(new_emb_file); + + // need to make directory? + FileName path = new_emb.onlyPath(); + if (!path.createPath()) { + throw ExceptionMessage(ErrorException, _("Sync file failure"), + bformat(_("Cannot create file path '%1$s'.\n" + "Please check whether the path is writeable."), + from_utf8(path.absFilename()))); + return; + } + + if (old_emb.copyTo(new_emb)) { + LYXERR(Debug::FILES, "Sync inzip file from " << inzip_name_ + << " to " << new_inzip_name); + inzip_name_ = new_inzip_name; + return; + } + throw ExceptionMessage(ErrorException, _("Sync file failure"), + bformat(_("Cannot copy file %1$s to %2$s.\n" + "Please check whether the directory exists and is writeable."), + from_utf8(old_emb_file), from_utf8(new_emb_file))); +} + bool operator==(EmbeddedFile const & lhs, EmbeddedFile const & rhs) { diff --git a/src/EmbeddedFiles.h b/src/EmbeddedFiles.h index 403f9cfa2a..b0d719ba85 100644 --- a/src/EmbeddedFiles.h +++ b/src/EmbeddedFiles.h @@ -102,7 +102,27 @@ public: std::string const & buffer_path = std::string()); /// set filename and inzipName. + /** + * NOTE: inzip_name_ is not unique across operation systems and is not + * guaranteed to be the same across different versions of lyx. + * inzip_name_ will be saved to the lyx file, and is used to indicate + * whether or not a file is embedded, and where the embedded file is in + * the bundled file. However, the embedded file will be renamed to the + * name set here when an EmbeddedFile is enabled. It is therefore + * safe to change the naming scheme here. + * + * NOTE that this treatment does not welcome an UUID solution because + * all embedded files will have to be renamed when an embedded file is + * opened. It is of course possible to use saved inzipname, but that is + * not easy. For example, when a new EmbeddedFile is created with the same + * file as an old one, it needs to be synced to the old inzipname... + **/ void set(std::string const & filename, std::string const & buffer_path); + /** Set the inzip name of an EmbeddedFile, which should be the name + * of an actual embedded file on disk. When an EmbeddedFile is enabled, + * this file will be renamed to the default inzipName if needed. + */ + void setInzipName(std::string const & name); /// filename in the zip file, which is the relative path std::string inzipName() const { return inzip_name_; } @@ -147,6 +167,13 @@ public: /// Calculate checksum of availableFile unsigned long checksum() const; +private: + // calculate inzip_name_ from filename + std::string calcInzipName(std::string const & buffer_path); + // move an embedded disk file with an existing inzip_name_ to + // an calculated inzip_name_ + void syncInzipFile(std::string const & buffer_path); + private: /// filename in zip file std::string inzip_name_; diff --git a/src/frontends/qt4/GuiBibtex.cpp b/src/frontends/qt4/GuiBibtex.cpp index e7620194d1..5635660836 100644 --- a/src/frontends/qt4/GuiBibtex.cpp +++ b/src/frontends/qt4/GuiBibtex.cpp @@ -215,7 +215,7 @@ void GuiBibtex::addDatabase() db->setFlags(db->flags() | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); db->setCheckState(Qt::Checked); - databaseLW->addItem(f); + databaseLW->addItem(db); } } @@ -269,7 +269,7 @@ void GuiBibtex::updateContents() QListWidgetItem * db = new QListWidgetItem(toqstr(bib)); db->setFlags(db->flags() | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); - db->setCheckState(emb == _("true") ? Qt::Checked : Qt::Unchecked); + db->setCheckState(emb.empty() ? Qt::Unchecked : Qt::Checked); databaseLW->addItem(db); } } @@ -281,11 +281,7 @@ void GuiBibtex::updateContents() for (vector::const_iterator it = bib_str.begin(); it != bib_str.end(); ++it) { string bibItem(changeExtension(*it, "")); - QListWidgetItem * db = new QListWidgetItem(toqstr(bibItem)); - db->setFlags(db->flags() & ~Qt::ItemIsSelectable - | Qt::ItemIsUserCheckable); - db->setCheckState(Qt::Unchecked); - add_->bibLW->addItem(db); + add_->bibLW->addItem(toqstr(bibItem)); } string bibstyle = getStylefile(); diff --git a/src/frontends/qt4/GuiInclude.cpp b/src/frontends/qt4/GuiInclude.cpp index ab6602a443..8165a7e9af 100644 --- a/src/frontends/qt4/GuiInclude.cpp +++ b/src/frontends/qt4/GuiInclude.cpp @@ -179,7 +179,7 @@ void GuiInclude::typeChanged(int v) void GuiInclude::updateContents() { filenameED->setText(toqstr(params_["filename"])); - embedCB->setCheckState(params_["embed"] == _("true") ? Qt::Checked : Qt::Unchecked); + embedCB->setCheckState(params_["embed"].empty() ? Qt::Unchecked : Qt::Checked); visiblespaceCB->setChecked(false); visiblespaceCB->setEnabled(false); diff --git a/src/insets/InsetBibtex.cpp b/src/insets/InsetBibtex.cpp index 4a92b784d8..d337641f25 100644 --- a/src/insets/InsetBibtex.cpp +++ b/src/insets/InsetBibtex.cpp @@ -90,10 +90,42 @@ void InsetBibtex::doDispatch(Cursor & cur, FuncRequest & cmd) } // InsetCommandParams orig = params(); + // returned "embed" is composed of "true" or "false", which needs to be adjusted + string tmp; + string emb; + + string newBibfiles; + string newEmbedStatus; + + string bibfiles = to_utf8(p["bibfiles"]); + string embedStatus = to_utf8(p["embed"]); + + bibfiles = split(bibfiles, tmp, ','); + embedStatus = split(embedStatus, emb, ','); + while (!tmp.empty()) { + EmbeddedFile file(changeExtension(tmp, "bib"), cur.buffer().filePath()); + if (!file.exists()) + continue; + if (!newBibfiles.empty()) + newBibfiles += ","; + newBibfiles += tmp; + if (!newEmbedStatus.empty()) + newEmbedStatus += ","; + if (emb == "true") + newEmbedStatus += file.inzipName(); + // Get next file name + bibfiles = split(bibfiles, tmp, ','); + embedStatus = split(embedStatus, emb, ','); + } + LYXERR(Debug::FILES, "Update parameters from " << p["bibfiles"] + << " " << p["embed"] << " to " << newBibfiles << " " + << newEmbedStatus); + p["bibfiles"] = from_utf8(newBibfiles); + p["embed"] = from_utf8(newEmbedStatus); + setParams(p); - // test parameter and copy files try { - // enable() in getFiles will try to copy files + // test parameter and copy files getFiles(cur.buffer()); } catch (ExceptionMessage const & message) { Alert::error(message.title_, message.details_); @@ -337,7 +369,7 @@ EmbeddedFileList const InsetBibtex::getFiles(Buffer const & buffer) const bibfiles = split(bibfiles, tmp, ','); embedStatus = split(embedStatus, emb, ','); while (!tmp.empty()) { - if (emb == "true") { + if (!emb.empty()) { EmbeddedFile file(changeExtension(tmp, "bib"), buffer.filePath()); // If the file structure is correct, this should not fail. file.setEmbed(true); @@ -831,10 +863,11 @@ void InsetBibtex::updateEmbeddedFile(Buffer const & buf, EmbeddedFile const & fi if (!first) { bibfiles += ','; embed += ','; + } else first = false; - } bibfiles += from_utf8(it->outputFilename(buf.filePath())); - embed += it->embedded() ? _("true") : _("false"); + if (it->embedded()) + embed += from_utf8(it->inzipName()); } setParam("bibfiles", bibfiles); setParam("embed", embed); diff --git a/src/insets/InsetExternal.cpp b/src/insets/InsetExternal.cpp index 2b5a87e83c..9da0d772e7 100644 --- a/src/insets/InsetExternal.cpp +++ b/src/insets/InsetExternal.cpp @@ -189,7 +189,7 @@ void InsetExternalParams::write(Buffer const & buffer, ostream & os) const if (!filename.empty()) { os << "\tfilename " << filename.outputFilename(buffer.filePath()) << '\n'; - os << "\tembed " << (filename.embedded() ? "true" : "false") << '\n'; + os << "\tembed " << (filename.embedded() ? filename.inzipName() : "\"\"") << '\n'; } if (display != defaultDisplayType) os << "\tdisplay " @@ -303,7 +303,9 @@ bool InsetExternalParams::read(Buffer const & buffer, Lexer & lex) case EX_EMBED: { lex.next(); - filename.setEmbed(lex.getBool()); + string const name = lex.getString(); + filename.setInzipName(name); + filename.setEmbed(!name.empty()); break; } diff --git a/src/insets/InsetGraphicsParams.cpp b/src/insets/InsetGraphicsParams.cpp index a0dba1a5d1..78aa53a333 100644 --- a/src/insets/InsetGraphicsParams.cpp +++ b/src/insets/InsetGraphicsParams.cpp @@ -145,7 +145,7 @@ void InsetGraphicsParams::Write(ostream & os, Buffer const & buffer) const // Do not write the default values if (!filename.empty()) { os << "\tfilename " << filename.outputFilename(buffer.filePath()) << '\n'; - os << "\tembed " << (filename.embedded() ? "true" : "false") << '\n'; + os << "\tembed " << (filename.embedded() ? filename.inzipName() : "\"\"") << '\n'; } if (lyxscale != 100) os << "\tlyxscale " << lyxscale << '\n'; @@ -201,7 +201,9 @@ bool InsetGraphicsParams::Read(Lexer & lex, string const & token, string const & lex.eatLine(); } else if (token == "embed") { lex.next(); - filename.setEmbed(lex.getBool()); + string const name = lex.getString(); + filename.setInzipName(name); + filename.setEmbed(!name.empty()); } else if (token == "lyxscale") { lex.next(); lyxscale = lex.getInteger(); diff --git a/src/insets/InsetInclude.cpp b/src/insets/InsetInclude.cpp index 9235db1d37..768806451a 100644 --- a/src/insets/InsetInclude.cpp +++ b/src/insets/InsetInclude.cpp @@ -140,7 +140,7 @@ EmbeddedFile const includedFilename(Buffer const & buffer, // each time, but there seems to be no easy way around. EmbeddedFile file(to_utf8(params["filename"]), onlyPath(parentFilename(buffer))); - file.setEmbed(params["embed"] == _("true") ? true : false); + file.setEmbed(!params["embed"].empty()); file.enable(buffer.embedded(), &buffer); return file; } @@ -202,6 +202,13 @@ void InsetInclude::doDispatch(Cursor & cur, FuncRequest & cmd) REF_CODE); } try { + // the embed parameter passed back from the dialog + // is "true" or "false", we need to change it. + if (p["embed"] == _("false")) + p["embed"].clear(); + else + p["embed"] = from_utf8(EmbeddedFile(to_utf8(p["filename"]), + onlyPath(parentFilename(cur.buffer()))).inzipName()); // test parameter includedFilename(cur.buffer(), p); } catch (ExceptionMessage const & message) { @@ -274,7 +281,7 @@ docstring const InsetInclude::getScreenLabel(Buffer const & buf) const else temp += from_utf8(onlyFilename(to_utf8(params()["filename"]))); - if (params()["embed"] == _("true")) + if (!params()["embed"].empty()) temp += _(" (embedded)"); return temp; } @@ -910,7 +917,7 @@ void InsetInclude::updateEmbeddedFile(Buffer const & buf, { InsetCommandParams p = params(); p["filename"] = from_utf8(file.outputFilename()); - p["embed"] = file.embedded() ? _("true") : _("false"); + p["embed"] = file.embedded() ? from_utf8(file.inzipName()) : docstring(); set(p, buf); } -- 2.39.5