diff --git a/development/FORMAT b/development/FORMAT index a206b21a92..3f1b2a4bb9 100644 --- a/development/FORMAT +++ b/development/FORMAT @@ -1,6 +1,9 @@ LyX file-format changes ----------------------- +2008-03-24 Richard Heck + * Format incremented to 322: local layout + 2008-03-18 Edwin Leuven * Format incremented to 321: drop row/col lines and ensure consistency between cell and row/col lines. diff --git a/lib/lyx2lyx/LyX.py b/lib/lyx2lyx/LyX.py index f0aa236dbd..f811695250 100644 --- a/lib/lyx2lyx/LyX.py +++ b/lib/lyx2lyx/LyX.py @@ -80,7 +80,7 @@ format_relation = [("0_06", [200], minor_versions("0.6" , 4)), ("1_3", [221], minor_versions("1.3" , 7)), ("1_4", range(222,246), minor_versions("1.4" , 5)), ("1_5", range(246,277), minor_versions("1.5" , 2)), - ("1_6", range(277,322), minor_versions("1.6" , 0))] + ("1_6", range(277,323), minor_versions("1.6" , 0))] # rgh: local layout def formats_list(): diff --git a/lib/lyx2lyx/lyx_1_6.py b/lib/lyx2lyx/lyx_1_6.py index ef269added..c978d2c407 100644 --- a/lib/lyx2lyx/lyx_1_6.py +++ b/lib/lyx2lyx/lyx_1_6.py @@ -1631,6 +1631,21 @@ def revert_protected_hfill(document): 'hspace*{\n\\backslash\nfill}\n\\end_layout\n\n\\end_inset\n\n') +def revert_local_layout(document): + ' Revert local layout headers.' + i = 0 + while True: + i = find_token(document.header, "\\begin_local_layout", i) + if i == -1: + return + j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout") + if j == -1: + # this should not happen + break + document.header[i : j + 1] = [] + + + ## # Conversion hub # @@ -1680,10 +1695,12 @@ convert = [[277, [fix_wrong_tables]], [318, []], [319, [convert_spaceinset, convert_hfill]], [320, []], - [321, [convert_tablines]] + [321, [convert_tablines]], + [322, []] ] -revert = [[320, [revert_tablines]], +revert = [[321, [revert_local_layout]], + [320, [revert_tablines]], [319, [revert_protected_hfill]], [318, [revert_spaceinset, revert_hfills, revert_hspace]], [317, [remove_extra_embedded_files]], diff --git a/src/Buffer.cpp b/src/Buffer.cpp index d68ef97c7a..87830ef76e 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -116,7 +116,7 @@ namespace os = support::os; namespace { -int const LYX_FORMAT = 321; +int const LYX_FORMAT = 322; // rgh: local layout typedef map DepClean; typedef map > RefCache; diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index 5eda910e93..9302a35c29 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -506,6 +506,8 @@ string BufferParams::readToken(Lexer & lex, string const & token, } else if (token == "\\begin_preamble") { readPreamble(lex); + } else if (token == "\\begin_local_layout") { + readLocalLayout(lex); } else if (token == "\\begin_modules") { readModules(lex); } else if (token == "\\options") { @@ -722,6 +724,15 @@ void BufferParams::writeFile(ostream & os) const os << "\\end_modules" << '\n'; } + // local layout information + if (!local_layout.empty()) { + // remove '\n' from the end + string const tmplocal = rtrim(local_layout, "\n"); + os << "\\begin_local_layout\n" + << tmplocal + << "\n\\end_local_layout\n"; + } + // then the text parameters if (language != ignore_language) os << "\\language " << language->lang() << '\n'; @@ -1501,6 +1512,12 @@ void BufferParams::makeDocumentClass() frontend::Alert::warning(_("Read Error"), msg); } } + if (!local_layout.empty()) { + if (!doc_class_->read(local_layout, TextClass::MODULE)) { + docstring const msg = _("Error reading internal layout information"); + frontend::Alert::warning(_("Read Error"), msg); + } + } } @@ -1555,6 +1572,16 @@ void BufferParams::readPreamble(Lexer & lex) } +void BufferParams::readLocalLayout(Lexer & lex) +{ + if (lex.getString() != "\\begin_local_layout") + lyxerr << "Error (BufferParams::readLocalLayout):" + "consistency check failed." << endl; + + local_layout = lex.getLongString("\\end_local_layout"); +} + + void BufferParams::readLanguage(Lexer & lex) { if (!lex.next()) return; diff --git a/src/BufferParams.h b/src/BufferParams.h index 3ca3a735e1..55749646c5 100644 --- a/src/BufferParams.h +++ b/src/BufferParams.h @@ -220,6 +220,8 @@ public: /// std::string preamble; /// + std::string local_layout; + /// std::string options; /// std::string float_placement; @@ -316,6 +318,8 @@ private: /// void readPreamble(Lexer &); /// + void readLocalLayout(Lexer &); + /// void readLanguage(Lexer &); /// void readGraphicsDriver(Lexer &); diff --git a/src/TextClass.cpp b/src/TextClass.cpp index 13adf04675..9b5ff07431 100644 --- a/src/TextClass.cpp +++ b/src/TextClass.cpp @@ -36,6 +36,7 @@ #include "support/os.h" #include +#include #include #include "boost/assert.hpp" @@ -101,6 +102,8 @@ std::string translateRT(TextClass::ReadType rt) return "input file"; case TextClass::MODULE: return "module file"; + case TextClass::VALIDATION: + return "validation"; } // shutup warning return string(); @@ -180,14 +183,7 @@ enum TextClassTags { }; -// Reads a textclass structure from file. -bool TextClass::read(FileName const & filename, ReadType rt) -{ - if (!filename.isReadableFile()) { - lyxerr << "Cannot read layout file `" << filename << "'." - << endl; - return false; - } +namespace { keyword_item textClassTags[] = { { "classoptions", TC_CLASSOPTIONS }, @@ -216,6 +212,28 @@ bool TextClass::read(FileName const & filename, ReadType rt) { "titlelatextype", TC_TITLELATEXTYPE }, { "tocdepth", TC_TOCDEPTH } }; + +} //namespace anon + + +bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt) +{ + LYXERR(Debug::TCLASS, "Converting layout file to " << FORMAT); + FileName const tempfile = FileName::tempName(); + bool success = layout2layout(filename, tempfile); + if (success) + success = read(tempfile, rt); + tempfile.removeFile(); + return success; +} + +bool TextClass::read(FileName const & filename, ReadType rt) +{ + if (!filename.isReadableFile()) { + lyxerr << "Cannot read layout file `" << filename << "'." + << endl; + return false; + } LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " + to_utf8(makeDisplayPath(filename.absFilename()))); @@ -223,7 +241,7 @@ bool TextClass::read(FileName const & filename, ReadType rt) // Define the `empty' layout used in table cells, ert, etc. Note that // we do this before loading any layout file, so that classes can // override features of this layout if they should choose to do so. - if (rt == BASECLASS) { + if (rt == BASECLASS && !hasLayout(emptylayout_)) { static char const * s = "Margin Static\n" "LatexType Paragraph\n" "LatexName dummy\n" @@ -243,11 +261,71 @@ bool TextClass::read(FileName const & filename, ReadType rt) }; layoutlist_.push_back(lay); } - Lexer lexrc(textClassTags, sizeof(textClassTags) / sizeof(textClassTags[0])); lexrc.setFile(filename); + ReturnValues retval = read(lexrc, rt); + + LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " + + to_utf8(makeDisplayPath(filename.absFilename()))); + + if (retval != FORMAT_MISMATCH) + return retval == OK; + + bool const worx = convertLayoutFormat(filename, rt); + if (!worx) { + lyxerr << "Unable to convert " << filename << + " to format " << FORMAT << std::endl; + return false; + } + return true; +} + + +bool TextClass::validate(std::string const & str) +{ + TextClass tc; + return tc.read(str, VALIDATION); +} + + +bool TextClass::read(std::string const & str, ReadType rt) +{ + Lexer lexrc(textClassTags, + sizeof(textClassTags) / sizeof(textClassTags[0])); + istringstream is(str); + lexrc.setStream(is); + ReturnValues retval = read(lexrc, rt); + + if (retval != FORMAT_MISMATCH) + return retval == OK; + + // write the layout string to a temporary file + FileName const tempfile = FileName::tempName(); + ofstream os(tempfile.toFilesystemEncoding().c_str()); + if (!os) { + lyxerr << "Unable to create tempoary file in TextClass::read!!" + << std::endl; + return false; + } + os << str; + os.close(); + + // now try to convert it + bool const worx = convertLayoutFormat(tempfile, rt); + if (!worx) { + lyxerr << "Unable to convert internal layout information to format " + << FORMAT << std::endl; + } + tempfile.removeFile(); + return worx; +} + + +// Reads a textclass structure from file. +TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt) +{ bool error = !lexrc.isOK(); // Format of files before the 'Format' tag was introduced @@ -316,9 +394,8 @@ bool TextClass::read(FileName const & filename, ReadType rt) + lexrc.getString() + " is probably not valid UTF-8!"; lexrc.printError(s.c_str()); Layout lay; - //FIXME If we're just dropping this layout, do we really - //care whether there's an error?? Or should we just set - //error to true, since we couldn't even read the name? + // Since we couldn't read the name, we just scan the rest + // of the style and discard it. error = !readStyle(lexrc, lay); } else if (hasLayout(name)) { Layout & lay = operator[](name); @@ -482,22 +559,11 @@ bool TextClass::read(FileName const & filename, ReadType rt) break; } - if (format != FORMAT) { - LYXERR(Debug::TCLASS, "Converting layout file from format " - << format << " to " << FORMAT); - FileName const tempfile = FileName::tempName(); - bool success = layout2layout(filename, tempfile); - if (success) - read(tempfile, rt); - tempfile.removeFile(); - return success; - } - - LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " + - to_utf8(makeDisplayPath(filename.absFilename()))); + if (format != FORMAT) + return FORMAT_MISMATCH; if (rt != BASECLASS) - return !error; + return (error ? ERROR : OK); if (defaultlayout_.empty()) { lyxerr << "Error: Textclass '" << name_ @@ -547,7 +613,7 @@ bool TextClass::read(FileName const & filename, ReadType rt) LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_ << ", maximum is " << max_toclevel_); - return !error; + return (error ? ERROR : OK); } diff --git a/src/TextClass.h b/src/TextClass.h index 67293610ce..71a2241a40 100644 --- a/src/TextClass.h +++ b/src/TextClass.h @@ -127,11 +127,25 @@ public: enum ReadType { BASECLASS, //>This is a base class, i.e., top-level layout file MERGE, //>This is a file included in a layout file - MODULE //>This is a layout module + MODULE, //>This is a layout module + VALIDATION //>We're just validating }; + /// return values for read() + enum ReturnValues { + OK, + ERROR, + FORMAT_MISMATCH + }; + /// Performs the read of the layout file. /// \return true on success. bool read(support::FileName const & filename, ReadType rt = BASECLASS); + /// + bool read(std::string const & str, ReadType rt = BASECLASS); + /// + ReturnValues read(Lexer & lex, ReadType rt = BASECLASS); + /// validates the layout information passed in str + static bool validate(std::string const & str); /////////////////////////////////////////////////////////////////// // loading @@ -242,6 +256,8 @@ private: /////////////////////////////////////////////////////////////////// /// bool deleteLayout(docstring const &); + /// + bool convertLayoutFormat(support::FileName const &, ReadType); /// \return true for success. bool readStyle(Lexer &, Layout &); /// diff --git a/src/frontends/qt4/GuiDocument.cpp b/src/frontends/qt4/GuiDocument.cpp index 327d2d367b..3c6810d81a 100644 --- a/src/frontends/qt4/GuiDocument.cpp +++ b/src/frontends/qt4/GuiDocument.cpp @@ -1296,6 +1296,10 @@ void GuiDocument::classChanged() return; } } + // FIXME Note that by doing things this way, we load the TextClass + // as soon as it is selected. So, if you use the scroll wheel when + // sitting on the combo box, we'll load a lot of TextClass objects + // very quickly. This could be changed. if (!bp_.setBaseClass(classname)) { Alert::error(_("Error"), _("Unable to set document class.")); return;