]> git.lyx.org Git - lyx.git/blobdiff - src/text.C
bug 2298: cursorTop/Bottom/Home/End does not redraw after dEPM
[lyx.git] / src / text.C
index 6536f40233481cdf4a3a4a4e7680435acf08d451..9746afc7b25d30288466e8e429ddf42dbdc2d48c 100644 (file)
@@ -315,8 +315,10 @@ void readParToken(Buffer const & buf, Paragraph & par, LyXLex & lex,
        } else if (token == "\\change_unchanged") {
                // Hack ! Needed for empty paragraphs :/
                // FIXME: is it still ??
+               /*
                if (!par.size())
                        par.cleanChanges();
+               */
                change = Change(Change::UNCHANGED);
        } else if (token == "\\change_inserted") {
                lex.eatLine();
@@ -375,6 +377,9 @@ void readParagraph(Buffer const & buf, Paragraph & par, LyXLex & lex)
                        break;
                }
        }
+       // Final change goes to paragraph break:
+       par.setChangeFull(par.size(), change);
+       
        // Initialize begin_of_body_ on load; redoParagraph maintains
        par.setBeginOfBody();
 }
@@ -474,8 +479,8 @@ int LyXText::leftMargin(pit_type const pit, pos_type const pos) const
        l_margin += font_metrics::signedWidth(tclass.leftmargin(), tclass.defaultfont());
 
        if (par.getDepth() != 0) {
-       // find the next level paragraph
-       pit_type newpar = outerHook(pit, pars_);
+               // find the next level paragraph
+               pit_type newpar = outerHook(pit, pars_);
                if (newpar != pit_type(pars_.size())) {
                        if (pars_[newpar].layout()->isEnvironment()) {
                                l_margin = leftMargin(newpar);
@@ -489,6 +494,13 @@ int LyXText::leftMargin(pit_type const pit, pos_type const pos) const
                }
        }
 
+       // This happens after sections in standard classes. The 1.3.x
+       // code compared depths too, but it does not seem necessary
+       // (JMarc)
+       if (par.layout() == tclass.defaultLayout()
+           && pit > 0 && pars_[pit - 1].layout()->nextnoindent)
+               parindent.erase();
+
        LyXFont const labelfont = getLabelFont(par);
        switch (layout->margintype) {
        case MARGIN_DYNAMIC:
@@ -1002,7 +1014,8 @@ void LyXText::setHeightOfRow(pit_type const pit, Row & row)
        if (bv_owner->text() == this) {
                if (pit == 0 && row.pos() == 0)
                        maxasc += 20;
-               if (pit + 1 == pars_.size() && row.endpos() == par.size())
+               if (pit + 1 == pit_type(pars_.size()) &&
+                   row.endpos() == par.size())
                        maxdesc += 20;
        }
 
@@ -1018,14 +1031,10 @@ namespace {
 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
+
        Paragraph & cpar = cur.paragraph();
        pit_type cpit = cur.pit();
 
-       if (cur.pos() != 0 && cur.pos() != cur.lastpos()
-           && cpar.isChangeEdited(0, cur.pos()))
-               return;
-
        LyXTextClass const & tclass = cur.buffer().params().getLyXTextClass();
        LyXLayout_ptr const & layout = cpar.layout();
 
@@ -1080,6 +1089,12 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
 
        updateCounters(cur.buffer());
 
+       // Mark "carriage return" as inserted if change tracking:
+       if (cur.buffer().params().tracking_changes) {
+               cur.paragraph().setChange(cur.paragraph().size(), 
+                       Change::INSERTED);
+       }
+
        // 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)
@@ -1146,16 +1161,6 @@ void LyXText::insertChar(LCursor & cur, char c)
        // difference are the special checks when calculating the row.fill
        // (blank does not count at the end of a row) and the check here
 
-       // The bug is triggered when we type in a description environment:
-       // The current_font is not changed when we go from label to main text
-       // and it should (along with realtmpfont) when we type the space.
-       // CHECK There is a bug here! (Asger)
-
-       // store the current font.  This is because of the use of cursor
-       // movements. The moving cursor would refresh the current font
-       LyXFont realtmpfont = real_current_font;
-       LyXFont rawtmpfont = current_font;
-
        // When the free-spacing option is set for the current layout,
        // disable the double-space checking
        if (!freeSpacing && IsLineSeparatorChar(c)) {
@@ -1182,12 +1187,8 @@ void LyXText::insertChar(LCursor & cur, char c)
                }
        }
 
-       par.insertChar(cur.pos(), c, rawtmpfont);
-
-       current_font = rawtmpfont;
-       real_current_font = realtmpfont;
-       //setCursor(cur, cur.pit(), cur.pos() + 1, false, cur.boundary());
-       setCursor(cur, cur.pit(), cur.pos() + 1, false, true);
+       par.insertChar(cur.pos(), c, current_font);
+       setCursor(cur, cur.pit(), cur.pos() + 1, false, cur.boundary());
        charInserted();
 }
 
@@ -1332,13 +1333,12 @@ bool LyXText::cursorRightOneWord(LCursor & cur)
                ++old.pit();
                old.pos() = 0;
        } else {
-               // Skip through initial nonword stuff.
-               // Treat floats and insets as words.
-               while (old.pos() != old.lastpos() && !old.paragraph().isLetter(old.pos()))
-                       ++old.pos();
                // 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();
        }
        return setCursor(cur, old.pit(), old.pos());
 }
