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