]> git.lyx.org Git - lyx.git/blobdiff - src/paragraph.C
Fix bug 2474; partial fix for 1777. Added last_reference_ member to QRef class and...
[lyx.git] / src / paragraph.C
index 6060876d9fc62aa759cd58de7104515d26db8933..ea5ab786842b01cb96bead2c746589f2aa7f4580 100644 (file)
@@ -18,7 +18,6 @@
 #include <config.h>
 
 #include "paragraph.h"
-#include "paragraph_pimpl.h"
 
 #include "buffer.h"
 #include "bufferparams.h"
 #include "gettext.h"
 #include "language.h"
 #include "LaTeXFeatures.h"
+#include "LColor.h"
+#include "lyxlength.h"
 #include "lyxfont.h"
 #include "lyxrc.h"
 #include "lyxrow.h"
+#include "messages.h"
 #include "outputparams.h"
+#include "output_latex.h"
 #include "paragraph_funcs.h"
-#include "ParagraphList_fwd.h"
+#include "ParagraphParameters.h"
+#include "rowpainter.h"
 #include "sgml.h"
 #include "texrow.h"
 #include "vspace.h"
 
+#include "frontends/FontMetrics.h"
+
 #include "insets/insetbibitem.h"
 #include "insets/insetoptarg.h"
 
 #include "support/lstrings.h"
 #include "support/textutils.h"
 #include "support/convert.h"
+#include "support/unicode.h"
 
-#include <boost/tuple/tuple.hpp>
 #include <boost/bind.hpp>
+#include <boost/next_prior.hpp>
 
 #include <algorithm>
-#include <list>
-#include <stack>
 #include <sstream>
 
