]> git.lyx.org Git - lyx.git/blob - src/paragraph.C
partial framebox support
[lyx.git] / src / paragraph.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2001 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation
15 #endif
16
17 #include "paragraph.h"
18 #include "paragraph_pimpl.h"
19 #include "lyxrc.h"
20 #include "layout.h"
21 #include "language.h"
22 #include "tex-strings.h"
23 #include "buffer.h"
24 #include "bufferparams.h"
25 #include "debug.h"
26 #include "texrow.h"
27 #include "BufferView.h"
28 #include "encoding.h"
29 #include "ParameterStruct.h"
30 #include "gettext.h"
31
32 #include "insets/insetinclude.h"
33 #include "insets/insetbib.h"
34 #include "insets/insettext.h"
35 #include "insets/insetoptarg.h"
36
37 #include "support/filetools.h"
38 #include "support/lstrings.h"
39 #include "support/lyxmanip.h"
40 #include "support/FileInfo.h"
41 #include "support/LAssert.h"
42 #include "support/textutils.h"
43
44 #include <algorithm>
45 #include <fstream>
46 #include <csignal>
47
48 using std::ostream;
49 using std::endl;
50 using std::fstream;
51 using std::ios;
52 using std::lower_bound;
53 using std::upper_bound;
54 using std::reverse;
55
56 using lyx::pos_type;
57
58 // this is a bad idea, but how can Paragraph find its buffer to get
59 // parameters? (JMarc)
60
61 extern string bibitemWidest(Buffer const *);
62
63 // this is a minibuffer
64
65 namespace {
66
67 char minibuffer_char;
68 LyXFont minibuffer_font;
69 Inset * minibuffer_inset;
70
71 } // namespace anon
72
73
74 extern BufferView * current_view;
75
76
77 Paragraph::Paragraph()
78         : pimpl_(new Paragraph::Pimpl(this))
79 {
80 #ifndef NO_NEXT
81         next_ = 0;
82         previous_ = 0;
83 #endif
84         enumdepth = 0;
85         itemdepth = 0;
86         bibkey = 0; // ale970302
87         params().clear();
88 }
89
90
91 #ifndef NO_NEXT
92 // This constructor inserts the new paragraph in a list.
93 Paragraph::Paragraph(Paragraph * par)
94         : pimpl_(new Paragraph::Pimpl(this))
95 {
96         enumdepth = 0;
97         itemdepth = 0;
98
99         // double linked list begin
100         next_ = par->next_;
101         if (next_)
102                 next_->previous_ = this;
103         previous_ = par;
104         previous_->next_ = this;
105         // end
106
107         bibkey = 0; // ale970302
108         params().clear();
109 }
110 #endif
111
112
113 Paragraph::Paragraph(Paragraph const & lp, bool same_ids)
114         : pimpl_(new Paragraph::Pimpl(*lp.pimpl_, this, same_ids))
115 {
116         enumdepth = 0;
117         itemdepth = 0;
118 #ifndef NO_NEXT
119         next_     = 0;
120         previous_ = 0;
121 #endif
122         // this is because of the dummy layout of the paragraphs that
123         // follow footnotes
124         layout_ = lp.layout();
125
126         // ale970302
127         if (lp.bibkey) {
128                 bibkey = static_cast<InsetBibKey *>
129                         (lp.bibkey->clone(*current_view->buffer()));
130         } else {
131                 bibkey = 0;
132         }
133
134         // copy everything behind the break-position to the new paragraph
135         insetlist = lp.insetlist;
136         InsetList::iterator it = insetlist.begin();
137         InsetList::iterator end = insetlist.end();
138         for (; it != end; ++it) {
139                 it.setInset(it.getInset()->clone(*current_view->buffer(),
140                                                  same_ids));
141                 // tell the new inset who is the boss now
142                 it.getInset()->parOwner(this);
143         }
144 }
145
146
147 // the destructor removes the new paragraph from the list
148 Paragraph::~Paragraph()
149 {
150 #ifndef NO_NEXT
151         if (previous_)
152                 previous_->next_ = next_;
153         if (next_)
154                 next_->previous_ = previous_;
155 #endif
156
157         // ale970302
158         delete bibkey;
159
160         delete pimpl_;
161         //
162         //lyxerr << "Paragraph::paragraph_id = "
163         //       << Paragraph::paragraph_id << endl;
164 }
165
166
167 void Paragraph::write(Buffer const * buf, ostream & os,
168                           BufferParams const & bparams,
169                           depth_type & dth) const
170 {
171         // The beginning or end of a deeper (i.e. nested) area?
172         if (dth != params().depth()) {
173                 if (params().depth() > dth) {
174                         while (params().depth() > dth) {
175                                 os << "\n\\begin_deeper ";
176                                 ++dth;
177                         }
178                 } else {
179                         while (params().depth() < dth) {
180                                 os << "\n\\end_deeper ";
181                                 --dth;
182                         }
183                 }
184         }
185
186         // First write the layout
187         os << "\n\\layout " << layout()->name() << "\n";
188
189         // Maybe some vertical spaces.
190         if (params().spaceTop().kind() != VSpace::NONE)
191                 os << "\\added_space_top "
192                    << params().spaceTop().asLyXCommand() << " ";
193         if (params().spaceBottom().kind() != VSpace::NONE)
194                 os << "\\added_space_bottom "
195                    << params().spaceBottom().asLyXCommand() << " ";
196
197         // Maybe the paragraph has special spacing
198         params().spacing().writeFile(os, true);
199
200         // The labelwidth string used in lists.
201         if (!params().labelWidthString().empty())
202                 os << "\\labelwidthstring "
203                    << params().labelWidthString() << '\n';
204
205         // Lines above or below?
206         if (params().lineTop())
207                 os << "\\line_top ";
208         if (params().lineBottom())
209                 os << "\\line_bottom ";
210
211         // Pagebreaks above or below?
212         if (params().pagebreakTop())
213                 os << "\\pagebreak_top ";
214         if (params().pagebreakBottom())
215                 os << "\\pagebreak_bottom ";
216
217         // Start of appendix?
218         if (params().startOfAppendix())
219                 os << "\\start_of_appendix ";
220
221         // Noindent?
222         if (params().noindent())
223                 os << "\\noindent ";
224
225         // Do we have a manual left indent?
226         if (!params().leftIndent().zero())
227                 os << "\\leftindent " << params().leftIndent().asString() << " ";
228
229         // Alignment?
230         if (params().align() != LYX_ALIGN_LAYOUT) {
231                 int h = 0;
232                 switch (params().align()) {
233                 case LYX_ALIGN_LEFT: h = 1; break;
234                 case LYX_ALIGN_RIGHT: h = 2; break;
235                 case LYX_ALIGN_CENTER: h = 3; break;
236                 default: h = 0; break;
237                 }
238                 os << "\\align " << string_align[h] << " ";
239         }
240
241         // bibitem  ale970302
242         if (bibkey)
243                 bibkey->write(buf, os);
244
245         LyXFont font1(LyXFont::ALL_INHERIT, bparams.language);
246
247         int column = 0;
248         for (pos_type i = 0; i < size(); ++i) {
249                 if (!i) {
250                         os << "\n";
251                         column = 0;
252                 }
253
254                 // Write font changes
255                 LyXFont font2 = getFontSettings(bparams, i);
256                 if (font2 != font1) {
257 #ifndef INHERIT_LANGUAGE
258                         font2.lyxWriteChanges(font1, os);
259 #else
260                         font2.lyxWriteChanges(font1, bparams.language, os);
261 #endif
262                         column = 0;
263                         font1 = font2;
264                 }
265
266                 value_type const c = getChar(i);
267                 switch (c) {
268                 case META_INSET:
269                 {
270                         Inset const * inset = getInset(i);
271                         if (inset)
272                                 if (inset->directWrite()) {
273                                         // international char, let it write
274                                         // code directly so it's shorter in
275                                         // the file
276                                         inset->write(buf, os);
277                                 } else {
278                                         os << "\n\\begin_inset ";
279                                         inset->write(buf, os);
280                                         os << "\n\\end_inset \n\n";
281                                         column = 0;
282                                 }
283                 }
284                 break;
285                 case META_NEWLINE:
286                         os << "\n\\newline \n";
287                         column = 0;
288                         break;
289                 case META_HFILL:
290                         os << "\n\\hfill \n";
291                         column = 0;
292                         break;
293                 case '\\':
294                         os << "\n\\backslash \n";
295                         column = 0;
296                         break;
297                 case '.':
298                         if (i + 1 < size() && getChar(i + 1) == ' ') {
299                                 os << ".\n";
300                                 column = 0;
301                         } else
302                                 os << ".";
303                         break;
304                 default:
305                         if ((column > 70 && c == ' ')
306                             || column > 79) {
307                                 os << "\n";
308                                 column = 0;
309                         }
310                         // this check is to amend a bug. LyX sometimes
311                         // inserts '\0' this could cause problems.
312                         if (c != '\0')
313                                 os << c;
314                         else
315                                 lyxerr << "ERROR (Paragraph::writeFile):"
316                                         " NULL char in structure." << endl;
317                         ++column;
318                         break;
319                 }
320         }
321 }
322
323
324 void Paragraph::validate(LaTeXFeatures & features) const
325 {
326         pimpl_->validate(features, *layout());
327 }
328
329
330 // First few functions needed for cut and paste and paragraph breaking.
331 void Paragraph::copyIntoMinibuffer(Buffer const & buffer, pos_type pos) const
332 {
333         BufferParams bparams = buffer.params;
334
335         minibuffer_char = getChar(pos);
336         minibuffer_font = getFontSettings(bparams, pos);
337         minibuffer_inset = 0;
338         if (minibuffer_char == Paragraph::META_INSET) {
339                 if (getInset(pos)) {
340                         minibuffer_inset = getInset(pos)->clone(buffer);
341                 } else {
342                         minibuffer_inset = 0;
343                         minibuffer_char = ' ';
344                         // This reflects what GetInset() does (ARRae)
345                 }
346         }
347 }
348
349
350 void Paragraph::cutIntoMinibuffer(BufferParams const & bparams, pos_type pos)
351 {
352         minibuffer_char = getChar(pos);
353         minibuffer_font = getFontSettings(bparams, pos);
354         minibuffer_inset = 0;
355         if (minibuffer_char == Paragraph::META_INSET) {
356                 if (getInset(pos)) {
357                         // the inset is not in a paragraph anymore
358                         minibuffer_inset = insetlist.release(pos);
359                         minibuffer_inset->parOwner(0);
360                 } else {
361                         minibuffer_inset = 0;
362                         minibuffer_char = ' ';
363                         // This reflects what GetInset() does (ARRae)
364                 }
365
366         }
367
368         // Erase(pos); now the caller is responsible for that.
369 }
370
371
372 bool Paragraph::insertFromMinibuffer(pos_type pos)
373 {
374         if (minibuffer_char == Paragraph::META_INSET) {
375                 if (!insetAllowed(minibuffer_inset->lyxCode())) {
376                         return false;
377                 }
378                 insertInset(pos, minibuffer_inset, minibuffer_font);
379         } else {
380                 LyXFont f = minibuffer_font;
381                 if (!checkInsertChar(f)) {
382                         return false;
383                 }
384                 insertChar(pos, minibuffer_char, f);
385         }
386         return true;
387 }
388
389 // end of minibuffer
390
391
392 void Paragraph::erase(pos_type pos)
393 {
394         pimpl_->erase(pos);
395 }
396
397
398 bool Paragraph::checkInsertChar(LyXFont & font)
399 {
400         if (pimpl_->inset_owner)
401                 return pimpl_->inset_owner->checkInsertChar(font);
402         return true;
403 }
404
405
406 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c)
407 {
408         LyXFont const f(LyXFont::ALL_INHERIT);
409         insertChar(pos, c, f);
410 }
411
412
413 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
414                            LyXFont const & font)
415 {
416         pimpl_->insertChar(pos, c, font);
417 }
418
419
420 void Paragraph::insertInset(pos_type pos, Inset * inset)
421 {
422         LyXFont const f(LyXFont::ALL_INHERIT);
423         insertInset(pos, inset, f);
424 }
425
426
427 void Paragraph::insertInset(pos_type pos, Inset * inset, LyXFont const & font)
428 {
429         pimpl_->insertInset(pos, inset, font);
430 }
431
432
433 bool Paragraph::insetAllowed(Inset::Code code)
434 {
435         //lyxerr << "Paragraph::InsertInsetAllowed" << endl;
436
437         if (pimpl_->inset_owner)
438                 return pimpl_->inset_owner->insetAllowed(code);
439         return true;
440 }
441
442
443 Inset * Paragraph::getInset(pos_type pos)
444 {
445         lyx::Assert(pos < size());
446
447         return insetlist.get(pos);
448 }
449
450
451 Inset const * Paragraph::getInset(pos_type pos) const
452 {
453         lyx::Assert(pos < size());
454
455         return insetlist.get(pos);
456 }
457
458
459 // Gets uninstantiated font setting at position.
460 LyXFont const Paragraph::getFontSettings(BufferParams const & bparams,
461                                          pos_type pos) const
462 {
463         lyx::Assert(pos <= size());
464
465         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
466         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
467         for (; cit != end; ++cit) {
468                 if (cit->pos() >= pos)
469                         break;
470         }
471
472         LyXFont retfont;
473         if (cit != end) {
474                 retfont = cit->font();
475         } else if (pos == size() && !empty()) {
476                 retfont = getFontSettings(bparams, pos - 1);
477         } else
478                 retfont = LyXFont(LyXFont::ALL_INHERIT, getParLanguage(bparams));
479 #ifdef INHERIT_LANGUAGE
480         if (retfont.language() == inherit_language)
481                 retfont.setLanguage(bparams.language);
482 #endif
483
484         return retfont;
485 }
486
487
488 // Gets uninstantiated font setting at position 0
489 LyXFont const Paragraph::getFirstFontSettings() const
490 {
491         if (!empty() && !pimpl_->fontlist.empty())
492                 return pimpl_->fontlist[0].font();
493
494         return LyXFont(LyXFont::ALL_INHERIT);
495 }
496
497
498 // Gets the fully instantiated font at a given position in a paragraph
499 // This is basically the same function as LyXText::GetFont() in text2.C.
500 // The difference is that this one is used for generating the LaTeX file,
501 // and thus cosmetic "improvements" are disallowed: This has to deliver
502 // the true picture of the buffer. (Asger)
503 // If position is -1, we get the layout font of the paragraph.
504 // If position is -2, we get the font of the manual label of the paragraph.
505 LyXFont const Paragraph::getFont(BufferParams const & bparams,
506                                  pos_type pos) const
507 {
508         lyx::Assert(pos >= 0);
509
510         LyXLayout_ptr const & lout = layout();
511
512         pos_type main_body = 0;
513         if (lout->labeltype == LABEL_MANUAL)
514                 main_body = beginningOfMainBody();
515
516         LyXFont layoutfont;
517         if (pos < main_body)
518                 layoutfont = lout->labelfont;
519         else
520                 layoutfont = lout->font;
521
522         LyXFont tmpfont = getFontSettings(bparams, pos);
523 #ifndef INHERIT_LANGUAGE
524         tmpfont.realize(layoutfont);
525 #else
526         tmpfont.realize(layoutfont, bparams.language);
527 #endif
528
529         return pimpl_->realizeFont(tmpfont, bparams);
530 }
531
532
533 LyXFont const Paragraph::getLabelFont(BufferParams const & bparams) const
534 {
535         LyXLayout_ptr const & lout = layout();
536
537         LyXFont tmpfont = lout->labelfont;
538         tmpfont.setLanguage(getParLanguage(bparams));
539
540         return pimpl_->realizeFont(tmpfont, bparams);
541 }
542
543
544 LyXFont const Paragraph::getLayoutFont(BufferParams const & bparams) const
545 {
546         LyXLayout_ptr const & lout = layout();
547
548         LyXFont tmpfont = lout->font;
549         tmpfont.setLanguage(getParLanguage(bparams));
550
551         return pimpl_->realizeFont(tmpfont, bparams);
552 }
553
554
555 /// Returns the height of the highest font in range
556 LyXFont::FONT_SIZE
557 Paragraph::highestFontInRange(pos_type startpos, pos_type endpos,
558                               LyXFont::FONT_SIZE const def_size) const
559 {
560         if (pimpl_->fontlist.empty())
561                 return def_size;
562
563         Pimpl::FontList::const_iterator end_it = pimpl_->fontlist.begin();
564         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
565         for (; end_it != end; ++end_it) {
566                 if (end_it->pos() >= endpos)
567                         break;
568         }
569
570         if (end_it != end)
571                 ++end_it;
572
573         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
574         for (; cit != end; ++cit) {
575                 if (cit->pos() >= startpos)
576                         break;
577         }
578
579         LyXFont::FONT_SIZE maxsize = LyXFont::SIZE_TINY;
580         for (; cit != end_it; ++cit) {
581                 LyXFont::FONT_SIZE size = cit->font().size();
582                 if (size == LyXFont::INHERIT_SIZE)
583                         size = def_size;
584                 if (size > maxsize && size <= LyXFont::SIZE_HUGER)
585                         maxsize = size;
586         }
587         return maxsize;
588 }
589
590
591 Paragraph::value_type
592 Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
593 {
594         value_type c = getChar(pos);
595         if (!lyxrc.rtl_support)
596                 return c;
597
598         value_type uc = c;
599         switch (c) {
600         case '(':
601                 uc = ')';
602                 break;
603         case ')':
604                 uc = '(';
605                 break;
606         case '[':
607                 uc = ']';
608                 break;
609         case ']':
610                 uc = '[';
611                 break;
612         case '{':
613                 uc = '}';
614                 break;
615         case '}':
616                 uc = '{';
617                 break;
618         case '<':
619                 uc = '>';
620                 break;
621         case '>':
622                 uc = '<';
623                 break;
624         }
625         if (uc != c && getFontSettings(bparams, pos).isRightToLeft())
626                 return uc;
627         else
628                 return c;
629 }
630
631
632 void Paragraph::setFont(pos_type pos, LyXFont const & font)
633 {
634         lyx::Assert(pos <= size());
635
636         // First, reduce font against layout/label font
637         // Update: The SetCharFont() routine in text2.C already
638         // reduces font, so we don't need to do that here. (Asger)
639         // No need to simplify this because it will disappear
640         // in a new kernel. (Asger)
641         // Next search font table
642
643         Pimpl::FontList::iterator beg = pimpl_->fontlist.begin();
644         Pimpl::FontList::iterator it = beg;
645         Pimpl::FontList::iterator endit = pimpl_->fontlist.end();
646         for (; it != endit; ++it) {
647                 if (it->pos() >= pos)
648                         break;
649         }
650         unsigned int i = std::distance(beg, it);
651         bool notfound = (it == endit);
652
653         if (!notfound && pimpl_->fontlist[i].font() == font)
654                 return;
655
656         bool begin = pos == 0 || notfound ||
657                 (i > 0 && pimpl_->fontlist[i - 1].pos() == pos - 1);
658         // Is position pos is a beginning of a font block?
659         bool end = !notfound && pimpl_->fontlist[i].pos() == pos;
660         // Is position pos is the end of a font block?
661         if (begin && end) { // A single char block
662                 if (i + 1 < pimpl_->fontlist.size() &&
663                     pimpl_->fontlist[i + 1].font() == font) {
664                         // Merge the singleton block with the next block
665                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
666                         if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
667                                 pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i - 1);
668                 } else if (i > 0 && pimpl_->fontlist[i - 1].font() == font) {
669                         // Merge the singleton block with the previous block
670                         pimpl_->fontlist[i - 1].pos(pos);
671                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
672                 } else
673                         pimpl_->fontlist[i].font(font);
674         } else if (begin) {
675                 if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
676                         pimpl_->fontlist[i - 1].pos(pos);
677                 else
678                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
679                                         Pimpl::FontTable(pos, font));
680         } else if (end) {
681                 pimpl_->fontlist[i].pos(pos - 1);
682                 if (!(i + 1 < pimpl_->fontlist.size() &&
683                       pimpl_->fontlist[i + 1].font() == font))
684                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
685                                         Pimpl::FontTable(pos, font));
686         } else { // The general case. The block is splitted into 3 blocks
687                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
688                                 Pimpl::FontTable(pos - 1, pimpl_->fontlist[i].font()));
689                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
690                                 Pimpl::FontTable(pos, font));
691         }
692 }
693
694
695 #ifndef NO_NEXT
696 void Paragraph::next(Paragraph * p)
697 {
698         next_ = p;
699 }
700
701
702 // This function is able to hide closed footnotes.
703 Paragraph * Paragraph::next()
704 {
705         return next_;
706 }
707
708
709 Paragraph const * Paragraph::next() const
710 {
711         return next_;
712 }
713
714
715 void Paragraph::previous(Paragraph * p)
716 {
717         previous_ = p;
718 }
719
720
721 // This function is able to hide closed footnotes.
722 Paragraph * Paragraph::previous()
723 {
724         return previous_;
725 }
726
727
728 // This function is able to hide closed footnotes.
729 Paragraph const * Paragraph::previous() const
730 {
731         return previous_;
732 }
733 #endif
734
735
736 void Paragraph::makeSameLayout(Paragraph const * par)
737 {
738         layout(par->layout());
739         // move to pimpl?
740         params() = par->params();
741 }
742
743
744 int Paragraph::stripLeadingSpaces()
745 {
746         if (layout()->free_spacing ||
747             isFreeSpacing()) {
748                 return 0;
749         }
750
751         int i = 0;
752         while (!empty() && (isNewline(0) || isLineSeparator(0))) {
753                 erase(0);
754                 ++i;
755         }
756
757         return i;
758 }
759
760
761 bool Paragraph::hasSameLayout(Paragraph const * par) const
762 {
763         return
764                 par->layout() == layout() &&
765                 params().sameLayout(par->params());
766 }
767
768
769 int Paragraph::getEndLabel() const
770 {
771         Paragraph const * par = this;
772         depth_type par_depth = getDepth();
773         while (par) {
774                 LyXLayout_ptr const & layout = par->layout();
775                 int const endlabeltype = layout->endlabeltype;
776
777                 if (endlabeltype != END_LABEL_NO_LABEL) {
778                         if (!next_)
779                                 return endlabeltype;
780
781                         depth_type const next_depth = next_->getDepth();
782                         if (par_depth > next_depth ||
783                             (par_depth == next_depth
784                              && layout != next_->layout()))
785                                 return endlabeltype;
786                         break;
787                 }
788                 if (par_depth == 0)
789                         break;
790                 par = par->outerHook();
791                 if (par)
792                         par_depth = par->getDepth();
793         }
794         return END_LABEL_NO_LABEL;
795 }
796
797
798 Paragraph::depth_type Paragraph::getDepth() const
799 {
800         return params().depth();
801 }
802
803
804 Paragraph::depth_type Paragraph::getMaxDepthAfter() const
805 {
806         bool const isenv = layout()->isEnvironment();
807
808         if (isenv)
809                 return params().depth() + 1;
810         else
811                 return params().depth();
812
813 }
814
815 char Paragraph::getAlign() const
816 {
817         return params().align();
818 }
819
820
821 string const & Paragraph::getLabelstring() const
822 {
823         return params().labelString();
824 }
825
826
827 // the next two functions are for the manual labels
828 string const Paragraph::getLabelWidthString() const
829 {
830         if (!params().labelWidthString().empty())
831                 return params().labelWidthString();
832         else
833                 return _("Senseless with this layout!");
834 }
835
836
837 void Paragraph::setLabelWidthString(string const & s)
838 {
839         params().labelWidthString(s);
840 }
841
842
843 void Paragraph::applyLayout(LyXLayout_ptr const & new_layout)
844 {
845         layout(new_layout);
846         params().labelWidthString(string());
847         params().align(LYX_ALIGN_LAYOUT);
848         params().spaceTop(VSpace(VSpace::NONE));
849         params().spaceBottom(VSpace(VSpace::NONE));
850         params().spacing(Spacing(Spacing::Default));
851 }
852
853
854 // if the layout of a paragraph contains a manual label, the beginning of the
855 // main body is the beginning of the second word. This is what the par-
856 // function returns. If the layout does not contain a label, the main
857 // body always starts with position 0. This differentiation is necessary,
858 // because there cannot be a newline or a blank <= the beginning of the
859 // main body in TeX.
860
861 int Paragraph::beginningOfMainBody() const
862 {
863         // Unroll the first two cycles of the loop
864         // and remember the previous character to
865         // remove unnecessary GetChar() calls
866         pos_type i = 0;
867         if (i < size() && getChar(i) != Paragraph::META_NEWLINE) {
868                 ++i;
869                 char previous_char = 0;
870                 char temp = 0;
871                 if (i < size()
872                     && (previous_char = getChar(i)) != Paragraph::META_NEWLINE) {
873                         // Yes, this  ^ is supposed to be "= " not "=="
874                         ++i;
875                         while (i < size()
876                                && previous_char != ' '
877                                && (temp = getChar(i)) != Paragraph::META_NEWLINE) {
878                                 ++i;
879                                 previous_char = temp;
880                         }
881                 }
882         }
883
884         return i;
885 }
886
887
888 Paragraph * Paragraph::depthHook(depth_type depth)
889 {
890         Paragraph * newpar = this;
891
892         do {
893                 newpar = newpar->previous();
894         } while (newpar && newpar->getDepth() > depth);
895
896         if (!newpar) {
897                 if (previous() || getDepth())
898                         lyxerr << "ERROR (Paragraph::DepthHook): "
899                                 "no hook." << endl;
900                 newpar = this;
901         }
902
903         return newpar;
904 }
905
906
907 Paragraph const * Paragraph::depthHook(depth_type depth) const
908 {
909         Paragraph const * newpar = this;
910
911         do {
912                 newpar = newpar->previous();
913         } while (newpar && newpar->getDepth() > depth);
914
915         if (!newpar) {
916                 if (previous() || getDepth())
917                         lyxerr << "ERROR (Paragraph::DepthHook): "
918                                 "no hook." << endl;
919                 newpar = this;
920         }
921
922         return newpar;
923 }
924
925
926 Paragraph * Paragraph::outerHook()
927 {
928         if (!getDepth())
929                 return 0;
930         return depthHook(depth_type(getDepth() - 1));
931 }
932
933
934 Paragraph const * Paragraph::outerHook() const
935 {
936         if (!getDepth())
937                 return 0;
938         return depthHook(depth_type(getDepth() - 1));
939 }
940
941
942 // returns -1 if inset not found
943 int Paragraph::getPositionOfInset(Inset const * inset) const
944 {
945         // Find the entry.
946         InsetList::iterator it = insetlist.begin();
947         InsetList::iterator end = insetlist.end();
948         for (; it != end; ++it) {
949                 if (it.getInset() == inset) {
950                         return it.getPos();
951                 }
952         }
953         if (inset == bibkey)
954                 return 0;
955
956         return -1;
957 }
958
959 namespace {
960
961 InsetOptArg * optArgInset(Paragraph const & par)
962 {
963         // Find the entry.
964         InsetList::iterator it = par.insetlist.begin();
965         InsetList::iterator end = par.insetlist.end();
966         for (; it != end; ++it) {
967                 Inset * ins = it.getInset();
968                 if (ins->lyxCode() == Inset::OPTARG_CODE) {
969                         return static_cast<InsetOptArg *>(ins);
970                 }
971         }
972         return 0;
973 }
974
975 } // end namespace
976
977 Paragraph * Paragraph::TeXOnePar(Buffer const * buf,
978                                  BufferParams const & bparams,
979                                  ostream & os, TexRow & texrow,
980                                  bool moving_arg)
981 {
982         lyxerr[Debug::LATEX] << "TeXOnePar...     " << this << endl;
983         Inset const * in = inInset();
984         bool further_blank_line = false;
985         LyXLayout_ptr style;
986
987         // well we have to check if we are in an inset with unlimited
988         // lenght (all in one row) if that is true then we don't allow
989         // any special options in the paragraph and also we don't allow
990         // any environment other then "Standard" to be valid!
991         if ((in == 0) || !in->forceDefaultParagraphs(in)) {
992                 style = layout();
993
994                 if (params().startOfAppendix()) {
995                         os << "\\appendix\n";
996                         texrow.newline();
997                 }
998
999                 if (!params().spacing().isDefault()
1000                         && (!previous() || !previous()->hasSameLayout(this))) {
1001                         os << params().spacing().writeEnvirBegin() << "\n";
1002                         texrow.newline();
1003                 }
1004
1005                 if (style->isCommand()) {
1006                         os << '\n';
1007                         texrow.newline();
1008                 }
1009
1010                 if (params().pagebreakTop()) {
1011                         os << "\\newpage";
1012                         further_blank_line = true;
1013                 }
1014                 if (params().spaceTop().kind() != VSpace::NONE) {
1015                         os << params().spaceTop().asLatexCommand(bparams);
1016                         further_blank_line = true;
1017                 }
1018
1019                 if (params().lineTop()) {
1020                         os << "\\lyxline{\\" << getFont(bparams, 0).latexSize() << '}'
1021                            << "\\vspace{-1\\parskip}";
1022                         further_blank_line = true;
1023                 }
1024
1025                 if (further_blank_line) {
1026                         os << '\n';
1027                         texrow.newline();
1028                 }
1029         } else {
1030                 style = bparams.getLyXTextClass().defaultLayout();
1031         }
1032
1033         Language const * language = getParLanguage(bparams);
1034         Language const * doc_language = bparams.language;
1035         Language const * previous_language = previous()
1036                 ? previous()->getParLanguage(bparams) : doc_language;
1037
1038         if (language->babel() != previous_language->babel()
1039             // check if we already put language command in TeXEnvironment()
1040             && !(style->isEnvironment()
1041                  && (!previous() || previous()->layout() != layout() ||
1042                          previous()->params().depth() != params().depth())))
1043         {
1044                 if (!lyxrc.language_command_end.empty() &&
1045                     previous_language->babel() != doc_language->babel())
1046                 {
1047                         os << subst(lyxrc.language_command_end, "$$lang",
1048                                     previous_language->babel())
1049                            << endl;
1050                         texrow.newline();
1051                 }
1052
1053                 if (lyxrc.language_command_end.empty() ||
1054                     language->babel() != doc_language->babel())
1055                 {
1056                         os << subst(lyxrc.language_command_begin, "$$lang",
1057                                     language->babel())
1058                            << endl;
1059                         texrow.newline();
1060                 }
1061         }
1062
1063         if (bparams.inputenc == "auto" &&
1064             language->encoding() != previous_language->encoding()) {
1065                 os << "\\inputencoding{"
1066                    << language->encoding()->LatexName()
1067                    << "}" << endl;
1068                 texrow.newline();
1069         }
1070
1071         switch (style->latextype) {
1072         case LATEX_COMMAND:
1073                 os << '\\'
1074                    << style->latexname();
1075
1076                 // Separate handling of optional argument inset.
1077                 if (style->optionalargs == 1) {
1078                         InsetOptArg * it = optArgInset(*this);
1079                         if (it != 0)
1080                                 it->latexOptional(buf, os, false, false);
1081                 }
1082                 else
1083                         os << style->latexparam();
1084                 break;
1085         case LATEX_ITEM_ENVIRONMENT:
1086                 if (bibkey) {
1087                         bibkey->latex(buf, os, false, false);
1088                 } else
1089                         os << "\\item ";
1090                 break;
1091         case LATEX_LIST_ENVIRONMENT:
1092                 os << "\\item ";
1093                 break;
1094         default:
1095                 break;
1096         }
1097
1098         bool need_par = simpleTeXOnePar(buf, bparams, os, texrow, moving_arg);
1099
1100         // Make sure that \\par is done with the font of the last
1101         // character if this has another size as the default.
1102         // This is necessary because LaTeX (and LyX on the screen)
1103         // calculates the space between the baselines according
1104         // to this font. (Matthias)
1105         //
1106         // Is this really needed ? (Dekel)
1107         // We do not need to use to change the font for the last paragraph
1108         // or for a command.
1109         LyXFont const font =
1110                 (empty()
1111                  ? getLayoutFont(bparams) : getFont(bparams, size() - 1));
1112
1113         bool is_command = style->isCommand();
1114
1115         if (style->resfont.size() != font.size() && next_ && !is_command) {
1116                 if (!need_par)
1117                         os << "{";
1118                 os << "\\" << font.latexSize() << " \\par}";
1119         } else if (need_par) {
1120                 os << "\\par}";
1121         } else if (is_command)
1122                 os << "}";
1123
1124         switch (style->latextype) {
1125         case LATEX_ITEM_ENVIRONMENT:
1126         case LATEX_LIST_ENVIRONMENT:
1127                 if (next_ && (params().depth() < next_->params().depth())) {
1128                         os << '\n';
1129                         texrow.newline();
1130                 }
1131                 break;
1132         case LATEX_ENVIRONMENT:
1133                 // if its the last paragraph of the current environment
1134                 // skip it otherwise fall through
1135                 if (next_
1136                     && (next_->layout() != layout()
1137                         || next_->params().depth() != params().depth()))
1138                         break;
1139                 // fall through possible
1140         default:
1141                 // we don't need it for the last paragraph!!!
1142                 if (next_) {
1143                         os << '\n';
1144                         texrow.newline();
1145                 }
1146         }
1147
1148         if ((in == 0) || !in->forceDefaultParagraphs(in)) {
1149                 further_blank_line = false;
1150                 if (params().lineBottom()) {
1151                         os << "\\lyxline{\\" << font.latexSize() << '}';
1152                         further_blank_line = true;
1153                 }
1154
1155                 if (params().spaceBottom().kind() != VSpace::NONE) {
1156                         os << params().spaceBottom().asLatexCommand(bparams);
1157                         further_blank_line = true;
1158                 }
1159
1160                 if (params().pagebreakBottom()) {
1161                         os << "\\newpage";
1162                         further_blank_line = true;
1163                 }
1164
1165                 if (further_blank_line) {
1166                         os << '\n';
1167                         texrow.newline();
1168                 }
1169
1170                 if (!params().spacing().isDefault()
1171                         && (!next_ || !next_->hasSameLayout(this))) {
1172                         os << params().spacing().writeEnvirEnd() << "\n";
1173                         texrow.newline();
1174                 }
1175         }
1176
1177         // we don't need it for the last paragraph!!!
1178         if (next_) {
1179                 os << '\n';
1180                 texrow.newline();
1181         } else {
1182                 // Since \selectlanguage write the language to the aux file,
1183                 // we need to reset the language at the end of footnote or
1184                 // float.
1185
1186                 if (language->babel() != doc_language->babel()) {
1187                         if (lyxrc.language_command_end.empty())
1188                                 os << subst(lyxrc.language_command_begin,
1189                                             "$$lang",
1190                                             doc_language->babel())
1191                                    << endl;
1192                         else
1193                                 os << subst(lyxrc.language_command_end,
1194                                             "$$lang",
1195                                             language->babel())
1196                                    << endl;
1197                         texrow.newline();
1198                 }
1199         }
1200
1201         lyxerr[Debug::LATEX] << "TeXOnePar...done " << next_ << endl;
1202         return next_;
1203 }
1204
1205
1206 // This could go to ParagraphParameters if we want to
1207 int Paragraph::startTeXParParams(BufferParams const & bparams,
1208                                  ostream & os, bool moving_arg) const
1209 {
1210         int column = 0;
1211
1212         if (params().noindent()) {
1213                 os << "\\noindent ";
1214                 column += 10;
1215         }
1216
1217         switch (params().align()) {
1218         case LYX_ALIGN_NONE:
1219         case LYX_ALIGN_BLOCK:
1220         case LYX_ALIGN_LAYOUT:
1221         case LYX_ALIGN_SPECIAL:
1222                 break;
1223         case LYX_ALIGN_LEFT:
1224         case LYX_ALIGN_RIGHT:
1225         case LYX_ALIGN_CENTER:
1226                 if (moving_arg) {
1227                         os << "\\protect";
1228                         column = 8;
1229                 }
1230                 break;
1231         }
1232
1233         switch (params().align()) {
1234         case LYX_ALIGN_NONE:
1235         case LYX_ALIGN_BLOCK:
1236         case LYX_ALIGN_LAYOUT:
1237         case LYX_ALIGN_SPECIAL:
1238                 break;
1239         case LYX_ALIGN_LEFT:
1240                 if (getParLanguage(bparams)->babel() != "hebrew") {
1241                         os << "\\begin{flushleft}";
1242                         column += 17;
1243                 } else {
1244                         os << "\\begin{flushright}";
1245                         column += 18;
1246                 }
1247                 break;
1248         case LYX_ALIGN_RIGHT:
1249                 if (getParLanguage(bparams)->babel() != "hebrew") {
1250                         os << "\\begin{flushright}";
1251                         column += 18;
1252                 } else {
1253                         os << "\\begin{flushleft}";
1254                         column += 17;
1255                 }
1256                 break;
1257         case LYX_ALIGN_CENTER:
1258                 os << "\\begin{center}";
1259                 column += 14;
1260                 break;
1261         }
1262
1263         return column;
1264 }
1265
1266
1267 // This could go to ParagraphParameters if we want to
1268 int Paragraph::endTeXParParams(BufferParams const & bparams,
1269                                ostream & os, bool moving_arg) const
1270 {
1271         int column = 0;
1272
1273         switch (params().align()) {
1274         case LYX_ALIGN_NONE:
1275         case LYX_ALIGN_BLOCK:
1276         case LYX_ALIGN_LAYOUT:
1277         case LYX_ALIGN_SPECIAL:
1278                 break;
1279         case LYX_ALIGN_LEFT:
1280         case LYX_ALIGN_RIGHT:
1281         case LYX_ALIGN_CENTER:
1282                 if (moving_arg) {
1283                         os << "\\protect";
1284                         column = 8;
1285                 }
1286                 break;
1287         }
1288
1289         switch (params().align()) {
1290         case LYX_ALIGN_NONE:
1291         case LYX_ALIGN_BLOCK:
1292         case LYX_ALIGN_LAYOUT:
1293         case LYX_ALIGN_SPECIAL:
1294                 break;
1295         case LYX_ALIGN_LEFT:
1296                 if (getParLanguage(bparams)->babel() != "hebrew") {
1297                         os << "\\end{flushleft}";
1298                         column = 15;
1299                 } else {
1300                         os << "\\end{flushright}";
1301                         column = 16;
1302                 }
1303                 break;
1304         case LYX_ALIGN_RIGHT:
1305                 if (getParLanguage(bparams)->babel() != "hebrew") {
1306                         os << "\\end{flushright}";
1307                         column+= 16;
1308                 } else {
1309                         os << "\\end{flushleft}";
1310                         column = 15;
1311                 }
1312                 break;
1313         case LYX_ALIGN_CENTER:
1314                 os << "\\end{center}";
1315                 column = 12;
1316                 break;
1317         }
1318         return column;
1319 }
1320
1321
1322 // This one spits out the text of the paragraph
1323 bool Paragraph::simpleTeXOnePar(Buffer const * buf,
1324                                 BufferParams const & bparams,
1325                                 ostream & os, TexRow & texrow,
1326                                 bool moving_arg)
1327 {
1328         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...     " << this << endl;
1329
1330         bool return_value = false;
1331
1332         LyXLayout_ptr style;
1333
1334         // well we have to check if we are in an inset with unlimited
1335         // lenght (all in one row) if that is true then we don't allow
1336         // any special options in the paragraph and also we don't allow
1337         // any environment other then "Standard" to be valid!
1338         bool asdefault =
1339                 (inInset() && inInset()->forceDefaultParagraphs(inInset()));
1340
1341         if (asdefault) {
1342                 style = bparams.getLyXTextClass().defaultLayout();
1343         } else {
1344                 style = layout();
1345         }
1346
1347         LyXFont basefont;
1348
1349         // Maybe we have to create a optional argument.
1350         pos_type main_body;
1351         if (style->labeltype != LABEL_MANUAL)
1352                 main_body = 0;
1353         else
1354                 main_body = beginningOfMainBody();
1355
1356         int column = 0;
1357
1358         if (main_body > 0) {
1359                 os << '[';
1360                 ++column;
1361                 basefont = getLabelFont(bparams);
1362         } else {
1363                 basefont = getLayoutFont(bparams);
1364         }
1365
1366         moving_arg |= style->needprotect;
1367
1368         // Which font is currently active?
1369         LyXFont running_font(basefont);
1370         // Do we have an open font change?
1371         bool open_font = false;
1372
1373         texrow.start(this, 0);
1374
1375         // if the paragraph is empty, the loop will not be entered at all
1376         if (empty()) {
1377                 if (style->isCommand()) {
1378                         os << '{';
1379                         ++column;
1380                 }
1381                 if (!asdefault)
1382                         column += startTeXParParams(bparams, os, moving_arg);
1383
1384         }
1385
1386         for (pos_type i = 0; i < size(); ++i) {
1387                 ++column;
1388                 // First char in paragraph or after label?
1389                 if (i == main_body) {
1390                         if (main_body > 0) {
1391                                 if (open_font) {
1392                                         column += running_font.latexWriteEndChanges(os, basefont, basefont);
1393                                         open_font = false;
1394                                 }
1395                                 basefont = getLayoutFont(bparams);
1396                                 running_font = basefont;
1397                                 os << ']';
1398                                 ++column;
1399                         }
1400                         if (style->isCommand()) {
1401                                 os << '{';
1402                                 ++column;
1403                         }
1404
1405                         if (!asdefault)
1406                                 column += startTeXParParams(bparams, os,
1407                                                             moving_arg);
1408                 }
1409
1410                 value_type c = getChar(i);
1411
1412                 // Fully instantiated font
1413                 LyXFont font = getFont(bparams, i);
1414
1415                 LyXFont const last_font = running_font;
1416
1417                 // Spaces at end of font change are simulated to be
1418                 // outside font change, i.e. we write "\textXX{text} "
1419                 // rather than "\textXX{text }". (Asger)
1420                 if (open_font && c == ' ' && i <= size() - 2) {
1421                         LyXFont const & next_font = getFont(bparams, i + 1);
1422                         if (next_font != running_font
1423                             && next_font != font) {
1424                                 font = next_font;
1425                         }
1426                 }
1427
1428                 // We end font definition before blanks
1429                 if (open_font &&
1430                     (font != running_font ||
1431                      font.language() != running_font.language()))
1432                 {
1433                         column += running_font.latexWriteEndChanges(os,
1434                                                                     basefont,
1435                                                                     (i == main_body-1) ? basefont : font);
1436                         running_font = basefont;
1437                         open_font = false;
1438                 }
1439
1440                 // Blanks are printed before start of fontswitch
1441                 if (c == ' ') {
1442                         // Do not print the separation of the optional argument
1443                         if (i != main_body - 1) {
1444                                 pimpl_->simpleTeXBlanks(os, texrow, i,
1445                                                        column, font, *style);
1446                         }
1447                 }
1448
1449                 // Do we need to change font?
1450                 if ((font != running_font ||
1451                      font.language() != running_font.language()) &&
1452                         i != main_body - 1)
1453                 {
1454                         column += font.latexWriteStartChanges(os, basefont,
1455                                                               last_font);
1456                         running_font = font;
1457                         open_font = true;
1458                 }
1459
1460                 if (c == Paragraph::META_NEWLINE) {
1461                         // newlines are handled differently here than
1462                         // the default in SimpleTeXSpecialChars().
1463                         if (!style->newline_allowed) {
1464                                 os << '\n';
1465                         } else {
1466                                 if (open_font) {
1467                                         column += running_font.latexWriteEndChanges(os, basefont, basefont);
1468                                         open_font = false;
1469                                 }
1470                                 basefont = getLayoutFont(bparams);
1471                                 running_font = basefont;
1472                                 if (font.family() ==
1473                                     LyXFont::TYPEWRITER_FAMILY) {
1474                                         os << "~";
1475                                 }
1476                                 if (moving_arg)
1477                                         os << "\\protect ";
1478
1479                                 os << "\\\\\n";
1480                         }
1481                         texrow.newline();
1482                         texrow.start(this, i + 1);
1483                         column = 0;
1484                 } else {
1485                         pimpl_->simpleTeXSpecialChars(buf, bparams,
1486                                                       os, texrow, moving_arg,
1487                                                       font, running_font,
1488                                                       basefont, open_font,
1489                                                       *style, i, column, c);
1490                 }
1491         }
1492
1493         // If we have an open font definition, we have to close it
1494         if (open_font) {
1495 #ifdef FIXED_LANGUAGE_END_DETECTION
1496                 if (next_) {
1497                         running_font
1498                                 .latexWriteEndChanges(os, basefont,
1499                                                       next_->getFont(bparams,
1500                                                       0));
1501                 } else {
1502                         running_font.latexWriteEndChanges(os, basefont,
1503                                                           basefont);
1504                 }
1505 #else
1506 #ifdef WITH_WARNINGS
1507 //#warning For now we ALWAYS have to close the foreign font settings if they are
1508 //#warning there as we start another \selectlanguage with the next paragraph if
1509 //#warning we are in need of this. This should be fixed sometime (Jug)
1510 #endif
1511                 running_font.latexWriteEndChanges(os, basefont,  basefont);
1512 #endif
1513         }
1514
1515         // Needed if there is an optional argument but no contents.
1516         if (main_body > 0 && main_body == size()) {
1517                 os << "]~";
1518                 return_value = false;
1519         }
1520
1521         if (!asdefault) {
1522                 column += endTeXParParams(bparams, os, moving_arg);
1523         }
1524
1525         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...done " << this << endl;
1526         return return_value;
1527 }
1528
1529
1530 Paragraph * Paragraph::TeXEnvironment(Buffer const * buf,
1531                                             BufferParams const & bparams,
1532                                             ostream & os, TexRow & texrow)
1533 {
1534         lyxerr[Debug::LATEX] << "TeXEnvironment...     " << this << endl;
1535
1536         LyXLayout_ptr const & style = layout();
1537
1538         Language const * language = getParLanguage(bparams);
1539         Language const * doc_language = bparams.language;
1540         Language const * previous_language = previous_
1541                 ? previous_->getParLanguage(bparams) : doc_language;
1542         if (language->babel() != previous_language->babel()) {
1543
1544                 if (!lyxrc.language_command_end.empty() &&
1545                     previous_language->babel() != doc_language->babel()) {
1546                         os << subst(lyxrc.language_command_end, "$$lang",
1547                                     previous_language->babel())
1548                            << endl;
1549                         texrow.newline();
1550                 }
1551
1552                 if (lyxrc.language_command_end.empty() ||
1553                     language->babel() != doc_language->babel()) {
1554                         os << subst(lyxrc.language_command_begin, "$$lang",
1555                                     language->babel())
1556                            << endl;
1557                         texrow.newline();
1558                 }
1559         }
1560
1561         bool leftindent_open = false;
1562         if (!params().leftIndent().zero()) {
1563                 os << "\\begin{LyXParagraphLeftIndent}{" <<
1564                         params().leftIndent().asLatexString() << "}\n";
1565                 texrow.newline();
1566                 leftindent_open = true;
1567         }
1568
1569         if (style->isEnvironment()) {
1570                 if (style->latextype == LATEX_LIST_ENVIRONMENT) {
1571                         os << "\\begin{" << style->latexname() << "}{"
1572                            << params().labelWidthString() << "}\n";
1573                 } else if (style->labeltype == LABEL_BIBLIO) {
1574                         // ale970405
1575                         os << "\\begin{" << style->latexname() << "}{"
1576                            <<  bibitemWidest(buf)
1577                            << "}\n";
1578                 } else if (style->latextype == LATEX_ITEM_ENVIRONMENT) {
1579                         os << "\\begin{" << style->latexname() << '}'
1580                            << style->latexparam() << '\n';
1581                 } else
1582                         os << "\\begin{" << style->latexname() << '}'
1583                            << style->latexparam() << '\n';
1584                 texrow.newline();
1585         }
1586         Paragraph * par = this;
1587         do {
1588                 par = par->TeXOnePar(buf, bparams, os, texrow, false);
1589
1590                 if (par && par->params().depth() > params().depth()) {
1591                             if (par->layout()->isParagraph()) {
1592
1593                             // Thinko!
1594                             // How to handle this? (Lgb)
1595                             //&& !suffixIs(os, "\n\n")
1596                                     //) {
1597                                 // There should be at least one '\n' already
1598                                 // but we need there to be two for Standard
1599                                 // paragraphs that are depth-increment'ed to be
1600                                 // output correctly.  However, tables can
1601                                 // also be paragraphs so don't adjust them.
1602                                 // ARRae
1603                                 // Thinkee:
1604                                 // Will it ever harm to have one '\n' too
1605                                 // many? i.e. that we sometimes will have
1606                                 // three in a row. (Lgb)
1607                                 os << '\n';
1608                                 texrow.newline();
1609                         }
1610                         par = par->pimpl_->TeXDeeper(buf, bparams, os, texrow);
1611                 }
1612         } while (par
1613                  && par->layout() == layout()
1614                  && par->params().depth() == params().depth()
1615                  && par->params().leftIndent() == params().leftIndent());
1616
1617         if (style->isEnvironment()) {
1618                 os << "\\end{" << style->latexname() << "}\n";
1619                 texrow.newline();
1620         }
1621
1622         if (leftindent_open) {
1623                 os << "\\end{LyXParagraphLeftIndent}\n";
1624                 texrow.newline();
1625         }
1626
1627         lyxerr[Debug::LATEX] << "TeXEnvironment...done " << par << endl;
1628         return par;  // ale970302
1629 }
1630
1631
1632 bool Paragraph::isHfill(pos_type pos) const
1633 {
1634         return IsHfillChar(getChar(pos));
1635 }
1636
1637
1638 bool Paragraph::isInset(pos_type pos) const
1639 {
1640         return IsInsetChar(getChar(pos));
1641 }
1642
1643
1644 bool Paragraph::isNewline(pos_type pos) const
1645 {
1646         return pos >= 0 && IsNewlineChar(getChar(pos));
1647 }
1648
1649
1650 bool Paragraph::isSeparator(pos_type pos) const
1651 {
1652         return IsSeparatorChar(getChar(pos));
1653 }
1654
1655
1656 bool Paragraph::isLineSeparator(pos_type pos) const
1657 {
1658         value_type const c = getChar(pos);
1659         return IsLineSeparatorChar(c)
1660                 || (IsInsetChar(c) && getInset(pos) &&
1661                 getInset(pos)->isLineSeparator());
1662 }
1663
1664
1665 bool Paragraph::isKomma(pos_type pos) const
1666 {
1667         return IsKommaChar(getChar(pos));
1668 }
1669
1670
1671 /// Used by the spellchecker
1672 bool Paragraph::isLetter(pos_type pos) const
1673 {
1674         value_type const c = getChar(pos);
1675         if (IsLetterChar(c))
1676                 return true;
1677         if (isInset(pos))
1678                 return getInset(pos)->isLetter();
1679         // We want to pass the ' and escape chars to ispell
1680         string const extra = lyxrc.isp_esc_chars + '\'';
1681         return contains(extra, c);
1682 }
1683
1684
1685 bool Paragraph::isWord(pos_type pos) const
1686 {
1687         return IsWordChar(getChar(pos)) ;
1688 }
1689
1690
1691 Language const *
1692 Paragraph::getParLanguage(BufferParams const & bparams) const
1693 {
1694         if (!empty()) {
1695 #ifndef INHERIT_LANGUAGE
1696                 return getFirstFontSettings().language();
1697 #else
1698                 Language const * lang = getFirstFontSettings().language();
1699 #ifdef WITH_WARNINGS
1700 #warning We should make this somewhat better, any ideas? (Jug)
1701 #endif
1702                 if (lang == inherit_language || lang == ignore_language)
1703                         lang = bparams.language;
1704                 return lang;
1705 #endif
1706         } else if (previous_)
1707                 return previous_->getParLanguage(bparams);
1708         else
1709                 return bparams.language;
1710 }
1711
1712
1713 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
1714 {
1715         return lyxrc.rtl_support
1716                 && getParLanguage(bparams)->RightToLeft()
1717                 && !(inInset() && inInset()->owner() &&
1718                      inInset()->owner()->lyxCode() == Inset::ERT_CODE);
1719 }
1720
1721
1722 void Paragraph::changeLanguage(BufferParams const & bparams,
1723                                   Language const * from, Language const * to)
1724 {
1725         for (pos_type i = 0; i < size(); ++i) {
1726                 LyXFont font = getFontSettings(bparams, i);
1727                 if (font.language() == from) {
1728                         font.setLanguage(to);
1729                         setFont(i, font);
1730                 }
1731         }
1732 }
1733
1734
1735 bool Paragraph::isMultiLingual(BufferParams const & bparams)
1736 {
1737         Language const * doc_language = bparams.language;
1738         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1739         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
1740
1741         for (; cit != end; ++cit)
1742                 if (cit->font().language() != ignore_language &&
1743                     cit->font().language() != latex_language &&
1744 #ifdef INHERIT_LANGUAGE
1745                         cit->font().language() != inherit_language &&
1746 #endif
1747                         cit->font().language() != doc_language)
1748                         return true;
1749         return false;
1750 }
1751
1752
1753 // Convert the paragraph to a string.
1754 // Used for building the table of contents
1755 string const Paragraph::asString(Buffer const * buffer, bool label)
1756 {
1757         BufferParams const & bparams = buffer->params;
1758         string s;
1759         if (label && !params().labelString().empty())
1760                 s += params().labelString() + ' ';
1761         string::size_type const len = s.size();
1762
1763         for (pos_type i = 0; i < size(); ++i) {
1764                 value_type c = getChar(i);
1765                 if (IsPrintable(c))
1766                         s += c;
1767                 else if (c == META_INSET &&
1768                          getInset(i)->lyxCode() == Inset::MATH_CODE) {
1769                         ostringstream ost;
1770                         getInset(i)->ascii(buffer, ost);
1771                         s += subst(ost.str().c_str(),'\n',' ');
1772                 }
1773         }
1774
1775         if (isRightToLeftPar(bparams))
1776                 reverse(s.begin() + len,s.end());
1777
1778         return s;
1779 }
1780
1781
1782 string const Paragraph::asString(Buffer const * buffer,
1783                                  pos_type beg, pos_type end, bool label)
1784 {
1785         ostringstream ost;
1786
1787         if (beg == 0 && label && !params().labelString().empty())
1788                 ost << params().labelString() << ' ';
1789
1790         for (pos_type i = beg; i < end; ++i) {
1791                 value_type const c = getUChar(buffer->params, i);
1792                 if (IsPrintable(c))
1793                         ost << c;
1794                 else if (c == META_NEWLINE)
1795                         ost << '\n';
1796                 else if (c == META_HFILL)
1797                         ost << '\t';
1798                 else if (c == META_INSET) {
1799                         getInset(i)->ascii(buffer, ost);
1800                 }
1801         }
1802
1803         return ost.str().c_str();
1804 }
1805
1806
1807 void Paragraph::setInsetOwner(Inset * i)
1808 {
1809         pimpl_->inset_owner = i;
1810         InsetList::iterator it = insetlist.begin();
1811         InsetList::iterator end = insetlist.end();
1812         for (; it != end; ++it) {
1813                 if (it.getInset())
1814                         it.getInset()->setOwner(i);
1815         }
1816 }
1817
1818
1819 void Paragraph::deleteInsetsLyXText(BufferView * bv)
1820 {
1821         // then the insets
1822         insetlist.deleteInsetsLyXText(bv);
1823 }
1824
1825
1826 void Paragraph::resizeInsetsLyXText(BufferView * bv)
1827 {
1828         // then the insets
1829         insetlist.resizeInsetsLyXText(bv);
1830 }
1831
1832
1833 void Paragraph::setContentsFromPar(Paragraph * par)
1834 {
1835         pimpl_->setContentsFromPar(par);
1836 }
1837
1838
1839 lyx::pos_type Paragraph::size() const
1840 {
1841         return pimpl_->size();
1842 }
1843
1844
1845 bool Paragraph::empty() const
1846 {
1847         return pimpl_->empty();
1848 }
1849
1850
1851 Paragraph::value_type Paragraph::getChar(pos_type pos) const
1852 {
1853         return pimpl_->getChar(pos);
1854 }
1855
1856
1857 int Paragraph::id() const
1858 {
1859         return pimpl_->id_;
1860 }
1861
1862
1863 LyXLayout_ptr const & Paragraph::layout() const
1864 {
1865         return layout_;
1866 }
1867
1868
1869 void Paragraph::layout(LyXLayout_ptr const & new_layout)
1870 {
1871         layout_ = new_layout;
1872 }
1873
1874
1875 bool Paragraph::isFirstInSequence() const
1876 {
1877         Paragraph const * dhook = depthHook(getDepth());
1878         return (dhook == this
1879                 || dhook->layout() != layout()
1880                 || dhook->getDepth() != getDepth());
1881 }
1882
1883
1884 Inset * Paragraph::inInset() const
1885 {
1886         return pimpl_->inset_owner;
1887 }
1888
1889
1890 void Paragraph::clearContents()
1891 {
1892         pimpl_->clear();
1893 }
1894
1895 void Paragraph::setChar(pos_type pos, value_type c)
1896 {
1897         pimpl_->setChar(pos, c);
1898 }
1899
1900
1901 ParagraphParameters & Paragraph::params()
1902 {
1903         return pimpl_->params;
1904 }
1905
1906
1907 ParagraphParameters const & Paragraph::params() const
1908 {
1909         return pimpl_->params;
1910 }
1911
1912
1913 Paragraph * Paragraph::getParFromID(int id) const
1914 {
1915         return pimpl_->getParFromID(id);
1916 }
1917
1918
1919 bool Paragraph::isFreeSpacing() const
1920 {
1921         // for now we just need this, later should we need this in some
1922         // other way we can always add a function to Inset::() too.
1923         if (pimpl_->inset_owner && pimpl_->inset_owner->owner())
1924                 return (pimpl_->inset_owner->owner()->lyxCode() == Inset::ERT_CODE);
1925         return false;
1926 }