]> git.lyx.org Git - lyx.git/blob - src/paragraph.C
18a65dae301803561a49ae29158394c372da0f4d
[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\\begin_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
293 bool Paragraph::insertFromMinibuffer(pos_type pos)
294 {
295         if (minibuffer_char == Paragraph::META_INSET) {
296                 if (!insetAllowed(minibuffer_inset->lyxCode()))
297                         return false;
298                 insertInset(pos, minibuffer_inset, minibuffer_font);
299         } else {
300                 LyXFont f = minibuffer_font;
301                 if (!checkInsertChar(f))
302                         return false;
303                 insertChar(pos, minibuffer_char, f);
304         }
305         return true;
306 }
307
308 // end of minibuffer
309
310
311 void Paragraph::eraseIntern(lyx::pos_type pos)
312 {
313         pimpl_->eraseIntern(pos);
314 }
315
316
317 bool Paragraph::erase(pos_type pos)
318 {
319         return pimpl_->erase(pos);
320 }
321
322
323 int Paragraph::erase(pos_type start, pos_type end)
324 {
325         return pimpl_->erase(start, end);
326 }
327
328
329 bool Paragraph::checkInsertChar(LyXFont & font)
330 {
331         if (pimpl_->inset_owner)
332                 return pimpl_->inset_owner->checkInsertChar(font);
333         return true;
334 }
335
336
337 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c)
338 {
339         insertChar(pos, c, LyXFont(LyXFont::ALL_INHERIT));
340 }
341
342
343 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
344                            LyXFont const & font, Change change)
345 {
346         pimpl_->insertChar(pos, c, font, change);
347 }
348
349
350 void Paragraph::insertInset(pos_type pos, InsetOld * inset)
351 {
352         insertInset(pos, inset, LyXFont(LyXFont::ALL_INHERIT));
353 }
354
355
356 void Paragraph::insertInset(pos_type pos, InsetOld * inset, LyXFont const & font, Change change)
357 {
358         pimpl_->insertInset(pos, inset, font, change);
359 }
360
361
362 bool Paragraph::insetAllowed(InsetOld::Code code)
363 {
364         //lyxerr << "Paragraph::InsertInsetAllowed" << endl;
365         if (pimpl_->inset_owner)
366                 return pimpl_->inset_owner->insetAllowed(code);
367         return true;
368 }
369
370
371 InsetOld * Paragraph::getInset(pos_type pos)
372 {
373         Assert(pos < size());
374         return insetlist.get(pos);
375 }
376
377
378 InsetOld const * Paragraph::getInset(pos_type pos) const
379 {
380         Assert(pos < size());
381         return insetlist.get(pos);
382 }
383
384
385 // Gets uninstantiated font setting at position.
386 LyXFont const Paragraph::getFontSettings(BufferParams const & bparams,
387                                          pos_type pos) const
388 {
389         Assert(pos <= size());
390
391         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
392         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
393         for (; cit != end; ++cit)
394                 if (cit->pos() >= pos)
395                         break;
396
397         if (cit != end)
398                 return cit->font();
399
400         if (pos == size() && !empty())
401                 return getFontSettings(bparams, pos - 1);
402
403         return LyXFont(LyXFont::ALL_INHERIT, getParLanguage(bparams));
404 }
405
406
407 lyx::pos_type Paragraph::getEndPosOfFontSpan(lyx::pos_type pos) const
408 {
409         Assert(pos <= size());
410
411         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
412         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
413         for (; cit != end; ++cit)
414                 if (cit->pos() >= pos)
415                         return cit->pos();
416
417         // This should not happen, but if so, we take no chances.
418         lyxerr << "Pararaph::getEndPosOfFontSpan: This should not happen!\n";
419         return pos;
420 }
421
422
423 // Gets uninstantiated font setting at position 0
424 LyXFont const Paragraph::getFirstFontSettings() const
425 {
426         if (!empty() && !pimpl_->fontlist.empty())
427                 return pimpl_->fontlist[0].font();
428
429         return LyXFont(LyXFont::ALL_INHERIT);
430 }
431
432
433 // Gets the fully instantiated font at a given position in a paragraph
434 // This is basically the same function as LyXText::GetFont() in text2.C.
435 // The difference is that this one is used for generating the LaTeX file,
436 // and thus cosmetic "improvements" are disallowed: This has to deliver
437 // the true picture of the buffer. (Asger)
438 LyXFont const Paragraph::getFont(BufferParams const & bparams, pos_type pos,
439                                  LyXFont const & outerfont) const
440 {
441         Assert(pos >= 0);
442
443         LyXLayout_ptr const & lout = layout();
444
445         pos_type const body_pos = beginningOfBody();
446
447         LyXFont layoutfont;
448         if (pos < body_pos)
449                 layoutfont = lout->labelfont;
450         else
451                 layoutfont = lout->font;
452
453         LyXFont tmpfont = getFontSettings(bparams, pos);
454         tmpfont.realize(layoutfont);
455         tmpfont.realize(outerfont);
456         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
457
458         return tmpfont;
459 }
460
461
462 LyXFont const Paragraph::getLabelFont(BufferParams const & bparams,
463                                       LyXFont const & outerfont) const
464 {
465         LyXFont tmpfont = layout()->labelfont;
466         tmpfont.setLanguage(getParLanguage(bparams));
467         tmpfont.realize(outerfont);
468         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
469         return tmpfont;
470 }
471
472
473 LyXFont const Paragraph::getLayoutFont(BufferParams const & bparams,
474                                        LyXFont const & outerfont) const
475 {
476         LyXFont tmpfont = layout()->font;
477         tmpfont.setLanguage(getParLanguage(bparams));
478         tmpfont.realize(outerfont);
479         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
480         return tmpfont;
481 }
482
483
484 /// Returns the height of the highest font in range
485 LyXFont::FONT_SIZE
486 Paragraph::highestFontInRange(pos_type startpos, pos_type endpos,
487                               LyXFont::FONT_SIZE const def_size) const
488 {
489         if (pimpl_->fontlist.empty())
490                 return def_size;
491
492         Pimpl::FontList::const_iterator end_it = pimpl_->fontlist.begin();
493         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
494         for (; end_it != end; ++end_it) {
495                 if (end_it->pos() >= endpos)
496                         break;
497         }
498
499         if (end_it != end)
500                 ++end_it;
501
502         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
503         for (; cit != end; ++cit) {
504                 if (cit->pos() >= startpos)
505                         break;
506         }
507
508         LyXFont::FONT_SIZE maxsize = LyXFont::SIZE_TINY;
509         for (; cit != end_it; ++cit) {
510                 LyXFont::FONT_SIZE size = cit->font().size();
511                 if (size == LyXFont::INHERIT_SIZE)
512                         size = def_size;
513                 if (size > maxsize && size <= LyXFont::SIZE_HUGER)
514                         maxsize = size;
515         }
516         return maxsize;
517 }
518
519
520 Paragraph::value_type
521 Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
522 {
523         value_type c = getChar(pos);
524         if (!lyxrc.rtl_support)
525                 return c;
526
527         value_type uc = c;
528         switch (c) {
529         case '(':
530                 uc = ')';
531                 break;
532         case ')':
533                 uc = '(';
534                 break;
535         case '[':
536                 uc = ']';
537                 break;
538         case ']':
539                 uc = '[';
540                 break;
541         case '{':
542                 uc = '}';
543                 break;
544         case '}':
545                 uc = '{';
546                 break;
547         case '<':
548                 uc = '>';
549                 break;
550         case '>':
551                 uc = '<';
552                 break;
553         }
554         if (uc != c && getFontSettings(bparams, pos).isRightToLeft())
555                 return uc;
556         else
557                 return c;
558 }
559
560
561 void Paragraph::setFont(pos_type pos, LyXFont const & font)
562 {
563         Assert(pos <= size());
564
565         // First, reduce font against layout/label font
566         // Update: The SetCharFont() routine in text2.C already
567         // reduces font, so we don't need to do that here. (Asger)
568         // No need to simplify this because it will disappear
569         // in a new kernel. (Asger)
570         // Next search font table
571
572         Pimpl::FontList::iterator beg = pimpl_->fontlist.begin();
573         Pimpl::FontList::iterator it = beg;
574         Pimpl::FontList::iterator endit = pimpl_->fontlist.end();
575         for (; it != endit; ++it) {
576                 if (it->pos() >= pos)
577                         break;
578         }
579         unsigned int i = std::distance(beg, it);
580         bool notfound = (it == endit);
581
582         if (!notfound && pimpl_->fontlist[i].font() == font)
583                 return;
584
585         bool begin = pos == 0 || notfound ||
586                 (i > 0 && pimpl_->fontlist[i - 1].pos() == pos - 1);
587         // Is position pos is a beginning of a font block?
588         bool end = !notfound && pimpl_->fontlist[i].pos() == pos;
589         // Is position pos is the end of a font block?
590         if (begin && end) { // A single char block
591                 if (i + 1 < pimpl_->fontlist.size() &&
592                     pimpl_->fontlist[i + 1].font() == font) {
593                         // Merge the singleton block with the next block
594                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
595                         if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
596                                 pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i - 1);
597                 } else if (i > 0 && pimpl_->fontlist[i - 1].font() == font) {
598                         // Merge the singleton block with the previous block
599                         pimpl_->fontlist[i - 1].pos(pos);
600                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
601                 } else
602                         pimpl_->fontlist[i].font(font);
603         } else if (begin) {
604                 if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
605                         pimpl_->fontlist[i - 1].pos(pos);
606                 else
607                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
608                                         Pimpl::FontTable(pos, font));
609         } else if (end) {
610                 pimpl_->fontlist[i].pos(pos - 1);
611                 if (!(i + 1 < pimpl_->fontlist.size() &&
612                       pimpl_->fontlist[i + 1].font() == font))
613                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
614                                         Pimpl::FontTable(pos, font));
615         } else { // The general case. The block is splitted into 3 blocks
616                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
617                                 Pimpl::FontTable(pos - 1, pimpl_->fontlist[i].font()));
618                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
619                                 Pimpl::FontTable(pos, font));
620         }
621 }
622
623
624 void Paragraph::makeSameLayout(Paragraph const & par)
625 {
626         layout(par.layout());
627         // move to pimpl?
628         params() = par.params();
629 }
630
631
632 int Paragraph::stripLeadingSpaces()
633 {
634         if (isFreeSpacing())
635                 return 0;
636
637         int i = 0;
638         while (!empty() && (isNewline(0) || isLineSeparator(0))) {
639                 pimpl_->eraseIntern(0);
640                 ++i;
641         }
642
643         return i;
644 }
645
646
647 bool Paragraph::hasSameLayout(Paragraph const & par) const
648 {
649         return
650                 par.layout() == layout() &&
651                 params().sameLayout(par.params());
652 }
653
654
655 Paragraph::depth_type Paragraph::getDepth() const
656 {
657         return params().depth();
658 }
659
660
661 Paragraph::depth_type Paragraph::getMaxDepthAfter() const
662 {
663         if (layout()->isEnvironment())
664                 return params().depth() + 1;
665         else
666                 return params().depth();
667 }
668
669
670 char Paragraph::getAlign() const
671 {
672         return params().align();
673 }
674
675
676 string const & Paragraph::getLabelstring() const
677 {
678         return params().labelString();
679 }
680
681
682 // the next two functions are for the manual labels
683 string const Paragraph::getLabelWidthString() const
684 {
685         if (!params().labelWidthString().empty())
686                 return params().labelWidthString();
687         else
688                 return _("Senseless with this layout!");
689 }
690
691
692 void Paragraph::setLabelWidthString(string const & s)
693 {
694         params().labelWidthString(s);
695 }
696
697
698 void Paragraph::applyLayout(LyXLayout_ptr const & new_layout)
699 {
700         layout(new_layout);
701         params().labelWidthString(string());
702         params().align(LYX_ALIGN_LAYOUT);
703         params().spaceTop(VSpace(VSpace::NONE));
704         params().spaceBottom(VSpace(VSpace::NONE));
705         params().spacing(Spacing(Spacing::Default));
706 }
707
708
709 int Paragraph::beginningOfBody() const
710 {
711         if (layout()->labeltype != LABEL_MANUAL)
712                 return 0;
713
714         // Unroll the first two cycles of the loop
715         // and remember the previous character to
716         // remove unnecessary GetChar() calls
717         pos_type i = 0;
718         if (i < size() && !isNewline(i)) {
719                 ++i;
720                 char previous_char = 0;
721                 char temp = 0;
722                 if (i < size()) {
723                         previous_char = getChar(i);
724                         if (!isNewline(i)) {
725                                 ++i;
726                                 while (i < size() && previous_char != ' ') {
727                                         temp = getChar(i);
728                                         if (isNewline(i))
729                                                 break;
730
731                                         ++i;
732                                         previous_char = temp;
733                                 }
734                         }
735                 }
736         }
737
738         return i;
739 }
740
741
742 // returns -1 if inset not found
743 int Paragraph::getPositionOfInset(InsetOld const * inset) const
744 {
745         // Find the entry.
746         InsetList::const_iterator it = insetlist.begin();
747         InsetList::const_iterator end = insetlist.end();
748         for (; it != end; ++it)
749                 if (it->inset == inset)
750                         return it->pos;
751         return -1;
752 }
753
754
755 InsetBibitem * Paragraph::bibitem() const
756 {
757         InsetList::const_iterator it = insetlist.begin();
758         if (it != insetlist.end() && it->inset->lyxCode() == InsetOld::BIBTEX_CODE)
759                 return static_cast<InsetBibitem *>(it->inset);
760         return 0;
761 }
762
763
764
765 // This could go to ParagraphParameters if we want to
766 int Paragraph::startTeXParParams(BufferParams const & bparams,
767                                  ostream & os, bool moving_arg) const
768 {
769         int column = 0;
770
771         if (params().noindent()) {
772                 os << "\\noindent ";
773                 column += 10;
774         }
775
776         switch (params().align()) {
777         case LYX_ALIGN_NONE:
778         case LYX_ALIGN_BLOCK:
779         case LYX_ALIGN_LAYOUT:
780         case LYX_ALIGN_SPECIAL:
781                 break;
782         case LYX_ALIGN_LEFT:
783         case LYX_ALIGN_RIGHT:
784         case LYX_ALIGN_CENTER:
785                 if (moving_arg) {
786                         os << "\\protect";
787                         column = 8;
788                 }
789                 break;
790         }
791
792         switch (params().align()) {
793         case LYX_ALIGN_NONE:
794         case LYX_ALIGN_BLOCK:
795         case LYX_ALIGN_LAYOUT:
796         case LYX_ALIGN_SPECIAL:
797                 break;
798         case LYX_ALIGN_LEFT:
799                 if (getParLanguage(bparams)->babel() != "hebrew") {
800                         os << "\\begin{flushleft}";
801                         column += 17;
802                 } else {
803                         os << "\\begin{flushright}";
804                         column += 18;
805                 }
806                 break;
807         case LYX_ALIGN_RIGHT:
808                 if (getParLanguage(bparams)->babel() != "hebrew") {
809                         os << "\\begin{flushright}";
810                         column += 18;
811                 } else {
812                         os << "\\begin{flushleft}";
813                         column += 17;
814                 }
815                 break;
816         case LYX_ALIGN_CENTER:
817                 os << "\\begin{center}";
818                 column += 14;
819                 break;
820         }
821
822         return column;
823 }
824
825
826 // This could go to ParagraphParameters if we want to
827 int Paragraph::endTeXParParams(BufferParams const & bparams,
828                                ostream & os, bool moving_arg) const
829 {
830         int column = 0;
831
832         switch (params().align()) {
833         case LYX_ALIGN_NONE:
834         case LYX_ALIGN_BLOCK:
835         case LYX_ALIGN_LAYOUT:
836         case LYX_ALIGN_SPECIAL:
837                 break;
838         case LYX_ALIGN_LEFT:
839         case LYX_ALIGN_RIGHT:
840         case LYX_ALIGN_CENTER:
841                 if (moving_arg) {
842                         os << "\\protect";
843                         column = 8;
844                 }
845                 break;
846         }
847
848         switch (params().align()) {
849         case LYX_ALIGN_NONE:
850         case LYX_ALIGN_BLOCK:
851         case LYX_ALIGN_LAYOUT:
852         case LYX_ALIGN_SPECIAL:
853                 break;
854         case LYX_ALIGN_LEFT:
855                 if (getParLanguage(bparams)->babel() != "hebrew") {
856                         os << "\\end{flushleft}";
857                         column = 15;
858                 } else {
859                         os << "\\end{flushright}";
860                         column = 16;
861                 }
862                 break;
863         case LYX_ALIGN_RIGHT:
864                 if (getParLanguage(bparams)->babel() != "hebrew") {
865                         os << "\\end{flushright}";
866                         column+= 16;
867                 } else {
868                         os << "\\end{flushleft}";
869                         column = 15;
870                 }
871                 break;
872         case LYX_ALIGN_CENTER:
873                 os << "\\end{center}";
874                 column = 12;
875                 break;
876         }
877         return column;
878 }
879
880
881 // This one spits out the text of the paragraph
882 bool Paragraph::simpleTeXOnePar(Buffer const * buf,
883                                 BufferParams const & bparams,
884                                 LyXFont const & outerfont,
885                                 ostream & os, TexRow & texrow,
886                                 LatexRunParams const & runparams)
887 {
888         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...     " << this << endl;
889
890         bool return_value = false;
891
892         LyXLayout_ptr style;
893
894         // well we have to check if we are in an inset with unlimited
895         // lenght (all in one row) if that is true then we don't allow
896         // any special options in the paragraph and also we don't allow
897         // any environment other then "Standard" to be valid!
898         bool asdefault =
899                 (inInset() && inInset()->forceDefaultParagraphs(inInset()));
900
901         if (asdefault) {
902                 style = bparams.getLyXTextClass().defaultLayout();
903         } else {
904                 style = layout();
905         }
906
907         LyXFont basefont;
908
909         // Maybe we have to create a optional argument.
910         pos_type body_pos;
911
912         // FIXME: can we actually skip this check and just call
913         // beginningOfBody() ??
914         if (style->labeltype != LABEL_MANUAL) {
915                 body_pos = 0;
916         } else {
917                 body_pos = beginningOfBody();
918         }
919
920         unsigned int column = 0;
921
922         if (body_pos > 0) {
923                 os << '[';
924                 ++column;
925                 basefont = getLabelFont(bparams, outerfont);
926         } else {
927                 basefont = getLayoutFont(bparams, outerfont);
928         }
929
930         bool moving_arg = runparams.moving_arg;
931         moving_arg |= style->needprotect;
932
933         // Which font is currently active?
934         LyXFont running_font(basefont);
935         // Do we have an open font change?
936         bool open_font = false;
937
938         Change::Type running_change = Change::UNCHANGED;
939
940         texrow.start(id(), 0);
941
942         // if the paragraph is empty, the loop will not be entered at all
943         if (empty()) {
944                 if (style->isCommand()) {
945                         os << '{';
946                         ++column;
947                 }
948                 if (!asdefault)
949                         column += startTeXParParams(bparams, os, moving_arg);
950
951         }
952
953         for (pos_type i = 0; i < size(); ++i) {
954                 ++column;
955                 // First char in paragraph or after label?
956                 if (i == body_pos) {
957                         if (body_pos > 0) {
958                                 if (open_font) {
959                                         column += running_font.latexWriteEndChanges(os, basefont, basefont);
960                                         open_font = false;
961                                 }
962                                 basefont = getLayoutFont(bparams, outerfont);
963                                 running_font = basefont;
964                                 os << ']';
965                                 ++column;
966                         }
967                         if (style->isCommand()) {
968                                 os << '{';
969                                 ++column;
970                         }
971
972                         if (!asdefault)
973                                 column += startTeXParParams(bparams, os,
974                                                             moving_arg);
975                 }
976
977                 value_type c = getChar(i);
978
979                 // Fully instantiated font
980                 LyXFont font = getFont(bparams, i, outerfont);
981
982                 LyXFont const last_font = running_font;
983
984                 // Spaces at end of font change are simulated to be
985                 // outside font change, i.e. we write "\textXX{text} "
986                 // rather than "\textXX{text }". (Asger)
987                 if (open_font && c == ' ' && i <= size() - 2) {
988                         LyXFont const & next_font = getFont(bparams, i + 1, outerfont);
989                         if (next_font != running_font
990                             && next_font != font) {
991                                 font = next_font;
992                         }
993                 }
994
995                 // We end font definition before blanks
996                 if (open_font &&
997                     (font != running_font ||
998                      font.language() != running_font.language()))
999                 {
1000                         column += running_font.latexWriteEndChanges(os,
1001                                                                     basefont,
1002                                                                     (i == body_pos-1) ? basefont : font);
1003                         running_font = basefont;
1004                         open_font = false;
1005                 }
1006
1007                 // Blanks are printed before start of fontswitch
1008                 if (c == ' ') {
1009                         // Do not print the separation of the optional argument
1010                         if (i != body_pos - 1) {
1011                                 pimpl_->simpleTeXBlanks(os, texrow, i,
1012                                                        column, font, *style);
1013                         }
1014                 }
1015
1016                 // Do we need to change font?
1017                 if ((font != running_font ||
1018                      font.language() != running_font.language()) &&
1019                         i != body_pos - 1)
1020                 {
1021                         column += font.latexWriteStartChanges(os, basefont,
1022                                                               last_font);
1023                         running_font = font;
1024                         open_font = true;
1025                 }
1026
1027                 Change::Type change = pimpl_->lookupChange(i);
1028
1029                 column += Changes::latexMarkChange(os, running_change, change);
1030                 running_change = change;
1031
1032                 LatexRunParams rp = runparams;
1033                 rp.moving_arg = moving_arg;
1034                 rp.free_spacing = style->free_spacing;
1035                 pimpl_->simpleTeXSpecialChars(buf, bparams,
1036                                               os, texrow, runparams,
1037                                               font, running_font,
1038                                               basefont, outerfont, open_font,
1039                                               running_change,
1040                                               *style, i, column, c);
1041         }
1042
1043         column += Changes::latexMarkChange(os,
1044                         running_change, Change::UNCHANGED);
1045
1046         // If we have an open font definition, we have to close it
1047         if (open_font) {
1048 #ifdef FIXED_LANGUAGE_END_DETECTION
1049                 if (next_) {
1050                         running_font
1051                                 .latexWriteEndChanges(os, basefont,
1052                                                       next_->getFont(bparams,
1053                                                       0, outerfont));
1054                 } else {
1055                         running_font.latexWriteEndChanges(os, basefont,
1056                                                           basefont);
1057                 }
1058 #else
1059 #ifdef WITH_WARNINGS
1060 //#warning For now we ALWAYS have to close the foreign font settings if they are
1061 //#warning there as we start another \selectlanguage with the next paragraph if
1062 //#warning we are in need of this. This should be fixed sometime (Jug)
1063 #endif
1064                 running_font.latexWriteEndChanges(os, basefont,  basefont);
1065 #endif
1066         }
1067
1068         // Needed if there is an optional argument but no contents.
1069         if (body_pos > 0 && body_pos == size()) {
1070                 os << "]~";
1071                 return_value = false;
1072         }
1073
1074         if (!asdefault) {
1075                 column += endTeXParParams(bparams, os, moving_arg);
1076         }
1077
1078         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...done " << this << endl;
1079         return return_value;
1080 }
1081
1082
1083
1084
1085 bool Paragraph::isHfill(pos_type pos) const
1086 {
1087         return IsInsetChar(getChar(pos))
1088                && getInset(pos)->lyxCode() == InsetOld::HFILL_CODE;
1089 }
1090
1091
1092 bool Paragraph::isInset(pos_type pos) const
1093 {
1094         return IsInsetChar(getChar(pos));
1095 }
1096
1097
1098 bool Paragraph::isNewline(pos_type pos) const
1099 {
1100         return IsInsetChar(getChar(pos))
1101                && getInset(pos)->lyxCode() == InsetOld::NEWLINE_CODE;
1102 }
1103
1104
1105 bool Paragraph::isSeparator(pos_type pos) const
1106 {
1107         return IsSeparatorChar(getChar(pos));
1108 }
1109
1110
1111 bool Paragraph::isLineSeparator(pos_type pos) const
1112 {
1113         value_type const c = getChar(pos);
1114         return IsLineSeparatorChar(c)
1115                 || (IsInsetChar(c) && getInset(pos) &&
1116                 getInset(pos)->isLineSeparator());
1117 }
1118
1119
1120 bool Paragraph::isKomma(pos_type pos) const
1121 {
1122         return IsKommaChar(getChar(pos));
1123 }
1124
1125
1126 /// Used by the spellchecker
1127 bool Paragraph::isLetter(pos_type pos) const
1128 {
1129         value_type const c = getChar(pos);
1130         if (IsLetterChar(c))
1131                 return true;
1132         if (isInset(pos))
1133                 return getInset(pos)->isLetter();
1134         // We want to pass the ' and escape chars to ispell
1135         string const extra = lyxrc.isp_esc_chars + '\'';
1136         return contains(extra, c);
1137 }
1138
1139
1140 bool Paragraph::isWord(pos_type pos) const
1141 {
1142         return IsWordChar(getChar(pos)) ;
1143 }
1144
1145
1146 Language const *
1147 Paragraph::getParLanguage(BufferParams const & bparams) const
1148 {
1149         if (!empty())
1150                 return getFirstFontSettings().language();
1151 #warning FIXME we should check the prev par as well (Lgb)
1152         return bparams.language;
1153 }
1154
1155
1156 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
1157 {
1158         return lyxrc.rtl_support
1159                 && getParLanguage(bparams)->RightToLeft()
1160                 && !(inInset() && inInset()->owner() &&
1161                      inInset()->owner()->lyxCode() == InsetOld::ERT_CODE);
1162 }
1163
1164
1165 void Paragraph::changeLanguage(BufferParams const & bparams,
1166                                Language const * from, Language const * to)
1167 {
1168         for (pos_type i = 0; i < size(); ++i) {
1169                 LyXFont font = getFontSettings(bparams, i);
1170                 if (font.language() == from) {
1171                         font.setLanguage(to);
1172                         setFont(i, font);
1173                 }
1174         }
1175 }
1176
1177
1178 bool Paragraph::isMultiLingual(BufferParams const & bparams)
1179 {
1180         Language const * doc_language = bparams.language;
1181         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1182         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
1183
1184         for (; cit != end; ++cit)
1185                 if (cit->font().language() != ignore_language &&
1186                     cit->font().language() != latex_language &&
1187                         cit->font().language() != doc_language)
1188                         return true;
1189         return false;
1190 }
1191
1192
1193 // Convert the paragraph to a string.
1194 // Used for building the table of contents
1195 string const Paragraph::asString(Buffer const * buffer, bool label) const
1196 {
1197 #if 0
1198         string s;
1199         if (label && !params().labelString().empty())
1200                 s += params().labelString() + ' ';
1201
1202         for (pos_type i = 0; i < size(); ++i) {
1203                 value_type c = getChar(i);
1204                 if (IsPrintable(c))
1205                         s += c;
1206                 else if (c == META_INSET &&
1207                          getInset(i)->lyxCode() == InsetOld::MATH_CODE) {
1208                         ostringstream ost;
1209                         getInset(i)->ascii(buffer, ost);
1210                         s += subst(STRCONV(ost.str()),'\n',' ');
1211                 }
1212         }
1213
1214         return s;
1215 #else
1216         // This should really be done by the caller and not here.
1217         string ret(asString(buffer, 0, size(), label));
1218         return subst(ret, '\n', ' ');
1219 #endif
1220 }
1221
1222
1223 string const Paragraph::asString(Buffer const * buffer,
1224                                  pos_type beg, pos_type end, bool label) const
1225 {
1226         ostringstream os;
1227
1228         if (beg == 0 && label && !params().labelString().empty())
1229                 os << params().labelString() << ' ';
1230
1231         for (pos_type i = beg; i < end; ++i) {
1232                 value_type const c = getUChar(buffer->params, i);
1233                 if (IsPrintable(c))
1234                         os << c;
1235                 else if (c == META_INSET)
1236                         getInset(i)->ascii(buffer, os);
1237         }
1238
1239         return STRCONV(os.str());
1240 }
1241
1242
1243 void Paragraph::setInsetOwner(UpdatableInset * inset)
1244 {
1245         pimpl_->inset_owner = inset;
1246         InsetList::iterator it = insetlist.begin();
1247         InsetList::iterator end = insetlist.end();
1248         for (; it != end; ++it)
1249                 if (it->inset)
1250                         it->inset->setOwner(inset);
1251 }
1252
1253
1254 void Paragraph::deleteInsetsLyXText(BufferView * bv)
1255 {
1256         insetlist.deleteInsetsLyXText(bv);
1257 }
1258
1259
1260 void Paragraph::resizeInsetsLyXText(BufferView * bv)
1261 {
1262         insetlist.resizeInsetsLyXText(bv);
1263 }
1264
1265
1266 void Paragraph::setContentsFromPar(Paragraph const & par)
1267 {
1268         pimpl_->setContentsFromPar(par);
1269 }
1270
1271
1272 void Paragraph::trackChanges(Change::Type type)
1273 {
1274         pimpl_->trackChanges(type);
1275 }
1276
1277
1278 void Paragraph::untrackChanges()
1279 {
1280         pimpl_->untrackChanges();
1281 }
1282
1283
1284 void Paragraph::cleanChanges()
1285 {
1286         pimpl_->cleanChanges();
1287 }
1288
1289
1290 Change::Type Paragraph::lookupChange(lyx::pos_type pos) const
1291 {
1292         Assert(!size() || pos < size());
1293         return pimpl_->lookupChange(pos);
1294 }
1295
1296
1297 Change const Paragraph::lookupChangeFull(lyx::pos_type pos) const
1298 {
1299         Assert(!size() || pos < size());
1300         return pimpl_->lookupChangeFull(pos);
1301 }
1302
1303
1304 bool Paragraph::isChanged(pos_type start, pos_type end) const
1305 {
1306         return pimpl_->isChanged(start, end);
1307 }
1308
1309
1310 bool Paragraph::isChangeEdited(pos_type start, pos_type end) const
1311 {
1312         return pimpl_->isChangeEdited(start, end);
1313 }
1314
1315
1316 void Paragraph::setChange(lyx::pos_type pos, Change::Type type)
1317 {
1318         pimpl_->setChange(pos, type);
1319
1320 }
1321
1322
1323 void Paragraph::markErased()
1324 {
1325         pimpl_->markErased();
1326 }
1327
1328
1329 void Paragraph::acceptChange(pos_type start, pos_type end)
1330 {
1331         return pimpl_->acceptChange(start, end);
1332 }
1333
1334
1335 void Paragraph::rejectChange(pos_type start, pos_type end)
1336 {
1337         return pimpl_->rejectChange(start, end);
1338 }
1339
1340
1341 lyx::pos_type Paragraph::size() const
1342 {
1343         return pimpl_->size();
1344 }
1345
1346
1347 bool Paragraph::empty() const
1348 {
1349         return pimpl_->empty();
1350 }
1351
1352
1353 Paragraph::value_type Paragraph::getChar(pos_type pos) const
1354 {
1355         return pimpl_->getChar(pos);
1356 }
1357
1358
1359 int Paragraph::id() const
1360 {
1361         return pimpl_->id_;
1362 }
1363
1364
1365 void Paragraph::id(int i)
1366 {
1367         pimpl_->id_ = i;
1368 }
1369
1370
1371 LyXLayout_ptr const & Paragraph::layout() const
1372 {
1373 /*
1374         InsetOld * inset = inInset();
1375         if (inset && inset->lyxCode() == InsetOld::ENVIRONMENT_CODE)
1376                 return static_cast<InsetEnvironment*>(inset)->layout();
1377 */
1378         return layout_;
1379 }
1380
1381
1382 void Paragraph::layout(LyXLayout_ptr const & new_layout)
1383 {
1384         layout_ = new_layout;
1385 }
1386
1387
1388 UpdatableInset * Paragraph::inInset() const
1389 {
1390         return pimpl_->inset_owner;
1391 }
1392
1393
1394 void Paragraph::clearContents()
1395 {
1396         pimpl_->clear();
1397 }
1398
1399 void Paragraph::setChar(pos_type pos, value_type c)
1400 {
1401         pimpl_->setChar(pos, c);
1402 }
1403
1404
1405 ParagraphParameters & Paragraph::params()
1406 {
1407         return pimpl_->params;
1408 }
1409
1410
1411 ParagraphParameters const & Paragraph::params() const
1412 {
1413         return pimpl_->params;
1414 }
1415
1416
1417 bool Paragraph::isFreeSpacing() const
1418 {
1419         if (layout()->free_spacing)
1420                 return true;
1421
1422         // for now we just need this, later should we need this in some
1423         // other way we can always add a function to InsetOld::() too.
1424         if (pimpl_->inset_owner && pimpl_->inset_owner->owner())
1425                 return (pimpl_->inset_owner->owner()->lyxCode() == InsetOld::ERT_CODE);
1426         return false;
1427 }
1428
1429
1430 bool Paragraph::allowEmpty() const
1431 {
1432         if (layout()->keepempty)
1433                 return true;
1434         if (pimpl_->inset_owner && pimpl_->inset_owner->owner())
1435                 return (pimpl_->inset_owner->owner()->lyxCode() == InsetOld::ERT_CODE);
1436         return false;
1437 }
1438
1439
1440 bool operator==(Paragraph const & lhs, Paragraph const & rhs)
1441 {
1442 #warning FIXME this implementatoin must be completely wrong...
1443         return &lhs == &rhs;
1444 }