-using lyx::pos_type;
+using std::distance;
+using std::endl;
+using std::string;
+using std::ostream;
+
+namespace lyx {
+
+using support::contains;
+using support::rsplit;
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// Paragraph::Pimpl
+//
+/////////////////////////////////////////////////////////////////////
+
+class Encoding;
+class LyXLayout;
+
+
+class Paragraph::Pimpl {
+public:
+       ///
+       Pimpl(Paragraph * owner);
+       /// "Copy constructor"
+       Pimpl(Pimpl const &, Paragraph * owner);
+
+       //
+       // Change tracking
+       //
+       /// look up change at given pos
+       Change const & lookupChange(pos_type pos) const;
+       /// is there a change within the given range ?
+       bool isChanged(pos_type start, pos_type end) const;
+       /// will the paragraph be physically merged with the next
+       /// one if the imaginary end-of-par character is logically deleted?
+       bool isMergedOnEndOfParDeletion(bool trackChanges) const;
+       /// set change for the entire par
+       void setChange(Change const & change);
+       /// set change at given pos
+       void setChange(pos_type pos, Change const & change);
+       /// accept changes within the given range
+       void acceptChanges(BufferParams const & bparams, pos_type start, pos_type end);
+       /// reject changes within the given range
+       void rejectChanges(BufferParams const & bparams, pos_type start, pos_type end);
+
+       ///
+       value_type getChar(pos_type pos) const;
+       ///
+       void insertChar(pos_type pos, value_type c, Change const & change);
+       ///
+       void insertInset(pos_type pos, InsetBase * inset, Change const & change);
+       /// (logically) erase the char at pos; return true if it was actually erased
+       bool eraseChar(pos_type pos, bool trackChanges);
+       /// (logically) erase the given range; return the number of chars actually erased
+       int eraseChars(pos_type start, pos_type end, bool trackChanges);
+       ///
+       InsetBase * inset_owner;
+
+       /** A font entry covers a range of positions. Notice that the
+           entries in the list are inserted in random order.
+           I don't think it's worth the effort to implement a more effective
+           datastructure, because the number of different fonts in a paragraph
+           is limited. (Asger)
+           Nevertheless, I decided to store fontlist using a sorted vector:
+           fontlist = { {pos_1,font_1} , {pos_2,font_2} , ... } where
+           pos_1 < pos_2 < ..., font_{i-1} != font_i for all i,
+           and font_i covers the chars in positions pos_{i-1}+1,...,pos_i
+           (font_1 covers the chars 0,...,pos_1) (Dekel)
+       */
+       class FontTable  {
+       public:
+               ///
+               FontTable(pos_type p, LyXFont const & f)
+                       : pos_(p), font_(f)
+               {}
+               ///
+               pos_type pos() const { return pos_; }
+               ///
+               void pos(pos_type p) { pos_ = p; }
+               ///
+               LyXFont const & font() const { return font_; }
+               ///
+               void font(LyXFont const & f) { font_ = f;}
+       private:
+               /// End position of paragraph this font attribute covers
+               pos_type pos_;
+               /** Font. Interpretation of the font values:
+                   If a value is LyXFont::INHERIT_*, it means that the font
+                   attribute is inherited from either the layout of this
+                   paragraph or, in the case of nested paragraphs, from the
+                   layout in the environment one level up until completely
+                   resolved.
+                   The values LyXFont::IGNORE_* and LyXFont::TOGGLE are NOT
+                   allowed in these font tables.
+               */
+               LyXFont font_;
+       };
+       ///
+       friend class matchFT;
+       ///
+       class matchFT {
+       public:
+               /// used by lower_bound and upper_bound
+               int operator()(FontTable const & a, FontTable const & b) const {
+                       return a.pos() < b.pos();
+               }
+       };
+
+       ///
+       typedef std::vector<FontTable> FontList;
+       ///
+       FontList fontlist;
+
+       /// Output the surrogate pair formed by \p c and \p next to \p os.
+       /// \return the number of characters written.
+       int latexSurrogatePair(odocstream & os, value_type c, value_type next,
+                              Encoding const &);
+       /// Output a space in appropriate formatting (or a surrogate pair
+       /// if the next character is a combining character).
+       /// \return whether a surrogate pair was output.
+       bool simpleTeXBlanks(Encoding const &,
+                            odocstream &, TexRow & texrow,
+                            pos_type & i,
+                            unsigned int & column,
+                            LyXFont const & font,
+                            LyXLayout const & style);
+       ///
+       void simpleTeXSpecialChars(Buffer const &, BufferParams const &,
+                                  odocstream &,
+                                  TexRow & texrow, OutputParams const &,
+                                  LyXFont & running_font,
+                                  LyXFont & basefont,
+                                  LyXFont const & outerfont,
+                                  bool & open_font,
+                                  Change::Type & running_change,
+                                  LyXLayout const & style,
+                                  pos_type & i,
+                                  unsigned int & column, value_type const c);
+
+       ///
+       void validate(LaTeXFeatures & features,
+                     LyXLayout const & layout) const;
+
+       ///
+       unsigned int id_;
+       ///
+       static unsigned int paragraph_id;
+       ///
+       ParagraphParameters params;
+
+private:
+       ///
+       pos_type size() const { return owner_->size(); }
+       /// match a string against a particular point in the paragraph
+       bool isTextAt(std::string const & str, pos_type pos) const;
+
+       /// for recording and looking up changes
+       Changes changes_;
+
+       /// Who owns us?
+       Paragraph * owner_;
+};
+
+
+
+
+using std::endl;
+using std::upper_bound;
+using std::lower_bound;
+using std::string;
+
+
+// Initialization of the counter for the paragraph id's,
+unsigned int Paragraph::Pimpl::paragraph_id = 0;
+
+namespace {
+
+struct special_phrase {
+       string phrase;
+       docstring macro;
+       bool builtin;
+};
+
+special_phrase const special_phrases[] = {
+       { "LyX", from_ascii("\\LyX{}"), false },
+       { "TeX", from_ascii("\\TeX{}"), true },
+       { "LaTeX2e", from_ascii("\\LaTeXe{}"), true },
+       { "LaTeX", from_ascii("\\LaTeX{}"), true },
+};
+
+size_t const phrases_nr = sizeof(special_phrases)/sizeof(special_phrase);
+
+} // namespace anon
+
+
+Paragraph::Pimpl::Pimpl(Paragraph * owner)
+       : owner_(owner)
+{
+       inset_owner = 0;
+       id_ = paragraph_id++;
+}
+
+
+Paragraph::Pimpl::Pimpl(Pimpl const & p, Paragraph * owner)
+       : params(p.params), changes_(p.changes_), owner_(owner)
+{
+       inset_owner = p.inset_owner;
+       fontlist = p.fontlist;
+       id_ = paragraph_id++;
+}
+
+
+bool Paragraph::Pimpl::isChanged(pos_type start, pos_type end) const
+{
+       BOOST_ASSERT(start >= 0 && start <= size());
+       BOOST_ASSERT(end > start && end <= size() + 1);
+
+       return changes_.isChanged(start, end);
+}
+
+
+bool Paragraph::Pimpl::isMergedOnEndOfParDeletion(bool trackChanges) const {
+       // keep the logic here in sync with the logic of eraseChars()
+
+       if (!trackChanges) {
+               return true;
+       }
+
+       Change change = changes_.lookup(size());
+
+       return change.type == Change::INSERTED && change.author == 0;
+}
+
+
+void Paragraph::Pimpl::setChange(Change const & change)
+{
+       // beware of the imaginary end-of-par character!
+       changes_.set(change, 0, size() + 1);
+
+       /*
+        * Propagate the change recursively - but not in case of DELETED!
+        *
+        * Imagine that your co-author makes changes in an existing inset. He
+        * sends your document to you and you come to the conclusion that the
+        * inset should go completely. If you erase it, LyX must not delete all
+        * text within the inset. Otherwise, the change tracked insertions of
+        * your co-author get lost and there is no way to restore them later.
+        *
+        * Conclusion: An inset's content should remain untouched if you delete it
+        */
+
+       if (change.type != Change::DELETED) {
+               for (pos_type pos = 0; pos < size(); ++pos) {
+                       if (owner_->isInset(pos)) {
+                               owner_->getInset(pos)->setChange(change);
+                       }
+               }
+       }
+}
+
+
+void Paragraph::Pimpl::setChange(pos_type pos, Change const & change)
+{
+       BOOST_ASSERT(pos >= 0 && pos <= size());
+
+       changes_.set(change, pos);
+
+       // see comment in setChange(Change const &) above
+
+       if (change.type != Change::DELETED &&
+           pos < size() && owner_->isInset(pos)) {
+               owner_->getInset(pos)->setChange(change);
+       }
+}
+
+
+Change const & Paragraph::Pimpl::lookupChange(pos_type pos) const
+{
+       BOOST_ASSERT(pos >= 0 && pos <= size());
+
+       return changes_.lookup(pos);
+}
+
+
+void Paragraph::Pimpl::acceptChanges(BufferParams const & bparams, pos_type start, pos_type end)
+{
+       BOOST_ASSERT(start >= 0 && start <= size());
+       BOOST_ASSERT(end > start && end <= size() + 1);
+
+       for (pos_type pos = start; pos < end; ++pos) {
+               switch (lookupChange(pos).type) {
+                       case Change::UNCHANGED:
+                               // accept changes in nested inset
+                               if (pos < size() && owner_->isInset(pos)) {
+                                       owner_->getInset(pos)->acceptChanges(bparams);
+                               }
+
+                               break;
+
+                       case Change::INSERTED:
+                               changes_.set(Change(Change::UNCHANGED), pos);
+                               // also accept changes in nested inset
+                               if (pos < size() && owner_->isInset(pos)) {
+                                       owner_->getInset(pos)->acceptChanges(bparams);
+                               }
+                               break;
+
+                       case Change::DELETED:
+                               // Suppress access to non-existent
+                               // "end-of-paragraph char"
+                               if (pos < size()) {
+                                       eraseChar(pos, false);
+                                       --end;
+                                       --pos;
+                               }
+                               break;
+               }
+
+       }
+}
+
+
+void Paragraph::Pimpl::rejectChanges(BufferParams const & bparams, pos_type start, pos_type end)
+{
+       BOOST_ASSERT(start >= 0 && start <= size());
+       BOOST_ASSERT(end > start && end <= size() + 1);
+
+       for (pos_type pos = start; pos < end; ++pos) {
+               switch (lookupChange(pos).type) {
+                       case Change::UNCHANGED:
+                               // reject changes in nested inset
+                               if (pos < size() && owner_->isInset(pos)) {
+                                       owner_->getInset(pos)->rejectChanges(bparams);
+                               }
+                               break;
+
+                       case Change::INSERTED:
+                               // Suppress access to non-existent
+                               // "end-of-paragraph char"
+                               if (pos < size()) {
+                                       eraseChar(pos, false);
+                                       --end;
+                                       --pos;
+                               }
+                               break;
+
+                       case Change::DELETED:
+                               changes_.set(Change(Change::UNCHANGED), pos);
+
+                               // Do NOT reject changes within a deleted inset!
+                               // There may be insertions of a co-author inside of it!
+
+                               break;
+               }
+       }
+}
+
+
+Paragraph::value_type Paragraph::Pimpl::getChar(pos_type pos) const
+{
+       BOOST_ASSERT(pos >= 0 && pos <= size());
+
+       return owner_->getChar(pos);
+}
+
+
+void Paragraph::Pimpl::insertChar(pos_type pos, value_type c, Change const & change)
+{
+       BOOST_ASSERT(pos >= 0 && pos <= size());
+
+       // track change
+       changes_.insert(change, pos);
+
+       // This is actually very common when parsing buffers (and
+       // maybe inserting ascii text)
+       if (pos == size()) {
+               // when appending characters, no need to update tables
+               owner_->text_.push_back(c);
+               return;
+       }
+
+       owner_->text_.insert(owner_->text_.begin() + pos, c);
+
+       // Update the font table.
+       FontTable search_font(pos, LyXFont());
+       for (FontList::iterator it 
+             = lower_bound(fontlist.begin(), fontlist.end(), search_font, matchFT());
+            it != fontlist.end(); ++it)
+       {
+               it->pos(it->pos() + 1);
+       }
+
+       // Update the insets
+       owner_->insetlist.increasePosAfterPos(pos);
+}
+
+
+void Paragraph::Pimpl::insertInset(pos_type pos, InsetBase * inset,
+                                   Change const & change)
+{
+       BOOST_ASSERT(inset);
+       BOOST_ASSERT(pos >= 0 && pos <= size());
+
+       insertChar(pos, META_INSET, change);
+       BOOST_ASSERT(owner_->text_[pos] == META_INSET);
+
+       // Add a new entry in the insetlist.
+       owner_->insetlist.insert(inset, pos);
+}
+
+
+bool Paragraph::Pimpl::eraseChar(pos_type pos, bool trackChanges)
+{
+       BOOST_ASSERT(pos >= 0 && pos <= size());
+
+       // keep the logic here in sync with the logic of isMergedOnEndOfParDeletion()
+
+       if (trackChanges) {
+               Change change = changes_.lookup(pos);
+
+               // set the character to DELETED if 
+               //  a) it was previously unchanged or
+               //  b) it was inserted by a co-author
+
+               if (change.type == Change::UNCHANGED ||
+                   (change.type == Change::INSERTED && change.author != 0)) {
+                       setChange(pos, Change(Change::DELETED));
+                       return false;
+               }
+
+               if (change.type == Change::DELETED)
+                       return false;
+       }
+
+       // Don't physically access the imaginary end-of-paragraph character.
+       // eraseChar() can only mark it as DELETED. A physical deletion of
+       // end-of-par must be handled externally.
+       if (pos == size()) {
+               return false;
+       }
+
+       // track change
+       changes_.erase(pos);
+
+       // if it is an inset, delete the inset entry
+       if (owner_->text_[pos] == Paragraph::META_INSET) {
+               owner_->insetlist.erase(pos);
+       }
+
+       owner_->text_.erase(owner_->text_.begin() + pos);
+
+       // Erase entries in the tables.
+       FontTable search_font(pos, LyXFont());
+
+       FontList::iterator it =
+               lower_bound(fontlist.begin(),
+                           fontlist.end(),
+                           search_font, matchFT());
+       if (it != fontlist.end() && it->pos() == pos &&
+           (pos == 0 ||
+            (it != fontlist.begin()
+             && boost::prior(it)->pos() == pos - 1))) {
+               // If it is a multi-character font
+               // entry, we just make it smaller
+               // (see update below), otherwise we
+               // should delete it.
+               unsigned int const i = it - fontlist.begin();
+               fontlist.erase(fontlist.begin() + i);
+               it = fontlist.begin() + i;
+               if (i > 0 && i < fontlist.size() &&
+                   fontlist[i - 1].font() == fontlist[i].font()) {
+                       fontlist.erase(fontlist.begin() + i - 1);
+                       it = fontlist.begin() + i - 1;
+               }
+       }
+
+       // Update all other entries
+       FontList::iterator fend = fontlist.end();
+       for (; it != fend; ++it)
+               it->pos(it->pos() - 1);
+
+       // Update the insetlist
+       owner_->insetlist.decreasePosAfterPos(pos);
+
+       return true;
+}
+
+
+int Paragraph::Pimpl::eraseChars(pos_type start, pos_type end, bool trackChanges)
+{
+       BOOST_ASSERT(start >= 0 && start <= size());
+       BOOST_ASSERT(end >= start && end <= size() + 1);
+
+       pos_type i = start;
+       for (pos_type count = end - start; count; --count) {
+               if (!eraseChar(i, trackChanges))
+                       ++i;
+       }
+       return end - i;
+}
+
+
+int Paragraph::Pimpl::latexSurrogatePair(odocstream & os, value_type c,
+               value_type next, Encoding const & encoding)
+{
+       // Writing next here may circumvent a possible font change between
+       // c and next. Since next is only output if it forms a surrogate pair
+       // with c we can ignore this:
+       // A font change inside a surrogate pair does not make sense and is
+       // hopefully impossible to input.
+       // FIXME: change tracking
+       // Is this correct WRT change tracking?
+       docstring const latex1 = encoding.latexChar(next);
+       docstring const latex2 = encoding.latexChar(c);
+       os << latex1 << '{' << latex2 << '}';
+       return latex1.length() + latex2.length() + 2;
+}
+
+
+bool Paragraph::Pimpl::simpleTeXBlanks(Encoding const & encoding,
+                                       odocstream & os, TexRow & texrow,
+                                       pos_type & i,
+                                      unsigned int & column,
+                                      LyXFont const & font,
+                                      LyXLayout const & style)
+{
+       if (style.pass_thru)
+               return false;
+
+       if (i < size() - 1) {
+               char_type next = getChar(i + 1);
+               if (Encodings::isCombiningChar(next)) {
+                       // This space has an accent, so we must always output it.
+                       column += latexSurrogatePair(os, ' ', next, encoding) - 1;
+                       ++i;
+                       return true;
+               }
+       }
+
+       if (lyxrc.plaintext_linelen > 0
+           && column > lyxrc.plaintext_linelen
+           && i
+           && getChar(i - 1) != ' '
+           && (i < size() - 1)
+           // same in FreeSpacing mode
+           && !owner_->isFreeSpacing()
+           // In typewriter mode, we want to avoid
+           // ! . ? : at the end of a line
+           && !(font.family() == LyXFont::TYPEWRITER_FAMILY
+                && (getChar(i - 1) == '.'
+                    || getChar(i - 1) == '?'
+                    || getChar(i - 1) == ':'
+                    || getChar(i - 1) == '!'))) {
+               os << '\n';
+               texrow.newline();
+               texrow.start(owner_->id(), i + 1);
+               column = 0;
+       } else if (style.free_spacing) {
+               os << '~';
+       } else {
+               os << ' ';
+       }
+       return false;
+}
+
+
+bool Paragraph::Pimpl::isTextAt(string const & str, pos_type pos) const
+{
+       pos_type const len = str.length();
+
+       // is the paragraph large enough?
+       if (pos + len > size())
+               return false;
+
+       // does the wanted text start at point?
+       for (string::size_type i = 0; i < str.length(); ++i) {
+               // Caution: direct comparison of characters works only
+               // because str is pure ASCII.
+               if (str[i] != owner_->text_[pos + i])
+                       return false;
+       }
+
+       // is there a font change in middle of the word?
+       FontList::const_iterator cit = fontlist.begin();
+       FontList::const_iterator end = fontlist.end();
+       for (; cit != end; ++cit) {
+               if (cit->pos() >= pos)
+                       break;
+       }
+       if (cit != end && pos + len - 1 > cit->pos())
+               return false;
+
+       return true;
+}
+
+
+void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const & buf,
+                                            BufferParams const & bparams,
+                                            odocstream & os,
+                                            TexRow & texrow,
+                                            OutputParams const & runparams,
+                                            LyXFont & running_font,
+                                            LyXFont & basefont,
+                                            LyXFont const & outerfont,
+                                            bool & open_font,
+                                            Change::Type & running_change,
+                                            LyXLayout const & style,
+                                            pos_type & i,
+                                            unsigned int & column,
+                                            value_type const c)
+{
+       if (style.pass_thru) {
+               if (c != Paragraph::META_INSET) {
+                       if (c != '\0')
+                               // FIXME UNICODE: This can fail if c cannot
+                               // be encoded in the current encoding.
+                               os.put(c);
+               } else
+                       owner_->getInset(i)->plaintext(buf, os, runparams);
+               return;
+       }
+
+       // Two major modes:  LaTeX or plain
+       // Handle here those cases common to both modes
+       // and then split to handle the two modes separately.
+       switch (c) {
+       case Paragraph::META_INSET: {
+               InsetBase * inset = owner_->getInset(i);
+
+               // FIXME: remove this check
+               if (!inset)
+                       break;
+
+               // FIXME: move this to InsetNewline::latex
+               if (inset->lyxCode() == InsetBase::NEWLINE_CODE) {
+                       // newlines are handled differently here than
+                       // the default in simpleTeXSpecialChars().
+                       if (!style.newline_allowed) {
+                               os << '\n';
+                       } else {
+                               if (open_font) {
+                                       column += running_font.latexWriteEndChanges(
+                                               os, basefont, basefont);
+                                       open_font = false;
+                               }
+
+                               if (running_font.family() == LyXFont::TYPEWRITER_FAMILY)
+                                       os << '~';
+
+                               basefont = owner_->getLayoutFont(bparams, outerfont);
+                               running_font = basefont;
+
+                               if (runparams.moving_arg)
+                                       os << "\\protect ";
+
+                               os << "\\\\\n";
+                       }
+                       texrow.newline();
+                       texrow.start(owner_->id(), i + 1);
+                       column = 0;
+                       break;
+               }
+
+               // output change tracking marks only if desired,
+               // if dvipost is installed,
+               // and with dvi/ps (other formats don't work)
+               LaTeXFeatures features(buf, bparams, runparams);
+               bool const output = bparams.outputChanges
+                       && runparams.flavor == OutputParams::LATEX
+                       && features.isAvailable("dvipost");
+
+               if (inset->canTrackChanges()) {
+                       column += Changes::latexMarkChange(os, running_change,
+                               Change::UNCHANGED, output);
+                       running_change = Change::UNCHANGED;
+               }
+
+               bool close = false;
+               odocstream::pos_type const len = os.tellp();
+
+               if ((inset->lyxCode() == InsetBase::GRAPHICS_CODE
+                    || inset->lyxCode() == InsetBase::MATH_CODE
+                    || inset->lyxCode() == InsetBase::URL_CODE)
+                   && running_font.isRightToLeft()) {
+                       os << "\\L{";
+                       close = true;
+               }
+
+#ifdef WITH_WARNINGS
+#warning Bug: we can have an empty font change here!
+// if there has just been a font change, we are going to close it
+// right now, which means stupid latex code like \textsf{}. AFAIK,
+// this does not harm dvi output. A minor bug, thus (JMarc)
+#endif
+               // some insets cannot be inside a font change command
+               if (open_font && inset->noFontChange()) {
+                       column += running_font.latexWriteEndChanges(
+                                       os, basefont, basefont);
+                       open_font = false;
+                       basefont = owner_->getLayoutFont(bparams, outerfont);
+                       running_font = basefont;
+               }
+
+               int tmp = inset->latex(buf, os, runparams);
+
+               if (close)
+                       os << '}';
+
+               if (tmp) {
+                       for (int j = 0; j < tmp; ++j) {
+                               texrow.newline();
+                       }
+                       texrow.start(owner_->id(), i + 1);
+                       column = 0;
+               } else {
+                       column += os.tellp() - len;
+               }
+       }
+       break;
+
+       default:
+               // And now for the special cases within each mode
+
+               switch (c) {
+               case '\\':
+                       os << "\\textbackslash{}";
+                       column += 15;
+                       break;
+
+               // The following characters could be written literally in latin1, but they
+               // would be wrongly converted on systems where char is signed, so we give
+               // the code points.
+               // This also makes us independant from the encoding of this source file.
+               case '|': case '<': case '>':
+                       // In T1 encoding, these characters exist
+                       if (lyxrc.fontenc == "T1") {
+                               os.put(c);
+                               //... but we should avoid ligatures
+                               if ((c == '>' || c == '<')
+                                   && i <= size() - 2
+                                   && getChar(i + 1) == c) {
+                                       //os << "\\textcompwordmark{}";
+                                       //column += 19;
+                                       // Jean-Marc, have a look at
+                                       // this. I think this works
+                                       // equally well:
+                                       os << "\\,{}";
+                                       // Lgb
+                                       column += 3;
+                               }
+                               break;
+                       }
+                       // Typewriter font also has them
+                       if (running_font.family() == LyXFont::TYPEWRITER_FAMILY) {
+                               os.put(c);
+                               break;
+                       }
+                       // Otherwise, we use what LaTeX
+                       // provides us.
+                       switch (c) {
+                       case '<':
+                               os << "\\textless{}";
+                               column += 10;
+                               break;
+                       case '>':
+                               os << "\\textgreater{}";
+                               column += 13;
+                               break;
+                       case '|':
+                               os << "\\textbar{}";
+                               column += 9;
+                               break;
+                       }
+                       break;
+
+               case '-': // "--" in Typewriter mode -> "-{}-"
+                       if (i <= size() - 2 &&
+                           getChar(i + 1) == '-' &&
+                           running_font.family() == LyXFont::TYPEWRITER_FAMILY) {
+                               os << "-{}";
+                               column += 2;
+                       } else {
+                               os << '-';
+                       }
+                       break;
+
+               case '\"':
+                       os << "\\char`\\\"{}";
+                       column += 9;
+                       break;
+
+               case '$': case '&':
+               case '%': case '#': case '{':
+               case '}': case '_':
+                       os << '\\';
+                       os.put(c);
+                       column += 1;
+                       break;
+
+               case '~':
+                       os << "\\textasciitilde{}";
+                       column += 16;
+                       break;
+
+               case '^':
+                       os << "\\textasciicircum{}";
+                       column += 17;
+                       break;
+
+               case '*': case '[':
+                       // avoid being mistaken for optional arguments
+                       os << '{';
+                       os.put(c);
+                       os << '}';
+                       column += 2;
+                       break;
+
+               case ' ':
+                       // Blanks are printed before font switching.
+                       // Sure? I am not! (try nice-latex)
+                       // I am sure it's correct. LyX might be smarter
+                       // in the future, but for now, nothing wrong is
+                       // written. (Asger)
+                       break;
+
+               default:
+
+                       // I assume this is hack treating typewriter as verbatim
+                       // FIXME UNICODE: This can fail if c cannot be encoded
+                       // in the current encoding.
+                       if (running_font.family() == LyXFont::TYPEWRITER_FAMILY) {
+                               if (c != '\0') {
+                                       os.put(c);
+                               }
+                               break;
+                       }
+
+                       // LyX, LaTeX etc.
+
+                       // FIXME: if we have "LaTeX" with a font
+                       // change in the middle (before the 'T', then
+                       // the "TeX" part is still special cased.
+                       // Really we should only operate this on
+                       // "words" for some definition of word
+
+                       size_t pnr = 0;
+
+                       for (; pnr < phrases_nr; ++pnr) {
+                               if (isTextAt(special_phrases[pnr].phrase, i)) {
+                                       os << special_phrases[pnr].macro;
+                                       i += special_phrases[pnr].phrase.length() - 1;
+                                       column += special_phrases[pnr].macro.length() - 1;
+                                       break;
+                               }
+                       }
+
+                       if (pnr == phrases_nr && c != '\0') {
+                               Encoding const & encoding = *(runparams.encoding);
+                               if (i < size() - 1) {
+                                       char_type next = getChar(i + 1);
+                                       if (Encodings::isCombiningChar(next)) {
+                                               column += latexSurrogatePair(os, c, next, encoding) - 1;
+                                               ++i;
+                                               break;
+                                       }
+                               }
+                               docstring const latex = encoding.latexChar(c);
+                               if (latex.length() > 1 &&
+                                   latex[latex.length() - 1] != '}') {
+                                       // Prevent eating of a following
+                                       // space or command corruption by
+                                       // following characters
+                                       column += latex.length() + 1;
+                                       os << latex << "{}";
+                               } else {
+                                       column += latex.length() - 1;
+                                       os << latex;
+                               }
+                       }
+                       break;
+               }
+       }
+}
+
+
+void Paragraph::Pimpl::validate(LaTeXFeatures & features,
+                               LyXLayout const & layout) const
+{
+       BufferParams const & bparams = features.bufferParams();
+
+       // check the params.
+       if (!params.spacing().isDefault())
+               features.require("setspace");
+
+       // then the layouts
+       features.useLayout(layout.name());
+
+       // then the fonts
+       Language const * doc_language = bparams.language;
+
+       FontList::const_iterator fcit = fontlist.begin();
+       FontList::const_iterator fend = fontlist.end();
+       for (; fcit != fend; ++fcit) {
+               if (fcit->font().noun() == LyXFont::ON) {
+                       LYXERR(Debug::LATEX) << "font.noun: "
+                                            << fcit->font().noun()
+                                            << endl;
+                       features.require("noun");
+                       LYXERR(Debug::LATEX) << "Noun enabled. Font: "
+                                            << to_utf8(fcit->font().stateText(0))
+                                            << endl;
+               }
+               switch (fcit->font().color()) {
+               case LColor::none:
+               case LColor::inherit:
+               case LColor::ignore:
+                       // probably we should put here all interface colors used for
+                       // font displaying! For now I just add this ones I know of (Jug)
+               case LColor::latex:
+               case LColor::note:
+                       break;
+               default:
+                       features.require("color");
+                       LYXERR(Debug::LATEX) << "Color enabled. Font: "
+                                            << to_utf8(fcit->font().stateText(0))
+                                            << endl;
+               }
+
+               Language const * language = fcit->font().language();
+               if (language->babel() != doc_language->babel() &&
+                   language != ignore_language &&
+                   language != latex_language)
+               {
+                       features.useLanguage(language);
+                       LYXERR(Debug::LATEX) << "Found language "
+                                            << language->babel() << endl;
+               }
+       }
+
+       if (!params.leftIndent().zero())
+               features.require("ParagraphLeftIndent");
+
+       // then the insets
+       InsetList::const_iterator icit = owner_->insetlist.begin();
+       InsetList::const_iterator iend = owner_->insetlist.end();
+       for (; icit != iend; ++icit) {
+               if (icit->inset) {
+                       icit->inset->validate(features);
+                       if (layout.needprotect &&
+                           icit->inset->lyxCode() == InsetBase::FOOT_CODE)
+                               features.require("NeedLyXFootnoteCode");
+               }
+       }
+
+       // then the contents
+       for (pos_type i = 0; i < size() ; ++i) {
+               for (size_t pnr = 0; pnr < phrases_nr; ++pnr) {
+                       if (!special_phrases[pnr].builtin
+                           && isTextAt(special_phrases[pnr].phrase, i)) {
+                               features.require(special_phrases[pnr].phrase);
+                               break;
+                       }
+               }
+               Encodings::validate(getChar(i), features);
+       }
+}
 
