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