@@ -1355,7 +1355,6 @@ bool LyXText::cursorLeftOneWord(LCursor & cur)
                old.pos() = old.lastpos();
        } else {
                // Skip through initial nonword stuff.
-               // Treat floats and insets as words.
                while (old.pos() != 0 && !old.paragraph().isLetter(old.pos() - 1))
                        --old.pos();
                // Advance through word.
@@ -1400,18 +1399,34 @@ void LyXText::acceptChange(LCursor & cur)
        if (!cur.selection() && cur.lastpos() != 0)
                return;
 
-       CursorSlice const & startc = cur.selBegin();
-       CursorSlice const & endc = cur.selEnd();
-       if (startc.pit() == endc.pit()) {
-               recordUndoSelection(cur, Undo::INSERT);
-               pars_[startc.pit()].acceptChange(startc.pos(), endc.pos());
-               finishUndo();
-               cur.clearSelection();
-               setCursorIntern(cur, startc.pit(), 0);
+       recordUndoSelection(cur, Undo::INSERT);
+       
+       DocIterator it = cur.selectionBegin();
+       DocIterator et = cur.selectionEnd();
+       pit_type pit = it.pit();
+       Change::Type const type = pars_[pit].lookupChange(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 );
+               pars_[pit].acceptChange(left, right);
        }
-#ifdef WITH_WARNINGS
-#warning handle multi par selection
-#endif
+       if (type == Change::DELETED) {
+               ParagraphList & plist = paragraphs();
+               if (it.pit() + 1 < et.pit())
+                       pars_.erase(plist.begin() + it.pit() + 1,
+                                   plist.begin() + et.pit());
+               
+               // Paragraph merge if appropriate:
+               if (pars_[it.pit()].lookupChange(pars_[it.pit()].size()) 
+                       == Change::DELETED) {
+                       setCursorIntern(cur, it.pit() + 1, 0);
+                       backspacePos0(cur);
+               }
+       }
+       finishUndo();
+       cur.clearSelection();
+       setCursorIntern(cur, it.pit(), 0);
 }
 
 
@@ -1421,18 +1436,33 @@ void LyXText::rejectChange(LCursor & cur)
        if (!cur.selection() && cur.lastpos() != 0)
                return;
 
-       CursorSlice const & startc = cur.selBegin();
-       CursorSlice const & endc = cur.selEnd();
-       if (startc.pit() == endc.pit()) {
-               recordUndoSelection(cur, Undo::INSERT);
-               pars_[startc.pit()].rejectChange(startc.pos(), endc.pos());
-               finishUndo();
-               cur.clearSelection();
-               setCursorIntern(cur, startc.pit(), 0);
+       recordUndoSelection(cur, Undo::INSERT);
+
+       DocIterator it = cur.selectionBegin();
+       DocIterator et = cur.selectionEnd();
+       pit_type pit = it.pit();
+       Change::Type const type = pars_[pit].lookupChange(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 );
+               pars_[pit].rejectChange(left, right);
        }
-#ifdef WITH_WARNINGS
-#warning handle multi par selection
-#endif
+       if (type == Change::INSERTED) {
+               ParagraphList & plist = paragraphs();
+               if (it.pit() + 1 < et.pit())
+                       pars_.erase(plist.begin() + it.pit() + 1,
+                                   plist.begin() + et.pit());
+               // Paragraph merge if appropriate:
+               if (pars_[it.pit()].lookupChange(pars_[it.pit()].size()) 
+                       == Change::INSERTED) {
+                       setCursorIntern(cur, it.pit() + 1, 0);
+                       backspacePos0(cur);
+               }
+       }
+       finishUndo();
+       cur.clearSelection();
+       setCursorIntern(cur, it.pit(), 0);
 }
 
 
@@ -1501,7 +1531,7 @@ void LyXText::changeCase(LCursor & cur, LyXText::TextCase action)
        } else {
                from = cur.top();
                getWord(from, to, lyx::PARTIAL_WORD);
-               setCursor(cur, to.pit(), to.pos() + 1);
+               cursorRightOneWord(cur);
        }
 
        recordUndoSelection(cur);