-using lyx::support::subst;
 
-using std::distance;
-using std::endl;
-using std::list;
-using std::stack;
-using std::string;
-using std::ostream;
-using std::ostringstream;
+} // namespace lyx
 
 
-ParagraphList::ParagraphList()
-{}
+/////////////////////////////////////////////////////////////////////
+//
+// Paragraph
+//
+/////////////////////////////////////////////////////////////////////
 
+namespace lyx {
 
 Paragraph::Paragraph()
        : begin_of_body_(0), pimpl_(new Paragraph::Pimpl(this))
@@ -79,12 +1048,10 @@ Paragraph::Paragraph()
 
 
 Paragraph::Paragraph(Paragraph const & par)
-       :       itemdepth(par.itemdepth), insetlist(par.insetlist),
-               dim_(par.dim_),
-               rows_(par.rows_), rowSignature_(par.rowSignature_), 
-               layout_(par.layout_),
-               text_(par.text_), begin_of_body_(par.begin_of_body_),
-         pimpl_(new Paragraph::Pimpl(*par.pimpl_, this))
+       : itemdepth(par.itemdepth), insetlist(par.insetlist),
+       layout_(par.layout_),
+       text_(par.text_), begin_of_body_(par.begin_of_body_),
+       pimpl_(new Paragraph::Pimpl(*par.pimpl_, this))
 {
        //lyxerr << "Paragraph::Paragraph(Paragraph const&)" << endl;
        InsetList::iterator it = insetlist.begin();
@@ -106,9 +1073,6 @@ Paragraph & Paragraph::operator=(Paragraph const & par)
                for (; it != end; ++it)
                        it->inset = it->inset->clone().release();
 
-               rows_ = par.rows_;
-               dim_ = par.dim_;
-               rowSignature_ = par.rowSignature_;
                layout_ = par.layout();
                text_ = par.text_;
                begin_of_body_ = par.begin_of_body_;
@@ -156,13 +1120,12 @@ void Paragraph::write(Buffer const & buf, ostream & os,
        LyXFont font1(LyXFont::ALL_INHERIT, bparams.language);
 
        Change running_change = Change(Change::UNCHANGED);
-       lyx::time_type const curtime(lyx::current_time());
 
        int column = 0;
        for (pos_type i = 0; i <= size(); ++i) {
 
-               Change change = pimpl_->lookupChangeFull(i);
-               Changes::lyxMarkChange(os, column, curtime, running_change, change);
+               Change change = pimpl_->lookupChange(i);
+               Changes::lyxMarkChange(os, column, running_change, change);
                running_change = change;
 
                if (i == size())
@@ -216,9 +1179,11 @@ void Paragraph::write(Buffer const & buf, ostream & os,
                        }
                        // this check is to amend a bug. LyX sometimes
                        // inserts '\0' this could cause problems.
-                       if (c != '\0')
-                               os << c;
-                       else
+                       if (c != '\0') {
+                               std::vector<char> tmp = ucs4_to_utf8(c);
+                               tmp.push_back('\0');
+                               os << &tmp[0];
+                       } else
                                lyxerr << "ERROR (Paragraph::writeFile):"
                                        " NULL char in structure." << endl;
                        ++column;
@@ -236,55 +1201,60 @@ void Paragraph::validate(LaTeXFeatures & features) const
 }
 
 
-void Paragraph::eraseIntern(lyx::pos_type pos)
+bool Paragraph::eraseChar(pos_type pos, bool trackChanges)
 {
-       pimpl_->eraseIntern(pos);
+       return pimpl_->eraseChar(pos, trackChanges);
 }
 
 
-bool Paragraph::erase(pos_type pos)
+int Paragraph::eraseChars(pos_type start, pos_type end, bool trackChanges)
 {
-       return pimpl_->erase(pos);
+       return pimpl_->eraseChars(start, end, trackChanges);
 }
 
 
-int Paragraph::erase(pos_type start, pos_type end)
+void Paragraph::insert(pos_type start, docstring const & str,
+                       LyXFont const & font, Change const & change)
 {
-       return pimpl_->erase(start, end);
+       for (size_t i = 0, n = str.size(); i != n ; ++i)
+               insertChar(start + i, str[i], font, change);
 }
 
 
-void Paragraph::insert(pos_type start, string const & str,
-                      LyXFont const & font)
+void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
+                           bool trackChanges)
 {
-       for (size_t i = 0, n = str.size(); i != n ; ++i)
-               insertChar(start + i, str[i], font);
+       pimpl_->insertChar(pos, c, Change(trackChanges ?
+                          Change::INSERTED : Change::UNCHANGED));
 }
 
 
 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
-                          Change change)
+                           LyXFont const & font, bool trackChanges)
 {
-       pimpl_->insertChar(pos, c, change);
+       pimpl_->insertChar(pos, c, Change(trackChanges ?
+                          Change::INSERTED : Change::UNCHANGED));
+       setFont(pos, font);
 }
 
 
 void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
