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