]> git.lyx.org Git - lyx.git/blob - src/Paragraph.cpp
Restor 1.4.x behaviour: Don't touch the preamble QTextEdit if the preamble text is...
[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, basefont, basefont);
706                                         open_font = false;
707                                 }
708
709                                 if (running_font.family() == Font::TYPEWRITER_FAMILY)
710                                         os << '~';
711
712                                 basefont = owner_->getLayoutFont(bparams, outerfont);
713                                 running_font = basefont;
714
715                                 if (runparams.moving_arg)
716                                         os << "\\protect ";
717
718                                 os << "\\\\\n";
719                         }
720                         texrow.newline();
721                         texrow.start(owner_->id(), i + 1);
722                         column = 0;
723                         break;
724                 }
725
726                 // output change tracking marks only if desired,
727                 // if dvipost is installed,
728                 // and with dvi/ps (other formats don't work)
729                 LaTeXFeatures features(buf, bparams, runparams);
730                 bool const output = bparams.outputChanges
731                         && runparams.flavor == OutputParams::LATEX
732                         && features.isAvailable("dvipost");
733
734                 if (inset->canTrackChanges()) {
735                         column += Changes::latexMarkChange(os, running_change,
736                                 Change::UNCHANGED, output);
737                         running_change = Change::UNCHANGED;
738                 }
739
740                 bool close = false;
741                 odocstream::pos_type const len = os.tellp();
742
743                 if ((inset->lyxCode() == Inset::GRAPHICS_CODE
744                      || inset->lyxCode() == Inset::MATH_CODE
745                      || inset->lyxCode() == Inset::URL_CODE)
746                     && running_font.isRightToLeft()) {
747                         os << "\\L{";
748                         close = true;
749                 }
750
751 #ifdef WITH_WARNINGS
752 #warning Bug: we can have an empty font change here!
753 // if there has just been a font change, we are going to close it
754 // right now, which means stupid latex code like \textsf{}. AFAIK,
755 // this does not harm dvi output. A minor bug, thus (JMarc)
756 #endif
757                 // some insets cannot be inside a font change command
758                 if (open_font && inset->noFontChange()) {
759                         column += running_font.latexWriteEndChanges(
760                                         os, basefont, basefont);
761                         open_font = false;
762                         basefont = owner_->getLayoutFont(bparams, outerfont);
763                         running_font = basefont;
764                 }
765
766                 int tmp = inset->latex(buf, os, runparams);
767
768                 if (close)
769                         os << '}';
770
771                 if (tmp) {
772                         for (int j = 0; j < tmp; ++j) {
773                                 texrow.newline();
774                         }
775                         texrow.start(owner_->id(), i + 1);
776                         column = 0;
777                 } else {
778                         column += os.tellp() - len;
779                 }
780         }
781         break;
782
783         default:
784                 // And now for the special cases within each mode
785
786                 switch (c) {
787                 case '\\':
788                         os << "\\textbackslash{}";
789                         column += 15;
790                         break;
791
792                 case '|': case '<': case '>':
793                         // In T1 encoding, these characters exist
794                         if (lyxrc.fontenc == "T1") {
795                                 os.put(c);
796                                 //... but we should avoid ligatures
797                                 if ((c == '>' || c == '<')
798                                     && i <= size() - 2
799                                     && getChar(i + 1) == c) {
800                                         //os << "\\textcompwordmark{}";
801                                         //column += 19;
802                                         // Jean-Marc, have a look at
803                                         // this. I think this works
804                                         // equally well:
805                                         os << "\\,{}";
806                                         // Lgb
807                                         column += 3;
808                                 }
809                                 break;
810                         }
811                         // Typewriter font also has them
812                         if (running_font.family() == Font::TYPEWRITER_FAMILY) {
813                                 os.put(c);
814                                 break;
815                         }
816                         // Otherwise, we use what LaTeX
817                         // provides us.
818                         switch (c) {
819                         case '<':
820                                 os << "\\textless{}";
821                                 column += 10;
822                                 break;
823                         case '>':
824                                 os << "\\textgreater{}";
825                                 column += 13;
826                                 break;
827                         case '|':
828                                 os << "\\textbar{}";
829                                 column += 9;
830                                 break;
831                         }
832                         break;
833
834                 case '-': // "--" in Typewriter mode -> "-{}-"
835                         if (i <= size() - 2 &&
836                             getChar(i + 1) == '-' &&
837                             running_font.family() == Font::TYPEWRITER_FAMILY) {
838                                 os << "-{}";
839                                 column += 2;
840                         } else {
841                                 os << '-';
842                         }
843                         break;
844
845                 case '\"':
846                         os << "\\char`\\\"{}";
847                         column += 9;
848                         break;
849
850                 case '$': case '&':
851                 case '%': case '#': case '{':
852                 case '}': case '_':
853                         os << '\\';
854                         os.put(c);
855                         column += 1;
856                         break;
857
858                 case '~':
859                         os << "\\textasciitilde{}";
860                         column += 16;
861                         break;
862
863                 case '^':
864                         os << "\\textasciicircum{}";
865                         column += 17;
866                         break;
867
868                 case '*': case '[':
869                         // avoid being mistaken for optional arguments
870                         os << '{';
871                         os.put(c);
872                         os << '}';
873                         column += 2;
874                         break;
875
876                 case ' ':
877                         // Blanks are printed before font switching.
878                         // Sure? I am not! (try nice-latex)
879                         // I am sure it's correct. LyX might be smarter
880                         // in the future, but for now, nothing wrong is
881                         // written. (Asger)
882                         break;
883
884                 default:
885
886                         // I assume this is hack treating typewriter as verbatim
887                         // FIXME UNICODE: This can fail if c cannot be encoded
888                         // in the current encoding.
889                         if (running_font.family() == Font::TYPEWRITER_FAMILY) {
890                                 if (c != '\0') {
891                                         os.put(c);
892                                 }
893                                 break;
894                         }
895
896                         // LyX, LaTeX etc.
897
898                         // FIXME: if we have "LaTeX" with a font
899                         // change in the middle (before the 'T', then
900                         // the "TeX" part is still special cased.
901                         // Really we should only operate this on
902                         // "words" for some definition of word
903
904                         size_t pnr = 0;
905
906                         for (; pnr < phrases_nr; ++pnr) {
907                                 if (isTextAt(special_phrases[pnr].phrase, i)) {
908                                         os << special_phrases[pnr].macro;
909                                         i += special_phrases[pnr].phrase.length() - 1;
910                                         column += special_phrases[pnr].macro.length() - 1;
911                                         break;
912                                 }
913                         }
914
915                         if (pnr == phrases_nr && c != '\0') {
916                                 Encoding const & encoding = *(runparams.encoding);
917                                 if (i < size() - 1) {
918                                         char_type next = getChar(i + 1);
919                                         if (Encodings::isCombiningChar(next)) {
920                                                 column += latexSurrogatePair(os, c, next, encoding) - 1;
921                                                 ++i;
922                                                 break;
923                                         }
924                                 }
925                                 docstring const latex = encoding.latexChar(c);
926                                 if (latex.length() > 1 &&
927                                     latex[latex.length() - 1] != '}') {
928                                         // Prevent eating of a following
929                                         // space or command corruption by
930                                         // following characters
931                                         column += latex.length() + 1;
932                                         os << latex << "{}";
933                                 } else {
934                                         column += latex.length() - 1;
935                                         os << latex;
936                                 }
937                         }
938                         break;
939                 }
940         }
941 }
942
943
944 void Paragraph::Pimpl::validate(LaTeXFeatures & features,
945                                 Layout const & layout) const
946 {
947         BufferParams const & bparams = features.bufferParams();
948
949         // check the params.
950         if (!params.spacing().isDefault())
951                 features.require("setspace");
952
953         // then the layouts
954         features.useLayout(layout.name());
955
956         // then the fonts
957         Language const * doc_language = bparams.language;
958
959         FontList::const_iterator fcit = fontlist.begin();
960         FontList::const_iterator fend = fontlist.end();
961         for (; fcit != fend; ++fcit) {
962                 if (fcit->font().noun() == Font::ON) {
963                         LYXERR(Debug::LATEX) << "font.noun: "
964                                              << fcit->font().noun()
965                                              << endl;
966                         features.require("noun");
967                         LYXERR(Debug::LATEX) << "Noun enabled. Font: "
968                                              << to_utf8(fcit->font().stateText(0))
969                                              << endl;
970                 }
971                 switch (fcit->font().color()) {
972                 case Color::none:
973                 case Color::inherit:
974                 case Color::ignore:
975                         // probably we should put here all interface colors used for
976                         // font displaying! For now I just add this ones I know of (Jug)
977                 case Color::latex:
978                 case Color::note:
979                         break;
980                 default:
981                         features.require("color");
982                         LYXERR(Debug::LATEX) << "Color enabled. Font: "
983                                              << to_utf8(fcit->font().stateText(0))
984                                              << endl;
985                 }
986
987                 Language const * language = fcit->font().language();
988                 if (language->babel() != doc_language->babel() &&
989                     language != ignore_language &&
990                     language != latex_language)
991                 {
992                         features.useLanguage(language);
993                         LYXERR(Debug::LATEX) << "Found language "
994                                              << language->babel() << endl;
995                 }
996         }
997
998         if (!params.leftIndent().zero())
999                 features.require("ParagraphLeftIndent");
1000
1001         // then the insets
1002         InsetList::const_iterator icit = owner_->insetlist.begin();
1003         InsetList::const_iterator iend = owner_->insetlist.end();
1004         for (; icit != iend; ++icit) {
1005                 if (icit->inset) {
1006                         icit->inset->validate(features);
1007                         if (layout.needprotect &&
1008                             icit->inset->lyxCode() == Inset::FOOT_CODE)
1009                                 features.require("NeedLyXFootnoteCode");
1010                 }
1011         }
1012
1013         // then the contents
1014         for (pos_type i = 0; i < size() ; ++i) {
1015                 for (size_t pnr = 0; pnr < phrases_nr; ++pnr) {
1016                         if (!special_phrases[pnr].builtin
1017                             && isTextAt(special_phrases[pnr].phrase, i)) {
1018                                 features.require(special_phrases[pnr].phrase);
1019                                 break;
1020                         }
1021                 }
1022                 Encodings::validate(getChar(i), features);
1023         }
1024 }
1025
1026
1027 } // namespace lyx
1028
1029
1030 /////////////////////////////////////////////////////////////////////
1031 //
1032 // Paragraph
1033 //
1034 /////////////////////////////////////////////////////////////////////
1035
1036 namespace lyx {
1037
1038 Paragraph::Paragraph()
1039         : begin_of_body_(0), pimpl_(new Paragraph::Pimpl(this))
1040 {
1041         itemdepth = 0;
1042         params().clear();
1043 }
1044
1045
1046 Paragraph::Paragraph(Paragraph const & par)
1047         : itemdepth(par.itemdepth), insetlist(par.insetlist),
1048         layout_(par.layout_),
1049         text_(par.text_), begin_of_body_(par.begin_of_body_),
1050         pimpl_(new Paragraph::Pimpl(*par.pimpl_, this))
1051 {
1052         //lyxerr << "Paragraph::Paragraph(Paragraph const&)" << endl;
1053         InsetList::iterator it = insetlist.begin();
1054         InsetList::iterator end = insetlist.end();
1055         for (; it != end; ++it)
1056                 it->inset = it->inset->clone().release();
1057 }
1058
1059
1060 Paragraph & Paragraph::operator=(Paragraph const & par)
1061 {
1062         // needed as we will destroy the pimpl_ before copying it
1063         if (&par != this) {
1064                 itemdepth = par.itemdepth;
1065
1066                 insetlist = par.insetlist;
1067                 InsetList::iterator it = insetlist.begin();
1068                 InsetList::iterator end = insetlist.end();
1069                 for (; it != end; ++it)
1070                         it->inset = it->inset->clone().release();
1071
1072                 layout_ = par.layout();
1073                 text_ = par.text_;
1074                 begin_of_body_ = par.begin_of_body_;
1075
1076                 delete pimpl_;
1077                 pimpl_ = new Pimpl(*par.pimpl_, this);
1078         }
1079         return *this;
1080 }
1081
1082
1083 Paragraph::~Paragraph()
1084 {
1085         delete pimpl_;
1086         //
1087         //lyxerr << "Paragraph::paragraph_id = "
1088         //       << Paragraph::paragraph_id << endl;
1089 }
1090
1091
1092 void Paragraph::write(Buffer const & buf, ostream & os,
1093                           BufferParams const & bparams,
1094                           depth_type & dth) const
1095 {
1096         // The beginning or end of a deeper (i.e. nested) area?
1097         if (dth != params().depth()) {
1098                 if (params().depth() > dth) {
1099                         while (params().depth() > dth) {
1100                                 os << "\n\\begin_deeper";
1101                                 ++dth;
1102                         }
1103                 } else {
1104                         while (params().depth() < dth) {
1105                                 os << "\n\\end_deeper";
1106                                 --dth;
1107                         }
1108                 }
1109         }
1110
1111         // First write the layout
1112         os << "\n\\begin_layout " << layout()->name() << '\n';
1113
1114         params().write(os);
1115
1116         Font font1(Font::ALL_INHERIT, bparams.language);
1117
1118         Change running_change = Change(Change::UNCHANGED);
1119
1120         int column = 0;
1121         for (pos_type i = 0; i <= size(); ++i) {
1122
1123                 Change change = pimpl_->lookupChange(i);
1124                 Changes::lyxMarkChange(os, column, running_change, change);
1125                 running_change = change;
1126
1127                 if (i == size())
1128                         break;
1129
1130                 // Write font changes
1131                 Font font2 = getFontSettings(bparams, i);
1132                 if (font2 != font1) {
1133                         font2.lyxWriteChanges(font1, os);
1134                         column = 0;
1135                         font1 = font2;
1136                 }
1137
1138                 value_type const c = getChar(i);
1139                 switch (c) {
1140                 case META_INSET:
1141                 {
1142                         Inset const * inset = getInset(i);
1143                         if (inset)
1144                                 if (inset->directWrite()) {
1145                                         // international char, let it write
1146                                         // code directly so it's shorter in
1147                                         // the file
1148                                         inset->write(buf, os);
1149                                 } else {
1150                                         if (i)
1151                                                 os << '\n';
1152                                         os << "\\begin_inset ";
1153                                         inset->write(buf, os);
1154                                         os << "\n\\end_inset\n\n";
1155                                         column = 0;
1156                                 }
1157                 }
1158                 break;
1159                 case '\\':
1160                         os << "\n\\backslash\n";
1161                         column = 0;
1162                         break;
1163                 case '.':
1164                         if (i + 1 < size() && getChar(i + 1) == ' ') {
1165                                 os << ".\n";
1166                                 column = 0;
1167                         } else
1168                                 os << '.';
1169                         break;
1170                 default:
1171                         if ((column > 70 && c == ' ')
1172                             || column > 79) {
1173                                 os << '\n';
1174                                 column = 0;
1175                         }
1176                         // this check is to amend a bug. LyX sometimes
1177                         // inserts '\0' this could cause problems.
1178                         if (c != '\0') {
1179                                 std::vector<char> tmp = ucs4_to_utf8(c);
1180                                 tmp.push_back('\0');
1181                                 os << &tmp[0];
1182                         } else
1183                                 lyxerr << "ERROR (Paragraph::writeFile):"
1184                                         " NULL char in structure." << endl;
1185                         ++column;
1186                         break;
1187                 }
1188         }
1189
1190         os << "\n\\end_layout\n";
1191 }
1192
1193
1194 void Paragraph::validate(LaTeXFeatures & features) const
1195 {
1196         pimpl_->validate(features, *layout());
1197 }
1198
1199
1200 bool Paragraph::eraseChar(pos_type pos, bool trackChanges)
1201 {
1202         return pimpl_->eraseChar(pos, trackChanges);
1203 }
1204
1205
1206 int Paragraph::eraseChars(pos_type start, pos_type end, bool trackChanges)
1207 {
1208         return pimpl_->eraseChars(start, end, trackChanges);
1209 }
1210
1211
1212 void Paragraph::insert(pos_type start, docstring const & str,
1213                        Font const & font, Change const & change)
1214 {
1215         for (size_t i = 0, n = str.size(); i != n ; ++i)
1216                 insertChar(start + i, str[i], font, change);
1217 }
1218
1219
1220 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
1221                            bool trackChanges)
1222 {
1223         pimpl_->insertChar(pos, c, Change(trackChanges ?
1224                            Change::INSERTED : Change::UNCHANGED));
1225 }
1226
1227
1228 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
1229                            Font const & font, bool trackChanges)
1230 {
1231         pimpl_->insertChar(pos, c, Change(trackChanges ?
1232                            Change::INSERTED : Change::UNCHANGED));
1233         setFont(pos, font);
1234 }
1235
1236
1237 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
1238                            Font const & font, Change const & change)
1239 {
1240         pimpl_->insertChar(pos, c, change);
1241         setFont(pos, font);
1242 }
1243
1244
1245 void Paragraph::insertInset(pos_type pos, Inset * inset,
1246                             Change const & change)
1247 {
1248         pimpl_->insertInset(pos, inset, change);
1249 }
1250
1251
1252 void Paragraph::insertInset(pos_type pos, Inset * inset,
1253                             Font const & font, Change const & change)
1254 {
1255         pimpl_->insertInset(pos, inset, change);
1256         setFont(pos, font);
1257 }
1258
1259
1260 bool Paragraph::insetAllowed(Inset_code code)
1261 {
1262         return !pimpl_->inset_owner || pimpl_->inset_owner->insetAllowed(code);
1263 }
1264
1265
1266 // Gets uninstantiated font setting at position.
1267 Font const Paragraph::getFontSettings(BufferParams const & bparams,
1268                                          pos_type pos) const
1269 {
1270         if (pos > size()) {
1271                 lyxerr << " pos: " << pos << " size: " << size() << endl;
1272                 BOOST_ASSERT(pos <= size());
1273         }
1274
1275         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1276         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
1277         for (; cit != end; ++cit)
1278                 if (cit->pos() >= pos)
1279                         break;
1280
1281         if (cit != end)
1282                 return cit->font();
1283
1284         if (pos == size() && !empty())
1285                 return getFontSettings(bparams, pos - 1);
1286
1287         return Font(Font::ALL_INHERIT, getParLanguage(bparams));
1288 }
1289
1290
1291 FontSpan Paragraph::fontSpan(pos_type pos) const
1292 {
1293         BOOST_ASSERT(pos <= size());
1294         pos_type start = 0;
1295
1296         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1297         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
1298         for (; cit != end; ++cit) {
1299                 if (cit->pos() >= pos) {
1300                         if (pos >= beginOfBody())
1301                                 return FontSpan(std::max(start, beginOfBody()),
1302                                                 cit->pos());
1303                         else
1304                                 return FontSpan(start,
1305                                                 std::min(beginOfBody() - 1,
1306                                                          cit->pos()));
1307                 }
1308                 start = cit->pos() + 1;
1309         }
1310
1311         // This should not happen, but if so, we take no chances.
1312         //lyxerr << "Paragraph::getEndPosOfFontSpan: This should not happen!"
1313         //      << endl;
1314         return FontSpan(pos, pos);
1315 }
1316
1317
1318 // Gets uninstantiated font setting at position 0
1319 Font const Paragraph::getFirstFontSettings(BufferParams const & bparams) const
1320 {
1321         if (!empty() && !pimpl_->fontlist.empty())
1322                 return pimpl_->fontlist[0].font();
1323
1324         return Font(Font::ALL_INHERIT, bparams.language);
1325 }
1326
1327
1328 // Gets the fully instantiated font at a given position in a paragraph
1329 // This is basically the same function as Text::GetFont() in text2.cpp.
1330 // The difference is that this one is used for generating the LaTeX file,
1331 // and thus cosmetic "improvements" are disallowed: This has to deliver
1332 // the true picture of the buffer. (Asger)
1333 Font const Paragraph::getFont(BufferParams const & bparams, pos_type pos,
1334                                  Font const & outerfont) const
1335 {
1336         BOOST_ASSERT(pos >= 0);
1337
1338         Layout_ptr const & lout = layout();
1339
1340         pos_type const body_pos = beginOfBody();
1341
1342         Font layoutfont;
1343         if (pos < body_pos)
1344                 layoutfont = lout->labelfont;
1345         else
1346                 layoutfont = lout->font;
1347
1348         Font font = getFontSettings(bparams, pos);
1349         font.realize(layoutfont);
1350         font.realize(outerfont);
1351         font.realize(bparams.getFont());
1352
1353         return font;
1354 }
1355
1356
1357 Font const Paragraph::getLabelFont
1358         (BufferParams const & bparams, Font const & outerfont) const
1359 {
1360         Font tmpfont = layout()->labelfont;
1361         tmpfont.setLanguage(getParLanguage(bparams));
1362         tmpfont.realize(outerfont);
1363         tmpfont.realize(bparams.getFont());
1364         return tmpfont;
1365 }
1366
1367
1368 Font const Paragraph::getLayoutFont
1369         (BufferParams const & bparams, Font const & outerfont) const
1370 {
1371         Font tmpfont = layout()->font;
1372         tmpfont.setLanguage(getParLanguage(bparams));
1373         tmpfont.realize(outerfont);
1374         tmpfont.realize(bparams.getFont());
1375         return tmpfont;
1376 }
1377
1378
1379 /// Returns the height of the highest font in range
1380 Font_size Paragraph::highestFontInRange
1381         (pos_type startpos, pos_type endpos, Font_size def_size) const
1382 {
1383         if (pimpl_->fontlist.empty())
1384                 return def_size;
1385
1386         Pimpl::FontList::const_iterator end_it = pimpl_->fontlist.begin();
1387         Pimpl::FontList::const_iterator const end = pimpl_->fontlist.end();
1388         for (; end_it != end; ++end_it) {
1389                 if (end_it->pos() >= endpos)
1390                         break;
1391         }
1392
1393         if (end_it != end)
1394                 ++end_it;
1395
1396         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
1397         for (; cit != end; ++cit) {
1398                 if (cit->pos() >= startpos)
1399                         break;
1400         }
1401
1402         Font::FONT_SIZE maxsize = Font::SIZE_TINY;
1403         for (; cit != end_it; ++cit) {
1404                 Font::FONT_SIZE size = cit->font().size();
1405                 if (size == Font::INHERIT_SIZE)
1406                         size = def_size;
1407                 if (size > maxsize && size <= Font::SIZE_HUGER)
1408                         maxsize = size;
1409         }
1410         return maxsize;
1411 }
1412
1413
1414 Paragraph::value_type
1415 Paragraph::getUChar(BufferParams const & bparams, pos_type pos) const
1416 {
1417         value_type c = getChar(pos);
1418         if (!lyxrc.rtl_support)
1419                 return c;
1420
1421         value_type uc = c;
1422         switch (c) {
1423         case '(':
1424                 uc = ')';
1425                 break;
1426         case ')':
1427                 uc = '(';
1428                 break;
1429         case '[':
1430                 uc = ']';
1431                 break;
1432         case ']':
1433                 uc = '[';
1434                 break;
1435         case '{':
1436                 uc = '}';
1437                 break;
1438         case '}':
1439                 uc = '{';
1440                 break;
1441         case '<':
1442                 uc = '>';
1443                 break;
1444         case '>':
1445                 uc = '<';
1446                 break;
1447         }
1448         if (uc != c && getFontSettings(bparams, pos).isRightToLeft())
1449                 return uc;
1450         else
1451                 return c;
1452 }
1453
1454
1455 void Paragraph::setFont(pos_type pos, Font const & font)
1456 {
1457         BOOST_ASSERT(pos <= size());
1458
1459         // First, reduce font against layout/label font
1460         // Update: The setCharFont() routine in text2.cpp already
1461         // reduces font, so we don't need to do that here. (Asger)
1462         // No need to simplify this because it will disappear
1463         // in a new kernel. (Asger)
1464         // Next search font table
1465
1466         Pimpl::FontList::iterator beg = pimpl_->fontlist.begin();
1467         Pimpl::FontList::iterator it = beg;
1468         Pimpl::FontList::iterator endit = pimpl_->fontlist.end();
1469         for (; it != endit; ++it) {
1470                 if (it->pos() >= pos)
1471                         break;
1472         }
1473         size_t const i = distance(beg, it);
1474         bool notfound = (it == endit);
1475
1476         if (!notfound && pimpl_->fontlist[i].font() == font)
1477                 return;
1478
1479         bool begin = pos == 0 || notfound ||
1480                 (i > 0 && pimpl_->fontlist[i - 1].pos() == pos - 1);
1481         // Is position pos is a beginning of a font block?
1482         bool end = !notfound && pimpl_->fontlist[i].pos() == pos;
1483         // Is position pos is the end of a font block?
1484         if (begin && end) { // A single char block
1485                 if (i + 1 < pimpl_->fontlist.size() &&
1486                     pimpl_->fontlist[i + 1].font() == font) {
1487                         // Merge the singleton block with the next block
1488                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
1489                         if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
1490                                 pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i - 1);
1491                 } else if (i > 0 && pimpl_->fontlist[i - 1].font() == font) {
1492                         // Merge the singleton block with the previous block
1493                         pimpl_->fontlist[i - 1].pos(pos);
1494                         pimpl_->fontlist.erase(pimpl_->fontlist.begin() + i);
1495                 } else
1496                         pimpl_->fontlist[i].font(font);
1497         } else if (begin) {
1498                 if (i > 0 && pimpl_->fontlist[i - 1].font() == font)
1499                         pimpl_->fontlist[i - 1].pos(pos);
1500                 else
1501                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
1502                                         Pimpl::FontTable(pos, font));
1503         } else if (end) {
1504                 pimpl_->fontlist[i].pos(pos - 1);
1505                 if (!(i + 1 < pimpl_->fontlist.size() &&
1506                       pimpl_->fontlist[i + 1].font() == font))
1507                         pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
1508                                         Pimpl::FontTable(pos, font));
1509         } else { // The general case. The block is splitted into 3 blocks
1510                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i,
1511                                 Pimpl::FontTable(pos - 1, pimpl_->fontlist[i].font()));
1512                 pimpl_->fontlist.insert(pimpl_->fontlist.begin() + i + 1,
1513                                 Pimpl::FontTable(pos, font));
1514         }
1515 }
1516
1517
1518 void Paragraph::makeSameLayout(Paragraph const & par)
1519 {
1520         layout(par.layout());
1521         // move to pimpl?
1522         params() = par.params();
1523 }
1524
1525
1526 bool Paragraph::stripLeadingSpaces(bool trackChanges)
1527 {
1528         if (isFreeSpacing())
1529                 return false;
1530
1531         int pos = 0;
1532         int count = 0;
1533
1534         while (pos < size() && (isNewline(pos) || isLineSeparator(pos))) {
1535                 if (eraseChar(pos, trackChanges))
1536                         ++count;
1537                 else
1538                         ++pos;
1539         }
1540
1541         return count > 0 || pos > 0;
1542 }
1543
1544
1545 bool Paragraph::hasSameLayout(Paragraph const & par) const
1546 {
1547         return par.layout() == layout() && params().sameLayout(par.params());
1548 }
1549
1550
1551 depth_type Paragraph::getDepth() const
1552 {
1553         return params().depth();
1554 }
1555
1556
1557 depth_type Paragraph::getMaxDepthAfter() const
1558 {
1559         if (layout()->isEnvironment())
1560                 return params().depth() + 1;
1561         else
1562                 return params().depth();
1563 }
1564
1565
1566 char Paragraph::getAlign() const
1567 {
1568         if (params().align() == LYX_ALIGN_LAYOUT)
1569                 return layout()->align;
1570         else
1571                 return params().align();
1572 }
1573
1574
1575 docstring const & Paragraph::getLabelstring() const
1576 {
1577         return params().labelString();
1578 }
1579
1580
1581 // the next two functions are for the manual labels
1582 docstring const Paragraph::getLabelWidthString() const
1583 {
1584         if (!params().labelWidthString().empty())
1585                 return params().labelWidthString();
1586         else
1587                 return _("Senseless with this layout!");
1588 }
1589
1590
1591 void Paragraph::setLabelWidthString(docstring const & s)
1592 {
1593         params().labelWidthString(s);
1594 }
1595
1596
1597 docstring const Paragraph::translateIfPossible(docstring const & s,
1598                 BufferParams const & bparams) const
1599 {
1600         if (!support::isAscii(s) || s.empty()) {
1601                 // This must be a user defined layout. We cannot translate
1602                 // this, since gettext accepts only ascii keys.
1603                 return s;
1604         }
1605         // Probably standard layout, try to translate
1606         Messages & m = getMessages(getParLanguage(bparams)->code());
1607         return m.get(to_ascii(s));
1608 }
1609
1610
1611 docstring Paragraph::expandLabel(Layout_ptr const & layout,
1612                 BufferParams const & bparams, bool process_appendix) const
1613 {
1614         TextClass const & tclass = bparams.getTextClass();
1615
1616         docstring fmt;
1617         if (process_appendix && params().appendix())
1618                 fmt = translateIfPossible(layout->labelstring_appendix(),
1619                         bparams);
1620         else
1621                 fmt = translateIfPossible(layout->labelstring(), bparams);
1622
1623         // handle 'inherited level parts' in 'fmt',
1624         // i.e. the stuff between '@' in   '@Section@.\arabic{subsection}'
1625         size_t const i = fmt.find('@', 0);
1626         if (i != docstring::npos) {
1627                 size_t const j = fmt.find('@', i + 1);
1628                 if (j != docstring::npos) {
1629                         docstring parent(fmt, i + 1, j - i - 1);
1630                         // FIXME UNICODE
1631                         docstring label = expandLabel(tclass[to_utf8(parent)], bparams);
1632                         fmt = docstring(fmt, 0, i) + label + docstring(fmt, j + 1, docstring::npos);
1633                 }
1634         }
1635
1636         return tclass.counters().counterLabel(fmt);
1637 }
1638
1639
1640 void Paragraph::applyLayout(Layout_ptr const & new_layout)
1641 {
1642         layout(new_layout);
1643         params().labelWidthString(docstring());
1644         params().align(LYX_ALIGN_LAYOUT);
1645         params().spacing(Spacing(Spacing::Default));
1646 }
1647
1648
1649 pos_type Paragraph::beginOfBody() const
1650 {
1651         return begin_of_body_;
1652 }
1653
1654
1655 void Paragraph::setBeginOfBody()
1656 {
1657         if (layout()->labeltype != LABEL_MANUAL) {
1658                 begin_of_body_ = 0;
1659                 return;
1660         }
1661
1662         // Unroll the first two cycles of the loop
1663         // and remember the previous character to
1664         // remove unnecessary getChar() calls
1665         pos_type i = 0;
1666         pos_type end = size();
1667         if (i < end && !isNewline(i)) {
1668                 ++i;
1669                 char_type previous_char = 0;
1670                 char_type temp = 0;
1671                 if (i < end) {
1672                         previous_char = text_[i];
1673                         if (!isNewline(i)) {
1674                                 ++i;
1675                                 while (i < end && previous_char != ' ') {
1676                                         temp = text_[i];
1677                                         if (isNewline(i))
1678                                                 break;
1679                                         ++i;
1680                                         previous_char = temp;
1681                                 }
1682                         }
1683                 }
1684         }
1685
1686         begin_of_body_ = i;
1687 }
1688
1689
1690 // returns -1 if inset not found
1691 int Paragraph::getPositionOfInset(Inset const * inset) const
1692 {
1693         // Find the entry.
1694         InsetList::const_iterator it = insetlist.begin();
1695         InsetList::const_iterator end = insetlist.end();
1696         for (; it != end; ++it)
1697                 if (it->inset == inset)
1698                         return it->pos;
1699         return -1;
1700 }
1701
1702
1703 InsetBibitem * Paragraph::bibitem() const
1704 {
1705         if (!insetlist.empty()) {
1706                 Inset * inset = insetlist.begin()->inset;
1707                 if (inset->lyxCode() == Inset::BIBITEM_CODE)
1708                         return static_cast<InsetBibitem *>(inset);
1709         }
1710         return 0;
1711 }
1712
1713
1714 bool Paragraph::forceDefaultParagraphs() const
1715 {
1716         return inInset() && inInset()->forceDefaultParagraphs(0);
1717 }
1718
1719
1720 namespace {
1721
1722 // paragraphs inside floats need different alignment tags to avoid
1723 // unwanted space
1724
1725 bool noTrivlistCentering(Inset::Code code)
1726 {
1727         return code == Inset::FLOAT_CODE || code == Inset::WRAP_CODE;
1728 }
1729
1730
1731 string correction(string const & orig)
1732 {
1733         if (orig == "flushleft")
1734                 return "raggedright";
1735         if (orig == "flushright")
1736                 return "raggedleft";
1737         if (orig == "center")
1738                 return "centering";
1739         return orig;
1740 }
1741
1742
1743 string const corrected_env(string const & suffix, string const & env,
1744         Inset::Code code)
1745 {
1746         string output = suffix + "{";
1747         if (noTrivlistCentering(code))
1748                 output += correction(env);
1749         else
1750                 output += env;
1751         output += "}";
1752         if (suffix == "\\begin")
1753                 output += "\n";
1754         return output;
1755 }
1756
1757
1758 void adjust_row_column(string const & str, TexRow & texrow, int & column)
1759 {
1760         if (!contains(str, "\n"))
1761                 column += str.size();
1762         else {
1763                 string tmp;
1764                 texrow.newline();
1765                 column = rsplit(str, tmp, '\n').size();
1766         }
1767 }
1768
1769 } // namespace anon
1770
1771
1772 // This could go to ParagraphParameters if we want to
1773 int Paragraph::startTeXParParams(BufferParams const & bparams,
1774                                  odocstream & os, TexRow & texrow, 
1775                                  bool moving_arg) const
1776 {
1777         int column = 0;
1778
1779         if (params().noindent()) {
1780                 os << "\\noindent ";
1781                 column += 10;
1782         }
1783
1784         switch (params().align()) {
1785         case LYX_ALIGN_NONE:
1786         case LYX_ALIGN_BLOCK:
1787         case LYX_ALIGN_LAYOUT:
1788         case LYX_ALIGN_SPECIAL:
1789                 break;
1790         case LYX_ALIGN_LEFT:
1791         case LYX_ALIGN_RIGHT:
1792         case LYX_ALIGN_CENTER:
1793                 if (moving_arg) {
1794                         os << "\\protect";
1795                         column += 8;
1796                 }
1797                 break;
1798         }
1799
1800         switch (params().align()) {
1801         case LYX_ALIGN_NONE:
1802         case LYX_ALIGN_BLOCK:
1803         case LYX_ALIGN_LAYOUT:
1804         case LYX_ALIGN_SPECIAL:
1805                 break;
1806         case LYX_ALIGN_LEFT: {
1807                 string output;
1808                 if (getParLanguage(bparams)->babel() != "hebrew")
1809                         output = corrected_env("\\begin", "flushleft", ownerCode());
1810                 else
1811                         output = corrected_env("\\begin", "flushright", ownerCode());
1812                 os << from_ascii(output);
1813                 adjust_row_column(output, texrow, column);
1814                 break;
1815         } case LYX_ALIGN_RIGHT: {
1816                 string output;
1817                 if (getParLanguage(bparams)->babel() != "hebrew")
1818                         output = corrected_env("\\begin", "flushright", ownerCode());
1819                 else
1820                         output = corrected_env("\\begin", "flushleft", ownerCode());
1821                 os << from_ascii(output);
1822                 adjust_row_column(output, texrow, column);
1823                 break;
1824         } case LYX_ALIGN_CENTER: {
1825                 string output;
1826                 output = corrected_env("\\begin", "center", ownerCode());
1827                 os << from_ascii(output);
1828                 adjust_row_column(output, texrow, column);
1829                 break;
1830         }
1831         }
1832
1833         return column;
1834 }
1835
1836
1837 // This could go to ParagraphParameters if we want to
1838 int Paragraph::endTeXParParams(BufferParams const & bparams,  
1839                                odocstream & os, TexRow & texrow,
1840                                bool moving_arg) const
1841 {
1842         int column = 0;
1843
1844         switch (params().align()) {
1845         case LYX_ALIGN_NONE:
1846         case LYX_ALIGN_BLOCK:
1847         case LYX_ALIGN_LAYOUT:
1848         case LYX_ALIGN_SPECIAL:
1849                 break;
1850         case LYX_ALIGN_LEFT:
1851         case LYX_ALIGN_RIGHT:
1852         case LYX_ALIGN_CENTER:
1853                 if (moving_arg) {
1854                         os << "\\protect";
1855                         column = 8;
1856                 }
1857                 break;
1858         }
1859
1860         switch (params().align()) {
1861         case LYX_ALIGN_NONE:
1862         case LYX_ALIGN_BLOCK:
1863         case LYX_ALIGN_LAYOUT:
1864         case LYX_ALIGN_SPECIAL:
1865                 break;
1866         case LYX_ALIGN_LEFT: {
1867                 string output;
1868                 if (getParLanguage(bparams)->babel() != "hebrew")
1869                         output = corrected_env("\n\\par\\end", "flushleft", ownerCode());
1870                 else
1871                         output = corrected_env("\n\\par\\end", "flushright", ownerCode());
1872                 os << from_ascii(output);
1873                 adjust_row_column(output, texrow, column);
1874                 break;
1875         } case LYX_ALIGN_RIGHT: {
1876                 string output;
1877                 if (getParLanguage(bparams)->babel() != "hebrew")
1878                         output = corrected_env("\n\\par\\end", "flushright", ownerCode());
1879                 else
1880                         output = corrected_env("\n\\par\\end", "flushleft", ownerCode());
1881                 os << from_ascii(output);
1882                 adjust_row_column(output, texrow, column);
1883                 break;
1884         } case LYX_ALIGN_CENTER: {
1885                 string output;
1886                 output = corrected_env("\n\\par\\end", "center", ownerCode());
1887                 os << from_ascii(output);
1888                 adjust_row_column(output, texrow, column);
1889                 break;
1890         }
1891         }
1892
1893         return column;
1894 }
1895
1896
1897 // This one spits out the text of the paragraph
1898 bool Paragraph::simpleTeXOnePar(Buffer const & buf,
1899                                 BufferParams const & bparams,
1900                                 Font const & outerfont,
1901                                 odocstream & os, TexRow & texrow,
1902                                 OutputParams const & runparams) const
1903 {
1904         LYXERR(Debug::LATEX) << "SimpleTeXOnePar...     " << this << endl;
1905
1906         bool return_value = false;
1907
1908         Layout_ptr style;
1909
1910         // well we have to check if we are in an inset with unlimited
1911         // length (all in one row) if that is true then we don't allow
1912         // any special options in the paragraph and also we don't allow
1913         // any environment other than the default layout of the text class
1914         // to be valid!
1915         bool asdefault = forceDefaultParagraphs();
1916
1917         if (asdefault) {
1918                 style = bparams.getTextClass().defaultLayout();
1919         } else {
1920                 style = layout();
1921         }
1922
1923         // Current base font for all inherited font changes, without any
1924         // change caused by an individual character, except for the language:
1925         // It is set to the language of the first character.
1926         // As long as we are in the label, this font is the base font of the
1927         // label. Before the first body character it is set to the base font
1928         // of the body.
1929         Font basefont;
1930
1931         // output change tracking marks only if desired,
1932         // if dvipost is installed,
1933         // and with dvi/ps (other formats don't work)
1934         bool const output = bparams.outputChanges
1935                 && runparams.flavor == OutputParams::LATEX
1936                 && LaTeXFeatures::isAvailable("dvipost");
1937
1938         // Maybe we have to create a optional argument.
1939         pos_type body_pos = beginOfBody();
1940         unsigned int column = 0;
1941
1942         if (body_pos > 0) {
1943                 // the optional argument is kept in curly brackets in
1944                 // case it contains a ']'
1945                 os << "[{";
1946                 column += 2;
1947                 basefont = getLabelFont(bparams, outerfont);
1948         } else {
1949                 basefont = getLayoutFont(bparams, outerfont);
1950         }
1951
1952         // Which font is currently active?
1953         Font running_font(basefont);
1954         // Do we have an open font change?
1955         bool open_font = false;
1956
1957         Change::Type runningChangeType = Change::UNCHANGED;
1958
1959         texrow.start(id(), 0);
1960
1961         // if the paragraph is empty, the loop will not be entered at all
1962         if (empty()) {
1963                 if (style->isCommand()) {
1964                         os << '{';
1965                         ++column;
1966                 }
1967                 if (!asdefault)
1968                         column += startTeXParParams(bparams, os, texrow,
1969                                                     runparams.moving_arg);
1970         }
1971
1972         for (pos_type i = 0; i < size(); ++i) {
1973                 // First char in paragraph or after label?
1974                 if (i == body_pos) {
1975                         if (body_pos > 0) {
1976                                 if (open_font) {
1977                                         column += running_font.latexWriteEndChanges(
1978                                                 os, basefont, basefont);
1979                                         open_font = false;
1980                                 }
1981                                 basefont = getLayoutFont(bparams, outerfont);
1982                                 running_font = basefont;
1983
1984                                 column += Changes::latexMarkChange(os,
1985                                                 runningChangeType, Change::UNCHANGED, output);
1986                                 runningChangeType = Change::UNCHANGED;
1987
1988                                 os << "}] ";
1989                                 column +=3;
1990                         }
1991                         if (style->isCommand()) {
1992                                 os << '{';
1993                                 ++column;
1994                         }
1995
1996                         if (!asdefault)
1997                                 column += startTeXParParams(bparams, os, 
1998                                                             texrow,
1999                                                             runparams.moving_arg);
2000                 }
2001
2002                 Change::Type changeType = pimpl_->lookupChange(i).type;
2003
2004                 // do not output text which is marked deleted
2005                 // if change tracking output is disabled
2006                 if (!output && changeType == Change::DELETED) {
2007                         runningChangeType = changeType;
2008                         continue;
2009                 }
2010
2011                 ++column;
2012                 
2013                 column += Changes::latexMarkChange(os, runningChangeType,
2014                         changeType, output);
2015                 runningChangeType = changeType;
2016
2017                 value_type const c = getChar(i);
2018
2019                 // Fully instantiated font
2020                 Font const font = getFont(bparams, i, outerfont);
2021
2022                 Font const last_font = running_font;
2023
2024                 // Do we need to close the previous font?
2025                 if (open_font &&
2026                     (font != running_font ||
2027                      font.language() != running_font.language()))
2028                 {
2029                         column += running_font.latexWriteEndChanges(
2030                                         os, basefont,
2031                                         (i == body_pos-1) ? basefont : font);
2032                         running_font = basefont;
2033                         open_font = false;
2034                 }
2035
2036                 // Switch file encoding if necessary
2037                 int const count = switchEncoding(os, bparams,
2038                                 runparams.moving_arg, *(runparams.encoding),
2039                                 *(font.language()->encoding()));
2040                 if (count > 0) {
2041                         column += count;
2042                         runparams.encoding = font.language()->encoding();
2043                 }
2044
2045                 // Do we need to change font?
2046                 if ((font != running_font ||
2047                      font.language() != running_font.language()) &&
2048                         i != body_pos - 1)
2049                 {
2050                         column += font.latexWriteStartChanges(os, basefont,
2051                                                               last_font);
2052                         running_font = font;
2053                         open_font = true;
2054                 }
2055
2056                 if (c == ' ') {
2057                         // Do not print the separation of the optional argument
2058                         // if style->pass_thru is false. This works because
2059                         // simpleTeXSpecialChars ignores spaces if
2060                         // style->pass_thru is false.
2061                         if (i != body_pos - 1) {
2062                                 if (pimpl_->simpleTeXBlanks(
2063                                                 *(runparams.encoding), os, texrow,
2064                                                 i, column, font, *style))
2065                                         // A surrogate pair was output. We
2066                                         // must not call simpleTeXSpecialChars
2067                                         // in this iteration, since
2068                                         // simpleTeXBlanks incremented i, and
2069                                         // simpleTeXSpecialChars would output
2070                                         // the combining character again.
2071                                         continue;
2072                         }
2073                 }
2074
2075                 OutputParams rp = runparams;
2076                 rp.free_spacing = style->free_spacing;
2077                 rp.local_font = &font;
2078                 rp.intitle = style->intitle;
2079                 pimpl_->simpleTeXSpecialChars(buf, bparams, os,
2080                                         texrow, rp, running_font,
2081                                         basefont, outerfont, open_font,
2082                                         runningChangeType, *style, i, column, c);
2083         }
2084
2085         // If we have an open font definition, we have to close it
2086         if (open_font) {
2087 #ifdef FIXED_LANGUAGE_END_DETECTION
2088                 if (next_) {
2089                         running_font
2090                                 .latexWriteEndChanges(os, basefont,
2091                                         next_->getFont(bparams, 0, outerfont));
2092                 } else {
2093                         running_font.latexWriteEndChanges(os, basefont,
2094                                                           basefont);
2095                 }
2096 #else
2097 #ifdef WITH_WARNINGS
2098 //#warning For now we ALWAYS have to close the foreign font settings if they are
2099 //#warning there as we start another \selectlanguage with the next paragraph if
2100 //#warning we are in need of this. This should be fixed sometime (Jug)
2101 #endif
2102                 running_font.latexWriteEndChanges(os, basefont, basefont);
2103 #endif
2104         }
2105
2106         column += Changes::latexMarkChange(os,
2107                         runningChangeType, Change::UNCHANGED, output);
2108
2109         // Needed if there is an optional argument but no contents.
2110         if (body_pos > 0 && body_pos == size()) {
2111                 os << "}]~";
2112                 return_value = false;
2113         }
2114
2115         if (!asdefault) {
2116                 column += endTeXParParams(bparams, os, texrow, 
2117                                           runparams.moving_arg);
2118         }
2119
2120         LYXERR(Debug::LATEX) << "SimpleTeXOnePar...done " << this << endl;
2121         return return_value;
2122 }
2123
2124
2125 namespace {
2126
2127 enum PAR_TAG {
2128         PAR_NONE=0,
2129         TT = 1,
2130         SF = 2,
2131         BF = 4,
2132         IT = 8,
2133         SL = 16,
2134         EM = 32
2135 };
2136
2137
2138 string tag_name(PAR_TAG const & pt) {
2139         switch (pt) {
2140         case PAR_NONE: return "!-- --";
2141         case TT: return "tt";
2142         case SF: return "sf";
2143         case BF: return "bf";
2144         case IT: return "it";
2145         case SL: return "sl";
2146         case EM: return "em";
2147         }
2148         return "";
2149 }
2150
2151
2152 inline
2153 void operator|=(PAR_TAG & p1, PAR_TAG const & p2)
2154 {
2155         p1 = static_cast<PAR_TAG>(p1 | p2);
2156 }
2157
2158
2159 inline
2160 void reset(PAR_TAG & p1, PAR_TAG const & p2)
2161 {
2162         p1 = static_cast<PAR_TAG>(p1 & ~p2);
2163 }
2164
2165 } // anon
2166
2167
2168 bool Paragraph::emptyTag() const
2169 {
2170         for (pos_type i = 0; i < size(); ++i) {
2171                 if (isInset(i)) {
2172                         Inset const * inset = getInset(i);
2173                         Inset::Code lyx_code = inset->lyxCode();
2174                         if (lyx_code != Inset::TOC_CODE &&
2175                             lyx_code != Inset::INCLUDE_CODE &&
2176                             lyx_code != Inset::GRAPHICS_CODE &&
2177                             lyx_code != Inset::ERT_CODE &&
2178                             lyx_code != Inset::FLOAT_CODE &&
2179                             lyx_code != Inset::TABULAR_CODE) {
2180                                 return false;
2181                         }
2182                 } else {
2183                         value_type c = getChar(i);
2184                         if (c != ' ' && c != '\t')
2185                                 return false;
2186                 }
2187         }
2188         return true;
2189 }
2190
2191
2192 string Paragraph::getID(Buffer const & buf, OutputParams const & runparams) const
2193 {
2194         for (pos_type i = 0; i < size(); ++i) {
2195                 if (isInset(i)) {
2196                         Inset const * inset = getInset(i);
2197                         Inset::Code lyx_code = inset->lyxCode();
2198                         if (lyx_code == Inset::LABEL_CODE) {
2199                                 string const id = static_cast<InsetCommand const *>(inset)->getContents();
2200                                 return "id='" + to_utf8(sgml::cleanID(buf, runparams, from_utf8(id))) + "'";
2201                         }
2202                 }
2203
2204         }
2205         return string();
2206 }
2207
2208
2209 pos_type Paragraph::getFirstWord(Buffer const & buf, odocstream & os, OutputParams const & runparams) const
2210 {
2211         pos_type i;
2212         for (i = 0; i < size(); ++i) {
2213                 if (isInset(i)) {
2214                         Inset const * inset = getInset(i);
2215                         inset->docbook(buf, os, runparams);
2216                 } else {
2217                         value_type c = getChar(i);
2218                         if (c == ' ')
2219                                 break;
2220                         os << sgml::escapeChar(c);
2221                 }
2222         }
2223         return i;
2224 }
2225
2226
2227 bool Paragraph::onlyText(Buffer const & buf, Font const & outerfont, pos_type initial) const
2228 {
2229         Font font_old;
2230
2231         for (pos_type i = initial; i < size(); ++i) {
2232                 Font font = getFont(buf.params(), i, outerfont);
2233                 if (isInset(i))
2234                         return false;
2235                 if (i != initial && font != font_old)
2236                         return false;
2237                 font_old = font;
2238         }
2239
2240         return true;
2241 }
2242
2243
2244 void Paragraph::simpleDocBookOnePar(Buffer const & buf,
2245                                     odocstream & os,
2246                                     OutputParams const & runparams,
2247                                     Font const & outerfont,
2248                                     pos_type initial) const
2249 {
2250         bool emph_flag = false;
2251
2252         Layout_ptr const & style = layout();
2253         Font font_old =
2254                 style->labeltype == LABEL_MANUAL ? style->labelfont : style->font;
2255
2256         if (style->pass_thru && !onlyText(buf, outerfont, initial))
2257                 os << "]]>";
2258
2259         // parsing main loop
2260         for (pos_type i = initial; i < size(); ++i) {
2261                 Font font = getFont(buf.params(), i, outerfont);
2262
2263                 // handle <emphasis> tag
2264                 if (font_old.emph() != font.emph()) {
2265                         if (font.emph() == Font::ON) {
2266                                 os << "<emphasis>";
2267                                 emph_flag = true;
2268                         } else if (i != initial) {
2269                                 os << "</emphasis>";
2270                                 emph_flag = false;
2271                         }
2272                 }
2273
2274                 if (isInset(i)) {
2275                         Inset const * inset = getInset(i);
2276                         inset->docbook(buf, os, runparams);
2277                 } else {
2278                         value_type c = getChar(i);
2279
2280                         if (style->pass_thru)
2281                                 os.put(c);
2282                         else
2283                                 os << sgml::escapeChar(c);
2284                 }
2285                 font_old = font;
2286         }
2287
2288         if (emph_flag) {
2289                 os << "</emphasis>";
2290         }
2291
2292         if (style->free_spacing)
2293                 os << '\n';
2294         if (style->pass_thru && !onlyText(buf, outerfont, initial))
2295                 os << "<![CDATA[";
2296 }
2297
2298
2299 bool Paragraph::isNewline(pos_type pos) const
2300 {
2301         return isInset(pos)
2302                 && getInset(pos)->lyxCode() == Inset::NEWLINE_CODE;
2303 }
2304
2305
2306 bool Paragraph::isLineSeparator(pos_type pos) const
2307 {
2308         value_type const c = getChar(pos);
2309         return isLineSeparatorChar(c)
2310                 || (c == Paragraph::META_INSET && getInset(pos) &&
2311                 getInset(pos)->isLineSeparator());
2312 }
2313
2314
2315 /// Used by the spellchecker
2316 bool Paragraph::isLetter(pos_type pos) const
2317 {
2318         if (isInset(pos))
2319                 return getInset(pos)->isLetter();
2320         else {
2321                 value_type const c = getChar(pos);
2322                 return isLetterChar(c) || isDigit(c);
2323         }
2324 }
2325
2326
2327 Language const *
2328 Paragraph::getParLanguage(BufferParams const & bparams) const
2329 {
2330         if (!empty())
2331                 return getFirstFontSettings(bparams).language();
2332 #ifdef WITH_WARNINGS
2333 #warning FIXME we should check the prev par as well (Lgb)
2334 #endif
2335         return bparams.language;
2336 }
2337
2338
2339 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
2340 {
2341         return lyxrc.rtl_support
2342                 && getParLanguage(bparams)->rightToLeft()
2343                 && ownerCode() != Inset::ERT_CODE;
2344 }
2345
2346
2347 void Paragraph::changeLanguage(BufferParams const & bparams,
2348                                Language const * from, Language const * to)
2349 {
2350         // change language including dummy font change at the end
2351         for (pos_type i = 0; i <= size(); ++i) {
2352                 Font font = getFontSettings(bparams, i);
2353                 if (font.language() == from) {
2354                         font.setLanguage(to);
2355                         setFont(i, font);
2356                 }
2357         }
2358 }
2359
2360
2361 bool Paragraph::isMultiLingual(BufferParams const & bparams) const
2362 {
2363         Language const * doc_language = bparams.language;
2364         Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
2365         Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
2366
2367         for (; cit != end; ++cit)
2368                 if (cit->font().language() != ignore_language &&
2369                     cit->font().language() != latex_language &&
2370                     cit->font().language() != doc_language)
2371                         return true;
2372         return false;
2373 }
2374
2375
2376 // Convert the paragraph to a string.
2377 // Used for building the table of contents
2378 docstring const Paragraph::asString(Buffer const & buffer, bool label) const
2379 {
2380         return asString(buffer, 0, size(), label);
2381 }
2382
2383
2384 docstring const Paragraph::asString(Buffer const & buffer,
2385                                  pos_type beg, pos_type end, bool label) const
2386 {
2387
2388         odocstringstream os;
2389
2390         if (beg == 0 && label && !params().labelString().empty())
2391                 os << params().labelString() << ' ';
2392
2393         for (pos_type i = beg; i < end; ++i) {
2394                 value_type const c = getUChar(buffer.params(), i);
2395                 if (isPrintable(c))
2396                         os.put(c);
2397                 else if (c == META_INSET)
2398                         getInset(i)->textString(buffer, os);
2399         }
2400
2401         return os.str();
2402 }
2403
2404
2405 void Paragraph::setInsetOwner(Inset * inset)
2406 {
2407         pimpl_->inset_owner = inset;
2408 }
2409
2410
2411 Change const & Paragraph::lookupChange(pos_type pos) const
2412 {
2413         BOOST_ASSERT(pos <= size());
2414         return pimpl_->lookupChange(pos);
2415 }
2416
2417
2418 bool Paragraph::isChanged(pos_type start, pos_type end) const
2419 {
2420         return pimpl_->isChanged(start, end);
2421 }
2422
2423
2424 bool Paragraph::isMergedOnEndOfParDeletion(bool trackChanges) const
2425 {
2426         return pimpl_->isMergedOnEndOfParDeletion(trackChanges);
2427 }
2428
2429
2430 void Paragraph::setChange(Change const & change)
2431 {
2432         pimpl_->setChange(change);
2433 }
2434
2435
2436 void Paragraph::setChange(pos_type pos, Change const & change)
2437 {
2438         pimpl_->setChange(pos, change);
2439 }
2440
2441
2442 void Paragraph::acceptChanges(BufferParams const & bparams, pos_type start, pos_type end)
2443 {
2444         return pimpl_->acceptChanges(bparams, start, end);
2445 }
2446
2447
2448 void Paragraph::rejectChanges(BufferParams const & bparams, pos_type start, pos_type end)
2449 {
2450         return pimpl_->rejectChanges(bparams, start, end);
2451 }
2452
2453
2454 int Paragraph::id() const
2455 {
2456         return pimpl_->id_;
2457 }
2458
2459
2460 Layout_ptr const & Paragraph::layout() const
2461 {
2462         return layout_;
2463 }
2464
2465
2466 void Paragraph::layout(Layout_ptr const & new_layout)
2467 {
2468         layout_ = new_layout;
2469 }
2470
2471
2472 Inset * Paragraph::inInset() const
2473 {
2474         return pimpl_->inset_owner;
2475 }
2476
2477
2478 Inset::Code Paragraph::ownerCode() const
2479 {
2480         return pimpl_->inset_owner
2481                 ? pimpl_->inset_owner->lyxCode() : Inset::NO_CODE;
2482 }
2483
2484
2485 ParagraphParameters & Paragraph::params()
2486 {
2487         return pimpl_->params;
2488 }
2489
2490
2491 ParagraphParameters const & Paragraph::params() const
2492 {
2493         return pimpl_->params;
2494 }
2495
2496
2497 bool Paragraph::isFreeSpacing() const
2498 {
2499         if (layout()->free_spacing)
2500                 return true;
2501
2502         // for now we just need this, later should we need this in some
2503         // other way we can always add a function to Inset too.
2504         return ownerCode() == Inset::ERT_CODE;
2505 }
2506
2507
2508 bool Paragraph::allowEmpty() const
2509 {
2510         if (layout()->keepempty)
2511                 return true;
2512         return ownerCode() == Inset::ERT_CODE;
2513 }
2514
2515
2516 char_type Paragraph::transformChar(char_type c, pos_type pos) const
2517 {
2518         if (!Encodings::is_arabic(c))
2519                 return c;
2520
2521         value_type prev_char = ' ';
2522         value_type next_char = ' ';
2523
2524         for (pos_type i = pos - 1; i >= 0; --i) {
2525                 value_type const par_char = getChar(i);
2526                 if (!Encodings::isComposeChar_arabic(par_char)) {
2527                         prev_char = par_char;
2528                         break;
2529                 }
2530         }
2531
2532         for (pos_type i = pos + 1, end = size(); i < end; ++i) {
2533                 value_type const par_char = getChar(i);
2534                 if (!Encodings::isComposeChar_arabic(par_char)) {
2535                         next_char = par_char;
2536                         break;
2537                 }
2538         }
2539
2540         if (Encodings::is_arabic(next_char)) {
2541                 if (Encodings::is_arabic(prev_char) &&
2542                         !Encodings::is_arabic_special(prev_char))
2543                         return Encodings::transformChar(c, Encodings::FORM_MEDIAL);
2544                 else
2545                         return Encodings::transformChar(c, Encodings::FORM_INITIAL);
2546         } else {
2547                 if (Encodings::is_arabic(prev_char) &&
2548                         !Encodings::is_arabic_special(prev_char))
2549                         return Encodings::transformChar(c, Encodings::FORM_FINAL);
2550                 else
2551                         return Encodings::transformChar(c, Encodings::FORM_ISOLATED);
2552         }
2553 }
2554
2555
2556 bool Paragraph::hfillExpansion(Row const & row, pos_type pos) const
2557 {
2558         if (!isHfill(pos))
2559                 return false;
2560
2561         BOOST_ASSERT(pos >= row.pos() && pos < row.endpos());
2562
2563         // expand at the end of a row only if there is another hfill on the same row
2564         if (pos == row.endpos() - 1) {
2565                 for (pos_type i = row.pos(); i < pos; i++) {
2566                         if (isHfill(i))
2567                                 return true;
2568                 }
2569                 return false;
2570         }
2571
2572         // expand at the beginning of a row only if it is the first row of a paragraph
2573         if (pos == row.pos()) {
2574                 return pos == 0;
2575         }
2576
2577         // do not expand in some labels
2578         if (layout()->margintype != MARGIN_MANUAL && pos < beginOfBody())
2579                 return false;
2580
2581         // if there is anything between the first char of the row and
2582         // the specified position that is neither a newline nor an hfill,
2583         // the hfill will be expanded, otherwise it won't
2584         for (pos_type i = row.pos(); i < pos; i++) {
2585                 if (!isNewline(i) && !isHfill(i))
2586                         return true;
2587         }
2588         return false;
2589 }
2590
2591
2592 bool Paragraph::checkBiblio(bool track_changes)
2593 {
2594         // Add bibitem insets if necessary
2595         if (layout()->labeltype != LABEL_BIBLIO)
2596                 return false;
2597
2598         bool hasbibitem = !insetlist.empty()
2599                 // Insist on it being in pos 0
2600                 && getChar(0) == Paragraph::META_INSET
2601                 && insetlist.begin()->inset->lyxCode() == Inset::BIBITEM_CODE;
2602
2603         if (hasbibitem)
2604                 return false;
2605
2606         InsetBibitem * inset(new InsetBibitem(InsetCommandParams("bibitem")));
2607         insertInset(0, static_cast<Inset *>(inset),
2608                 Change(track_changes ? Change::INSERTED : Change::UNCHANGED));
2609
2610         return true;
2611 }
2612
2613 } // namespace lyx