]> git.lyx.org Git - lyx.git/blobdiff - src/Text.cpp
1.) some white spaces removed
[lyx.git] / src / Text.cpp
index bf80ba3dad0068f01c21d7997aaa6d4dbe1d3335..91b8a5f02a456ad58c5df78fd1736cfc0c0e8030 100644 (file)
@@ -4,14 +4,14 @@
  * Licence details can be found in the file COPYING.
  *
  * \author Asger Alstrup
- * \author Lars Gullik Bjønnes
+ * \author Lars Gullik Bjønnes
  * \author Dov Feldstern
  * \author Jean-Marc Lasgouttes
  * \author John Levon
- * \author André Pönitz
+ * \author André Pönitz
  * \author Stefan Schimanski
  * \author Dekel Tsur
- * \author Jürgen Vigna
+ * \author Jürgen Vigna
  *
  * Full author contact details are available in file CREDITS.
  */
 #include "BufferParams.h"
 #include "BufferView.h"
 #include "Changes.h"
+#include "CompletionList.h"
 #include "Cursor.h"
-#include "ParIterator.h"
 #include "CutAndPaste.h"
 #include "DispatchResult.h"
 #include "Encoding.h"
 #include "ErrorList.h"
 #include "FuncRequest.h"
 #include "factory.h"
-#include "FontIterator.h"
 #include "Language.h"
 #include "Length.h"
 #include "Lexer.h"
 #include "Paragraph.h"
 #include "paragraph_funcs.h"
 #include "ParagraphParameters.h"
+#include "ParIterator.h"
 #include "TextClass.h"
 #include "TextMetrics.h"
 #include "VSpace.h"
 #include "WordLangTuple.h"
+#include "WordList.h"
 
 #include "insets/InsetText.h"
 #include "insets/InsetBibitem.h"
 #include "insets/InsetCaption.h"
-#include "insets/InsetHFill.h"
 #include "insets/InsetLine.h"
 #include "insets/InsetNewline.h"
 #include "insets/InsetNewpage.h"
@@ -63,6 +63,7 @@
 #include "support/debug.h"
 #include "support/docstream.h"
 #include "support/gettext.h"
+#include "support/lassert.h"
 #include "support/lstrings.h"
 #include "support/textutils.h"
 
