]> git.lyx.org Git - lyx.git/blob - src/paragraph_funcs.C
ws changes only
[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 ParagraphList::iterator
251 TeXDeeper(Buffer const * buf,
252           BufferParams const & bparams,
253           ParagraphList::iterator pit,
254           ostream & os, TexRow & texrow)
255 {
256         lyxerr[Debug::LATEX] << "TeXDeeper...     " << &*pit << endl;
257         ParagraphList::iterator par = pit;
258
259         while (par != buf->paragraphs.end()&& par->params().depth() == pit->params().depth()) {
260                 if (par->layout()->isEnvironment()) {
261                         par = TeXEnvironment(buf, bparams, par,
262                                                   os, texrow);
263                 } else {
264                         par = TeXOnePar(buf, bparams, par,
265                                              os, texrow, false);
266                 }
267         }
268         lyxerr[Debug::LATEX] << "TeXDeeper...done " << &*par << endl;
269
270         return par;
271 }
272
273
274 ParagraphList::iterator
275 TeXEnvironment(Buffer const * buf,
276                BufferParams const & bparams,
277                ParagraphList::iterator pit,
278                ostream & os, TexRow & texrow)
279 {
280         lyxerr[Debug::LATEX] << "TeXEnvironment...     " << &*pit << endl;
281
282         LyXLayout_ptr const & style = pit->layout();
283
284         Language const * language = pit->getParLanguage(bparams);
285         Language const * doc_language = bparams.language;
286         Language const * previous_language = pit->previous()
287                 ? pit->previous()->getParLanguage(bparams) : doc_language;
288         if (language->babel() != previous_language->babel()) {
289
290                 if (!lyxrc.language_command_end.empty() &&
291                     previous_language->babel() != doc_language->babel()) {
292                         os << subst(lyxrc.language_command_end, "$$lang",
293                                     previous_language->babel())
294                            << endl;
295                         texrow.newline();
296                 }
297
298                 if (lyxrc.language_command_end.empty() ||
299                     language->babel() != doc_language->babel()) {
300                         os << subst(lyxrc.language_command_begin, "$$lang",
301                                     language->babel())
302                            << endl;
303                         texrow.newline();
304                 }
305         }
306
307         bool leftindent_open = false;
308         if (!pit->params().leftIndent().zero()) {
309                 os << "\\begin{LyXParagraphLeftIndent}{" <<
310                         pit->params().leftIndent().asLatexString() << "}\n";
311                 texrow.newline();
312                 leftindent_open = true;
313         }
314
315         if (style->isEnvironment()) {
316                 if (style->latextype == LATEX_LIST_ENVIRONMENT) {
317                         os << "\\begin{" << style->latexname() << "}{"
318                            << pit->params().labelWidthString() << "}\n";
319                 } else if (style->labeltype == LABEL_BIBLIO) {
320                         // ale970405
321                         os << "\\begin{" << style->latexname() << "}{"
322                            <<  bibitemWidest(buf)
323                            << "}\n";
324                 } else if (style->latextype == LATEX_ITEM_ENVIRONMENT) {
325                         os << "\\begin{" << style->latexname() << '}'
326                            << style->latexparam() << '\n';
327                 } else
328                         os << "\\begin{" << style->latexname() << '}'
329                            << style->latexparam() << '\n';
330                 texrow.newline();
331         }
332         ParagraphList::iterator par = pit;
333         do {
334                 par = TeXOnePar(buf, bparams, par, os, texrow, false);
335
336                 if (par != buf->paragraphs.end()&& par->params().depth() > pit->params().depth()) {
337                             if (par->layout()->isParagraph()) {
338
339                             // Thinko!
340                             // How to handle this? (Lgb)
341                             //&& !suffixIs(os, "\n\n")
342                                     //) {
343                                 // There should be at least one '\n' already
344                                 // but we need there to be two for Standard
345                                 // paragraphs that are depth-increment'ed to be
346                                 // output correctly.  However, tables can
347                                 // also be paragraphs so don't adjust them.
348                                 // ARRae
349                                 // Thinkee:
350                                 // Will it ever harm to have one '\n' too
351                                 // many? i.e. that we sometimes will have
352                                 // three in a row. (Lgb)
353                                 os << '\n';
354                                 texrow.newline();
355                         }
356                         par = TeXDeeper(buf, bparams, par, os, texrow);
357                 }
358         } while (par != buf->paragraphs.end()
359                  && par->layout() == pit->layout()
360                  && par->params().depth() == pit->params().depth()
361                  && par->params().leftIndent() == pit->params().leftIndent());
362
363         if (style->isEnvironment()) {
364                 os << "\\end{" << style->latexname() << "}\n";
365                 texrow.newline();
366         }
367
368         if (leftindent_open) {
369                 os << "\\end{LyXParagraphLeftIndent}\n";
370                 texrow.newline();
371         }
372
373         lyxerr[Debug::LATEX] << "TeXEnvironment...done " << &*par << endl;
374         return par;  // ale970302
375 }
376
377
378 namespace {
379
380 InsetOptArg * optArgInset(Paragraph const & par)
381 {
382         // Find the entry.
383         InsetList::iterator it = par.insetlist.begin();
384         InsetList::iterator end = par.insetlist.end();
385         for (; it != end; ++it) {
386                 Inset * ins = it.getInset();
387                 if (ins->lyxCode() == Inset::OPTARG_CODE) {
388                         return static_cast<InsetOptArg *>(ins);
389                 }
390         }
391         return 0;
392 }
393
394 } // end namespace
395
396
397 ParagraphList::iterator
398 TeXOnePar(Buffer const * buf,
399           BufferParams const & bparams,
400           ParagraphList::iterator pit,
401           ostream & os, TexRow & texrow,
402           bool moving_arg)
403 {
404         lyxerr[Debug::LATEX] << "TeXOnePar...     " << &*pit << endl;
405         Inset const * in = pit->inInset();
406         bool further_blank_line = false;
407         LyXLayout_ptr style;
408
409         // well we have to check if we are in an inset with unlimited
410         // lenght (all in one row) if that is true then we don't allow
411         // any special options in the paragraph and also we don't allow
412         // any environment other then "Standard" to be valid!
413         if ((in == 0) || !in->forceDefaultParagraphs(in)) {
414                 style = pit->layout();
415
416                 if (pit->params().startOfAppendix()) {
417                         os << "\\appendix\n";
418                         texrow.newline();
419                 }
420
421                 if (!pit->params().spacing().isDefault()
422                         && (!pit->previous() || !pit->previous()->hasSameLayout(&*pit))) {
423                         os << pit->params().spacing().writeEnvirBegin() << '\n';
424                         texrow.newline();
425                 }
426
427                 if (style->isCommand()) {
428                         os << '\n';
429                         texrow.newline();
430                 }
431
432                 if (pit->params().pagebreakTop()) {
433                         os << "\\newpage";
434                         further_blank_line = true;
435                 }
436                 if (pit->params().spaceTop().kind() != VSpace::NONE) {
437                         os << pit->params().spaceTop().asLatexCommand(bparams);
438                         further_blank_line = true;
439                 }
440
441                 if (pit->params().lineTop()) {
442                         os << "\\lyxline{\\" << pit->getFont(bparams, 0).latexSize() << '}'
443                            << "\\vspace{-1\\parskip}";
444                         further_blank_line = true;
445                 }
446
447                 if (further_blank_line) {
448                         os << '\n';
449                         texrow.newline();
450                 }
451         } else {
452                 style = bparams.getLyXTextClass().defaultLayout();
453         }
454
455         Language const * language = pit->getParLanguage(bparams);
456         Language const * doc_language = bparams.language;
457         Language const * previous_language = pit->previous()
458                 ? pit->previous()->getParLanguage(bparams) : doc_language;
459
460         if (language->babel() != previous_language->babel()
461             // check if we already put language command in TeXEnvironment()
462             && !(style->isEnvironment()
463                  && (!pit->previous() ||
464                      (pit->previous()->layout() != pit->layout() &&
465                       pit->previous()->getDepth() <= pit->getDepth())
466                      || pit->previous()->getDepth() < pit->getDepth())))
467         {
468                 if (!lyxrc.language_command_end.empty() &&
469                     previous_language->babel() != doc_language->babel())
470                 {
471                         os << subst(lyxrc.language_command_end, "$$lang",
472                                     previous_language->babel())
473                            << endl;
474                         texrow.newline();
475                 }
476
477                 if (lyxrc.language_command_end.empty() ||
478                     language->babel() != doc_language->babel())
479                 {
480                         os << subst(lyxrc.language_command_begin, "$$lang",
481                                     language->babel())
482                            << endl;
483                         texrow.newline();
484                 }
485         }
486
487         if (bparams.inputenc == "auto" &&
488             language->encoding() != previous_language->encoding()) {
489                 os << "\\inputencoding{"
490                    << language->encoding()->LatexName()
491                    << "}\n";
492                 texrow.newline();
493         }
494
495         switch (style->latextype) {
496         case LATEX_COMMAND:
497                 os << '\\' << style->latexname();
498
499                 // Separate handling of optional argument inset.
500                 if (style->optionalargs == 1) {
501                         InsetOptArg * it = optArgInset(*pit);
502                         if (it)
503                                 it->latexOptional(buf, os, false, false);
504                 }
505                 else
506                         os << style->latexparam();
507                 break;
508         case LATEX_ITEM_ENVIRONMENT:
509         case LATEX_LIST_ENVIRONMENT:
510                 os << "\\item ";
511                 break;
512         case LATEX_BIB_ENVIRONMENT:
513                 // ignore this, the inset will write itself
514                 break;
515         default:
516                 break;
517         }
518
519         bool need_par = pit->simpleTeXOnePar(buf, bparams, os, texrow, moving_arg);
520
521         // Make sure that \\par is done with the font of the last
522         // character if this has another size as the default.
523         // This is necessary because LaTeX (and LyX on the screen)
524         // calculates the space between the baselines according
525         // to this font. (Matthias)
526         //
527         // Is this really needed ? (Dekel)
528         // We do not need to use to change the font for the last paragraph
529         // or for a command.
530         LyXFont const font =
531                 (pit->empty()
532                  ? pit->getLayoutFont(bparams) : pit->getFont(bparams, pit->size() - 1));
533
534         bool is_command = style->isCommand();
535
536         if (style->resfont.size() != font.size() && pit->next() && !is_command) {
537                 if (!need_par)
538                         os << '{';
539                 os << "\\" << font.latexSize() << " \\par}";
540         } else if (need_par) {
541                 os << "\\par}";
542         } else if (is_command)
543                 os << '}';
544
545         switch (style->latextype) {
546         case LATEX_ITEM_ENVIRONMENT:
547         case LATEX_LIST_ENVIRONMENT:
548                 if (pit->next() && (pit->params().depth() < pit->next()->params().depth())) {
549                         os << '\n';
550                         texrow.newline();
551                 }
552                 break;
553         case LATEX_ENVIRONMENT:
554                 // if its the last paragraph of the current environment
555                 // skip it otherwise fall through
556                 if (pit->next()
557                     && (pit->next()->layout() != pit->layout()
558                         || pit->next()->params().depth() != pit->params().depth()))
559                         break;
560                 // fall through possible
561         default:
562                 // we don't need it for the last paragraph!!!
563                 if (pit->next()) {
564                         os << '\n';
565                         texrow.newline();
566                 }
567         }
568
569         if ((in == 0) || !in->forceDefaultParagraphs(in)) {
570                 further_blank_line = false;
571                 if (pit->params().lineBottom()) {
572                         os << "\\lyxline{\\" << font.latexSize() << '}';
573                         further_blank_line = true;
574                 }
575
576                 if (pit->params().spaceBottom().kind() != VSpace::NONE) {
577                         os << pit->params().spaceBottom().asLatexCommand(bparams);
578                         further_blank_line = true;
579                 }
580
581                 if (pit->params().pagebreakBottom()) {
582                         os << "\\newpage";
583                         further_blank_line = true;
584                 }
585
586                 if (further_blank_line) {
587                         os << '\n';
588                         texrow.newline();
589                 }
590
591                 if (!pit->params().spacing().isDefault()
592                         && (!pit->next() || !pit->next()->hasSameLayout(&*pit))) {
593                         os << pit->params().spacing().writeEnvirEnd() << '\n';
594                         texrow.newline();
595                 }
596         }
597
598         // we don't need it for the last paragraph!!!
599         if (pit->next()) {
600                 os << '\n';
601                 texrow.newline();
602         } else {
603                 // Since \selectlanguage write the language to the aux file,
604                 // we need to reset the language at the end of footnote or
605                 // float.
606
607                 if (language->babel() != doc_language->babel()) {
608                         if (lyxrc.language_command_end.empty())
609                                 os << subst(lyxrc.language_command_begin,
610                                             "$$lang",
611                                             doc_language->babel())
612                                    << endl;
613                         else
614                                 os << subst(lyxrc.language_command_end,
615                                             "$$lang",
616                                             language->babel())
617                                    << endl;
618                         texrow.newline();
619                 }
620         }
621
622         lyxerr[Debug::LATEX] << "TeXOnePar...done " << pit->next() << endl;
623         return ++pit;
624 }