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