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