]> git.lyx.org Git - lyx.git/blob - src/paragraph.C
The 'bformat' introduction
[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 #include "paragraph.h"
14 #include "paragraph_pimpl.h"
15 #include "lyxrc.h"
16 #include "layout.h"
17 #include "language.h"
18 #include "buffer.h"
19 #include "bufferparams.h"
20 #include "debug.h"
21 #include "texrow.h"
22 #include "BufferView.h"
23 #include "encoding.h"
24 #include "ParameterStruct.h"
25 #include "gettext.h"
26 #include "changes.h"
27 #include "paragraph_funcs.h"
28 #include "Lsstream.h"
29
30 #include "insets/insetbibitem.h"
31 #include "insets/insetoptarg.h"
32
33 #include "support/filetools.h"
34 #include "support/lstrings.h"
35 #include "support/lyxmanip.h"
36 #include "support/FileInfo.h"
37 #include "support/LAssert.h"
38 #include "support/textutils.h"
39
40 #include <algorithm>
41 #include <fstream>
42 #include <csignal>
43 #include <ctime>
44
45 using std::ostream;
46 using std::endl;
47 using std::fstream;
48 using std::ios;
49 using std::lower_bound;
50 using std::upper_bound;
51
52 using lyx::pos_type;
53
54
55 // this is a minibuffer
56
57 namespace {
58
59 char minibuffer_char;
60 LyXFont minibuffer_font;
61 Inset * minibuffer_inset;
62
63 } // namespace anon
64
65
66 extern BufferView * current_view;
67
68
69 Paragraph::Paragraph()
70         : pimpl_(new Paragraph::Pimpl(this))
71 {
72 #ifndef NO_NEXT
73         next_ = 0;
74         previous_ = 0;
75 #endif
76         enumdepth = 0;
77         itemdepth = 0;
78         params().clear();
79 }
80
81
82 #ifndef NO_NEXT
83 // This constructor inserts the new paragraph in a list.
84 // It is placed after par.
85 Paragraph::Paragraph(Paragraph * par)
86         : pimpl_(new Paragraph::Pimpl(this))
87 {
88         enumdepth = 0;
89         itemdepth = 0;
90
91         // double linked list begin
92         next_ = par->next_;
93         if (next_)
94                 next_->previous_ = this;
95         previous_ = par;
96         previous_->next_ = this;
97         // end
98
99         params().clear();
100 }
101 #endif
102
103
104 Paragraph::Paragraph(Paragraph const & lp, bool same_ids)
105         : pimpl_(new Paragraph::Pimpl(*lp.pimpl_, this, same_ids))
106 {
107         enumdepth = 0;
108         itemdepth = 0;
109 #ifndef NO_NEXT
110         next_     = 0;
111         previous_ = 0;
112 #endif
113         // this is because of the dummy layout of the paragraphs that
114         // follow footnotes
115         layout_ = lp.layout();
116
117         // copy everything behind the break-position to the new paragraph
118         insetlist = lp.insetlist;
119         InsetList::iterator it = insetlist.begin();
120         InsetList::iterator end = insetlist.end();
121         for (; it != end; ++it) {
122                 it.setInset(it.getInset()->clone(*current_view->buffer(),
123                                                  same_ids));
124                 // tell the new inset who is the boss now
125                 it.getInset()->parOwner(this);
126         }
127 }
128
129
130 // the destructor removes the new paragraph from the list
131 Paragraph::~Paragraph()
132 {
133 #ifndef NO_NEXT
134         if (previous_)
135                 previous_->next_ = next_;
136         if (next_)
137                 next_->previous_ = previous_;
138 #endif
139
140         delete pimpl_;
141         //
142         //lyxerr << "Paragraph::paragraph_id = "
143         //       << Paragraph::paragraph_id << endl;
144 }
145
146
147 void Paragraph::write(Buffer const * buf, ostream & os,
148                           BufferParams const & bparams,
149                           depth_type & dth) const
150 {
151         // The beginning or end of a deeper (i.e. nested) area?
152         if (dth != params().depth()) {
153                 if (params().depth() > dth) {
154                         while (params().depth() > dth) {
155                                 os << "\n\\begin_deeper ";
156                                 ++dth;
157                         }
158                 } else {
159                         while (params().depth() < dth) {
160                                 os << "\n\\end_deeper ";
161                                 --dth;
162                         }
163                 }
164         }
165
166         // First write the layout
167         os << "\n\\layout " << layout()->name() << '\n';
168
169         params().write(os);
170
171         LyXFont font1(LyXFont::ALL_INHERIT, bparams.language);
172
173         Change running_change = Change(Change::UNCHANGED);
174         lyx::time_type const curtime(lyx::current_time());
175
176         int column = 0;
177         for (pos_type i = 0; i < size(); ++i) {
178                 if (!i) {
179                         os << '\n';
180                         column = 0;
181                 }
182
183                 Change change = pimpl_->lookupChangeFull(i);
184                 Changes::lyxMarkChange(os, column, curtime, running_change, change);
185                 running_change = change;
186
187                 // Write font changes
188                 LyXFont font2 = getFontSettings(bparams, i);
189                 if (font2 != font1) {
190                         font2.lyxWriteChanges(font1, os);
191                         column = 0;
192                         font1 = font2;
193                 }
194
195                 value_type const c = getChar(i);
196                 switch (c) {
197                 case META_INSET:
198                 {
199                         Inset const * inset = getInset(i);
200                         if (inset)
201                                 if (inset->directWrite()) {
202                                         // international char, let it write
203                                         // code directly so it's shorter in
204                                         // the file
205                                         inset->write(buf, os);
206                                 } else {
207                                         os << "\n\\begin_inset ";
208                                         inset->write(buf, os);
209                                         os << "\n\\end_inset \n\n";
210                                         column = 0;
211                                 }
212                 }
213                 break;
214                 case '\\':
215                         os << "\n\\backslash \n";
216                         column = 0;
217                         break;
218                 case '.':
219                         if (i + 1 < size() && getChar(i + 1) == ' ') {
220                                 os << ".\n";
221                                 column = 0;
222                         } else
223                                 os << '.';
224                         break;
225                 default:
226                         if ((column > 70 && c == ' ')
227                             || column > 79) {
228                                 os << '\n';
229                                 column = 0;
230                         }
231                         // this check is to amend a bug. LyX sometimes
232                         // inserts '\0' this could cause problems.
233                         if (c != '\0')
234                                 os << c;
235                         else
236                                 lyxerr << "ERROR (Paragraph::writeFile):"
237                                         " NULL char in structure." << endl;
238                         ++column;
239                         break;
240                 }
241         }
242
243         // to make reading work properly
244         if (!size()) {
245                 running_change = pimpl_->lookupChange(0);
246                 Changes::lyxMarkChange(os, column, curtime,
247                         Change(Change::UNCHANGED), running_change);
248         }
249         Changes::lyxMarkChange(os, column, curtime,
250                 running_change, Change(Change::UNCHANGED));
251 }
252
253
254 void Paragraph::validate(LaTeXFeatures & features) const
255 {
256         pimpl_->validate(features, *layout());
257 }
258
259
260 // First few functions needed for cut and paste and paragraph breaking.
261 void Paragraph::copyIntoMinibuffer(Buffer const & buffer, pos_type pos) const
262 {
263         BufferParams bparams = buffer.params;
264
265         minibuffer_char = getChar(pos);
266         minibuffer_font = getFontSettings(bparams, pos);
267         minibuffer_inset = 0;
268         if (minibuffer_char == Paragraph::META_INSET) {
269                 if (getInset(pos)) {
270                         minibuffer_inset = getInset(pos)->clone(buffer);
271                 } else {
272                         minibuffer_inset = 0;
273                         minibuffer_char = ' ';
274                         // This reflects what GetInset() does (ARRae)
275                 }
276         }
277 }
278
279
280 void Paragraph::cutIntoMinibuffer(BufferParams const & bparams, pos_type pos)
281 {
282         minibuffer_char = getChar(pos);
283         minibuffer_font = getFontSettings(bparams, pos);
284         minibuffer_inset = 0;
285         if (minibuffer_char == Paragraph::META_INSET) {
286                 if (getInset(pos)) {
287                         // the inset is not in a paragraph anymore
288                         minibuffer_inset = insetlist.release(pos);
289                         minibuffer_inset->parOwner(0);
290                 } else {
291                         minibuffer_inset = 0;
292                         minibuffer_char = ' ';
293                         // This reflects what GetInset() does (ARRae)
294                 }
295
296         }
297
298         // Erase(pos); now the caller is responsible for that.
299 }
300
301
302 bool Paragraph::insertFromMinibuffer(pos_type pos)
303 {
304         if (minibuffer_char == Paragraph::META_INSET) {
305                 if (!insetAllowed(minibuffer_inset->lyxCode())) {
306                         return false;
307                 }
308                 insertInset(pos, minibuffer_inset, minibuffer_font);
309         } else {
310                 LyXFont f = minibuffer_font;
311                 if (!checkInsertChar(f)) {
312                         return false;
313                 }
314                 insertChar(pos, minibuffer_char, f);
315         }
316         return true;
317 }
318
319 // end of minibuffer
320
321
322 void Paragraph::eraseIntern(lyx::pos_type pos)
323 {
324         pimpl_->eraseIntern(pos);
325 }
326
327
328 bool Paragraph::erase(pos_type pos)
329 {
330         return pimpl_->erase(pos);
331 }
332
333
334 int Paragraph::erase(pos_type start, pos_type end)
335 {
336         return pimpl_->erase(start, end);
337 }
338
339
340 bool Paragraph::checkInsertChar(LyXFont & font)
341 {
342         if (pimpl_->inset_owner)
343                 return pimpl_->inset_owner->checkInsertChar(font);
344         return true;
345 }
346
347
348 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c)
349 {
350         LyXFont const f(LyXFont::ALL_INHERIT);
351         insertChar(pos, c, f);
352 }
353
354
355 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
356                            LyXFont const & font, Change change)
357 {
358         pimpl_->insertChar(pos, c, font, change);
359 }
360
361
362 void Paragraph::insertInset(pos_type pos, Inset * inset)
363 {
364         LyXFont const f(LyXFont::ALL_INHERIT);
365         insertInset(pos, inset, f);
366 }
367
368
369 void Paragraph::insertInset(pos_type pos, Inset * inset, LyXFont const & font, Change change)
370 {
371         pimpl_->insertInset(pos, inset, font, change);
372 }
373
374
375 bool Paragraph::insetAllowed(Inset::Code code)
376 {
377         //lyxerr << "Paragraph::InsertInsetAllowed" << endl;
378         if (pimpl_->inset_owner)
379                 return pimpl_->inset_owner->insetAllowed(code);
380         return true;
381 }
382
383
384 Inset * Paragraph::getInset(pos_type pos)
385 {
386         lyx::Assert(pos < size());
387         return insetlist.get(pos);
388 }
389
390
391 Inset const * Paragraph::getInset(pos_type pos) const
392 {
393         lyx::Assert(pos < size());
394         return insetlist.get(pos);
395 }
396
397
398 // Gets uninstantiated font setting at position.
399 LyXFont const Paragraph::getFontSettings(BufferParams const & bparams,
400                                          pos_type pos) const
401 {
402         lyx::Assert(pos <= size());
403
404         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
405         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
406         for (; cit != end; ++cit) {
407                 if (cit->pos() >= pos)
408                         break;
409         }
410
411         LyXFont retfont;
412         if (cit != end)
413                 retfont = cit->font();
414         else if (pos == size() && !empty())
415                 retfont = getFontSettings(bparams, pos - 1);
416         else
417                 retfont = LyXFont(LyXFont::ALL_INHERIT, getParLanguage(bparams));
418
419         return retfont;
420 }
421
422
423 // Gets uninstantiated font setting at position 0
424 LyXFont const Paragraph::getFirstFontSettings() const
425 {
426         if (!empty() && !pimpl_->fontlist.empty())
427                 return pimpl_->fontlist[0].font();
428
429         return LyXFont(LyXFont::ALL_INHERIT);
430 }
431
432
433 // Gets the fully instantiated font at a given position in a paragraph
434 // This is basically the same function as LyXText::GetFont() in text2.C.
435 // The difference is that this one is used for generating the LaTeX file,
436 // and thus cosmetic "improvements" are disallowed: This has to deliver
437 // the true picture of the buffer. (Asger)
438 LyXFont const Paragraph::getFont(BufferParams const & bparams, pos_type pos,
439                                  LyXFont const & outerfont) const
440 {
441         lyx::Assert(pos >= 0);
442
443         LyXLayout_ptr const & lout = layout();
444
445         pos_type const body_pos = beginningOfBody();
446
447         LyXFont layoutfont;
448         if (pos < body_pos)
449                 layoutfont = lout->labelfont;
450         else
451                 layoutfont = lout->font;
452
453         LyXFont tmpfont = getFontSettings(bparams, pos);
454         tmpfont.realize(layoutfont);
455         tmpfont.realize(outerfont);
456
457         return realizeFont(tmpfont, bparams);
458 }
459
460
461 LyXFont const Paragraph::getLabelFont(BufferParams const & bparams,
462                                       LyXFont const & outerfont) const
463 {
464         LyXLayout_ptr const & lout = layout();
465
466         LyXFont tmpfont = lout->labelfont;
467         tmpfont.setLanguage(getParLanguage(bparams));
468         tmpfont.realize(outerfont);
469
470         return realizeFont(tmpfont, bparams);
471 }
472
473
474 LyXFont const Paragraph::getLayoutFont(BufferParams const & bparams,
475                                        LyXFont const & outerfont) const
476 {
477         LyXLayout_ptr const & lout = layout();
478
479         LyXFont tmpfont = lout->font;
480         tmpfont.setLanguage(getParLanguage(bparams));
481         tmpfont.realize(outerfont);
482
483         return realizeFont(tmpfont, bparams);
484 }
485
486
487 /// Returns the height of the highest font in range
488 LyXFont::FONT_SIZE
489 Paragraph::highestFontInRange(pos_type startpos, pos_type endpos,
490                               LyXFont::FONT_SIZE const def_size) const
491 {
492         if (pimpl_->fontlist.empty())
493                 return def_size;
494
495         Pimpl::FontList::const_iterator end_it = pimpl_->fontlist.begin();
496         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
497         for (; end_it != end; ++end_it) {
498                 if (end_it->pos() >= endpos)
499                         break;
500         }
501
502         if (end_it != end)
503                 ++end_it;
504
505         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
506         for (; cit != end; ++cit) {
507                 if (cit->pos() >= startpos)
508                         break;
509         }
510
511         LyXFont::FONT_SIZE maxsize = LyXFont::SIZE_TINY;
512         for (; cit != end_it; ++cit) {
513                 LyXFont::FONT_SIZE size = cit->font().size();
514                 if (size == LyXFont::INHERIT_SIZE)
515                         size = def_size;
516                 if (size > maxsize && size <= LyXFont::SIZE_HUGER)
517                         maxsize = size;
518         }
519         return maxsize;
520 }
521
522
523 Paragraph::value_type
524 Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
525 {
526         value_type c = getChar(pos);
527         if (!lyxrc.rtl_support)
528                 return c;
529
530         value_type uc = c;
531         switch (c) {
532         case '(':
533                 uc = ')';
534                 break;
535         case ')':
536                 uc = '(';
537                 break;
538         case '[':
539                 uc = ']';
540                 break;
541         case ']':
542                 uc = '[';
543                 break;
544         case '{':
545                 uc = '}';
546                 break;
547         case '}':
548                 uc = '{';
549                 break;
550         case '<':
551                 uc = '>';
552                 break;
553         case '>':
554                 uc = '<';
555                 break;
556         }
557         if (uc != c && getFontSettings(bparams, pos).isRightToLeft())
558                 return uc;
559         else
560                 return c;
561 }
562
563
564 void Paragraph::setFont(pos_type pos, LyXFont const & font)
565 {
566         lyx::Assert(pos <= size());
567
568         // First, reduce font against layout/label font
569         // Update: The SetCharFont() routine in text2.C already
570         // reduces font, so we don't need to do that here. (Asger)
571         // No need to simplify this because it will disappear
572         // in a new kernel. (Asger)
573         // Next search font table
574
575         Pimpl::FontList::iterator beg = pimpl_->fontlist.begin();
576         Pimpl::FontList::iterator it = beg;
577         Pimpl::FontList::iterator endit = pimpl_->fontlist.end();
578         for (; it != endit; ++it) {
579                 if (it->pos() >= pos)
580                         break;
581         }
582         unsigned int i = std::distance(beg, it);
583         bool notfound = (it == endit);
584
585         if (!notfound && pimpl_->fontlist[i].font() == font)
586                 return;
587
588         bool begin = pos == 0 || notfound ||
589                 (i > 0 && pimpl_->fontlist[i - 1].pos() == pos - 1);
590         // Is position pos is a beginning of a font block?
591         bool end = !notfound && pimpl_->fontlist[i].pos() == pos;
592         // Is position pos is the end of a font block?
593         if (begin && end) { // A single char block
594                 if (i + 1 < pimpl_->fontlist.size() &&
595                     pimpl_->fontlist[i + 1].font() == font) {
596                         // Merge the singleton block with the next block
597                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
598                         if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
599                                 pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i - 1);
600                 } else if (i > 0 && pimpl_->fontlist[i - 1].font() == font) {
601                         // Merge the singleton block with the previous block
602                         pimpl_->fontlist[i - 1].pos(pos);
603                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
604                 } else
605                         pimpl_->fontlist[i].font(font);
606         } else if (begin) {
607                 if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
608                         pimpl_->fontlist[i - 1].pos(pos);
609                 else
610                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
611                                         Pimpl::FontTable(pos, font));
612         } else if (end) {
613                 pimpl_->fontlist[i].pos(pos - 1);
614                 if (!(i + 1 < pimpl_->fontlist.size() &&
615                       pimpl_->fontlist[i + 1].font() == font))
616                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
617                                         Pimpl::FontTable(pos, font));
618         } else { // The general case. The block is splitted into 3 blocks
619                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
620                                 Pimpl::FontTable(pos - 1, pimpl_->fontlist[i].font()));
621                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
622                                 Pimpl::FontTable(pos, font));
623         }
624 }
625
626
627 #ifndef NO_NEXT
628 void Paragraph::next(Paragraph * p)
629 {
630         next_ = p;
631 }
632
633
634 // This function is able to hide closed footnotes.
635 Paragraph * Paragraph::next()
636 {
637         return next_;
638 }
639
640
641 Paragraph const * Paragraph::next() const
642 {
643         return next_;
644 }
645
646
647 void Paragraph::previous(Paragraph * p)
648 {
649         previous_ = p;
650 }
651
652
653 // This function is able to hide closed footnotes.
654 Paragraph * Paragraph::previous()
655 {
656         return previous_;
657 }
658
659
660 // This function is able to hide closed footnotes.
661 Paragraph const * Paragraph::previous() const
662 {
663         return previous_;
664 }
665 #endif
666
667
668 void Paragraph::makeSameLayout(Paragraph const & par)
669 {
670         layout(par.layout());
671         // move to pimpl?
672         params() = par.params();
673 }
674
675
676 int Paragraph::stripLeadingSpaces()
677 {
678         if (layout()->free_spacing || isFreeSpacing())
679                 return 0;
680
681         int i = 0;
682         while (!empty() && (isNewline(0) || isLineSeparator(0))) {
683                 pimpl_->eraseIntern(0);
684                 ++i;
685         }
686
687         return i;
688 }
689
690
691 bool Paragraph::hasSameLayout(Paragraph const & par) const
692 {
693         return
694                 par.layout() == layout() &&
695                 params().sameLayout(par.params());
696 }
697
698
699 Paragraph::depth_type Paragraph::getDepth() const
700 {
701         return params().depth();
702 }
703
704
705 Paragraph::depth_type Paragraph::getMaxDepthAfter() const
706 {
707         if (layout()->isEnvironment())
708                 return params().depth() + 1;
709         else
710                 return params().depth();
711 }
712
713
714 char Paragraph::getAlign() const
715 {
716         return params().align();
717 }
718
719
720 string const & Paragraph::getLabelstring() const
721 {
722         return params().labelString();
723 }
724
725
726 // the next two functions are for the manual labels
727 string const Paragraph::getLabelWidthString() const
728 {
729         if (!params().labelWidthString().empty())
730                 return params().labelWidthString();
731         else
732                 return _("Senseless with this layout!");
733 }
734
735
736 void Paragraph::setLabelWidthString(string const & s)
737 {
738         params().labelWidthString(s);
739 }
740
741
742 void Paragraph::applyLayout(LyXLayout_ptr const & new_layout)
743 {
744         layout(new_layout);
745         params().labelWidthString(string());
746         params().align(LYX_ALIGN_LAYOUT);
747         params().spaceTop(VSpace(VSpace::NONE));
748         params().spaceBottom(VSpace(VSpace::NONE));
749         params().spacing(Spacing(Spacing::Default));
750 }
751
752
753 int Paragraph::beginningOfBody() const
754 {
755         if (layout()->labeltype != LABEL_MANUAL)
756                 return 0;
757
758         // Unroll the first two cycles of the loop
759         // and remember the previous character to
760         // remove unnecessary GetChar() calls
761         pos_type i = 0;
762         if (i < size() && !isNewline(i)) {
763                 ++i;
764                 char previous_char = 0;
765                 char temp = 0;
766                 if (i < size()) {
767                         previous_char = getChar(i);
768                         if (!isNewline(i)) {
769                                 ++i;
770                                 while (i < size() && previous_char != ' ') {
771                                         temp = getChar(i);
772                                         if (isNewline(i))
773                                                 break;
774
775                                         ++i;
776                                         previous_char = temp;
777                                 }
778                         }
779                 }
780         }
781
782         return i;
783 }
784
785
786 // returns -1 if inset not found
787 int Paragraph::getPositionOfInset(Inset const * inset) const
788 {
789         // Find the entry.
790         InsetList::iterator it = insetlist.begin();
791         InsetList::iterator end = insetlist.end();
792         for (; it != end; ++it)
793                 if (it.getInset() == inset)
794                         return it.getPos();
795         return -1;
796 }
797
798
799 InsetBibitem * Paragraph::bibitem()
800 {
801         InsetList::iterator it = insetlist.begin();
802         if (it != insetlist.end() && it.getInset()->lyxCode() == Inset::BIBTEX_CODE)
803                 return static_cast<InsetBibitem *>(it.getInset());
804         return 0;
805 }
806
807
808
809 // This could go to ParagraphParameters if we want to
810 int Paragraph::startTeXParParams(BufferParams const & bparams,
811                                  ostream & os, bool moving_arg) const
812 {
813         int column = 0;
814
815         if (params().noindent()) {
816                 os << "\\noindent ";
817                 column += 10;
818         }
819
820         switch (params().align()) {
821         case LYX_ALIGN_NONE:
822         case LYX_ALIGN_BLOCK:
823         case LYX_ALIGN_LAYOUT:
824         case LYX_ALIGN_SPECIAL:
825                 break;
826         case LYX_ALIGN_LEFT:
827         case LYX_ALIGN_RIGHT:
828         case LYX_ALIGN_CENTER:
829                 if (moving_arg) {
830                         os << "\\protect";
831                         column = 8;
832                 }
833                 break;
834         }
835
836         switch (params().align()) {
837         case LYX_ALIGN_NONE:
838         case LYX_ALIGN_BLOCK:
839         case LYX_ALIGN_LAYOUT:
840         case LYX_ALIGN_SPECIAL:
841                 break;
842         case LYX_ALIGN_LEFT:
843                 if (getParLanguage(bparams)->babel() != "hebrew") {
844                         os << "\\begin{flushleft}";
845                         column += 17;
846                 } else {
847                         os << "\\begin{flushright}";
848                         column += 18;
849                 }
850                 break;
851         case LYX_ALIGN_RIGHT:
852                 if (getParLanguage(bparams)->babel() != "hebrew") {
853                         os << "\\begin{flushright}";
854                         column += 18;
855                 } else {
856                         os << "\\begin{flushleft}";
857                         column += 17;
858                 }
859                 break;
860         case LYX_ALIGN_CENTER:
861                 os << "\\begin{center}";
862                 column += 14;
863                 break;
864         }
865
866         return column;
867 }
868
869
870 // This could go to ParagraphParameters if we want to
871 int Paragraph::endTeXParParams(BufferParams const & bparams,
872                                ostream & os, bool moving_arg) const
873 {
874         int column = 0;
875
876         switch (params().align()) {
877         case LYX_ALIGN_NONE:
878         case LYX_ALIGN_BLOCK:
879         case LYX_ALIGN_LAYOUT:
880         case LYX_ALIGN_SPECIAL:
881                 break;
882         case LYX_ALIGN_LEFT:
883         case LYX_ALIGN_RIGHT:
884         case LYX_ALIGN_CENTER:
885                 if (moving_arg) {
886                         os << "\\protect";
887                         column = 8;
888                 }
889                 break;
890         }
891
892         switch (params().align()) {
893         case LYX_ALIGN_NONE:
894         case LYX_ALIGN_BLOCK:
895         case LYX_ALIGN_LAYOUT:
896         case LYX_ALIGN_SPECIAL:
897                 break;
898         case LYX_ALIGN_LEFT:
899                 if (getParLanguage(bparams)->babel() != "hebrew") {
900                         os << "\\end{flushleft}";
901                         column = 15;
902                 } else {
903                         os << "\\end{flushright}";
904                         column = 16;
905                 }
906                 break;
907         case LYX_ALIGN_RIGHT:
908                 if (getParLanguage(bparams)->babel() != "hebrew") {
909                         os << "\\end{flushright}";
910                         column+= 16;
911                 } else {
912                         os << "\\end{flushleft}";
913                         column = 15;
914                 }
915                 break;
916         case LYX_ALIGN_CENTER:
917                 os << "\\end{center}";
918                 column = 12;
919                 break;
920         }
921         return column;
922 }
923
924
925 // This one spits out the text of the paragraph
926 bool Paragraph::simpleTeXOnePar(Buffer const * buf,
927                                 BufferParams const & bparams,
928                                 LyXFont const & outerfont,
929                                 ostream & os, TexRow & texrow,
930                                 bool moving_arg)
931 {
932         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...     " << this << endl;
933
934         bool return_value = false;
935
936         LyXLayout_ptr style;
937
938         // well we have to check if we are in an inset with unlimited
939         // lenght (all in one row) if that is true then we don't allow
940         // any special options in the paragraph and also we don't allow
941         // any environment other then "Standard" to be valid!
942         bool asdefault =
943                 (inInset() && inInset()->forceDefaultParagraphs(inInset()));
944
945         if (asdefault) {
946                 style = bparams.getLyXTextClass().defaultLayout();
947         } else {
948                 style = layout();
949         }
950
951         LyXFont basefont;
952
953         // Maybe we have to create a optional argument.
954         pos_type body_pos;
955
956         // FIXME: can we actually skip this check and just call
957         // beginningOfBody() ??
958         if (style->labeltype != LABEL_MANUAL) {
959                 body_pos = 0;
960         } else {
961                 body_pos = beginningOfBody();
962         }
963
964         unsigned int column = 0;
965
966         if (body_pos > 0) {
967                 os << '[';
968                 ++column;
969                 basefont = getLabelFont(bparams, outerfont);
970         } else {
971                 basefont = getLayoutFont(bparams, outerfont);
972         }
973
974         moving_arg |= style->needprotect;
975
976         // Which font is currently active?
977         LyXFont running_font(basefont);
978         // Do we have an open font change?
979         bool open_font = false;
980
981         Change::Type running_change = Change::UNCHANGED;
982
983         texrow.start(id(), 0);
984
985         // if the paragraph is empty, the loop will not be entered at all
986         if (empty()) {
987                 if (style->isCommand()) {
988                         os << '{';
989                         ++column;
990                 }
991                 if (!asdefault)
992                         column += startTeXParParams(bparams, os, moving_arg);
993
994         }
995
996         for (pos_type i = 0; i < size(); ++i) {
997                 ++column;
998                 // First char in paragraph or after label?
999                 if (i == body_pos) {
1000                         if (body_pos > 0) {
1001                                 if (open_font) {
1002                                         column += running_font.latexWriteEndChanges(os, basefont, basefont);
1003                                         open_font = false;
1004                                 }
1005                                 basefont = getLayoutFont(bparams, outerfont);
1006                                 running_font = basefont;
1007                                 os << ']';
1008                                 ++column;
1009                         }
1010                         if (style->isCommand()) {
1011                                 os << '{';
1012                                 ++column;
1013                         }
1014
1015                         if (!asdefault)
1016                                 column += startTeXParParams(bparams, os,
1017                                                             moving_arg);
1018                 }
1019
1020                 value_type c = getChar(i);
1021
1022                 // Fully instantiated font
1023                 LyXFont font = getFont(bparams, i, outerfont);
1024
1025                 LyXFont const last_font = running_font;
1026
1027                 // Spaces at end of font change are simulated to be
1028                 // outside font change, i.e. we write "\textXX{text} "
1029                 // rather than "\textXX{text }". (Asger)
1030                 if (open_font && c == ' ' && i <= size() - 2) {
1031                         LyXFont const & next_font = getFont(bparams, i + 1, outerfont);
1032                         if (next_font != running_font
1033                             && next_font != font) {
1034                                 font = next_font;
1035                         }
1036                 }
1037
1038                 // We end font definition before blanks
1039                 if (open_font &&
1040                     (font != running_font ||
1041                      font.language() != running_font.language()))
1042                 {
1043                         column += running_font.latexWriteEndChanges(os,
1044                                                                     basefont,
1045                                                                     (i == body_pos-1) ? basefont : font);
1046                         running_font = basefont;
1047                         open_font = false;
1048                 }
1049
1050                 // Blanks are printed before start of fontswitch
1051                 if (c == ' ') {
1052                         // Do not print the separation of the optional argument
1053                         if (i != body_pos - 1) {
1054                                 pimpl_->simpleTeXBlanks(os, texrow, i,
1055                                                        column, font, *style);
1056                         }
1057                 }
1058
1059                 // Do we need to change font?
1060                 if ((font != running_font ||
1061                      font.language() != running_font.language()) &&
1062                         i != body_pos - 1)
1063                 {
1064                         column += font.latexWriteStartChanges(os, basefont,
1065                                                               last_font);
1066                         running_font = font;
1067                         open_font = true;
1068                 }
1069
1070                 Change::Type change = pimpl_->lookupChange(i);
1071
1072                 column += Changes::latexMarkChange(os, running_change, change);
1073                 running_change = change;
1074
1075                 pimpl_->simpleTeXSpecialChars(buf, bparams,
1076                                               os, texrow, moving_arg,
1077                                               font, running_font,
1078                                               basefont, outerfont, open_font,
1079                                               running_change,
1080                                               *style, i, column, c);
1081         }
1082
1083         column += Changes::latexMarkChange(os,
1084                         running_change, Change::UNCHANGED);
1085
1086         // If we have an open font definition, we have to close it
1087         if (open_font) {
1088 #ifdef FIXED_LANGUAGE_END_DETECTION
1089                 if (next_) {
1090                         running_font
1091                                 .latexWriteEndChanges(os, basefont,
1092                                                       next_->getFont(bparams,
1093                                                       0, outerfont));
1094                 } else {
1095                         running_font.latexWriteEndChanges(os, basefont,
1096                                                           basefont);
1097                 }
1098 #else
1099 #ifdef WITH_WARNINGS
1100 //#warning For now we ALWAYS have to close the foreign font settings if they are
1101 //#warning there as we start another \selectlanguage with the next paragraph if
1102 //#warning we are in need of this. This should be fixed sometime (Jug)
1103 #endif
1104                 running_font.latexWriteEndChanges(os, basefont,  basefont);
1105 #endif
1106         }
1107
1108         // Needed if there is an optional argument but no contents.
1109         if (body_pos > 0 && body_pos == size()) {
1110                 os << "]~";
1111                 return_value = false;
1112         }
1113
1114         if (!asdefault) {
1115                 column += endTeXParParams(bparams, os, moving_arg);
1116         }
1117
1118         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...done " << this << endl;
1119         return return_value;
1120 }
1121
1122
1123
1124
1125 bool Paragraph::isHfill(pos_type pos) const
1126 {
1127         return IsInsetChar(getChar(pos))
1128                && getInset(pos)->lyxCode() == Inset::HFILL_CODE;
1129 }
1130
1131
1132 bool Paragraph::isInset(pos_type pos) const
1133 {
1134         return IsInsetChar(getChar(pos));
1135 }
1136
1137
1138 bool Paragraph::isNewline(pos_type pos) const
1139 {
1140         return IsInsetChar(getChar(pos))
1141                && getInset(pos)->lyxCode() == Inset::NEWLINE_CODE;
1142 }
1143
1144
1145 bool Paragraph::isSeparator(pos_type pos) const
1146 {
1147         return IsSeparatorChar(getChar(pos));
1148 }
1149
1150
1151 bool Paragraph::isLineSeparator(pos_type pos) const
1152 {
1153         value_type const c = getChar(pos);
1154         return IsLineSeparatorChar(c)
1155                 || (IsInsetChar(c) && getInset(pos) &&
1156                 getInset(pos)->isLineSeparator());
1157 }
1158
1159
1160 bool Paragraph::isKomma(pos_type pos) const
1161 {
1162         return IsKommaChar(getChar(pos));
1163 }
1164
1165
1166 /// Used by the spellchecker
1167 bool Paragraph::isLetter(pos_type pos) const
1168 {
1169         value_type const c = getChar(pos);
1170         if (IsLetterChar(c))
1171                 return true;
1172         if (isInset(pos))
1173                 return getInset(pos)->isLetter();
1174         // We want to pass the ' and escape chars to ispell
1175         string const extra = lyxrc.isp_esc_chars + '\'';
1176         return contains(extra, c);
1177 }
1178
1179
1180 bool Paragraph::isWord(pos_type pos) const
1181 {
1182         return IsWordChar(getChar(pos)) ;
1183 }
1184
1185
1186 Language const *
1187 Paragraph::getParLanguage(BufferParams const & bparams) const
1188 {
1189         if (!empty()) {
1190                 return getFirstFontSettings().language();
1191 #warning FIXME we should check the prev par as well (Lgb)
1192 #if 0
1193         } else if (previous_) {
1194                 return previous_->getParLanguage(bparams);
1195 #endif
1196         }else
1197                 return bparams.language;
1198 }
1199
1200
1201 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
1202 {
1203         return lyxrc.rtl_support
1204                 && getParLanguage(bparams)->RightToLeft()
1205                 && !(inInset() && inInset()->owner() &&
1206                      inInset()->owner()->lyxCode() == Inset::ERT_CODE);
1207 }
1208
1209
1210 void Paragraph::changeLanguage(BufferParams const & bparams,
1211                                Language const * from, Language const * to)
1212 {
1213         for (pos_type i = 0; i < size(); ++i) {
1214                 LyXFont font = getFontSettings(bparams, i);
1215                 if (font.language() == from) {
1216                         font.setLanguage(to);
1217                         setFont(i, font);
1218                 }
1219         }
1220 }
1221
1222
1223 bool Paragraph::isMultiLingual(BufferParams const & bparams)
1224 {
1225         Language const * doc_language = bparams.language;
1226         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1227         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
1228
1229         for (; cit != end; ++cit)
1230                 if (cit->font().language() != ignore_language &&
1231                     cit->font().language() != latex_language &&
1232                         cit->font().language() != doc_language)
1233                         return true;
1234         return false;
1235 }
1236
1237
1238 // Convert the paragraph to a string.
1239 // Used for building the table of contents
1240 string const Paragraph::asString(Buffer const * buffer, bool label) const
1241 {
1242         string s;
1243         if (label && !params().labelString().empty())
1244                 s += params().labelString() + ' ';
1245
1246         for (pos_type i = 0; i < size(); ++i) {
1247                 value_type c = getChar(i);
1248                 if (IsPrintable(c))
1249                         s += c;
1250                 else if (c == META_INSET &&
1251                          getInset(i)->lyxCode() == Inset::MATH_CODE) {
1252                         ostringstream ost;
1253                         getInset(i)->ascii(buffer, ost);
1254                         s += subst(STRCONV(ost.str()),'\n',' ');
1255                 }
1256         }
1257
1258         return s;
1259 }
1260
1261
1262 string const Paragraph::asString(Buffer const * buffer,
1263                                  pos_type beg, pos_type end, bool label) const
1264 {
1265         ostringstream os;
1266
1267         if (beg == 0 && label && !params().labelString().empty())
1268                 os << params().labelString() << ' ';
1269
1270         for (pos_type i = beg; i < end; ++i) {
1271                 value_type const c = getUChar(buffer->params, i);
1272                 if (IsPrintable(c))
1273                         os << c;
1274                 else if (c == META_INSET)
1275                         getInset(i)->ascii(buffer, os);
1276         }
1277
1278         return STRCONV(os.str());
1279 }
1280
1281
1282 void Paragraph::setInsetOwner(Inset * i)
1283 {
1284         pimpl_->inset_owner = i;
1285         InsetList::iterator it = insetlist.begin();
1286         InsetList::iterator end = insetlist.end();
1287         for (; it != end; ++it)
1288                 if (it.getInset())
1289                         it.getInset()->setOwner(i);
1290 }
1291
1292
1293 void Paragraph::deleteInsetsLyXText(BufferView * bv)
1294 {
1295         // then the insets
1296         insetlist.deleteInsetsLyXText(bv);
1297 }
1298
1299
1300 void Paragraph::resizeInsetsLyXText(BufferView * bv)
1301 {
1302         // then the insets
1303         insetlist.resizeInsetsLyXText(bv);
1304 }
1305
1306
1307 void Paragraph::setContentsFromPar(Paragraph const & par)
1308 {
1309         pimpl_->setContentsFromPar(par);
1310 }
1311
1312
1313 void Paragraph::trackChanges(Change::Type type)
1314 {
1315         pimpl_->trackChanges(type);
1316 }
1317
1318
1319 void Paragraph::untrackChanges()
1320 {
1321         pimpl_->untrackChanges();
1322 }
1323
1324
1325 void Paragraph::cleanChanges()
1326 {
1327         pimpl_->cleanChanges();
1328 }
1329
1330
1331 Change::Type Paragraph::lookupChange(lyx::pos_type pos) const
1332 {
1333         lyx::Assert(!size() || pos < size());
1334         return pimpl_->lookupChange(pos);
1335 }
1336
1337
1338 Change const Paragraph::lookupChangeFull(lyx::pos_type pos) const
1339 {
1340         lyx::Assert(!size() || pos < size());
1341         return pimpl_->lookupChangeFull(pos);
1342 }
1343
1344
1345 bool Paragraph::isChanged(pos_type start, pos_type end) const
1346 {
1347         return pimpl_->isChanged(start, end);
1348 }
1349
1350
1351 bool Paragraph::isChangeEdited(pos_type start, pos_type end) const
1352 {
1353         return pimpl_->isChangeEdited(start, end);
1354 }
1355
1356
1357 void Paragraph::setChange(lyx::pos_type pos, Change::Type type)
1358 {
1359         pimpl_->setChange(pos, type);
1360
1361 }
1362
1363
1364 void Paragraph::markErased()
1365 {
1366         pimpl_->markErased();
1367 }
1368
1369
1370 void Paragraph::acceptChange(pos_type start, pos_type end)
1371 {
1372         return pimpl_->acceptChange(start, end);
1373 }
1374
1375
1376 void Paragraph::rejectChange(pos_type start, pos_type end)
1377 {
1378         return pimpl_->rejectChange(start, end);
1379 }
1380
1381
1382 lyx::pos_type Paragraph::size() const
1383 {
1384         return pimpl_->size();
1385 }
1386
1387
1388 bool Paragraph::empty() const
1389 {
1390         return pimpl_->empty();
1391 }
1392
1393
1394 Paragraph::value_type Paragraph::getChar(pos_type pos) const
1395 {
1396         return pimpl_->getChar(pos);
1397 }
1398
1399
1400 int Paragraph::id() const
1401 {
1402         return pimpl_->id_;
1403 }
1404
1405
1406 LyXLayout_ptr const & Paragraph::layout() const
1407 {
1408         return layout_;
1409 }
1410
1411
1412 void Paragraph::layout(LyXLayout_ptr const & new_layout)
1413 {
1414         layout_ = new_layout;
1415 }
1416
1417
1418 Inset * Paragraph::inInset() const
1419 {
1420         return pimpl_->inset_owner;
1421 }
1422
1423
1424 void Paragraph::clearContents()
1425 {
1426         pimpl_->clear();
1427 }
1428
1429 void Paragraph::setChar(pos_type pos, value_type c)
1430 {
1431         pimpl_->setChar(pos, c);
1432 }
1433
1434
1435 ParagraphParameters & Paragraph::params()
1436 {
1437         return pimpl_->params;
1438 }
1439
1440
1441 ParagraphParameters const & Paragraph::params() const
1442 {
1443         return pimpl_->params;
1444 }
1445
1446
1447 bool Paragraph::isFreeSpacing() const
1448 {
1449         // for now we just need this, later should we need this in some
1450         // other way we can always add a function to Inset::() too.
1451         if (pimpl_->inset_owner && pimpl_->inset_owner->owner())
1452                 return (pimpl_->inset_owner->owner()->lyxCode() == Inset::ERT_CODE);
1453         return false;
1454 }