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