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