-                          LyXFont const & font, Change change)
+                           LyXFont const & font, Change const & change)
 {
        pimpl_->insertChar(pos, c, change);
        setFont(pos, font);
 }
 
 
-void Paragraph::insertInset(pos_type pos, InsetBase * inset, Change change)
+void Paragraph::insertInset(pos_type pos, InsetBase * inset,
+                            Change const & change)
 {
        pimpl_->insertInset(pos, inset, change);
 }
 
 
 void Paragraph::insertInset(pos_type pos, InsetBase * inset,
-                           LyXFont const & font, Change change)
+                            LyXFont const & font, Change const & change)
 {
        pimpl_->insertInset(pos, inset, change);
        setFont(pos, font);
@@ -322,10 +1292,10 @@ LyXFont const Paragraph::getFontSettings(BufferParams const & bparams,
 }
 
 
-FontSpan Paragraph::fontSpan(lyx::pos_type pos) const
+FontSpan Paragraph::fontSpan(pos_type pos) const
 {
        BOOST_ASSERT(pos <= size());
-       lyx::pos_type start = 0;
+       pos_type start = 0;
 
        Pimpl::FontList::const_iterator cit = pimpl_->fontlist.begin();
        Pimpl::FontList::const_iterator end = pimpl_->fontlist.end();
@@ -335,8 +1305,8 @@ FontSpan Paragraph::fontSpan(lyx::pos_type pos) const
                                return FontSpan(std::max(start, beginOfBody()),
                                                cit->pos());
                        else
-                               return FontSpan(start, 
-                                               std::min(beginOfBody() - 1, 
+                               return FontSpan(start,
+                                               std::min(beginOfBody() - 1,
                                                         cit->pos()));
                }
                start = cit->pos() + 1;
@@ -382,7 +1352,7 @@ LyXFont const Paragraph::getFont(BufferParams const & bparams, pos_type pos,
        LyXFont font = getFontSettings(bparams, pos);
        font.realize(layoutfont);
        font.realize(outerfont);
-       font.realize(bparams.getLyXTextClass().defaultfont());
+       font.realize(bparams.getFont());
 
        return font;
 }
@@ -394,7 +1364,7 @@ LyXFont const Paragraph::getLabelFont
        LyXFont tmpfont = layout()->labelfont;
        tmpfont.setLanguage(getParLanguage(bparams));
        tmpfont.realize(outerfont);
-       tmpfont.realize(bparams.getLyXTextClass().defaultfont());
+       tmpfont.realize(bparams.getFont());
        return tmpfont;
 }
 
@@ -405,7 +1375,7 @@ LyXFont const Paragraph::getLayoutFont
        LyXFont tmpfont = layout()->font;
        tmpfont.setLanguage(getParLanguage(bparams));
        tmpfont.realize(outerfont);
-       tmpfont.realize(bparams.getLyXTextClass().defaultfont());
+       tmpfont.realize(bparams.getFont());
        return tmpfont;
 }
 
@@ -557,18 +1527,22 @@ void Paragraph::makeSameLayout(Paragraph const & par)
 }
 
 
