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