]> git.lyx.org Git - lyx.git/blobdiff - src/Text.cpp
Revert 23154.
[lyx.git] / src / Text.cpp
index 2339b2bef27667a1d4dc5148ac488b5153ec3a86..c29876d9a1efe030cd6e0403c79387c6679887c6 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * \file src/text.cpp
+ * \file src/Text.cpp
  * This file is part of LyX, the document processor.
  * Licence details can be found in the file COPYING.
  *
 #include "Cursor.h"
 #include "ParIterator.h"
 #include "CutAndPaste.h"
-#include "debug.h"
 #include "DispatchResult.h"
 #include "Encoding.h"
 #include "ErrorList.h"
 #include "FuncRequest.h"
 #include "factory.h"
 #include "FontIterator.h"
-#include "gettext.h"
 #include "Language.h"
 #include "Length.h"
 #include "Lexer.h"
 #include "Paragraph.h"
 #include "paragraph_funcs.h"
 #include "ParagraphParameters.h"
+#include "TextClass.h"
 #include "TextMetrics.h"
 #include "VSpace.h"
 #include "WordLangTuple.h"
 
-#include "frontends/FontMetrics.h"
-#include "frontends/Painter.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/InsetPagebreak.h"
+#include "insets/InsetNewpage.h"
 #include "insets/InsetOptArg.h"
 #include "insets/InsetSpace.h"
 #include "insets/InsetSpecialChar.h"
 #include "insets/InsetTabular.h"
 
+#include "support/convert.h"
+#include "support/debug.h"
+#include "support/docstream.h"
+#include "support/gettext.h"
 #include "support/lstrings.h"
 #include "support/textutils.h"
-#include "support/convert.h"
 
-#include <boost/current_function.hpp>
 #include <boost/next_prior.hpp>
 
 #include <sstream>
 
