]> git.lyx.org Git - lyx.git/blobdiff - src/text.C
unneeded header
[lyx.git] / src / text.C
index 674b7103f16c146a25b47e9055825379d873104b..a0a3c05f83bb3ed35115edd7e21222b97ea12dc0 100644 (file)
@@ -218,7 +218,7 @@ void readParToken(Buffer const & buf, Paragraph & par, LyXLex & lex,
                        par.insertInset(par.size(), inset, font, change);
                else {
                        lex.eatLine();
-                       docstring line = from_utf8(lex.getString());
+                       docstring line = lex.getDocString();
                        errorList.push_back(ErrorItem(_("Unknown Inset"), line,
                                            par.id(), 0, par.size()));
                }
@@ -316,6 +316,10 @@ void readParToken(Buffer const & buf, Paragraph & par, LyXLex & lex,
                par.insertInset(par.size(), new InsetLine, font, change);
        } else if (token == "\\newpage") {
                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") {
@@ -348,7 +352,7 @@ void readParToken(Buffer const & buf, Paragraph & par, LyXLex & lex,
                lex.eatLine();
                errorList.push_back(ErrorItem(_("Unknown token"),
                        bformat(_("Unknown token: %1$s %2$s\n"), from_utf8(token),
-                       from_utf8(lex.getString())),
+                       lex.getDocString()),
                        par.id(), 0, par.size()));
        }
 }
@@ -1070,10 +1074,6 @@ void LyXText::setHeightOfRow(BufferView const & bv, pit_type const pit,
 }
 
 
-namespace {
-
-}
-
 void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
 {
        BOOST_ASSERT(this == cur.text());
@@ -1086,8 +1086,8 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
 
        // this is only allowed, if the current paragraph is not empty
        // or caption and if it has not the keepempty flag active
-       if (cur.lastpos() == 0 && !cpar.allowEmpty()
-          && layout->labeltype != LABEL_SENSITIVE)
+       if (cur.lastpos() == 0 && !cpar.allowEmpty() &&
+           layout->labeltype != LABEL_SENSITIVE)
                return;
 
        // a layout change may affect also the following paragraph
@@ -1096,10 +1096,9 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
        // Always break behind a space
        // It is better to erase the space (Dekel)
        if (cur.pos() != cur.lastpos() && cpar.isLineSeparator(cur.pos()))
-               // FIXME: change tracking (MG)
                cpar.eraseChar(cur.pos(), cur.buffer().params().trackChanges);
 
-       // How should the layout for the new paragraph be?
+       // What should the layout for the new paragraph be?
        int preserve_layout = 0;
        if (keep_layout)
                preserve_layout = 2;
@@ -1132,7 +1131,6 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
        }
 
        while (!pars_[next_par].empty() && pars_[next_par].isNewline(0))
-               // FIXME: change tracking (MG)
                pars_[next_par].eraseChar(0, cur.buffer().params().trackChanges);
 
        ParIterator current_it(cur);
@@ -1142,19 +1140,15 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
 
        updateLabels(cur.buffer(), current_it, last_it);
 
-       // Mark "carriage return" as inserted if change tracking:
-       if (cur.buffer().params().trackChanges) {
-               // FIXME: Change tracking (MG)
-               cur.paragraph().setChange(cur.paragraph().size(),
-                       Change(Change::INSERTED));
-       }
-
        // FIXME: Breaking a paragraph has nothing to do with setting a cursor.
        // Because of the mix between the model (the paragraph contents) and the
        // view (the paragraph breaking in rows, we have to do this here before
        // the setCursor() call below.
-       redoParagraph(cur.bv(), cpit);
-       redoParagraph(cur.bv(), cpit + 1);
+       bool changed_height = redoParagraph(cur.bv(), cpit);
+       changed_height |= redoParagraph(cur.bv(), cpit + 1);
+       if (changed_height)
+               // A singlePar update is not enough in this case.
+               cur.updateFlags(Update::Force);
 
        // This check is necessary. Otherwise the new empty paragraph will
        // be deleted automatically. And it is more friendly for the user!