@@ -101,29 +102,24 @@ void readParToken(Buffer const & buf, Paragraph & par, Lexer & lex,
                if (layoutname.empty())
                        layoutname = tclass.defaultLayoutName();
 
-               if (par.forceEmptyLayout()) {
+               if (par.forcePlainLayout()) {
                        // in this case only the empty layout is allowed
-                       layoutname = tclass.emptyLayoutName();
-               } else if (par.useEmptyLayout()) {
+                       layoutname = tclass.plainLayoutName();
+               } else if (par.usePlainLayout()) {
                        // in this case, default layout maps to empty layout 
                        if (layoutname == tclass.defaultLayoutName())
-                               layoutname = tclass.emptyLayoutName();
+                               layoutname = tclass.plainLayoutName();
                } else { 
                        // otherwise, the empty layout maps to the default
-                       if (layoutname == tclass.emptyLayoutName())
+                       if (layoutname == tclass.plainLayoutName())
                                layoutname = tclass.defaultLayoutName();
                }
 
-               bool hasLayout = tclass.hasLayout(layoutname);
-
-               if (!hasLayout) {
-                       errorList.push_back(ErrorItem(_("Unknown layout"),
-                       bformat(_("Layout '%1$s' does not exist in textclass '%2$s'\nTrying to use the default instead.\n"),
-                       layoutname, from_utf8(tclass.name())), par.id(), 0, par.size()));
-                       layoutname = par.useEmptyLayout() ? 
-                                       tclass.emptyLayoutName() :
-                                       tclass.defaultLayoutName();
-               }
+               // When we apply an unknown layout to a document, we add this layout to the textclass
+               // of this document. For example, when you apply class article to a beamer document,
+               // all unknown layouts such as frame will be added to document class article so that
+               // these layouts can keep their original names.
+               tclass.addLayoutIfNeeded(layoutname);
 
                par.setLayout(bp.documentClass()[layoutname]);
 
@@ -135,10 +131,10 @@ void readParToken(Buffer const & buf, Paragraph & par, Lexer & lex,
                par.params().read(lex);
 
        } else if (token == "\\end_layout") {
-               LYXERR0("Solitary \\end_layout in line " << lex.getLineNo() << "\n"
+               LYXERR0("Solitary \\end_layout in line " << lex.lineNumber() << "\n"
                       << "Missing \\begin_layout ?");
        } else if (token == "\\end_inset") {
-               LYXERR0("Solitary \\end_inset in line " << lex.getLineNo() << "\n"
+               LYXERR0("Solitary \\end_inset in line " << lex.lineNumber() << "\n"
                       << "Missing \\begin_inset ?");
        } else if (token == "\\begin_inset") {
                Inset * inset = readInset(lex, buf);
@@ -197,59 +193,20 @@ void readParToken(Buffer const & buf, Paragraph & par, Lexer & lex,
        } else if (token == "\\color") {
                lex.next();
                setLyXColor(lex.getString(), font.fontInfo());
-       } else if (token == "\\InsetSpace" || token == "\\SpecialChar") {
-
-               // Insets don't make sense in a free-spacing context! ---Kayvan
-               if (par.isFreeSpacing()) {
-                       if (token == "\\InsetSpace")
-                               par.appendChar(' ', font, change);
-                       else if (lex.isOK()) {
-                               lex.next();
-                               string const next_token = lex.getString();
-                               if (next_token == "\\-")
-                                       par.appendChar('-', font, change);
-                               else {
-                                       lex.printError("Token `$$Token' "
-                                                      "is in free space "
-                                                      "paragraph layout!");
-                               }
-                       }
-               } else {
+       } else if (token == "\\SpecialChar") {
                        auto_ptr<Inset> inset;
-                       if (token == "\\SpecialChar" )
-                               inset.reset(new InsetSpecialChar);
-                       else
-                               inset.reset(new InsetSpace);
+                       inset.reset(new InsetSpecialChar);
                        inset->read(lex);
                        par.insertInset(par.size(), inset.release(),
                                        font, change);
-               }
        } else if (token == "\\backslash") {
                par.appendChar('\\', font, change);
-       } else if (token == "\\linebreak") {
-               auto_ptr<Inset> inset(new InsetLinebreak);
-               inset->read(lex);
-               par.insertInset(par.size(), inset.release(), font, change);
-       } else if (token == "\\newline") {
-               auto_ptr<Inset> inset(new InsetNewline);
-               inset->read(lex);
-               par.insertInset(par.size(), inset.release(), font, change);
        } else if (token == "\\LyXTable") {
-               auto_ptr<Inset> inset(new InsetTabular(buf));
+               auto_ptr<Inset> inset(new InsetTabular(const_cast<Buffer &>(buf)));
                inset->read(lex);
                par.insertInset(par.size(), inset.release(), font, change);
-       } else if (token == "\\hfill") {
-               par.insertInset(par.size(), new InsetHFill, font, change);
        } else if (token == "\\lyxline") {
                par.insertInset(par.size(), new InsetLine, font, change);
-       } else if (token == "\\newpage") {
-               par.insertInset(par.size(), new InsetNewpage, font, change);
-       } else if (token == "\\pagebreak") {
-               par.insertInset(par.size(), new InsetPagebreak, font, change);
-       } else if (token == "\\clearpage") {
-               par.insertInset(par.size(), new InsetClearPage, font, change);
-       } else if (token == "\\cleardoublepage") {
-               par.insertInset(par.size(), new InsetClearDoublePage, font, change);
        } else if (token == "\\change_unchanged") {
                change = Change(Change::UNCHANGED);
        } else if (token == "\\change_inserted") {
@@ -316,7 +273,7 @@ void readParagraph(Buffer const & buf, Paragraph & par, Lexer & lex,
                    || token == "\\end_deeper") {
                        lex.pushToken(token);
                        lyxerr << "Paragraph ended in line "
-                              << lex.getLineNo() << "\n"
+                              << lex.lineNumber() << "\n"
                               << "Missing \\end_layout.\n";
                        break;
                }
@@ -331,6 +288,36 @@ void readParagraph(Buffer const & buf, Paragraph & par, Lexer & lex,
 
 } // namespace anon
 
+class TextCompletionList : public CompletionList
+{
+public:
+       ///
+       TextCompletionList(Cursor const & cur)
+               : buffer_(cur.buffer()), pos_(0)
+       {}
+       ///
+       virtual ~TextCompletionList() {}
+       
+       ///
+       virtual bool sorted() const { return true; }
+       ///
+       virtual size_t size() const
+       {
+               return theWordList().size();
+       }
+       ///
+       virtual docstring const & data(size_t idx) const
+       {
+               return theWordList().word(idx);
+       }
+       
+private:
+       ///
+       Buffer const * buffer_;
+       ///
+       size_t pos_;
+};
+
 
 bool Text::empty() const
 {
@@ -350,12 +337,12 @@ double Text::spacing(Buffer const & buffer, Paragraph const & par) const
 
 void Text::breakParagraph(Cursor & cur, bool inverse_logic)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
 
        Paragraph & cpar = cur.paragraph();
        pit_type cpit = cur.pit();
 
-       DocumentClass const & tclass = cur.buffer().params().documentClass();
+       DocumentClass const & tclass = cur.buffer()->params().documentClass();
        Layout const & layout = cpar.layout();
 
        // this is only allowed, if the current paragraph is not empty
@@ -370,7 +357,7 @@ void Text::breakParagraph(Cursor & cur, bool inverse_logic)
        // Always break behind a space
        // It is better to erase the space (Dekel)
        if (cur.pos() != cur.lastpos() && cpar.isLineSeparator(cur.pos()))
-               cpar.eraseChar(cur.pos(), cur.buffer().params().trackChanges);
+               cpar.eraseChar(cur.pos(), cur.buffer()->params().trackChanges);
 
        // What should the layout for the new paragraph be?
        bool keep_layout = inverse_logic ? 
@@ -384,7 +371,7 @@ void Text::breakParagraph(Cursor & cur, bool inverse_logic)
        // we need to set this before we insert the paragraph.
        bool const isempty = cpar.allowEmpty() && cpar.empty();
 
-       lyx::breakParagraph(cur.buffer().params(), paragraphs(), cpit,
+       lyx::breakParagraph(cur.buffer()->params(), paragraphs(), cpit,
                         cur.pos(), keep_layout);
 
        // After this, neither paragraph contains any rows!
@@ -396,20 +383,20 @@ void Text::breakParagraph(Cursor & cur, bool inverse_logic)
        if (sensitive) {
                if (cur.pos() == 0)
                        // set to standard-layout
-               //FIXME Check if this should be emptyLayout() in some cases
+               //FIXME Check if this should be plainLayout() in some cases
                        pars_[cpit].applyLayout(tclass.defaultLayout());
                else
                        // set to standard-layout
-                       //FIXME Check if this should be emptyLayout() in some cases
+                       //FIXME Check if this should be plainLayout() in some cases
                        pars_[next_par].applyLayout(tclass.defaultLayout());
        }
 
        while (!pars_[next_par].empty() && pars_[next_par].isNewline(0)) {
-               if (!pars_[next_par].eraseChar(0, cur.buffer().params().trackChanges))
+               if (!pars_[next_par].eraseChar(0, cur.buffer()->params().trackChanges))
                        break; // the character couldn't be deleted physically due to change tracking
        }
 
-       updateLabels(cur.buffer());
+       cur.buffer()->updateLabels();
 
        // A singlePar update is not enough in this case.
        cur.updateFlags(Update::Force);
@@ -427,12 +414,12 @@ void Text::breakParagraph(Cursor & cur, bool inverse_logic)
 // same Paragraph one to the right and make a rebreak
 void Text::insertChar(Cursor & cur, char_type c)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
 
        cur.recordUndo(INSERT_UNDO);
 
        TextMetrics const & tm = cur.bv().textMetrics(this);
-       Buffer const & buffer = cur.buffer();
+       Buffer const & buffer = *cur.buffer();
        Paragraph & par = cur.paragraph();
        // try to remove this
        pit_type const pit = cur.pit();
@@ -539,7 +526,7 @@ void Text::insertChar(Cursor & cur, char_type c)
                        }
                        return;
                }
-               BOOST_ASSERT(cur.pos() > 0);
+               LASSERT(cur.pos() > 0, /**/);
                if ((par.isLineSeparator(cur.pos() - 1) || par.isNewline(cur.pos() - 1))
                    && !par.isDeleted(cur.pos() - 1)) {
                        static bool sent_space_message = false;
@@ -552,7 +539,8 @@ void Text::insertChar(Cursor & cur, char_type c)
                }
        }
 
-       par.insertChar(cur.pos(), c, cur.current_font, cur.buffer().params().trackChanges);
+       par.insertChar(cur.pos(), c, cur.current_font,
+               cur.buffer()->params().trackChanges);
        cur.checkBufferStructure();
 
 //             cur.updateFlags(Update::Force);
@@ -580,7 +568,7 @@ void Text::charInserted(Cursor & cur)
            && par.isLetter(cur.pos() - 2)
            && !par.isLetter(cur.pos() - 1)) {
                // get the word in front of cursor
-               BOOST_ASSERT(this == cur.text());
+               LASSERT(this == cur.text(), /**/);
                cur.paragraph().updateWords(cur.top());
        }
 }
@@ -591,49 +579,171 @@ void Text::charInserted(Cursor & cur)
 
 bool Text::cursorForwardOneWord(Cursor & cur)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
+
+       pos_type const lastpos = cur.lastpos();
+       pit_type pit = cur.pit();
+       pos_type pos = cur.pos();
+       Paragraph const & par = cur.paragraph();
 
-       Cursor old = cur;
+       // Paragraph boundary is a word boundary
+       if (pos == lastpos) {
+               if (pit != cur.lastpit())
+                       return setCursor(cur, pit + 1, 0);
+               else
+                       return false;
+       }
 
-       if (old.pos() == old.lastpos() && old.pit() != old.lastpit()) {
-               ++old.pit();
-               old.pos() = 0;
+       if (lyxrc.mac_like_word_movement) {
+               // Skip through trailing punctuation and spaces.
+               while (pos != lastpos && (par.isChar(pos) || par.isSpace(pos)))
+                        ++pos;
+
+               // Skip over either a non-char inset or a full word
+               if (pos != lastpos && !par.isLetter(pos))
+                       ++pos;
+               else while (pos != lastpos && par.isLetter(pos))
+                            ++pos;
        } else {
-               // Advance through word.
-               while (old.pos() != old.lastpos() && old.paragraph().isLetter(old.pos()))
-                       ++old.pos();
-               // Skip through trailing nonword stuff.
-               while (old.pos() != old.lastpos() && !old.paragraph().isLetter(old.pos()))
-                       ++old.pos();
+               LASSERT(pos < lastpos, /**/); // see above
+               if (par.isLetter(pos))
+                       while (pos != lastpos && par.isLetter(pos))
+                               ++pos;
+               else if (par.isChar(pos))
+                       while (pos != lastpos && par.isChar(pos))
+                               ++pos;
+               else if (!par.isSpace(pos)) // non-char inset
+                       ++pos;
+
+               // Skip over white space
+               while (pos != lastpos && par.isSpace(pos))
+                            ++pos;             
        }
-       return setCursor(cur, old.pit(), old.pos());
+
+       return setCursor(cur, pit, pos);
 }
 
 
 bool Text::cursorBackwardOneWord(Cursor & cur)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
 
-       Cursor old = cur;
+       pit_type pit = cur.pit();
+       pos_type pos = cur.pos();
+       Paragraph & par = cur.paragraph();
+
+       // Paragraph boundary is a word boundary
+       if (pos == 0 && pit != 0)
+               return setCursor(cur, pit - 1, getPar(pit - 1).size());
+
+       if (lyxrc.mac_like_word_movement) {
+               // Skip through punctuation and spaces.
+               while (pos != 0 && (par.isChar(pos - 1) || par.isSpace(pos - 1)))
+                       --pos;
 
-       if (old.pos() == 0 && old.pit() != 0) {
-               --old.pit();
-               old.pos() = old.lastpos();
+               // Skip over either a non-char inset or a full word
+               if (pos != 0 && !par.isLetter(pos - 1) && !par.isChar(pos - 1))
+                       --pos;
+               else while (pos != 0 && par.isLetter(pos - 1))
+                            --pos;
        } else {
-               // Skip through initial nonword stuff.
-               while (old.pos() != 0 && !old.paragraph().isLetter(old.pos() - 1))
-                       --old.pos();
-               // Advance through word.
-               while (old.pos() != 0 && old.paragraph().isLetter(old.pos() - 1))
-                       --old.pos();
+               // Skip over white space
+               while (pos != 0 && par.isSpace(pos - 1))
+                            --pos;
+
+               if (pos != 0 && par.isLetter(pos - 1))
+                       while (pos != 0 && par.isLetter(pos - 1))
+                               --pos;
+               else if (pos != 0 && par.isChar(pos - 1))
+                       while (pos != 0 && par.isChar(pos - 1))
+                               --pos;
+               else if (pos != 0 && !par.isSpace(pos - 1)) // non-char inset
+                       --pos;
+       }
+
+       return setCursor(cur, pit, pos);
+}
+
+
+bool Text::cursorVisLeftOneWord(Cursor & cur)
+{
+       LASSERT(this == cur.text(), /**/);
+
+       pos_type left_pos, right_pos;
+       bool left_is_letter, right_is_letter;
+
+       Cursor temp_cur = cur;
+
+       // always try to move at least once...
+       while (temp_cur.posVisLeft(true /* skip_inset */)) {
+
+               // collect some information about current cursor position
+               temp_cur.getSurroundingPos(left_pos, right_pos);
+               left_is_letter = 
+                       (left_pos > -1 ? temp_cur.paragraph().isLetter(left_pos) : false);
+               right_is_letter = 
+                       (right_pos > -1 ? temp_cur.paragraph().isLetter(right_pos) : false);
+
+               // if we're not at a letter/non-letter boundary, continue moving
+               if (left_is_letter == right_is_letter)
+                       continue;
+
+               // we should stop when we have an LTR word on our right or an RTL word
+               // on our left
+               if ((left_is_letter && temp_cur.paragraph().getFontSettings(
+                               temp_cur.buffer()->params(), left_pos).isRightToLeft())
+                       || (right_is_letter && !temp_cur.paragraph().getFontSettings(
+                               temp_cur.buffer()->params(), right_pos).isRightToLeft()))
+                       break;
        }
-       return setCursor(cur, old.pit(), old.pos());
+
+       return setCursor(cur, temp_cur.pit(), temp_cur.pos(), 
+                                        true, temp_cur.boundary());
+}
+
+
+bool Text::cursorVisRightOneWord(Cursor & cur)
+{
+       LASSERT(this == cur.text(), /**/);
+
+       pos_type left_pos, right_pos;
+       bool left_is_letter, right_is_letter;
+
+       Cursor temp_cur = cur;
+
+       // always try to move at least once...
+       while (temp_cur.posVisRight(true /* skip_inset */)) {
+
+               // collect some information about current cursor position
+               temp_cur.getSurroundingPos(left_pos, right_pos);
+               left_is_letter = 
+                       (left_pos > -1 ? temp_cur.paragraph().isLetter(left_pos) : false);
+               right_is_letter = 
+                       (right_pos > -1 ? temp_cur.paragraph().isLetter(right_pos) : false);
+
+               // if we're not at a letter/non-letter boundary, continue moving
+               if (left_is_letter == right_is_letter)
+                       continue;
+
+               // we should stop when we have an LTR word on our right or an RTL word
+               // on our left
+               if ((left_is_letter && temp_cur.paragraph().getFontSettings(
+                               temp_cur.buffer()->params(), 
+                               left_pos).isRightToLeft())
+                       || (right_is_letter && !temp_cur.paragraph().getFontSettings(
+                               temp_cur.buffer()->params(), 
+                               right_pos).isRightToLeft()))
+                       break;
+       }
+
+       return setCursor(cur, temp_cur.pit(), temp_cur.pos(), 
+                                        true, temp_cur.boundary());
 }
 
 
 void Text::selectWord(Cursor & cur, word_location loc)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
        CursorSlice from = cur.top();
        CursorSlice to = cur.top();
        getWord(from, to, loc);
@@ -647,11 +757,30 @@ void Text::selectWord(Cursor & cur, word_location loc)
 }
 
 
+void Text::selectAll(Cursor & cur)
+{
+       LASSERT(this == cur.text(), /**/);
+       if (cur.lastpos() == 0 && cur.lastpit() == 0)
+               return;
+       // If the cursor is at the beginning, make sure the cursor ends there
+       if (cur.pit() == 0 && cur.pos() == 0) {
+               setCursor(cur, cur.lastpit(), getPar(cur.lastpit()).size());
+               cur.resetAnchor();
+               setCursor(cur, 0, 0);           
+       } else {
+               setCursor(cur, 0, 0);
+               cur.resetAnchor();
+               setCursor(cur, cur.lastpit(), getPar(cur.lastpit()).size());
+       }
+       cur.setSelection();
+}
+
+
 // Select the word currently under the cursor when no
 // selection is currently set
 bool Text::selectWordWhenUnderCursor(Cursor & cur, word_location loc)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
        if (cur.selection())
                return false;
        selectWord(cur, loc);
@@ -661,7 +790,7 @@ bool Text::selectWordWhenUnderCursor(Cursor & cur, word_location loc)
 
 void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
 
        if (!cur.selection())
                return;
@@ -699,9 +828,9 @@ void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op)
                pos_type right = (pit == endPit ? endPos : parSize);
 
                if (op == ACCEPT) {
-                       pars_[pit].acceptChanges(cur.buffer().params(), left, right);
+                       pars_[pit].acceptChanges(cur.buffer()->params(), left, right);
                } else {
-                       pars_[pit].rejectChanges(cur.buffer().params(), left, right);
+                       pars_[pit].rejectChanges(cur.buffer()->params(), left, right);
                }
        }
 
