]> git.lyx.org Git - lyx.git/blob - src/paragraph_funcs.C
Fix math fonts problem
[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 "language.h"
20 #include "encoding.h"
21 #include "lyxrc.h"
22 #include "support/lstrings.h"
23 #include "insets/insetoptarg.h"
24
25 extern string bibitemWidest(Buffer const *);
26
27 using lyx::pos_type;
28 //using lyx::layout_type;
29 using std::endl;
30 using std::ostream;
31
32 void breakParagraph(BufferParams const & bparams,
33                     Paragraph * par,
34                     pos_type pos,
35                     int flag)
36 {
37         // create a new paragraph
38         Paragraph * tmp = new Paragraph(par);
39         // without doing that we get a crash when typing <Return> at the
40         // end of a paragraph
41         tmp->layout(bparams.getLyXTextClass().defaultLayout());
42         // remember to set the inset_owner
43         tmp->setInsetOwner(par->inInset());
44
45         if (bparams.tracking_changes)
46                 tmp->trackChanges();
47
48         // this is an idea for a more userfriendly layout handling, I will
49         // see what the users say
50
51         // layout stays the same with latex-environments
52         if (flag) {
53                 tmp->layout(par->layout());
54                 tmp->setLabelWidthString(par->params().labelWidthString());
55         }
56
57         bool const isempty = (par->layout()->keepempty && par->empty());
58
59         if (!isempty && (par->size() > pos || par->empty() || flag == 2)) {
60                 tmp->layout(par->layout());
61                 tmp->params().align(par->params().align());
62                 tmp->setLabelWidthString(par->params().labelWidthString());
63
64                 tmp->params().lineBottom(par->params().lineBottom());
65                 par->params().lineBottom(false);
66                 tmp->params().pagebreakBottom(par->params().pagebreakBottom());
67                 par->params().pagebreakBottom(false);
68                 tmp->params().spaceBottom(par->params().spaceBottom());
69                 par->params().spaceBottom(VSpace(VSpace::NONE));
70
71                 tmp->params().depth(par->params().depth());
72                 tmp->params().noindent(par->params().noindent());
73
74                 // copy everything behind the break-position
75                 // to the new paragraph
76
77 #ifdef WITH_WARNINGS
78 #warning this seems wrong
79 #endif
80                 /* FIXME: if !keepempty, empty() == true, then we reach
81                  * here with size() == 0. So pos_end becomes - 1. Why
82                  * doesn't this cause problems ???
83                  */
84                 pos_type pos_end = par->size() - 1;
85                 pos_type i = pos;
86                 pos_type j = pos;
87
88                 for (; i <= pos_end; ++i) {
89                         Change::Type change(par->lookupChange(i));
90                         par->cutIntoMinibuffer(bparams, i);
91                         if (tmp->insertFromMinibuffer(j - pos)) {
92                                 tmp->pimpl_->setChange(j - pos, change);
93                                 ++j;
94                         }
95                 }
96                 for (i = pos_end; i >= pos; --i) {
97                         par->pimpl_->eraseIntern(i);
98                 }
99         }
100
101         if (pos)
102                 return;
103
104         tmp->params().lineTop(par->params().lineTop());
105         tmp->params().pagebreakTop(par->params().pagebreakTop());
106         tmp->params().spaceTop(par->params().spaceTop());
107         par->params().clear();
108
109         par->layout(bparams.getLyXTextClass().defaultLayout());
110
111         // layout stays the same with latex-environments
112         if (flag) {
113                 par->layout(tmp->layout());
114                 par->setLabelWidthString(tmp->params().labelWidthString());
115                 par->params().depth(tmp->params().depth());
116         }
117
118         // subtle, but needed to get empty pars working right
119         if (bparams.tracking_changes) {
120                 if (!par->size()) {
121                         par->cleanChanges();
122                 } else if (!tmp->size()) {
123                         tmp->cleanChanges();
124                 }
125         }
126 }
127
128
129 void breakParagraphConservative(BufferParams const & bparams,
130                                 Paragraph * par,
131                                 pos_type pos)
132 {
133         // create a new paragraph
134         Paragraph * tmp = new Paragraph(par);
135         tmp->makeSameLayout(par);
136
137         // When can pos > Last()?
138         // I guess pos == Last() is possible.
139         if (par->size() > pos) {
140                 // copy everything behind the break-position to the new
141                 // paragraph
142                 pos_type pos_end = par->size() - 1;
143
144                 for (pos_type i = pos, j = pos; i <= pos_end; ++i) {
145                         par->cutIntoMinibuffer(bparams, i);
146                         if (tmp->insertFromMinibuffer(j - pos))
147                                 ++j;
148                 }
149
150                 for (pos_type k = pos_end; k >= pos; --k) {
151                         par->erase(k);
152                 }
153         }
154 }
155
156
157 void mergeParagraph(BufferParams const & bparams, Paragraph * par)
158 {
159         Paragraph * the_next = par->next();
160
161         // first the DTP-stuff
162         par->params().lineBottom(the_next->params().lineBottom());
163         par->params().spaceBottom(the_next->params().spaceBottom());
164         par->params().pagebreakBottom(the_next->params().pagebreakBottom());
165
166         pos_type pos_end = the_next->size() - 1;
167         pos_type pos_insert = par->size();
168
169         // ok, now copy the paragraph
170         for (pos_type i = 0, j = 0; i <= pos_end; ++i) {
171                 the_next->cutIntoMinibuffer(bparams, i);
172                 if (par->insertFromMinibuffer(pos_insert + j))
173                         ++j;
174         }
175
176         // delete the next paragraph
177         Paragraph * ppar = the_next->previous();
178         Paragraph * npar = the_next->next();
179         delete the_next;
180         ppar->next(npar);
181 }
182
183
184 #if 0
185 Paragraph * depthHook(Paragraph * par, Paragraph::depth_type depth)
186 {
187         Paragraph * newpar = par;
188
189         do {
190                 newpar = newpar->previous();
191         } while (newpar && newpar->getDepth() > depth);
192
193         if (!newpar) {
194                 if (par->previous() || par->getDepth())
195                         lyxerr << "Error (depthHook): "
196                                << "no hook." << endl;
197                 newpar = par;
198         }
199         return newpar;
200 }
201
202
203 Paragraph * outerHook(Paragraph * par)
204 {
205         if (!par->getDepth())
206                 return 0;
207         return depthHook(par, Paragraph::depth_type(par->getDepth() - 1));
208 }
209
210
211 bool isFirstInSequence(Paragraph * par)
212 {
213         Paragraph const * dhook = depthHook(par, par->getDepth());
214         return (dhook == par
215                 || dhook->getLayout() != par->getLayout()
216                 || dhook->getDepth() != par->getDepth());
217 }
218
219
220 int getEndLabel(Paragraph * para, BufferParams const & bparams)
221 {
222         Paragraph * par = para;
223         while (par) {
224                 Paragraph::depth_type par_depth = par->getDepth();
225                 layout_type layout = par->getLayout();
226                 int const endlabeltype =
227                         textclasslist.Style(bparams.textclass,
228                                             layout).endlabeltype;
229                 if (endlabeltype != END_LABEL_NO_LABEL) {
230                         if (!para->next())
231                                 return endlabeltype;
232
233                         Paragraph::depth_type const next_depth =
234                                 para->next()->getDepth();
235                         if (par_depth > next_depth ||
236                             (par_depth == next_depth
237                              && layout != para->next()->getLayout()))
238                                 return endlabeltype;
239                         break;
240                 }
241                 if (par_depth == 0)
242                         break;
243                 par = outerHook(par);
244         }
245         return END_LABEL_NO_LABEL;
246 }
247 #endif
248
249
250 Paragraph * TeXDeeper(Buffer const * buf,
251                       BufferParams const & bparams,
252                       Paragraph * pit,
253                       ostream & os, TexRow & texrow)
254 {
255         lyxerr[Debug::LATEX] << "TeXDeeper...     " << pit << endl;
256         Paragraph * par = pit;
257
258         while (par && par->params().depth() == pit->params().depth()) {
259                 if (par->layout()->isEnvironment()) {
260                         par = TeXEnvironment(buf, bparams, par,
261                                                   os, texrow);
262                 } else {
263                         par = TeXOnePar(buf, bparams, par,
264                                              os, texrow, false);
265                 }
266         }
267         lyxerr[Debug::LATEX] << "TeXDeeper...done " << par << endl;
268
269         return par;
270 }
271
272
273 Paragraph * TeXEnvironment(Buffer const * buf,
274                            BufferParams const & bparams,
275                            Paragraph * pit,
276                            ostream & os, TexRow & texrow)
277 {
278         lyxerr[Debug::LATEX] << "TeXEnvironment...     " << pit << endl;
279
280         LyXLayout_ptr const & style = pit->layout();
281
282         Language const * language = pit->getParLanguage(bparams);
283         Language const * doc_language = bparams.language;
284         Language const * previous_language = pit->previous()
285                 ? pit->previous()->getParLanguage(bparams) : doc_language;
286         if (language->babel() != previous_language->babel()) {
287
288                 if (!lyxrc.language_command_end.empty() &&
289                     previous_language->babel() != doc_language->babel()) {
290                         os << subst(lyxrc.language_command_end, "$$lang",
291                                     previous_language->babel())
292                            << endl;
293                         texrow.newline();
294                 }
295
296                 if (lyxrc.language_command_end.empty() ||
297                     language->babel() != doc_language->babel()) {
298                         os << subst(lyxrc.language_command_begin, "$$lang",
299                                     language->babel())
300                            << endl;
301                         texrow.newline();
302                 }
303         }
304
305         bool leftindent_open = false;
306         if (!pit->params().leftIndent().zero()) {
307                 os << "\\begin{LyXParagraphLeftIndent}{" <<
308                         pit->params().leftIndent().asLatexString() << "}\n";
309                 texrow.newline();
310                 leftindent_open = true;
311         }
312
313         if (style->isEnvironment()) {
314                 if (style->latextype == LATEX_LIST_ENVIRONMENT) {
315                         os << "\\begin{" << style->latexname() << "}{"
316                            << pit->params().labelWidthString() << "}\n";
317                 } else if (style->labeltype == LABEL_BIBLIO) {
318                         // ale970405
319                         os << "\\begin{" << style->latexname() << "}{"
320                            <<  bibitemWidest(buf)
321                            << "}\n";
322                 } else if (style->latextype == LATEX_ITEM_ENVIRONMENT) {
323                         os << "\\begin{" << style->latexname() << '}'
324                            << style->latexparam() << '\n';
325                 } else
326                         os << "\\begin{" << style->latexname() << '}'
327                            << style->latexparam() << '\n';
328                 texrow.newline();
329         }
330         Paragraph * par = pit;
331         do {
332                 par = TeXOnePar(buf, bparams, par, os, texrow, false);
333
334                 if (par && par->params().depth() > pit->params().depth()) {
335                             if (par->layout()->isParagraph()) {
336
337                             // Thinko!
338                             // How to handle this? (Lgb)
339                             //&& !suffixIs(os, "\n\n")
340                                     //) {
341                                 // There should be at least one '\n' already
342                                 // but we need there to be two for Standard
343                                 // paragraphs that are depth-increment'ed to be
344                                 // output correctly.  However, tables can
345                                 // also be paragraphs so don't adjust them.
346                                 // ARRae
347                                 // Thinkee:
348                                 // Will it ever harm to have one '\n' too
349                                 // many? i.e. that we sometimes will have
350                                 // three in a row. (Lgb)
351                                 os << '\n';
352                                 texrow.newline();
353                         }
354                         par = TeXDeeper(buf, bparams, par, os, texrow);
355                 }
356         } while (par
357                  && par->layout() == pit->layout()
358                  && par->params().depth() == pit->params().depth()
359                  && par->params().leftIndent() == pit->params().leftIndent());
360
361         if (style->isEnvironment()) {
362                 os << "\\end{" << style->latexname() << "}\n";
363                 texrow.newline();
364         }
365
366         if (leftindent_open) {
367                 os << "\\end{LyXParagraphLeftIndent}\n";
368                 texrow.newline();
369         }
370
371         lyxerr[Debug::LATEX] << "TeXEnvironment...done " << par << endl;
372         return par;  // ale970302
373 }
374
375
376 namespace {
377
378 InsetOptArg * optArgInset(Paragraph const & par)
379 {
380         // Find the entry.
381         InsetList::iterator it = par.insetlist.begin();
382         InsetList::iterator end = par.insetlist.end();
383         for (; it != end; ++it) {
384                 Inset * ins = it.getInset();
385                 if (ins->lyxCode() == Inset::OPTARG_CODE) {
386                         return static_cast<InsetOptArg *>(ins);
387                 }
388         }
389         return 0;
390 }
391
392 } // end namespace
393
394
395 Paragraph * TeXOnePar(Buffer const * buf,
396                       BufferParams const & bparams,
397                       Paragraph * pit,
398                       ostream & os, TexRow & texrow,
399                       bool moving_arg)
400 {
401         lyxerr[Debug::LATEX] << "TeXOnePar...     " << pit << endl;
402         Inset const * in = pit->inInset();
403         bool further_blank_line = false;
404         LyXLayout_ptr style;
405
406         // well we have to check if we are in an inset with unlimited
407         // lenght (all in one row) if that is true then we don't allow
408         // any special options in the paragraph and also we don't allow
409         // any environment other then "Standard" to be valid!
410         if ((in == 0) || !in->forceDefaultParagraphs(in)) {
411                 style = pit->layout();
412
413                 if (pit->params().startOfAppendix()) {
414                         os << "\\appendix\n";
415                         texrow.newline();
416                 }
417
418                 if (!pit->params().spacing().isDefault()
419                         && (!pit->previous() || !pit->previous()->hasSameLayout(pit))) {
420                         os << pit->params().spacing().writeEnvirBegin() << '\n';
421                         texrow.newline();
422                 }
423
424                 if (style->isCommand()) {
425                         os << '\n';
426                         texrow.newline();
427                 }
428
429                 if (pit->params().pagebreakTop()) {
430                         os << "\\newpage";
431                         further_blank_line = true;
432                 }
433                 if (pit->params().spaceTop().kind() != VSpace::NONE) {
434                         os << pit->params().spaceTop().asLatexCommand(bparams);
435                         further_blank_line = true;
436                 }
437
438                 if (pit->params().lineTop()) {
439                         os << "\\lyxline{\\" << pit->getFont(bparams, 0).latexSize() << '}'
440                            << "\\vspace{-1\\parskip}";
441                         further_blank_line = true;
442                 }
443
444                 if (further_blank_line) {
445                         os << '\n';
446                         texrow.newline();
447                 }
448         } else {
449                 style = bparams.getLyXTextClass().defaultLayout();
450         }
451
452         Language const * language = pit->getParLanguage(bparams);
453         Language const * doc_language = bparams.language;
454         Language const * previous_language = pit->previous()
455                 ? pit->previous()->getParLanguage(bparams) : doc_language;
456
457         if (language->babel() != previous_language->babel()
458             // check if we already put language command in TeXEnvironment()
459             && !(style->isEnvironment()
460                  && (!pit->previous() ||
461                      (pit->previous()->layout() != pit->layout() &&
462                       pit->previous()->getDepth() <= pit->getDepth())
463                      || pit->previous()->getDepth() < pit->getDepth())))
464         {
465                 if (!lyxrc.language_command_end.empty() &&
466                     previous_language->babel() != doc_language->babel())
467                 {
468                         os << subst(lyxrc.language_command_end, "$$lang",
469                                     previous_language->babel())
470                            << endl;
471                         texrow.newline();
472                 }
473
474                 if (lyxrc.language_command_end.empty() ||
475                     language->babel() != doc_language->babel())
476                 {
477                         os << subst(lyxrc.language_command_begin, "$$lang",
478                                     language->babel())
479                            << endl;
480                         texrow.newline();
481                 }
482         }
483
484         if (bparams.inputenc == "auto" &&
485             language->encoding() != previous_language->encoding()) {
486                 os << "\\inputencoding{"
487                    << language->encoding()->LatexName()
488                    << "}\n";
489                 texrow.newline();
490         }
491
492         switch (style->latextype) {
493         case LATEX_COMMAND:
494                 os << '\\' << style->latexname();
495
496                 // Separate handling of optional argument inset.
497                 if (style->optionalargs == 1) {
498                         InsetOptArg * it = optArgInset(*pit);
499                         if (it)
500                                 it->latexOptional(buf, os, false, false);
501                 }
502                 else
503                         os << style->latexparam();
504                 break;
505         case LATEX_ITEM_ENVIRONMENT:
506         case LATEX_LIST_ENVIRONMENT:
507                 os << "\\item ";
508                 break;
509         case LATEX_BIB_ENVIRONMENT:
510                 // ignore this, the inset will write itself
511                 break;
512         default:
513                 break;
514         }
515
516         bool need_par = pit->simpleTeXOnePar(buf, bparams, os, texrow, moving_arg);
517
518         // Make sure that \\par is done with the font of the last
519         // character if this has another size as the default.
520         // This is necessary because LaTeX (and LyX on the screen)
521         // calculates the space between the baselines according
522         // to this font. (Matthias)
523         //
524         // Is this really needed ? (Dekel)
525         // We do not need to use to change the font for the last paragraph
526         // or for a command.
527         LyXFont const font =
528                 (pit->empty()
529                  ? pit->getLayoutFont(bparams) : pit->getFont(bparams, pit->size() - 1));
530
531         bool is_command = style->isCommand();
532
533         if (style->resfont.size() != font.size() && pit->next() && !is_command) {
534                 if (!need_par)
535                         os << '{';
536                 os << "\\" << font.latexSize() << " \\par}";
537         } else if (need_par) {
538                 os << "\\par}";
539         } else if (is_command)
540                 os << '}';
541
542         switch (style->latextype) {
543         case LATEX_ITEM_ENVIRONMENT:
544         case LATEX_LIST_ENVIRONMENT:
545                 if (pit->next() && (pit->params().depth() < pit->next()->params().depth())) {
546                         os << '\n';
547                         texrow.newline();
548                 }
549                 break;
550         case LATEX_ENVIRONMENT:
551                 // if its the last paragraph of the current environment
552                 // skip it otherwise fall through
553                 if (pit->next()
554                     && (pit->next()->layout() != pit->layout()
555                         || pit->next()->params().depth() != pit->params().depth()))
556                         break;
557                 // fall through possible
558         default:
559                 // we don't need it for the last paragraph!!!
560                 if (pit->next()) {
561                         os << '\n';
562                         texrow.newline();
563                 }
564         }
565
566         if ((in == 0) || !in->forceDefaultParagraphs(in)) {
567                 further_blank_line = false;
568                 if (pit->params().lineBottom()) {
569                         os << "\\lyxline{\\" << font.latexSize() << '}';
570                         further_blank_line = true;
571                 }
572
573                 if (pit->params().spaceBottom().kind() != VSpace::NONE) {
574                         os << pit->params().spaceBottom().asLatexCommand(bparams);
575                         further_blank_line = true;
576                 }
577
578                 if (pit->params().pagebreakBottom()) {
579                         os << "\\newpage";
580                         further_blank_line = true;
581                 }
582
583                 if (further_blank_line) {
584                         os << '\n';
585                         texrow.newline();
586                 }
587
588                 if (!pit->params().spacing().isDefault()
589                         && (!pit->next() || !pit->next()->hasSameLayout(pit))) {
590                         os << pit->params().spacing().writeEnvirEnd() << '\n';
591                         texrow.newline();
592                 }
593         }
594
595         // we don't need it for the last paragraph!!!
596         if (pit->next()) {
597                 os << '\n';
598                 texrow.newline();
599         } else {
600                 // Since \selectlanguage write the language to the aux file,
601                 // we need to reset the language at the end of footnote or
602                 // float.
603
604                 if (language->babel() != doc_language->babel()) {
605                         if (lyxrc.language_command_end.empty())
606                                 os << subst(lyxrc.language_command_begin,
607                                             "$$lang",
608                                             doc_language->babel())
609                                    << endl;
610                         else
611                                 os << subst(lyxrc.language_command_end,
612                                             "$$lang",
613                                             language->babel())
614                                    << endl;
615                         texrow.newline();
616                 }
617         }
618
619         lyxerr[Debug::LATEX] << "TeXOnePar...done " << pit->next() << endl;
620         return pit->next();
621 }