@@ -1540,106 +1570,127 @@ void LyXText::changeCase(LCursor & cur, LyXText::TextCase action)
 }
 
 
-void LyXText::Delete(LCursor & cur)
+bool LyXText::Delete(LCursor & cur)
 {
        BOOST_ASSERT(this == cur.text());
+       bool needsUpdate = false;
 
        if (cur.pos() != cur.lastpos()) {
                recordUndo(cur, Undo::DELETE, cur.pit());
                setCursorIntern(cur, cur.pit(), cur.pos() + 1, false, cur.boundary());
-               backspace(cur);
+               needsUpdate = backspace(cur);
        } 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());
-                       backspace(cur);
+                       needsUpdate = backspace(cur);
                } else {
                        setCursorIntern(scur, scur.pit(), scur.pos(), false, scur.boundary());
                }
        }
+       return needsUpdate;
 }
 
 
-void LyXText::backspace(LCursor & cur)
+bool LyXText::backspacePos0(LCursor & cur)
 {
        BOOST_ASSERT(this == cur.text());
-       if (cur.pos() == 0) {
-               // The cursor is at the beginning of a paragraph, so
-               // the the backspace will collapse two paragraphs into
-               // one.
+       bool needsUpdate = false;
 
-               // but it's not allowed unless it's new
-               Paragraph & par = cur.paragraph();
-               if (par.isChangeEdited(0, par.size()))
-                       return;
+       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.
 
-               // we may paste some paragraphs
-
-               // 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());
-                                }
-                                
-                               cursorLeft(cur);
-                               return;
+               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());
                        }
+                                
+                       cursorLeft(cur);
+                       return true;
                }
+       }
 
-               if (cur.pit() != 0)
-                       recordUndo(cur, Undo::DELETE, cur.pit() - 1);
+       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);
+       }
 
-               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);
-               }
+       // 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();
 
-               // 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);
+       if (cpit != tmppit
+           && (pars_[cpit].layout() == pars_[tmppit].layout()
+               || pars_[tmppit].layout() == tclass.defaultLayout()))
+       {
+               mergeParagraph(bufparams, pars_, cpit);
+               needsUpdate = true;
 
-                       if (cur.pos() != 0 && pars_[cpit].isSeparator(cur.pos() - 1))
+               if (cur.pos() != 0 && pars_[cpit].isSeparator(cur.pos() - 1))
                                --cur.pos();
 
-                       // the counters may have changed
-                       updateCounters(cur.buffer());
-                       setCursor(cur, cur.pit(), cur.pos(), false);
+               // the counters may have changed
+               updateCounters(cur.buffer());
+               setCursor(cur, cur.pit(), cur.pos(), false);
+       }
+       return needsUpdate;
+}
+
+
+bool LyXText::backspace(LCursor & cur)
+{
+       BOOST_ASSERT(this == cur.text());
+       bool needsUpdate = false;
+       if (cur.pos() == 0) {
+               // The cursor is at the beginning of a paragraph, so
+               // the the backspace will collapse two paragraphs into
+               // one.
+
+               if (cur.buffer().params().tracking_changes) {
+                       // Previous paragraph, mark "carriage return" as
+                       // deleted:
+                       Paragraph & par = pars_[cur.pit() - 1];
+                       // Take care of a just inserted para break:
+                       if (par.lookupChange(par.size()) != Change::INSERTED) {
+                               par.setChange(par.size(), Change::DELETED);
+                               setCursorIntern(cur, cur.pit() - 1, par.size());
+                               return false;
+                       }
                }
+
+               needsUpdate = backspacePos0(cur);
+
        } else {
                // this is the code for a normal backspace, not pasting
                // any paragraphs
@@ -1657,6 +1708,8 @@ void LyXText::backspace(LCursor & cur)
                setCurrentFont(cur);
 
        setCursor(cur, cur.pit(), cur.pos(), false, cur.boundary());
+
+       return needsUpdate;
 }
 
 
@@ -1690,12 +1743,18 @@ bool LyXText::redoParagraph(pit_type const pit)
        }
 
        // redo insets
+       // FIXME: We should always use getFont(), see documentation of
+       // noFontChange() in insetbase.h.
+       LyXFont const bufferfont = bv()->buffer()->params().getFont();
        InsetList::iterator ii = par.insetlist.begin();
        InsetList::iterator iend = par.insetlist.end();
        for (; ii != iend; ++ii) {
                Dimension dim;
                int const w = maxwidth_ - leftMargin(pit, ii->pos) - rightMargin(par);
-               MetricsInfo mi(bv(), getFont(par, ii->pos), w);
+               LyXFont const & font = ii->inset->noFontChange() ?
+                       bufferfont :
+                       getFont(par, ii->pos);
+               MetricsInfo mi(bv(), font, w);
                ii->inset->metrics(mi, dim);
        }
 