-int Paragraph::stripLeadingSpaces()
+bool Paragraph::stripLeadingSpaces(bool trackChanges)
 {
        if (isFreeSpacing())
-               return 0;
+               return false;
 
-       int i = 0;
-       while (!empty() && (isNewline(0) || isLineSeparator(0))) {
-               erase(0);
-               ++i;
+       int pos = 0;
+       int count = 0;
+
+       while (pos < size() && (isNewline(pos) || isLineSeparator(pos))) {
+               if (eraseChar(pos, trackChanges))
+                       ++count;
+               else
+                       ++pos;
        }
 
-       return i;
+       return count > 0 || pos > 0;
 }
 
 
@@ -578,13 +1552,13 @@ bool Paragraph::hasSameLayout(Paragraph const & par) const
 }
 
 
-Paragraph::depth_type Paragraph::getDepth() const
+depth_type Paragraph::getDepth() const
 {
        return params().depth();
 }
 
 
-Paragraph::depth_type Paragraph::getMaxDepthAfter() const
+depth_type Paragraph::getMaxDepthAfter() const
 {
        if (layout()->isEnvironment())
                return params().depth() + 1;
@@ -595,18 +1569,21 @@ Paragraph::depth_type Paragraph::getMaxDepthAfter() const
 
 char Paragraph::getAlign() const
 {
-       return params().align();
+       if (params().align() == LYX_ALIGN_LAYOUT)
+               return layout()->align;
+       else
+               return params().align();
 }
 
 
-string const & Paragraph::getLabelstring() const
+docstring const & Paragraph::getLabelstring() const
 {
        return params().labelString();
 }
 
 
 // the next two functions are for the manual labels
-string const Paragraph::getLabelWidthString() const
+docstring const Paragraph::getLabelWidthString() const
 {
        if (!params().labelWidthString().empty())
                return params().labelWidthString();
@@ -615,16 +1592,59 @@ string const Paragraph::getLabelWidthString() const
 }
 
 
-void Paragraph::setLabelWidthString(string const & s)
+void Paragraph::setLabelWidthString(docstring const & s)
 {
        params().labelWidthString(s);
 }
 
 
+docstring const Paragraph::translateIfPossible(docstring const & s,
+               BufferParams const & bparams) const
+{
+       if (!support::isAscii(s) || s.empty()) {
+               // This must be a user defined layout. We cannot translate
+               // this, since gettext accepts only ascii keys.
+               return s;
+       }
+       // Probably standard layout, try to translate
+       Messages & m = getMessages(getParLanguage(bparams)->code());
+       return m.get(to_ascii(s));
+}
+
+
+docstring Paragraph::expandLabel(LyXLayout_ptr const & layout,
+               BufferParams const & bparams, bool process_appendix) const
+{
+       LyXTextClass const & tclass = bparams.getLyXTextClass();
+
+       docstring fmt;
+       if (process_appendix && params().appendix())
+               fmt = translateIfPossible(layout->labelstring_appendix(),
+                       bparams);
+       else
+               fmt = translateIfPossible(layout->labelstring(), bparams);
+
+       // handle 'inherited level parts' in 'fmt',
+       // i.e. the stuff between '@' in   '@Section@.\arabic{subsection}'
+       size_t const i = fmt.find('@', 0);
+       if (i != docstring::npos) {
+               size_t const j = fmt.find('@', i + 1);
+               if (j != docstring::npos) {
+                       docstring parent(fmt, i + 1, j - i - 1);
+                       // FIXME UNICODE
+                       docstring label = expandLabel(tclass[to_utf8(parent)], bparams);
+                       fmt = docstring(fmt, 0, i) + label + docstring(fmt, j + 1, docstring::npos);
+               }
+       }
+
+       return tclass.counters().counterLabel(fmt);
+}
+
+
 void Paragraph::applyLayout(LyXLayout_ptr const & new_layout)
 {
        layout(new_layout);
-       params().labelWidthString(string());
+       params().labelWidthString(docstring());
        params().align(LYX_ALIGN_LAYOUT);
        params().spacing(Spacing(Spacing::Default));
 }
@@ -650,8 +1670,8 @@ void Paragraph::setBeginOfBody()
        pos_type end = size();
        if (i < end && !isNewline(i)) {
                ++i;
-               char previous_char = 0;
-               char temp = 0;
+               char_type previous_char = 0;
+               char_type temp = 0;
                if (i < end) {
                        previous_char = text_[i];
                        if (!isNewline(i)) {
@@ -732,7 +1752,22 @@ string const corrected_env(string const & suffix, string const & env,
                output += correction(env);
        else
                output += env;
-       return output + "}";
+       output += "}";
+       if (suffix == "\\begin")
+               output += "\n";
+       return output;
+}
+
+
+void adjust_row_column(string const & str, TexRow & texrow, int & column)
+{
+       if (!contains(str, "\n"))
+               column += str.size();
+       else {
+               string tmp;
+               texrow.newline();
+               column = rsplit(str, tmp, '\n').size();
+       }
 }
 
 } // namespace anon
@@ -740,7 +1775,8 @@ string const corrected_env(string const & suffix, string const & env,
 
 // This could go to ParagraphParameters if we want to
 int Paragraph::startTeXParParams(BufferParams const & bparams,
-                                ostream & os, bool moving_arg) const
+                                 odocstream & os, TexRow & texrow, 
+                                bool moving_arg) const
 {
        int column = 0;
 
@@ -777,8 +1813,8 @@ int Paragraph::startTeXParParams(BufferParams const & bparams,
                        output = corrected_env("\\begin", "flushleft", ownerCode());
                else
                        output = corrected_env("\\begin", "flushright", ownerCode());
-               os << output;
-               column += output.size();
+               os << from_ascii(output);
+               adjust_row_column(output, texrow, column);
                break;
        } case LYX_ALIGN_RIGHT: {
                string output;
@@ -786,14 +1822,14 @@ int Paragraph::startTeXParParams(BufferParams const & bparams,
                        output = corrected_env("\\begin", "flushright", ownerCode());
                else
                        output = corrected_env("\\begin", "flushleft", ownerCode());
-               os << output;
-               column += output.size();
+               os << from_ascii(output);
+               adjust_row_column(output, texrow, column);
                break;
        } case LYX_ALIGN_CENTER: {
                string output;
                output = corrected_env("\\begin", "center", ownerCode());
-               os << output;
-               column += output.size();
+               os << from_ascii(output);
+               adjust_row_column(output, texrow, column);
                break;
        }
        }
@@ -803,8 +1839,9 @@ int Paragraph::startTeXParParams(BufferParams const & bparams,
 
 
 // This could go to ParagraphParameters if we want to
-int Paragraph::endTeXParParams(BufferParams const & bparams,
-                              ostream & os, bool moving_arg) const
+int Paragraph::endTeXParParams(BufferParams const & bparams,  
+                               odocstream & os, TexRow & texrow,
+                              bool moving_arg) const
 {
        int column = 0;
 
@@ -833,26 +1870,26 @@ int Paragraph::endTeXParParams(BufferParams const & bparams,
        case LYX_ALIGN_LEFT: {
                string output;
                if (getParLanguage(bparams)->babel() != "hebrew")
-                       output = corrected_env("\\par\\end", "flushleft", ownerCode());
+                       output = corrected_env("\n\\par\\end", "flushleft", ownerCode());
                else
-                       output = corrected_env("\\par\\end", "flushright", ownerCode());
-               os << output;
-               column += output.size();
+                       output = corrected_env("\n\\par\\end", "flushright", ownerCode());
+               os << from_ascii(output);
+               adjust_row_column(output, texrow, column);
                break;
        } case LYX_ALIGN_RIGHT: {
                string output;
                if (getParLanguage(bparams)->babel() != "hebrew")
-                       output = corrected_env("\\par\\end", "flushright", ownerCode());
+                       output = corrected_env("\n\\par\\end", "flushright", ownerCode());
                else
-                       output = corrected_env("\\par\\end", "flushleft", ownerCode());
-               os << output;
-               column += output.size();
+                       output = corrected_env("\n\\par\\end", "flushleft", ownerCode());
+               os << from_ascii(output);
+               adjust_row_column(output, texrow, column);
                break;
        } case LYX_ALIGN_CENTER: {
                string output;
-               output = corrected_env("\\par\\end", "center", ownerCode());
-               os << output;
-               column += output.size();
+               output = corrected_env("\n\\par\\end", "center", ownerCode());
+               os << from_ascii(output);
+               adjust_row_column(output, texrow, column);
                break;
        }
        }
@@ -865,10 +1902,10 @@ int Paragraph::endTeXParParams(BufferParams const & bparams,
 bool Paragraph::simpleTeXOnePar(Buffer const & buf,
                                BufferParams const & bparams,
                                LyXFont const & outerfont,
-                               ostream & os, TexRow & texrow,
+                               odocstream & os, TexRow & texrow,
                                OutputParams const & runparams) const
 {
-       lyxerr[Debug::LATEX] << "SimpleTeXOnePar...     " << this << endl;
+       LYXERR(Debug::LATEX) << "SimpleTeXOnePar...     " << this << endl;
 
        bool return_value = false;
 
@@ -877,7 +1914,8 @@ bool Paragraph::simpleTeXOnePar(Buffer const & buf,
        // well we have to check if we are in an inset with unlimited
        // length (all in one row) if that is true then we don't allow
        // any special options in the paragraph and also we don't allow
-       // any environment other then "Standard" to be valid!
+       // any environment other than the default layout of the text class
+       // to be valid!
        bool asdefault = forceDefaultParagraphs();
 
        if (asdefault) {
@@ -886,16 +1924,20 @@ bool Paragraph::simpleTeXOnePar(Buffer const & buf,
                style = layout();
        }
 
+       // Current base font for all inherited font changes, without any
+       // change caused by an individual character, except for the language:
+       // It is set to the language of the first character.
+       // As long as we are in the label, this font is the base font of the
+       // label. Before the first body character it is set to the base font
+       // of the body.
        LyXFont basefont;
 
-       LaTeXFeatures features(buf, bparams, runparams.nice);
-
        // output change tracking marks only if desired,
        // if dvipost is installed,
        // and with dvi/ps (other formats don't work)
-       bool const output = bparams.output_changes
+       bool const output = bparams.outputChanges
                && runparams.flavor == OutputParams::LATEX
-               && features.isAvailable("dvipost");
+               && LaTeXFeatures::isAvailable("dvipost");
 
        // Maybe we have to create a optional argument.
        pos_type body_pos = beginOfBody();
@@ -916,7 +1958,7 @@ bool Paragraph::simpleTeXOnePar(Buffer const & buf,
        // Do we have an open font change?
        bool open_font = false;
 
-       Change::Type running_change = Change::UNCHANGED;
+       Change::Type runningChangeType = Change::UNCHANGED;
 
        texrow.start(id(), 0);
 
@@ -927,21 +1969,26 @@ bool Paragraph::simpleTeXOnePar(Buffer const & buf,
                        ++column;
                }
                if (!asdefault)
-                       column += startTeXParParams(bparams, os,
+                       column += startTeXParParams(bparams, os, texrow,
                                                    runparams.moving_arg);
        }
 
        for (pos_type i = 0; i < size(); ++i) {
-               ++column;
                // First char in paragraph or after label?
                if (i == body_pos) {
                        if (body_pos > 0) {
                                if (open_font) {
-                                       column += running_font.latexWriteEndChanges(os, basefont, basefont);
+                                       column += running_font.latexWriteEndChanges(
+                                               os, basefont, basefont);
                                        open_font = false;
                                }
                                basefont = getLayoutFont(bparams, outerfont);
                                running_font = basefont;
+
+                               column += Changes::latexMarkChange(os,
+                                               runningChangeType, Change::UNCHANGED, output);
+                               runningChangeType = Change::UNCHANGED;
+
                                os << "}] ";
                                column +=3;
                        }
@@ -951,46 +1998,52 @@ bool Paragraph::simpleTeXOnePar(Buffer const & buf,
                        }
 
                        if (!asdefault)
-                               column += startTeXParParams(bparams, os,
+                               column += startTeXParParams(bparams, os, 
+                                                           texrow,
                                                            runparams.moving_arg);
                }
 
-               value_type c = getChar(i);
+               Change::Type changeType = pimpl_->lookupChange(i).type;
+
+               // do not output text which is marked deleted
+               // if change tracking output is disabled
+               if (!output && changeType == Change::DELETED) {
+                       runningChangeType = changeType;
+                       continue;
+               }
+
+               ++column;
+               
+               column += Changes::latexMarkChange(os, runningChangeType,
+                       changeType, output);
+               runningChangeType = changeType;
+
+               value_type const c = getChar(i);
 
                // Fully instantiated font
-               LyXFont font = getFont(bparams, i, outerfont);
+               LyXFont const font = getFont(bparams, i, outerfont);
 
                LyXFont const last_font = running_font;
 
-               // Spaces at end of font change are simulated to be
-               // outside font change, i.e. we write "\textXX{text} "
-               // rather than "\textXX{text }". (Asger)
-               if (open_font && c == ' ' && i <= size() - 2) {
-                       LyXFont const & next_font = getFont(bparams, i + 1, outerfont);
-                       if (next_font != running_font && next_font != font) {
-                               font = next_font;
-                       }
-               }
-
-               // We end font definition before blanks
+               // Do we need to close the previous font?
                if (open_font &&
                    (font != running_font ||
                     font.language() != running_font.language()))
                {
-                       column += running_font.latexWriteEndChanges(os,
-                                                                   basefont,
-                                                                   (i == body_pos-1) ? basefont : font);
+                       column += running_font.latexWriteEndChanges(
+                                       os, basefont,
+                                       (i == body_pos-1) ? basefont : font);
                        running_font = basefont;
                        open_font = false;
                }
 
-               // Blanks are printed before start of fontswitch
-               if (c == ' ') {
-                       // Do not print the separation of the optional argument
-                       if (i != body_pos - 1) {
-                               pimpl_->simpleTeXBlanks(os, texrow, i,
-                                                      column, font, *style);
-                       }
+               // Switch file encoding if necessary
+               int const count = switchEncoding(os, bparams,
+                               runparams.moving_arg, *(runparams.encoding),
+                               *(font.language()->encoding()));
+               if (count > 0) {
+                       column += count;
+                       runparams.encoding = font.language()->encoding();
                }
 
                // Do we need to change font?
@@ -999,35 +2052,39 @@ bool Paragraph::simpleTeXOnePar(Buffer const & buf,
                        i != body_pos - 1)
                {
                        column += font.latexWriteStartChanges(os, basefont,
-                                                             last_font);
+                                                             last_font);
                        running_font = font;
                        open_font = true;
                }
 
-               Change::Type change = pimpl_->lookupChange(i);
-
-               column += Changes::latexMarkChange(os, running_change,
-                       change, output);
-               running_change = change;
-
-               // do not output text which is marked deleted
-               // if change tracking output is not desired
-               if (output || running_change != Change::DELETED) {
-                       OutputParams rp = runparams;
-                       rp.free_spacing = style->free_spacing;
-                       rp.local_font = &font;
-                       rp.intitle = style->intitle;
-                       pimpl_->simpleTeXSpecialChars(buf, bparams,
-                                               os, texrow, rp,
-                                               font, running_font,
-                                               basefont, outerfont, open_font,
-                                               running_change,
-                                               *style, i, column, c);
+               if (c == ' ') {
+                       // Do not print the separation of the optional argument
+                       // if style->pass_thru is false. This works because
+                       // simpleTeXSpecialChars ignores spaces if
+                       // style->pass_thru is false.
+                       if (i != body_pos - 1) {
+                               if (pimpl_->simpleTeXBlanks(
+                                               *(runparams.encoding), os, texrow,
+                                               i, column, font, *style))
+                                       // A surrogate pair was output. We
+                                       // must not call simpleTeXSpecialChars
+                                       // in this iteration, since
+                                       // simpleTeXBlanks incremented i, and
+                                       // simpleTeXSpecialChars would output
+                                       // the combining character again.
+                                       continue;
+                       }
                }
-       }
 
-       column += Changes::latexMarkChange(os,
-                       running_change, Change::UNCHANGED, output);
+               OutputParams rp = runparams;
+               rp.free_spacing = style->free_spacing;
+               rp.local_font = &font;
+               rp.intitle = style->intitle;
+               pimpl_->simpleTeXSpecialChars(buf, bparams, os,
+                                       texrow, rp, running_font,
+                                       basefont, outerfont, open_font,
+                                       runningChangeType, *style, i, column, c);
+       }
 
        // If we have an open font definition, we have to close it
        if (open_font) {
@@ -1035,8 +2092,7 @@ bool Paragraph::simpleTeXOnePar(Buffer const & buf,
                if (next_) {
                        running_font
                                .latexWriteEndChanges(os, basefont,
-                                                     next_->getFont(bparams,
-                                                     0, outerfont));
+                                       next_->getFont(bparams, 0, outerfont));
                } else {
                        running_font.latexWriteEndChanges(os, basefont,
                                                          basefont);
@@ -1047,10 +2103,13 @@ bool Paragraph::simpleTeXOnePar(Buffer const & buf,
 //#warning there as we start another \selectlanguage with the next paragraph if
 //#warning we are in need of this. This should be fixed sometime (Jug)
 #endif
-               running_font.latexWriteEndChanges(os, basefont,  basefont);
+               running_font.latexWriteEndChanges(os, basefont, basefont);
 #endif
        }
 
+       column += Changes::latexMarkChange(os,
+                       runningChangeType, Change::UNCHANGED, output);
+
        // Needed if there is an optional argument but no contents.
        if (body_pos > 0 && body_pos == size()) {
                os << "}]~";
@@ -1058,28 +2117,17 @@ bool Paragraph::simpleTeXOnePar(Buffer const & buf,
        }
 
        if (!asdefault) {
-               column += endTeXParParams(bparams, os, runparams.moving_arg);
+               column += endTeXParParams(bparams, os, texrow, 
+                                         runparams.moving_arg);
        }
 
-       lyxerr[Debug::LATEX] << "SimpleTeXOnePar...done " << this << endl;
+       LYXERR(Debug::LATEX) << "SimpleTeXOnePar...done " << this << endl;
        return return_value;
 }
 
 
 namespace {
 
-// checks, if newcol chars should be put into this line
-// writes newline, if necessary.
-void sgmlLineBreak(ostream & os, string::size_type & colcount,
-                         string::size_type newcol)
-{
-       colcount += newcol;
-       if (colcount > lyxrc.ascii_linelen) {
-               os << "\n";
-               colcount = newcol; // assume write after this call
-       }
-}
-
 enum PAR_TAG {
        PAR_NONE=0,
        TT = 1,
@@ -1121,201 +2169,6 @@ void reset(PAR_TAG & p1, PAR_TAG const & p2)
 } // anon
 
 
-// Handle internal paragraph parsing -- layout already processed.
-void Paragraph::simpleLinuxDocOnePar(Buffer const & buf,
-                                    ostream & os,
-                                    LyXFont const & outerfont,
-                                    OutputParams const & runparams,
-                                    lyx::depth_type /*depth*/) const
-{
-       LyXLayout_ptr const & style = layout();
-
-       string::size_type char_line_count = 5;     // Heuristic choice ;-)
-
-       // gets paragraph main font
-       LyXFont font_old;
-       bool desc_on;
-       if (style->labeltype == LABEL_MANUAL) {
-               font_old = style->labelfont;
-               desc_on = true;
-       } else {
-               font_old = style->font;
-               desc_on = false;
-       }
-
-       LyXFont::FONT_FAMILY family_type = LyXFont::ROMAN_FAMILY;
-       LyXFont::FONT_SERIES series_type = LyXFont::MEDIUM_SERIES;
-       LyXFont::FONT_SHAPE  shape_type  = LyXFont::UP_SHAPE;
-       bool is_em = false;
-
-       stack<PAR_TAG> tag_state;
-       // parsing main loop
-       for (pos_type i = 0; i < size(); ++i) {
-
-               PAR_TAG tag_close = PAR_NONE;
-               list < PAR_TAG > tag_open;
-
-               LyXFont const font = getFont(buf.params(), i, outerfont);
-
-               if (font_old.family() != font.family()) {
-                       switch (family_type) {
-                       case LyXFont::SANS_FAMILY:
-                               tag_close |= SF;
-                               break;
-                       case LyXFont::TYPEWRITER_FAMILY:
-                               tag_close |= TT;
-                               break;
-                       default:
-                               break;
-                       }
-
-                       family_type = font.family();
-
-                       switch (family_type) {
-                       case LyXFont::SANS_FAMILY:
-                               tag_open.push_back(SF);
-                               break;
-                       case LyXFont::TYPEWRITER_FAMILY:
-                               tag_open.push_back(TT);
-                               break;
-                       default:
-                               break;
-                       }
-               }
-
-               if (font_old.series() != font.series()) {
-                       switch (series_type) {
-                       case LyXFont::BOLD_SERIES:
-                               tag_close |= BF;
-                               break;
-                       default:
-                               break;
-                       }
-
-                       series_type = font.series();
-
-                       switch (series_type) {
-                       case LyXFont::BOLD_SERIES:
-                               tag_open.push_back(BF);
-                               break;
-                       default:
-                               break;
-                       }
-
-               }
-
-               if (font_old.shape() != font.shape()) {
-                       switch (shape_type) {
-                       case LyXFont::ITALIC_SHAPE:
-                               tag_close |= IT;
-                               break;
-                       case LyXFont::SLANTED_SHAPE:
-                               tag_close |= SL;
-                               break;
-                       default:
-                               break;
-                       }
-
-                       shape_type = font.shape();
-
-                       switch (shape_type) {
-                       case LyXFont::ITALIC_SHAPE:
-                               tag_open.push_back(IT);
-                               break;
-                       case LyXFont::SLANTED_SHAPE:
-                               tag_open.push_back(SL);
-                               break;
-                       default:
-                               break;
-                       }
-               }
-               // handle <em> tag
-               if (font_old.emph() != font.emph()) {
-                       if (font.emph() == LyXFont::ON) {
-                               tag_open.push_back(EM);
-                               is_em = true;
-                       }
-                       else if (is_em) {
-                               tag_close |= EM;
-                               is_em = false;
-                       }
-               }
-
-               list < PAR_TAG > temp;
-               while (!tag_state.empty() && tag_close) {
-                       PAR_TAG k =  tag_state.top();
-                       tag_state.pop();
-                       os << "</" << tag_name(k) << '>';
-                       if (tag_close & k)
-                               reset(tag_close,k);
-                       else
-                               temp.push_back(k);
-               }
-
-               for(list< PAR_TAG >::const_iterator j = temp.begin();
-                   j != temp.end(); ++j) {
-                       tag_state.push(*j);
-                       os << '<' << tag_name(*j) << '>';
-               }
-
-               for(list< PAR_TAG >::const_iterator j = tag_open.begin();
-                   j != tag_open.end(); ++j) {
-                       tag_state.push(*j);
-                       os << '<' << tag_name(*j) << '>';
-               }
-
-               value_type c = getChar(i);
-
-
-               if (c == Paragraph::META_INSET) {
-                       getInset(i)->linuxdoc(buf, os, runparams);
-                       font_old = font;
-                       continue;
-               }
-
-               if (style->latexparam() == "CDATA") {
-                       // "TeX"-Mode on == > SGML-Mode on.
-                       if (c != '\0')
-                               os << c;
-                       ++char_line_count;
-               } else {
-                       bool ws;
-                       string str;
-                       boost::tie(ws, str) = sgml::escapeChar(c);
-                       if (ws && !isFreeSpacing()) {
-                               // in freespacing mode, spaces are
-                               // non-breaking characters
-                               if (desc_on) { // if char is ' ' then...
-                                       ++char_line_count;
-                                       sgmlLineBreak(os, char_line_count, 6);
-                                       os << "</tag>";
-                                       desc_on = false;
-                               } else  {
-                                       sgmlLineBreak(os, char_line_count, 1);
-                                       os << c;
-                               }
-                       } else {
-                               os << str;
-                               char_line_count += str.length();
-                       }
-               }
-               font_old = font;
-       }
-
-       while (!tag_state.empty()) {
-               os << "</" << tag_name(tag_state.top()) << '>';
-               tag_state.pop();
-       }
-
-       // resets description flag correctly
-       if (desc_on) {
-               // <tag> not closed...
-               sgmlLineBreak(os, char_line_count, 6);
-               os << "</tag>";
-       }
-}
-
-
 bool Paragraph::emptyTag() const
 {
        for (pos_type i = 0; i < size(); ++i) {
@@ -1348,7 +2201,7 @@ string Paragraph::getID(Buffer const & buf, OutputParams const & runparams) cons
                        InsetBase::Code lyx_code = inset->lyxCode();
                        if (lyx_code == InsetBase::LABEL_CODE) {
                                string const id = static_cast<InsetCommand const *>(inset)->getContents();
-                               return "id=\"" + sgml::cleanID(buf, runparams, id) + "\"";
+                               return "id='" + to_utf8(sgml::cleanID(buf, runparams, from_utf8(id))) + "'";
                        }
                }
 
@@ -1357,7 +2210,7 @@ string Paragraph::getID(Buffer const & buf, OutputParams const & runparams) cons
 }
 
 
-pos_type Paragraph::getFirstWord(Buffer const & buf, ostream & os, OutputParams const & runparams) const
+pos_type Paragraph::getFirstWord(Buffer const & buf, odocstream & os, OutputParams const & runparams) const
 {
        pos_type i;
        for (i = 0; i < size(); ++i) {
@@ -1368,12 +2221,8 @@ pos_type Paragraph::getFirstWord(Buffer const & buf, ostream & os, OutputParams
                        value_type c = getChar(i);
                        if (c == ' ')
                                break;
-                       bool ws;
-                       string str;
-                       boost::tie(ws, str) = sgml::escapeChar(c);
-
-                       os << str;
-               }
+                       os << sgml::escapeChar(c);
+               }
        }
        return i;
 }
@@ -1397,7 +2246,7 @@ bool Paragraph::onlyText(Buffer const & buf, LyXFont const & outerfont, pos_type
 
 
 void Paragraph::simpleDocBookOnePar(Buffer const & buf,
-                                   ostream & os,
+                                   odocstream & os,
                                    OutputParams const & runparams,
                                    LyXFont const & outerfont,
                                    pos_type initial) const
@@ -1431,14 +2280,11 @@ void Paragraph::simpleDocBookOnePar(Buffer const & buf,
                        inset->docbook(buf, os, runparams);
                } else {
                        value_type c = getChar(i);
-                       bool ws;
-                       string str;
-                       boost::tie(ws, str) = sgml::escapeChar(c);
 
                        if (style->pass_thru)
-                               os << c;
+                                os.put(c);
                        else
-                               os << str;
+                                os << sgml::escapeChar(c);
                }
                font_old = font;
        }
@@ -1464,7 +2310,7 @@ bool Paragraph::isNewline(pos_type pos) const
 bool Paragraph::isLineSeparator(pos_type pos) const
 {
        value_type const c = getChar(pos);
-       return IsLineSeparatorChar(c)
+       return isLineSeparatorChar(c)
                || (c == Paragraph::META_INSET && getInset(pos) &&
                getInset(pos)->isLineSeparator());
 }
@@ -1477,7 +2323,7 @@ bool Paragraph::isLetter(pos_type pos) const
                return getInset(pos)->isLetter();
        else {
                value_type const c = getChar(pos);
-               return IsLetterChar(c) || IsDigit(c);
+               return isLetterChar(c) || isDigit(c);
        }
 }
 
@@ -1497,7 +2343,7 @@ Paragraph::getParLanguage(BufferParams const & bparams) const
 bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
 {
        return lyxrc.rtl_support
-               && getParLanguage(bparams)->RightToLeft()
+               && getParLanguage(bparams)->rightToLeft()
                && ownerCode() != InsetBase::ERT_CODE;
 }
 
@@ -1505,7 +2351,8 @@ bool Paragraph::isRightToLeftPar(BufferParams const & bparams) const
 void Paragraph::changeLanguage(BufferParams const & bparams,
                               Language const * from, Language const * to)
 {
-       for (pos_type i = 0; i < size(); ++i) {
+       // change language including dummy font change at the end
+       for (pos_type i = 0; i <= size(); ++i) {
                LyXFont font = getFontSettings(bparams, i);
                if (font.language() == from) {
                        font.setLanguage(to);
@@ -1532,67 +2379,27 @@ bool Paragraph::isMultiLingual(BufferParams const & bparams) const
 
 // Convert the paragraph to a string.
 // Used for building the table of contents
-string const Paragraph::asString(Buffer const & buffer, bool label) const
-{
-       OutputParams runparams;
-       return asString(buffer, runparams, label);
-}
-
-
-string const Paragraph::asString(Buffer const & buffer,
-                                OutputParams const & runparams,
-                                bool label) const
+docstring const Paragraph::asString(Buffer const & buffer, bool label) const
 {
-#if 0
-       string s;
-       if (label && !params().labelString().empty())
-               s += params().labelString() + ' ';
-
-       for (pos_type i = 0; i < size(); ++i) {
-               value_type c = getChar(i);
-               if (IsPrintable(c))
-                       s += c;
-               else if (c == META_INSET &&
-                        getInset(i)->lyxCode() == InsetBase::MATH_CODE) {
-                       ostringstream os;
-                       getInset(i)->plaintext(buffer, os, runparams);
-                       s += subst(STRCONV(os.str()),'\n',' ');
-               }
-       }
-
-       return s;
-#else
-       // This should really be done by the caller and not here.
-       string ret = asString(buffer, runparams, 0, size(), label);
-       return subst(ret, '\n', ' ');
-#endif
+       return asString(buffer, 0, size(), label);
 }
 
 
-string const Paragraph::asString(Buffer const & buffer,
+docstring const Paragraph::asString(Buffer const & buffer,
                                 pos_type beg, pos_type end, bool label) const
 {
 
-       OutputParams const runparams;
-       return asString(buffer, runparams, beg, end, label);
-}
-
-
-string const Paragraph::asString(Buffer const & buffer,
-                                OutputParams const & runparams,
-                                pos_type beg, pos_type end, bool label) const
-{
-       ostringstream os;
+       odocstringstream os;
 
        if (beg == 0 && label && !params().labelString().empty())
                os << params().labelString() << ' ';
 
        for (pos_type i = beg; i < end; ++i) {
                value_type const c = getUChar(buffer.params(), i);
-               if (IsPrintable(c))
-                       os << c;
+               if (isPrintable(c))
+                       os.put(c);
                else if (c == META_INSET)
-                       getInset(i)->textString(buffer, os, runparams);
+                       getInset(i)->textString(buffer, os);
        }
 
        return os.str();
@@ -1605,83 +2412,46 @@ void Paragraph::setInsetOwner(InsetBase * inset)
 }
 
 
-void Paragraph::setContentsFromPar(Paragraph const & par)
-{
-       pimpl_->setContentsFromPar(par);
-}
-
-
-void Paragraph::trackChanges(Change::Type type)
-{
-       pimpl_->trackChanges(type);
-}
-
-
-void Paragraph::untrackChanges()
-{
-       pimpl_->untrackChanges();
-}
-
-
-void Paragraph::cleanChanges()
-{
-       pimpl_->cleanChanges();
-}
-
-
-Change::Type Paragraph::lookupChange(lyx::pos_type pos) const
+Change const & Paragraph::lookupChange(pos_type pos) const
 {
        BOOST_ASSERT(pos <= size());
        return pimpl_->lookupChange(pos);
 }
 
 
-Change const Paragraph::lookupChangeFull(lyx::pos_type pos) const
-{
-       BOOST_ASSERT(pos <= size());
-       return pimpl_->lookupChangeFull(pos);
-}
-
-
 bool Paragraph::isChanged(pos_type start, pos_type end) const
 {
        return pimpl_->isChanged(start, end);
 }
 
 
-bool Paragraph::isChangeEdited(pos_type start, pos_type end) const
-{
-       return pimpl_->isChangeEdited(start, end);
-}
-
-
-void Paragraph::setChange(lyx::pos_type pos, Change::Type type)
+bool Paragraph::isMergedOnEndOfParDeletion(bool trackChanges) const
 {
-       pimpl_->setChange(pos, type);
+       return pimpl_->isMergedOnEndOfParDeletion(trackChanges);
 }
 
 
-void Paragraph::setChangeFull(lyx::pos_type pos, Change change)
+void Paragraph::setChange(Change const & change)
 {
-       pimpl_->setChangeFull(pos, change);
+       pimpl_->setChange(change);
 }
 
 
-void Paragraph::markErased(bool erased)
+void Paragraph::setChange(pos_type pos, Change const & change)
 {
-       pimpl_->markErased(erased);
+       pimpl_->setChange(pos, change);
 }
 
 
-void Paragraph::acceptChange(pos_type start, pos_type end)
+void Paragraph::acceptChanges(BufferParams const & bparams, pos_type start, pos_type end)
 {
-       return pimpl_->acceptChange(start, end);
+       return pimpl_->acceptChanges(bparams, start, end);
 }
 
 
-void Paragraph::rejectChange(pos_type start, pos_type end)
+void Paragraph::rejectChanges(BufferParams const & bparams, pos_type start, pos_type end)
 {
-       return pimpl_->rejectChange(start, end);
+       return pimpl_->rejectChanges(bparams, start, end);
 }
 
 
@@ -1716,18 +2486,6 @@ InsetBase::Code Paragraph::ownerCode() const
 }
 
 
-void Paragraph::clearContents()
-{
-       text_.clear();
-}
-
-
-void Paragraph::setChar(pos_type pos, value_type c)
-{
-       text_[pos] = c;
-}
-
-
 ParagraphParameters & Paragraph::params()
 {
        return pimpl_->params;
@@ -1759,72 +2517,17 @@ bool Paragraph::allowEmpty() const
 }
 
 
-Row & Paragraph::getRow(pos_type pos, bool boundary)
-{
-       BOOST_ASSERT(!rows().empty());
-
-       // If boundary is set we should return the row on which
-       // the character before is inside.
-       if (pos > 0 && boundary)
-               --pos;
-
-       RowList::iterator rit = rows_.end();
-       RowList::iterator const begin = rows_.begin();
-
-       for (--rit; rit != begin && rit->pos() > pos; --rit)
-               ;
-
-       return *rit;
-}
-
-
-Row const & Paragraph::getRow(pos_type pos, bool boundary) const
-{
-       BOOST_ASSERT(!rows().empty());
-
-       // If boundary is set we should return the row on which
-       // the character before is inside.
-       if (pos > 0 && boundary)
-               --pos;
-
-       RowList::const_iterator rit = rows_.end();
-       RowList::const_iterator const begin = rows_.begin();
-
-       for (--rit; rit != begin && rit->pos() > pos; --rit)
-               ;
-
-       return *rit;
-}
-
-
-size_t Paragraph::pos2row(pos_type pos) const
-{
-       BOOST_ASSERT(!rows().empty());
-
-       RowList::const_iterator rit = rows_.end();
-       RowList::const_iterator const begin = rows_.begin();
-
-       for (--rit; rit != begin && rit->pos() > pos; --rit)
-               ;
-
-       return rit - begin;
-}
-
-
-unsigned char Paragraph::transformChar(unsigned char c, pos_type pos) const
+char_type Paragraph::transformChar(char_type c, pos_type pos) const
 {
        if (!Encodings::is_arabic(c))
-               if (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 && IsDigit(c))
-                       return c + (0xb0 - '0');
-               else
-                       return c;
+               return c;
 
-       unsigned char const prev_char = pos > 0 ? getChar(pos - 1) : ' ';
-       unsigned char next_char = ' ';
+       value_type const prev_char = pos > 0 ? getChar(pos - 1) : ' ';
+       value_type next_char = ' ';
 
        for (pos_type i = pos + 1, end = size(); i < end; ++i) {
-               unsigned char const par_char = getChar(i);
-               if (!Encodings::IsComposeChar_arabic(par_char)) {
+               value_type const par_char = getChar(i);
+               if (!Encodings::isComposeChar_arabic(par_char)) {
                        next_char = par_char;
                        break;
                }
@@ -1833,24 +2536,74 @@ unsigned char Paragraph::transformChar(unsigned char c, pos_type pos) const
        if (Encodings::is_arabic(next_char)) {
                if (Encodings::is_arabic(prev_char) &&
                        !Encodings::is_arabic_special(prev_char))
-                       return Encodings::TransformChar(c, Encodings::FORM_MEDIAL);
+                       return Encodings::transformChar(c, Encodings::FORM_MEDIAL);
                else
-                       return Encodings::TransformChar(c, Encodings::FORM_INITIAL);
+                       return Encodings::transformChar(c, Encodings::FORM_INITIAL);
        } else {
                if (Encodings::is_arabic(prev_char) &&
                        !Encodings::is_arabic_special(prev_char))
-                       return Encodings::TransformChar(c, Encodings::FORM_FINAL);
+                       return Encodings::transformChar(c, Encodings::FORM_FINAL);
                else
-                       return Encodings::TransformChar(c, Encodings::FORM_ISOLATED);
+                       return Encodings::transformChar(c, Encodings::FORM_ISOLATED);
        }
 }
 
 
-void Paragraph::dump() const
+bool Paragraph::hfillExpansion(Row const & row, pos_type pos) const
 {
-       lyxerr << "Paragraph::dump: rows.size(): " << rows_.size() << endl;
-       for (size_t i = 0; i != rows_.size(); ++i) {
-               lyxerr << "  row " << i << ":   ";
-               rows_[i].dump();
+       if (!isHfill(pos))
+               return false;
+
+       BOOST_ASSERT(pos >= row.pos() && pos < row.endpos());
+
+       // expand at the end of a row only if there is another hfill on the same row
+       if (pos == row.endpos() - 1) {
+               for (pos_type i = row.pos(); i < pos; i++) {
+                       if (isHfill(i))
+                               return true;
+               }
+               return false;
+       }
+
+       // expand at the beginning of a row only if it is the first row of a paragraph
+       if (pos == row.pos()) {
+               return pos == 0;
+       }
+
+       // do not expand in some labels
+       if (layout()->margintype != MARGIN_MANUAL && pos < beginOfBody())
+               return false;
+
+       // if there is anything between the first char of the row and
+       // the specified position that is neither a newline nor an hfill,
+       // the hfill will be expanded, otherwise it won't
+       for (pos_type i = row.pos(); i < pos; i++) {
+               if (!isNewline(i) && !isHfill(i))
+                       return true;
        }
+       return false;
+}
+
+
+bool Paragraph::checkBiblio(bool track_changes)
+{
+       // Add bibitem insets if necessary
+       if (layout()->labeltype != LABEL_BIBLIO)
+               return false;
+
+       bool hasbibitem = !insetlist.empty()
+               // Insist on it being in pos 0
+               && getChar(0) == Paragraph::META_INSET
+               && insetlist.begin()->inset->lyxCode() == InsetBase::BIBITEM_CODE;
+
+       if (hasbibitem)
+               return false;
+
+       InsetBibitem * inset(new InsetBibitem(InsetCommandParams("bibitem")));
+       insertInset(0, static_cast<InsetBase *>(inset),
+               Change(track_changes ? Change::INSERTED : Change::UNCHANGED));
+
+       return true;
 }
+
+} // namespace lyx