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