]> git.lyx.org Git - lyx.git/blob - src/paragraph.C
1dec5968edb708a13aa3997c57942645e511ab28
[lyx.git] / src / paragraph.C
1 /**
2  * \file paragraph.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Angus Leeming
10  * \author John Levon
11  * \author André Pönitz
12  * \author Dekel Tsur
13  * \author Jürgen Vigna
14  *
15  * Full author contact details are available in file CREDITS.
16  */
17
18 #include <config.h>
19
20 #include "paragraph.h"
21 #include "paragraph_pimpl.h"
22
23 #include "buffer.h"
24 #include "bufferparams.h"
25 #include "counters.h"
26 #include "encoding.h"
27 #include "debug.h"
28 #include "gettext.h"
29 #include "language.h"
30 #include "lyxfont.h"
31 #include "lyxrc.h"
32 #include "lyxrow.h"
33 #include "outputparams.h"
34 #include "paragraph_funcs.h"
35 #include "sgml.h"
36 #include "texrow.h"
37 #include "vspace.h"
38
39 #include "insets/insetbibitem.h"
40 #include "insets/insetoptarg.h"
41
42 #include "support/lstrings.h"
43 #include "support/std_sstream.h"
44 #include "support/textutils.h"
45 #include "support/tostr.h"
46
47 #include <boost/tuple/tuple.hpp>
48 #include <boost/bind.hpp>
49
50 #include <list>
51 #include <stack>
52
53 using lyx::pos_type;
54
55 using lyx::support::contains;
56 using lyx::support::subst;
57
58 using std::endl;
59 using std::list;
60 using std::stack;
61 using std::string;
62 using std::ostream;
63 using std::ostringstream;
64
65
66 Paragraph::Paragraph()
67         : y(0), height(0), begin_of_body_(0),
68           pimpl_(new Paragraph::Pimpl(this))
69 {
70         itemdepth = 0;
71         params().clear();
72 }
73
74
75 Paragraph::Paragraph(Paragraph const & par)
76         : y(0), height(0), text_(par.text_), begin_of_body_(par.begin_of_body_),
77           pimpl_(new Paragraph::Pimpl(*par.pimpl_, this))
78 {
79         itemdepth = 0;
80         // this is because of the dummy layout of the paragraphs that
81         // follow footnotes
82         layout_ = par.layout();
83
84         // copy everything behind the break-position to the new paragraph
85         insetlist = par.insetlist;
86         InsetList::iterator it = insetlist.begin();
87         InsetList::iterator end = insetlist.end();
88         for (; it != end; ++it)
89                 it->inset = it->inset->clone().release();
90 }
91
92
93 void Paragraph::operator=(Paragraph const & par)
94 {
95         // needed as we will destroy the pimpl_ before copying it
96         if (&par != this)
97                 return;
98
99         lyxerr << "Paragraph::operator=()" << endl;
100
101         text_ = par.text_;
102
103         delete pimpl_;
104         pimpl_ = new Pimpl(*par.pimpl_, this);
105
106         itemdepth = par.itemdepth;
107         // this is because of the dummy layout of the paragraphs that
108         // follow footnotes
109         layout_ = par.layout();
110
111         // copy everything behind the break-position to the new paragraph
112         insetlist = par.insetlist;
113         InsetList::iterator it = insetlist.begin();
114         InsetList::iterator end = insetlist.end();
115         for (; it != end; ++it)
116                 it->inset = it->inset->clone().release();
117 }
118
119
120 Paragraph::~Paragraph()
121 {
122         delete pimpl_;
123         //
124         //lyxerr << "Paragraph::paragraph_id = "
125         //       << Paragraph::paragraph_id << endl;
126 }
127
128
129 void Paragraph::write(Buffer const & buf, ostream & os,
130                           BufferParams const & bparams,
131                           depth_type & dth) const
132 {
133         // The beginning or end of a deeper (i.e. nested) area?
134         if (dth != params().depth()) {
135                 if (params().depth() > dth) {
136                         while (params().depth() > dth) {
137                                 os << "\n\\begin_deeper ";
138                                 ++dth;
139                         }
140                 } else {
141                         while (params().depth() < dth) {
142                                 os << "\n\\end_deeper ";
143                                 --dth;
144                         }
145                 }
146         }
147
148         // First write the layout
149         os << "\n\\begin_layout " << layout()->name() << '\n';
150
151         params().write(os);
152
153         LyXFont font1(LyXFont::ALL_INHERIT, bparams.language);
154
155         Change running_change = Change(Change::UNCHANGED);
156         lyx::time_type const curtime(lyx::current_time());
157
158         int column = 0;
159         for (pos_type i = 0; i < size(); ++i) {
160                 if (!i) {
161                         os << '\n';
162                         column = 0;
163                 }
164
165                 Change change = pimpl_->lookupChangeFull(i);
166                 Changes::lyxMarkChange(os, column, curtime, running_change, change);
167                 running_change = change;
168
169                 // Write font changes
170                 LyXFont font2 = getFontSettings(bparams, i);
171                 if (font2 != font1) {
172                         font2.lyxWriteChanges(font1, os);
173                         column = 0;
174                         font1 = font2;
175                 }
176
177                 value_type const c = getChar(i);
178                 switch (c) {
179                 case META_INSET:
180                 {
181                         InsetBase const * inset = getInset(i);
182                         if (inset)
183                                 if (inset->directWrite()) {
184                                         // international char, let it write
185                                         // code directly so it's shorter in
186                                         // the file
187                                         inset->write(buf, os);
188                                 } else {
189                                         os << "\n\\begin_inset ";
190                                         inset->write(buf, os);
191                                         os << "\n\\end_inset \n\n";
192                                         column = 0;
193                                 }
194                 }
195                 break;
196                 case '\\':
197                         os << "\n\\backslash \n";
198                         column = 0;
199                         break;
200                 case '.':
201                         if (i + 1 < size() && getChar(i + 1) == ' ') {
202                                 os << ".\n";
203                                 column = 0;
204                         } else
205                                 os << '.';
206                         break;
207                 default:
208                         if ((column > 70 && c == ' ')
209                             || column > 79) {
210                                 os << '\n';
211                                 column = 0;
212                         }
213                         // this check is to amend a bug. LyX sometimes
214                         // inserts '\0' this could cause problems.
215                         if (c != '\0')
216                                 os << c;
217                         else
218                                 lyxerr << "ERROR (Paragraph::writeFile):"
219                                         " NULL char in structure." << endl;
220                         ++column;
221                         break;
222                 }
223         }
224
225         // to make reading work properly
226         if (!size()) {
227                 running_change = pimpl_->lookupChange(0);
228                 Changes::lyxMarkChange(os, column, curtime,
229                         Change(Change::UNCHANGED), running_change);
230         }
231         Changes::lyxMarkChange(os, column, curtime,
232                 running_change, Change(Change::UNCHANGED));
233
234         os << "\n\\end_layout\n";
235 }
236
237
238 void Paragraph::validate(LaTeXFeatures & features) const
239 {
240         pimpl_->validate(features, *layout());
241 }
242
243
244 void Paragraph::eraseIntern(lyx::pos_type pos)
245 {
246         pimpl_->eraseIntern(pos);
247 }
248
249
250 bool Paragraph::erase(pos_type pos)
251 {
252         return pimpl_->erase(pos);
253 }
254
255
256 int Paragraph::erase(pos_type start, pos_type end)
257 {
258         return pimpl_->erase(start, end);
259 }
260
261
262 void Paragraph::insert(pos_type start, string const & str,
263                        LyXFont const & font)
264 {
265         int size = str.size();
266         for (int i = 0 ; i < size ; ++i)
267                 insertChar(start + i, str[i], font);
268 }
269
270
271 bool Paragraph::checkInsertChar(LyXFont &)
272 {
273         return true;
274 }
275
276
277 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c)
278 {
279         insertChar(pos, c, LyXFont(LyXFont::ALL_INHERIT));
280 }
281
282
283 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
284                            LyXFont const & font, Change change)
285 {
286         pimpl_->insertChar(pos, c, font, change);
287 }
288
289
290 void Paragraph::insertInset(pos_type pos, InsetBase * inset)
291 {
292         insertInset(pos, inset, LyXFont(LyXFont::ALL_INHERIT));
293 }
294
295
296 void Paragraph::insertInset(pos_type pos, InsetBase * inset,
297         LyXFont const & font, Change change)
298 {
299         pimpl_->insertInset(pos, inset, font, change);
300 }
301
302
303 bool Paragraph::insetAllowed(InsetOld_code code)
304 {
305         //lyxerr << "Paragraph::InsertInsetAllowed" << endl;
306         if (pimpl_->inset_owner)
307                 return pimpl_->inset_owner->insetAllowed(code);
308         return true;
309 }
310
311
312 InsetBase * Paragraph::getInset(pos_type pos)
313 {
314         BOOST_ASSERT(pos < size());
315         return insetlist.get(pos);
316 }
317
318
319 InsetBase const * Paragraph::getInset(pos_type pos) const
320 {
321         BOOST_ASSERT(pos < size());
322         return insetlist.get(pos);
323 }
324
325
326 // Gets uninstantiated font setting at position.
327 LyXFont const Paragraph::getFontSettings(BufferParams const & bparams,
328                                          pos_type pos) const
329 {
330         BOOST_ASSERT(pos <= size());
331
332         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
333         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
334         for (; cit != end; ++cit)
335                 if (cit->pos() >= pos)
336                         break;
337
338         if (cit != end)
339                 return cit->font();
340
341         if (pos == size() && !empty())
342                 return getFontSettings(bparams, pos - 1);
343
344         return LyXFont(LyXFont::ALL_INHERIT, getParLanguage(bparams));
345 }
346
347
348 lyx::pos_type Paragraph::getEndPosOfFontSpan(lyx::pos_type pos) const
349 {
350         BOOST_ASSERT(pos <= size());
351
352         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
353         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
354         for (; cit != end; ++cit)
355                 if (cit->pos() >= pos)
356                         return cit->pos();
357
358         // This should not happen, but if so, we take no chances.
359         //lyxerr << "Paragraph::getEndPosOfFontSpan: This should not happen!"
360         //      << endl;
361         return pos;
362 }
363
364
365 // Gets uninstantiated font setting at position 0
366 LyXFont const Paragraph::getFirstFontSettings() const
367 {
368         if (!empty() && !pimpl_->fontlist.empty())
369                 return pimpl_->fontlist[0].font();
370
371         return LyXFont(LyXFont::ALL_INHERIT);
372 }
373
374
375 // Gets the fully instantiated font at a given position in a paragraph
376 // This is basically the same function as LyXText::GetFont() in text2.C.
377 // The difference is that this one is used for generating the LaTeX file,
378 // and thus cosmetic "improvements" are disallowed: This has to deliver
379 // the true picture of the buffer. (Asger)
380 LyXFont const Paragraph::getFont(BufferParams const & bparams, pos_type pos,
381                                  LyXFont const & outerfont) const
382 {
383         BOOST_ASSERT(pos >= 0);
384
385         LyXLayout_ptr const & lout = layout();
386
387         pos_type const body_pos = beginOfBody();
388
389         LyXFont layoutfont;
390         if (pos < body_pos)
391                 layoutfont = lout->labelfont;
392         else
393                 layoutfont = lout->font;
394
395         LyXFont font = getFontSettings(bparams, pos);
396         font.realize(layoutfont);
397         font.realize(outerfont);
398         font.realize(bparams.getLyXTextClass().defaultfont());
399
400         return font;
401 }
402
403
404 LyXFont const Paragraph::getLabelFont(BufferParams const & bparams,
405                                       LyXFont const & outerfont) const
406 {
407         LyXFont tmpfont = layout()->labelfont;
408         tmpfont.setLanguage(getParLanguage(bparams));
409         tmpfont.realize(outerfont);
410         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
411         return tmpfont;
412 }
413
414
415 LyXFont const Paragraph::getLayoutFont(BufferParams const & bparams,
416                                        LyXFont const & outerfont) const
417 {
418         LyXFont tmpfont = layout()->font;
419         tmpfont.setLanguage(getParLanguage(bparams));
420         tmpfont.realize(outerfont);
421         tmpfont.realize(bparams.getLyXTextClass().defaultfont());
422         return tmpfont;
423 }
424
425
426 /// Returns the height of the highest font in range
427 LyXFont_size
428 Paragraph::highestFontInRange(pos_type startpos, pos_type endpos,
429                               LyXFont_size def_size) const
430 {
431         if (pimpl_->fontlist.empty())
432                 return def_size;
433
434         Pimpl::FontList::const_iterator end_it = pimpl_->fontlist.begin();
435         Pimpl::FontList::const_iterator const end = pimpl_->fontlist.end();
436         for (; end_it != end; ++end_it) {
437                 if (end_it->pos() >= endpos)
438                         break;
439         }
440
441         if (end_it != end)
442                 ++end_it;
443
444         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
445         for (; cit != end; ++cit) {
446                 if (cit->pos() >= startpos)
447                         break;
448         }
449
450         LyXFont::FONT_SIZE maxsize = LyXFont::SIZE_TINY;
451         for (; cit != end_it; ++cit) {
452                 LyXFont::FONT_SIZE size = cit->font().size();
453                 if (size == LyXFont::INHERIT_SIZE)
454                         size = def_size;
455                 if (size > maxsize && size <= LyXFont::SIZE_HUGER)
456                         maxsize = size;
457         }
458         return maxsize;
459 }
460
461
462 Paragraph::value_type
463 Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
464 {
465         value_type c = getChar(pos);
466         if (!lyxrc.rtl_support)
467                 return c;
468
469         value_type uc = c;
470         switch (c) {
471         case '(':
472                 uc = ')';
473                 break;
474         case ')':
475                 uc = '(';
476                 break;
477         case '[':
478                 uc = ']';
479                 break;
480         case ']':
481                 uc = '[';
482                 break;
483         case '{':
484                 uc = '}';
485                 break;
486         case '}':
487                 uc = '{';
488                 break;
489         case '<':
490                 uc = '>';
491                 break;
492         case '>':
493                 uc = '<';
494                 break;
495         }
496         if (uc != c && getFontSettings(bparams, pos).isRightToLeft())
497                 return uc;
498         else
499                 return c;
500 }
501
502
503 void Paragraph::setFont(pos_type pos, LyXFont const & font)
504 {
505         BOOST_ASSERT(pos <= size());
506
507         // First, reduce font against layout/label font
508         // Update: The setCharFont() routine in text2.C already
509         // reduces font, so we don't need to do that here. (Asger)
510         // No need to simplify this because it will disappear
511         // in a new kernel. (Asger)
512         // Next search font table
513
514         Pimpl::FontList::iterator beg = pimpl_->fontlist.begin();
515         Pimpl::FontList::iterator it = beg;
516         Pimpl::FontList::iterator endit = pimpl_->fontlist.end();
517         for (; it != endit; ++it) {
518                 if (it->pos() >= pos)
519                         break;
520         }
521         unsigned int i = std::distance(beg, it);
522         bool notfound = (it == endit);
523
524         if (!notfound && pimpl_->fontlist[i].font() == font)
525                 return;
526
527         bool begin = pos == 0 || notfound ||
528                 (i > 0 && pimpl_->fontlist[i - 1].pos() == pos - 1);
529         // Is position pos is a beginning of a font block?
530         bool end = !notfound && pimpl_->fontlist[i].pos() == pos;
531         // Is position pos is the end of a font block?
532         if (begin && end) { // A single char block
533                 if (i + 1 < pimpl_->fontlist.size() &&
534                     pimpl_->fontlist[i + 1].font() == font) {
535                         // Merge the singleton block with the next block
536                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
537                         if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
538                                 pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i - 1);
539                 } else if (i > 0 && pimpl_->fontlist[i - 1].font() == font) {
540                         // Merge the singleton block with the previous block
541                         pimpl_->fontlist[i - 1].pos(pos);
542                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
543                 } else
544                         pimpl_->fontlist[i].font(font);
545         } else if (begin) {
546                 if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
547                         pimpl_->fontlist[i - 1].pos(pos);
548                 else
549                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
550                                         Pimpl::FontTable(pos, font));
551         } else if (end) {
552                 pimpl_->fontlist[i].pos(pos - 1);
553                 if (!(i + 1 < pimpl_->fontlist.size() &&
554                       pimpl_->fontlist[i + 1].font() == font))
555                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
556                                         Pimpl::FontTable(pos, font));
557         } else { // The general case. The block is splitted into 3 blocks
558                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
559                                 Pimpl::FontTable(pos - 1, pimpl_->fontlist[i].font()));
560                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
561                                 Pimpl::FontTable(pos, font));
562         }
563 }
564
565
566 void Paragraph::makeSameLayout(Paragraph const & par)
567 {
568         layout(par.layout());
569         // move to pimpl?
570         params() = par.params();
571 }
572
573
574 int Paragraph::stripLeadingSpaces()
575 {
576         if (isFreeSpacing())
577                 return 0;
578
579         int i = 0;
580         while (!empty() && (isNewline(0) || isLineSeparator(0))) {
581                 pimpl_->eraseIntern(0);
582                 ++i;
583         }
584
585         return i;
586 }
587
588
589 bool Paragraph::hasSameLayout(Paragraph const & par) const
590 {
591         return par.layout() == layout() && params().sameLayout(par.params());
592 }
593
594
595 Paragraph::depth_type Paragraph::getDepth() const
596 {
597         return params().depth();
598 }
599
600
601 Paragraph::depth_type Paragraph::getMaxDepthAfter() const
602 {
603         if (layout()->isEnvironment())
604                 return params().depth() + 1;
605         else
606                 return params().depth();
607 }
608
609
610 char Paragraph::getAlign() const
611 {
612         return params().align();
613 }
614
615
616 string const & Paragraph::getLabelstring() const
617 {
618         return params().labelString();
619 }
620
621
622 // the next two functions are for the manual labels
623 string const Paragraph::getLabelWidthString() const
624 {
625         if (!params().labelWidthString().empty())
626                 return params().labelWidthString();
627         else
628                 return _("Senseless with this layout!");
629 }
630
631
632 void Paragraph::setLabelWidthString(string const & s)
633 {
634         params().labelWidthString(s);
635 }
636
637
638 void Paragraph::applyLayout(LyXLayout_ptr const & new_layout)
639 {
640         layout(new_layout);
641         params().labelWidthString(string());
642         params().align(LYX_ALIGN_LAYOUT);
643         params().spacing(Spacing(Spacing::Default));
644 }
645
646
647 int Paragraph::beginOfBody() const
648 {
649         return begin_of_body_;
650 }
651
652
653 void Paragraph::setBeginOfBody()
654 {
655         if (layout()->labeltype != LABEL_MANUAL) {
656                 begin_of_body_ = 0;
657                 return;
658         }
659
660         // Unroll the first two cycles of the loop
661         // and remember the previous character to
662         // remove unnecessary getChar() calls
663         pos_type i = 0;
664         pos_type end = size();
665         if (i < end && !isNewline(i)) {
666                 ++i;
667                 char previous_char = 0;
668                 char temp = 0;
669                 if (i < end) {
670                         previous_char = text_[i];
671                         if (!isNewline(i)) {
672                                 ++i;
673                                 while (i < end && previous_char != ' ') {
674                                         temp = text_[i];
675                                         if (isNewline(i))
676                                                 break;
677                                         ++i;
678                                         previous_char = temp;
679                                 }
680                         }
681                 }
682         }
683
684         begin_of_body_ = i;
685 }
686
687
688 // returns -1 if inset not found
689 int Paragraph::getPositionOfInset(InsetBase const * inset) const
690 {
691         // Find the entry.
692         InsetList::const_iterator it = insetlist.begin();
693         InsetList::const_iterator end = insetlist.end();
694         for (; it != end; ++it)
695                 if (it->inset == inset)
696                         return it->pos;
697         return -1;
698 }
699
700
701 InsetBibitem * Paragraph::bibitem() const
702 {
703         if (!insetlist.empty()) {
704                 InsetBase * inset = insetlist.begin()->inset;
705                 if (inset->lyxCode() == InsetBase::BIBTEX_CODE)
706                         return static_cast<InsetBibitem *>(inset);
707         }
708         return 0;
709 }
710
711
712 namespace {
713
714 // paragraphs inside floats need different alignment tags to avoid
715 // unwanted space 
716
717 bool noTrivlistCentering(UpdatableInset const * inset)
718 {
719         if (inset && inset->owner()) {
720                 InsetBase::Code const code = inset->owner()->lyxCode();
721                 return code == InsetBase::FLOAT_CODE ||
722                         code == InsetBase::WRAP_CODE;
723         }
724         return false;
725 }
726
727
728 string correction(string const & orig)
729 {
730         if (orig == "flushleft")
731                 return "raggedright";
732         if (orig == "flushright")
733                 return "raggedleft";
734         if (orig == "center")
735                 return "centering";
736         return orig;
737 }
738
739
740 string const corrected_env(string const & suffix, string const & env,
741                            UpdatableInset const * inset)
742 {
743         string output = suffix + "{";
744         if (noTrivlistCentering(inset))
745                 output += correction(env);
746         else
747                 output += env;
748         return output + "}";
749 }
750
751 } // namespace anon
752
753
754 // This could go to ParagraphParameters if we want to
755 int Paragraph::startTeXParParams(BufferParams const & bparams,
756                                  ostream & os, bool moving_arg) const
757 {
758         int column = 0;
759
760         if (params().noindent()) {
761                 os << "\\noindent ";
762                 column += 10;
763         }
764
765         switch (params().align()) {
766         case LYX_ALIGN_NONE:
767         case LYX_ALIGN_BLOCK:
768         case LYX_ALIGN_LAYOUT:
769         case LYX_ALIGN_SPECIAL:
770                 break;
771         case LYX_ALIGN_LEFT:
772         case LYX_ALIGN_RIGHT:
773         case LYX_ALIGN_CENTER:
774                 if (moving_arg) {
775                         os << "\\protect";
776                         column = 8;
777                 }
778                 break;
779         }
780
781         switch (params().align()) {
782         case LYX_ALIGN_NONE:
783         case LYX_ALIGN_BLOCK:
784         case LYX_ALIGN_LAYOUT:
785         case LYX_ALIGN_SPECIAL:
786                 break;
787         case LYX_ALIGN_LEFT: {
788                 string output;
789                 UpdatableInset const * const inset = pimpl_->inset_owner;
790                 if (getParLanguage(bparams)->babel() != "hebrew")
791                         output = corrected_env("\\begin", "flushleft", inset);
792                 else
793                         output = corrected_env("\\begin", "flushright", inset);
794                 os << output;
795                 column += output.size();
796                 break;
797         } case LYX_ALIGN_RIGHT: {
798                 string output;
799                 UpdatableInset const * const inset = pimpl_->inset_owner;
800                 if (getParLanguage(bparams)->babel() != "hebrew")
801                         output = corrected_env("\\begin", "flushright", inset);
802                 else
803                         output = corrected_env("\\begin", "flushleft", inset);
804                 os << output;
805                 column += output.size();
806                 break;
807         } case LYX_ALIGN_CENTER: {
808                 string output;
809                 output = corrected_env("\\begin", "center", pimpl_->inset_owner);
810                 os << output;
811                 column += output.size();
812                 break;
813         }
814         }
815
816         return column;
817 }
818
819
820 // This could go to ParagraphParameters if we want to
821 int Paragraph::endTeXParParams(BufferParams const & bparams,
822                                ostream & os, bool moving_arg) const
823 {
824         int column = 0;
825
826         switch (params().align()) {
827         case LYX_ALIGN_NONE:
828         case LYX_ALIGN_BLOCK:
829         case LYX_ALIGN_LAYOUT:
830         case LYX_ALIGN_SPECIAL:
831                 break;
832         case LYX_ALIGN_LEFT:
833         case LYX_ALIGN_RIGHT:
834         case LYX_ALIGN_CENTER:
835                 if (moving_arg) {
836                         os << "\\protect";
837                         column = 8;
838                 }
839                 break;
840         }
841
842         switch (params().align()) {
843         case LYX_ALIGN_NONE:
844         case LYX_ALIGN_BLOCK:
845         case LYX_ALIGN_LAYOUT:
846         case LYX_ALIGN_SPECIAL:
847                 break;
848         case LYX_ALIGN_LEFT: {
849                 string output;
850                 UpdatableInset const * const inset = pimpl_->inset_owner;
851                 if (getParLanguage(bparams)->babel() != "hebrew")
852                         output = corrected_env("\\par\\end", "flushleft", inset);
853                 else
854                         output = corrected_env("\\par\\end", "flushright", inset);
855                 os << output;
856                 column += output.size();
857                 break;
858         } case LYX_ALIGN_RIGHT: {
859                 string output;
860                 UpdatableInset const * const inset = pimpl_->inset_owner;
861                 if (getParLanguage(bparams)->babel() != "hebrew")
862                         output = corrected_env("\\par\\end", "flushright", inset);
863                 else
864                         output = corrected_env("\\par\\end", "flushleft", inset);
865                 os << output;
866                 column += output.size();
867                 break;
868         } case LYX_ALIGN_CENTER: {
869                 string output;
870                 output = corrected_env("\\par\\end", "center", pimpl_->inset_owner);
871                 os << output;
872                 column += output.size();
873                 break;
874         }
875         }
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                                 OutputParams 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         // length (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 = beginOfBody();
911         unsigned int column = 0;
912
913         if (body_pos > 0) {
914                 os << '[';
915                 ++column;
916                 basefont = getLabelFont(bparams, outerfont);
917         } else {
918                 basefont = getLayoutFont(bparams, outerfont);
919         }
920
921         bool moving_arg = runparams.moving_arg;
922         moving_arg |= style->needprotect;
923
924         // Which font is currently active?
925         LyXFont running_font(basefont);
926         // Do we have an open font change?
927         bool open_font = false;
928
929         Change::Type running_change = Change::UNCHANGED;
930
931         texrow.start(id(), 0);
932
933         // if the paragraph is empty, the loop will not be entered at all
934         if (empty()) {
935                 if (style->isCommand()) {
936                         os << '{';
937                         ++column;
938                 }
939                 if (!asdefault)
940                         column += startTeXParParams(bparams, os, moving_arg);
941         }
942
943         for (pos_type i = 0; i < size(); ++i) {
944                 ++column;
945                 // First char in paragraph or after label?
946                 if (i == body_pos) {
947                         if (body_pos > 0) {
948                                 if (open_font) {
949                                         column += running_font.latexWriteEndChanges(os, basefont, basefont);
950                                         open_font = false;
951                                 }
952                                 basefont = getLayoutFont(bparams, outerfont);
953                                 running_font = basefont;
954                                 os << ']';
955                                 ++column;
956                         }
957                         if (style->isCommand()) {
958                                 os << '{';
959                                 ++column;
960                         }
961
962                         if (!asdefault)
963                                 column += startTeXParParams(bparams, os,
964                                                             moving_arg);
965                 }
966
967                 value_type c = getChar(i);
968
969                 // Fully instantiated font
970                 LyXFont font = getFont(bparams, i, outerfont);
971
972                 LyXFont const last_font = running_font;
973
974                 // Spaces at end of font change are simulated to be
975                 // outside font change, i.e. we write "\textXX{text} "
976                 // rather than "\textXX{text }". (Asger)
977                 if (open_font && c == ' ' && i <= size() - 2) {
978                         LyXFont const & next_font = getFont(bparams, i + 1, outerfont);
979                         if (next_font != running_font && next_font != font) {
980                                 font = next_font;
981                         }
982                 }
983
984                 // We end font definition before blanks
985                 if (open_font &&
986                     (font != running_font ||
987                      font.language() != running_font.language()))
988                 {
989                         column += running_font.latexWriteEndChanges(os,
990                                                                     basefont,
991                                                                     (i == body_pos-1) ? basefont : font);
992                         running_font = basefont;
993                         open_font = false;
994                 }
995
996                 // Blanks are printed before start of fontswitch
997                 if (c == ' ') {
998                         // Do not print the separation of the optional argument
999                         if (i != body_pos - 1) {
1000                                 pimpl_->simpleTeXBlanks(os, texrow, i,
1001                                                        column, font, *style);
1002                         }
1003                 }
1004
1005                 // Do we need to change font?
1006                 if ((font != running_font ||
1007                      font.language() != running_font.language()) &&
1008                         i != body_pos - 1)
1009                 {
1010                         column += font.latexWriteStartChanges(os, basefont,
1011                                                               last_font);
1012                         running_font = font;
1013                         open_font = true;
1014                 }
1015
1016                 Change::Type change = pimpl_->lookupChange(i);
1017
1018                 column += Changes::latexMarkChange(os, running_change, change);
1019                 running_change = change;
1020
1021                 OutputParams rp = runparams;
1022                 rp.moving_arg = moving_arg;
1023                 rp.free_spacing = style->free_spacing;
1024                 pimpl_->simpleTeXSpecialChars(buf, bparams,
1025                                               os, texrow, runparams,
1026                                               font, running_font,
1027                                               basefont, outerfont, open_font,
1028                                               running_change,
1029                                               *style, i, column, c);
1030         }
1031
1032         column += Changes::latexMarkChange(os,
1033                         running_change, Change::UNCHANGED);
1034
1035         // If we have an open font definition, we have to close it
1036         if (open_font) {
1037 #ifdef FIXED_LANGUAGE_END_DETECTION
1038                 if (next_) {
1039                         running_font
1040                                 .latexWriteEndChanges(os, basefont,
1041                                                       next_->getFont(bparams,
1042                                                       0, outerfont));
1043                 } else {
1044                         running_font.latexWriteEndChanges(os, basefont,
1045                                                           basefont);
1046                 }
1047 #else
1048 #ifdef WITH_WARNINGS
1049 //#warning For now we ALWAYS have to close the foreign font settings if they are
1050 //#warning there as we start another \selectlanguage with the next paragraph if
1051 //#warning we are in need of this. This should be fixed sometime (Jug)
1052 #endif
1053                 running_font.latexWriteEndChanges(os, basefont,  basefont);
1054 #endif
1055         }
1056
1057         // Needed if there is an optional argument but no contents.
1058         if (body_pos > 0 && body_pos == size()) {
1059                 os << "]~";
1060                 return_value = false;
1061         }
1062
1063         if (!asdefault) {
1064                 column += endTeXParParams(bparams, os, moving_arg);
1065         }
1066
1067         lyxerr[Debug::LATEX] << "SimpleTeXOnePar...done " << this << endl;
1068         return return_value;
1069 }
1070
1071
1072 namespace {
1073
1074 // checks, if newcol chars should be put into this line
1075 // writes newline, if necessary.
1076 void sgmlLineBreak(ostream & os, string::size_type & colcount,
1077                           string::size_type newcol)
1078 {
1079         colcount += newcol;
1080         if (colcount > lyxrc.ascii_linelen) {
1081                 os << "\n";
1082                 colcount = newcol; // assume write after this call
1083         }
1084 }
1085
1086 enum PAR_TAG {
1087         PAR_NONE=0,
1088         TT = 1,
1089         SF = 2,
1090         BF = 4,
1091         IT = 8,
1092         SL = 16,
1093         EM = 32
1094 };
1095
1096
1097 string tag_name(PAR_TAG const & pt) {
1098         switch (pt) {
1099         case PAR_NONE: return "!-- --";
1100         case TT: return "tt";
1101         case SF: return "sf";
1102         case BF: return "bf";
1103         case IT: return "it";
1104         case SL: return "sl";
1105         case EM: return "em";
1106         }
1107         return "";
1108 }
1109
1110
1111 inline
1112 void operator|=(PAR_TAG & p1, PAR_TAG const & p2)
1113 {
1114         p1 = static_cast<PAR_TAG>(p1 | p2);
1115 }
1116
1117
1118 inline
1119 void reset(PAR_TAG & p1, PAR_TAG const & p2)
1120 {
1121         p1 = static_cast<PAR_TAG>(p1 & ~p2);
1122 }
1123
1124 } // anon
1125
1126
1127 // Handle internal paragraph parsing -- layout already processed.
1128 void Paragraph::simpleLinuxDocOnePar(Buffer const & buf,
1129                                      ostream & os,
1130                                      LyXFont const & outerfont,
1131                                      OutputParams const & runparams,
1132                                      lyx::depth_type /*depth*/) const
1133 {
1134         LyXLayout_ptr const & style = layout();
1135
1136         string::size_type char_line_count = 5;     // Heuristic choice ;-)
1137
1138         // gets paragraph main font
1139         LyXFont font_old;
1140         bool desc_on;
1141         if (style->labeltype == LABEL_MANUAL) {
1142                 font_old = style->labelfont;
1143                 desc_on = true;
1144         } else {
1145                 font_old = style->font;
1146                 desc_on = false;
1147         }
1148
1149         LyXFont::FONT_FAMILY family_type = LyXFont::ROMAN_FAMILY;
1150         LyXFont::FONT_SERIES series_type = LyXFont::MEDIUM_SERIES;
1151         LyXFont::FONT_SHAPE  shape_type  = LyXFont::UP_SHAPE;
1152         bool is_em = false;
1153
1154         stack<PAR_TAG> tag_state;
1155         // parsing main loop
1156         for (pos_type i = 0; i < size(); ++i) {
1157
1158                 PAR_TAG tag_close = PAR_NONE;
1159                 list < PAR_TAG > tag_open;
1160
1161                 LyXFont const font = getFont(buf.params(), i, outerfont);
1162
1163                 if (font_old.family() != font.family()) {
1164                         switch (family_type) {
1165                         case LyXFont::SANS_FAMILY:
1166                                 tag_close |= SF;
1167                                 break;
1168                         case LyXFont::TYPEWRITER_FAMILY:
1169                                 tag_close |= TT;
1170                                 break;
1171                         default:
1172                                 break;
1173                         }
1174
1175                         family_type = font.family();
1176
1177                         switch (family_type) {
1178                         case LyXFont::SANS_FAMILY:
1179                                 tag_open.push_back(SF);
1180                                 break;
1181                         case LyXFont::TYPEWRITER_FAMILY:
1182                                 tag_open.push_back(TT);
1183                                 break;
1184                         default:
1185                                 break;
1186                         }
1187                 }
1188
1189                 if (font_old.series() != font.series()) {
1190                         switch (series_type) {
1191                         case LyXFont::BOLD_SERIES:
1192                                 tag_close |= BF;
1193                                 break;
1194                         default:
1195                                 break;
1196                         }
1197
1198                         series_type = font.series();
1199
1200                         switch (series_type) {
1201                         case LyXFont::BOLD_SERIES:
1202                                 tag_open.push_back(BF);
1203                                 break;
1204                         default:
1205                                 break;
1206                         }
1207
1208                 }
1209
1210                 if (font_old.shape() != font.shape()) {
1211                         switch (shape_type) {
1212                         case LyXFont::ITALIC_SHAPE:
1213                                 tag_close |= IT;
1214                                 break;
1215                         case LyXFont::SLANTED_SHAPE:
1216                                 tag_close |= SL;
1217                                 break;
1218                         default:
1219                                 break;
1220                         }
1221
1222                         shape_type = font.shape();
1223
1224                         switch (shape_type) {
1225                         case LyXFont::ITALIC_SHAPE:
1226                                 tag_open.push_back(IT);
1227                                 break;
1228                         case LyXFont::SLANTED_SHAPE:
1229                                 tag_open.push_back(SL);
1230                                 break;
1231                         default:
1232                                 break;
1233                         }
1234                 }
1235                 // handle <em> tag
1236                 if (font_old.emph() != font.emph()) {
1237                         if (font.emph() == LyXFont::ON) {
1238                                 tag_open.push_back(EM);
1239                                 is_em = true;
1240                         }
1241                         else if (is_em) {
1242                                 tag_close |= EM;
1243                                 is_em = false;
1244                         }
1245                 }
1246
1247                 list < PAR_TAG > temp;
1248                 while (!tag_state.empty() && tag_close) {
1249                         PAR_TAG k =  tag_state.top();
1250                         tag_state.pop();
1251                         os << "</" << tag_name(k) << '>';
1252                         if (tag_close & k)
1253                                 reset(tag_close,k);
1254                         else
1255                                 temp.push_back(k);
1256                 }
1257
1258                 for(list< PAR_TAG >::const_iterator j = temp.begin();
1259                     j != temp.end(); ++j) {
1260                         tag_state.push(*j);
1261                         os << '<' << tag_name(*j) << '>';
1262                 }
1263
1264                 for(list< PAR_TAG >::const_iterator j = tag_open.begin();
1265                     j != tag_open.end(); ++j) {
1266                         tag_state.push(*j);
1267                         os << '<' << tag_name(*j) << '>';
1268                 }
1269
1270                 char c = getChar(i);
1271
1272
1273                 if (c == Paragraph::META_INSET) {
1274                         getInset(i)->linuxdoc(buf, os, runparams);
1275                         font_old = font;
1276                         continue;
1277                 }
1278
1279                 if (style->latexparam() == "CDATA") {
1280                         // "TeX"-Mode on == > SGML-Mode on.
1281                         if (c != '\0')
1282                                 os << c;
1283                         ++char_line_count;
1284                 } else {
1285                         bool ws;
1286                         string str;
1287                         boost::tie(ws, str) = sgml::escapeChar(c);
1288                         if (ws && !isFreeSpacing()) {
1289                                 // in freespacing mode, spaces are
1290                                 // non-breaking characters
1291                                 if (desc_on) { // if char is ' ' then...
1292                                         ++char_line_count;
1293                                         sgmlLineBreak(os, char_line_count, 6);
1294                                         os << "</tag>";
1295                                         desc_on = false;
1296                                 } else  {
1297                                         sgmlLineBreak(os, char_line_count, 1);
1298                                         os << c;
1299                                 }
1300                         } else {
1301                                 os << str;
1302                                 char_line_count += str.length();
1303                         }
1304                 }
1305                 font_old = font;
1306         }
1307
1308         while (!tag_state.empty()) {
1309                 os << "</" << tag_name(tag_state.top()) << '>';
1310                 tag_state.pop();
1311         }
1312
1313         // resets description flag correctly
1314         if (desc_on) {
1315                 // <tag> not closed...
1316                 sgmlLineBreak(os, char_line_count, 6);
1317                 os << "</tag>";
1318         }
1319 }
1320
1321
1322 void Paragraph::simpleDocBookOnePar(Buffer const & buf,
1323                                     ostream & os,
1324                                     LyXFont const & outerfont,
1325                                     OutputParams const & runparams,
1326                                     lyx::depth_type depth,
1327                                     bool labelid) const
1328 {
1329         bool emph_flag = false;
1330
1331         LyXLayout_ptr const & style = layout();
1332         LyXLayout_ptr const & defaultstyle =
1333                 buf.params().getLyXTextClass().defaultLayout();
1334
1335         LyXFont font_old =
1336                 style->labeltype == LABEL_MANUAL ? style->labelfont : style->font;
1337
1338         int char_line_count = depth;
1339         bool label_closed = true;
1340         bool para_closed = true;
1341         
1342         if (style->latextype == LATEX_ITEM_ENVIRONMENT) {
1343                 string ls = "";
1344                 Counters & counters = buf.params().getLyXTextClass().counters();
1345                 if (!style->free_spacing)
1346                         os << string(depth,' ');
1347                 if (!style->labeltag().empty()) {
1348                         os << "<" << style->labeltag() << ">\n";
1349                         label_closed = false;
1350                 } else {
1351                         if (!defaultstyle->latexparam().empty()) {
1352                                 counters.step("para");
1353                                 ls = tostr(counters.value("para"));
1354                                 ls = " id=\"" 
1355                                         + subst(defaultstyle->latexparam(), "#", ls) + '"';
1356                         }
1357                         os << "<" << style->itemtag() << ">\n" 
1358                            << string(depth, ' ') << "<" 
1359                            << defaultstyle->latexname() << ls << ">\n";
1360                         para_closed = false;
1361                 }
1362         }
1363
1364         // parsing main loop
1365         for (pos_type i = 0; i < size(); ++i) {
1366                 LyXFont font = getFont(buf.params(), i, outerfont);
1367
1368                 // handle <emphasis> tag
1369                 if (font_old.emph() != font.emph()) {
1370                         if (font.emph() == LyXFont::ON) {
1371                                 if (style->latexparam() == "CDATA")
1372                                         os << "]]>";
1373                                 os << "<emphasis>";
1374                                 if (style->latexparam() == "CDATA")
1375                                         os << "<![CDATA[";
1376                                 emph_flag = true;
1377                         } else if (i) {
1378                                 if (style->latexparam() == "CDATA")
1379                                         os << "]]>";
1380                                 os << "</emphasis>";
1381                                 if (style->latexparam() == "CDATA")
1382                                         os << "<![CDATA[";
1383                                 emph_flag = false;
1384                         }
1385                 }
1386
1387
1388                 if (isInset(i)) {
1389                         InsetBase const * inset = getInset(i);
1390                         // don't print the inset in position 0 if desc_on == 3 (label)
1391                         //if (i || desc_on != 3) {
1392                         if (!labelid) {
1393                                 if (style->latexparam() == "CDATA")
1394                                         os << "]]>";
1395                                 inset->docbook(buf, os, runparams);
1396                                 if (style->latexparam() == "CDATA")
1397                                         os << "<![CDATA[";
1398                         }
1399                 } else {
1400                         char c = getChar(i);
1401                         bool ws;
1402                         string str;
1403                         boost::tie(ws, str) = sgml::escapeChar(c);
1404
1405                         if (style->pass_thru) {
1406                                 os << c;
1407                         } else if (isFreeSpacing() || c != ' ') {
1408                                         os << str;
1409                         } else if (!style->labeltag().empty() && !label_closed) {
1410                                 ++char_line_count;
1411                                 os << "\n</" << style->labeltag() << "><" 
1412                                    << style->itemtag() << "><" 
1413                                    << defaultstyle->latexname() << ">";
1414                                 label_closed = true;
1415                                 para_closed = false;
1416                         } else {
1417                                 os << ' ';
1418                         }
1419                 }
1420                 font_old = font;
1421         }
1422
1423         if (emph_flag) {
1424                 if (style->latexparam() == "CDATA")
1425                         os << "]]>";
1426                 os << "</emphasis>";
1427                 if (style->latexparam() == "CDATA")
1428                         os << "<![CDATA[";
1429         }
1430
1431         // resets description flag correctly
1432         if (!label_closed) {
1433                 // <term> not closed...
1434                 os << "</" << style->labeltag() << ">\n<" 
1435                    << style->itemtag() << "><" 
1436                    << defaultstyle->latexname() << ">&nbsp;";
1437         }
1438         if (!para_closed) {
1439                 os << "\n" << string(depth, ' ') << "</" 
1440                    << defaultstyle->latexname() << ">\n";
1441         }
1442         if (style->free_spacing)
1443                 os << '\n';
1444 }
1445
1446
1447 namespace {
1448
1449 /// return true if the char is a meta-character for an inset
1450 inline
1451 bool IsInsetChar(char c)
1452 {
1453         return (c == Paragraph::META_INSET);
1454 }
1455
1456 } // namespace anon
1457
1458
1459
1460 bool Paragraph::isHfill(pos_type pos) const
1461 {
1462         return IsInsetChar(getChar(pos))
1463                && getInset(pos)->lyxCode() == InsetBase::HFILL_CODE;
1464 }
1465
1466
1467 bool Paragraph::isInset(pos_type pos) const
1468 {
1469         return IsInsetChar(getChar(pos));
1470 }
1471
1472
1473 bool Paragraph::isNewline(pos_type pos) const
1474 {
1475         return IsInsetChar(getChar(pos))
1476                && getInset(pos)->lyxCode() == InsetBase::NEWLINE_CODE;
1477 }
1478
1479
1480 bool Paragraph::isSeparator(pos_type pos) const
1481 {
1482         return IsSeparatorChar(getChar(pos));
1483 }
1484
1485
1486 bool Paragraph::isLineSeparator(pos_type pos) const
1487 {
1488         value_type const c = getChar(pos);
1489         return IsLineSeparatorChar(c)
1490                 || (IsInsetChar(c) && getInset(pos) &&
1491                 getInset(pos)->isLineSeparator());
1492 }
1493
1494
1495 bool Paragraph::isKomma(pos_type pos) const
1496 {
1497         return IsKommaChar(getChar(pos));
1498 }
1499
1500
1501 /// Used by the spellchecker
1502 bool Paragraph::isLetter(pos_type pos) const
1503 {
1504         value_type const c = getChar(pos);
1505         if (IsLetterChar(c))
1506                 return true;
1507         if (isInset(pos))
1508                 return getInset(pos)->isLetter();
1509         // We want to pass the ' and escape chars to ispell
1510         string const extra = lyxrc.isp_esc_chars + '\'';
1511         return contains(extra, c);
1512 }
1513
1514
1515 bool Paragraph::isWord(pos_type pos) const
1516 {
1517         unsigned char const c = getChar(pos);
1518         return !(IsSeparatorChar(c)
1519                   || IsKommaChar(c)
1520                   || IsInsetChar(c));
1521 }
1522
1523
1524 Language const *
1525 Paragraph::getParLanguage(BufferParams const & bparams) const
1526 {
1527         if (!empty())
1528                 return getFirstFontSettings().language();
1529 #warning FIXME we should check the prev par as well (Lgb)
1530         return bparams.language;
1531 }
1532
1533
1534 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
1535 {
1536         return lyxrc.rtl_support
1537                 && getParLanguage(bparams)->RightToLeft()
1538                 && !(inInset() && inInset()->owner() &&
1539                      inInset()->owner()->lyxCode() == InsetBase::ERT_CODE);
1540 }
1541
1542
1543 void Paragraph::changeLanguage(BufferParams const & bparams,
1544                                Language const * from, Language const * to)
1545 {
1546         for (pos_type i = 0; i < size(); ++i) {
1547                 LyXFont font = getFontSettings(bparams, i);
1548                 if (font.language() == from) {
1549                         font.setLanguage(to);
1550                         setFont(i, font);
1551                 }
1552         }
1553 }
1554
1555
1556 bool Paragraph::isMultiLingual(BufferParams const & bparams)
1557 {
1558         Language const * doc_language = bparams.language;
1559         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1560         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
1561
1562         for (; cit != end; ++cit)
1563                 if (cit->font().language() != ignore_language &&
1564                     cit->font().language() != latex_language &&
1565                     cit->font().language() != doc_language)
1566                         return true;
1567         return false;
1568 }
1569
1570
1571 // Convert the paragraph to a string.
1572 // Used for building the table of contents
1573 string const Paragraph::asString(Buffer const & buffer,
1574                                  bool label) const
1575 {
1576         OutputParams runparams;
1577         return asString(buffer, runparams, label);
1578 }
1579
1580
1581 string const Paragraph::asString(Buffer const & buffer,
1582                                  OutputParams const & runparams,
1583                                  bool label) const
1584 {
1585 #if 0
1586         string s;
1587         if (label && !params().labelString().empty())
1588                 s += params().labelString() + ' ';
1589
1590         for (pos_type i = 0; i < size(); ++i) {
1591                 value_type c = getChar(i);
1592                 if (IsPrintable(c))
1593                         s += c;
1594                 else if (c == META_INSET &&
1595                          getInset(i)->lyxCode() == InsetBase::MATH_CODE) {
1596                         ostringstream os;
1597                         getInset(i)->plaintext(buffer, os, runparams);
1598                         s += subst(STRCONV(os.str()),'\n',' ');
1599                 }
1600         }
1601
1602         return s;
1603 #else
1604         // This should really be done by the caller and not here.
1605         string ret = asString(buffer, runparams, 0, size(), label);
1606         return subst(ret, '\n', ' ');
1607 #endif
1608 }
1609
1610
1611 string const Paragraph::asString(Buffer const & buffer,
1612                                  pos_type beg, pos_type end, bool label) const
1613 {
1614
1615         OutputParams const runparams;
1616         return asString(buffer, runparams, beg, end, label);
1617 }
1618
1619
1620 string const Paragraph::asString(Buffer const & buffer,
1621                                  OutputParams const & runparams,
1622                                  pos_type beg, pos_type end, bool label) const
1623 {
1624         ostringstream os;
1625
1626         if (beg == 0 && label && !params().labelString().empty())
1627                 os << params().labelString() << ' ';
1628
1629         for (pos_type i = beg; i < end; ++i) {
1630                 value_type const c = getUChar(buffer.params(), i);
1631                 if (IsPrintable(c))
1632                         os << c;
1633                 else if (c == META_INSET)
1634                         getInset(i)->plaintext(buffer, os, runparams);
1635         }
1636
1637         return os.str();
1638 }
1639
1640
1641 void Paragraph::setInsetOwner(UpdatableInset * inset)
1642 {
1643         pimpl_->inset_owner = inset;
1644         InsetList::iterator it = insetlist.begin();
1645         InsetList::iterator end = insetlist.end();
1646         for (; it != end; ++it)
1647                 if (it->inset)
1648                         it->inset->setOwner(inset);
1649 }
1650
1651
1652 void Paragraph::setContentsFromPar(Paragraph const & par)
1653 {
1654         pimpl_->setContentsFromPar(par);
1655 }
1656
1657
1658 void Paragraph::trackChanges(Change::Type type)
1659 {
1660         pimpl_->trackChanges(type);
1661 }
1662
1663
1664 void Paragraph::untrackChanges()
1665 {
1666         pimpl_->untrackChanges();
1667 }
1668
1669
1670 void Paragraph::cleanChanges()
1671 {
1672         pimpl_->cleanChanges();
1673 }
1674
1675
1676 Change::Type Paragraph::lookupChange(lyx::pos_type pos) const
1677 {
1678         BOOST_ASSERT(!size() || pos < size());
1679         return pimpl_->lookupChange(pos);
1680 }
1681
1682
1683 Change const Paragraph::lookupChangeFull(lyx::pos_type pos) const
1684 {
1685         BOOST_ASSERT(!size() || pos < size());
1686         return pimpl_->lookupChangeFull(pos);
1687 }
1688
1689
1690 bool Paragraph::isChanged(pos_type start, pos_type end) const
1691 {
1692         return pimpl_->isChanged(start, end);
1693 }
1694
1695
1696 bool Paragraph::isChangeEdited(pos_type start, pos_type end) const
1697 {
1698         return pimpl_->isChangeEdited(start, end);
1699 }
1700
1701
1702 void Paragraph::setChange(lyx::pos_type pos, Change::Type type)
1703 {
1704         pimpl_->setChange(pos, type);
1705
1706 }
1707
1708
1709 void Paragraph::markErased()
1710 {
1711         pimpl_->markErased();
1712 }
1713
1714
1715 void Paragraph::acceptChange(pos_type start, pos_type end)
1716 {
1717         return pimpl_->acceptChange(start, end);
1718 }
1719
1720
1721 void Paragraph::rejectChange(pos_type start, pos_type end)
1722 {
1723         return pimpl_->rejectChange(start, end);
1724 }
1725
1726
1727 Paragraph::value_type Paragraph::getChar(pos_type pos) const
1728 {
1729         // This is in the critical path!
1730         pos_type const siz = text_.size();
1731
1732         BOOST_ASSERT(0 <= pos);
1733         BOOST_ASSERT(pos <= siz);
1734
1735         if (pos == siz) {
1736                 lyxerr << "getChar() on pos " << pos << " in par id "
1737                        << id() << " of size " << siz
1738                        << "  is a bit silly !" << endl;
1739                 BOOST_ASSERT(false);
1740                 return '\0';
1741         }
1742
1743         return text_[pos];
1744 }
1745
1746
1747 int Paragraph::id() const
1748 {
1749         return pimpl_->id_;
1750 }
1751
1752
1753 LyXLayout_ptr const & Paragraph::layout() const
1754 {
1755 /*
1756         InsetBase * inset = inInset();
1757         if (inset && inset->lyxCode() == InsetBase::ENVIRONMENT_CODE)
1758                 return static_cast<InsetEnvironment*>(inset)->layout();
1759 */
1760         return layout_;
1761 }
1762
1763
1764 void Paragraph::layout(LyXLayout_ptr const & new_layout)
1765 {
1766         layout_ = new_layout;
1767 }
1768
1769
1770 UpdatableInset * Paragraph::inInset() const
1771 {
1772         return pimpl_->inset_owner;
1773 }
1774
1775
1776 void Paragraph::clearContents()
1777 {
1778         text_.clear();
1779 }
1780
1781
1782 void Paragraph::setChar(pos_type pos, value_type c)
1783 {
1784         text_[pos] = c;
1785 }
1786
1787
1788 ParagraphParameters & Paragraph::params()
1789 {
1790         return pimpl_->params;
1791 }
1792
1793
1794 ParagraphParameters const & Paragraph::params() const
1795 {
1796         return pimpl_->params;
1797 }
1798
1799
1800 bool Paragraph::isFreeSpacing() const
1801 {
1802         if (layout()->free_spacing)
1803                 return true;
1804
1805         // for now we just need this, later should we need this in some
1806         // other way we can always add a function to InsetBase too.
1807         if (pimpl_->inset_owner && pimpl_->inset_owner->owner())
1808                 return pimpl_->inset_owner->owner()->lyxCode() == InsetBase::ERT_CODE;
1809         return false;
1810 }
1811
1812
1813 bool Paragraph::allowEmpty() const
1814 {
1815         if (layout()->keepempty)
1816                 return true;
1817         if (pimpl_->inset_owner && pimpl_->inset_owner->owner())
1818                 return pimpl_->inset_owner->owner()->lyxCode() == InsetBase::ERT_CODE;
1819         return false;
1820 }
1821
1822
1823 RowList::iterator Paragraph::getRow(pos_type pos)
1824 {
1825         RowList::iterator rit = rows.end();
1826         RowList::iterator const begin = rows.begin();
1827
1828         for (--rit; rit != begin && rit->pos() > pos; --rit)
1829                 ;
1830
1831         return rit;
1832 }
1833
1834
1835 unsigned char Paragraph::transformChar(unsigned char c, pos_type pos) const
1836 {
1837         if (!Encodings::is_arabic(c))
1838                 if (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 && IsDigit(c))
1839                         return c + (0xb0 - '0');
1840                 else
1841                         return c;
1842
1843         unsigned char const prev_char = pos > 0 ? getChar(pos - 1) : ' ';
1844         unsigned char next_char = ' ';
1845
1846         for (pos_type i = pos + 1, end = size(); i < end; ++i) {
1847                 unsigned char const par_char = getChar(i);
1848                 if (!Encodings::IsComposeChar_arabic(par_char)) {
1849                         next_char = par_char;
1850                         break;
1851                 }
1852         }
1853
1854         if (Encodings::is_arabic(next_char)) {
1855                 if (Encodings::is_arabic(prev_char) &&
1856                         !Encodings::is_arabic_special(prev_char))
1857                         return Encodings::TransformChar(c, Encodings::FORM_MEDIAL);
1858                 else
1859                         return Encodings::TransformChar(c, Encodings::FORM_INITIAL);
1860         } else {
1861                 if (Encodings::is_arabic(prev_char) &&
1862                         !Encodings::is_arabic_special(prev_char))
1863                         return Encodings::TransformChar(c, Encodings::FORM_FINAL);
1864                 else
1865                         return Encodings::TransformChar(c, Encodings::FORM_ISOLATED);
1866         }
1867 }