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