]> git.lyx.org Git - features.git/blob - src/Paragraph.cpp
Correctly generate latex for font/language/encoding switches inside and around
[features.git] / src / Paragraph.cpp
1 /**
2  * \file Paragraph.cpp
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
22 #include "Buffer.h"
23 #include "BufferParams.h"
24 #include "Counters.h"
25 #include "Encoding.h"
26 #include "debug.h"
27 #include "gettext.h"
28 #include "Language.h"
29 #include "LaTeXFeatures.h"
30 #include "Color.h"
31 #include "Length.h"
32 #include "Font.h"
33 #include "LyXRC.h"
34 #include "Row.h"
35 #include "Messages.h"
36 #include "OutputParams.h"
37 #include "output_latex.h"
38 #include "paragraph_funcs.h"
39 #include "ParagraphParameters.h"
40 #include "rowpainter.h"
41 #include "sgml.h"
42 #include "TexRow.h"
43 #include "VSpace.h"
44
45 #include "frontends/alert.h"
46 #include "frontends/FontMetrics.h"
47
48 #include "insets/InsetBibitem.h"
49 #include "insets/InsetOptArg.h"
50
51 #include "support/lstrings.h"
52 #include "support/textutils.h"
53 #include "support/convert.h"
54 #include "support/unicode.h"
55
56 #include <boost/bind.hpp>
57 #include <boost/next_prior.hpp>
58
59 #include <algorithm>
60 #include <sstream>
61
62 using std::distance;
63 using std::endl;
64 using std::string;
65 using std::ostream;
66
67 namespace lyx {
68
69 using support::contains;
70 using support::suffixIs;
71 using support::rsplit;
72
73
74 /////////////////////////////////////////////////////////////////////
75 //
76 // Paragraph::Pimpl
77 //
78 /////////////////////////////////////////////////////////////////////
79
80 class Encoding;
81 class Layout;
82
83
84 class Paragraph::Pimpl {
85 public:
86         ///
87         Pimpl(Paragraph * owner);
88         /// "Copy constructor"
89         Pimpl(Pimpl const &, Paragraph * owner);
90
91         //
92         // Change tracking
93         //
94         /// look up change at given pos
95         Change const & lookupChange(pos_type pos) const;
96         /// is there a change within the given range ?
97         bool isChanged(pos_type start, pos_type end) const;
98         /// will the paragraph be physically merged with the next
99         /// one if the imaginary end-of-par character is logically deleted?
100         bool isMergedOnEndOfParDeletion(bool trackChanges) const;
101         /// set change for the entire par
102         void setChange(Change const & change);
103         /// set change at given pos
104         void setChange(pos_type pos, Change const & change);
105         /// accept changes within the given range
106         void acceptChanges(BufferParams const & bparams, pos_type start, pos_type end);
107         /// reject changes within the given range
108         void rejectChanges(BufferParams const & bparams, pos_type start, pos_type end);
109
110         ///
111         value_type getChar(pos_type pos) const;
112         ///
113         void insertChar(pos_type pos, value_type c, Change const & change);
114         ///
115         void insertInset(pos_type pos, Inset * inset, Change const & change);
116         /// (logically) erase the char at pos; return true if it was actually erased
117         bool eraseChar(pos_type pos, bool trackChanges);
118         /// (logically) erase the given range; return the number of chars actually erased
119         int eraseChars(pos_type start, pos_type end, bool trackChanges);
120         ///
121         Inset * inset_owner;
122
123         /** A font entry covers a range of positions. Notice that the
124             entries in the list are inserted in random order.
125             I don't think it's worth the effort to implement a more effective
126             datastructure, because the number of different fonts in a paragraph
127             is limited. (Asger)
128             Nevertheless, I decided to store fontlist using a sorted vector:
129             fontlist = { {pos_1,font_1} , {pos_2,font_2} , ... } where
130             pos_1 < pos_2 < ..., font_{i-1} != font_i for all i,
131             and font_i covers the chars in positions pos_{i-1}+1,...,pos_i
132             (font_1 covers the chars 0,...,pos_1) (Dekel)
133         */
134         class FontTable  {
135         public:
136                 ///
137                 FontTable(pos_type p, Font const & f)
138                         : pos_(p), font_(f)
139                 {}
140                 ///
141                 pos_type pos() const { return pos_; }
142                 ///
143                 void pos(pos_type p) { pos_ = p; }
144                 ///
145                 Font const & font() const { return font_; }
146                 ///
147                 void font(Font const & f) { font_ = f;}
148         private:
149                 /// End position of paragraph this font attribute covers
150                 pos_type pos_;
151                 /** Font. Interpretation of the font values:
152                     If a value is Font::INHERIT_*, it means that the font
153                     attribute is inherited from either the layout of this
154                     paragraph or, in the case of nested paragraphs, from the
155                     layout in the environment one level up until completely
156                     resolved.
157                     The values Font::IGNORE_* and Font::TOGGLE are NOT
158                     allowed in these font tables.
159                 */
160                 Font font_;
161         };
162         ///
163         friend class matchFT;
164         ///
165         class matchFT {
166         public:
167                 /// used by lower_bound and upper_bound
168                 int operator()(FontTable const & a, FontTable const & b) const {
169                         return a.pos() < b.pos();
170                 }
171         };
172
173         ///
174         typedef std::vector<FontTable> FontList;
175         ///
176         FontList fontlist;
177
178         /// Output the surrogate pair formed by \p c and \p next to \p os.
179         /// \return the number of characters written.
180         int latexSurrogatePair(odocstream & os, value_type c, value_type next,
181                                Encoding const &);
182         /// Output a space in appropriate formatting (or a surrogate pair
183         /// if the next character is a combining character).
184         /// \return whether a surrogate pair was output.
185         bool simpleTeXBlanks(Encoding const &,
186                              odocstream &, TexRow & texrow,
187                              pos_type & i,
188                              unsigned int & column,
189                              Font const & font,
190                              Layout const & style);
191         ///
192         void simpleTeXSpecialChars(Buffer const &, BufferParams const &,
193                                    odocstream &,
194                                    TexRow & texrow, OutputParams &,
195                                    Font & running_font,
196                                    Font & basefont,
197                                    Font const & outerfont,
198                                    bool & open_font,
199                                    Change & running_change,
200                                    Layout const & style,
201                                    pos_type & i,
202                                    unsigned int & column, value_type const c);
203
204         ///
205         void validate(LaTeXFeatures & features,
206                       Layout const & layout) const;
207
208         ///
209         unsigned int id_;
210         ///
211         static unsigned int paragraph_id;
212         ///
213         ParagraphParameters params;
214
215 //private:
216         ///
217         pos_type size() const { return owner_->size(); }
218         /// match a string against a particular point in the paragraph
219         bool isTextAt(std::string const & str, pos_type pos) const;
220
221         /// for recording and looking up changes
222         Changes changes_;
223
224         /// Who owns us?
225         Paragraph * owner_;
226 };
227
228
229
230
231 using std::endl;
232 using std::upper_bound;
233 using std::lower_bound;
234 using std::string;
235
236
237 // Initialization of the counter for the paragraph id's,
238 unsigned int Paragraph::Pimpl::paragraph_id = 0;
239
240 namespace {
241
242 struct special_phrase {
243         string phrase;
244         docstring macro;
245         bool builtin;
246 };
247
248 special_phrase const special_phrases[] = {
249         { "LyX", from_ascii("\\LyX{}"), false },
250         { "TeX", from_ascii("\\TeX{}"), true },
251         { "LaTeX2e", from_ascii("\\LaTeXe{}"), true },
252         { "LaTeX", from_ascii("\\LaTeX{}"), true },
253 };
254
255 size_t const phrases_nr = sizeof(special_phrases)/sizeof(special_phrase);
256
257 } // namespace anon
258
259
260 Paragraph::Pimpl::Pimpl(Paragraph * owner)
261         : owner_(owner)
262 {
263         inset_owner = 0;
264         id_ = paragraph_id++;
265 }
266
267
268 Paragraph::Pimpl::Pimpl(Pimpl const & p, Paragraph * owner)
269         : params(p.params), changes_(p.changes_), owner_(owner)
270 {
271         inset_owner = p.inset_owner;
272         fontlist = p.fontlist;
273         id_ = paragraph_id++;
274 }
275
276
277 bool Paragraph::Pimpl::isChanged(pos_type start, pos_type end) const
278 {
279         BOOST_ASSERT(start >= 0 && start <= size());
280         BOOST_ASSERT(end > start && end <= size() + 1);
281
282         return changes_.isChanged(start, end);
283 }
284
285
286 bool Paragraph::Pimpl::isMergedOnEndOfParDeletion(bool trackChanges) const {
287         // keep the logic here in sync with the logic of eraseChars()
288
289         if (!trackChanges) {
290                 return true;
291         }
292
293         Change change = changes_.lookup(size());
294
295         return change.type == Change::INSERTED && change.author == 0;
296 }
297
298
299 void Paragraph::Pimpl::setChange(Change const & change)
300 {
301         // beware of the imaginary end-of-par character!
302         changes_.set(change, 0, size() + 1);
303
304         /*
305          * Propagate the change recursively - but not in case of DELETED!
306          *
307          * Imagine that your co-author makes changes in an existing inset. He
308          * sends your document to you and you come to the conclusion that the
309          * inset should go completely. If you erase it, LyX must not delete all
310          * text within the inset. Otherwise, the change tracked insertions of
311          * your co-author get lost and there is no way to restore them later.
312          *
313          * Conclusion: An inset's content should remain untouched if you delete it
314          */
315
316         if (change.type != Change::DELETED) {
317                 for (pos_type pos = 0; pos < size(); ++pos) {
318                         if (owner_->isInset(pos)) {
319                                 owner_->getInset(pos)->setChange(change);
320                         }
321                 }
322         }
323 }
324
325
326 void Paragraph::Pimpl::setChange(pos_type pos, Change const & change)
327 {
328         BOOST_ASSERT(pos >= 0 && pos <= size());
329
330         changes_.set(change, pos);
331
332         // see comment in setChange(Change const &) above
333
334         if (change.type != Change::DELETED &&
335             pos < size() && owner_->isInset(pos)) {
336                 owner_->getInset(pos)->setChange(change);
337         }
338 }
339
340
341 Change const & Paragraph::Pimpl::lookupChange(pos_type pos) const
342 {
343         BOOST_ASSERT(pos >= 0 && pos <= size());
344
345         return changes_.lookup(pos);
346 }
347
348
349 void Paragraph::Pimpl::acceptChanges(BufferParams const & bparams, pos_type start, pos_type end)
350 {
351         BOOST_ASSERT(start >= 0 && start <= size());
352         BOOST_ASSERT(end > start && end <= size() + 1);
353
354         for (pos_type pos = start; pos < end; ++pos) {
355                 switch (lookupChange(pos).type) {
356                         case Change::UNCHANGED:
357                                 // accept changes in nested inset
358                                 if (pos < size() && owner_->isInset(pos)) {
359                                         owner_->getInset(pos)->acceptChanges(bparams);
360                                 }
361
362                                 break;
363
364                         case Change::INSERTED:
365                                 changes_.set(Change(Change::UNCHANGED), pos);
366                                 // also accept changes in nested inset
367                                 if (pos < size() && owner_->isInset(pos)) {
368                                         owner_->getInset(pos)->acceptChanges(bparams);
369                                 }
370                                 break;
371
372                         case Change::DELETED:
373                                 // Suppress access to non-existent
374                                 // "end-of-paragraph char"
375                                 if (pos < size()) {
376                                         eraseChar(pos, false);
377                                         --end;
378                                         --pos;
379                                 }
380                                 break;
381                 }
382
383         }
384 }
385
386
387 void Paragraph::Pimpl::rejectChanges(BufferParams const & bparams, pos_type start, pos_type end)
388 {
389         BOOST_ASSERT(start >= 0 && start <= size());
390         BOOST_ASSERT(end > start && end <= size() + 1);
391
392         for (pos_type pos = start; pos < end; ++pos) {
393                 switch (lookupChange(pos).type) {
394                         case Change::UNCHANGED:
395                                 // reject changes in nested inset
396                                 if (pos < size() && owner_->isInset(pos)) {
397                                         owner_->getInset(pos)->rejectChanges(bparams);
398                                 }
399                                 break;
400
401                         case Change::INSERTED:
402                                 // Suppress access to non-existent
403                                 // "end-of-paragraph char"
404                                 if (pos < size()) {
405                                         eraseChar(pos, false);
406                                         --end;
407                                         --pos;
408                                 }
409                                 break;
410
411                         case Change::DELETED:
412                                 changes_.set(Change(Change::UNCHANGED), pos);
413
414                                 // Do NOT reject changes within a deleted inset!
415                                 // There may be insertions of a co-author inside of it!
416
417                                 break;
418                 }
419         }
420 }
421
422
423 Paragraph::value_type Paragraph::Pimpl::getChar(pos_type pos) const
424 {
425         BOOST_ASSERT(pos >= 0 && pos <= size());
426
427         return owner_->getChar(pos);
428 }
429
430
431 void Paragraph::Pimpl::insertChar(pos_type pos, value_type c, Change const & change)
432 {
433         BOOST_ASSERT(pos >= 0 && pos <= size());
434
435         // track change
436         changes_.insert(change, pos);
437
438         // This is actually very common when parsing buffers (and
439         // maybe inserting ascii text)
440         if (pos == size()) {
441                 // when appending characters, no need to update tables
442                 owner_->text_.push_back(c);
443                 return;
444         }
445
446         owner_->text_.insert(owner_->text_.begin() + pos, c);
447
448         // Update the font table.
449         FontTable search_font(pos, Font());
450         for (FontList::iterator it
451               = lower_bound(fontlist.begin(), fontlist.end(), search_font, matchFT());
452              it != fontlist.end(); ++it)
453         {
454                 it->pos(it->pos() + 1);
455         }
456
457         // Update the insets
458         owner_->insetlist.increasePosAfterPos(pos);
459 }
460
461
462 void Paragraph::Pimpl::insertInset(pos_type pos, Inset * inset,
463                                    Change const & change)
464 {
465         BOOST_ASSERT(inset);
466         BOOST_ASSERT(pos >= 0 && pos <= size());
467
468         insertChar(pos, META_INSET, change);
469         BOOST_ASSERT(owner_->text_[pos] == META_INSET);
470
471         // Add a new entry in the insetlist.
472         owner_->insetlist.insert(inset, pos);
473 }
474
475
476 bool Paragraph::Pimpl::eraseChar(pos_type pos, bool trackChanges)
477 {
478         BOOST_ASSERT(pos >= 0 && pos <= size());
479
480         // keep the logic here in sync with the logic of isMergedOnEndOfParDeletion()
481
482         if (trackChanges) {
483                 Change change = changes_.lookup(pos);
484
485                 // set the character to DELETED if
486                 //  a) it was previously unchanged or
487                 //  b) it was inserted by a co-author
488
489                 if (change.type == Change::UNCHANGED ||
490                     (change.type == Change::INSERTED && change.author != 0)) {
491                         setChange(pos, Change(Change::DELETED));
492                         return false;
493                 }
494
495                 if (change.type == Change::DELETED)
496                         return false;
497         }
498
499         // Don't physically access the imaginary end-of-paragraph character.
500         // eraseChar() can only mark it as DELETED. A physical deletion of
501         // end-of-par must be handled externally.
502         if (pos == size()) {
503                 return false;
504         }
505
506         // track change
507         changes_.erase(pos);
508
509         // if it is an inset, delete the inset entry
510         if (owner_->text_[pos] == Paragraph::META_INSET) {
511                 owner_->insetlist.erase(pos);
512         }
513
514         owner_->text_.erase(owner_->text_.begin() + pos);
515
516         // Erase entries in the tables.
517         FontTable search_font(pos, Font());
518
519         FontList::iterator it =
520                 lower_bound(fontlist.begin(),
521                             fontlist.end(),
522                             search_font, matchFT());
523         if (it != fontlist.end() && it->pos() == pos &&
524             (pos == 0 ||
525              (it != fontlist.begin()
526               && boost::prior(it)->pos() == pos - 1))) {
527                 // If it is a multi-character font
528                 // entry, we just make it smaller
529                 // (see update below), otherwise we
530                 // should delete it.
531                 unsigned int const i = it - fontlist.begin();
532                 fontlist.erase(fontlist.begin() + i);
533                 it = fontlist.begin() + i;
534                 if (i > 0 && i < fontlist.size() &&
535                     fontlist[i - 1].font() == fontlist[i].font()) {
536                         fontlist.erase(fontlist.begin() + i - 1);
537                         it = fontlist.begin() + i - 1;
538                 }
539         }
540
541         // Update all other entries
542         FontList::iterator fend = fontlist.end();
543         for (; it != fend; ++it)
544                 it->pos(it->pos() - 1);
545
546         // Update the insetlist
547         owner_->insetlist.decreasePosAfterPos(pos);
548
549         return true;
550 }
551
552
553 int Paragraph::Pimpl::eraseChars(pos_type start, pos_type end, bool trackChanges)
554 {
555         BOOST_ASSERT(start >= 0 && start <= size());
556         BOOST_ASSERT(end >= start && end <= size() + 1);
557
558         pos_type i = start;
559         for (pos_type count = end - start; count; --count) {
560                 if (!eraseChar(i, trackChanges))
561                         ++i;
562         }
563         return end - i;
564 }
565
566
567 int Paragraph::Pimpl::latexSurrogatePair(odocstream & os, value_type c,
568                 value_type next, Encoding const & encoding)
569 {
570         // Writing next here may circumvent a possible font change between
571         // c and next. Since next is only output if it forms a surrogate pair
572         // with c we can ignore this:
573         // A font change inside a surrogate pair does not make sense and is
574         // hopefully impossible to input.
575         // FIXME: change tracking
576         // Is this correct WRT change tracking?
577         docstring const latex1 = encoding.latexChar(next);
578         docstring const latex2 = encoding.latexChar(c);
579         os << latex1 << '{' << latex2 << '}';
580         return latex1.length() + latex2.length() + 2;
581 }
582
583
584 bool Paragraph::Pimpl::simpleTeXBlanks(Encoding const & encoding,
585                                        odocstream & os, TexRow & texrow,
586                                        pos_type & i,
587                                        unsigned int & column,
588                                        Font const & font,
589                                        Layout const & style)
590 {
591         if (style.pass_thru)
592                 return false;
593
594         if (i < size() - 1) {
595                 char_type next = getChar(i + 1);
596                 if (Encodings::isCombiningChar(next)) {
597                         // This space has an accent, so we must always output it.
598                         column += latexSurrogatePair(os, ' ', next, encoding) - 1;
599                         ++i;
600                         return true;
601                 }
602         }
603
604         if (lyxrc.plaintext_linelen > 0
605             && column > lyxrc.plaintext_linelen
606             && i
607             && getChar(i - 1) != ' '
608             && (i < size() - 1)
609             // same in FreeSpacing mode
610             && !owner_->isFreeSpacing()
611             // In typewriter mode, we want to avoid
612             // ! . ? : at the end of a line
613             && !(font.family() == Font::TYPEWRITER_FAMILY
614                  && (getChar(i - 1) == '.'
615                      || getChar(i - 1) == '?'
616                      || getChar(i - 1) == ':'
617                      || getChar(i - 1) == '!'))) {
618                 os << '\n';
619                 texrow.newline();
620                 texrow.start(owner_->id(), i + 1);
621                 column = 0;
622         } else if (style.free_spacing) {
623                 os << '~';
624         } else {
625                 os << ' ';
626         }
627         return false;
628 }
629
630
631 bool Paragraph::Pimpl::isTextAt(string const & str, pos_type pos) const
632 {
633         pos_type const len = str.length();
634
635         // is the paragraph large enough?
636         if (pos + len > size())
637                 return false;
638
639         // does the wanted text start at point?
640         for (string::size_type i = 0; i < str.length(); ++i) {
641                 // Caution: direct comparison of characters works only
642                 // because str is pure ASCII.
643                 if (str[i] != owner_->text_[pos + i])
644                         return false;
645         }
646
647         // is there a font change in middle of the word?
648         FontList::const_iterator cit = fontlist.begin();
649         FontList::const_iterator end = fontlist.end();
650         for (; cit != end; ++cit) {
651                 if (cit->pos() >= pos)
652                         break;
653         }
654         if (cit != end && pos + len - 1 > cit->pos())
655                 return false;
656
657         return true;
658 }
659
660
661 void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const & buf,
662                                              BufferParams const & bparams,
663                                              odocstream & os,
664                                              TexRow & texrow,
665                                              OutputParams & runparams,
666                                              Font & running_font,
667                                              Font & basefont,
668                                              Font const & outerfont,
669                                              bool & open_font,
670                                              Change & running_change,
671                                              Layout const & style,
672                                              pos_type & i,
673                                              unsigned int & column,
674                                              value_type const c)
675 {
676         if (style.pass_thru) {
677                 if (c != Paragraph::META_INSET) {
678                         if (c != '\0')
679                                 // FIXME UNICODE: This can fail if c cannot
680                                 // be encoded in the current encoding.
681                                 os.put(c);
682                 } else
683                         owner_->getInset(i)->plaintext(buf, os, runparams);
684                 return;
685         }
686
687         // Two major modes:  LaTeX or plain
688         // Handle here those cases common to both modes
689         // and then split to handle the two modes separately.
690         switch (c) {
691         case Paragraph::META_INSET: {
692                 Inset * inset = owner_->getInset(i);
693
694                 // FIXME: remove this check
695                 if (!inset)
696                         break;
697
698                 // FIXME: move this to InsetNewline::latex
699                 if (inset->lyxCode() == Inset::NEWLINE_CODE) {
700                         // newlines are handled differently here than
701                         // the default in simpleTeXSpecialChars().
702                         if (!style.newline_allowed) {
703                                 os << '\n';
704                         } else {
705                                 if (open_font) {
706                                         column += running_font.latexWriteEndChanges(
707                                                 os, bparams, runparams,
708                                                 basefont, basefont);
709                                         open_font = false;
710                                 }
711
712                                 if (running_font.family() == Font::TYPEWRITER_FAMILY)
713                                         os << '~';
714
715                                 basefont = owner_->getLayoutFont(bparams, outerfont);
716                                 running_font = basefont;
717
718                                 if (runparams.moving_arg)
719                                         os << "\\protect ";
720
721                                 os << "\\\\\n";
722                         }
723                         texrow.newline();
724                         texrow.start(owner_->id(), i + 1);
725                         column = 0;
726                         break;
727                 }
728
729                 if (inset->canTrackChanges()) {
730                         column += Changes::latexMarkChange(os, bparams, running_change,
731                                 Change(Change::UNCHANGED));
732                         running_change = Change(Change::UNCHANGED);
733                 }
734
735                 bool close = false;
736                 odocstream::pos_type const len = os.tellp();
737
738                 if ((inset->lyxCode() == Inset::GRAPHICS_CODE
739                      || inset->lyxCode() == Inset::MATH_CODE
740                      || inset->lyxCode() == Inset::URL_CODE)
741                     && running_font.isRightToLeft()) {
742                         if (running_font.language()->lang() == "farsi")
743                                 os << "\\beginL{}";
744                         else
745                                 os << "\\L{";
746                         close = true;
747                 }
748
749 #ifdef WITH_WARNINGS
750 #warning Bug: we can have an empty font change here!
751 // if there has just been a font change, we are going to close it
752 // right now, which means stupid latex code like \textsf{}. AFAIK,
753 // this does not harm dvi output. A minor bug, thus (JMarc)
754 #endif
755                 // Some insets cannot be inside a font change command.
756                 // However, even such insets *can* be placed in \L or \R
757                 // or their equivalents (for RTL language switches), so we don't
758                 // close the language in those cases.
759                 // ArabTeX, though, cannot handle this special behavior, it seems.
760                 bool arabtex = basefont.language()->lang() == "arabic_arabtex" ||
761                                            running_font.language()->lang() == "arabic_arabtex";
762                 if (open_font && inset->noFontChange()) {
763                         bool closeLanguage = arabtex ||
764                                 basefont.isRightToLeft() == running_font.isRightToLeft();
765                         unsigned int count = running_font.latexWriteEndChanges(
766                                         os, bparams, runparams,
767                                                 basefont, basefont, closeLanguage);
768                         column += count;
769                         // if any font properties were closed, update the running_font, 
770                         // making sure, however, to leave the language as it was
771                         if (count > 0) {
772                                 // FIXME: probably a better way to keep track of the old 
773                                 // language, than copying the entire font?
774                                 Font const copy_font(running_font);
775                                 basefont = owner_->getLayoutFont(bparams, outerfont);
776                                 running_font = basefont;
777                                 if (!closeLanguage)
778                                         running_font.setLanguage(copy_font.language());
779                                 // leave font open if language is still open
780                                 open_font = (running_font.language() == basefont.language());
781                                 if (closeLanguage)
782                                         runparams.local_font = &basefont;
783                         }
784                 }
785
786                 int tmp = inset->latex(buf, os, runparams);
787
788                 if (close) {
789                         if (running_font.language()->lang() == "farsi")
790                                 os << "\\endL{}";
791                         else
792                                 os << '}';
793                 }
794
795                 if (tmp) {
796                         for (int j = 0; j < tmp; ++j) {
797                                 texrow.newline();
798                         }
799                         texrow.start(owner_->id(), i + 1);
800                         column = 0;
801                 } else {
802                         column += os.tellp() - len;
803                 }
804         }
805         break;
806
807         default:
808                 // And now for the special cases within each mode
809
810                 switch (c) {
811                 case '\\':
812                         os << "\\textbackslash{}";
813                         column += 15;
814                         break;
815
816                 case '|': case '<': case '>':
817                         // In T1 encoding, these characters exist
818                         if (lyxrc.fontenc == "T1") {
819                                 os.put(c);
820                                 //... but we should avoid ligatures
821                                 if ((c == '>' || c == '<')
822                                     && i <= size() - 2
823                                     && getChar(i + 1) == c) {
824                                         //os << "\\textcompwordmark{}";
825                                         //column += 19;
826                                         // Jean-Marc, have a look at
827                                         // this. I think this works
828                                         // equally well:
829                                         os << "\\,{}";
830                                         // Lgb
831                                         column += 3;
832                                 }
833                                 break;
834                         }
835                         // Typewriter font also has them
836                         if (running_font.family() == Font::TYPEWRITER_FAMILY) {
837                                 os.put(c);
838                                 break;
839                         }
840                         // Otherwise, we use what LaTeX
841                         // provides us.
842                         switch (c) {
843                         case '<':
844                                 os << "\\textless{}";
845                                 column += 10;
846                                 break;
847                         case '>':
848                                 os << "\\textgreater{}";
849                                 column += 13;
850                                 break;
851                         case '|':
852                                 os << "\\textbar{}";
853                                 column += 9;
854                                 break;
855                         }
856                         break;
857
858                 case '-': // "--" in Typewriter mode -> "-{}-"
859                         if (i <= size() - 2 &&
860                             getChar(i + 1) == '-' &&
861                             running_font.family() == Font::TYPEWRITER_FAMILY) {
862                                 os << "-{}";
863                                 column += 2;
864                         } else {
865                                 os << '-';
866                         }
867                         break;
868
869                 case '\"':
870                         os << "\\char`\\\"{}";
871                         column += 9;
872                         break;
873
874                 case '$': case '&':
875                 case '%': case '#': case '{':
876                 case '}': case '_':
877                         os << '\\';
878                         os.put(c);
879                         column += 1;
880                         break;
881
882                 case '~':
883                         os << "\\textasciitilde{}";
884                         column += 16;
885                         break;
886
887                 case '^':
888                         os << "\\textasciicircum{}";
889                         column += 17;
890                         break;
891
892                 case '*': case '[':
893                         // avoid being mistaken for optional arguments
894                         os << '{';
895                         os.put(c);
896                         os << '}';
897                         column += 2;
898                         break;
899
900                 case ' ':
901                         // Blanks are printed before font switching.
902                         // Sure? I am not! (try nice-latex)
903                         // I am sure it's correct. LyX might be smarter
904                         // in the future, but for now, nothing wrong is
905                         // written. (Asger)
906                         break;
907
908                 default:
909
910                         // I assume this is hack treating typewriter as verbatim
911                         // FIXME UNICODE: This can fail if c cannot be encoded
912                         // in the current encoding.
913                         if (running_font.family() == Font::TYPEWRITER_FAMILY) {
914                                 if (c != '\0') {
915                                         os.put(c);
916                                 }
917                                 break;
918                         }
919
920                         // LyX, LaTeX etc.
921
922                         // FIXME: if we have "LaTeX" with a font
923                         // change in the middle (before the 'T', then
924                         // the "TeX" part is still special cased.
925                         // Really we should only operate this on
926                         // "words" for some definition of word
927
928                         size_t pnr = 0;
929
930                         for (; pnr < phrases_nr; ++pnr) {
931                                 if (isTextAt(special_phrases[pnr].phrase, i)) {
932                                         os << special_phrases[pnr].macro;
933                                         i += special_phrases[pnr].phrase.length() - 1;
934                                         column += special_phrases[pnr].macro.length() - 1;
935                                         break;
936                                 }
937                         }
938
939                         if (pnr == phrases_nr && c != '\0') {
940                                 Encoding const & encoding = *(runparams.encoding);
941                                 if (i < size() - 1) {
942                                         char_type next = getChar(i + 1);
943                                         if (Encodings::isCombiningChar(next)) {
944                                                 column += latexSurrogatePair(os, c, next, encoding) - 1;
945                                                 ++i;
946                                                 break;
947                                         }
948                                 }
949                                 docstring const latex = encoding.latexChar(c);
950                                 if (latex.length() > 1 &&
951                                     latex[latex.length() - 1] != '}') {
952                                         // Prevent eating of a following
953                                         // space or command corruption by
954                                         // following characters
955                                         column += latex.length() + 1;
956                                         os << latex << "{}";
957                                 } else {
958                                         column += latex.length() - 1;
959                                         os << latex;
960                                 }
961                         }
962                         break;
963                 }
964         }
965 }
966
967
968 void Paragraph::Pimpl::validate(LaTeXFeatures & features,
969                                 Layout const & layout) const
970 {
971         BufferParams const & bparams = features.bufferParams();
972
973         // check the params.
974         if (!params.spacing().isDefault())
975                 features.require("setspace");
976
977         // then the layouts
978         features.useLayout(layout.name());
979
980         // then the fonts
981         Language const * doc_language = bparams.language;
982
983         FontList::const_iterator fcit = fontlist.begin();
984         FontList::const_iterator fend = fontlist.end();
985         for (; fcit != fend; ++fcit) {
986                 if (fcit->font().noun() == Font::ON) {
987                         LYXERR(Debug::LATEX) << "font.noun: "
988                                              << fcit->font().noun()
989                                              << endl;
990                         features.require("noun");
991                         LYXERR(Debug::LATEX) << "Noun enabled. Font: "
992                                              << to_utf8(fcit->font().stateText(0))
993                                              << endl;
994                 }
995                 switch (fcit->font().color()) {
996                 case Color::none:
997                 case Color::inherit:
998                 case Color::ignore:
999                         // probably we should put here all interface colors used for
1000                         // font displaying! For now I just add this ones I know of (Jug)
1001                 case Color::latex:
1002                 case Color::note:
1003                         break;
1004                 default:
1005                         features.require("color");
1006                         LYXERR(Debug::LATEX) << "Color enabled. Font: "
1007                                              << to_utf8(fcit->font().stateText(0))
1008                                              << endl;
1009                 }
1010
1011                 Language const * language = fcit->font().language();
1012                 if (language->babel() != doc_language->babel() &&
1013                     language != ignore_language &&
1014                     language != latex_language)
1015                 {
1016                         features.useLanguage(language);
1017                         LYXERR(Debug::LATEX) << "Found language "
1018                                              << language->lang() << endl;
1019                 }
1020         }
1021
1022         if (!params.leftIndent().zero())
1023                 features.require("ParagraphLeftIndent");
1024
1025         // then the insets
1026         InsetList::const_iterator icit = owner_->insetlist.begin();
1027         InsetList::const_iterator iend = owner_->insetlist.end();
1028         for (; icit != iend; ++icit) {
1029                 if (icit->inset) {
1030                         icit->inset->validate(features);
1031                         if (layout.needprotect &&
1032                             icit->inset->lyxCode() == Inset::FOOT_CODE)
1033                                 features.require("NeedLyXFootnoteCode");
1034                 }
1035         }
1036
1037         // then the contents
1038         for (pos_type i = 0; i < size() ; ++i) {
1039                 for (size_t pnr = 0; pnr < phrases_nr; ++pnr) {
1040                         if (!special_phrases[pnr].builtin
1041                             && isTextAt(special_phrases[pnr].phrase, i)) {
1042                                 features.require(special_phrases[pnr].phrase);
1043                                 break;
1044                         }
1045                 }
1046                 Encodings::validate(getChar(i), features);
1047         }
1048 }
1049
1050
1051 } // namespace lyx
1052
1053
1054 /////////////////////////////////////////////////////////////////////
1055 //
1056 // Paragraph
1057 //
1058 /////////////////////////////////////////////////////////////////////
1059
1060 namespace lyx {
1061
1062 Paragraph::Paragraph()
1063         : begin_of_body_(0), pimpl_(new Paragraph::Pimpl(this))
1064 {
1065         itemdepth = 0;
1066         params().clear();
1067 }
1068
1069
1070 Paragraph::Paragraph(Paragraph const & par)
1071         : itemdepth(par.itemdepth), insetlist(par.insetlist),
1072         layout_(par.layout_),
1073         text_(par.text_), begin_of_body_(par.begin_of_body_),
1074         pimpl_(new Paragraph::Pimpl(*par.pimpl_, this))
1075 {
1076         //lyxerr << "Paragraph::Paragraph(Paragraph const&)" << endl;
1077         InsetList::iterator it = insetlist.begin();
1078         InsetList::iterator end = insetlist.end();
1079         for (; it != end; ++it)
1080                 it->inset = it->inset->clone().release();
1081 }
1082
1083
1084 Paragraph & Paragraph::operator=(Paragraph const & par)
1085 {
1086         // needed as we will destroy the pimpl_ before copying it
1087         if (&par != this) {
1088                 itemdepth = par.itemdepth;
1089
1090                 insetlist = par.insetlist;
1091                 InsetList::iterator it = insetlist.begin();
1092                 InsetList::iterator end = insetlist.end();
1093                 for (; it != end; ++it)
1094                         it->inset = it->inset->clone().release();
1095
1096                 layout_ = par.layout();
1097                 text_ = par.text_;
1098                 begin_of_body_ = par.begin_of_body_;
1099
1100                 delete pimpl_;
1101                 pimpl_ = new Pimpl(*par.pimpl_, this);
1102         }
1103         return *this;
1104 }
1105
1106
1107 Paragraph::~Paragraph()
1108 {
1109         delete pimpl_;
1110         //
1111         //lyxerr << "Paragraph::paragraph_id = "
1112         //       << Paragraph::paragraph_id << endl;
1113 }
1114
1115
1116 void Paragraph::write(Buffer const & buf, ostream & os,
1117                           BufferParams const & bparams,
1118                           depth_type & dth) const
1119 {
1120         // The beginning or end of a deeper (i.e. nested) area?
1121         if (dth != params().depth()) {
1122                 if (params().depth() > dth) {
1123                         while (params().depth() > dth) {
1124                                 os << "\n\\begin_deeper";
1125                                 ++dth;
1126                         }
1127                 } else {
1128                         while (params().depth() < dth) {
1129                                 os << "\n\\end_deeper";
1130                                 --dth;
1131                         }
1132                 }
1133         }
1134
1135         // First write the layout
1136         os << "\n\\begin_layout " << to_utf8(layout()->name()) << '\n';
1137
1138         params().write(os);
1139
1140         Font font1(Font::ALL_INHERIT, bparams.language);
1141
1142         Change running_change = Change(Change::UNCHANGED);
1143
1144         int column = 0;
1145         for (pos_type i = 0; i <= size(); ++i) {
1146
1147                 Change change = pimpl_->lookupChange(i);
1148                 Changes::lyxMarkChange(os, column, running_change, change);
1149                 running_change = change;
1150
1151                 if (i == size())
1152                         break;
1153
1154                 // Write font changes
1155                 Font font2 = getFontSettings(bparams, i);
1156                 if (font2 != font1) {
1157                         font2.lyxWriteChanges(font1, os);
1158                         column = 0;
1159                         font1 = font2;
1160                 }
1161
1162                 value_type const c = getChar(i);
1163                 switch (c) {
1164                 case META_INSET:
1165                 {
1166                         Inset const * inset = getInset(i);
1167                         if (inset)
1168                                 if (inset->directWrite()) {
1169                                         // international char, let it write
1170                                         // code directly so it's shorter in
1171                                         // the file
1172                                         inset->write(buf, os);
1173                                 } else {
1174                                         if (i)
1175                                                 os << '\n';
1176                                         os << "\\begin_inset ";
1177                                         inset->write(buf, os);
1178                                         os << "\n\\end_inset\n\n";
1179                                         column = 0;
1180                                 }
1181                 }
1182                 break;
1183                 case '\\':
1184                         os << "\n\\backslash\n";
1185                         column = 0;
1186                         break;
1187                 case '.':
1188                         if (i + 1 < size() && getChar(i + 1) == ' ') {
1189                                 os << ".\n";
1190                                 column = 0;
1191                         } else
1192                                 os << '.';
1193                         break;
1194                 default:
1195                         if ((column > 70 && c == ' ')
1196                             || column > 79) {
1197                                 os << '\n';
1198                                 column = 0;
1199                         }
1200                         // this check is to amend a bug. LyX sometimes
1201                         // inserts '\0' this could cause problems.
1202                         if (c != '\0') {
1203                                 std::vector<char> tmp = ucs4_to_utf8(c);
1204                                 tmp.push_back('\0');
1205                                 os << &tmp[0];
1206                         } else
1207                                 lyxerr << "ERROR (Paragraph::writeFile):"
1208                                         " NULL char in structure." << endl;
1209                         ++column;
1210                         break;
1211                 }
1212         }
1213
1214         os << "\n\\end_layout\n";
1215 }
1216
1217
1218 void Paragraph::validate(LaTeXFeatures & features) const
1219 {
1220         pimpl_->validate(features, *layout());
1221 }
1222
1223
1224 bool Paragraph::eraseChar(pos_type pos, bool trackChanges)
1225 {
1226         return pimpl_->eraseChar(pos, trackChanges);
1227 }
1228
1229
1230 int Paragraph::eraseChars(pos_type start, pos_type end, bool trackChanges)
1231 {
1232         return pimpl_->eraseChars(start, end, trackChanges);
1233 }
1234
1235
1236 void Paragraph::insert(pos_type start, docstring const & str,
1237                        Font const & font, Change const & change)
1238 {
1239         for (size_t i = 0, n = str.size(); i != n ; ++i)
1240                 insertChar(start + i, str[i], font, change);
1241 }
1242
1243
1244 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
1245                            bool trackChanges)
1246 {
1247         pimpl_->insertChar(pos, c, Change(trackChanges ?
1248                            Change::INSERTED : Change::UNCHANGED));
1249 }
1250
1251
1252 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
1253                            Font const & font, bool trackChanges)
1254 {
1255         pimpl_->insertChar(pos, c, Change(trackChanges ?
1256                            Change::INSERTED : Change::UNCHANGED));
1257         setFont(pos, font);
1258 }
1259
1260
1261 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
1262                            Font const & font, Change const & change)
1263 {
1264         pimpl_->insertChar(pos, c, change);
1265         setFont(pos, font);
1266 }
1267
1268
1269 void Paragraph::insertInset(pos_type pos, Inset * inset,
1270                             Change const & change)
1271 {
1272         pimpl_->insertInset(pos, inset, change);
1273 }
1274
1275
1276 void Paragraph::insertInset(pos_type pos, Inset * inset,
1277                             Font const & font, Change const & change)
1278 {
1279         pimpl_->insertInset(pos, inset, change);
1280         // Set the font/language of the inset...
1281         setFont(pos, font);
1282         // ... as well as the font/language of the text inside the inset
1283         // FIXME: This is far from perfect. It basically overrides work being done
1284         // in the InsetText constructor. Also, it doesn't work for Tables 
1285         // (precisely because each cell's font/language is set in the Table's 
1286         // constructor, so by now it's too late). The long-term solution should
1287         // be moving current_font into Cursor, and getting rid of all this...
1288         // (see http://thread.gmane.org/gmane.editors.lyx.devel/88869/focus=88944)
1289         if (inset->asTextInset()) {
1290                 inset->asTextInset()->text_.current_font = font;
1291                 inset->asTextInset()->text_.real_current_font = font;
1292         }
1293 }
1294
1295
1296 bool Paragraph::insetAllowed(Inset_code code)
1297 {
1298         return !pimpl_->inset_owner || pimpl_->inset_owner->insetAllowed(code);
1299 }
1300
1301
1302 // Gets uninstantiated font setting at position.
1303 Font const Paragraph::getFontSettings(BufferParams const & bparams,
1304                                          pos_type pos) const
1305 {
1306         if (pos > size()) {
1307                 lyxerr << " pos: " << pos << " size: " << size() << endl;
1308                 BOOST_ASSERT(pos <= size());
1309         }
1310
1311         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1312         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
1313         for (; cit != end; ++cit)
1314                 if (cit->pos() >= pos)
1315                         break;
1316
1317         if (cit != end)
1318                 return cit->font();
1319
1320         if (pos == size() && !empty())
1321                 return getFontSettings(bparams, pos - 1);
1322
1323         return Font(Font::ALL_INHERIT, getParLanguage(bparams));
1324 }
1325
1326
1327 FontSpan Paragraph::fontSpan(pos_type pos) const
1328 {
1329         BOOST_ASSERT(pos <= size());
1330         pos_type start = 0;
1331
1332         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1333         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
1334         for (; cit != end; ++cit) {
1335                 if (cit->pos() >= pos) {
1336                         if (pos >= beginOfBody())
1337                                 return FontSpan(std::max(start, beginOfBody()),
1338                                                 cit->pos());
1339                         else
1340                                 return FontSpan(start,
1341                                                 std::min(beginOfBody() - 1,
1342                                                          cit->pos()));
1343                 }
1344                 start = cit->pos() + 1;
1345         }
1346
1347         // This should not happen, but if so, we take no chances.
1348         //lyxerr << "Paragraph::getEndPosOfFontSpan: This should not happen!"
1349         //      << endl;
1350         return FontSpan(pos, pos);
1351 }
1352
1353
1354 // Gets uninstantiated font setting at position 0
1355 Font const Paragraph::getFirstFontSettings(BufferParams const & bparams) const
1356 {
1357         if (!empty() && !pimpl_->fontlist.empty())
1358                 return pimpl_->fontlist[0].font();
1359
1360         return Font(Font::ALL_INHERIT, bparams.language);
1361 }
1362
1363
1364 // Gets the fully instantiated font at a given position in a paragraph
1365 // This is basically the same function as Text::GetFont() in text2.cpp.
1366 // The difference is that this one is used for generating the LaTeX file,
1367 // and thus cosmetic "improvements" are disallowed: This has to deliver
1368 // the true picture of the buffer. (Asger)
1369 Font const Paragraph::getFont(BufferParams const & bparams, pos_type pos,
1370                                  Font const & outerfont) const
1371 {
1372         BOOST_ASSERT(pos >= 0);
1373
1374         Layout_ptr const & lout = layout();
1375
1376         pos_type const body_pos = beginOfBody();
1377
1378         Font layoutfont;
1379         if (pos < body_pos)
1380                 layoutfont = lout->labelfont;
1381         else
1382                 layoutfont = lout->font;
1383
1384         Font font = getFontSettings(bparams, pos);
1385         font.realize(layoutfont);
1386         font.realize(outerfont);
1387         font.realize(bparams.getFont());
1388
1389         return font;
1390 }
1391
1392
1393 Font const Paragraph::getLabelFont
1394         (BufferParams const & bparams, Font const & outerfont) const
1395 {
1396         Font tmpfont = layout()->labelfont;
1397         tmpfont.setLanguage(getParLanguage(bparams));
1398         tmpfont.realize(outerfont);
1399         tmpfont.realize(bparams.getFont());
1400         return tmpfont;
1401 }
1402
1403
1404 Font const Paragraph::getLayoutFont
1405         (BufferParams const & bparams, Font const & outerfont) const
1406 {
1407         Font tmpfont = layout()->font;
1408         tmpfont.setLanguage(getParLanguage(bparams));
1409         tmpfont.realize(outerfont);
1410         tmpfont.realize(bparams.getFont());
1411         return tmpfont;
1412 }
1413
1414
1415 /// Returns the height of the highest font in range
1416 Font_size Paragraph::highestFontInRange
1417         (pos_type startpos, pos_type endpos, Font_size def_size) const
1418 {
1419         if (pimpl_->fontlist.empty())
1420                 return def_size;
1421
1422         Pimpl::FontList::const_iterator end_it = pimpl_->fontlist.begin();
1423         Pimpl::FontList::const_iterator const end = pimpl_->fontlist.end();
1424         for (; end_it != end; ++end_it) {
1425                 if (end_it->pos() >= endpos)
1426                         break;
1427         }
1428
1429         if (end_it != end)
1430                 ++end_it;
1431
1432         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1433         for (; cit != end; ++cit) {
1434                 if (cit->pos() >= startpos)
1435                         break;
1436         }
1437
1438         Font::FONT_SIZE maxsize = Font::SIZE_TINY;
1439         for (; cit != end_it; ++cit) {
1440                 Font::FONT_SIZE size = cit->font().size();
1441                 if (size == Font::INHERIT_SIZE)
1442                         size = def_size;
1443                 if (size > maxsize && size <= Font::SIZE_HUGER)
1444                         maxsize = size;
1445         }
1446         return maxsize;
1447 }
1448
1449
1450 Paragraph::value_type
1451 Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
1452 {
1453         value_type c = getChar(pos);
1454         if (!lyxrc.rtl_support)
1455                 return c;
1456
1457         value_type uc = c;
1458         switch (c) {
1459         case '(':
1460                 uc = ')';
1461                 break;
1462         case ')':
1463                 uc = '(';
1464                 break;
1465         case '[':
1466                 uc = ']';
1467                 break;
1468         case ']':
1469                 uc = '[';
1470                 break;
1471         case '{':
1472                 uc = '}';
1473                 break;
1474         case '}':
1475                 uc = '{';
1476                 break;
1477         case '<':
1478                 uc = '>';
1479                 break;
1480         case '>':
1481                 uc = '<';
1482                 break;
1483         }
1484         if (uc != c && getFontSettings(bparams, pos).isRightToLeft())
1485                 return uc;
1486         else
1487                 return c;
1488 }
1489
1490
1491 void Paragraph::setFont(pos_type pos, Font const & font)
1492 {
1493         BOOST_ASSERT(pos <= size());
1494
1495         // First, reduce font against layout/label font
1496         // Update: The setCharFont() routine in text2.cpp already
1497         // reduces font, so we don't need to do that here. (Asger)
1498         // No need to simplify this because it will disappear
1499         // in a new kernel. (Asger)
1500         // Next search font table
1501
1502         Pimpl::FontList::iterator beg = pimpl_->fontlist.begin();
1503         Pimpl::FontList::iterator it = beg;
1504         Pimpl::FontList::iterator endit = pimpl_->fontlist.end();
1505         for (; it != endit; ++it) {
1506                 if (it->pos() >= pos)
1507                         break;
1508         }
1509         size_t const i = distance(beg, it);
1510         bool notfound = (it == endit);
1511
1512         if (!notfound && pimpl_->fontlist[i].font() == font)
1513                 return;
1514
1515         bool begin = pos == 0 || notfound ||
1516                 (i > 0 && pimpl_->fontlist[i - 1].pos() == pos - 1);
1517         // Is position pos is a beginning of a font block?
1518         bool end = !notfound && pimpl_->fontlist[i].pos() == pos;
1519         // Is position pos is the end of a font block?
1520         if (begin && end) { // A single char block
1521                 if (i + 1 < pimpl_->fontlist.size() &&
1522                     pimpl_->fontlist[i + 1].font() == font) {
1523                         // Merge the singleton block with the next block
1524                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
1525                         if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
1526                                 pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i - 1);
1527                 } else if (i > 0 && pimpl_->fontlist[i - 1].font() == font) {
1528                         // Merge the singleton block with the previous block
1529                         pimpl_->fontlist[i - 1].pos(pos);
1530                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
1531                 } else
1532                         pimpl_->fontlist[i].font(font);
1533         } else if (begin) {
1534                 if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
1535                         pimpl_->fontlist[i - 1].pos(pos);
1536                 else
1537                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
1538                                         Pimpl::FontTable(pos, font));
1539         } else if (end) {
1540                 pimpl_->fontlist[i].pos(pos - 1);
1541                 if (!(i + 1 < pimpl_->fontlist.size() &&
1542                       pimpl_->fontlist[i + 1].font() == font))
1543                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
1544                                         Pimpl::FontTable(pos, font));
1545         } else { // The general case. The block is splitted into 3 blocks
1546                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
1547                                 Pimpl::FontTable(pos - 1, pimpl_->fontlist[i].font()));
1548                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
1549                                 Pimpl::FontTable(pos, font));
1550         }
1551 }
1552
1553
1554 void Paragraph::makeSameLayout(Paragraph const & par)
1555 {
1556         layout(par.layout());
1557         // move to pimpl?
1558         params() = par.params();
1559 }
1560
1561
1562 bool Paragraph::stripLeadingSpaces(bool trackChanges)
1563 {
1564         if (isFreeSpacing())
1565                 return false;
1566
1567         int pos = 0;
1568         int count = 0;
1569
1570         while (pos < size() && (isNewline(pos) || isLineSeparator(pos))) {
1571                 if (eraseChar(pos, trackChanges))
1572                         ++count;
1573                 else
1574                         ++pos;
1575         }
1576
1577         return count > 0 || pos > 0;
1578 }
1579
1580
1581 bool Paragraph::hasSameLayout(Paragraph const & par) const
1582 {
1583         return par.layout() == layout() && params().sameLayout(par.params());
1584 }
1585
1586
1587 depth_type Paragraph::getDepth() const
1588 {
1589         return params().depth();
1590 }
1591
1592
1593 depth_type Paragraph::getMaxDepthAfter() const
1594 {
1595         if (layout()->isEnvironment())
1596                 return params().depth() + 1;
1597         else
1598                 return params().depth();
1599 }
1600
1601
1602 char Paragraph::getAlign() const
1603 {
1604         if (params().align() == LYX_ALIGN_LAYOUT)
1605                 return layout()->align;
1606         else
1607                 return params().align();
1608 }
1609
1610
1611 docstring const & Paragraph::getLabelstring() const
1612 {
1613         return params().labelString();
1614 }
1615
1616
1617 // the next two functions are for the manual labels
1618 docstring const Paragraph::getLabelWidthString() const
1619 {
1620         if (!params().labelWidthString().empty())
1621                 return params().labelWidthString();
1622         else
1623                 return _("Senseless with this layout!");
1624 }
1625
1626
1627 void Paragraph::setLabelWidthString(docstring const & s)
1628 {
1629         params().labelWidthString(s);
1630 }
1631
1632
1633 docstring const Paragraph::translateIfPossible(docstring const & s,
1634                 BufferParams const & bparams) const
1635 {
1636         if (!support::isAscii(s) || s.empty()) {
1637                 // This must be a user defined layout. We cannot translate
1638                 // this, since gettext accepts only ascii keys.
1639                 return s;
1640         }
1641         // Probably standard layout, try to translate
1642         Messages & m = getMessages(getParLanguage(bparams)->code());
1643         return m.get(to_ascii(s));
1644 }
1645
1646
1647 docstring Paragraph::expandLabel(Layout_ptr const & layout,
1648                 BufferParams const & bparams, bool process_appendix) const
1649 {
1650         TextClass const & tclass = bparams.getTextClass();
1651
1652         docstring fmt;
1653         if (process_appendix && params().appendix())
1654                 fmt = translateIfPossible(layout->labelstring_appendix(),
1655                         bparams);
1656         else
1657                 fmt = translateIfPossible(layout->labelstring(), bparams);
1658
1659         // handle 'inherited level parts' in 'fmt',
1660         // i.e. the stuff between '@' in   '@Section@.\arabic{subsection}'
1661         size_t const i = fmt.find('@', 0);
1662         if (i != docstring::npos) {
1663                 size_t const j = fmt.find('@', i + 1);
1664                 if (j != docstring::npos) {
1665                         docstring parent(fmt, i + 1, j - i - 1);
1666                         docstring label = expandLabel(tclass[parent], bparams);
1667                         fmt = docstring(fmt, 0, i) + label + docstring(fmt, j + 1, docstring::npos);
1668                 }
1669         }
1670
1671         return tclass.counters().counterLabel(fmt);
1672 }
1673
1674
1675 void Paragraph::applyLayout(Layout_ptr const & new_layout)
1676 {
1677         layout(new_layout);
1678         LyXAlignment const oldAlign = params().align();
1679         // FIXME The first check is due to the fact that LYX_ALIGN_LAYOUT
1680         // is not required to be possible. A fix is on the way.
1681         if ((oldAlign != LYX_ALIGN_LAYOUT) && 
1682             !(oldAlign & layout()->alignpossible)) {
1683                 frontend::Alert::warning(_("Alignment not permitted"), 
1684                         _("The new layout does not permit the alignment previously used.\nSetting to default."));
1685                 params().align(LYX_ALIGN_LAYOUT);
1686         }
1687 }
1688
1689
1690 pos_type Paragraph::beginOfBody() const
1691 {
1692         return begin_of_body_;
1693 }
1694
1695
1696 void Paragraph::setBeginOfBody()
1697 {
1698         if (layout()->labeltype != LABEL_MANUAL) {
1699                 begin_of_body_ = 0;
1700                 return;
1701         }
1702
1703         // Unroll the first two cycles of the loop
1704         // and remember the previous character to
1705         // remove unnecessary getChar() calls
1706         pos_type i = 0;
1707         pos_type end = size();
1708         if (i < end && !isNewline(i)) {
1709                 ++i;
1710                 char_type previous_char = 0;
1711                 char_type temp = 0;
1712                 if (i < end) {
1713                         previous_char = text_[i];
1714                         if (!isNewline(i)) {
1715                                 ++i;
1716                                 while (i < end && previous_char != ' ') {
1717                                         temp = text_[i];
1718                                         if (isNewline(i))
1719                                                 break;
1720                                         ++i;
1721                                         previous_char = temp;
1722                                 }
1723                         }
1724                 }
1725         }
1726
1727         begin_of_body_ = i;
1728 }
1729
1730
1731 // returns -1 if inset not found
1732 int Paragraph::getPositionOfInset(Inset const * inset) const
1733 {
1734         // Find the entry.
1735         InsetList::const_iterator it = insetlist.begin();
1736         InsetList::const_iterator end = insetlist.end();
1737         for (; it != end; ++it)
1738                 if (it->inset == inset)
1739                         return it->pos;
1740         return -1;
1741 }
1742
1743
1744 InsetBibitem * Paragraph::bibitem() const
1745 {
1746         if (!insetlist.empty()) {
1747                 Inset * inset = insetlist.begin()->inset;
1748                 if (inset->lyxCode() == Inset::BIBITEM_CODE)
1749                         return static_cast<InsetBibitem *>(inset);
1750         }
1751         return 0;
1752 }
1753
1754
1755 bool Paragraph::forceDefaultParagraphs() const
1756 {
1757         return inInset() && inInset()->forceDefaultParagraphs(0);
1758 }
1759
1760
1761 namespace {
1762
1763 // paragraphs inside floats need different alignment tags to avoid
1764 // unwanted space
1765
1766 bool noTrivlistCentering(Inset::Code code)
1767 {
1768         return code == Inset::FLOAT_CODE || code == Inset::WRAP_CODE;
1769 }
1770
1771
1772 string correction(string const & orig)
1773 {
1774         if (orig == "flushleft")
1775                 return "raggedright";
1776         if (orig == "flushright")
1777                 return "raggedleft";
1778         if (orig == "center")
1779                 return "centering";
1780         return orig;
1781 }
1782
1783
1784 string const corrected_env(string const & suffix, string const & env,
1785         Inset::Code code)
1786 {
1787         string output = suffix + "{";
1788         if (noTrivlistCentering(code))
1789                 output += correction(env);
1790         else
1791                 output += env;
1792         output += "}";
1793         if (suffix == "\\begin")
1794                 output += "\n";
1795         return output;
1796 }
1797
1798
1799 void adjust_row_column(string const & str, TexRow & texrow, int & column)
1800 {
1801         if (!contains(str, "\n"))
1802                 column += str.size();
1803         else {
1804                 string tmp;
1805                 texrow.newline();
1806                 column = rsplit(str, tmp, '\n').size();
1807         }
1808 }
1809
1810 } // namespace anon
1811
1812
1813 // This could go to ParagraphParameters if we want to
1814 int Paragraph::startTeXParParams(BufferParams const & bparams,
1815                                  odocstream & os, TexRow & texrow,
1816                                  bool moving_arg) const
1817 {
1818         int column = 0;
1819
1820         if (params().noindent()) {
1821                 os << "\\noindent ";
1822                 column += 10;
1823         }
1824         
1825         LyXAlignment const curAlign = params().align();
1826
1827         if (curAlign == layout()->align)
1828                 return column;
1829
1830         switch (curAlign) {
1831         case LYX_ALIGN_NONE:
1832         case LYX_ALIGN_BLOCK:
1833         case LYX_ALIGN_LAYOUT:
1834         case LYX_ALIGN_SPECIAL:
1835                 break;
1836         case LYX_ALIGN_LEFT:
1837         case LYX_ALIGN_RIGHT:
1838         case LYX_ALIGN_CENTER:
1839                 if (moving_arg) {
1840                         os << "\\protect";
1841                         column += 8;
1842                 }
1843                 break;
1844         }
1845
1846         switch (curAlign) {
1847         case LYX_ALIGN_NONE:
1848         case LYX_ALIGN_BLOCK:
1849         case LYX_ALIGN_LAYOUT:
1850         case LYX_ALIGN_SPECIAL:
1851                 break;
1852         case LYX_ALIGN_LEFT: {
1853                 string output;
1854                 if (getParLanguage(bparams)->babel() != "hebrew")
1855                         output = corrected_env("\\begin", "flushleft", ownerCode());
1856                 else
1857                         output = corrected_env("\\begin", "flushright", ownerCode());
1858                 os << from_ascii(output);
1859                 adjust_row_column(output, texrow, column);
1860                 break;
1861         } case LYX_ALIGN_RIGHT: {
1862                 string output;
1863                 if (getParLanguage(bparams)->babel() != "hebrew")
1864                         output = corrected_env("\\begin", "flushright", ownerCode());
1865                 else
1866                         output = corrected_env("\\begin", "flushleft", ownerCode());
1867                 os << from_ascii(output);
1868                 adjust_row_column(output, texrow, column);
1869                 break;
1870         } case LYX_ALIGN_CENTER: {
1871                 string output;
1872                 output = corrected_env("\\begin", "center", ownerCode());
1873                 os << from_ascii(output);
1874                 adjust_row_column(output, texrow, column);
1875                 break;
1876         }
1877         }
1878
1879         return column;
1880 }
1881
1882
1883 // This could go to ParagraphParameters if we want to
1884 int Paragraph::endTeXParParams(BufferParams const & bparams,
1885                                odocstream & os, TexRow & texrow,
1886                                bool moving_arg) const
1887 {
1888         int column = 0;
1889
1890         switch (params().align()) {
1891         case LYX_ALIGN_NONE:
1892         case LYX_ALIGN_BLOCK:
1893         case LYX_ALIGN_LAYOUT:
1894         case LYX_ALIGN_SPECIAL:
1895                 break;
1896         case LYX_ALIGN_LEFT:
1897         case LYX_ALIGN_RIGHT:
1898         case LYX_ALIGN_CENTER:
1899                 if (moving_arg) {
1900                         os << "\\protect";
1901                         column = 8;
1902                 }
1903                 break;
1904         }
1905
1906         switch (params().align()) {
1907         case LYX_ALIGN_NONE:
1908         case LYX_ALIGN_BLOCK:
1909         case LYX_ALIGN_LAYOUT:
1910         case LYX_ALIGN_SPECIAL:
1911                 break;
1912         case LYX_ALIGN_LEFT: {
1913                 string output;
1914                 if (getParLanguage(bparams)->babel() != "hebrew")
1915                         output = corrected_env("\n\\par\\end", "flushleft", ownerCode());
1916                 else
1917                         output = corrected_env("\n\\par\\end", "flushright", ownerCode());
1918                 os << from_ascii(output);
1919                 adjust_row_column(output, texrow, column);
1920                 break;
1921         } case LYX_ALIGN_RIGHT: {
1922                 string output;
1923                 if (getParLanguage(bparams)->babel() != "hebrew")
1924                         output = corrected_env("\n\\par\\end", "flushright", ownerCode());
1925                 else
1926                         output = corrected_env("\n\\par\\end", "flushleft", ownerCode());
1927                 os << from_ascii(output);
1928                 adjust_row_column(output, texrow, column);
1929                 break;
1930         } case LYX_ALIGN_CENTER: {
1931                 string output;
1932                 output = corrected_env("\n\\par\\end", "center", ownerCode());
1933                 os << from_ascii(output);
1934                 adjust_row_column(output, texrow, column);
1935                 break;
1936         }
1937         }
1938
1939         return column;
1940 }
1941
1942
1943 // This one spits out the text of the paragraph
1944 bool Paragraph::simpleTeXOnePar(Buffer const & buf,
1945                                 BufferParams const & bparams,
1946                                 Font const & outerfont,
1947                                 odocstream & os, TexRow & texrow,
1948                                 OutputParams const & runparams) const
1949 {
1950         LYXERR(Debug::LATEX) << "SimpleTeXOnePar...     " << this << endl;
1951
1952         bool return_value = false;
1953
1954         Layout_ptr style;
1955
1956         // well we have to check if we are in an inset with unlimited
1957         // length (all in one row) if that is true then we don't allow
1958         // any special options in the paragraph and also we don't allow
1959         // any environment other than the default layout of the text class
1960         // to be valid!
1961         bool asdefault = forceDefaultParagraphs();
1962
1963         if (asdefault) {
1964                 style = bparams.getTextClass().defaultLayout();
1965         } else {
1966                 style = layout();
1967         }
1968
1969         // Current base font for all inherited font changes, without any
1970         // change caused by an individual character, except for the language:
1971         // It is set to the language of the first character.
1972         // As long as we are in the label, this font is the base font of the
1973         // label. Before the first body character it is set to the base font
1974         // of the body.
1975         Font basefont;
1976
1977         // Maybe we have to create a optional argument.
1978         pos_type body_pos = beginOfBody();
1979         unsigned int column = 0;
1980
1981         if (body_pos > 0) {
1982                 // the optional argument is kept in curly brackets in
1983                 // case it contains a ']'
1984                 os << "[{";
1985                 column += 2;
1986                 basefont = getLabelFont(bparams, outerfont);
1987         } else {
1988                 basefont = getLayoutFont(bparams, outerfont);
1989         }
1990
1991         // Which font is currently active?
1992         Font running_font(basefont);
1993         // Do we have an open font change?
1994         bool open_font = false;
1995
1996         Change runningChange = Change(Change::UNCHANGED);
1997
1998         texrow.start(id(), 0);
1999
2000         // if the paragraph is empty, the loop will not be entered at all
2001         if (empty()) {
2002                 if (style->isCommand()) {
2003                         os << '{';
2004                         ++column;
2005                 }
2006                 if (!asdefault)
2007                         column += startTeXParParams(bparams, os, texrow,
2008                                                     runparams.moving_arg);
2009         }
2010
2011         for (pos_type i = 0; i < size(); ++i) {
2012                 // First char in paragraph or after label?
2013                 if (i == body_pos) {
2014                         if (body_pos > 0) {
2015                                 if (open_font) {
2016                                         column += running_font.latexWriteEndChanges(
2017                                                 os, bparams, runparams,
2018                                                 basefont, basefont);
2019                                         open_font = false;
2020                                 }
2021                                 basefont = getLayoutFont(bparams, outerfont);
2022                                 running_font = basefont;
2023
2024                                 column += Changes::latexMarkChange(os, bparams,
2025                                                 runningChange, Change(Change::UNCHANGED));
2026                                 runningChange = Change(Change::UNCHANGED);
2027
2028                                 os << "}] ";
2029                                 column +=3;
2030                         }
2031                         if (style->isCommand()) {
2032                                 os << '{';
2033                                 ++column;
2034                         }
2035
2036                         if (!asdefault)
2037                                 column += startTeXParParams(bparams, os,
2038                                                             texrow,
2039                                                             runparams.moving_arg);
2040                 }
2041
2042                 Change const & change = pimpl_->lookupChange(i);
2043
2044                 if (bparams.outputChanges && runningChange != change) {
2045                         if (open_font) {
2046                                 column += running_font.latexWriteEndChanges(
2047                                                 os, bparams, runparams, basefont, basefont);
2048                                 open_font = false;
2049                         }
2050                         basefont = getLayoutFont(bparams, outerfont);
2051                         running_font = basefont;
2052
2053                         column += Changes::latexMarkChange(os, bparams, runningChange, change);
2054                         runningChange = change;
2055                 }
2056
2057                 // do not output text which is marked deleted
2058                 // if change tracking output is disabled
2059                 if (!bparams.outputChanges && change.type == Change::DELETED) {
2060                         continue;
2061                 }
2062
2063                 ++column;
2064
2065                 value_type const c = getChar(i);
2066
2067                 // Fully instantiated font
2068                 Font const font = getFont(bparams, i, outerfont);
2069
2070                 Font const last_font = running_font;
2071
2072                 // Do we need to close the previous font?
2073                 if (open_font &&
2074                     (font != running_font ||
2075                      font.language() != running_font.language()))
2076                 {
2077                         column += running_font.latexWriteEndChanges(
2078                                         os, bparams, runparams, basefont,
2079                                         (i == body_pos-1) ? basefont : font);
2080                         running_font = basefont;
2081                         open_font = false;
2082                 }
2083
2084                 // Switch file encoding if necessary
2085                 if (runparams.encoding->package() == Encoding::inputenc &&
2086                     font.language()->encoding()->package() == Encoding::inputenc) {
2087                         std::pair<bool, int> const enc_switch = switchEncoding(os, bparams,
2088                                         runparams.moving_arg, *(runparams.encoding),
2089                                         *(font.language()->encoding()));
2090                         if (enc_switch.first) {
2091                                 column += enc_switch.second;
2092                                 runparams.encoding = font.language()->encoding();
2093                         }
2094                 }
2095
2096                 // Do we need to change font?
2097                 if ((font != running_font ||
2098                      font.language() != running_font.language()) &&
2099                         i != body_pos - 1)
2100                 {
2101                         odocstringstream ods;
2102                         column += font.latexWriteStartChanges(ods, bparams,
2103                                                               runparams, basefont,
2104                                                               last_font);
2105                         running_font = font;
2106                         open_font = true;
2107                         docstring fontchange = ods.str();
2108                         // check if the fontchange ends with a trailing blank
2109                         // (like "\small " (see bug 3382)
2110                         if (suffixIs(fontchange, ' ') && c == ' ')
2111                                 os << fontchange.substr(0, fontchange.size() - 1) 
2112                                    << from_ascii("{}");
2113                         else
2114                                 os << fontchange;
2115                 }
2116
2117                 if (c == ' ') {
2118                         // Do not print the separation of the optional argument
2119                         // if style->pass_thru is false. This works because
2120                         // simpleTeXSpecialChars ignores spaces if
2121                         // style->pass_thru is false.
2122                         if (i != body_pos - 1) {
2123                                 if (pimpl_->simpleTeXBlanks(
2124                                                 *(runparams.encoding), os, texrow,
2125                                                 i, column, font, *style))
2126                                         // A surrogate pair was output. We
2127                                         // must not call simpleTeXSpecialChars
2128                                         // in this iteration, since
2129                                         // simpleTeXBlanks incremented i, and
2130                                         // simpleTeXSpecialChars would output
2131                                         // the combining character again.
2132                                         continue;
2133                         }
2134                 }
2135
2136                 OutputParams rp = runparams;
2137                 rp.free_spacing = style->free_spacing;
2138                 rp.local_font = &font;
2139                 rp.intitle = style->intitle;
2140                 pimpl_->simpleTeXSpecialChars(buf, bparams, os,
2141                                         texrow, rp, running_font,
2142                                         basefont, outerfont, open_font,
2143                                         runningChange, *style, i, column, c);
2144
2145                 // Set the encoding to that returned from simpleTeXSpecialChars (see
2146                 // comment for encoding member in OutputParams.h)
2147                 runparams.encoding = rp.encoding;
2148         }
2149
2150         // If we have an open font definition, we have to close it
2151         if (open_font) {
2152 #ifdef FIXED_LANGUAGE_END_DETECTION
2153                 if (next_) {
2154                         running_font
2155                                 .latexWriteEndChanges(os, bparams, runparams,
2156                                         basefont,
2157                                         next_->getFont(bparams, 0, outerfont));
2158                 } else {
2159                         running_font.latexWriteEndChanges(os, bparams,
2160                                         runparams, basefont, basefont);
2161                 }
2162 #else
2163 #ifdef WITH_WARNINGS
2164 //#warning For now we ALWAYS have to close the foreign font settings if they are
2165 //#warning there as we start another \selectlanguage with the next paragraph if
2166 //#warning we are in need of this. This should be fixed sometime (Jug)
2167 #endif
2168                 running_font.latexWriteEndChanges(os, bparams, runparams,
2169                                 basefont, basefont);
2170 #endif
2171         }
2172
2173         column += Changes::latexMarkChange(os, bparams, runningChange, Change(Change::UNCHANGED));
2174
2175         // Needed if there is an optional argument but no contents.
2176         if (body_pos > 0 && body_pos == size()) {
2177                 os << "}]~";
2178                 return_value = false;
2179         }
2180
2181         if (!asdefault) {
2182                 column += endTeXParParams(bparams, os, texrow,
2183                                           runparams.moving_arg);
2184         }
2185
2186         LYXERR(Debug::LATEX) << "SimpleTeXOnePar...done " << this << endl;
2187         return return_value;
2188 }
2189
2190
2191 namespace {
2192
2193 enum PAR_TAG {
2194         PAR_NONE=0,
2195         TT = 1,
2196         SF = 2,
2197         BF = 4,
2198         IT = 8,
2199         SL = 16,
2200         EM = 32
2201 };
2202
2203
2204 string tag_name(PAR_TAG const & pt) {
2205         switch (pt) {
2206         case PAR_NONE: return "!-- --";
2207         case TT: return "tt";
2208         case SF: return "sf";
2209         case BF: return "bf";
2210         case IT: return "it";
2211         case SL: return "sl";
2212         case EM: return "em";
2213         }
2214         return "";
2215 }
2216
2217
2218 inline
2219 void operator|=(PAR_TAG & p1, PAR_TAG const & p2)
2220 {
2221         p1 = static_cast<PAR_TAG>(p1 | p2);
2222 }
2223
2224
2225 inline
2226 void reset(PAR_TAG & p1, PAR_TAG const & p2)
2227 {
2228         p1 = static_cast<PAR_TAG>(p1 & ~p2);
2229 }
2230
2231 } // anon
2232
2233
2234 bool Paragraph::emptyTag() const
2235 {
2236         for (pos_type i = 0; i < size(); ++i) {
2237                 if (isInset(i)) {
2238                         Inset const * inset = getInset(i);
2239                         Inset::Code lyx_code = inset->lyxCode();
2240                         if (lyx_code != Inset::TOC_CODE &&
2241                             lyx_code != Inset::INCLUDE_CODE &&
2242                             lyx_code != Inset::GRAPHICS_CODE &&
2243                             lyx_code != Inset::ERT_CODE &&
2244                             lyx_code != Inset::LISTINGS_CODE &&
2245                             lyx_code != Inset::FLOAT_CODE &&
2246                             lyx_code != Inset::TABULAR_CODE) {
2247                                 return false;
2248                         }
2249                 } else {
2250                         value_type c = getChar(i);
2251                         if (c != ' ' && c != '\t')
2252                                 return false;
2253                 }
2254         }
2255         return true;
2256 }
2257
2258
2259 string Paragraph::getID(Buffer const & buf, OutputParams const & runparams) const
2260 {
2261         for (pos_type i = 0; i < size(); ++i) {
2262                 if (isInset(i)) {
2263                         Inset const * inset = getInset(i);
2264                         Inset::Code lyx_code = inset->lyxCode();
2265                         if (lyx_code == Inset::LABEL_CODE) {
2266                                 string const id = static_cast<InsetCommand const *>(inset)->getContents();
2267                                 return "id='" + to_utf8(sgml::cleanID(buf, runparams, from_utf8(id))) + "'";
2268                         }
2269                 }
2270
2271         }
2272         return string();
2273 }
2274
2275
2276 pos_type Paragraph::getFirstWord(Buffer const & buf, odocstream & os, OutputParams const & runparams) const
2277 {
2278         pos_type i;
2279         for (i = 0; i < size(); ++i) {
2280                 if (isInset(i)) {
2281                         Inset const * inset = getInset(i);
2282                         inset->docbook(buf, os, runparams);
2283                 } else {
2284                         value_type c = getChar(i);
2285                         if (c == ' ')
2286                                 break;
2287                         os << sgml::escapeChar(c);
2288                 }
2289         }
2290         return i;
2291 }
2292
2293
2294 bool Paragraph::onlyText(Buffer const & buf, Font const & outerfont, pos_type initial) const
2295 {
2296         Font font_old;
2297
2298         for (pos_type i = initial; i < size(); ++i) {
2299                 Font font = getFont(buf.params(), i, outerfont);
2300                 if (isInset(i))
2301                         return false;
2302                 if (i != initial && font != font_old)
2303                         return false;
2304                 font_old = font;
2305         }
2306
2307         return true;
2308 }
2309
2310
2311 void Paragraph::simpleDocBookOnePar(Buffer const & buf,
2312                                     odocstream & os,
2313                                     OutputParams const & runparams,
2314                                     Font const & outerfont,
2315                                     pos_type initial) const
2316 {
2317         bool emph_flag = false;
2318
2319         Layout_ptr const & style = layout();
2320         Font font_old =
2321                 style->labeltype == LABEL_MANUAL ? style->labelfont : style->font;
2322
2323         if (style->pass_thru && !onlyText(buf, outerfont, initial))
2324                 os << "]]>";
2325
2326         // parsing main loop
2327         for (pos_type i = initial; i < size(); ++i) {
2328                 Font font = getFont(buf.params(), i, outerfont);
2329
2330                 // handle <emphasis> tag
2331                 if (font_old.emph() != font.emph()) {
2332                         if (font.emph() == Font::ON) {
2333                                 os << "<emphasis>";
2334                                 emph_flag = true;
2335                         } else if (i != initial) {
2336                                 os << "</emphasis>";
2337                                 emph_flag = false;
2338                         }
2339                 }
2340
2341                 if (isInset(i)) {
2342                         Inset const * inset = getInset(i);
2343                         inset->docbook(buf, os, runparams);
2344                 } else {
2345                         value_type c = getChar(i);
2346
2347                         if (style->pass_thru)
2348                                 os.put(c);
2349                         else
2350                                 os << sgml::escapeChar(c);
2351                 }
2352                 font_old = font;
2353         }
2354
2355         if (emph_flag) {
2356                 os << "</emphasis>";
2357         }
2358
2359         if (style->free_spacing)
2360                 os << '\n';
2361         if (style->pass_thru && !onlyText(buf, outerfont, initial))
2362                 os << "<![CDATA[";
2363 }
2364
2365
2366 bool Paragraph::isNewline(pos_type pos) const
2367 {
2368         return isInset(pos)
2369                 && getInset(pos)->lyxCode() == Inset::NEWLINE_CODE;
2370 }
2371
2372
2373 bool Paragraph::isLineSeparator(pos_type pos) const
2374 {
2375         value_type const c = getChar(pos);
2376         return isLineSeparatorChar(c)
2377                 || (c == Paragraph::META_INSET && getInset(pos) &&
2378                 getInset(pos)->isLineSeparator());
2379 }
2380
2381
2382 /// Used by the spellchecker
2383 bool Paragraph::isLetter(pos_type pos) const
2384 {
2385         if (isInset(pos))
2386                 return getInset(pos)->isLetter();
2387         else {
2388                 value_type const c = getChar(pos);
2389                 return isLetterChar(c) || isDigit(c);
2390         }
2391 }
2392
2393
2394 Language const *
2395 Paragraph::getParLanguage(BufferParams const & bparams) const
2396 {
2397         if (!empty())
2398                 return getFirstFontSettings(bparams).language();
2399 #ifdef WITH_WARNINGS
2400 #warning FIXME we should check the prev par as well (Lgb)
2401 #endif
2402         return bparams.language;
2403 }
2404
2405
2406 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
2407 {
2408         return lyxrc.rtl_support
2409                 && getParLanguage(bparams)->rightToLeft()
2410                 && ownerCode() != Inset::ERT_CODE
2411                 && ownerCode() != Inset::LISTINGS_CODE;
2412 }
2413
2414
2415 void Paragraph::changeLanguage(BufferParams const & bparams,
2416                                Language const * from, Language const * to)
2417 {
2418         // change language including dummy font change at the end
2419         for (pos_type i = 0; i <= size(); ++i) {
2420                 Font font = getFontSettings(bparams, i);
2421                 if (font.language() == from) {
2422                         font.setLanguage(to);
2423                         setFont(i, font);
2424                 }
2425         }
2426 }
2427
2428
2429 bool Paragraph::isMultiLingual(BufferParams const & bparams) const
2430 {
2431         Language const * doc_language = bparams.language;
2432         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
2433         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
2434
2435         for (; cit != end; ++cit)
2436                 if (cit->font().language() != ignore_language &&
2437                     cit->font().language() != latex_language &&
2438                     cit->font().language() != doc_language)
2439                         return true;
2440         return false;
2441 }
2442
2443
2444 // Convert the paragraph to a string.
2445 // Used for building the table of contents
2446 docstring const Paragraph::asString(Buffer const & buffer, bool label) const
2447 {
2448         return asString(buffer, 0, size(), label);
2449 }
2450
2451
2452 docstring const Paragraph::asString(Buffer const & buffer,
2453                                  pos_type beg, pos_type end, bool label) const
2454 {
2455
2456         odocstringstream os;
2457
2458         if (beg == 0 && label && !params().labelString().empty())
2459                 os << params().labelString() << ' ';
2460
2461         for (pos_type i = beg; i < end; ++i) {
2462                 value_type const c = getChar(i);
2463                 if (isPrintable(c))
2464                         os.put(c);
2465                 else if (c == META_INSET)
2466                         getInset(i)->textString(buffer, os);
2467         }
2468
2469         return os.str();
2470 }
2471
2472
2473 void Paragraph::setInsetOwner(Inset * inset)
2474 {
2475         pimpl_->inset_owner = inset;
2476 }
2477
2478
2479 Change const & Paragraph::lookupChange(pos_type pos) const
2480 {
2481         BOOST_ASSERT(pos <= size());
2482         return pimpl_->lookupChange(pos);
2483 }
2484
2485
2486 bool Paragraph::isChanged(pos_type start, pos_type end) const
2487 {
2488         return pimpl_->isChanged(start, end);
2489 }
2490
2491
2492 bool Paragraph::isMergedOnEndOfParDeletion(bool trackChanges) const
2493 {
2494         return pimpl_->isMergedOnEndOfParDeletion(trackChanges);
2495 }
2496
2497
2498 void Paragraph::setChange(Change const & change)
2499 {
2500         pimpl_->setChange(change);
2501 }
2502
2503
2504 void Paragraph::setChange(pos_type pos, Change const & change)
2505 {
2506         pimpl_->setChange(pos, change);
2507 }
2508
2509
2510 void Paragraph::acceptChanges(BufferParams const & bparams, pos_type start, pos_type end)
2511 {
2512         return pimpl_->acceptChanges(bparams, start, end);
2513 }
2514
2515
2516 void Paragraph::rejectChanges(BufferParams const & bparams, pos_type start, pos_type end)
2517 {
2518         return pimpl_->rejectChanges(bparams, start, end);
2519 }
2520
2521
2522 int Paragraph::id() const
2523 {
2524         return pimpl_->id_;
2525 }
2526
2527
2528 Layout_ptr const & Paragraph::layout() const
2529 {
2530         return layout_;
2531 }
2532
2533
2534 void Paragraph::layout(Layout_ptr const & new_layout)
2535 {
2536         layout_ = new_layout;
2537 }
2538
2539
2540 Inset * Paragraph::inInset() const
2541 {
2542         return pimpl_->inset_owner;
2543 }
2544
2545
2546 Inset::Code Paragraph::ownerCode() const
2547 {
2548         return pimpl_->inset_owner
2549                 ? pimpl_->inset_owner->lyxCode() : Inset::NO_CODE;
2550 }
2551
2552
2553 ParagraphParameters & Paragraph::params()
2554 {
2555         return pimpl_->params;
2556 }
2557
2558
2559 ParagraphParameters const & Paragraph::params() const
2560 {
2561         return pimpl_->params;
2562 }
2563
2564
2565 bool Paragraph::isFreeSpacing() const
2566 {
2567         if (layout()->free_spacing)
2568                 return true;
2569
2570         // for now we just need this, later should we need this in some
2571         // other way we can always add a function to Inset too.
2572         return ownerCode() == Inset::ERT_CODE || ownerCode() == Inset::LISTINGS_CODE;
2573 }
2574
2575
2576 bool Paragraph::allowEmpty() const
2577 {
2578         if (layout()->keepempty)
2579                 return true;
2580         return ownerCode() == Inset::ERT_CODE || ownerCode() == Inset::LISTINGS_CODE;
2581 }
2582
2583
2584 char_type Paragraph::transformChar(char_type c, pos_type pos) const
2585 {
2586         if (!Encodings::is_arabic(c))
2587                 return c;
2588
2589         value_type prev_char = ' ';
2590         value_type next_char = ' ';
2591
2592         for (pos_type i = pos - 1; i >= 0; --i) {
2593                 value_type const par_char = getChar(i);
2594                 if (!Encodings::isComposeChar_arabic(par_char)) {
2595                         prev_char = par_char;
2596                         break;
2597                 }
2598         }
2599
2600         for (pos_type i = pos + 1, end = size(); i < end; ++i) {
2601                 value_type const par_char = getChar(i);
2602                 if (!Encodings::isComposeChar_arabic(par_char)) {
2603                         next_char = par_char;
2604                         break;
2605                 }
2606         }
2607
2608         if (Encodings::is_arabic(next_char)) {
2609                 if (Encodings::is_arabic(prev_char) &&
2610                         !Encodings::is_arabic_special(prev_char))
2611                         return Encodings::transformChar(c, Encodings::FORM_MEDIAL);
2612                 else
2613                         return Encodings::transformChar(c, Encodings::FORM_INITIAL);
2614         } else {
2615                 if (Encodings::is_arabic(prev_char) &&
2616                         !Encodings::is_arabic_special(prev_char))
2617                         return Encodings::transformChar(c, Encodings::FORM_FINAL);
2618                 else
2619                         return Encodings::transformChar(c, Encodings::FORM_ISOLATED);
2620         }
2621 }
2622
2623
2624 bool Paragraph::hfillExpansion(Row const & row, pos_type pos) const
2625 {
2626         if (!isHfill(pos))
2627                 return false;
2628
2629         BOOST_ASSERT(pos >= row.pos() && pos < row.endpos());
2630
2631         // expand at the end of a row only if there is another hfill on the same row
2632         if (pos == row.endpos() - 1) {
2633                 for (pos_type i = row.pos(); i < pos; i++) {
2634                         if (isHfill(i))
2635                                 return true;
2636                 }
2637                 return false;
2638         }
2639
2640         // expand at the beginning of a row only if it is the first row of a paragraph
2641         if (pos == row.pos()) {
2642                 return pos == 0;
2643         }
2644
2645         // do not expand in some labels
2646         if (layout()->margintype != MARGIN_MANUAL && pos < beginOfBody())
2647                 return false;
2648
2649         // if there is anything between the first char of the row and
2650         // the specified position that is neither a newline nor an hfill,
2651         // the hfill will be expanded, otherwise it won't
2652         for (pos_type i = row.pos(); i < pos; i++) {
2653                 if (!isNewline(i) && !isHfill(i))
2654                         return true;
2655         }
2656         return false;
2657 }
2658
2659
2660 int Paragraph::checkBiblio(bool track_changes)
2661 {
2662         //FIXME From JS:
2663         //This is getting more and more a mess. ...We really should clean
2664         //up this bibitem issue for 1.6. See also bug 2743.
2665
2666         // Add bibitem insets if necessary
2667         if (layout()->labeltype != LABEL_BIBLIO)
2668                 return 0;
2669
2670         bool hasbibitem = !insetlist.empty()
2671                 // Insist on it being in pos 0
2672                 && getChar(0) == Paragraph::META_INSET
2673                 && insetlist.begin()->inset->lyxCode() == Inset::BIBITEM_CODE;
2674
2675         docstring oldkey;
2676         docstring oldlabel;
2677
2678         // remove a bibitem in pos != 0
2679         // restore it later in pos 0 if necessary
2680         // (e.g. if a user inserts contents _before_ the item)
2681         // we're assuming there's only one of these, which there
2682         // should be.
2683         int erasedInsetPosition = -1;
2684         InsetList::iterator it = insetlist.begin();
2685         InsetList::iterator end = insetlist.end();
2686         for (; it != end; ++it)
2687                 if (it->inset->lyxCode() == Inset::BIBITEM_CODE
2688                     && it->pos > 0) {
2689                         InsetBibitem * olditem = static_cast<InsetBibitem *>(it->inset);
2690                         oldkey = olditem->getParam("key");
2691                         oldlabel = olditem->getParam("label");
2692                         erasedInsetPosition = it->pos;
2693                         eraseChar(erasedInsetPosition, track_changes);
2694                         break;
2695         }
2696
2697         //There was an InsetBibitem at the beginning, and we didn't
2698         //have to erase one.
2699         if (hasbibitem && erasedInsetPosition < 0)
2700                         return 0;
2701
2702         //There was an InsetBibitem at the beginning and we did have to
2703         //erase one. So we give its properties to the beginning inset.
2704         if (hasbibitem) {
2705                 InsetBibitem * inset =
2706                         static_cast<InsetBibitem *>(insetlist.begin()->inset);
2707                 if (!oldkey.empty())
2708                         inset->setParam("key", oldkey);
2709                 inset->setParam("label", oldlabel);
2710                 return -erasedInsetPosition;
2711         }
2712
2713         //There was no inset at the beginning, so we need to create one with
2714         //the key and label of the one we erased.
2715         InsetBibitem * inset(new InsetBibitem(InsetCommandParams("bibitem")));
2716         // restore values of previously deleted item in this par.
2717         if (!oldkey.empty())
2718                 inset->setParam("key", oldkey);
2719         inset->setParam("label", oldlabel);
2720         insertInset(0, static_cast<Inset *>(inset),
2721                     Change(track_changes ? Change::INSERTED : Change::UNCHANGED));
2722
2723         return 1;
2724 }
2725
2726
2727 void Paragraph::checkAuthors(AuthorList const & authorList)
2728 {
2729         pimpl_->changes_.checkAuthors(authorList);
2730 }
2731
2732 } // namespace lyx