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