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