]> git.lyx.org Git - lyx.git/blobdiff - src/paragraph_funcs.C
fix some C++ parsing bugs
[lyx.git] / src / paragraph_funcs.C
index 2bc417722464ac5af9947a4e3e05a304f7b39e98..36b4cc0337b66165ea4dd83e25a431ad52aeb08b 100644 (file)
 #include "ParagraphParameters.h"
 #include "lyxtextclasslist.h"
 #include "debug.h"
+#include "gettext.h"
 #include "language.h"
 #include "encoding.h"
 #include "lyxrc.h"
+#include "lyxlex.h"
+#include "BoostFormat.h"
+#include "factory.h"
 #include "support/lstrings.h"
 #include "insets/insetoptarg.h"
+#include "insets/insetcommandparams.h"
+#include "insets/insetbibitem.h"
+#include "insets/insetspecialchar.h"
+#include "insets/insetlatexaccent.h"
+#include "insets/insettabular.h"
+#include "insets/insethfill.h"
+#include "insets/inseterror.h"
+#include "insets/insetnewline.h"
 
 extern string bibitemWidest(Buffer const *);
 
@@ -30,12 +42,15 @@ using std::endl;
 using std::ostream;
 
 void breakParagraph(BufferParams const & bparams,
+                   ParagraphList & paragraphs,
                    ParagraphList::iterator par,
                    pos_type pos,
                    int flag)
 {
        // create a new paragraph, and insert into the list
-       Paragraph * tmp = new Paragraph(&*par);
+       ParagraphList::iterator tmp = paragraphs.insert(boost::next(par),
+                                                       new Paragraph);
+
        // without doing that we get a crash when typing <Return> at the
        // end of a paragraph
        tmp->layout(bparams.getLyXTextClass().defaultLayout());
@@ -127,11 +142,13 @@ void breakParagraph(BufferParams const & bparams,
 
 
 void breakParagraphConservative(BufferParams const & bparams,
+                               ParagraphList & paragraphs,
                                ParagraphList::iterator par,
                                pos_type pos)
 {
        // create a new paragraph
-       Paragraph * tmp = new Paragraph(&*par);
+       ParagraphList::iterator tmp = paragraphs.insert(boost::next(par),
+                                                       new Paragraph);
        tmp->makeSameLayout(&*par);
 
        // When can pos > Last()?
@@ -154,7 +171,9 @@ void breakParagraphConservative(BufferParams const & bparams,
 }
 
 
-void mergeParagraph(BufferParams const & bparams, ParagraphList::iterator par)
+void mergeParagraph(BufferParams const & bparams,
+                   ParagraphList & paragraphs,
+                   ParagraphList::iterator par)
 {
        ParagraphList::iterator the_next = boost::next(par);
 
@@ -173,13 +192,7 @@ void mergeParagraph(BufferParams const & bparams, ParagraphList::iterator par)
                        ++j;
        }
 
-       // delete the next paragraph
-#warning a ParagraphList::erase is needed. (Lgb)
-       // Isn't this really just par?
-       ParagraphList::iterator ppar = boost::prior(the_next);
-       ParagraphList::iterator npar = boost::next(the_next);
-       delete &*the_next;
-       ppar->next(&*npar);
+       paragraphs.erase(the_next);
 }
 
 
@@ -249,21 +262,37 @@ int getEndLabel(Paragraph * para, BufferParams const & bparams)
 #endif
 
 
+namespace {
+
+ParagraphList::iterator
+TeXEnvironment(Buffer const * buf,
+              ParagraphList const & paragraphs,
+              ParagraphList::iterator pit,
+              ostream & os, TexRow & texrow);
+
+ParagraphList::iterator
+TeXOnePar(Buffer const * buf,
+         ParagraphList const & paragraphs,
+         ParagraphList::iterator pit,
+         ostream & os, TexRow & texrow,
+         bool moving_arg);
+
+
 ParagraphList::iterator
 TeXDeeper(Buffer const * buf,
-         BufferParams const & bparams,
+         ParagraphList const & paragraphs,
          ParagraphList::iterator pit,
          ostream & os, TexRow & texrow)
 {
        lyxerr[Debug::LATEX] << "TeXDeeper...     " << &*pit << endl;
        ParagraphList::iterator par = pit;
 
-       while (par != buf->paragraphs.end()&& par->params().depth() == pit->params().depth()) {
+       while (par != paragraphs.end()&& par->params().depth() == pit->params().depth()) {
                if (par->layout()->isEnvironment()) {
-                       par = TeXEnvironment(buf, bparams, par,
+                       par = TeXEnvironment(buf, paragraphs, par,
                                                  os, texrow);
                } else {
-                       par = TeXOnePar(buf, bparams, par,
+                       par = TeXOnePar(buf, paragraphs, par,
                                             os, texrow, false);
                }
        }
@@ -275,18 +304,22 @@ TeXDeeper(Buffer const * buf,
 
 ParagraphList::iterator
 TeXEnvironment(Buffer const * buf,
-              BufferParams const & bparams,
+              ParagraphList const & paragraphs,
               ParagraphList::iterator pit,
               ostream & os, TexRow & texrow)
 {
        lyxerr[Debug::LATEX] << "TeXEnvironment...     " << &*pit << endl;
 
+       BufferParams const & bparams = buf->params;
+
        LyXLayout_ptr const & style = pit->layout();
 
        Language const * language = pit->getParLanguage(bparams);
        Language const * doc_language = bparams.language;
-       Language const * previous_language = pit->previous()
-               ? pit->previous()->getParLanguage(bparams) : doc_language;
+       Language const * previous_language =
+               (pit != paragraphs.begin())
+               ? boost::prior(pit)->getParLanguage(bparams)
+               : doc_language;
        if (language->babel() != previous_language->babel()) {
 
                if (!lyxrc.language_command_end.empty() &&
@@ -333,9 +366,9 @@ TeXEnvironment(Buffer const * buf,
        }
        ParagraphList::iterator par = pit;
        do {
-               par = TeXOnePar(buf, bparams, par, os, texrow, false);
+               par = TeXOnePar(buf, paragraphs, par, os, texrow, false);
 
-               if (par != buf->paragraphs.end()&& par->params().depth() > pit->params().depth()) {
+               if (par != paragraphs.end()&& par->params().depth() > pit->params().depth()) {
                            if (par->layout()->isParagraph()) {
 
                            // Thinko!
@@ -355,9 +388,9 @@ TeXEnvironment(Buffer const * buf,
                                os << '\n';
                                texrow.newline();
                        }
-                       par = TeXDeeper(buf, bparams, par, os, texrow);
+                       par = TeXDeeper(buf, paragraphs, par, os, texrow);
                }
-       } while (par != buf->paragraphs.end()
+       } while (par != paragraphs.end()
                 && par->layout() == pit->layout()
                 && par->params().depth() == pit->params().depth()
                 && par->params().leftIndent() == pit->params().leftIndent());
@@ -377,8 +410,6 @@ TeXEnvironment(Buffer const * buf,
 }
 
 
-namespace {
-
 InsetOptArg * optArgInset(Paragraph const & par)
 {
        // Find the entry.
@@ -393,17 +424,17 @@ InsetOptArg * optArgInset(Paragraph const & par)
        return 0;
 }
 
-} // end namespace
-
 
 ParagraphList::iterator
 TeXOnePar(Buffer const * buf,
-         BufferParams const & bparams,
+         ParagraphList const & paragraphs,
          ParagraphList::iterator pit,
          ostream & os, TexRow & texrow,
          bool moving_arg)
 {
        lyxerr[Debug::LATEX] << "TeXOnePar...     " << &*pit << endl;
+       BufferParams const & bparams = buf->params;
+
        Inset const * in = pit->inInset();
        bool further_blank_line = false;
        LyXLayout_ptr style;
@@ -421,7 +452,7 @@ TeXOnePar(Buffer const * buf,
                }
 
                if (!pit->params().spacing().isDefault()
-                       && (!pit->previous() || !pit->previous()->hasSameLayout(&*pit))) {
+                       && (pit == paragraphs.begin() || !boost::prior(pit)->hasSameLayout(&*pit))) {
                        os << pit->params().spacing().writeEnvirBegin() << '\n';
                        texrow.newline();
                }
@@ -456,16 +487,18 @@ TeXOnePar(Buffer const * buf,
 
        Language const * language = pit->getParLanguage(bparams);
        Language const * doc_language = bparams.language;
-       Language const * previous_language = pit->previous()
-               ? pit->previous()->getParLanguage(bparams) : doc_language;
+       Language const * previous_language =
+               (pit != paragraphs.begin())
+               ? boost::prior(pit)->getParLanguage(bparams)
+               : doc_language;
 
        if (language->babel() != previous_language->babel()
            // check if we already put language command in TeXEnvironment()
            && !(style->isEnvironment()
-                && (!pit->previous() ||
-                    (pit->previous()->layout() != pit->layout() &&
-                     pit->previous()->getDepth() <= pit->getDepth())
-                    || pit->previous()->getDepth() < pit->getDepth())))
+                && (pit == paragraphs.begin() ||
+                    (boost::prior(pit)->layout() != pit->layout() &&
+                     boost::prior(pit)->getDepth() <= pit->getDepth())
+                    || boost::prior(pit)->getDepth() < pit->getDepth())))
        {
                if (!lyxrc.language_command_end.empty() &&
                    previous_language->babel() != doc_language->babel())
@@ -535,7 +568,9 @@ TeXOnePar(Buffer const * buf,
 
        bool is_command = style->isCommand();
 
-       if (style->resfont.size() != font.size() && pit->next() && !is_command) {
+       if (style->resfont.size() != font.size()
+           && boost::next(pit) != paragraphs.end()
+           && !is_command) {
                if (!need_par)
                        os << '{';
                os << "\\" << font.latexSize() << " \\par}";
@@ -547,7 +582,8 @@ TeXOnePar(Buffer const * buf,
        switch (style->latextype) {
        case LATEX_ITEM_ENVIRONMENT:
        case LATEX_LIST_ENVIRONMENT:
-               if (pit->next() && (pit->params().depth() < pit->next()->params().depth())) {
+               if (boost::next(pit) != paragraphs.end()
+                   && (pit->params().depth() < boost::next(pit)->params().depth())) {
                        os << '\n';
                        texrow.newline();
                }
@@ -555,14 +591,14 @@ TeXOnePar(Buffer const * buf,
        case LATEX_ENVIRONMENT:
                // if its the last paragraph of the current environment
                // skip it otherwise fall through
-               if (pit->next()
-                   && (pit->next()->layout() != pit->layout()
-                       || pit->next()->params().depth() != pit->params().depth()))
+               if (boost::next(pit) != paragraphs.end()
+                   && (boost::next(pit)->layout() != pit->layout()
+                       || boost::next(pit)->params().depth() != pit->params().depth()))
                        break;
                // fall through possible
        default:
                // we don't need it for the last paragraph!!!
-               if (pit->next()) {
+               if (boost::next(pit) != paragraphs.end()) {
                        os << '\n';
                        texrow.newline();
                }
@@ -591,14 +627,14 @@ TeXOnePar(Buffer const * buf,
                }
 
                if (!pit->params().spacing().isDefault()
-                       && (!pit->next() || !pit->next()->hasSameLayout(&*pit))) {
+                       && (boost::next(pit) == paragraphs.end()|| !boost::next(pit)->hasSameLayout(&*pit))) {
                        os << pit->params().spacing().writeEnvirEnd() << '\n';
                        texrow.newline();
                }
        }
 
        // we don't need it for the last paragraph!!!
-       if (pit->next()) {
+       if (boost::next(pit) != paragraphs.end()) {
                os << '\n';
                texrow.newline();
        } else {
@@ -621,6 +657,370 @@ TeXOnePar(Buffer const * buf,
                }
        }
 
-       lyxerr[Debug::LATEX] << "TeXOnePar...done " << pit->next() << endl;
+       lyxerr[Debug::LATEX] << "TeXOnePar...done " << &*boost::next(pit) << endl;
        return ++pit;
 }
+
+} // anon namespace
+
+
+//
+// LaTeX all paragraphs from par to endpar, if endpar == 0 then to the end
+//
+void latexParagraphs(Buffer const * buf,
+                    ParagraphList const & paragraphs,
+                    ParagraphList::iterator par,
+                    ParagraphList::iterator endpar,
+                    ostream & ofs,
+                    TexRow & texrow,
+                    bool moving_arg)
+{
+       bool was_title = false;
+       bool already_title = false;
+       LyXTextClass const & tclass = buf->params.getLyXTextClass();
+
+       // if only_body
+       while (par != endpar) {
+               Inset * in = par->inInset();
+               // well we have to check if we are in an inset with unlimited
+               // length (all in one row) if that is true then we don't allow
+               // any special options in the paragraph and also we don't allow
+               // any environment other then "Standard" to be valid!
+               if ((in == 0) || !in->forceDefaultParagraphs(in)) {
+                       LyXLayout_ptr const & layout = par->layout();
+
+                       if (layout->intitle) {
+                               if (already_title) {
+                                       lyxerr <<"Error in latexParagraphs: You"
+                                               " should not mix title layouts"
+                                               " with normal ones." << endl;
+                               } else if (!was_title) {
+                                       was_title = true;
+                                       if (tclass.titletype() == TITLE_ENVIRONMENT) {
+                                               ofs << "\\begin{"
+                                                   << tclass.titlename()
+                                                   << "}\n";
+                                               texrow.newline();
+                                       }
+                               }
+                       } else if (was_title && !already_title) {
+                               if (tclass.titletype() == TITLE_ENVIRONMENT) {
+                                       ofs << "\\end{" << tclass.titlename()
+                                           << "}\n";
+                               }
+                               else {
+                                       ofs << "\\" << tclass.titlename()
+                                           << "\n";
+                               }
+                               texrow.newline();
+                               already_title = true;
+                               was_title = false;
+                       }
+
+                       if (layout->isEnvironment() ||
+                               !par->params().leftIndent().zero())
+                       {
+                               par = TeXEnvironment(buf, paragraphs, par, ofs, texrow);
+                       } else {
+                               par = TeXOnePar(buf, paragraphs, par, ofs, texrow, moving_arg);
+                       }
+               } else {
+                       par = TeXOnePar(buf, paragraphs, par, ofs, texrow, moving_arg);
+               }
+       }
+       // It might be that we only have a title in this document
+       if (was_title && !already_title) {
+               if (tclass.titletype() == TITLE_ENVIRONMENT) {
+                       ofs << "\\end{" << tclass.titlename()
+                           << "}\n";
+               }
+               else {
+                       ofs << "\\" << tclass.titlename()
+                           << "\n";
+                               }
+               texrow.newline();
+       }
+}
+
+
+namespace {
+
+int readParToken(Buffer & buf, Paragraph & par, LyXLex & lex, string const & token)
+{
+       static LyXFont font;
+       static Change change;
+
+       BufferParams const & bp = buf.params;
+
+       if (token[0] != '\\') {
+               string::const_iterator cit = token.begin();
+               for (; cit != token.end(); ++cit) {
+                       par.insertChar(par.size(), (*cit), font, change);
+               }
+       } else if (token == "\\layout") {
+               lex.eatLine();
+               string layoutname = lex.getString();
+
+               font = LyXFont(LyXFont::ALL_INHERIT, bp.language);
+               change = Change();
+
+               LyXTextClass const & tclass = bp.getLyXTextClass();
+
+               if (layoutname.empty()) {
+                       layoutname = tclass.defaultLayoutName();
+               }
+
+               bool hasLayout = tclass.hasLayout(layoutname);
+
+               if (!hasLayout) {
+                       lyxerr << "Layout '" << layoutname << "' does not"
+                              << " exist in textclass '" << tclass.name()
+                              << "'." << endl;
+                       lyxerr << "Trying to use default layout instead."
+                              << endl;
+                       layoutname = tclass.defaultLayoutName();
+               }
+
+#ifdef USE_CAPTION
+               // The is the compability reading of layout caption.
+               if (compare_ascii_no_case(layoutname, "caption") == 0) {
+                       // We expect that the par we are now working on is
+                       // really inside a InsetText inside a InsetFloat.
+                       // We also know that captions can only be
+                       // one paragraph. (Lgb)
+
+                       // We should now read until the next "\layout"
+                       // is reached.
+                       // This is probably not good enough, what if the
+                       // caption is the last par in the document (Lgb)
+                       istream & ist = lex.getStream();
+                       stringstream ss;
+                       string line;
+                       int begin = 0;
+                       while (true) {
+                               getline(ist, line);
+                               if (prefixIs(line, "\\layout")) {
+                                       lex.pushToken(line);
+                                       break;
+                               }
+                               if (prefixIs(line, "\\begin_inset"))
+                                       ++begin;
+                               if (prefixIs(line, "\\end_inset")) {
+                                       if (begin)
+                                               --begin;
+                                       else {
+                                               lex.pushToken(line);
+                                               break;
+                                       }
+                               }
+
+                               ss << line << '\n';
+                       }
+
+                       // Now we should have the whole layout in ss
+                       // we should now be able to give this to the
+                       // caption inset.
+                       ss << "\\end_inset\n";
+
+                       // This seems like a bug in stringstream.
+                       // We really should be able to use ss
+                       // directly. (Lgb)
+                       istringstream is(ss.str());
+                       LyXLex tmplex(0, 0);
+                       tmplex.setStream(is);
+                       Inset * inset = new InsetCaption;
+                       inset->Read(this, tmplex);
+                       par.insertInset(pos, inset, font);
+                       ++pos;
+               } else {
+#endif
+                       par.layout(bp.getLyXTextClass()[layoutname]);
+
+                       // Test whether the layout is obsolete.
+                       LyXLayout_ptr const & layout = par.layout();
+                       if (!layout->obsoleted_by().empty())
+                               par.layout(bp.getLyXTextClass()[layout->obsoleted_by()]);
+
+                       par.params().read(lex);
+#if USE_CAPTION
+               }
+#endif
+
+       } else if (token == "\\end_inset") {
+               lyxerr << "Solitary \\end_inset in line " << lex.getLineNo() << "\n"
+                      << "Missing \\begin_inset?.\n";
+               // Simply ignore this. The insets do not have
+               // to read this.
+               // But insets should read it, it is a part of
+               // the inset isn't it? Lgb.
+       } else if (token == "\\begin_inset") {
+               Inset * i = readInset(lex, buf);
+               par.insertInset(par.size(), i, font, change);
+       } else if (token == "\\family") {
+               lex.next();
+               font.setLyXFamily(lex.getString());
+       } else if (token == "\\series") {
+               lex.next();
+               font.setLyXSeries(lex.getString());
+       } else if (token == "\\shape") {
+               lex.next();
+               font.setLyXShape(lex.getString());
+       } else if (token == "\\size") {
+               lex.next();
+               font.setLyXSize(lex.getString());
+       } else if (token == "\\lang") {
+               lex.next();
+               string const tok = lex.getString();
+               Language const * lang = languages.getLanguage(tok);
+               if (lang) {
+                       font.setLanguage(lang);
+               } else {
+                       font.setLanguage(bp.language);
+                       lex.printError("Unknown language `$$Token'");
+               }
+       } else if (token == "\\numeric") {
+               lex.next();
+               font.setNumber(font.setLyXMisc(lex.getString()));
+       } else if (token == "\\emph") {
+               lex.next();
+               font.setEmph(font.setLyXMisc(lex.getString()));
+       } else if (token == "\\bar") {
+               lex.next();
+               string const tok = lex.getString();
+
+               if (tok == "under")
+                       font.setUnderbar(LyXFont::ON);
+               else if (tok == "no")
+                       font.setUnderbar(LyXFont::OFF);
+               else if (tok == "default")
+                       font.setUnderbar(LyXFont::INHERIT);
+               else
+                       lex.printError("Unknown bar font flag "
+                                      "`$$Token'");
+       } else if (token == "\\noun") {
+               lex.next();
+               font.setNoun(font.setLyXMisc(lex.getString()));
+       } else if (token == "\\color") {
+               lex.next();
+               font.setLyXColor(lex.getString());
+       } else if (token == "\\SpecialChar") {
+               LyXLayout_ptr const & layout = par.layout();
+
+               // Insets don't make sense in a free-spacing context! ---Kayvan
+               if (layout->free_spacing || par.isFreeSpacing()) {
+                       if (lex.isOK()) {
+                               lex.next();
+                               string const next_token = lex.getString();
+                               if (next_token == "\\-") {
+                                       par.insertChar(par.size(), '-', font, change);
+                               } else if (next_token == "~") {
+                                       par.insertChar(par.size(), ' ', font, change);
+                               } else {
+                                       lex.printError("Token `$$Token' "
+                                                      "is in free space "
+                                                      "paragraph layout!");
+                               }
+                       }
+               } else {
+                       Inset * inset = new InsetSpecialChar;
+                       inset->read(&buf, lex);
+                       par.insertInset(par.size(), inset, font, change);
+               }
+       } else if (token == "\\i") {
+               Inset * inset = new InsetLatexAccent;
+               inset->read(&buf, lex);
+               par.insertInset(par.size(), inset, font, change);
+       } else if (token == "\\backslash") {
+               par.insertChar(par.size(), '\\', font, change);
+       } else if (token == "\\newline") {
+               Inset * inset = new InsetNewline;
+               inset->read(&buf, lex);
+               par.insertInset(par.size(), inset, font, change);
+       } else if (token == "\\LyXTable") {
+               Inset * inset = new InsetTabular(buf);
+               inset->read(&buf, lex);
+               par.insertInset(par.size(), inset, font, change);
+       } else if (token == "\\bibitem") {
+               InsetCommandParams p("bibitem", "dummy");
+               InsetBibitem * inset = new InsetBibitem(p);
+               inset->read(&buf, lex);
+               par.insertInset(par.size(), inset, font, change);
+       } else if (token == "\\hfill") {
+               par.insertInset(par.size(), new InsetHFill(), font, change);
+       } else if (token == "\\change_unchanged") {
+               // Hack ! Needed for empty paragraphs :/
+               // FIXME: is it still ??
+               if (!par.size())
+                       par.cleanChanges();
+               change = Change(Change::UNCHANGED);
+       } else if (token == "\\change_inserted") {
+               lex.nextToken();
+               istringstream istr(lex.getString());
+               int aid;
+               lyx::time_type ct;
+               istr >> aid;
+               istr >> ct;
+               change = Change(Change::INSERTED, bp.author_map[aid], ct);
+       } else if (token == "\\change_deleted") {
+               lex.nextToken();
+               istringstream istr(lex.getString());
+               int aid;
+               lyx::time_type ct;
+               istr >> aid;
+               istr >> ct;
+               change = Change(Change::DELETED, bp.author_map[aid], ct);
+       } else {
+               lex.eatLine();
+#if USE_BOOST_FORMAT
+               boost::format fmt(_("Unknown token: %1$s %2$s\n"));
+               fmt % token % lex.text();
+               string const s = fmt.str();
+#else
+               string const s = _("Unknown token: ") + token
+                       + ' ' + lex.text() + '\n';
+#endif
+               // we can do this here this way because we're actually reading
+               // the buffer and don't care about LyXText right now.
+               InsetError * inset = new InsetError(s);
+               par.insertInset(par.size(), inset, font);
+               return 1;
+       }
+       return 0;
+}
+
+}
+
+
+int readParagraph(Buffer & buf, Paragraph & par, LyXLex & lex)
+{
+       int unknown = 0;
+
+       lex.nextToken();
+       string token = lex.getString();
+
+       while (lex.isOK()) {
+
+               unknown += readParToken(buf, par, lex, token);
+
+               lex.nextToken();
+               token = lex.getString();
+
+               if (token.empty())
+                       continue;
+
+               lyxerr[Debug::PARSER] << "Handling paragraph token: `"
+                                     << token << '\'' << endl;
+
+               // reached the next paragraph. FIXME: really we should
+               // change the file format to indicate the end of a par
+               // clearly, but for now, this hack will do
+               if (token == "\\layout" || token == "\\the_end"
+                   || token == "\\end_inset" || token == "\\begin_deeper"
+                   || token == "\\end_deeper") {
+                       lex.pushToken(token);
+                       break;
+               }
+       }
+
+       return unknown;
+}