@@ -728,7 +857,7 @@ void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op)
                                        // instead, we mark it unchanged
                                        pars_[pit].setChange(pos, Change(Change::UNCHANGED));
                                } else {
-                                       mergeParagraph(cur.buffer().params(), pars_, pit);
+                                       mergeParagraph(cur.buffer()->params(), pars_, pit);
                                        --endPit;
                                        --pit;
                                }
@@ -741,7 +870,7 @@ void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op)
                                        // we mark the par break at the end of the last paragraph unchanged
                                        pars_[pit].setChange(pos, Change(Change::UNCHANGED));
                                } else {
-                                       mergeParagraph(cur.buffer().params(), pars_, pit);
+                                       mergeParagraph(cur.buffer()->params(), pars_, pit);
                                        --endPit;
                                        --pit;
                                }
@@ -751,7 +880,7 @@ void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op)
 
        // finally, invoke the DEPM
 
-       deleteEmptyParagraphMechanism(begPit, endPit, cur.buffer().params().trackChanges);
+       deleteEmptyParagraphMechanism(begPit, endPit, cur.buffer()->params().trackChanges);
 
        //
 
@@ -759,7 +888,7 @@ void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op)
        cur.clearSelection();
        setCursorIntern(cur, begPit, begPos);
        cur.updateFlags(Update::Force);
