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