]> git.lyx.org Git - lyx.git/blob - src/paragraph_funcs.C
added a parseError signal to Buffer and use it
[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 #include "paragraph_pimpl.h"
15 #include "buffer.h"
16 #include "ParagraphParameters.h"
17 #include "lyxtextclasslist.h"
18 #include "debug.h"
19 #include "gettext.h"
20 #include "language.h"
21 #include "encoding.h"
22 #include "lyxrc.h"
23 #include "lyxlex.h"
24 #include "factory.h"
25 #include "Lsstream.h"
26 #include "support/lstrings.h"
27 #include "insets/insetoptarg.h"
28 #include "insets/insetcommandparams.h"
29 #include "insets/insetbibitem.h"
30 #include "insets/insetspace.h"
31 #include "insets/insetspecialchar.h"
32 #include "insets/insetlatexaccent.h"
33 #include "insets/insettabular.h"
34 #include "insets/insethfill.h"
35 #include "insets/insetnewline.h"
36
37 extern string bibitemWidest(Buffer const *);
38
39 using lyx::pos_type;
40 //using lyx::layout_type;
41 using std::endl;
42 using std::ostream;
43
44
45 void breakParagraph(BufferParams const & bparams,
46                     ParagraphList & paragraphs,
47                     ParagraphList::iterator par,
48                     pos_type pos,
49                     int flag)
50 {
51         // create a new paragraph, and insert into the list
52         ParagraphList::iterator tmp = paragraphs.insert(boost::next(par),
53                                                         Paragraph());
54
55         // without doing that we get a crash when typing <Return> at the
56         // end of a paragraph
57         tmp->layout(bparams.getLyXTextClass().defaultLayout());
58         // remember to set the inset_owner
59         tmp->setInsetOwner(par->inInset());
60
61         if (bparams.tracking_changes)
62                 tmp->trackChanges();
63
64         // this is an idea for a more userfriendly layout handling, I will
65         // see what the users say
66
67         // layout stays the same with latex-environments
68         if (flag) {
69                 tmp->layout(par->layout());
70                 tmp->setLabelWidthString(par->params().labelWidthString());
71         }
72
73         bool const isempty = (par->allowEmpty() && par->empty());
74
75         if (!isempty && (par->size() > pos || par->empty() || flag == 2)) {
76                 tmp->layout(par->layout());
77                 tmp->params().align(par->params().align());
78                 tmp->setLabelWidthString(par->params().labelWidthString());
79
80                 tmp->params().lineBottom(par->params().lineBottom());
81                 par->params().lineBottom(false);
82                 tmp->params().pagebreakBottom(par->params().pagebreakBottom());
83                 par->params().pagebreakBottom(false);
84                 tmp->params().spaceBottom(par->params().spaceBottom());
85                 par->params().spaceBottom(VSpace(VSpace::NONE));
86
87                 tmp->params().depth(par->params().depth());
88                 tmp->params().noindent(par->params().noindent());
89
90                 // copy everything behind the break-position
91                 // to the new paragraph
92
93 #ifdef WITH_WARNINGS
94 #warning this seems wrong
95 #endif
96                 /* FIXME: if !keepempty, empty() == true, then we reach
97                  * here with size() == 0. So pos_end becomes - 1. Why
98                  * doesn't this cause problems ???
99                  */
100                 pos_type pos_end = par->size() - 1;
101                 pos_type i = pos;
102                 pos_type j = pos;
103
104                 for (; i <= pos_end; ++i) {
105                         Change::Type change(par->lookupChange(i));
106                         par->cutIntoMinibuffer(bparams, i);
107                         if (tmp->insertFromMinibuffer(j - pos)) {
108                                 tmp->setChange(j - pos, change);
109                                 ++j;
110                         }
111                 }
112                 for (i = pos_end; i >= pos; --i) {
113                         par->eraseIntern(i);
114                 }
115         }
116
117         if (pos)
118                 return;
119
120         tmp->params().lineTop(par->params().lineTop());
121         tmp->params().pagebreakTop(par->params().pagebreakTop());
122         tmp->params().spaceTop(par->params().spaceTop());
123         par->params().clear();
124
125         par->layout(bparams.getLyXTextClass().defaultLayout());
126
127         // layout stays the same with latex-environments
128         if (flag) {
129                 par->layout(tmp->layout());
130                 par->setLabelWidthString(tmp->params().labelWidthString());
131                 par->params().depth(tmp->params().depth());
132         }
133
134         // subtle, but needed to get empty pars working right
135         if (bparams.tracking_changes) {
136                 if (!par->size()) {
137                         par->cleanChanges();
138                 } else if (!tmp->size()) {
139                         tmp->cleanChanges();
140                 }
141         }
142 }
143
144
145 void breakParagraphConservative(BufferParams const & bparams,
146                                 ParagraphList & paragraphs,
147                                 ParagraphList::iterator par,
148                                 pos_type pos)
149 {
150         // create a new paragraph
151         ParagraphList::iterator tmp = paragraphs.insert(boost::next(par),
152                                                         Paragraph());
153         tmp->makeSameLayout(*par);
154
155         // When can pos > size()?
156         // I guess pos == size() is possible.
157         if (par->size() > pos) {
158                 // copy everything behind the break-position to the new
159                 // paragraph
160                 pos_type pos_end = par->size() - 1;
161
162                 for (pos_type i = pos, j = pos; i <= pos_end; ++i) {
163                         par->cutIntoMinibuffer(bparams, i);
164                         if (tmp->insertFromMinibuffer(j - pos))
165                                 ++j;
166                 }
167
168                 for (pos_type k = pos_end; k >= pos; --k) {
169                         par->erase(k);
170                 }
171         }
172 }
173
174
175 void mergeParagraph(BufferParams const & bparams,
176                     ParagraphList & paragraphs,
177                     ParagraphList::iterator par)
178 {
179         ParagraphList::iterator the_next = boost::next(par);
180
181         // first the DTP-stuff
182         par->params().lineBottom(the_next->params().lineBottom());
183         par->params().spaceBottom(the_next->params().spaceBottom());
184         par->params().pagebreakBottom(the_next->params().pagebreakBottom());
185
186         pos_type pos_end = the_next->size() - 1;
187         pos_type pos_insert = par->size();
188
189         // ok, now copy the paragraph
190         for (pos_type i = 0, j = 0; i <= pos_end; ++i) {
191                 the_next->cutIntoMinibuffer(bparams, i);
192                 if (par->insertFromMinibuffer(pos_insert + j))
193                         ++j;
194         }
195
196         paragraphs.erase(the_next);
197 }
198
199
200 ParagraphList::iterator depthHook(ParagraphList::iterator pit,
201                                   ParagraphList const & plist,
202                                   Paragraph::depth_type depth)
203 {
204         ParagraphList::iterator newpit = pit;
205         ParagraphList::iterator beg = const_cast<ParagraphList&>(plist).begin();
206
207         if (newpit != beg)
208                 --newpit;
209
210         while (newpit !=  beg && newpit->getDepth() > depth) {
211                 --newpit;
212         }
213
214         if (newpit->getDepth() > depth)
215                 return pit;
216
217         return newpit;
218 }
219
220
221 ParagraphList::iterator outerHook(ParagraphList::iterator pit,
222                                   ParagraphList const & plist)
223 {
224         if (!pit->getDepth())
225                 return const_cast<ParagraphList&>(plist).end();
226         return depthHook(pit, plist,
227                          Paragraph::depth_type(pit->getDepth() - 1));
228 }
229
230
231 bool isFirstInSequence(ParagraphList::iterator pit,
232                        ParagraphList const & plist)
233 {
234         ParagraphList::iterator dhook = depthHook(pit, plist, pit->getDepth());
235         return (dhook == pit
236                 || dhook->layout() != pit->layout()
237                 || dhook->getDepth() != pit->getDepth());
238 }
239
240
241 int getEndLabel(ParagraphList::iterator p, ParagraphList const & plist)
242 {
243         ParagraphList::iterator pit = p;
244         Paragraph::depth_type par_depth = p->getDepth();
245         while (pit != const_cast<ParagraphList&>(plist).end()) {
246                 LyXLayout_ptr const & layout = pit->layout();
247                 int const endlabeltype = layout->endlabeltype;
248
249                 if (endlabeltype != END_LABEL_NO_LABEL) {
250                         if (boost::next(p) == const_cast<ParagraphList&>(plist).end())
251                                 return endlabeltype;
252
253                         Paragraph::depth_type const next_depth = boost::next(p)->getDepth();
254                         if (par_depth > next_depth ||
255                             (par_depth == next_depth &&
256                              layout != boost::next(p)->layout()))
257                                 return endlabeltype;
258                         break;
259                 }
260                 if (par_depth == 0)
261                         break;
262                 pit = outerHook(pit, plist);
263                 if (pit != const_cast<ParagraphList&>(plist).end())
264                         par_depth = pit->getDepth();
265         }
266         return END_LABEL_NO_LABEL;
267 }
268
269
270 namespace {
271
272 ParagraphList::iterator
273 TeXEnvironment(Buffer const * buf,
274                ParagraphList const & paragraphs,
275                ParagraphList::iterator pit,
276                ostream & os, TexRow & texrow,
277                LatexRunParams const & runparams);
278
279 ParagraphList::iterator
280 TeXOnePar(Buffer const * buf,
281           ParagraphList const & paragraphs,
282           ParagraphList::iterator pit,
283           ostream & os, TexRow & texrow,
284           LatexRunParams const & runparams,
285           string const & everypar = string());
286
287
288 ParagraphList::iterator
289 TeXDeeper(Buffer const * buf,
290           ParagraphList const & paragraphs,
291           ParagraphList::iterator pit,
292           ostream & os, TexRow & texrow,
293           LatexRunParams const & runparams)
294 {
295         lyxerr[Debug::LATEX] << "TeXDeeper...     " << &*pit << endl;
296         ParagraphList::iterator par = pit;
297
298         while (par != const_cast<ParagraphList&>(paragraphs).end() &&
299                      par->params().depth() == pit->params().depth()) {
300                 if (par->layout()->isEnvironment()) {
301                         par = TeXEnvironment(buf, paragraphs, par,
302                                              os, texrow, runparams);
303                 } else {
304                         par = TeXOnePar(buf, paragraphs, par,
305                                              os, texrow, runparams);
306                 }
307         }
308         lyxerr[Debug::LATEX] << "TeXDeeper...done " << &*par << endl;
309
310         return par;
311 }
312
313
314 ParagraphList::iterator
315 TeXEnvironment(Buffer const * buf,
316                ParagraphList const & paragraphs,
317                ParagraphList::iterator pit,
318                ostream & os, TexRow & texrow,
319                LatexRunParams const & runparams)
320 {
321         lyxerr[Debug::LATEX] << "TeXEnvironment...     " << &*pit << endl;
322
323         BufferParams const & bparams = buf->params;
324
325         LyXLayout_ptr const & style = pit->layout();
326
327         Language const * language = pit->getParLanguage(bparams);
328         Language const * doc_language = bparams.language;
329         Language const * previous_language =
330                 (pit != const_cast<ParagraphList&>(paragraphs).begin())
331                 ? boost::prior(pit)->getParLanguage(bparams)
332                 : doc_language;
333         if (language->babel() != previous_language->babel()) {
334
335                 if (!lyxrc.language_command_end.empty() &&
336                     previous_language->babel() != doc_language->babel()) {
337                         os << subst(lyxrc.language_command_end, "$$lang",
338                                     previous_language->babel())
339                            << endl;
340                         texrow.newline();
341                 }
342
343                 if (lyxrc.language_command_end.empty() ||
344                     language->babel() != doc_language->babel()) {
345                         os << subst(lyxrc.language_command_begin, "$$lang",
346                                     language->babel())
347                            << endl;
348                         texrow.newline();
349                 }
350         }
351
352         bool leftindent_open = false;
353         if (!pit->params().leftIndent().zero()) {
354                 os << "\\begin{LyXParagraphLeftIndent}{" <<
355                         pit->params().leftIndent().asLatexString() << "}\n";
356                 texrow.newline();
357                 leftindent_open = true;
358         }
359
360         if (style->isEnvironment()) {
361                 if (style->latextype == LATEX_LIST_ENVIRONMENT) {
362                         os << "\\begin{" << style->latexname() << "}{"
363                            << pit->params().labelWidthString() << "}\n";
364                 } else if (style->labeltype == LABEL_BIBLIO) {
365                         // ale970405
366                         os << "\\begin{" << style->latexname() << "}{"
367                            <<  bibitemWidest(buf)
368                            << "}\n";
369                 } else if (style->latextype == LATEX_ITEM_ENVIRONMENT) {
370                         os << "\\begin{" << style->latexname() << '}'
371                            << style->latexparam() << '\n';
372                 } else
373                         os << "\\begin{" << style->latexname() << '}'
374                            << style->latexparam() << '\n';
375                 texrow.newline();
376         }
377         ParagraphList::iterator par = pit;
378         do {
379                 par = TeXOnePar(buf, paragraphs, par, os, texrow, runparams);
380
381                 if (par != const_cast<ParagraphList&>(paragraphs).end() && par->params().depth() > pit->params().depth()) {
382                             if (par->layout()->isParagraph()) {
383
384                             // Thinko!
385                             // How to handle this? (Lgb)
386                             //&& !suffixIs(os, "\n\n")
387                                     //) {
388                                 // There should be at least one '\n' already
389                                 // but we need there to be two for Standard
390                                 // paragraphs that are depth-increment'ed to be
391                                 // output correctly.  However, tables can
392                                 // also be paragraphs so don't adjust them.
393                                 // ARRae
394                                 // Thinkee:
395                                 // Will it ever harm to have one '\n' too
396                                 // many? i.e. that we sometimes will have
397                                 // three in a row. (Lgb)
398                                 os << '\n';
399                                 texrow.newline();
400                         }
401                         par = TeXDeeper(buf, paragraphs, par, os, texrow,
402                                         runparams);
403                 }
404         } while (par != const_cast<ParagraphList&>(paragraphs).end()
405                  && par->layout() == pit->layout()
406                  && par->params().depth() == pit->params().depth()
407                  && par->params().leftIndent() == pit->params().leftIndent());
408
409         if (style->isEnvironment()) {
410                 os << "\\end{" << style->latexname() << "}\n";
411                 texrow.newline();
412         }
413
414         if (leftindent_open) {
415                 os << "\\end{LyXParagraphLeftIndent}\n";
416                 texrow.newline();
417         }
418
419         lyxerr[Debug::LATEX] << "TeXEnvironment...done " << &*par << endl;
420         return par;  // ale970302
421 }
422
423
424 InsetOptArg * optArgInset(Paragraph const & par)
425 {
426         // Find the entry.
427         InsetList::const_iterator it = par.insetlist.begin();
428         InsetList::const_iterator end = par.insetlist.end();
429         for (; it != end; ++it) {
430                 Inset * ins = it->inset;
431                 if (ins->lyxCode() == Inset::OPTARG_CODE) {
432                         return static_cast<InsetOptArg *>(ins);
433                 }
434         }
435         return 0;
436 }
437
438
439 ParagraphList::iterator
440 TeXOnePar(Buffer const * buf,
441           ParagraphList const & paragraphs,
442           ParagraphList::iterator pit,
443           ostream & os, TexRow & texrow,
444           LatexRunParams const & runparams,
445           string const & everypar)
446 {
447         lyxerr[Debug::LATEX] << "TeXOnePar...     " << &*pit << " '" << everypar
448 << "'" << endl;
449         BufferParams const & bparams = buf->params;
450
451         Inset const * in = pit->inInset();
452         bool further_blank_line = false;
453         LyXLayout_ptr style;
454
455         // well we have to check if we are in an inset with unlimited
456         // lenght (all in one row) if that is true then we don't allow
457         // any special options in the paragraph and also we don't allow
458         // any environment other then "Standard" to be valid!
459         if ((in == 0) || !in->forceDefaultParagraphs(in)) {
460                 style = pit->layout();
461
462                 if (pit->params().startOfAppendix()) {
463                         os << "\\appendix\n";
464                         texrow.newline();
465                 }
466
467                 if (!pit->params().spacing().isDefault()
468                         && (pit == const_cast<ParagraphList&>(paragraphs).begin() || !boost::prior(pit)->hasSameLayout(*pit))) {
469                         os << pit->params().spacing().writeEnvirBegin() << '\n';
470                         texrow.newline();
471                 }
472
473                 if (style->isCommand()) {
474                         os << '\n';
475                         texrow.newline();
476                 }
477
478                 if (pit->params().pagebreakTop()) {
479                         os << "\\newpage";
480                         further_blank_line = true;
481                 }
482                 if (pit->params().spaceTop().kind() != VSpace::NONE) {
483                         os << pit->params().spaceTop().asLatexCommand(bparams);
484                         further_blank_line = true;
485                 }
486
487                 if (pit->params().lineTop()) {
488                         os << "\\lyxline{\\"
489                            << pit->getFont(bparams, 0, outerFont(pit, paragraphs)).latexSize()
490                            << '}'
491                            << "\\vspace{-1\\parskip}";
492                         further_blank_line = true;
493                 }
494
495                 if (further_blank_line) {
496                         os << '\n';
497                         texrow.newline();
498                 }
499         } else {
500                 style = bparams.getLyXTextClass().defaultLayout();
501         }
502
503         Language const * language = pit->getParLanguage(bparams);
504         Language const * doc_language = bparams.language;
505         Language const * previous_language =
506                 (pit != const_cast<ParagraphList&>(paragraphs).begin())
507                 ? boost::prior(pit)->getParLanguage(bparams)
508                 : doc_language;
509
510         if (language->babel() != previous_language->babel()
511             // check if we already put language command in TeXEnvironment()
512             && !(style->isEnvironment()
513                  && (pit == const_cast<ParagraphList&>(paragraphs).begin() ||
514                      (boost::prior(pit)->layout() != pit->layout() &&
515                       boost::prior(pit)->getDepth() <= pit->getDepth())
516                      || boost::prior(pit)->getDepth() < pit->getDepth())))
517         {
518                 if (!lyxrc.language_command_end.empty() &&
519                     previous_language->babel() != doc_language->babel())
520                 {
521                         os << subst(lyxrc.language_command_end, "$$lang",
522                                     previous_language->babel())
523                            << endl;
524                         texrow.newline();
525                 }
526
527                 if (lyxrc.language_command_end.empty() ||
528                     language->babel() != doc_language->babel())
529                 {
530                         os << subst(lyxrc.language_command_begin, "$$lang",
531                                     language->babel())
532                            << endl;
533                         texrow.newline();
534                 }
535         }
536
537         if (bparams.inputenc == "auto" &&
538             language->encoding() != previous_language->encoding()) {
539                 os << "\\inputencoding{"
540                    << language->encoding()->LatexName()
541                    << "}\n";
542                 texrow.newline();
543         }
544
545         switch (style->latextype) {
546         case LATEX_COMMAND:
547                 os << '\\' << style->latexname();
548
549                 // Separate handling of optional argument inset.
550                 if (style->optionalargs == 1) {
551                         InsetOptArg * it = optArgInset(*pit);
552                         if (it)
553                                 it->latexOptional(buf, os, runparams);
554                 }
555                 else
556                         os << style->latexparam();
557                 break;
558         case LATEX_ITEM_ENVIRONMENT:
559         case LATEX_LIST_ENVIRONMENT:
560                 os << "\\item ";
561                 break;
562         case LATEX_BIB_ENVIRONMENT:
563                 // ignore this, the inset will write itself
564                 break;
565         default:
566                 break;
567         }
568
569         os << everypar;
570         bool need_par = pit->simpleTeXOnePar(buf, bparams,
571                                              outerFont(pit, paragraphs),
572                                              os, texrow, runparams);
573
574         // Make sure that \\par is done with the font of the last
575         // character if this has another size as the default.
576         // This is necessary because LaTeX (and LyX on the screen)
577         // calculates the space between the baselines according
578         // to this font. (Matthias)
579         //
580         // Is this really needed ? (Dekel)
581         // We do not need to use to change the font for the last paragraph
582         // or for a command.
583         LyXFont const outerfont(outerFont(pit, paragraphs));
584
585         LyXFont const font =
586                 (pit->empty()
587                  ? pit->getLayoutFont(bparams, outerfont)
588                  : pit->getFont(bparams, pit->size() - 1, outerfont));
589
590         bool is_command = style->isCommand();
591
592         if (style->resfont.size() != font.size()
593             && boost::next(pit) != const_cast<ParagraphList&>(paragraphs).end()
594             && !is_command) {
595                 if (!need_par)
596                         os << '{';
597                 os << "\\" << font.latexSize() << " \\par}";
598         } else if (need_par) {
599                 os << "\\par}";
600         } else if (is_command)
601                 os << '}';
602
603         switch (style->latextype) {
604         case LATEX_ITEM_ENVIRONMENT:
605         case LATEX_LIST_ENVIRONMENT:
606                 if (boost::next(pit) != const_cast<ParagraphList&>(paragraphs).end()
607                     && (pit->params().depth() < boost::next(pit)->params().depth())) {
608                         os << '\n';
609                         texrow.newline();
610                 }
611                 break;
612         case LATEX_ENVIRONMENT: {
613                 // if its the last paragraph of the current environment
614                 // skip it otherwise fall through
615                 ParagraphList::iterator next = boost::next(pit);
616
617                 if (next != const_cast<ParagraphList&>(paragraphs).end()
618                     && (next->layout() != pit->layout()
619                         || next->params().depth() != pit->params().depth()))
620                         break;
621         }
622
623                 // fall through possible
624         default:
625                 // we don't need it for the last paragraph!!!
626                 if (boost::next(pit) != const_cast<ParagraphList&>(paragraphs).end()) {
627                         os << '\n';
628                         texrow.newline();
629                 }
630         }
631
632         if ((in == 0) || !in->forceDefaultParagraphs(in)) {
633                 further_blank_line = false;
634                 if (pit->params().lineBottom()) {
635                         os << "\\lyxline{\\" << font.latexSize() << '}';
636                         further_blank_line = true;
637                 }
638
639                 if (pit->params().spaceBottom().kind() != VSpace::NONE) {
640                         os << pit->params().spaceBottom().asLatexCommand(bparams);
641                         further_blank_line = true;
642                 }
643
644                 if (pit->params().pagebreakBottom()) {
645                         os << "\\newpage";
646                         further_blank_line = true;
647                 }
648
649                 if (further_blank_line) {
650                         os << '\n';
651                         texrow.newline();
652                 }
653
654                 if (!pit->params().spacing().isDefault()
655                         && (boost::next(pit) == const_cast<ParagraphList&>(paragraphs).end()|| !boost::next(pit)->hasSameLayout(*pit))) {
656                         os << pit->params().spacing().writeEnvirEnd() << '\n';
657                         texrow.newline();
658                 }
659         }
660
661         // we don't need it for the last paragraph!!!
662         if (boost::next(pit) != const_cast<ParagraphList&>(paragraphs).end()) {
663                 os << '\n';
664                 texrow.newline();
665         } else {
666                 // Since \selectlanguage write the language to the aux file,
667                 // we need to reset the language at the end of footnote or
668                 // float.
669
670                 if (language->babel() != doc_language->babel()) {
671                         if (lyxrc.language_command_end.empty())
672                                 os << subst(lyxrc.language_command_begin,
673                                             "$$lang",
674                                             doc_language->babel())
675                                    << endl;
676                         else
677                                 os << subst(lyxrc.language_command_end,
678                                             "$$lang",
679                                             language->babel())
680                                    << endl;
681                         texrow.newline();
682                 }
683         }
684
685         lyxerr[Debug::LATEX] << "TeXOnePar...done " << &*boost::next(pit) << endl;
686         return ++pit;
687 }
688
689 } // anon namespace
690
691
692 //
693 // LaTeX all paragraphs from par to endpar, if endpar == 0 then to the end
694 //
695 void latexParagraphs(Buffer const * buf,
696                      ParagraphList const & paragraphs,
697                      ostream & os,
698                      TexRow & texrow,
699                      LatexRunParams const & runparams,
700                      string const & everypar)
701 {
702         bool was_title = false;
703         bool already_title = false;
704         LyXTextClass const & tclass = buf->params.getLyXTextClass();
705         ParagraphList::iterator par = const_cast<ParagraphList&>(paragraphs).begin();
706         ParagraphList::iterator endpar = const_cast<ParagraphList&>(paragraphs).end();
707
708         // if only_body
709         while (par != endpar) {
710                 Inset * in = par->inInset();
711                 // well we have to check if we are in an inset with unlimited
712                 // length (all in one row) if that is true then we don't allow
713                 // any special options in the paragraph and also we don't allow
714                 // any environment other then "Standard" to be valid!
715                 if ((in == 0) || !in->forceDefaultParagraphs(in)) {
716                         LyXLayout_ptr const & layout = par->layout();
717
718                         if (layout->intitle) {
719                                 if (already_title) {
720                                         lyxerr <<"Error in latexParagraphs: You"
721                                                 " should not mix title layouts"
722                                                 " with normal ones." << endl;
723                                 } else if (!was_title) {
724                                         was_title = true;
725                                         if (tclass.titletype() == TITLE_ENVIRONMENT) {
726                                                 os << "\\begin{"
727                                                     << tclass.titlename()
728                                                     << "}\n";
729                                                 texrow.newline();
730                                         }
731                                 }
732                         } else if (was_title && !already_title) {
733                                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
734                                         os << "\\end{" << tclass.titlename()
735                                             << "}\n";
736                                 }
737                                 else {
738                                         os << "\\" << tclass.titlename()
739                                             << "\n";
740                                 }
741                                 texrow.newline();
742                                 already_title = true;
743                                 was_title = false;
744                         }
745
746                         if (layout->is_environment) {
747                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
748                                                 runparams, everypar);
749                         } else if (layout->isEnvironment() ||
750                                 !par->params().leftIndent().zero())
751                         {
752                                 par = TeXEnvironment(buf, paragraphs, par, os,
753                                                      texrow, runparams);
754                         } else {
755                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
756                                                 runparams, everypar);
757                         }
758                 } else {
759                         par = TeXOnePar(buf, paragraphs, par, os, texrow,
760                                         runparams, everypar);
761                 }
762         }
763         // It might be that we only have a title in this document
764         if (was_title && !already_title) {
765                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
766                         os << "\\end{" << tclass.titlename()
767                             << "}\n";
768                 }
769                 else {
770                         os << "\\" << tclass.titlename()
771                             << "\n";
772                                 }
773                 texrow.newline();
774         }
775 }
776
777
778 namespace {
779
780 int readParToken(Buffer & buf, Paragraph & par, LyXLex & lex, string const & token)
781 {
782         static LyXFont font;
783         static Change change;
784
785         BufferParams const & bp = buf.params;
786
787         if (token[0] != '\\') {
788                 string::const_iterator cit = token.begin();
789                 for (; cit != token.end(); ++cit) {
790                         par.insertChar(par.size(), (*cit), font, change);
791                 }
792         } else if (token == "\\layout") {
793                 lex.eatLine();
794                 string layoutname = lex.getString();
795
796                 font = LyXFont(LyXFont::ALL_INHERIT, bp.language);
797                 change = Change();
798
799                 LyXTextClass const & tclass = bp.getLyXTextClass();
800
801                 if (layoutname.empty()) {
802                         layoutname = tclass.defaultLayoutName();
803                 }
804
805                 bool hasLayout = tclass.hasLayout(layoutname);
806
807                 if (!hasLayout) {
808                         lyxerr << "Layout '" << layoutname << "' does not"
809                                << " exist in textclass '" << tclass.name()
810                                << "'." << endl;
811                         lyxerr << "Trying to use default layout instead."
812                                << endl;
813                         layoutname = tclass.defaultLayoutName();
814                 }
815
816 #ifdef USE_CAPTION
817                 // The is the compability reading of layout caption.
818                 if (compare_ascii_no_case(layoutname, "caption") == 0) {
819                         // We expect that the par we are now working on is
820                         // really inside a InsetText inside a InsetFloat.
821                         // We also know that captions can only be
822                         // one paragraph. (Lgb)
823
824                         // We should now read until the next "\layout"
825                         // is reached.
826                         // This is probably not good enough, what if the
827                         // caption is the last par in the document (Lgb)
828                         istream & ist = lex.getStream();
829                         stringstream ss;
830                         string line;
831                         int begin = 0;
832                         while (true) {
833                                 getline(ist, line);
834                                 if (prefixIs(line, "\\layout")) {
835                                         lex.pushToken(line);
836                                         break;
837                                 }
838                                 if (prefixIs(line, "\\begin_inset"))
839                                         ++begin;
840                                 if (prefixIs(line, "\\end_inset")) {
841                                         if (begin)
842                                                 --begin;
843                                         else {
844                                                 lex.pushToken(line);
845                                                 break;
846                                         }
847                                 }
848
849                                 ss << line << '\n';
850                         }
851
852                         // Now we should have the whole layout in ss
853                         // we should now be able to give this to the
854                         // caption inset.
855                         ss << "\\end_inset\n";
856
857                         // This seems like a bug in stringstream.
858                         // We really should be able to use ss
859                         // directly. (Lgb)
860                         istringstream is(ss.str());
861                         LyXLex tmplex(0, 0);
862                         tmplex.setStream(is);
863                         Inset * inset = new InsetCaption;
864                         inset->Read(this, tmplex);
865                         par.insertInset(pos, inset, font);
866                         ++pos;
867                 } else {
868 #endif
869                         par.layout(bp.getLyXTextClass()[layoutname]);
870
871                         // Test whether the layout is obsolete.
872                         LyXLayout_ptr const & layout = par.layout();
873                         if (!layout->obsoleted_by().empty())
874                                 par.layout(bp.getLyXTextClass()[layout->obsoleted_by()]);
875
876                         par.params().read(lex);
877 #if USE_CAPTION
878                 }
879 #endif
880
881         } else if (token == "\\end_inset") {
882                 lyxerr << "Solitary \\end_inset in line " << lex.getLineNo() << "\n"
883                        << "Missing \\begin_inset?.\n";
884                 // Simply ignore this. The insets do not have
885                 // to read this.
886                 // But insets should read it, it is a part of
887                 // the inset isn't it? Lgb.
888         } else if (token == "\\begin_inset") {
889                 Inset * i = readInset(lex, buf);
890                 par.insertInset(par.size(), i, font, change);
891         } else if (token == "\\family") {
892                 lex.next();
893                 font.setLyXFamily(lex.getString());
894         } else if (token == "\\series") {
895                 lex.next();
896                 font.setLyXSeries(lex.getString());
897         } else if (token == "\\shape") {
898                 lex.next();
899                 font.setLyXShape(lex.getString());
900         } else if (token == "\\size") {
901                 lex.next();
902                 font.setLyXSize(lex.getString());
903         } else if (token == "\\lang") {
904                 lex.next();
905                 string const tok = lex.getString();
906                 Language const * lang = languages.getLanguage(tok);
907                 if (lang) {
908                         font.setLanguage(lang);
909                 } else {
910                         font.setLanguage(bp.language);
911                         lex.printError("Unknown language `$$Token'");
912                 }
913         } else if (token == "\\numeric") {
914                 lex.next();
915                 font.setNumber(font.setLyXMisc(lex.getString()));
916         } else if (token == "\\emph") {
917                 lex.next();
918                 font.setEmph(font.setLyXMisc(lex.getString()));
919         } else if (token == "\\bar") {
920                 lex.next();
921                 string const tok = lex.getString();
922
923                 if (tok == "under")
924                         font.setUnderbar(LyXFont::ON);
925                 else if (tok == "no")
926                         font.setUnderbar(LyXFont::OFF);
927                 else if (tok == "default")
928                         font.setUnderbar(LyXFont::INHERIT);
929                 else
930                         lex.printError("Unknown bar font flag "
931                                        "`$$Token'");
932         } else if (token == "\\noun") {
933                 lex.next();
934                 font.setNoun(font.setLyXMisc(lex.getString()));
935         } else if (token == "\\color") {
936                 lex.next();
937                 font.setLyXColor(lex.getString());
938         } else if (token == "\\InsetSpace" || token == "\\SpecialChar") {
939
940                 // Insets don't make sense in a free-spacing context! ---Kayvan
941                 if (par.isFreeSpacing()) {
942                         if (token == "\\InsetSpace")
943                                 par.insertChar(par.size(), ' ', font, change);
944                         else if (lex.isOK()) {
945                                 lex.next();
946                                 string const next_token = lex.getString();
947                                 if (next_token == "\\-")
948                                         par.insertChar(par.size(), '-', font, change);
949                                 else {
950                                         lex.printError("Token `$$Token' "
951                                                        "is in free space "
952                                                        "paragraph layout!");
953                                 }
954                         }
955                 } else {
956                         Inset * inset = 0;
957                         if (token == "\\SpecialChar" )
958                                 inset = new InsetSpecialChar;
959                         else
960                                 inset = new InsetSpace;
961                         inset->read(&buf, lex);
962                         par.insertInset(par.size(), inset, font, change);
963                 }
964         } else if (token == "\\i") {
965                 Inset * inset = new InsetLatexAccent;
966                 inset->read(&buf, lex);
967                 par.insertInset(par.size(), inset, font, change);
968         } else if (token == "\\backslash") {
969                 par.insertChar(par.size(), '\\', font, change);
970         } else if (token == "\\newline") {
971                 Inset * inset = new InsetNewline;
972                 inset->read(&buf, lex);
973                 par.insertInset(par.size(), inset, font, change);
974         } else if (token == "\\LyXTable") {
975                 Inset * inset = new InsetTabular(buf);
976                 inset->read(&buf, lex);
977                 par.insertInset(par.size(), inset, font, change);
978         } else if (token == "\\bibitem") {
979                 InsetCommandParams p("bibitem", "dummy");
980                 InsetBibitem * inset = new InsetBibitem(p);
981                 inset->read(&buf, lex);
982                 par.insertInset(par.size(), inset, font, change);
983         } else if (token == "\\hfill") {
984                 par.insertInset(par.size(), new InsetHFill(), font, change);
985         } else if (token == "\\change_unchanged") {
986                 // Hack ! Needed for empty paragraphs :/
987                 // FIXME: is it still ??
988                 if (!par.size())
989                         par.cleanChanges();
990                 change = Change(Change::UNCHANGED);
991         } else if (token == "\\change_inserted") {
992                 lex.nextToken();
993                 istringstream is(STRCONV(lex.getString()));
994                 int aid;
995                 lyx::time_type ct;
996                 is >> aid >> ct;
997                 change = Change(Change::INSERTED, bp.author_map[aid], ct);
998         } else if (token == "\\change_deleted") {
999                 lex.nextToken();
1000                 istringstream is(STRCONV(lex.getString()));
1001                 int aid;
1002                 lyx::time_type ct;
1003                 is >> aid >> ct;
1004                 change = Change(Change::DELETED, bp.author_map[aid], ct);
1005         } else {
1006                 lex.eatLine();
1007                 string const s = bformat(_("Unknown token: %1$s %2$s\n"),
1008                         token, lex.getString());
1009
1010                 buf.parseError(ErrorItem(_("Unknown token"), s, 
1011                                          par.id(), 0, par.size()));
1012                 return 1;
1013         }
1014         return 0;
1015 }
1016
1017 }
1018
1019
1020 int readParagraph(Buffer & buf, Paragraph & par, LyXLex & lex)
1021 {
1022         int unknown = 0;
1023
1024         lex.nextToken();
1025         string token = lex.getString();
1026
1027         while (lex.isOK()) {
1028
1029                 unknown += readParToken(buf, par, lex, token);
1030
1031                 lex.nextToken();
1032                 token = lex.getString();
1033
1034                 if (token.empty())
1035                         continue;
1036
1037                 lyxerr[Debug::PARSER] << "Handling paragraph token: `"
1038                                       << token << '\'' << endl;
1039
1040                 // reached the next paragraph. FIXME: really we should
1041                 // change the file format to indicate the end of a par
1042                 // clearly, but for now, this hack will do
1043                 if (token == "\\layout" || token == "\\the_end"
1044                     || token == "\\end_inset" || token == "\\begin_deeper"
1045                     || token == "\\end_deeper") {
1046                         lex.pushToken(token);
1047                         break;
1048                 }
1049         }
1050
1051         return unknown;
1052 }
1053
1054
1055 LyXFont const outerFont(ParagraphList::iterator pit,
1056                         ParagraphList const & plist)
1057 {
1058         Paragraph::depth_type par_depth = pit->getDepth();
1059         LyXFont tmpfont(LyXFont::ALL_INHERIT);
1060
1061         // Resolve against environment font information
1062         while (pit != const_cast<ParagraphList&>(plist).end() &&
1063                par_depth && !tmpfont.resolved()) {
1064                 pit = outerHook(pit, plist);
1065                 if (pit != const_cast<ParagraphList&>(plist).end()) {
1066                         tmpfont.realize(pit->layout()->font);
1067                         par_depth = pit->getDepth();
1068                 }
1069         }
1070
1071         return tmpfont;
1072 }
1073
1074
1075 LyXFont const realizeFont(LyXFont const & font,
1076                           BufferParams const & params)
1077 {
1078         LyXTextClass const & tclass = params.getLyXTextClass();
1079         LyXFont tmpfont(font);
1080         tmpfont.realize(tclass.defaultfont());
1081         return tmpfont;
1082 }