-       updateLabels(cur.buffer());
+       cur.buffer()->updateLabels();
 }
 
 
@@ -807,12 +936,12 @@ void Text::rejectChanges(BufferParams const & bparams)
 
 void Text::deleteWordForward(Cursor & cur)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
        if (cur.lastpos() == 0)
                cursorForward(cur);
        else {
                cur.resetAnchor();
-               cur.selection() = true;
+               cur.setSelection(true);
                cursorForwardOneWord(cur);
                cur.setSelection();
                cutSelection(cur, true, false);
@@ -823,12 +952,12 @@ void Text::deleteWordForward(Cursor & cur)
 
 void Text::deleteWordBackward(Cursor & cur)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
        if (cur.lastpos() == 0)
                cursorBackward(cur);
        else {
                cur.resetAnchor();
-               cur.selection() = true;
+               cur.setSelection(true);
                cursorBackwardOneWord(cur);
                cur.setSelection();
                cutSelection(cur, true, false);
@@ -840,7 +969,7 @@ void Text::deleteWordBackward(Cursor & cur)
 // Kill to end of line.
 void Text::changeCase(Cursor & cur, TextCase action)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
        CursorSlice from;
        CursorSlice to;
 
@@ -869,7 +998,7 @@ void Text::changeCase(Cursor & cur, TextCase action)
                Paragraph & par = pars_[pit];
                pos_type const pos = (pit == begPit ? begPos : 0);
                right = (pit == endPit ? endPos : par.size());
-               par.changeCase(cur.buffer().params(), pos, right, action);
+               par.changeCase(cur.buffer()->params(), pos, right, action);
        }
 
        // the selection may have changed due to logically-only deleted chars
@@ -893,7 +1022,7 @@ bool Text::handleBibitems(Cursor & cur)
        if (cur.pos() != 0)
                return false;
 
-       BufferParams const & bufparams = cur.buffer().params();
+       BufferParams const & bufparams = cur.buffer()->params();
        Paragraph const & par = cur.paragraph();
        Cursor prevcur = cur;
        if (cur.pit() > 0) {
@@ -908,21 +1037,21 @@ bool Text::handleBibitems(Cursor & cur)
                cur.recordUndo(ATOMIC_UNDO, prevcur.pit());
                mergeParagraph(bufparams, cur.text()->paragraphs(),
                                                        prevcur.pit());
-               updateLabels(cur.buffer());
+               cur.buffer()->updateLabels();
                setCursorIntern(cur, prevcur.pit(), prevcur.pos());
                cur.updateFlags(Update::Force);
                return true;
        } 
 
        // otherwise reset to default
-       cur.paragraph().setEmptyOrDefaultLayout(bufparams.documentClass());
+       cur.paragraph().setPlainOrDefaultLayout(bufparams.documentClass());
        return true;
 }
 
 
 bool Text::erase(Cursor & cur)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), return false);
        bool needsUpdate = false;
        Paragraph & par = cur.paragraph();
 
