X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FLayoutFile.cpp;h=9c76e5bad0c9abaa9355899bcd8b55e151c90144;hb=c7d29be153debac82e3d2e8865fcc849f0a5f40d;hp=90ea111d90e395eafa9d20f4051f4f16c9ffc30e;hpb=b18251d61bf6f87c0322b6d6988d994e90d33f19;p=lyx.git diff --git a/src/LayoutFile.cpp b/src/LayoutFile.cpp index 90ea111d90..9c76e5bad0 100644 --- a/src/LayoutFile.cpp +++ b/src/LayoutFile.cpp @@ -3,7 +3,7 @@ * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * - * \author Lars Gullik Bjønnes + * \author Lars Gullik Bjønnes * \author John Levon * * Full author contact details are available in file CREDITS. @@ -18,14 +18,17 @@ #include "Lexer.h" #include "TextClass.h" -#include "support/lassert.h" +#include "frontends/alert.h" + #include "support/debug.h" #include "support/FileName.h" #include "support/filetools.h" #include "support/gettext.h" +#include "support/lassert.h" +#include "support/lstrings.h" -#include -#include +#include "support/regex.h" +#include "support/TempFile.h" #include @@ -34,19 +37,22 @@ using namespace lyx::support; namespace lyx { -using boost::bind; -using boost::regex; -using boost::smatch; + LayoutFile::LayoutFile(string const & fn, string const & cln, - string const & desc, bool texClassAvail ) + string const & desc, string const & prereq, + string const & category, bool texclassavail) { - name_ = fn; + name_ = onlyFileName(fn); + path_ = fn.rfind('/') == string::npos ? string() : onlyPath(fn); latexname_ = cln; description_ = desc; - texClassAvail_ = texClassAvail; + prerequisites_ = prereq; + category_ = category; + tex_class_avail_ = texclassavail; } + LayoutFileList::~LayoutFileList() { ClassMap::const_iterator it = classmap_.begin(); @@ -56,7 +62,8 @@ LayoutFileList::~LayoutFileList() } } -LayoutFileList & LayoutFileList::get() + +LayoutFileList & LayoutFileList::get() { static LayoutFileList baseclasslist; return baseclasslist; @@ -77,15 +84,16 @@ bool LayoutFileList::haveClass(string const & classname) const LayoutFile const & LayoutFileList::operator[](string const & classname) const { - LASSERT(haveClass(classname), /**/); + LATTEST(haveClass(classname)); + // safe to continue, since we will make an empty LayoutFile return *classmap_[classname]; } -LayoutFile & - LayoutFileList::operator[](string const & classname) +LayoutFile & LayoutFileList::operator[](string const & classname) { - LASSERT(haveClass(classname), /**/); + LATTEST(haveClass(classname)); + // safe to continue, since we will make an empty LayoutFile return *classmap_[classname]; } @@ -93,82 +101,76 @@ LayoutFile & // Reads LyX textclass definitions according to textclass config file bool LayoutFileList::read() { + bool success = false; Lexer lex; FileName const real_file = libFileSearch("", "textclass.lst"); - LYXERR(Debug::TCLASS, "Reading textclasses from `" << real_file << '\''); + LYXERR(Debug::TCLASS, "Reading textclasses from `" << real_file << "'."); if (real_file.empty()) { - lyxerr << "LayoutFileList::Read: unable to find " - "textclass file `" - << to_utf8(makeDisplayPath(real_file.absFilename(), 1000)) - << "'. Exiting." << endl; - return false; - // This causes LyX to end... Not a desirable behaviour. Lgb - // What do you propose? That the user gets a file dialog - // and is allowed to hunt for the file? (Asger) - // more that we have a layout for minimal.cls statically - // compiled in... (Lgb) - } - - if (!lex.setFile(real_file)) { - lyxerr << "LayoutFileList::Read: " - "lyxlex was not able to set file: " - << real_file << endl; - } - - if (!lex.isOK()) { - lyxerr << "LayoutFileList::Read: unable to open " - "textclass file `" - << to_utf8(makeDisplayPath(real_file.absFilename(), 1000)) - << "'\nCheck your installation. LyX can't continue." - << endl; - return false; - } - - bool finished = false; - // Parse config-file - LYXERR(Debug::TCLASS, "Starting parsing of textclass.lst"); - while (lex.isOK() && !finished) { - LYXERR(Debug::TCLASS, "\tline by line"); - switch (lex.lex()) { - case Lexer::LEX_FEOF: - finished = true; - break; - default: - string const fname = lex.getString(); - LYXERR(Debug::TCLASS, "Fname: " << fname); - if (lex.next()) { + LYXERR0("LayoutFileList::Read: unable to find textclass file " + << "`textclass.lst'."); + } else if (!lex.setFile(real_file)) { + LYXERR0("LayoutFileList::Read: lyxlex was not able to set file: " + << real_file << '.'); + } else if (!lex.isOK()) { + LYXERR0("LayoutFileList::Read: unable to open textclass file `" + << makeDisplayPath(real_file.absFileName(), 1000) + << "'\nCheck your installation."); + } else { + // we have a file we can read. + bool finished = false; + LYXERR(Debug::TCLASS, "Starting parsing of textclass.lst"); + while (lex.isOK() && !finished) { + LYXERR(Debug::TCLASS, "\tline by line"); + switch (lex.lex()) { + case Lexer::LEX_FEOF: + finished = true; + break; + default: + string const fname = lex.getString(); + LYXERR(Debug::TCLASS, "Fname: " << fname); + if (!lex.next()) + break; string const clname = lex.getString(); LYXERR(Debug::TCLASS, "Clname: " << clname); - if (lex.next()) { - string const desc = lex.getString(); - LYXERR(Debug::TCLASS, "Desc: " << desc); - if (lex.next()) { - bool avail = lex.getBool(); - LYXERR(Debug::TCLASS, "Avail: " << avail); - // This code is run when we have - // fname, clname, desc, and avail - LayoutFile * tmpl = new LayoutFile(fname, clname, desc, avail); - if (lyxerr.debugging(Debug::TCLASS)) { - // only system layout files are loaded here so no - // buffer path is needed. - tmpl->load(); - } - classmap_[fname] = tmpl; - } + if (!lex.next()) + break; + string const desc = lex.getString(); + LYXERR(Debug::TCLASS, "Desc: " << desc); + if (!lex.next()) + break; + bool avail = lex.getBool(); + LYXERR(Debug::TCLASS, "Avail: " << avail); + if (!lex.next()) + break; + string const prereq = lex.getString(); + LYXERR(Debug::TCLASS, "Prereq: " << prereq); + if (!lex.next()) + break; + string const category = lex.getString(); + LYXERR(Debug::TCLASS, "Category: " << category); + // This code is run when we have + // fname, clname, desc, prereq, and avail + LayoutFile * tmpl = new LayoutFile(fname, clname, desc, prereq, category, avail); + if (lyxerr.debugging(Debug::TCLASS)) { + // only system layout files are loaded here so no + // buffer path is needed. + tmpl->load(); } - } - } - } - LYXERR(Debug::TCLASS, "End of parsing of textclass.lst"); - - // lyx will start with an empty classmap_, but only reconfigure is allowed - // in this case. This gives users a second chance to configure lyx if - // initial configuration fails. (c.f. bug 2829) + classmap_[fname] = tmpl; + } // end of switch + } // end of while loop + LYXERR(Debug::TCLASS, "End parsing of textclass.lst"); + success = true; + } // end of else + + // LyX will start with an empty classmap_. This is OK because + // (a) we will give the user a chance to reconfigure (see bug 2829) and + // (b) even if that fails, we can use addEmptyClass() to get some basic + // functionality. if (classmap_.empty()) - lyxerr << "LayoutFileList::Read: no textclasses found!" - << endl; - return true; + LYXERR0("LayoutFileList::Read: no textclasses found!"); + return success; } @@ -183,32 +185,27 @@ std::vector LayoutFileList::classList() const } -void LayoutFileList::reset(LayoutFileIndex const & classname) { - LASSERT(haveClass(classname), /**/); +void LayoutFileList::reset(LayoutFileIndex const & classname) +{ + LATTEST(haveClass(classname)); + // safe to continue, since we will make an empty LayoutFile LayoutFile * tc = classmap_[classname]; - LayoutFile * tmpl = + LayoutFile * tmpl = new LayoutFile(tc->name(), tc->latexname(), tc->description(), - tc->isTeXClassAvailable()); + tc->prerequisites(), tc->category(), + tc->isTeXClassAvailable()); classmap_[classname] = tmpl; delete tc; } -LayoutFileIndex LayoutFileList::addEmptyClass(string const & textclass) -{ - if (haveClass(textclass)) - return textclass; +namespace { - FileName const tempLayout = FileName::tempName(); - ofstream ofs(tempLayout.toFilesystemEncoding().c_str()); - ofs << "# This layout is automatically generated\n" - "# \\DeclareLaTeXClass{" << textclass << "}\n\n" - "Format 7\n" - "Input stdclass.inc\n\n" - "Columns 1\n" - "Sides 1\n" - "SecNumDepth 2\n" - "TocDepth 2\n" +string layoutpost = + "Columns 1\n" + "Sides 1\n" + "SecNumDepth 2\n" + "TocDepth 2\n" "DefaultStyle Standard\n\n" "Style Standard\n" " Category MainText\n" @@ -221,98 +218,164 @@ LayoutFileIndex LayoutFileList::addEmptyClass(string const & textclass) " AlignPossible Block, Left, Right, Center\n" " LabelType No_Label\n" "End\n"; + +} + + +LayoutFileIndex LayoutFileList::addEmptyClass(string const & textclass) +{ + // FIXME This could be simplified a bit to call TextClass::read(string, ReadType). + + TempFile tempfile("basicXXXXXX.layout"); + FileName const tempLayout = tempfile.name(); + ofstream ofs(tempLayout.toFilesystemEncoding().c_str()); + // This writes a very basic class, but it also attempts to include + // stdclass.inc. That would give us something moderately usable. + ofs << "# This layout is automatically generated\n" + "# \\DeclareLaTeXClass{" << textclass << "}\n\n" + "Format " << LAYOUT_FORMAT << "\n" + "Input stdclass.inc\n\n" + << layoutpost; ofs.close(); // We do not know if a LaTeX class is available for this document, but setting // the last parameter to true will suppress a warning message about missing // tex class. - LayoutFile * tc = new LayoutFile(textclass, textclass, "Unknown text class " + textclass, true); - if (!tc->load(tempLayout.absFilename())) - // The only way this happens is because the hardcoded layout file above - // is wrong. - LASSERT(false, /**/); + LayoutFile * tc = new LayoutFile(textclass, textclass, + "Unknown text class " + textclass, textclass + ".cls", "", true); + + if (!tc->load(tempLayout.absFileName())) { + // The only way this happens is because the hardcoded layout file + // above is wrong or stdclass.inc cannot be found. So try again + // without stdclass.inc and without stdinsets.inc. + ofstream ofs2(tempLayout.toFilesystemEncoding().c_str()); + ofs2 << "# This layout is automatically generated\n" + "# \\DeclareLaTeXClass{" << textclass << "}\n\n" + "Format " << LAYOUT_FORMAT << "\n" + "Provides stdinsets 1\n" + << layoutpost; + ofs2.close(); + if (!tc->load(tempLayout.absFileName())) { + // This can only happen if the hardcoded file above is wrong + // or there is some weird filesystem error. + LATTEST(false); // We will get an empty layout or something. + } + } + classmap_[textclass] = tc; return textclass; } -LayoutFileIndex - LayoutFileList::addLocalLayout(string const & textclass, string const & path) +LayoutFileIndex LayoutFileList::addLocalLayout( + string const & textclass, string const & path, string const & oldpath) { // FIXME There is a bug here: 4593 // // only check for textclass.layout file, .cls can be anywhere in $TEXINPUTS - // NOTE: latex class name is defined in textclass.layout, which can be + // NOTE: latex class name is defined in textclass.layout, which can be // different from textclass string fullName = addName(path, textclass + ".layout"); - - FileName const layout_file(fullName); - if (layout_file.exists()) { - LYXERR(Debug::TCLASS, "Adding class " << textclass << " from directory " << path); - // Read .layout file and get description, real latex classname etc - // - // This is a C++ version of function processLayoutFile in configure.py, - // which uses the following regex - // \Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)} - ifstream ifs(layout_file.toFilesystemEncoding().c_str()); - static regex const reg("^#\\s*\\\\Declare(LaTeX|DocBook)Class\\s*" - "(?:\\[([^,]*)(?:,.*)*\\])*\\s*\\{(.*)\\}\\s*"); - string line; - while (getline(ifs, line)) { - // look for the \DeclareXXXClass line - smatch sub; - if (regex_match(line, sub, reg)) { - // returns: whole string, classtype (not used here), class name, description - LASSERT(sub.size() == 4, /**/); - // now, create a TextClass with description containing path information - string className(sub.str(2) == "" ? textclass : sub.str(2)); - LayoutFile * tmpl = - new LayoutFile(textclass, className, textclass, true); - // This textclass is added on request so it will definitely be - // used. Load it now because other load() calls may fail if they - // are called in a context without buffer path information. - tmpl->load(path); - // There will be only one textclass with this name, even if different - // layout files are loaded from different directories. - if (haveClass(textclass)) { - LYXERR0("Existing textclass " << textclass << " is redefined by " << fullName); - delete classmap_[textclass]; - } - classmap_[textclass] = tmpl; - return textclass; - } - } + + FileName layout_file(fullName); + bool moved = false; + + if (!layout_file.exists()) { + if (oldpath.empty()) + return string(); + // The document has been moved to a different directory. + // However, oldpath always points to the right spot, unless + // the user also moved the layout file. + fullName = addName(oldpath, textclass + ".layout"); + layout_file.set(fullName); + layout_file.refresh(); + if (!layout_file.exists()) + return string(); + moved = true; } - // If .layout is not in local directory, or an invalid layout is found, return null - return string(); -} + LYXERR(Debug::TCLASS, "Adding class " << textclass << " from directory " << path); + // Read .layout file and get description, real latex classname etc + // + // This is a C++ version of function processLayoutFile in configure.py, + // which uses the following regex + // \Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)} + ifstream ifs(layout_file.toFilesystemEncoding().c_str()); + static regex const reg("^\\s*#\\s*\\\\Declare(LaTeX|DocBook)Class\\s*" + "(?:\\[([^,]*)(?:,.*)*\\])*\\s*\\{(.*)\\}\\s*"); + static regex const catreg("^\\s*#\\s*\\\\DeclareCategory\\{(.*)\\}\\s*"); + string line; + string class_name; + string class_prereq; + string category; + bool have_declaration = false; + while (getline(ifs, line)) { + // look for the \DeclareXXXClass line + smatch sub; + if (regex_match(line, sub, reg)) { + // returns: whole string, classtype (not used here), class name, description + // LASSERT: Why would this fail? + LASSERT(sub.size() == 4, /**/); + // now, create a TextClass with description containing path information + class_name = (sub.str(2) == "" ? textclass : sub.str(2)); + class_prereq = class_name + ".cls"; + have_declaration = true; + } + else if (regex_match(line, sub, catreg)) { + category = sub.str(1); + } + if (have_declaration && !category.empty()) + break; + } -LayoutFileIndex defaultBaseclass() -{ - if (LayoutFileList::get().haveClass("article")) - return string("article"); - if (LayoutFileList::get().empty()) + if (!have_declaration) return string(); - return LayoutFileList::get().classList().front(); -} + LayoutFile * tmpl = + new LayoutFile(addName(moved ? oldpath : path, textclass), + class_name, textclass, class_prereq, category, true); + //FIXME: The prerequisites are available from the layout file and + // can be extracted from the above regex, but for now this + // field is simply set to class_name + ".cls" + // This textclass is added on request so it will definitely be + // used. Load it now because other load() calls may fail if they + // are called in a context without buffer path information. + tmpl->load(moved ? oldpath : path); + // There will be only one textclass with this name, even if different + // layout files are loaded from different directories. + if (haveClass(textclass)) { + // Unconditionally issuing the warning may be confusing when + // saving the document with a different name, as it is exactly + // the same textclass that is being re-established. + LYXERR(Debug::TCLASS, "Existing textclass " << textclass << " is redefined by " << fullName); + delete classmap_[textclass]; + } + classmap_[textclass] = tmpl; + return removeExtension(fullName); +} -// Reads the style files -bool LyXSetStyle() +bool LayoutFileList::load(string const & name, string const & buf_path) { - LYXERR(Debug::TCLASS, "LyXSetStyle: parsing configuration..."); - - if (!LayoutFileList::get().read()) { - LYXERR(Debug::TCLASS, "LyXSetStyle: an error occured " - "during parsing.\n Exiting."); + if (!haveClass(name)) { + LYXERR0("Document class \"" << name << "\" does not exist."); return false; } - LYXERR(Debug::TCLASS, "LyXSetStyle: configuration parsed."); - return true; + LayoutFile * tc = classmap_[name]; + return tc->load(buf_path); } +LayoutFileIndex defaultBaseclass() +{ + if (LayoutFileList::get().haveClass("article")) + return string("article"); + if (LayoutFileList::get().empty()) + // we'll call it that, since this gives the user a chance to + // have a functioning document when things improve. + return string("article"); + return LayoutFileList::get().classList().front(); +} + } // namespace lyx