@@ -1253,7 +1247,9 @@ void LyXText::insertChar(LCursor & cur, char_type c)
        // FIXME: Inserting a character has nothing to do with setting a cursor.
        // Because of the mix between the model (the paragraph contents) and the
        // view (the paragraph breaking in rows, we have to do this here.
-       redoParagraph(cur.bv(), cur.pit());
+       if (redoParagraph(cur.bv(), cur.pit()))
+               // A singlePar update is not enough in this case.
+               cur.updateFlags(Update::Force);
        setCursor(cur, cur.pit(), cur.pos() + 1, false, cur.boundary());
        charInserted();
 }
@@ -1462,34 +1458,31 @@ bool LyXText::selectWordWhenUnderCursor(LCursor & cur, word_location loc)
 
 void LyXText::acceptChange(LCursor & cur)
 {
+       // FIXME: change tracking (MG)
+
        BOOST_ASSERT(this == cur.text());
+
        if (!cur.selection() && cur.lastpos() != 0)
                return;
 
+       // FIXME: we must handle start = end = 0
+
        recordUndoSelection(cur, Undo::INSERT);
 
        DocIterator it = cur.selectionBegin();
        DocIterator et = cur.selectionEnd();
        pit_type pit = it.pit();
-       bool isDeleted = pars_[pit].isDeleted(it.pos());
        for (; pit <= et.pit(); ++pit) {
-               pos_type left  = ( pit == it.pit() ? it.pos() : 0 );
-               pos_type right =
-                   ( pit == et.pit() ? et.pos() : pars_[pit].size() + 1 );
+               pos_type left  = (pit == it.pit() ? it.pos() : 0);
+               pos_type right = (pit == et.pit() ? et.pos() : pars_[pit].size());
                pars_[pit].acceptChanges(left, right);
-       }
-       if (isDeleted) {
-               ParagraphList & plist = paragraphs();
-               if (it.pit() + 1 < et.pit())
-                       pars_.erase(boost::next(plist.begin(), it.pit() + 1),
-                                   boost::next(plist.begin(), et.pit()));
 
-               // Paragraph merge if appropriate:
-               // FIXME: change tracking (MG)
-               if (pars_[it.pit()].isDeleted(pars_[it.pit()].size())) {
-                       setCursorIntern(cur, it.pit() + 1, 0);
-                       backspacePos0(cur);
-               }
+               // merge paragraph if appropriate:
+               // if (right >= pars_[pit].size() && pit + 1 < et.pit() &&
+               //    pars_[pit].isDeleted(pars_[pit].size())) {
+               //      setCursorIntern(cur, pit + 1, 0);
+               //      backspacePos0(cur);
+               //}
        }
        finishUndo();
        cur.clearSelection();
@@ -1499,33 +1492,31 @@ void LyXText::acceptChange(LCursor & cur)
 
 void LyXText::rejectChange(LCursor & cur)
 {
+       // FIXME: change tracking (MG)
+
        BOOST_ASSERT(this == cur.text());
+
        if (!cur.selection() && cur.lastpos() != 0)
                return;
 
+       // FIXME: we must handle start = end = 0
+
        recordUndoSelection(cur, Undo::INSERT);
 
        DocIterator it = cur.selectionBegin();
        DocIterator et = cur.selectionEnd();
        pit_type pit = it.pit();
-       bool isInserted = pars_[pit].isInserted(it.pos());
        for (; pit <= et.pit(); ++pit) {
-               pos_type left  = ( pit == it.pit() ? it.pos() : 0 );
-               pos_type right =
-                   ( pit == et.pit() ? et.pos() : pars_[pit].size() + 1 );
+               pos_type left  = (pit == it.pit() ? it.pos() : 0);
+               pos_type right = (pit == et.pit() ? et.pos() : pars_[pit].size());
                pars_[pit].rejectChanges(left, right);
-       }
-       if (isInserted) {
-               ParagraphList & plist = paragraphs();
-               if (it.pit() + 1 < et.pit())
-                       pars_.erase(boost::next(plist.begin(), it.pit() + 1),
-                                   boost::next(plist.begin(), et.pit()));
-               // Paragraph merge if appropriate:
-               // FIXME: change tracking (MG)
-               if (pars_[it.pit()].isInserted(pars_[it.pit()].size())) {
-                       setCursorIntern(cur, it.pit() + 1, 0);
-                       backspacePos0(cur);
-               }
+
+               // merge paragraph if appropriate:      
+               // if (right >= pars_[pit].size() && pit + 1 < et.pit() &&
+               //    pars_[pit].isInserted(pars_[pit].size())) {
+               //      setCursorIntern(cur, pit + 1, 0);
+               //      backspacePos0(cur);
+               //}
        }
        finishUndo();
        cur.clearSelection();
@@ -1648,32 +1639,36 @@ bool LyXText::erase(LCursor & cur)
                // this is the code for a normal delete, not pasting
                // any paragraphs
                recordUndo(cur, Undo::DELETE);
-               // FIXME: change tracking (MG)
                par.eraseChar(cur.pos(), cur.buffer().params().trackChanges);
                if (par.isDeleted(cur.pos()))
-                       cur.forwardPos();
+                       cur.forwardPosNoDescend();
                needsUpdate = true;
-       } else if (cur.pit() != cur.lastpit()) {
-               if (cur.buffer().params().trackChanges
-                   && par.isInserted(cur.pos())) {
-                       // mark "carriage return" as deleted:
-                       // FIXME: Change tracking (MG)
+       } else {
+               if (cur.pit() == cur.lastpit())
+                       return dissolveInset(cur);
+
+               if (!par.isMergedOnEndOfParDeletion(cur.buffer().params().trackChanges)) {
                        par.setChange(cur.pos(), Change(Change::DELETED));
                        cur.forwardPos();
                        needsUpdate = true;
                } else {
                        setCursorIntern(cur, cur.pit() + 1, 0);
                        needsUpdate = backspacePos0(cur);
-                       // FIXME: Change tracking (MG)
-                       if (cur.paragraph().isDeleted(cur.pos()))
-                               cur.forwardPos();
                }
-       } else
-               needsUpdate = dissolveInset(cur);
+       }
 
-       // Make sure the cursor is correct. Is this really needed?
-       if (needsUpdate)
-               setCursorIntern(cur, cur.pit(), cur.pos());
+       // FIXME: Inserting characters has nothing to do with setting a cursor.
+       // Because of the mix between the model (the paragraph contents)
+       // and the view (the paragraph breaking in rows, we have to do this
+       // here before the setCursorIntern() call.
+       if (needsUpdate) {
+               if (redoParagraph(cur.bv(), cur.pit()))
+                       // A singlePar update is not enough in this case.
+                       cur.updateFlags(Update::Force);
+               // Make sure the cursor is correct. Is this really needed?
+               // No, not really... at least not here!
+               cur.text()->setCursorIntern(cur, cur.pit(), cur.pos());
+       }
        
        return needsUpdate;
 }
