]> git.lyx.org Git - lyx.git/blobdiff - src/text.C
hopefully fix tex2lyx linking.
[lyx.git] / src / text.C
index 872ef17c1bf785fcb5495f60ed24783a52911038..7ca49fe69e88754bee75bb0fcdb28d7305d990fd 100644 (file)
@@ -695,8 +695,8 @@ pos_type addressBreakPoint(pos_type i, Paragraph const & par)
 };
 
 
-void LyXText::rowBreakPoint(Buffer const & buffer, pit_type const pit,
-               Row & row) const
+void LyXText::rowBreakPoint(Buffer const & buffer, int right_margin,
+               pit_type const pit,     Row & row) const
 {
        Paragraph const & par = pars_[pit];
        pos_type const end = par.size();
@@ -707,7 +707,7 @@ void LyXText::rowBreakPoint(Buffer const & buffer, pit_type const pit,
        }
 
        // maximum pixel width of a row
-       int width = maxwidth_ - rightMargin(buffer, par); // - leftMargin(buffer, pit, row);
+       int width = maxwidth_ - right_margin; // - leftMargin(buffer, pit, row);
        if (width < 0) {
                row.endpos(end);
                return;
@@ -1149,6 +1149,17 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
                        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.
+       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!
        if (cur.pos() != 0 || isempty)
@@ -1242,6 +1253,13 @@ void LyXText::insertChar(LCursor & cur, char_type c)
        }
 
        par.insertChar(cur.pos(), c, current_font, cur.buffer().params().trackChanges);
+
+       // 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.
+       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();
 }
