]> git.lyx.org Git - lyx.git/blobdiff - src/text.C
fix crash when collapsing ert with cursor inside
[lyx.git] / src / text.C
index 8b1cde513f5b1e014d4a45648c3a0c0224a225f0..18c6df2476fc9f6cdb6fb1d6e29e4684bd6774f5 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "author.h"
 #include "buffer.h"
+#include "buffer_funcs.h"
 #include "bufferparams.h"
 #include "BufferView.h"
 #include "cursor.h"
@@ -67,7 +68,7 @@
 
 #include "support/lstrings.h"
 #include "support/textutils.h"
-#include "support/tostr.h"
+#include "support/convert.h"
 
 #include <sstream>
 
@@ -376,12 +377,6 @@ void readParagraph(Buffer const & buf, Paragraph & par, LyXLex & lex)
 } // namespace anon
 
 
-BufferView * LyXText::bv()
-{
-       BOOST_ASSERT(bv_owner != 0);
-       return bv_owner;
-}
-
 
 BufferView * LyXText::bv() const
 {
@@ -596,7 +591,7 @@ int LyXText::leftMargin(pit_type const pit, pos_type const pos) const
            && align == LYX_ALIGN_BLOCK
            && !par.params().noindent()
            // in charstyles, tabulars and ert paragraphs are never indented!
-           && (par.ownerCode() != InsetBase::TEXT_CODE
+           && ((par.ownerCode() != InsetBase::TEXT_CODE || isMainText())
                    && par.ownerCode() != InsetBase::ERT_CODE
                    && par.ownerCode() != InsetBase::CHARSTYLE_CODE)
            && (par.layout() != tclass.defaultLayout()
@@ -916,12 +911,13 @@ void LyXText::setHeightOfRow(pit_type const pit, Row & row)
                                maxasc += bufparams.getDefSkip().inPixels(*bv());
                }
 
-               if (pars_[pit].params().startOfAppendix())
+               if (par.params().startOfAppendix())
                        maxasc += int(3 * dh);
 
                // This is special code for the chapter, since the label of this
                // layout is printed in an extra row
-               if (layout->counter == "chapter" && bufparams.secnumdepth >= 0) {
+               if (layout->counter == "chapter"
+                   && !par.params().labelString().empty()) {
                        labeladdon = int(font_metrics::maxHeight(labelfont)
                                     * layout->spacing.getValue()
                                     * spacing(par));
@@ -999,7 +995,15 @@ void LyXText::setHeightOfRow(pit_type const pit, Row & row)
        // incalculate the layout spaces
        maxasc  += int(layoutasc  * 2 / (2 + pars_[pit].getDepth()));
        maxdesc += int(layoutdesc * 2 / (2 + pars_[pit].getDepth()));
-       
+
+       // Top and bottom margin of the document (only at top-level)
+       if (bv_owner->text() == this) {
+               if (pit == 0 && row.pos() == 0)
+                       maxasc += 20;
+               if (pit == pars_.size() - 1 && row.endpos() == par.size())
+                       maxdesc += 20;
+       }
+
        row.ascent(maxasc + labeladdon);
        row.descent(maxdesc);
 }
@@ -1009,7 +1013,7 @@ namespace {
 
 }
 
-void LyXText::breakParagraph(LCursor & cur, char keep_layout)
+void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
 {
        BOOST_ASSERT(this == cur.text());
        // allow only if at start or end, or all previous is new text
@@ -1037,25 +1041,30 @@ void LyXText::breakParagraph(LCursor & cur, char keep_layout)
        if (cur.pos() != cur.lastpos() && cpar.isLineSeparator(cur.pos()))
                cpar.erase(cur.pos());
 
-       // break the paragraph
+       // How should the layout for the new paragraph be?
+       int preserve_layout = 0;
        if (keep_layout)
-               keep_layout = 2;
+               preserve_layout = 2;
        else
-               keep_layout = layout->isEnvironment();
+               preserve_layout = layout->isEnvironment();
+
+       // We need to remember this before we break the paragraph, because
+       // that invalidates the layout variable
+       bool sensitive = layout->labeltype == LABEL_SENSITIVE;
 
-       // we need to set this before we insert the paragraph. IMO the
-       // breakParagraph call should return a bool if it inserts the
-       // paragraph before or behind and we should react on that one
-       // but we can fix this in 1.3.0 (Jug 20020509)
+       // we need to set this before we insert the paragraph.
        bool const isempty = cpar.allowEmpty() && cpar.empty();
+
        ::breakParagraph(cur.buffer().params(), paragraphs(), cpit,
-                        cur.pos(), keep_layout);
+                        cur.pos(), preserve_layout);
+
+       // After this, neither paragraph contains any rows!
 
        cpit = cur.pit();
        pit_type next_par = cpit + 1;
 
        // well this is the caption hack since one caption is really enough
-       if (layout->labeltype == LABEL_SENSITIVE) {
+       if (sensitive) {
                if (cur.pos() == 0)
                        // set to standard-layout
                        pars_[cpit].applyLayout(tclass.defaultLayout());
@@ -1064,20 +1073,10 @@ void LyXText::breakParagraph(LCursor & cur, char keep_layout)
                        pars_[next_par].applyLayout(tclass.defaultLayout());
        }
 
-       // if the cursor is at the beginning of a row without prior newline,
-       // move one row up!
-       // This touches only the screen-update. Otherwise we would may have
-       // an empty row on the screen
-       if (cur.pos() != 0 && cur.textRow().pos() == cur.pos()
-           && !pars_[cpit].isNewline(cur.pos() - 1))
-       {
-               cursorLeft(cur);
-       }
-
        while (!pars_[next_par].empty() && pars_[next_par].isNewline(0))
                pars_[next_par].erase(0);
 
-       updateCounters();
+       updateCounters(cur.buffer());
 
        // This check is necessary. Otherwise the new empty paragraph will
        // be deleted automatically. And it is more friendly for the user!
@@ -1164,8 +1163,8 @@ void LyXText::insertChar(LCursor & cur, char c)
                                cur.message(_("You cannot insert a space at the "
                                        "beginning of a paragraph. Please read the Tutorial."));
                                sent_space_message = true;
-                               return;
                        }
+                       return;
                }
                BOOST_ASSERT(cur.pos() > 0);
                if (par.isLineSeparator(cur.pos() - 1)
@@ -1319,41 +1318,47 @@ LyXText::computeRowMetrics(pit_type const pit, Row const & row) const
 // the cursor set functions have a special mechanism. When they
 // realize, that you left an empty paragraph, they will delete it.
 
-void LyXText::cursorRightOneWord(LCursor & cur)
+bool LyXText::cursorRightOneWord(LCursor & cur)
 {
        BOOST_ASSERT(this == cur.text());
-       if (cur.pos() == cur.lastpos() && cur.pit() != cur.lastpit()) {
-               ++cur.pit();
-               cur.pos() = 0;
+
+       LCursor old = cur;
+
+       if (old.pos() == old.lastpos() && old.pit() != old.lastpit()) {
+               ++old.pit();
+               old.pos() = 0;
        } else {
                // Skip through initial nonword stuff.
                // Treat floats and insets as words.
-               while (cur.pos() != cur.lastpos() && !cur.paragraph().isLetter(cur.pos()))
-                       ++cur.pos();
+               while (old.pos() != old.lastpos() && !old.paragraph().isLetter(old.pos()))
+                       ++old.pos();
                // Advance through word.
-               while (cur.pos() != cur.lastpos() && cur.paragraph().isLetter(cur.pos()))
-                       ++cur.pos();
+               while (old.pos() != old.lastpos() && old.paragraph().isLetter(old.pos()))
+                       ++old.pos();
        }
-       setCursor(cur, cur.pit(), cur.pos());
+       return setCursor(cur, old.pit(), old.pos());
 }
 
 
-void LyXText::cursorLeftOneWord(LCursor & cur)
+bool LyXText::cursorLeftOneWord(LCursor & cur)
 {
        BOOST_ASSERT(this == cur.text());
-       if (cur.pos() == 0 && cur.pit() != 0) {
-               --cur.pit();
-               cur.pos() = cur.lastpos();
+
+       LCursor old = cur;
+
+       if (old.pos() == 0 && old.pit() != 0) {
+               --old.pit();
+               old.pos() = old.lastpos();
        } else {
                // Skip through initial nonword stuff.
                // Treat floats and insets as words.
-               while (cur.pos() != 0 && !cur.paragraph().isLetter(cur.pos() - 1))
-                       --cur.pos();
+               while (old.pos() != 0 && !old.paragraph().isLetter(old.pos() - 1))
+                       --old.pos();
                // Advance through word.
-               while (cur.pos() != 0 && cur.paragraph().isLetter(cur.pos() - 1))
-                       --cur.pos();
+               while (old.pos() != 0 && old.paragraph().isLetter(old.pos() - 1))
+                       --old.pos();
        }
-       setCursor(cur, cur.pit(), cur.pos());
+       return setCursor(cur, old.pit(), old.pos());
 }
 
 
@@ -1606,7 +1611,7 @@ void LyXText::backspace(LCursor & cur)
                                --cur.pos();
 
                        // the counters may have changed
-                       updateCounters();
+                       updateCounters(cur.buffer());
                        setCursor(cur, cur.pit(), cur.pos(), false);
                }
        } else {
@@ -1649,6 +1654,23 @@ void LyXText::redoParagraph(pit_type const pit)
        // remove rows of paragraph, keep track of height changes
        Paragraph & par = pars_[pit];
 
+       // Add bibitem insets if necessary
+       if (par.layout()->labeltype == LABEL_BIBLIO) {
+               bool hasbibitem(false);
+               if (!par.insetlist.empty() 
+                       // Insist on it being in pos 0
+                       && par.getChar(0) == Paragraph::META_INSET) {
+                       InsetBase * inset = par.insetlist.begin()->inset;
+                       if (inset->lyxCode() == InsetBase::BIBITEM_CODE)
+                               hasbibitem = true;
+               }
+               if (!hasbibitem) {
+                       InsetBibitem * inset(new
+                               InsetBibitem(InsetCommandParams("bibitem")));
+                       par.insertInset(0, static_cast<InsetBase *>(inset));
+               }
+       }
+
        // redo insets
        InsetList::iterator ii = par.insetlist.begin();
        InsetList::iterator iend = par.insetlist.end();
@@ -1676,9 +1698,9 @@ void LyXText::redoParagraph(pit_type const pit)
                z = row.endpos();
        } while (z < par.size());
 
-       dim.asc += par.rows()[0].ascent(); 
-       dim.des -= par.rows()[0].ascent(); 
-       par.dim_ = dim;
+       dim.asc += par.rows()[0].ascent();
+       dim.des -= par.rows()[0].ascent();
+       par.dim() = dim;
        //lyxerr << "redoParagraph: " << par.rows().size() << " rows\n";
 }
 
@@ -1690,6 +1712,8 @@ void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
                maxwidth_ = mi.base.textwidth;
        //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth
        //      << " maxWidth: " << maxwidth_ << "\nfont: " << mi.base.font << endl;
+       // save the caller's font locally:
+       font_ = mi.base.font;
 
        unsigned int h = 0;
        unsigned int w = 0;
@@ -1734,7 +1758,7 @@ void LyXText::drawSelection(PainterInfo & pi, int x , int) const
 
        beg.top() = cur.selBegin();
        end.top() = cur.selEnd();
-       
+
        // the selection doesn't touch the visible screen
        if (bv_funcs::status(pi.base.bv, beg) == bv_funcs::CUR_BELOW
            || bv_funcs::status(pi.base.bv, end) == bv_funcs::CUR_ABOVE)
@@ -1742,10 +1766,10 @@ void LyXText::drawSelection(PainterInfo & pi, int x , int) const
 
        Paragraph const & par1 = pars_[beg.pit()];
        Paragraph const & par2 = pars_[end.pit()];
-       
+
        Row const & row1 = par1.getRow(beg.pos());
        Row const & row2 = par2.getRow(end.pos());
+
        int y1,x1,x2;
        if (bv_funcs::status(pi.base.bv, beg) == bv_funcs::CUR_ABOVE) {
                y1 = 0;
@@ -1769,9 +1793,9 @@ void LyXText::drawSelection(PainterInfo & pi, int x , int) const
                X1 = isRTL(par2) ? 0 : endx;
                X2 = isRTL(par2) ? endx : 0 + dim_.wid;
        }
-               
+
        lyxerr << " y1: " << y1 << " y2: " << y2
-               << " xo: " << xo_ << " wid: " << dim_.wid 
+               << " xo: " << xo_ << " wid: " << dim_.wid
                << endl;
 
        // paint big rectangle in one go
@@ -1804,7 +1828,7 @@ void LyXText::drawSelection(PainterInfo & pi, int x, int) const
 
        beg.top() = cur.selBegin();
        end.top() = cur.selEnd();
-       
+
        // the selection doesn't touch the visible screen
        if (bv_funcs::status(pi.base.bv, beg) == bv_funcs::CUR_BELOW
            || bv_funcs::status(pi.base.bv, end) == bv_funcs::CUR_ABOVE)
@@ -1812,7 +1836,7 @@ void LyXText::drawSelection(PainterInfo & pi, int x, int) const
 
        Paragraph const & par1 = pars_[beg.pit()];
        Paragraph const & par2 = pars_[end.pit()];
-       
+
        bool const above = (bv_funcs::status(pi.base.bv, beg)
                            == bv_funcs::CUR_ABOVE);
        bool const below = (bv_funcs::status(pi.base.bv, end)
@@ -2116,7 +2140,7 @@ string LyXText::currentState(LCursor & cur)
        // The paragraph depth
        int depth = cur.paragraph().getDepth();
        if (depth > 0)
-               os << bformat(_(", Depth: %1$s"), tostr(depth));
+               os << bformat(_(", Depth: %1$d"), depth);
 
        // The paragraph spacing, but only if different from
        // buffer spacing.
@@ -2134,7 +2158,7 @@ string LyXText::currentState(LCursor & cur)
                        os << _("Double");
                        break;
                case Spacing::Other:
-                       os << _("Other (") << spacing.getValue() << ')';
+                       os << _("Other (") << spacing.getValueAsString() << ')';
                        break;
                case Spacing::Default:
                        // should never happen, do nothing
@@ -2211,6 +2235,7 @@ string LyXText::getPossibleLabel(LCursor & cur) const
 
 pos_type LyXText::x2pos(pit_type pit, int row, int x) const
 {
+       BOOST_ASSERT(row < pars_[pit].rows().size());
        bool bound = false;
        Row const & r = pars_[pit].rows()[row];
        return r.pos() + getColumnNearX(pit, r, x, bound);
@@ -2222,7 +2247,7 @@ pos_type LyXText::x2pos(pit_type pit, int row, int x) const
 //     Paragraph const & par = pars_[pit];
 //     Row const & r = par.rows()[row];
 //     int x = 0;
-//     pos -= r.pos(); 
+//     pos -= r.pos();
 //}
 
 
@@ -2232,7 +2257,7 @@ void LyXText::setCursorFromCoordinates(LCursor & cur, int const x, int const y)
 {
        pit_type pit = getPitNearY(y);
        int yy = theCoords.get(this, pit).y_ - pars_[pit].ascent();
-       lyxerr << "setCursorFromCoordinates: x: " << x << " y: " << y 
+       lyxerr << "setCursorFromCoordinates: x: " << x << " y: " << y
                << " pit: " << pit << " yy: " << yy << endl;
 
        Paragraph const & par = pars_[pit];
@@ -2246,10 +2271,10 @@ void LyXText::setCursorFromCoordinates(LCursor & cur, int const x, int const y)
        }
 
        Row const & row = par.rows()[r];
+
        lyxerr << "setCursorFromCoordinates:  row " << r
               << " from pos: " << row.pos() << endl;
-       
+
        bool bound = false;
        int xx = x;
        pos_type const pos = row.pos() + getColumnNearX(pit, row, xx, bound);