@@ -1711,7 +1706,7 @@ bool LyXText::backspacePos0(LCursor & cur)
                needsUpdate = true;
        }
        // Pasting is not allowed, if the paragraphs have different
-       // layout. I think it is a real bug of all other
+       // 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
        else if (par.layout() == prevpar.layout()
@@ -1730,8 +1725,6 @@ bool LyXText::backspacePos0(LCursor & cur)
 }
 
 
-
-
 bool LyXText::backspace(LCursor & cur)
 {
        BOOST_ASSERT(this == cur.text());
@@ -1740,21 +1733,13 @@ bool LyXText::backspace(LCursor & cur)
                if (cur.pit() == 0)
                        return dissolveInset(cur);
 
-               if (cur.buffer().params().trackChanges) {
-                       // FIXME: Change tracking (MG)
-                       // Previous paragraph, mark "carriage return" as
-                       // deleted:
-                       Paragraph & par = pars_[cur.pit() - 1];
-                       // Take care of a just inserted para break:
-                       // FIXME: change tracking (MG)
-                       if (!par.isInserted(par.size())) {
-                               // FIXME: change tracking (MG)
-                               par.setChange(par.size(), Change(Change::DELETED));
-                               setCursorIntern(cur, cur.pit() - 1, par.size());
-                               return true;
-                       }
-               }
+               Paragraph & prev_par = pars_[cur.pit() - 1];
 
+               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;
+               }
                // The cursor is at the beginning of a paragraph, so
                // the backspace will collapse two paragraphs into one.
                needsUpdate = backspacePos0(cur);
