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