]> git.lyx.org Git - lyx.git/blob - src/paragraph_funcs.C
d2c6cef87fcfbd1e790e62084930c70f4fa586a9
[lyx.git] / src / paragraph_funcs.C
1 /**
2  * \file paragraph_funcs.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "paragraph_funcs.h"
14
15 #include "buffer.h"
16 #include "bufferparams.h"
17
18 #include "debug.h"
19 #include "encoding.h"
20 #include "errorlist.h"
21 #include "factory.h"
22 #include "gettext.h"
23 #include "iterators.h"
24 #include "language.h"
25 #include "latexrunparams.h"
26 #include "lyxlex.h"
27 #include "lyxrc.h"
28 #include "paragraph_pimpl.h"
29 #include "sgml.h"
30 #include "texrow.h"
31 #include "vspace.h"
32
33 #include "insets/insetbibitem.h"
34 #include "insets/insethfill.h"
35 #include "insets/insetlatexaccent.h"
36 #include "insets/insetline.h"
37 #include "insets/insetnewline.h"
38 #include "insets/insetpagebreak.h"
39 #include "insets/insetoptarg.h"
40 #include "insets/insetspace.h"
41 #include "insets/insetspecialchar.h"
42 #include "insets/insettabular.h"
43
44 #include "support/filetools.h"
45 #include "support/lstrings.h"
46 #include "support/lyxlib.h"
47 #include "support/std_sstream.h"
48
49 #include <vector>
50
51 using lyx::pos_type;
52
53 using lyx::support::ascii_lowercase;
54 using lyx::support::atoi;
55 using lyx::support::bformat;
56 using lyx::support::compare_ascii_no_case;
57 using lyx::support::compare_no_case;
58 using lyx::support::contains;
59 using lyx::support::split;
60 using lyx::support::subst;
61
62 using std::auto_ptr;
63 using std::endl;
64 using std::string;
65 using std::vector;
66 using std::istringstream;
67 using std::ostream;
68 using std::pair;
69
70 extern string bibitemWidest(Buffer const &);
71
72
73 namespace {
74
75 bool moveItem(Paragraph & from, Paragraph & to,
76         BufferParams const & params, pos_type i, pos_type j)
77 {
78         char const tmpchar = from.getChar(i);
79         LyXFont tmpfont = from.getFontSettings(params, i);
80
81         if (tmpchar == Paragraph::META_INSET) {
82                 InsetOld * tmpinset = 0;
83                 if (from.getInset(i)) {
84                         // the inset is not in a paragraph anymore
85                         tmpinset = from.insetlist.release(i);
86                 }
87
88                 if (!to.insetAllowed(tmpinset->lyxCode()))
89                         return false;
90                 to.insertInset(j, tmpinset, tmpfont);
91         } else {
92                 if (!to.checkInsertChar(tmpfont))
93                         return false;
94                 to.insertChar(j, tmpchar, tmpfont);
95         }
96         return true;
97 }
98
99 }
100
101
102 void breakParagraph(BufferParams const & bparams,
103                     ParagraphList & paragraphs,
104                     ParagraphList::iterator par,
105                     pos_type pos,
106                     int flag)
107 {
108         // create a new paragraph, and insert into the list
109         ParagraphList::iterator tmp = paragraphs.insert(boost::next(par),
110                                                         Paragraph());
111
112         // without doing that we get a crash when typing <Return> at the
113         // end of a paragraph
114         tmp->layout(bparams.getLyXTextClass().defaultLayout());
115         // remember to set the inset_owner
116         tmp->setInsetOwner(par->inInset());
117
118         if (bparams.tracking_changes)
119                 tmp->trackChanges();
120
121         // this is an idea for a more userfriendly layout handling, I will
122         // see what the users say
123
124         // layout stays the same with latex-environments
125         if (flag) {
126                 tmp->layout(par->layout());
127                 tmp->setLabelWidthString(par->params().labelWidthString());
128         }
129
130         bool const isempty = (par->allowEmpty() && par->empty());
131
132         if (!isempty && (par->size() > pos || par->empty() || flag == 2)) {
133                 tmp->layout(par->layout());
134                 tmp->params().align(par->params().align());
135                 tmp->setLabelWidthString(par->params().labelWidthString());
136
137                 tmp->params().spaceBottom(par->params().spaceBottom());
138                 par->params().spaceBottom(VSpace(VSpace::NONE));
139
140                 tmp->params().depth(par->params().depth());
141                 tmp->params().noindent(par->params().noindent());
142
143                 // copy everything behind the break-position
144                 // to the new paragraph
145
146 #ifdef WITH_WARNINGS
147 #warning this seems wrong
148 #endif
149                 /* FIXME: if !keepempty, empty() == true, then we reach
150                  * here with size() == 0. So pos_end becomes - 1. Why
151                  * doesn't this cause problems ???
152                  */
153                 pos_type pos_end = par->size() - 1;
154                 pos_type i = pos;
155                 pos_type j = pos;
156
157                 for (; i <= pos_end; ++i) {
158                         Change::Type change = par->lookupChange(i);
159                         if (moveItem(*par, *tmp, bparams, i, j - pos)) {
160                                 tmp->setChange(j - pos, change);
161                                 ++j;
162                         }
163                 }
164
165                 for (i = pos_end; i >= pos; --i)
166                         par->eraseIntern(i);
167         }
168
169         if (pos)
170                 return;
171
172         tmp->params().spaceTop(par->params().spaceTop());
173         par->params().clear();
174
175         par->layout(bparams.getLyXTextClass().defaultLayout());
176
177         // layout stays the same with latex-environments
178         if (flag) {
179                 par->layout(tmp->layout());
180                 par->setLabelWidthString(tmp->params().labelWidthString());
181                 par->params().depth(tmp->params().depth());
182         }
183
184         // subtle, but needed to get empty pars working right
185         if (bparams.tracking_changes) {
186                 if (!par->size()) {
187                         par->cleanChanges();
188                 } else if (!tmp->size()) {
189                         tmp->cleanChanges();
190                 }
191         }
192 }
193
194
195 void breakParagraphConservative(BufferParams const & bparams,
196                                 ParagraphList & paragraphs,
197                                 ParagraphList::iterator par,
198                                 pos_type pos)
199 {
200         // create a new paragraph
201         ParagraphList::iterator tmp = paragraphs.insert(boost::next(par),
202                                                         Paragraph());
203         tmp->makeSameLayout(*par);
204
205         // When can pos > size()?
206         // I guess pos == size() is possible.
207         if (par->size() > pos) {
208                 // copy everything behind the break-position to the new
209                 // paragraph
210                 pos_type pos_end = par->size() - 1;
211
212                 for (pos_type i = pos, j = pos; i <= pos_end; ++i)
213                         if (moveItem(*par, *tmp, bparams, i, j - pos))
214                                 ++j;
215
216                 for (pos_type k = pos_end; k >= pos; --k)
217                         par->erase(k);
218         }
219 }
220
221
222 void mergeParagraph(BufferParams const & bparams,
223                     ParagraphList & paragraphs,
224                     ParagraphList::iterator par)
225 {
226         ParagraphList::iterator the_next = boost::next(par);
227
228         // first the DTP-stuff
229         par->params().spaceBottom(the_next->params().spaceBottom());
230
231         pos_type pos_end = the_next->size() - 1;
232         pos_type pos_insert = par->size();
233
234         // ok, now copy the paragraph
235         for (pos_type i = 0, j = 0; i <= pos_end; ++i)
236                 if (moveItem(*the_next, *par, bparams, i, pos_insert + j))
237                         ++j;
238
239         paragraphs.erase(the_next);
240 }
241
242
243 ParagraphList::iterator depthHook(ParagraphList::iterator pit,
244                                   ParagraphList const & plist,
245                                   Paragraph::depth_type depth)
246 {
247         ParagraphList::iterator newpit = pit;
248         ParagraphList::iterator beg = const_cast<ParagraphList&>(plist).begin();
249
250         if (newpit != beg)
251                 --newpit;
252
253         while (newpit != beg && newpit->getDepth() > depth) {
254                 --newpit;
255         }
256
257         if (newpit->getDepth() > depth)
258                 return pit;
259
260         return newpit;
261 }
262
263
264 ParagraphList::iterator outerHook(ParagraphList::iterator pit,
265                                   ParagraphList const & plist)
266 {
267         if (!pit->getDepth())
268                 return const_cast<ParagraphList&>(plist).end();
269         return depthHook(pit, plist,
270                          Paragraph::depth_type(pit->getDepth() - 1));
271 }
272
273
274 bool isFirstInSequence(ParagraphList::iterator pit,
275                        ParagraphList const & plist)
276 {
277         ParagraphList::iterator dhook = depthHook(pit, plist, pit->getDepth());
278         return (dhook == pit
279                 || dhook->layout() != pit->layout()
280                 || dhook->getDepth() != pit->getDepth());
281 }
282
283
284 int getEndLabel(ParagraphList::iterator p, ParagraphList const & plist)
285 {
286         ParagraphList::iterator pit = p;
287         Paragraph::depth_type par_depth = p->getDepth();
288         while (pit != const_cast<ParagraphList&>(plist).end()) {
289                 LyXLayout_ptr const & layout = pit->layout();
290                 int const endlabeltype = layout->endlabeltype;
291
292                 if (endlabeltype != END_LABEL_NO_LABEL) {
293                         if (boost::next(p) == const_cast<ParagraphList&>(plist).end())
294                                 return endlabeltype;
295
296                         Paragraph::depth_type const next_depth = boost::next(p)->getDepth();
297                         if (par_depth > next_depth ||
298                             (par_depth == next_depth &&
299                              layout != boost::next(p)->layout()))
300                                 return endlabeltype;
301                         break;
302                 }
303                 if (par_depth == 0)
304                         break;
305                 pit = outerHook(pit, plist);
306                 if (pit != const_cast<ParagraphList&>(plist).end())
307                         par_depth = pit->getDepth();
308         }
309         return END_LABEL_NO_LABEL;
310 }
311
312
313 namespace {
314
315 ParagraphList::iterator
316 TeXEnvironment(Buffer const & buf,
317                ParagraphList const & paragraphs,
318                ParagraphList::iterator pit,
319                ostream & os, TexRow & texrow,
320                LatexRunParams const & runparams);
321
322 ParagraphList::iterator
323 TeXOnePar(Buffer const & buf,
324           ParagraphList const & paragraphs,
325           ParagraphList::iterator pit,
326           ostream & os, TexRow & texrow,
327           LatexRunParams const & runparams,
328           string const & everypar = string());
329
330
331 ParagraphList::iterator
332 TeXDeeper(Buffer const & buf,
333           ParagraphList const & paragraphs,
334           ParagraphList::iterator pit,
335           ostream & os, TexRow & texrow,
336           LatexRunParams const & runparams)
337 {
338         lyxerr[Debug::LATEX] << "TeXDeeper...     " << &*pit << endl;
339         ParagraphList::iterator par = pit;
340
341         while (par != const_cast<ParagraphList&>(paragraphs).end() &&
342                      par->params().depth() == pit->params().depth()) {
343                 if (par->layout()->isEnvironment()) {
344                         par = TeXEnvironment(buf, paragraphs, par,
345                                              os, texrow, runparams);
346                 } else {
347                         par = TeXOnePar(buf, paragraphs, par,
348                                              os, texrow, runparams);
349                 }
350         }
351         lyxerr[Debug::LATEX] << "TeXDeeper...done " << &*par << endl;
352
353         return par;
354 }
355
356
357 ParagraphList::iterator
358 TeXEnvironment(Buffer const & buf,
359                ParagraphList const & paragraphs,
360                ParagraphList::iterator pit,
361                ostream & os, TexRow & texrow,
362                LatexRunParams const & runparams)
363 {
364         lyxerr[Debug::LATEX] << "TeXEnvironment...     " << &*pit << endl;
365
366         BufferParams const & bparams = buf.params();
367
368         LyXLayout_ptr const & style = pit->layout();
369
370         Language const * language = pit->getParLanguage(bparams);
371         Language const * doc_language = bparams.language;
372         Language const * previous_language =
373                 (pit != const_cast<ParagraphList&>(paragraphs).begin())
374                 ? boost::prior(pit)->getParLanguage(bparams)
375                 : doc_language;
376         if (language->babel() != previous_language->babel()) {
377
378                 if (!lyxrc.language_command_end.empty() &&
379                     previous_language->babel() != doc_language->babel()) {
380                         os << subst(lyxrc.language_command_end, "$$lang",
381                                     previous_language->babel())
382                            << endl;
383                         texrow.newline();
384                 }
385
386                 if (lyxrc.language_command_end.empty() ||
387                     language->babel() != doc_language->babel()) {
388                         os << subst(lyxrc.language_command_begin, "$$lang",
389                                     language->babel())
390                            << endl;
391                         texrow.newline();
392                 }
393         }
394
395         bool leftindent_open = false;
396         if (!pit->params().leftIndent().zero()) {
397                 os << "\\begin{LyXParagraphLeftIndent}{" <<
398                         pit->params().leftIndent().asLatexString() << "}\n";
399                 texrow.newline();
400                 leftindent_open = true;
401         }
402
403         if (style->isEnvironment()) {
404                 if (style->latextype == LATEX_LIST_ENVIRONMENT) {
405                         os << "\\begin{" << style->latexname() << "}{"
406                            << pit->params().labelWidthString() << "}\n";
407                 } else if (style->labeltype == LABEL_BIBLIO) {
408                         // ale970405
409                         os << "\\begin{" << style->latexname() << "}{"
410                            <<  bibitemWidest(buf)
411                            << "}\n";
412                 } else if (style->latextype == LATEX_ITEM_ENVIRONMENT) {
413                         os << "\\begin{" << style->latexname() << '}'
414                            << style->latexparam() << '\n';
415                 } else
416                         os << "\\begin{" << style->latexname() << '}'
417                            << style->latexparam() << '\n';
418                 texrow.newline();
419         }
420         ParagraphList::iterator par = pit;
421         do {
422                 par = TeXOnePar(buf, paragraphs, par, os, texrow, runparams);
423
424                 if (par != const_cast<ParagraphList&>(paragraphs).end() && par->params().depth() > pit->params().depth()) {
425                             if (par->layout()->isParagraph()) {
426
427                             // Thinko!
428                             // How to handle this? (Lgb)
429                             //&& !suffixIs(os, "\n\n")
430                                     //) {
431                                 // There should be at least one '\n' already
432                                 // but we need there to be two for Standard
433                                 // paragraphs that are depth-increment'ed to be
434                                 // output correctly.  However, tables can
435                                 // also be paragraphs so don't adjust them.
436                                 // ARRae
437                                 // Thinkee:
438                                 // Will it ever harm to have one '\n' too
439                                 // many? i.e. that we sometimes will have
440                                 // three in a row. (Lgb)
441                                 os << '\n';
442                                 texrow.newline();
443                         }
444                         par = TeXDeeper(buf, paragraphs, par, os, texrow,
445                                         runparams);
446                 }
447         } while (par != const_cast<ParagraphList&>(paragraphs).end()
448                  && par->layout() == pit->layout()
449                  && par->params().depth() == pit->params().depth()
450                  && par->params().leftIndent() == pit->params().leftIndent());
451
452         if (style->isEnvironment()) {
453                 os << "\\end{" << style->latexname() << "}\n";
454                 texrow.newline();
455         }
456
457         if (leftindent_open) {
458                 os << "\\end{LyXParagraphLeftIndent}\n";
459                 texrow.newline();
460         }
461
462         lyxerr[Debug::LATEX] << "TeXEnvironment...done " << &*par << endl;
463         return par;  // ale970302
464 }
465
466
467 InsetOptArg * optArgInset(Paragraph const & par)
468 {
469         // Find the entry.
470         InsetList::const_iterator it = par.insetlist.begin();
471         InsetList::const_iterator end = par.insetlist.end();
472         for (; it != end; ++it) {
473                 InsetOld * ins = it->inset;
474                 if (ins->lyxCode() == InsetOld::OPTARG_CODE) {
475                         return static_cast<InsetOptArg *>(ins);
476                 }
477         }
478         return 0;
479 }
480
481
482 ParagraphList::iterator
483 TeXOnePar(Buffer const & buf,
484           ParagraphList const & paragraphs,
485           ParagraphList::iterator pit,
486           ostream & os, TexRow & texrow,
487           LatexRunParams const & runparams,
488           string const & everypar)
489 {
490         lyxerr[Debug::LATEX] << "TeXOnePar...     " << &*pit << " '"
491                 << everypar << "'" << endl;
492         BufferParams const & bparams = buf.params();
493
494         InsetOld const * in = pit->inInset();
495         bool further_blank_line = false;
496         LyXLayout_ptr style;
497
498         // well we have to check if we are in an inset with unlimited
499         // length (all in one row) if that is true then we don't allow
500         // any special options in the paragraph and also we don't allow
501         // any environment other then "Standard" to be valid!
502         if (in == 0 || !in->forceDefaultParagraphs(in)) {
503                 style = pit->layout();
504
505                 if (pit->params().startOfAppendix()) {
506                         os << "\\appendix\n";
507                         texrow.newline();
508                 }
509
510                 if (!pit->params().spacing().isDefault()
511                         && (pit == const_cast<ParagraphList&>(paragraphs).begin()
512                             || !boost::prior(pit)->hasSameLayout(*pit)))
513                 {
514                         os << pit->params().spacing().writeEnvirBegin() << '\n';
515                         texrow.newline();
516                 }
517
518                 if (style->isCommand()) {
519                         os << '\n';
520                         texrow.newline();
521                 }
522
523                 if (pit->params().spaceTop().kind() != VSpace::NONE) {
524                         os << pit->params().spaceTop().asLatexCommand(bparams);
525                         further_blank_line = true;
526                 }
527
528                 if (further_blank_line) {
529                         os << '\n';
530                         texrow.newline();
531                 }
532         } else {
533                 style = bparams.getLyXTextClass().defaultLayout();
534         }
535
536         Language const * language = pit->getParLanguage(bparams);
537         Language const * doc_language = bparams.language;
538         Language const * previous_language =
539                 (pit != const_cast<ParagraphList&>(paragraphs).begin())
540                 ? boost::prior(pit)->getParLanguage(bparams)
541                 : doc_language;
542
543         if (language->babel() != previous_language->babel()
544             // check if we already put language command in TeXEnvironment()
545             && !(style->isEnvironment()
546                  && (pit == const_cast<ParagraphList&>(paragraphs).begin() ||
547                      (boost::prior(pit)->layout() != pit->layout() &&
548                       boost::prior(pit)->getDepth() <= pit->getDepth())
549                      || boost::prior(pit)->getDepth() < pit->getDepth())))
550         {
551                 if (!lyxrc.language_command_end.empty() &&
552                     previous_language->babel() != doc_language->babel())
553                 {
554                         os << subst(lyxrc.language_command_end, "$$lang",
555                                     previous_language->babel())
556                            << endl;
557                         texrow.newline();
558                 }
559
560                 if (lyxrc.language_command_end.empty() ||
561                     language->babel() != doc_language->babel())
562                 {
563                         os << subst(lyxrc.language_command_begin, "$$lang",
564                                     language->babel())
565                            << endl;
566                         texrow.newline();
567                 }
568         }
569
570         if (bparams.inputenc == "auto" &&
571             language->encoding() != previous_language->encoding()) {
572                 os << "\\inputencoding{"
573                    << language->encoding()->LatexName()
574                    << "}\n";
575                 texrow.newline();
576         }
577
578         switch (style->latextype) {
579         case LATEX_COMMAND:
580                 os << '\\' << style->latexname();
581
582                 // Separate handling of optional argument inset.
583                 if (style->optionalargs == 1) {
584                         InsetOptArg * it = optArgInset(*pit);
585                         if (it)
586                                 it->latexOptional(buf, os, runparams);
587                 }
588                 else
589                         os << style->latexparam();
590                 break;
591         case LATEX_ITEM_ENVIRONMENT:
592         case LATEX_LIST_ENVIRONMENT:
593                 os << "\\item ";
594                 break;
595         case LATEX_BIB_ENVIRONMENT:
596                 // ignore this, the inset will write itself
597                 break;
598         default:
599                 break;
600         }
601
602         os << everypar;
603         bool need_par = pit->simpleTeXOnePar(buf, bparams,
604                                              outerFont(pit, paragraphs),
605                                              os, texrow, runparams);
606
607         // Make sure that \\par is done with the font of the last
608         // character if this has another size as the default.
609         // This is necessary because LaTeX (and LyX on the screen)
610         // calculates the space between the baselines according
611         // to this font. (Matthias)
612         //
613         // Is this really needed ? (Dekel)
614         // We do not need to use to change the font for the last paragraph
615         // or for a command.
616         LyXFont const outerfont(outerFont(pit, paragraphs));
617
618         LyXFont const font =
619                 (pit->empty()
620                  ? pit->getLayoutFont(bparams, outerfont)
621                  : pit->getFont(bparams, pit->size() - 1, outerfont));
622
623         bool is_command = style->isCommand();
624
625         if (style->resfont.size() != font.size()
626             && boost::next(pit) != const_cast<ParagraphList&>(paragraphs).end()
627             && !is_command) {
628                 if (!need_par)
629                         os << '{';
630                 os << "\\" << font.latexSize() << " \\par}";
631         } else if (need_par) {
632                 os << "\\par}";
633         } else if (is_command)
634                 os << '}';
635
636         switch (style->latextype) {
637         case LATEX_ITEM_ENVIRONMENT:
638         case LATEX_LIST_ENVIRONMENT:
639                 if (boost::next(pit) != const_cast<ParagraphList&>(paragraphs).end()
640                     && (pit->params().depth() < boost::next(pit)->params().depth())) {
641                         os << '\n';
642                         texrow.newline();
643                 }
644                 break;
645         case LATEX_ENVIRONMENT: {
646                 // if its the last paragraph of the current environment
647                 // skip it otherwise fall through
648                 ParagraphList::iterator next = boost::next(pit);
649
650                 if (next != const_cast<ParagraphList&>(paragraphs).end()
651                     && (next->layout() != pit->layout()
652                         || next->params().depth() != pit->params().depth()))
653                         break;
654         }
655
656                 // fall through possible
657         default:
658                 // we don't need it for the last paragraph!!!
659                 if (boost::next(pit) != const_cast<ParagraphList&>(paragraphs).end()) {
660                         os << '\n';
661                         texrow.newline();
662                 }
663         }
664
665         if (in == 0 || !in->forceDefaultParagraphs(in)) {
666                 further_blank_line = false;
667
668                 if (pit->params().spaceBottom().kind() != VSpace::NONE) {
669                         os << pit->params().spaceBottom().asLatexCommand(bparams);
670                         further_blank_line = true;
671                 }
672
673                 if (further_blank_line) {
674                         os << '\n';
675                         texrow.newline();
676                 }
677
678                 if (!pit->params().spacing().isDefault()
679                         && (boost::next(pit) == const_cast<ParagraphList&>(paragraphs).end()
680                             || !boost::next(pit)->hasSameLayout(*pit)))
681                 {
682                         os << pit->params().spacing().writeEnvirEnd() << '\n';
683                         texrow.newline();
684                 }
685         }
686
687         // we don't need it for the last paragraph!!!
688         if (boost::next(pit) != const_cast<ParagraphList&>(paragraphs).end()) {
689                 os << '\n';
690                 texrow.newline();
691         } else {
692                 // Since \selectlanguage write the language to the aux file,
693                 // we need to reset the language at the end of footnote or
694                 // float.
695
696                 if (language->babel() != doc_language->babel()) {
697                         if (lyxrc.language_command_end.empty())
698                                 os << subst(lyxrc.language_command_begin,
699                                             "$$lang",
700                                             doc_language->babel())
701                                    << endl;
702                         else
703                                 os << subst(lyxrc.language_command_end,
704                                             "$$lang",
705                                             language->babel())
706                                    << endl;
707                         texrow.newline();
708                 }
709         }
710
711         lyxerr[Debug::LATEX] << "TeXOnePar...done " << &*boost::next(pit) << endl;
712         return ++pit;
713 }
714
715 } // anon namespace
716
717
718 //
719 // LaTeX all paragraphs from par to endpar, if endpar == 0 then to the end
720 //
721 void latexParagraphs(Buffer const & buf,
722                      ParagraphList const & paragraphs,
723                      ostream & os,
724                      TexRow & texrow,
725                      LatexRunParams const & runparams,
726                      string const & everypar)
727 {
728         bool was_title = false;
729         bool already_title = false;
730         LyXTextClass const & tclass = buf.params().getLyXTextClass();
731         ParagraphList::iterator par = const_cast<ParagraphList&>(paragraphs).begin();
732         ParagraphList::iterator endpar = const_cast<ParagraphList&>(paragraphs).end();
733
734         // if only_body
735         while (par != endpar) {
736                 InsetOld * in = par->inInset();
737                 // well we have to check if we are in an inset with unlimited
738                 // length (all in one row) if that is true then we don't allow
739                 // any special options in the paragraph and also we don't allow
740                 // any environment other then "Standard" to be valid!
741                 if (in == 0 || !in->forceDefaultParagraphs(in)) {
742                         LyXLayout_ptr const & layout = par->layout();
743
744                         if (layout->intitle) {
745                                 if (already_title) {
746                                         lyxerr <<"Error in latexParagraphs: You"
747                                                 " should not mix title layouts"
748                                                 " with normal ones." << endl;
749                                 } else if (!was_title) {
750                                         was_title = true;
751                                         if (tclass.titletype() == TITLE_ENVIRONMENT) {
752                                                 os << "\\begin{"
753                                                     << tclass.titlename()
754                                                     << "}\n";
755                                                 texrow.newline();
756                                         }
757                                 }
758                         } else if (was_title && !already_title) {
759                                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
760                                         os << "\\end{" << tclass.titlename()
761                                             << "}\n";
762                                 }
763                                 else {
764                                         os << "\\" << tclass.titlename()
765                                             << "\n";
766                                 }
767                                 texrow.newline();
768                                 already_title = true;
769                                 was_title = false;
770                         }
771
772                         if (layout->is_environment) {
773                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
774                                                 runparams, everypar);
775                         } else if (layout->isEnvironment() ||
776                                 !par->params().leftIndent().zero())
777                         {
778                                 par = TeXEnvironment(buf, paragraphs, par, os,
779                                                      texrow, runparams);
780                         } else {
781                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
782                                                 runparams, everypar);
783                         }
784                 } else {
785                         par = TeXOnePar(buf, paragraphs, par, os, texrow,
786                                         runparams, everypar);
787                 }
788         }
789         // It might be that we only have a title in this document
790         if (was_title && !already_title) {
791                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
792                         os << "\\end{" << tclass.titlename()
793                             << "}\n";
794                 }
795                 else {
796                         os << "\\" << tclass.titlename()
797                             << "\n";
798                                 }
799                 texrow.newline();
800         }
801 }
802
803
804 namespace {
805
806 pair<int, string> const addDepth(int depth, int ldepth)
807 {
808         int d = depth * 2;
809         if (ldepth > depth)
810                 d += (ldepth - depth) * 2;
811         return make_pair(d, string(d, ' '));
812 }
813
814 }
815
816
817 void asciiParagraph(Buffer const & buf,
818                     Paragraph const & par,
819                     ostream & os,
820                     LatexRunParams const & runparams,
821                     bool noparbreak)
822 {
823         int ltype = 0;
824         Paragraph::depth_type ltype_depth = 0;
825         bool ref_printed = false;
826         Paragraph::depth_type depth = par.params().depth();
827
828         // First write the layout
829         string const & tmp = par.layout()->name();
830         if (compare_no_case(tmp, "itemize") == 0) {
831                 ltype = 1;
832                 ltype_depth = depth + 1;
833         } else if (compare_ascii_no_case(tmp, "enumerate") == 0) {
834                 ltype = 2;
835                 ltype_depth = depth + 1;
836         } else if (contains(ascii_lowercase(tmp), "ection")) {
837                 ltype = 3;
838                 ltype_depth = depth + 1;
839         } else if (contains(ascii_lowercase(tmp), "aragraph")) {
840                 ltype = 4;
841                 ltype_depth = depth + 1;
842         } else if (compare_ascii_no_case(tmp, "description") == 0) {
843                 ltype = 5;
844                 ltype_depth = depth + 1;
845         } else if (compare_ascii_no_case(tmp, "abstract") == 0) {
846                 ltype = 6;
847                 ltype_depth = 0;
848         } else if (compare_ascii_no_case(tmp, "bibliography") == 0) {
849                 ltype = 7;
850                 ltype_depth = 0;
851         } else {
852                 ltype = 0;
853                 ltype_depth = 0;
854         }
855
856         /* maybe some vertical spaces */
857
858         /* the labelwidthstring used in lists */
859
860         /* some lines? */
861
862         /* some pagebreaks? */
863
864         /* noindent ? */
865
866         /* what about the alignment */
867
868         // runparams.linelen <= 0 is special and means we don't have paragraph breaks
869
870         string::size_type currlinelen = 0;
871
872         if (!noparbreak) {
873                 if (runparams.linelen > 0)
874                         os << "\n\n";
875
876                 os << string(depth * 2, ' ');
877                 currlinelen += depth * 2;
878
879                 //--
880                 // we should probably change to the paragraph language in the
881                 // gettext here (if possible) so that strings are outputted in
882                 // the correct language! (20012712 Jug)
883                 //--
884                 switch (ltype) {
885                 case 0: // Standard
886                 case 4: // (Sub)Paragraph
887                 case 5: // Description
888                         break;
889                 case 6: // Abstract
890                         if (runparams.linelen > 0) {
891                                 os << _("Abstract") << "\n\n";
892                                 currlinelen = 0;
893                         } else {
894                                 string const abst = _("Abstract: ");
895                                 os << abst;
896                                 currlinelen += abst.length();
897                         }
898                         break;
899                 case 7: // Bibliography
900                         if (!ref_printed) {
901                                 if (runparams.linelen > 0) {
902                                         os << _("References") << "\n\n";
903                                         currlinelen = 0;
904                                 } else {
905                                         string const refs = _("References: ");
906                                         os << refs;
907                                         currlinelen += refs.length();
908                                 }
909
910                                 ref_printed = true;
911                         }
912                         break;
913                 default:
914                 {
915                         string const parlab = par.params().labelString();
916                         os << parlab << ' ';
917                         currlinelen += parlab.length() + 1;
918                 }
919                 break;
920
921                 }
922         }
923
924         if (!currlinelen) {
925                 pair<int, string> p = addDepth(depth, ltype_depth);
926                 os << p.second;
927                 currlinelen += p.first;
928         }
929
930         // this is to change the linebreak to do it by word a bit more
931         // intelligent hopefully! (only in the case where we have a
932         // max runparams.linelength!) (Jug)
933
934         string word;
935
936         for (pos_type i = 0; i < par.size(); ++i) {
937                 char c = par.getUChar(buf.params(), i);
938                 switch (c) {
939                 case Paragraph::META_INSET:
940                 {
941                         InsetOld const * inset = par.getInset(i);
942                         if (inset) {
943                                 if (runparams.linelen > 0) {
944                                         os << word;
945                                         currlinelen += word.length();
946                                         word.erase();
947                                 }
948                                 if (inset->ascii(buf, os, runparams)) {
949                                         // to be sure it breaks paragraph
950                                         currlinelen += runparams.linelen;
951                                 }
952                         }
953                 }
954                 break;
955
956                 default:
957                         if (c == ' ') {
958                                 if (runparams.linelen > 0 &&
959                                     currlinelen + word.length() > runparams.linelen - 10) {
960                                         os << "\n";
961                                         pair<int, string> p = addDepth(depth, ltype_depth);
962                                         os << p.second;
963                                         currlinelen = p.first;
964                                 }
965
966                                 os << word << ' ';
967                                 currlinelen += word.length() + 1;
968                                 word.erase();
969
970                         } else {
971                                 if (c != '\0') {
972                                         word += c;
973                                 } else {
974                                         lyxerr[Debug::INFO] <<
975                                                 "writeAsciiFile: NULL char in structure." << endl;
976                                 }
977                                 if ((runparams.linelen > 0) &&
978                                         (currlinelen + word.length()) > runparams.linelen)
979                                 {
980                                         os << "\n";
981
982                                         pair<int, string> p =
983                                                 addDepth(depth, ltype_depth);
984                                         os << p.second;
985                                         currlinelen = p.first;
986                                 }
987                         }
988                         break;
989                 }
990         }
991         os << word;
992 }
993
994
995 void linuxdocParagraphs(Buffer const & buf,
996                         ParagraphList const & paragraphs,
997                         ostream & os,
998                         LatexRunParams const & runparams)
999 {
1000
1001         Paragraph::depth_type depth = 0; // paragraph depth
1002         string item_name;
1003         vector<string> environment_stack(5);
1004
1005         ParagraphList::iterator pit = const_cast<ParagraphList&>(paragraphs).begin();
1006         ParagraphList::iterator pend = const_cast<ParagraphList&>(paragraphs).end();
1007         for (; pit != pend; ++pit) {
1008                 LyXLayout_ptr const & style = pit->layout();
1009                 // treat <toc> as a special case for compatibility with old code
1010                 if (pit->isInset(0)) {
1011                         InsetOld * inset = pit->getInset(0);
1012                         InsetOld::Code lyx_code = inset->lyxCode();
1013                         if (lyx_code == InsetOld::TOC_CODE) {
1014                                 string const temp = "toc";
1015                                 sgml::openTag(os, depth, false, temp);
1016                                 continue;
1017                         }
1018                 }
1019
1020                 // environment tag closing
1021                 for (; depth > pit->params().depth(); --depth) {
1022                         sgml::closeTag(os, depth, false, environment_stack[depth]);
1023                         environment_stack[depth].erase();
1024                 }
1025
1026                 // write opening SGML tags
1027                 switch (style->latextype) {
1028                 case LATEX_PARAGRAPH:
1029                         if (depth == pit->params().depth()
1030                            && !environment_stack[depth].empty()) {
1031                                 sgml::closeTag(os, depth, false, environment_stack[depth]);
1032                                 environment_stack[depth].erase();
1033                                 if (depth)
1034                                         --depth;
1035                                 else
1036                                         os << "</p>";
1037                         }
1038                         sgml::openTag(os, depth, false, style->latexname());
1039                         break;
1040
1041                 case LATEX_COMMAND:
1042                         if (depth != 0)
1043                                 //error(ErrorItem(_("Error:"), _("Wrong depth for LatexType Command.\n"), pit->id(), 0, pit->size()));
1044                                 ;
1045
1046                         if (!environment_stack[depth].empty()) {
1047                                 sgml::closeTag(os, depth, false, environment_stack[depth]);
1048                                 os << "</p>";
1049                         }
1050
1051                         environment_stack[depth].erase();
1052                         sgml::openTag(os, depth, false, style->latexname());
1053                         break;
1054
1055                 case LATEX_ENVIRONMENT:
1056                 case LATEX_ITEM_ENVIRONMENT:
1057                 case LATEX_BIB_ENVIRONMENT:
1058                 {
1059                         string const & latexname = style->latexname();
1060
1061                         if (depth == pit->params().depth()
1062                             && environment_stack[depth] != latexname) {
1063                                 sgml::closeTag(os, depth, false,
1064                                              environment_stack[depth]);
1065                                 environment_stack[depth].erase();
1066                         }
1067                         if (depth < pit->params().depth()) {
1068                                depth = pit->params().depth();
1069                                environment_stack[depth].erase();
1070                         }
1071                         if (environment_stack[depth] != latexname) {
1072                                 if (depth == 0) {
1073                                         sgml::openTag(os, depth, false, "p");
1074                                 }
1075                                 sgml::openTag(os, depth, false, latexname);
1076
1077                                 if (environment_stack.size() == depth + 1)
1078                                         environment_stack.push_back("!-- --");
1079                                 environment_stack[depth] = latexname;
1080                         }
1081
1082                         if (style->latexparam() == "CDATA")
1083                                 os << "<![CDATA[";
1084
1085                         if (style->latextype == LATEX_ENVIRONMENT) break;
1086
1087                         if (style->labeltype == LABEL_MANUAL)
1088                                 item_name = "tag";
1089                         else
1090                                 item_name = "item";
1091
1092                         sgml::openTag(os, depth + 1, false, item_name);
1093                 }
1094                 break;
1095
1096                 default:
1097                         sgml::openTag(os, depth, false, style->latexname());
1098                         break;
1099                 }
1100
1101                 pit->simpleLinuxDocOnePar(buf, os, outerFont(pit, paragraphs),
1102                                           runparams, depth);
1103
1104                 os << "\n";
1105                 // write closing SGML tags
1106                 switch (style->latextype) {
1107                 case LATEX_COMMAND:
1108                         break;
1109                 case LATEX_ENVIRONMENT:
1110                 case LATEX_ITEM_ENVIRONMENT:
1111                 case LATEX_BIB_ENVIRONMENT:
1112                         if (style->latexparam() == "CDATA")
1113                                 os << "]]>";
1114                         break;
1115                 default:
1116                         sgml::closeTag(os, depth, false, style->latexname());
1117                         break;
1118                 }
1119         }
1120
1121         // Close open tags
1122         for (int i = depth; i >= 0; --i)
1123                 sgml::closeTag(os, depth, false, environment_stack[i]);
1124 }
1125
1126
1127 void docbookParagraphs(Buffer const & buf,
1128                        ParagraphList const & paragraphs,
1129                        ostream & os,
1130                        LatexRunParams const & runparams)
1131 {
1132         vector<string> environment_stack(10);
1133         vector<string> environment_inner(10);
1134         vector<string> command_stack(10);
1135
1136         bool command_flag = false;
1137         Paragraph::depth_type command_depth = 0;
1138         Paragraph::depth_type command_base = 0;
1139         Paragraph::depth_type cmd_depth = 0;
1140         Paragraph::depth_type depth = 0; // paragraph depth
1141
1142         string item_name;
1143         string command_name;
1144
1145         ParagraphList::iterator par = const_cast<ParagraphList&>(paragraphs).begin();
1146         ParagraphList::iterator pend = const_cast<ParagraphList&>(paragraphs).end();
1147
1148         for (; par != pend; ++par) {
1149                 string sgmlparam;
1150                 string c_depth;
1151                 string c_params;
1152                 int desc_on = 0; // description mode
1153
1154                 LyXLayout_ptr const & style = par->layout();
1155
1156                 // environment tag closing
1157                 for (; depth > par->params().depth(); --depth) {
1158                         if (!environment_inner[depth].empty())
1159                         sgml::closeEnvTags(os, false, environment_inner[depth],
1160                                         command_depth + depth);
1161                         sgml::closeTag(os, depth + command_depth, false, environment_stack[depth]);
1162                         environment_stack[depth].erase();
1163                         environment_inner[depth].erase();
1164                 }
1165
1166                 if (depth == par->params().depth()
1167                    && environment_stack[depth] != style->latexname()
1168                    && !environment_stack[depth].empty()) {
1169                                 sgml::closeEnvTags(os, false, environment_inner[depth],
1170                                         command_depth + depth);
1171                         sgml::closeTag(os, depth + command_depth, false, environment_stack[depth]);
1172
1173                         environment_stack[depth].erase();
1174                         environment_inner[depth].erase();
1175                 }
1176
1177                 // Write opening SGML tags.
1178                 switch (style->latextype) {
1179                 case LATEX_PARAGRAPH:
1180                         sgml::openTag(os, depth + command_depth,
1181                                     false, style->latexname());
1182                         break;
1183
1184                 case LATEX_COMMAND:
1185                         if (depth != 0)
1186                                 //error(ErrorItem(_("Error"), _("Wrong depth for LatexType Command."), par->id(), 0, par->size()));
1187                                 ;
1188
1189                         command_name = style->latexname();
1190
1191                         sgmlparam = style->latexparam();
1192                         c_params = split(sgmlparam, c_depth,'|');
1193
1194                         cmd_depth = atoi(c_depth);
1195
1196                         if (command_flag) {
1197                                 if (cmd_depth < command_base) {
1198                                         for (Paragraph::depth_type j = command_depth;
1199                                              j >= command_base; --j) {
1200                                                 sgml::closeTag(os, j, false, command_stack[j]);
1201                                                 os << endl;
1202                                         }
1203                                         command_depth = command_base = cmd_depth;
1204                                 } else if (cmd_depth <= command_depth) {
1205                                         for (int j = command_depth;
1206                                              j >= int(cmd_depth); --j) {
1207                                                 sgml::closeTag(os, j, false, command_stack[j]);
1208                                                 os << endl;
1209                                         }
1210                                         command_depth = cmd_depth;
1211                                 } else
1212                                         command_depth = cmd_depth;
1213                         } else {
1214                                 command_depth = command_base = cmd_depth;
1215                                 command_flag = true;
1216                         }
1217                         if (command_stack.size() == command_depth + 1)
1218                                 command_stack.push_back(string());
1219                         command_stack[command_depth] = command_name;
1220
1221                         // treat label as a special case for
1222                         // more WYSIWYM handling.
1223                         // This is a hack while paragraphs can't have
1224                         // attributes, like id in this case.
1225                         if (par->isInset(0)) {
1226                                 InsetOld * inset = par->getInset(0);
1227                                 InsetOld::Code lyx_code = inset->lyxCode();
1228                                 if (lyx_code == InsetOld::LABEL_CODE) {
1229                                         command_name += " id=\"";
1230                                         command_name += (static_cast<InsetCommand *>(inset))->getContents();
1231                                         command_name += '"';
1232                                         desc_on = 3;
1233                                 }
1234                         }
1235
1236                         sgml::openTag(os, depth + command_depth, false, command_name);
1237
1238                         item_name = c_params.empty() ? "title" : c_params;
1239                         sgml::openTag(os, depth + 1 + command_depth, false, item_name);
1240                         break;
1241
1242                 case LATEX_ENVIRONMENT:
1243                 case LATEX_ITEM_ENVIRONMENT:
1244                         if (depth < par->params().depth()) {
1245                                 depth = par->params().depth();
1246                                 environment_stack[depth].erase();
1247                         }
1248
1249                         if (environment_stack[depth] != style->latexname()) {
1250                                 if (environment_stack.size() == depth + 1) {
1251                                         environment_stack.push_back("!-- --");
1252                                         environment_inner.push_back("!-- --");
1253                                 }
1254                                 environment_stack[depth] = style->latexname();
1255                                 environment_inner[depth] = "!-- --";
1256                                 sgml::openTag(os, depth + command_depth, false, environment_stack[depth]);
1257                         } else {
1258                                         sgml::closeEnvTags(os, false, environment_inner[depth],
1259                                                 command_depth + depth);
1260                         }
1261
1262                         if (style->latextype == LATEX_ENVIRONMENT) {
1263                                 if (!style->latexparam().empty()) {
1264                                         if (style->latexparam() == "CDATA")
1265                                                 os << "<![CDATA[";
1266                                         else
1267                                                 sgml::openTag(os, depth + command_depth, false, style->latexparam());
1268                                 }
1269                                 break;
1270                         }
1271
1272                         desc_on = (style->labeltype == LABEL_MANUAL);
1273
1274                         environment_inner[depth] = desc_on ? "varlistentry" : "listitem";
1275                         sgml::openTag(os, depth + 1 + command_depth,
1276                                     false, environment_inner[depth]);
1277
1278                         item_name = desc_on ? "term" : "para";
1279                         sgml::openTag(os, depth + 1 + command_depth,
1280                                     false, item_name);
1281                         break;
1282                 default:
1283                         sgml::openTag(os, depth + command_depth,
1284                                     false, style->latexname());
1285                         break;
1286                 }
1287
1288                 par->simpleDocBookOnePar(buf, os, outerFont(par, paragraphs), desc_on,
1289                                          runparams, depth + 1 + command_depth);
1290
1291                 string end_tag;
1292                 // write closing SGML tags
1293                 switch (style->latextype) {
1294                 case LATEX_COMMAND:
1295                         end_tag = c_params.empty() ? "title" : c_params;
1296                         sgml::closeTag(os, depth + command_depth,
1297                                      false, end_tag);
1298                         break;
1299                 case LATEX_ENVIRONMENT:
1300                         if (!style->latexparam().empty()) {
1301                                 if (style->latexparam() == "CDATA")
1302                                         os << "]]>";
1303                                 else
1304                                         sgml::closeTag(os, depth + command_depth, false, style->latexparam());
1305                         }
1306                         break;
1307                 case LATEX_ITEM_ENVIRONMENT:
1308                         if (desc_on == 1) break;
1309                         end_tag = "para";
1310                         sgml::closeTag(os, depth + 1 + command_depth, false, end_tag);
1311                         break;
1312                 case LATEX_PARAGRAPH:
1313                         sgml::closeTag(os, depth + command_depth, false, style->latexname());
1314                         break;
1315                 default:
1316                         sgml::closeTag(os, depth + command_depth, false, style->latexname());
1317                         break;
1318                 }
1319         }
1320
1321         // Close open tags
1322         for (int d = depth; d >= 0; --d) {
1323                 if (!environment_stack[depth].empty()) {
1324                                 sgml::closeEnvTags(os, false, environment_inner[depth],
1325                                         command_depth + depth);
1326                 }
1327         }
1328
1329         for (int j = command_depth; j >= 0 ; --j)
1330                 if (!command_stack[j].empty()) {
1331                         sgml::closeTag(os, j, false, command_stack[j]);
1332                         os << endl;
1333                 }
1334 }
1335
1336 namespace {
1337
1338 int readParToken(Buffer & buf, Paragraph & par, LyXLex & lex, string const & token)
1339 {
1340         static LyXFont font;
1341         static Change change;
1342
1343         BufferParams const & bp = buf.params();
1344
1345         if (token[0] != '\\') {
1346                 string::const_iterator cit = token.begin();
1347                 for (; cit != token.end(); ++cit) {
1348                         par.insertChar(par.size(), (*cit), font, change);
1349                 }
1350         } else if (token == "\\begin_layout") {
1351                 lex.eatLine();
1352                 string layoutname = lex.getString();
1353
1354                 font = LyXFont(LyXFont::ALL_INHERIT, bp.language);
1355                 change = Change();
1356
1357                 LyXTextClass const & tclass = bp.getLyXTextClass();
1358
1359                 if (layoutname.empty()) {
1360                         layoutname = tclass.defaultLayoutName();
1361                 }
1362
1363                 bool hasLayout = tclass.hasLayout(layoutname);
1364
1365                 if (!hasLayout) {
1366                         lyxerr << "Layout '" << layoutname << "' does not"
1367                                << " exist in textclass '" << tclass.name()
1368                                << "'." << endl;
1369                         lyxerr << "Trying to use default layout instead."
1370                                << endl;
1371                         layoutname = tclass.defaultLayoutName();
1372                 }
1373
1374                 par.layout(bp.getLyXTextClass()[layoutname]);
1375
1376                 // Test whether the layout is obsolete.
1377                 LyXLayout_ptr const & layout = par.layout();
1378                 if (!layout->obsoleted_by().empty())
1379                         par.layout(bp.getLyXTextClass()[layout->obsoleted_by()]);
1380
1381                 par.params().read(lex);
1382
1383         } else if (token == "\\end_layout") {
1384                 lyxerr << "Solitary \\end_layout in line " << lex.getLineNo() << "\n"
1385                        << "Missing \\begin_layout?.\n";
1386         } else if (token == "\\end_inset") {
1387                 lyxerr << "Solitary \\end_inset in line " << lex.getLineNo() << "\n"
1388                        << "Missing \\begin_inset?.\n";
1389         } else if (token == "\\begin_inset") {
1390                 InsetOld * inset = readInset(lex, buf);
1391                 if (inset)
1392                         par.insertInset(par.size(), inset, font, change);
1393                 else {
1394                         lex.eatLine();
1395                         string line = lex.getString();
1396                         buf.error(ErrorItem(_("Unknown Inset"), line,
1397                                             buf.paragraphs().back().id(),
1398                                             0, par.size()));
1399                         return 1;
1400                 }
1401         } else if (token == "\\family") {
1402                 lex.next();
1403                 font.setLyXFamily(lex.getString());
1404         } else if (token == "\\series") {
1405                 lex.next();
1406                 font.setLyXSeries(lex.getString());
1407         } else if (token == "\\shape") {
1408                 lex.next();
1409                 font.setLyXShape(lex.getString());
1410         } else if (token == "\\size") {
1411                 lex.next();
1412                 font.setLyXSize(lex.getString());
1413         } else if (token == "\\lang") {
1414                 lex.next();
1415                 string const tok = lex.getString();
1416                 Language const * lang = languages.getLanguage(tok);
1417                 if (lang) {
1418                         font.setLanguage(lang);
1419                 } else {
1420                         font.setLanguage(bp.language);
1421                         lex.printError("Unknown language `$$Token'");
1422                 }
1423         } else if (token == "\\numeric") {
1424                 lex.next();
1425                 font.setNumber(font.setLyXMisc(lex.getString()));
1426         } else if (token == "\\emph") {
1427                 lex.next();
1428                 font.setEmph(font.setLyXMisc(lex.getString()));
1429         } else if (token == "\\bar") {
1430                 lex.next();
1431                 string const tok = lex.getString();
1432
1433                 if (tok == "under")
1434                         font.setUnderbar(LyXFont::ON);
1435                 else if (tok == "no")
1436                         font.setUnderbar(LyXFont::OFF);
1437                 else if (tok == "default")
1438                         font.setUnderbar(LyXFont::INHERIT);
1439                 else
1440                         lex.printError("Unknown bar font flag "
1441                                        "`$$Token'");
1442         } else if (token == "\\noun") {
1443                 lex.next();
1444                 font.setNoun(font.setLyXMisc(lex.getString()));
1445         } else if (token == "\\color") {
1446                 lex.next();
1447                 font.setLyXColor(lex.getString());
1448         } else if (token == "\\InsetSpace" || token == "\\SpecialChar") {
1449
1450                 // Insets don't make sense in a free-spacing context! ---Kayvan
1451                 if (par.isFreeSpacing()) {
1452                         if (token == "\\InsetSpace")
1453                                 par.insertChar(par.size(), ' ', font, change);
1454                         else if (lex.isOK()) {
1455                                 lex.next();
1456                                 string const next_token = lex.getString();
1457                                 if (next_token == "\\-")
1458                                         par.insertChar(par.size(), '-', font, change);
1459                                 else {
1460                                         lex.printError("Token `$$Token' "
1461                                                        "is in free space "
1462                                                        "paragraph layout!");
1463                                 }
1464                         }
1465                 } else {
1466                         auto_ptr<InsetOld> inset;
1467                         if (token == "\\SpecialChar" )
1468                                 inset.reset(new InsetSpecialChar);
1469                         else
1470                                 inset.reset(new InsetSpace);
1471                         inset->read(buf, lex);
1472                         par.insertInset(par.size(), inset.release(),
1473                                         font, change);
1474                 }
1475         } else if (token == "\\i") {
1476                 auto_ptr<InsetOld> inset(new InsetLatexAccent);
1477                 inset->read(buf, lex);
1478                 par.insertInset(par.size(), inset.release(), font, change);
1479         } else if (token == "\\backslash") {
1480                 par.insertChar(par.size(), '\\', font, change);
1481         } else if (token == "\\newline") {
1482                 auto_ptr<InsetOld> inset(new InsetNewline);
1483                 inset->read(buf, lex);
1484                 par.insertInset(par.size(), inset.release(), font, change);
1485         } else if (token == "\\LyXTable") {
1486                 auto_ptr<InsetOld> inset(new InsetTabular(buf));
1487                 inset->read(buf, lex);
1488                 par.insertInset(par.size(), inset.release(), font, change);
1489         } else if (token == "\\bibitem") {
1490                 InsetCommandParams p("bibitem", "dummy");
1491                 auto_ptr<InsetBibitem> inset(new InsetBibitem(p));
1492                 inset->read(buf, lex);
1493                 par.insertInset(par.size(), inset.release(), font, change);
1494         } else if (token == "\\hfill") {
1495                 par.insertInset(par.size(), new InsetHFill, font, change);
1496         } else if (token == "\\lyxline") {
1497                 par.insertInset(par.size(), new InsetLine, font, change);
1498         } else if (token == "\\newpage") {
1499                 par.insertInset(par.size(), new InsetPagebreak, font, change);
1500         } else if (token == "\\change_unchanged") {
1501                 // Hack ! Needed for empty paragraphs :/
1502                 // FIXME: is it still ??
1503                 if (!par.size())
1504                         par.cleanChanges();
1505                 change = Change(Change::UNCHANGED);
1506         } else if (token == "\\change_inserted") {
1507                 lex.nextToken();
1508                 istringstream is(lex.getString());
1509                 int aid;
1510                 lyx::time_type ct;
1511                 is >> aid >> ct;
1512                 change = Change(Change::INSERTED, bp.author_map[aid], ct);
1513         } else if (token == "\\change_deleted") {
1514                 lex.nextToken();
1515                 istringstream is(lex.getString());
1516                 int aid;
1517                 lyx::time_type ct;
1518                 is >> aid >> ct;
1519                 change = Change(Change::DELETED, bp.author_map[aid], ct);
1520         } else {
1521                 lex.eatLine();
1522                 string const s = bformat(_("Unknown token: %1$s %2$s\n"),
1523                         token, lex.getString());
1524
1525                 buf.error(ErrorItem(_("Unknown token"), s,
1526                                     buf.paragraphs().back().id(),
1527                                     0, par.size()));
1528                 return 1;
1529         }
1530         return 0;
1531 }
1532
1533 }
1534
1535
1536 int readParagraph(Buffer & buf, Paragraph & par, LyXLex & lex)
1537 {
1538         int unknown = 0;
1539
1540         lex.nextToken();
1541         string token = lex.getString();
1542
1543         while (lex.isOK()) {
1544
1545                 unknown += readParToken(buf, par, lex, token);
1546
1547                 lex.nextToken();
1548                 token = lex.getString();
1549
1550                 if (token.empty())
1551                         continue;
1552
1553                 if (token == "\\end_layout") {
1554                         //Ok, paragraph finished
1555                         break;
1556                 }
1557
1558                 lyxerr[Debug::PARSER] << "Handling paragraph token: `"
1559                                       << token << '\'' << endl;
1560                 if (token == "\\begin_layout" || token == "\\end_document"
1561                     || token == "\\end_inset" || token == "\\begin_deeper"
1562                     || token == "\\end_deeper") {
1563                         lex.pushToken(token);
1564                         lyxerr << "Paragraph ended in line "
1565                                << lex.getLineNo() << "\n"
1566                                << "Missing \\end_layout.\n";
1567                         break;
1568                 }
1569         }
1570
1571         return unknown;
1572 }
1573
1574
1575 LyXFont const outerFont(ParagraphList::iterator pit,
1576                         ParagraphList const & plist)
1577 {
1578         Paragraph::depth_type par_depth = pit->getDepth();
1579         LyXFont tmpfont(LyXFont::ALL_INHERIT);
1580
1581         // Resolve against environment font information
1582         while (pit != const_cast<ParagraphList&>(plist).end() &&
1583                par_depth && !tmpfont.resolved()) {
1584                 pit = outerHook(pit, plist);
1585                 if (pit != const_cast<ParagraphList&>(plist).end()) {
1586                         tmpfont.realize(pit->layout()->font);
1587                         par_depth = pit->getDepth();
1588                 }
1589         }
1590
1591         return tmpfont;
1592 }
1593
1594
1595 ParagraphList::iterator outerPar(Buffer const & buf, InsetOld const * inset)
1596 {
1597         ParIterator pit = const_cast<Buffer &>(buf).par_iterator_begin();
1598         ParIterator end = const_cast<Buffer &>(buf).par_iterator_end();
1599         for ( ; pit != end; ++pit) {
1600
1601                 ParagraphList * plist;
1602                 // the second '=' below is intentional
1603                 for (int i = 0; (plist = inset->getParagraphs(i)); ++i)
1604                         if (plist == &pit.plist())
1605                                 return pit.outerPar();
1606
1607                 InsetList::iterator ii = pit->insetlist.begin();
1608                 InsetList::iterator iend = pit->insetlist.end();
1609                 for ( ; ii != iend; ++ii)
1610                         if (ii->inset == inset)
1611                                 return pit.outerPar();
1612         }
1613         lyxerr << "outerPar: should not happen" << endl;
1614         BOOST_ASSERT(false);
1615         return const_cast<Buffer &>(buf).paragraphs().end(); // shut up compiler
1616 }
1617
1618
1619 Paragraph const & ownerPar(Buffer const & buf, InsetOld const * inset)
1620 {
1621         ParConstIterator pit = buf.par_iterator_begin();
1622         ParConstIterator end = buf.par_iterator_end();
1623         for ( ; pit != end; ++pit) {
1624                 ParagraphList * plist;
1625                 // the second '=' below is intentional
1626                 for (int i = 0; (plist = inset->getParagraphs(i)); ++i)
1627                         if (plist == &pit.plist())
1628                                 return *pit.pit();
1629
1630                 InsetList::const_iterator ii = pit->insetlist.begin();
1631                 InsetList::const_iterator iend = pit->insetlist.end();
1632                 for ( ; ii != iend; ++ii)
1633                         if (ii->inset == inset)
1634                                 return *pit.pit();
1635         }
1636         lyxerr << "ownerPar: should not happen" << endl;
1637         BOOST_ASSERT(false);
1638         return buf.paragraphs().front(); // shut up compiler
1639 }