@@ -1769,7 +1754,6 @@ bool LyXText::backspace(LCursor & cur)
                // without the dreaded mechanism. (JMarc)
                setCursorIntern(cur, cur.pit(), cur.pos() - 1,
                                false, cur.boundary());
-               // FIXME: change tracking (MG)
                cur.paragraph().eraseChar(cur.pos(), cur.buffer().params().trackChanges);
        }
 
@@ -1780,7 +1764,9 @@ bool LyXText::backspace(LCursor & cur)
        // Because of the mix between the model (the paragraph contents)
        // and the view (the paragraph breaking in rows, we have to do this
        // here before the setCursor() call.
-       redoParagraph(cur.bv(), cur.pit());
+       if (redoParagraph(cur.bv(), cur.pit()))
+               // A singlePar update is not enough in this case.
+               cur.updateFlags(Update::Force);
        setCursor(cur, cur.pit(), cur.pos(), false, cur.boundary());
 
        return needsUpdate;
@@ -1793,6 +1779,7 @@ bool LyXText::dissolveInset(LCursor & cur) {
        if (isMainText(*cur.bv().buffer()) || cur.inset().nargs() != 1)
                return false;
 
+       bool const in_ert = cur.inset().lyxCode() == InsetBase::ERT_CODE;
        recordUndoInset(cur);
        cur.selHandle(false);
        // save position
@@ -1807,9 +1794,21 @@ bool LyXText::dissolveInset(LCursor & cur) {
                spos += cur.pos();
        spit += cur.pit();
        Buffer & b = cur.buffer();
-       // FIXME: change tracking (MG)
        cur.paragraph().eraseChar(cur.pos(), b.params().trackChanges);
        if (!plist.empty()) {
+               if (in_ert) {
+                       // ERT paragraphs have the Language latex_language.
+                       // This is invalid outside of ERT, so we need to
+                       // change it to the buffer language.
+                       ParagraphList::iterator it = plist.begin();
+                       ParagraphList::iterator it_end = plist.end();
+                       for (; it != it_end; it++) {
+                               it->changeLanguage(b.params(),
+                                               latex_language,
+                                               b.getLanguage());
+                       }
+               }
+
                pasteParagraphList(cur, plist, b.params().textclass,
                                   b.errorList("Paste"));
                // restore position
@@ -1834,6 +1833,8 @@ bool LyXText::redoParagraph(BufferView & bv, pit_type const pit)
        Paragraph & par = pars_[pit];
        Buffer const & buffer = *bv.buffer();
 
+       bool changed = false;
+
        // Add bibitem insets if necessary
        if (par.layout()->labeltype == LABEL_BIBLIO) {
                bool hasbibitem(false);
@@ -1862,8 +1863,8 @@ bool LyXText::redoParagraph(BufferView & bv, pit_type const pit)
        // FIXME: We should always use getFont(), see documentation of
        // noFontChange() in insetbase.h.
        LyXFont const bufferfont = buffer.params().getFont();
-       InsetList::iterator ii = par.insetlist.begin();
-       InsetList::iterator iend = par.insetlist.end();
+       InsetList::const_iterator ii = par.insetlist.begin();
+       InsetList::const_iterator iend = par.insetlist.end();
        for (; ii != iend; ++ii) {
                Dimension dim;
                int const w = maxwidth_ - leftMargin(buffer, pit, ii->pos)
@@ -1872,7 +1873,7 @@ bool LyXText::redoParagraph(BufferView & bv, pit_type const pit)
                        bufferfont :
                        getFont(buffer, par, ii->pos);
                MetricsInfo mi(&bv, font, w);
-               ii->inset->metrics(mi, dim);
+               changed |= ii->inset->metrics(mi, dim);
        }
 
        // rebreak the paragraph
@@ -1909,16 +1910,16 @@ bool LyXText::redoParagraph(BufferView & bv, pit_type const pit)
        dim.asc += par.rows()[0].ascent();
        dim.des -= par.rows()[0].ascent();
 
-       bool const same = dim == par.dim();
+       changed |= dim.height() != par.dim().height();
 
        par.dim() = dim;
        //lyxerr << "redoParagraph: " << par.rows().size() << " rows\n";
 
-       return !same;
+       return changed;
 }
 
 
-void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
+bool LyXText::metrics(MetricsInfo & mi, Dimension & dim)
 {
        //BOOST_ASSERT(mi.base.textwidth);
        if (mi.base.textwidth)
@@ -1928,10 +1929,12 @@ void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
        // save the caller's font locally:
        font_ = mi.base.font;
 
+       bool changed = false;
+
        unsigned int h = 0;
        unsigned int w = 0;
        for (pit_type pit = 0, n = paragraphs().size(); pit != n; ++pit) {
-               redoParagraph(*mi.base.bv, pit);
+               changed |= redoParagraph(*mi.base.bv, pit);
                Paragraph & par = paragraphs()[pit];
                h += par.height();
                if (w < par.width())
@@ -1942,7 +1945,9 @@ void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
        dim.asc = pars_[0].ascent();
        dim.des = h - dim.asc;
 
+       changed |= dim_ != dim;
        dim_ = dim;
+       return changed;
 }
 
 
@@ -2508,4 +2513,61 @@ bool LyXText::setCursorFromCoordinates(LCursor & cur, int const x, int const y)
 }
 
 
+void LyXText::charsTranspose(LCursor & cur)
+{
+       BOOST_ASSERT(this == cur.text());
+
+       pos_type pos = cur.pos();
+
+       // If cursor is at beginning or end of paragraph, do nothing.
+       if (pos == cur.lastpos() || pos == 0)
+               return;
+
+       Paragraph & par = cur.paragraph();
+
+       // Get the positions of the characters to be transposed. 
+       pos_type pos1 = pos - 1;
+       pos_type pos2 = pos;
+
+       // In change tracking mode, ignore deleted characters.
+       while (pos2 < cur.lastpos() && par.isDeleted(pos2))
+               ++pos2;
+       if (pos2 == cur.lastpos())
+               return;
+
+       while (pos1 >= 0 && par.isDeleted(pos1))
+               --pos1;
+       if (pos1 < 0)
+               return;
+
+       // Don't do anything if one of the "characters" is not regular text.
+       if (par.isInset(pos1) || par.isInset(pos2))
+               return;
+
+       // Store the characters to be transposed (including font information).
+       char_type char1 = par.getChar(pos1);
+       LyXFont const font1 =
+               par.getFontSettings(cur.buffer().params(), pos1);
+       
+       char_type char2 = par.getChar(pos2);
+       LyXFont const font2 =
+               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;
+
+       recordUndo(cur);
+
+       par.eraseChar(pos2, trackChanges);
+       par.eraseChar(pos1, trackChanges);
+       par.insertChar(pos1, char2, font2, trackChanges);
+       par.insertChar(pos2, char1, font1, trackChanges);
+
+       // After the transposition, move cursor to after the transposition.
+       setCursor(cur, cur.pit(), pos2);
+       cur.forwardPos();
+}
+
+
 } // namespace lyx