-using std::auto_ptr;
-using std::advance;
-using std::distance;
-using std::max;
-using std::min;
-using std::endl;
-using std::string;
+using namespace std;
+using namespace lyx::support;
 
 namespace lyx {
 
-using support::bformat;
-using support::contains;
-using support::split;
-
 using cap::cutSelection;
 using cap::pasteParagraphList;
 
-using frontend::FontMetrics;
-
 namespace {
 
 void readParToken(Buffer const & buf, Paragraph & par, Lexer & lex,
@@ -111,8 +98,20 @@ void readParToken(Buffer const & buf, Paragraph & par, Lexer & lex,
 
                TextClass const & tclass = bp.getTextClass();
 
-               if (layoutname.empty()) {
+               if (layoutname.empty())
                        layoutname = tclass.defaultLayoutName();
+
+               if (par.forceEmptyLayout()) {
+                       // in this case only the empty layout is allowed
+                       layoutname = tclass.emptyLayoutName();
+               } else if (par.useEmptyLayout()) {
+                       // in this case, default layout maps to empty layout 
+                       if (layoutname == tclass.defaultLayoutName())
+                               layoutname = tclass.emptyLayoutName();
+               } else { 
+                       // otherwise, the empty layout maps to the default
+                       if (layoutname == tclass.emptyLayoutName())
+                               layoutname = tclass.defaultLayoutName();
                }
 
                bool hasLayout = tclass.hasLayout(layoutname);
@@ -121,28 +120,26 @@ void readParToken(Buffer const & buf, Paragraph & par, Lexer & lex,
                        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 = tclass.defaultLayoutName();
+                       layoutname = par.useEmptyLayout() ? 
+                                       tclass.emptyLayoutName() :
+                                       tclass.defaultLayoutName();
                }
 
-               par.layout(bp.getTextClass()[layoutname]);
+               par.setLayout(bp.getTextClass()[layoutname]);
 
                // Test whether the layout is obsolete.
                LayoutPtr const & layout = par.layout();
                if (!layout->obsoleted_by().empty())
-                       par.layout(bp.getTextClass()[layout->obsoleted_by()]);
+                       par.setLayout(bp.getTextClass()[layout->obsoleted_by()]);
 
                par.params().read(lex);
 
        } else if (token == "\\end_layout") {
-               lyxerr << BOOST_CURRENT_FUNCTION
-                      << ": Solitary \\end_layout in line "
-                      << lex.getLineNo() << "\n"
-                      << "Missing \\begin_layout?.\n";
+               LYXERR0("Solitary \\end_layout in line " << lex.getLineNo() << "\n"
+                      << "Missing \\begin_layout ?");
        } else if (token == "\\end_inset") {
-               lyxerr << BOOST_CURRENT_FUNCTION
-                      << ": Solitary \\end_inset in line "
-                      << lex.getLineNo() << "\n"
-                      << "Missing \\begin_inset?.\n";
+               LYXERR0("Solitary \\end_inset in line " << lex.getLineNo() << "\n"
+                      << "Missing \\begin_inset ?");
        } else if (token == "\\begin_inset") {
                Inset * inset = readInset(lex, buf);
                if (inset)
@@ -229,6 +226,10 @@ void readParToken(Buffer const & buf, Paragraph & par, Lexer & lex,
                }
        } else if (token == "\\backslash") {
                par.appendChar('\\', font, change);
+       } else if (token == "\\linebreak") {
+               auto_ptr<Inset> inset(new InsetLinebreak);
+               inset->read(buf, lex);
+               par.insertInset(par.size(), inset.release(), font, change);
        } else if (token == "\\newline") {
                auto_ptr<Inset> inset(new InsetNewline);
                inset->read(buf, lex);
@@ -242,6 +243,8 @@ void readParToken(Buffer const & buf, Paragraph & par, Lexer & lex,
        } 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);
@@ -251,9 +254,9 @@ void readParToken(Buffer const & buf, Paragraph & par, Lexer & lex,
                change = Change(Change::UNCHANGED);
        } else if (token == "\\change_inserted") {
                lex.eatLine();
-               std::istringstream is(lex.getString());
+               istringstream is(lex.getString());
                unsigned int aid;
-               time_type ct;
+               time_t ct;
                is >> aid >> ct;
                if (aid >= bp.author_map.size()) {
                        errorList.push_back(ErrorItem(_("Change tracking error"),
@@ -264,9 +267,9 @@ void readParToken(Buffer const & buf, Paragraph & par, Lexer & lex,
                        change = Change(Change::INSERTED, bp.author_map[aid], ct);
        } else if (token == "\\change_deleted") {
                lex.eatLine();
-               std::istringstream is(lex.getString());
+               istringstream is(lex.getString());
                unsigned int aid;
-               time_type ct;
+               time_t ct;
                is >> aid >> ct;
                if (aid >= bp.author_map.size()) {
                        errorList.push_back(ErrorItem(_("Change tracking error"),
@@ -307,8 +310,7 @@ void readParagraph(Buffer const & buf, Paragraph & par, Lexer & lex,
                        break;
                }
 
-               LYXERR(Debug::PARSER) << "Handling paragraph token: `"
-                                     << token << '\'' << endl;
+               LYXERR(Debug::PARSER, "Handling paragraph token: `" << token << '\'');
                if (token == "\\begin_layout" || token == "\\end_document"
                    || token == "\\end_inset" || token == "\\begin_deeper"
                    || token == "\\end_deeper") {
@@ -338,8 +340,7 @@ bool Text::empty() const
 }
 
 
-double Text::spacing(Buffer const & buffer,
-               Paragraph const & par) const
+double Text::spacing(Buffer const & buffer, Paragraph const & par) const
 {
        if (par.params().spacing().isDefault())
                return buffer.params().spacing().getValue();
@@ -395,9 +396,11 @@ 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
                        pars_[cpit].applyLayout(tclass.defaultLayout());
                else
                        // set to standard-layout
+                       //FIXME Check if this should be emptyLayout() in some cases
                        pars_[next_par].applyLayout(tclass.defaultLayout());
        }
 
@@ -550,7 +553,7 @@ void Text::insertChar(Cursor & cur, char_type c)
        }
 
        par.insertChar(cur.pos(), c, cur.current_font, cur.buffer().params().trackChanges);
-       checkBufferStructure(cur.buffer(), cur);
+       cur.checkBufferStructure();
 
 //             cur.updateFlags(Update::Force);
        setCursor(cur.top(), cur.pit(), cur.pos() + 1);
@@ -560,6 +563,8 @@ void Text::insertChar(Cursor & cur, char_type c)
 
 void Text::charInserted(Cursor & cur)
 {
+       Paragraph & par = cur.paragraph();
+
        // Here we call finishUndo for every 20 characters inserted.
        // This is from my experience how emacs does it. (Lgb)
        static unsigned int counter;
@@ -569,13 +574,34 @@ void Text::charInserted(Cursor & cur)
                cur.finishUndo();
                counter = 0;
        }
+
+       // register word if a non-letter was entered
+       if (cur.pos() > 1
+           && par.isLetter(cur.pos() - 2)
+           && !par.isLetter(cur.pos() - 1)) {
+               // get the word in front of cursor
+               BOOST_ASSERT(this == cur.text());
+               CursorSlice focus = cur.top();
+               focus.backwardPos();
+               CursorSlice from = focus;
+               CursorSlice to = focus;
+               getWord(from, to, PREVIOUS_WORD);
+               if (focus == from || to == from)
+                       return;
+               docstring word
+               = par.asString(cur.buffer(), from.pos(), to.pos(), false);
+
+               // register words longer than 5 characters
+               if (word.length() > 5)
+                       cur.buffer().registerWord(word);
+       }
 }
 
 
 // the cursor set functions have a special mechanism. When they
 // realize, that you left an empty paragraph, they will delete it.
 
-bool Text::cursorRightOneWord(Cursor & cur)
+bool Text::cursorForwardOneWord(Cursor & cur)
 {
        BOOST_ASSERT(this == cur.text());
 
@@ -596,7 +622,7 @@ bool Text::cursorRightOneWord(Cursor & cur)
 }
 
 
-bool Text::cursorLeftOneWord(Cursor & cur)
+bool Text::cursorBackwardOneWord(Cursor & cur)
 {
        BOOST_ASSERT(this == cur.text());
 
@@ -799,10 +825,10 @@ void Text::deleteWordForward(Cursor & cur)
        else {
                cur.resetAnchor();
                cur.selection() = true;
-               cursorRightOneWord(cur);
+               cursorForwardOneWord(cur);
                cur.setSelection();
                cutSelection(cur, true, false);
-               checkBufferStructure(cur.buffer(), cur);
+               cur.checkBufferStructure();
        }
 }
 
@@ -815,10 +841,10 @@ void Text::deleteWordBackward(Cursor & cur)
        else {
                cur.resetAnchor();
                cur.selection() = true;
-               cursorLeftOneWord(cur);
+               cursorBackwardOneWord(cur);
                cur.setSelection();
                cutSelection(cur, true, false);
-               checkBufferStructure(cur.buffer(), cur);
+               cur.checkBufferStructure();
        }
 }
 
@@ -830,13 +856,15 @@ void Text::changeCase(Cursor & cur, TextCase action)
        CursorSlice from;
        CursorSlice to;
 
+       bool gotsel = false;
        if (cur.selection()) {
                from = cur.selBegin();
                to = cur.selEnd();
+               gotsel = true;
        } else {
                from = cur.top();
                getWord(from, to, PARTIAL_WORD);
-               cursorRightOneWord(cur);
+               cursorForwardOneWord(cur);
        }
 
        cur.recordUndoSelection();
@@ -857,12 +885,15 @@ void Text::changeCase(Cursor & cur, TextCase action)
        }
 
        // the selection may have changed due to logically-only deleted chars
-       setCursor(cur, begPit, begPos);
-       cur.resetAnchor();
-       setCursor(cur, endPit, right);
-       cur.setSelection();
+       if (gotsel) {
+               setCursor(cur, begPit, begPos);
+               cur.resetAnchor();
+               setCursor(cur, endPit, right);
+               cur.setSelection();
+       } else
+               setCursor(cur, endPit, right);
 
-       checkBufferStructure(cur.buffer(), cur);
+       cur.checkBufferStructure();
 }
 
 
@@ -870,31 +901,37 @@ bool Text::handleBibitems(Cursor & cur)
 {
        if (cur.paragraph().layout()->labeltype != LABEL_BIBLIO)
                return false;
+
+       if (cur.pos() != 0)
+               return false;
+
+       BufferParams const & bufparams = cur.buffer().params();
+       Paragraph const & par = cur.paragraph();
+       Cursor prevcur = cur;
+       if (cur.pit() > 0) {
+               --prevcur.pit();
+               prevcur.pos() = prevcur.lastpos();
+       }
+       Paragraph const & prevpar = prevcur.paragraph();
+
        // if a bibitem is deleted, merge with previous paragraph
        // if this is a bibliography item as well
-       if (cur.pos() == 0) {
-               BufferParams const & bufparams = cur.buffer().params();
-               Paragraph const & par = cur.paragraph();
-               Cursor prevcur = cur;
-               if (cur.pit() > 0) {
-                       --prevcur.pit();
-                       prevcur.pos() = prevcur.lastpos();
-               }
-               Paragraph const & prevpar = prevcur.paragraph();
-               if (cur.pit() > 0 && par.layout() == prevpar.layout()) {
-                       cur.recordUndo(ATOMIC_UNDO, prevcur.pit());
-                       mergeParagraph(bufparams, cur.text()->paragraphs(),
-                                      prevcur.pit());
-                       updateLabels(cur.buffer());
-                       setCursorIntern(cur, prevcur.pit(), prevcur.pos());
-                       cur.updateFlags(Update::Force);
-               // if not, reset the paragraph to default
-               } else
-                       cur.paragraph().layout(
-                               bufparams.getTextClass().defaultLayout());
+       if (cur.pit() > 0 && par.layout() == prevpar.layout()) {
+               cur.recordUndo(ATOMIC_UNDO, prevcur.pit());
+               mergeParagraph(bufparams, cur.text()->paragraphs(),
+                                                       prevcur.pit());
+               updateLabels(cur.buffer());
+               setCursorIntern(cur, prevcur.pit(), prevcur.pos());
+               cur.updateFlags(Update::Force);
                return true;
-       }
-       return false;
+       } 
+
+       // otherwise reset to default
+       if (par.useEmptyLayout())
+               cur.paragraph().setLayout(bufparams.getTextClass().emptyLayout());
+       else
+               cur.paragraph().setLayout(bufparams.getTextClass().defaultLayout());
+       return true;
 }
 
 
@@ -908,11 +945,15 @@ bool Text::erase(Cursor & cur)
                // this is the code for a normal delete, not pasting
                // any paragraphs
                cur.recordUndo(DELETE_UNDO);
-               if(!par.eraseChar(cur.pos(), cur.buffer().params().trackChanges)) {
+               bool const was_inset = cur.paragraph().isInset(cur.pos());
+               if(!par.eraseChar(cur.pos(), cur.buffer().params().trackChanges))
                        // the character has been logically deleted only => skip it
                        cur.top().forwardPos();
-               }
-               checkBufferStructure(cur.buffer(), cur);
+
+               if (was_inset)
+                       updateLabels(cur.buffer());
+               else
+                       cur.checkBufferStructure();
                needsUpdate = true;
        } else {
                if (cur.pit() == cur.lastpit())
@@ -934,7 +975,7 @@ bool Text::erase(Cursor & cur)
                // Make sure the cursor is correct. Is this really needed?
                // No, not really... at least not here!
                cur.text()->setCursor(cur.top(), cur.pit(), cur.pos());
-               checkBufferStructure(cur.buffer(), cur);
+               cur.checkBufferStructure();
        }
 
        return needsUpdate;
@@ -976,8 +1017,10 @@ bool Text::backspacePos0(Cursor & cur)
        // layouts. I think it is a real bug of all other
        // word processors to allow it. It confuses the user.
        // Correction: Pasting is always allowed with standard-layout
+       // or the empty layout.
        else if (par.layout() == prevpar.layout()
-                || par.layout() == tclass.defaultLayout()) {
+                || par.layout() == tclass.defaultLayout()
+                || par.layout() == tclass.emptyLayout()) {
                cur.recordUndo(ATOMIC_UNDO, prevcur.pit());
                mergeParagraph(bufparams, plist, prevcur.pit());
                needsUpdate = true;
@@ -1021,8 +1064,12 @@ bool Text::backspace(Cursor & cur)
                // without the dreaded mechanism. (JMarc)
                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);
-               checkBufferStructure(cur.buffer(), cur);
+               if (was_inset)
+                       updateLabels(cur.buffer());
+               else
+                       cur.checkBufferStructure();
        }
 
        if (cur.pos() == cur.lastpos())
@@ -1045,6 +1092,7 @@ bool Text::dissolveInset(Cursor & cur) {
                return false;
 
        cur.recordUndoInset();
+       cur.mark() = false;
        cur.selHandle(false);
        // save position
        pos_type spos = cur.pos();
@@ -1052,7 +1100,7 @@ bool Text::dissolveInset(Cursor & cur) {
        ParagraphList plist;
        if (cur.lastpit() != 0 || cur.lastpos() != 0)
                plist = paragraphs();
-       cur.popLeft();
+       cur.popBackward();
        // store cursor offset
        if (spit == 0)
                spos += cur.pos();
@@ -1071,8 +1119,8 @@ bool Text::dissolveInset(Cursor & cur) {
                pasteParagraphList(cur, plist, b.params().getTextClassPtr(),
                                   b.errorList("Paste"));
                // restore position
-               cur.pit() = std::min(cur.lastpit(), spit);
-               cur.pos() = std::min(cur.lastpos(), spos);
+               cur.pit() = min(cur.lastpit(), spit);
+               cur.pos() = min(cur.lastpos(), spos);
        }
        cur.clearSelection();
        cur.resetAnchor();
@@ -1106,8 +1154,7 @@ void Text::getWord(CursorSlice & from, CursorSlice & to,
                        --from.pos();
                break;
        case NEXT_WORD:
-               lyxerr << "Text::getWord: NEXT_WORD not implemented yet"
-                      << endl;
+               LYXERR0("Text::getWord: NEXT_WORD not implemented yet");
                break;
        case PARTIAL_WORD:
                // no need to move the 'from' cursor
@@ -1120,7 +1167,7 @@ void Text::getWord(CursorSlice & from, CursorSlice & to,
 }
 
 
-void Text::write(Buffer const & buf, std::ostream & os) const
+void Text::write(Buffer const & buf, ostream & os) const
 {
        ParagraphList::const_iterator pit = paragraphs().begin();
        ParagraphList::const_iterator end = paragraphs().end();
@@ -1134,7 +1181,8 @@ void Text::write(Buffer const & buf, std::ostream & os) const
 }
 
 
-bool Text::read(Buffer const & buf, Lexer & lex, ErrorList & errorList)
+bool Text::read(Buffer const & buf, Lexer & lex, 
+               ErrorList & errorList, InsetText * insetPtr)
 {
        depth_type depth = 0;
 
@@ -1163,6 +1211,7 @@ bool Text::read(Buffer const & buf, Lexer & lex, ErrorList & errorList)
                        Paragraph par;
                        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
@@ -1172,14 +1221,12 @@ bool Text::read(Buffer const & buf, Lexer & lex, ErrorList & errorList)
                } else if (token == "\\begin_deeper") {
                        ++depth;
                } else if (token == "\\end_deeper") {
-                       if (!depth) {
+                       if (!depth)
                                lex.printError("\\end_deeper: " "depth is already null");
-                       } else {
+                       else
                                --depth;
-                       }
                } else {
-                       lyxerr << "Handling unknown body token: `"
-                              << token << '\'' << endl;
+                       LYXERR0("Handling unknown body token: `" << token << '\'');
                }
        }
        return true;
@@ -1255,7 +1302,7 @@ docstring Text::currentState(Cursor & cur)
        if (!par.empty() && cur.pos() < par.size()) {
                // Force output of code point, not character
                size_t const c = par.getChar(cur.pos());
-               os << _(", Char: 0x") << std::hex << c;
+               os << _(", Char: 0x") << hex << c;
        }
        os << _(", Boundary: ") << cur.boundary();
 //     Row & row = cur.textRow();
@@ -1273,6 +1320,9 @@ docstring Text::getPossibleLabel(Cursor & cur) const
 
        docstring text;
        docstring par_text = pars_[pit].asString(cur.buffer(), false);
+       string piece;
+       // the return string of math matrices might contain linebreaks
+       par_text = subst(par_text, '\n', '-');
        for (int i = 0; i < lyxrc.label_init_length; ++i) {
                if (par_text.empty())
                        break;
@@ -1390,7 +1440,7 @@ void Text::charsTranspose(Cursor & cur)
        par.insertChar(pos1, char2, font2, trackChanges);
        par.insertChar(pos2, char1, font1, trackChanges);
 
-       checkBufferStructure(cur.buffer(), cur);
+       cur.checkBufferStructure();
 
        // After the transposition, move cursor to after the transposition.
        setCursor(cur, cur.pit(), pos2);
@@ -1398,4 +1448,16 @@ void Text::charsTranspose(Cursor & cur)
 }
 
 
+DocIterator Text::macrocontextPosition() const
+{
+       return macrocontext_position_;
+}
+
+
+void Text::setMacrocontextPosition(DocIterator const & pos)
+{
+       macrocontext_position_ = pos;
+}
+
+
 } // namespace lyx