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