@@ -1716,6 +1775,20 @@ bool LyXText::redoParagraph(pit_type const pit)
                z = row.endpos();
        } while (z < par.size());
 
+       // Make sure that if a par ends in newline, there is one more row
+       // under it
+       // FIXME this is a dirty trick. Now the _same_ position in the
+       // paragraph occurs in _two_ different rows, and has two different
+       // display positions, leading to weird behaviour when moving up/down.
+       if (z > 0 && par.isNewline(z - 1)) {
+               Row row(z - 1);
+               row.endpos(z - 1);
+               setRowWidth(pit, row);
+               setHeightOfRow(pit, row);
+               par.rows().push_back(row);
+               dim.des += row.height();
+       }
+           
        dim.asc += par.rows()[0].ascent();
        dim.des -= par.rows()[0].ascent();
 
@@ -1775,12 +1848,8 @@ void LyXText::drawSelection(PainterInfo & pi, int x , int) const
 
        lyxerr << "draw selection at " << x << endl;
 
-       // is there a better way of getting these two iterators?
-       DocIterator beg = cur;
-       DocIterator end = cur;
-
-       beg.top() = cur.selBegin();
-       end.top() = cur.selEnd();
+       DocIterator beg = cur.selectionBegin();
+       DocIterator end = cur.selectionEnd();
 
        // the selection doesn't touch the visible screen
        if (bv_funcs::status(pi.base.bv, beg) == bv_funcs::CUR_BELOW
@@ -1848,12 +1917,8 @@ void LyXText::drawSelection(PainterInfo & pi, int x, int) const
                 << "draw selection at " << x
                 << endl;
 
-       // is there a better way of getting these two iterators?
-       DocIterator beg = cur;
-       DocIterator end = cur;
-
-       beg.top() = cur.selBegin();
-       end.top() = cur.selEnd();
+       DocIterator beg = cur.selectionBegin();
+       DocIterator end = cur.selectionEnd();
 
        // the selection doesn't touch the visible screen
        if (bv_funcs::status(pi.base.bv, beg) == bv_funcs::CUR_BELOW
@@ -2172,9 +2237,11 @@ string LyXText::currentState(LCursor & cur)
        std::ostringstream os;
 
        bool const show_change = buf.params().tracking_changes
-               && cur.pos() != cur.lastpos()
                && par.lookupChange(cur.pos()) != Change::UNCHANGED;
 
+       if (buf.params().tracking_changes)
+               os << "[C] ";
+
        if (show_change) {
                Change change = par.lookupChangeFull(cur.pos());
                Author const & a = buf.params().authors().get(change.author);
@@ -2188,8 +2255,9 @@ string LyXText::currentState(LCursor & cur)
 
        // I think we should only show changes from the default
        // font. (Asger)
+       // No, from the document font (MV)
        LyXFont font = real_current_font;
-       font.reduce(buf.params().getLyXTextClass().defaultfont());
+       font.reduce(buf.params().getFont());
 
        // avoid _(...) re-entrance problem
        string const s = font.stateText(&buf.params());
@@ -2253,8 +2321,25 @@ string LyXText::getPossibleLabel(LCursor & cur) const
                }
        }
 
-       string text = layout->latexname().substr(0, 3);
-       if (layout->latexname() == "theorem")
+       string name = layout->latexname();
+
+       // for captions, we want the abbreviation of the float type
+       if (layout->labeltype == LABEL_SENSITIVE) {
+               // Search for the first float or wrap inset in the iterator
+               size_t i = cur.depth();
+               while (i > 0) {
+                       --i;
+                       InsetBase * const in = &cur[i].inset();
+                       if (in->lyxCode() == InsetBase::FLOAT_CODE
+                           || in->lyxCode() == InsetBase::WRAP_CODE) {
+                               name = in->getInsetName();
+                               break;
+                       }
+               }
+       }
+
+       string text = name.substr(0, 3);
+       if (name == "theorem")
                text = "thm"; // Create a correct prefix for prettyref
 
        text += ':';
@@ -2314,7 +2399,7 @@ pos_type LyXText::x2pos(pit_type pit, int row, int x) const
 
 // x,y are screen coordinates
 // sets cursor only within this LyXText
-void LyXText::setCursorFromCoordinates(LCursor & cur, int const x, int const y)
+bool 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();
@@ -2353,5 +2438,5 @@ void LyXText::setCursorFromCoordinates(LCursor & cur, int const x, int const y)
                 << " pos: " << pos
                 << endl;
         
-       setCursor(cur, pit, pos, true, bound);
+       return setCursor(cur, pit, pos, true, bound);
 }