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