@@ -1630,35 +1648,48 @@ bool LyXText::erase(LCursor & cur)
 {
        BOOST_ASSERT(this == cur.text());
        bool needsUpdate = false;
+       Paragraph & par = cur.paragraph();
 
        if (cur.pos() != cur.lastpos()) {
-               recordUndo(cur, Undo::DELETE, cur.pit());
-               setCursorIntern(cur, cur.pit(), cur.pos() + 1, false, cur.boundary());
-               needsUpdate = backspace(cur);
+               // this is the code for a normal delete, not pasting
+               // any paragraphs
+               recordUndo(cur, Undo::DELETE);
                // FIXME: change tracking (MG)
-               if (cur.paragraph().isDeleted(cur.pos()))
-                       cur.posRight();
+               par.eraseChar(cur.pos(), cur.buffer().params().trackChanges);
+               if (par.isDeleted(cur.pos()))
+                       cur.forwardPos();
+               needsUpdate = true;
        } else if (cur.pit() != cur.lastpit()) {
-               LCursor scur = cur;
-
-               setCursorIntern(cur, cur.pit() + 1, 0, false, false);
-               if (pars_[cur.pit()].layout() == pars_[scur.pit()].layout()) {
-                       recordUndo(scur, Undo::DELETE, scur.pit());
-                       needsUpdate = backspace(cur);
-                       if (cur.buffer().params().trackChanges) {
-                               // FIXME: Change tracking (MG)
-                               // move forward after the paragraph break is DELETED
-                               Paragraph & par = cur.paragraph();
-                               // FIXME: change tracking (MG)
-                               if (par.isDeleted(par.size()))
-                                       setCursorIntern(cur, cur.pit() + 1, 0);
-                               }
+               if (cur.buffer().params().trackChanges
+                   && par.isInserted(cur.pos())) {
+                       // mark "carriage return" as deleted:
+                       // FIXME: Change tracking (MG)
+                       par.setChange(cur.pos(), Change(Change::DELETED));
+                       cur.forwardPos();
+                       needsUpdate = true;
                } else {
-                       setCursorIntern(scur, scur.pit(), scur.pos(), false, scur.boundary());
+                       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);
 
+       // 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!
+               setCursorIntern(cur, cur.pit(), cur.pos());
+       }
+       
        return needsUpdate;
 }
 
@@ -1666,79 +1697,56 @@ bool LyXText::erase(LCursor & cur)
 bool LyXText::backspacePos0(LCursor & cur)
 {
        BOOST_ASSERT(this == cur.text());
+       if (cur.pit() == 0)
+               return false;
+
        bool needsUpdate = false;
 
-       Paragraph & par = cur.paragraph();
-       // is it an empty paragraph?
-       pos_type lastpos = cur.lastpos();
-       if (lastpos == 0 || (lastpos == 1 && par.isSeparator(0))) {
-               // This is an empty paragraph and we delete it just
-               // by moving the cursor one step
-               // left and let the DeleteEmptyParagraphMechanism
-               // handle the actual deletion of the paragraph.
-
-               if (cur.pit() != 0) {
-                       // For KeepEmpty layouts we need to get
-                       // rid of the keepEmpty setting first.
-                       // And the only way to do this is to
-                       // reset the layout to something
-                       // else: f.ex. the default layout.
-                       if (par.allowEmpty()) {
-                               Buffer & buf = cur.buffer();
-                               BufferParams const & bparams = buf.params();
-                               par.layout(bparams.getLyXTextClass().defaultLayout());
-                       }
+       BufferParams const & bufparams = cur.buffer().params();
+       LyXTextClass const & tclass = bufparams.getLyXTextClass();
+       ParagraphList & plist = cur.text()->paragraphs();
+       Paragraph const & par = cur.paragraph();
+       LCursor prevcur = cur;
+       --prevcur.pit();
+       prevcur.pos() = prevcur.lastpos();
+       Paragraph const & prevpar = prevcur.paragraph();
 
-                       cursorLeft(cur);
-                       return true;
-               }
+       // is it an empty paragraph?
+       if (cur.lastpos() == 0 
+           || (cur.lastpos() == 1 && par.isSeparator(0))) {
+               recordUndo(cur, Undo::ATOMIC, prevcur.pit(), cur.pit());
+               plist.erase(boost::next(plist.begin(), cur.pit()));
+               needsUpdate = true;
        }
-
-       if (cur.pit() != 0)
-               recordUndo(cur, Undo::DELETE, cur.pit() - 1);
-
-       pit_type tmppit = cur.pit();
-       // We used to do cursorLeftIntern() here, but it is
-       // not a good idea since it triggers the auto-delete
-       // mechanism. So we do a cursorLeftIntern()-lite,
-       // without the dreaded mechanism. (JMarc)
-       if (cur.pit() != 0) {
-               // steps into the above paragraph.
-               setCursorIntern(cur, cur.pit() - 1,
-                               pars_[cur.pit() - 1].size(),
-                               false);
+       // is previous par empty?
+       else if (prevcur.lastpos() == 0 
+                || (prevcur.lastpos() == 1 && prevpar.isSeparator(0))) {
+               recordUndo(cur, Undo::ATOMIC, prevcur.pit(), cur.pit());
+               plist.erase(boost::next(plist.begin(), prevcur.pit()));
+               needsUpdate = true;
        }
-
        // Pasting is not allowed, if the paragraphs have different
        // layout. 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
-       // Correction (Jug 20050717): Remove check about alignment!
-       Buffer & buf = cur.buffer();
-       BufferParams const & bufparams = buf.params();
-       LyXTextClass const & tclass = bufparams.getLyXTextClass();
-       pit_type const cpit = cur.pit();
-
-       if (cpit != tmppit
-           && (pars_[cpit].layout() == pars_[tmppit].layout()
-               || pars_[tmppit].layout() == tclass.defaultLayout()))
-       {
-               mergeParagraph(bufparams, pars_, cpit);
+       else if (par.layout() == prevpar.layout()
+                || par.layout() == tclass.defaultLayout()) {
+               recordUndo(cur, Undo::ATOMIC, prevcur.pit());
+               mergeParagraph(bufparams, plist, prevcur.pit());
                needsUpdate = true;
+       }
 
-               if (cur.pos() != 0 && pars_[cpit].isSeparator(cur.pos() - 1))
-                               --cur.pos();
-
-               // the counters may have changed
-               ParIterator par_it(cur);
-               updateLabels(cur.buffer(), par_it);
-
-               setCursor(cur, cur.pit(), cur.pos(), false);
+       if (needsUpdate) {
+               updateLabels(cur.buffer());
+               setCursorIntern(cur, prevcur.pit(), prevcur.pos());
        }
+
        return needsUpdate;
 }
 
 
+
+
 bool LyXText::backspace(LCursor & cur)
 {
        BOOST_ASSERT(this == cur.text());
@@ -1747,11 +1755,7 @@ bool LyXText::backspace(LCursor & cur)
                if (cur.pit() == 0)
                        return dissolveInset(cur);
 
-               // The cursor is at the beginning of a paragraph, so
-               // the the backspace will collapse two paragraphs into
-               // one.
-
-               if (cur.pit() != 0 && cur.buffer().params().trackChanges) {
+               if (cur.buffer().params().trackChanges) {
                        // FIXME: Change tracking (MG)
                        // Previous paragraph, mark "carriage return" as
                        // deleted:
@@ -1766,6 +1770,8 @@ bool LyXText::backspace(LCursor & cur)
                        }
                }
 
+               // The cursor is at the beginning of a paragraph, so
+               // the backspace will collapse two paragraphs into one.
                needsUpdate = backspacePos0(cur);
 
        } else {
@@ -1785,6 +1791,13 @@ bool LyXText::backspace(LCursor & cur)
        if (cur.pos() == cur.lastpos())
                setCurrentFont(cur);
 
+       // FIXME: Backspacing 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.
+       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;
@@ -1797,6 +1810,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
@@ -1814,6 +1828,19 @@ bool LyXText::dissolveInset(LCursor & cur) {
        // 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
@@ -1858,6 +1885,10 @@ bool LyXText::redoParagraph(BufferView & bv, pit_type const pit)
                }
        }
 
+       // Optimisation: this is used in the next two loops
+       // so better to calculate that once here.
+       int right_margin = rightMargin(buffer, par);
+
        // redo insets
        // FIXME: We should always use getFont(), see documentation of
        // noFontChange() in insetbase.h.
@@ -1867,7 +1898,7 @@ bool LyXText::redoParagraph(BufferView & bv, pit_type const pit)
        for (; ii != iend; ++ii) {
                Dimension dim;
                int const w = maxwidth_ - leftMargin(buffer, pit, ii->pos)
-                       - rightMargin(buffer, par);
+                       - right_margin;
                LyXFont const & font = ii->inset->noFontChange() ?
                        bufferfont :
                        getFont(buffer, par, ii->pos);
@@ -1883,7 +1914,7 @@ bool LyXText::redoParagraph(BufferView & bv, pit_type const pit)
        pos_type z = 0;
        do {
                Row row(z);
-               rowBreakPoint(buffer, pit, row);
+               rowBreakPoint(buffer, right_margin, pit, row);
                setRowWidth(buffer, pit, row);
                setHeightOfRow(bv, pit, row);
                par.rows().push_back(row);
@@ -1909,7 +1940,7 @@ 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();
+       bool const same = dim.height() == par.dim().height();
 
        par.dim() = dim;
        //lyxerr << "redoParagraph: " << par.rows().size() << " rows\n";
@@ -2372,7 +2403,7 @@ string LyXText::currentState(LCursor & cur)
 }
 
 
-string LyXText::getPossibleLabel(LCursor & cur) const
+docstring LyXText::getPossibleLabel(LCursor & cur) const
 {
        pit_type pit = cur.pit();
 
@@ -2386,7 +2417,7 @@ string LyXText::getPossibleLabel(LCursor & cur) const
                }
        }
 
-       string name = layout->latexname();
+       docstring name = from_ascii(layout->latexname());
 
        // for captions, we want the abbreviation of the float type
        if (layout->labeltype == LABEL_SENSITIVE) {
@@ -2395,26 +2426,25 @@ string LyXText::getPossibleLabel(LCursor & cur) const
                        InsetBase * const in = &cur[i].inset();
                        if (in->lyxCode() == InsetBase::FLOAT_CODE
                            || in->lyxCode() == InsetBase::WRAP_CODE) {
-                               name = to_utf8(in->getInsetName());
+                               name = in->getInsetName();
                                break;
                        }
                }
        }
 
-       string text = name.substr(0, 3);
+       docstring text = name.substr(0, 3);
        if (name == "theorem")
-               text = "thm"; // Create a correct prefix for prettyref
+               text = from_ascii("thm"); // Create a correct prefix for prettyref
 
        text += ':';
        if (layout->latextype == LATEX_PARAGRAPH || lyxrc.label_init_length < 0)
                text.erase();
 
-       // FIXME UNICODE
-       string par_text = to_utf8(pars_[pit].asString(cur.buffer(), false));
+       docstring par_text = pars_[pit].asString(cur.buffer(), false);
        for (int i = 0; i < lyxrc.label_init_length; ++i) {
                if (par_text.empty())
                        break;
-               string head;
+               docstring head;
                par_text = split(par_text, head, ' ');
                // Is it legal to use spaces in labels ?
                if (i > 0)
@@ -2509,4 +2539,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