@@ -931,12 +1060,12 @@ bool Text::erase(Cursor & cur)
                // any paragraphs
                cur.recordUndo(DELETE_UNDO);
                bool const was_inset = cur.paragraph().isInset(cur.pos());
-               if(!par.eraseChar(cur.pos(), cur.buffer().params().trackChanges))
+               if(!par.eraseChar(cur.pos(), cur.buffer()->params().trackChanges))
                        // the character has been logically deleted only => skip it
                        cur.top().forwardPos();
 
                if (was_inset)
-                       updateLabels(cur.buffer());
+                       cur.buffer()->updateLabels();
                else
                        cur.checkBufferStructure();
                needsUpdate = true;
@@ -944,7 +1073,7 @@ bool Text::erase(Cursor & cur)
                if (cur.pit() == cur.lastpit())
                        return dissolveInset(cur);
 
-               if (!par.isMergedOnEndOfParDeletion(cur.buffer().params().trackChanges)) {
+               if (!par.isMergedOnEndOfParDeletion(cur.buffer()->params().trackChanges)) {
                        par.setChange(cur.pos(), Change(Change::DELETED));
                        cur.forwardPos();
                        needsUpdate = true;
@@ -969,13 +1098,13 @@ bool Text::erase(Cursor & cur)
 
 bool Text::backspacePos0(Cursor & cur)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
        if (cur.pit() == 0)
                return false;
 
        bool needsUpdate = false;
 
-       BufferParams const & bufparams = cur.buffer().params();
+       BufferParams const & bufparams = cur.buffer()->params();
        DocumentClass const & tclass = bufparams.documentClass();
        ParagraphList & plist = cur.text()->paragraphs();
        Paragraph const & par = cur.paragraph();
@@ -1005,14 +1134,14 @@ bool Text::backspacePos0(Cursor & cur)
        // or the empty layout.
        else if (par.layout() == prevpar.layout()
                 || tclass.isDefaultLayout(par.layout())
-                || tclass.isEmptyLayout(par.layout())) {
+                || tclass.isPlainLayout(par.layout())) {
                cur.recordUndo(ATOMIC_UNDO, prevcur.pit());
                mergeParagraph(bufparams, plist, prevcur.pit());
                needsUpdate = true;
        }
 
        if (needsUpdate) {
-               updateLabels(cur.buffer());
+               cur.buffer()->updateLabels();
                setCursorIntern(cur, prevcur.pit(), prevcur.pos());
        }
 
@@ -1022,7 +1151,7 @@ bool Text::backspacePos0(Cursor & cur)
 
 bool Text::backspace(Cursor & cur)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
        bool needsUpdate = false;
        if (cur.pos() == 0) {
                if (cur.pit() == 0)
@@ -1030,7 +1159,7 @@ bool Text::backspace(Cursor & cur)
 
                Paragraph & prev_par = pars_[cur.pit() - 1];
 
-               if (!prev_par.isMergedOnEndOfParDeletion(cur.buffer().params().trackChanges)) {
+               if (!prev_par.isMergedOnEndOfParDeletion(cur.buffer()->params().trackChanges)) {
                        prev_par.setChange(prev_par.size(), Change(Change::DELETED));
                        setCursorIntern(cur, cur.pit() - 1, prev_par.size());
                        return true;
@@ -1050,9 +1179,9 @@ bool Text::backspace(Cursor & cur)
                setCursorIntern(cur, cur.pit(), cur.pos() - 1,
                                false, cur.boundary());
                bool const was_inset = cur.paragraph().isInset(cur.pos());
-               cur.paragraph().eraseChar(cur.pos(), cur.buffer().params().trackChanges);
+               cur.paragraph().eraseChar(cur.pos(), cur.buffer()->params().trackChanges);
                if (was_inset)
-                       updateLabels(cur.buffer());
+                       cur.buffer()->updateLabels();
                else
                        cur.checkBufferStructure();
        }
@@ -1070,14 +1199,15 @@ bool Text::backspace(Cursor & cur)
 }
 
 
-bool Text::dissolveInset(Cursor & cur) {
-       BOOST_ASSERT(this == cur.text());
+bool Text::dissolveInset(Cursor & cur)
+{
+       LASSERT(this == cur.text(), return false);
 
        if (isMainText(cur.bv().buffer()) || cur.inset().nargs() != 1)
                return false;
 
        cur.recordUndoInset();
-       cur.mark() = false;
+       cur.setMark(false);
        cur.selHandle(false);
        // save position
        pos_type spos = cur.pos();
@@ -1090,7 +1220,7 @@ bool Text::dissolveInset(Cursor & cur) {
        if (spit == 0)
                spos += cur.pos();
        spit += cur.pit();
-       Buffer & b = cur.buffer();
+       Buffer & b = *cur.buffer();
        cur.paragraph().eraseChar(cur.pos(), b.params().trackChanges);
        if (!plist.empty()) {
                // ERT paragraphs have the Language latex_language.
@@ -1170,6 +1300,7 @@ bool Text::read(Buffer const & buf, Lexer & lex,
                ErrorList & errorList, InsetText * insetPtr)
 {
        depth_type depth = 0;
+       bool res = true;
 
        while (lex.isOK()) {
                lex.nextToken();
@@ -1187,16 +1318,18 @@ bool Text::read(Buffer const & buf, Lexer & lex,
                if (token == "\\begin_body")
                        continue;
 
-               if (token == "\\end_document")
-                       return false;
+               if (token == "\\end_document") {
+                       res = false;
+                       break;
+               }
 
                if (token == "\\begin_layout") {
                        lex.pushToken(token);
 
                        Paragraph par;
+                       par.setInsetOwner(insetPtr);
                        par.params().depth(depth);
                        par.setFont(0, Font(inherit_font, buf.params().language));
-                       par.setInsetOwner(insetPtr);
                        pars_.push_back(par);
 
                        // FIXME: goddamn InsetTabular makes us pass a Buffer
@@ -1218,14 +1351,26 @@ bool Text::read(Buffer const & buf, Lexer & lex,
                        LYXERR0("Handling unknown body token: `" << token << '\'');
                }
        }
-       return true;
+
+       // avoid a crash on weird documents (bug 4859)
+       if (pars_.empty()) {
+               Paragraph par;
+               par.setInsetOwner(insetPtr);
+               par.params().depth(depth);
+               par.setFont(0, Font(inherit_font, 
+                                   buf.params().language));
+               par.setPlainOrDefaultLayout(buf.params().documentClass());
+               pars_.push_back(par);
+       }
+       
+       return res;
 }
 
 // Returns the current font and depth as a message.
-docstring Text::currentState(Cursor & cur)
+docstring Text::currentState(Cursor const & cur) const
 {
-       BOOST_ASSERT(this == cur.text());
-       Buffer & buf = cur.buffer();
+       LASSERT(this == cur.text(), /**/);
+       Buffer & buf = *cur.buffer();
        Paragraph const & par = cur.paragraph();
        odocstringstream os;
 
@@ -1301,14 +1446,14 @@ docstring Text::currentState(Cursor & cur)
 }
 
 
-docstring Text::getPossibleLabel(Cursor & cur) const
+docstring Text::getPossibleLabel(Cursor const & cur) const
 {
        pit_type pit = cur.pit();
 
        Layout const * layout = &(pars_[pit].layout());
 
        docstring text;
-       docstring par_text = pars_[pit].asString(false);
+       docstring par_text = pars_[pit].asString();
        string piece;
        // the return string of math matrices might contain linebreaks
        par_text = subst(par_text, '\n', '-');
@@ -1378,9 +1523,28 @@ docstring Text::getPossibleLabel(Cursor & cur) const
 }
 
 
+docstring Text::asString(int options) const
+{
+       return asString(0, pars_.size(), options);
+}
+
+
+docstring Text::asString(pit_type beg, pit_type end, int options) const
+{
+       size_t i = size_t(beg);
+       docstring str = pars_[i].asString(options);
+       for (++i; i != size_t(end); ++i) {
+               str += '\n';
+               str += pars_[i].asString(options);
+       }
+       return str;
+}
+
+
+
 void Text::charsTranspose(Cursor & cur)
 {
-       BOOST_ASSERT(this == cur.text());
+       LASSERT(this == cur.text(), /**/);
 
        pos_type pos = cur.pos();
 
@@ -1410,17 +1574,17 @@ void Text::charsTranspose(Cursor & cur)
                return;
 
        // Store the characters to be transposed (including font information).
-       char_type char1 = par.getChar(pos1);
+       char_type const char1 = par.getChar(pos1);
        Font const font1 =
-               par.getFontSettings(cur.buffer().params(), pos1);
+               par.getFontSettings(cur.buffer()->params(), pos1);
 
-       char_type char2 = par.getChar(pos2);
+       char_type const char2 = par.getChar(pos2);
        Font const font2 =
-               par.getFontSettings(cur.buffer().params(), pos2);
+               par.getFontSettings(cur.buffer()->params(), pos2);
 
        // And finally, we are ready to perform the transposition.
        // Track the changes if Change Tracking is enabled.
-       bool const trackChanges = cur.buffer().params().trackChanges;
+       bool const trackChanges = cur.buffer()->params().trackChanges;
 
        cur.recordUndo();
 
@@ -1449,4 +1613,48 @@ void Text::setMacrocontextPosition(DocIterator const & pos)
 }
 
 
+docstring Text::previousWord(CursorSlice const & sl) const
+{
+       CursorSlice from = sl;
+       CursorSlice to = sl;
+       getWord(from, to, PREVIOUS_WORD);
+       if (sl == from || to == from)
+               return docstring();
+       
+       Paragraph const & par = sl.paragraph();
+       return par.asString(from.pos(), to.pos());
+}
+
+
+bool Text::completionSupported(Cursor const & cur) const
+{
+       Paragraph const & par = cur.paragraph();
+       return cur.pos() > 0
+               && (cur.pos() >= par.size() || !par.isLetter(cur.pos()))
+               && par.isLetter(cur.pos() - 1);
+}
+
+
+CompletionList const * Text::createCompletionList(Cursor const & cur) const
+{
+       return new TextCompletionList(cur);
+}
+
+
+bool Text::insertCompletion(Cursor & cur, docstring const & s, bool /*finished*/)
+{      
+       LASSERT(cur.bv().cursor() == cur, /**/);
+       cur.insert(s);
+       cur.bv().cursor() = cur;
+       if (!(cur.disp_.update() & Update::Force))
+               cur.updateFlags(cur.disp_.update() | Update::SinglePar);
+       return true;
+}
+       
+       
+docstring Text::completionPrefix(Cursor const & cur) const
+{
+       return previousWord(cur.top());
+}
+
 } // namespace lyx