]> git.lyx.org Git - lyx.git/blobdiff - src/Text2.cpp
Fix text direction issue for InsetInfo in RTL context
[lyx.git] / src / Text2.cpp
index 4f6a975957e1e54a809a3269bfae49b76b008419..17f32ad7dbe8e1273fbd30f14e7832e00d4a618c 100644 (file)
@@ -22,7 +22,6 @@
 
 #include "Text.h"
 
-#include "Bidi.h"
 #include "Buffer.h"
 #include "buffer_funcs.h"
 #include "BufferList.h"
 #include "TextClass.h"
 #include "TextMetrics.h"
 
-#include "insets/InsetCollapsable.h"
+#include "insets/InsetCollapsible.h"
 
 #include "mathed/InsetMathHull.h"
 
 #include "support/lassert.h"
 #include "support/debug.h"
 #include "support/gettext.h"
+#include "support/lyxalgo.h"
 #include "support/textutils.h"
 
-#include <boost/next_prior.hpp>
-
 #include <sstream>
 
 using namespace std;
@@ -200,6 +198,7 @@ void Text::setLayout(Cursor & cur, docstring const & layout)
        pit_type end = cur.selEnd().pit() + 1;
        cur.recordUndoSelection();
        setLayout(start, end, layout);
+       cur.setCurrentFont();
        cur.forceBufferUpdate();
 }
 
@@ -264,6 +263,11 @@ void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type)
 void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
 {
        LASSERT(this == cur.text(), return);
+
+       // If there is a selection, record undo before the cursor font is changed.
+       if (cur.selection())
+               cur.recordUndoSelection();
+
        // Set the current_font
        // Determine basis font
        FontInfo layoutfont;
@@ -289,10 +293,9 @@ void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
                return;
 
        // Ok, we have a selection.
-       cur.recordUndoSelection();
        Font newfont = font;
 
-       if (toggleall) {        
+       if (toggleall) {
                // Toggling behaves as follows: We check the first character of the
                // selection. If it's (say) got EMPH on, then we set to off; if off,
                // then to on. With families and the like, we set it to INHERIT, if
@@ -300,30 +303,30 @@ void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
                CursorSlice const & sl = cur.selBegin();
                Text const & text = *sl.text();
                Paragraph const & par = text.getPar(sl.pit());
-       
+
                // get font at the position
                Font oldfont = par.getFont(cur.bv().buffer().params(), sl.pos(),
                        text.outerFont(sl.pit()));
                FontInfo const & oldfi = oldfont.fontInfo();
-       
+
                FontInfo & newfi = newfont.fontInfo();
-       
+
                FontFamily newfam = newfi.family();
                if (newfam !=   INHERIT_FAMILY && newfam != IGNORE_FAMILY &&
                                newfam == oldfi.family())
                        newfi.setFamily(INHERIT_FAMILY);
-               
+
                FontSeries newser = newfi.series();
                if (newser == BOLD_SERIES && oldfi.series() == BOLD_SERIES)
                        newfi.setSeries(INHERIT_SERIES);
-       
+
                FontShape newshp = newfi.shape();
                if (newshp != INHERIT_SHAPE && newshp != IGNORE_SHAPE &&
                                newshp == oldfi.shape())
                        newfi.setShape(INHERIT_SHAPE);
 
                ColorCode newcol = newfi.color();
-               if (newcol != Color_none && newcol != Color_inherit 
+               if (newcol != Color_none && newcol != Color_inherit
                    && newcol != Color_ignore && newcol == oldfi.color())
                        newfi.setColor(Color_none);
 
@@ -334,6 +337,8 @@ void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
                        newfi.setUnderbar(oldfi.underbar() == FONT_OFF ? FONT_ON : FONT_OFF);
                if (newfi.strikeout() == FONT_TOGGLE)
                        newfi.setStrikeout(oldfi.strikeout() == FONT_OFF ? FONT_ON : FONT_OFF);
+               if (newfi.xout() == FONT_TOGGLE)
+                       newfi.setXout(oldfi.xout() == FONT_OFF ? FONT_ON : FONT_OFF);
                if (newfi.uuline() == FONT_TOGGLE)
                        newfi.setUuline(oldfi.uuline() == FONT_OFF ? FONT_ON : FONT_OFF);
                if (newfi.uwave() == FONT_TOGGLE)
@@ -342,9 +347,11 @@ void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
                        newfi.setNoun(oldfi.noun() == FONT_OFF ? FONT_ON : FONT_OFF);
                if (newfi.number() == FONT_TOGGLE)
                        newfi.setNumber(oldfi.number() == FONT_OFF ? FONT_ON : FONT_OFF);
+               if (newfi.nospellcheck() == FONT_TOGGLE)
+                       newfi.setNoSpellcheck(oldfi.nospellcheck() == FONT_OFF ? FONT_ON : FONT_OFF);
        }
 
-       setFont(cur.bv(), cur.selectionBegin().top(), 
+       setFont(cur.bv(), cur.selectionBegin().top(),
                cur.selectionEnd().top(), newfont);
 }
 
@@ -374,7 +381,7 @@ void Text::setFont(BufferView const & bv, CursorSlice const & begin,
                Font f = tm.displayFont(pit, pos);
                f.update(font, language);
                setCharFont(pit, pos, f, tm.font_);
-               // font change may change language... 
+               // font change may change language...
                // spell checker has to know that
                pars_[pit].requestSpellCheck(pos);
        }
@@ -391,7 +398,7 @@ bool Text::cursorTop(Cursor & cur)
 bool Text::cursorBottom(Cursor & cur)
 {
        LBUFERR(this == cur.text());
-       return setCursor(cur, cur.lastpit(), boost::prior(paragraphs().end())->size());
+       return setCursor(cur, cur.lastpit(), prev(paragraphs().end(), 1)->size());
 }
 
 
@@ -427,7 +434,7 @@ void Text::toggleFree(Cursor & cur, Font const & font, bool toggleall)
 }
 
 
-docstring Text::getStringToIndex(Cursor const & cur)
+docstring Text::getStringForDialog(Cursor & cur)
 {
        LBUFERR(this == cur.text());
 
@@ -437,17 +444,10 @@ docstring Text::getStringToIndex(Cursor const & cur)
        // Try implicit word selection. If there is a change
        // in the language the implicit word selection is
        // disabled.
-       Cursor tmpcur = cur;
-       selectWord(tmpcur, PREVIOUS_WORD);
-
-       if (!tmpcur.selection())
-               cur.message(_("Nothing to index!"));
-       else if (tmpcur.selBegin().pit() != tmpcur.selEnd().pit())
-               cur.message(_("Cannot index more than one paragraph!"));
-       else
-               return tmpcur.selectionAsString(false);
-       
-       return docstring();
+       selectWordWhenUnderCursor(cur, WHOLE_WORD);
+       docstring const & retval = cur.selectionAsString(false);
+       cur.clearSelection();
+       return retval;
 }
 
 
@@ -498,7 +498,7 @@ void Text::setParagraphs(Cursor & cur, docstring arg, bool merge)
                // with same layout in a sequence.
                // Do this only once for a selected range of paragraphs
                // of the same layout and depth.
-               cur.recordUndo();
+               c.recordUndo();
                par.params().apply(params, par.layout());
                if (par.getDepth() != priordepth || par.layout() != priorlayout)
                        setLabelWidthStringToSequence(c, params.labelWidthString());
@@ -544,47 +544,23 @@ void Text::insertInset(Cursor & cur, Inset * inset)
 }
 
 
-bool Text::setCursor(Cursor & cur, pit_type par, pos_type pos,
+bool Text::setCursor(Cursor & cur, pit_type pit, pos_type pos,
                        bool setfont, bool boundary)
 {
        TextMetrics const & tm = cur.bv().textMetrics(this);
-       bool const update_needed = !tm.contains(par);
+       bool const update_needed = !tm.contains(pit);
        Cursor old = cur;
-       setCursorIntern(cur, par, pos, setfont, boundary);
+       setCursorIntern(cur, pit, pos, setfont, boundary);
        return cur.bv().checkDepm(cur, old) || update_needed;
 }
 
 
-void Text::setCursor(CursorSlice & cur, pit_type par, pos_type pos)
-{
-       LASSERT(par != int(paragraphs().size()), return);
-       cur.pit() = par;
-       cur.pos() = pos;
-
-       // now some strict checking
-       Paragraph & para = getPar(par);
-
-       // None of these should happen, but we're scaredy-cats
-       if (pos < 0) {
-               LYXERR0("Don't like -1!");
-               LATTEST(false);
-       }
-
-       if (pos > para.size()) {
-               LYXERR0("Don't like 1, pos: " << pos
-                      << " size: " << para.size()
-                      << " par: " << par);
-               LATTEST(false);
-       }
-}
-
-
-void Text::setCursorIntern(Cursor & cur,
-                             pit_type par, pos_type pos, bool setfont, bool boundary)
+void Text::setCursorIntern(Cursor & cur, pit_type pit, pos_type pos,
+                           bool setfont, bool boundary)
 {
        LBUFERR(this == cur.text());
        cur.boundary(boundary);
-       setCursor(cur.top(), par, pos);
+       cur.top().setPitPos(pit, pos);
        if (setfont)
                cur.setCurrentFont();
 }
@@ -611,6 +587,8 @@ bool Text::checkAndActivateInset(Cursor & cur, bool front)
        if (!front)
                --cur.pos();
        inset->edit(cur, front);
+       cur.setCurrentFont();
+       cur.boundary(false);
        return true;
 }
 
@@ -627,8 +605,10 @@ bool Text::checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool mo
                return false;
        if (cur.selection() && cur.realAnchor().find(inset) == -1)
                return false;
-       inset->edit(cur, movingForward, 
+       inset->edit(cur, movingForward,
                movingLeft ? Inset::ENTRY_DIRECTION_RIGHT : Inset::ENTRY_DIRECTION_LEFT);
+       cur.setCurrentFont();
+       cur.boundary(false);
        return true;
 }
 
@@ -660,11 +640,11 @@ bool Text::cursorBackward(Cursor & cur)
                                !cur.paragraph().isSeparator(cur.pos() - 1)) {
                        return setCursor(cur, cur.pit(), cur.pos(), true, true);
                }
-               
+
                // go left and try to enter inset
                if (checkAndActivateInset(cur, false))
                        return false;
-               
+
                // normal character left
                return setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
        }
@@ -690,7 +670,7 @@ bool Text::cursorVisLeft(Cursor & cur, bool skip_inset)
                cur = temp_cur;
                return false;
        }
-       return setCursor(cur, temp_cur.pit(), temp_cur.pos(), 
+       return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
                true, temp_cur.boundary());
 }
 
@@ -725,7 +705,7 @@ bool Text::cursorForward(Cursor & cur)
                if (cur.boundary() && !tm.isRTLBoundary(cur.pit(), cur.pos()))
                        return setCursor(cur, cur.pit(), cur.pos(), true, false);
 
-               // next position is left of boundary, 
+               // next position is left of boundary,
                // but go to next line for special cases like space, newline, linesep
 #if 0
                // some effectless debug code to see the values in the debugger
@@ -755,12 +735,12 @@ bool Text::cursorForward(Cursor & cur)
                                return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
                        }
                }
-               
+
                // in front of RTL boundary? Stay on this side of the boundary because:
                //   ab|cDDEEFFghi -> abc|DDEEFFghi
                if (tm.isRTLBoundary(cur.pit(), cur.pos() + 1))
                        return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
-               
+
                // move right
                return setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
        }
@@ -800,9 +780,11 @@ bool Text::cursorDownParagraph(Cursor & cur)
 }
 
 
-// fix the cursor `cur' after a characters has been deleted at `where'
+namespace {
+// fix the cursor `cur' after characters has been deleted at `where'
 // position. Called by deleteEmptyParagraphMechanism
-void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
+void fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where,
+                                                 pos_type from, pos_type to)
 {
        // Do nothing if cursor is not in the paragraph where the
        // deletion occurred,
@@ -810,8 +792,8 @@ void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
                return;
 
        // If cursor position is after the deletion place update it
-       if (cur.pos() > where.pos())
-               --cur.pos();
+       if (cur.pos() > from)
+               cur.pos() = max(from, cur.pos() - (to - from));
 
        // Check also if we don't want to set the cursor on a spot behind the
        // pagragraph because we erased the last character.
@@ -819,6 +801,8 @@ void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
                cur.pos() = cur.lastpos();
 }
 
+}
+
 
 bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
                Cursor & old, bool & need_anchor_change)
@@ -854,29 +838,41 @@ bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
                if (&old.inset() == &cur[depth].inset())
                        break;
 
-       // Whether a common inset is found and whether the cursor is still in 
+       // Whether a common inset is found and whether the cursor is still in
        // the same paragraph (possibly nested).
        bool const same_par = depth < cur.depth() && old.pit() == cur[depth].pit();
-       bool const same_par_pos = depth == cur.depth() - 1 && same_par 
+       bool const same_par_pos = depth == cur.depth() - 1 && same_par
                && old.pos() == cur[depth].pos();
-       
-       // If the chars around the old cursor were spaces, delete one of them.
+
+       // If the chars around the old cursor were spaces, delete some of
+       // them , but only if the cursor has really moved.
        if (!same_par_pos) {
-               // Only if the cursor has really moved.
-               if (old.pos() > 0
-                   && old.pos() < oldpar.size()
-                   && oldpar.isLineSeparator(old.pos())
-                   && oldpar.isLineSeparator(old.pos() - 1)
-                   && !oldpar.isDeleted(old.pos() - 1)
-                   && !oldpar.isDeleted(old.pos())) {
-                       oldpar.eraseChar(old.pos() - 1, cur.buffer()->params().track_changes);
+               // find range of spaces around cursors
+               int from = old.pos();
+               while (from > 0
+                      && oldpar.isLineSeparator(from - 1)
+                      && !oldpar.isDeleted(from - 1))
+                       --from;
+               int to = old.pos();
+               while (to < oldpar.size() - 1
+                      && oldpar.isLineSeparator(to)
+                      && !oldpar.isDeleted(to))
+                       ++to;
+
+               // If we are not at the extremity of the paragraph, keep one space
+               if (from != to && from > 0 && to < oldpar.size())
+                       ++from;
+
+               // Remove spaces and adapt cursor.
+               if (from < to) {
+                       oldpar.eraseChars(from, to, cur.buffer()->params().track_changes);
 // FIXME: This will not work anymore when we have multiple views of the same buffer
 // In this case, we will have to correct also the cursors held by
 // other bufferviews. It will probably be easier to do that in a more
 // automated way in CursorSlice code. (JMarc 26/09/2001)
                        // correct all cursor parts
                        if (same_par) {
-                               fixCursorAfterDelete(cur[depth], old.top());
+                               fixCursorAfterDelete(cur[depth], old.top(), from, to);
                                need_anchor_change = true;
                        }
                        return true;
@@ -897,17 +893,16 @@ bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
 
        if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
                // Delete old par.
-               old.recordUndo(ATOMIC_UNDO,
-                          max(old.pit() - 1, pit_type(0)),
-                          min(old.pit() + 1, old.lastpit()));
+               old.recordUndo(max(old.pit() - 1, pit_type(0)),
+                              min(old.pit() + 1, old.lastpit()));
                ParagraphList & plist = old.text()->paragraphs();
                bool const soa = oldpar.params().startOfAppendix();
-               plist.erase(boost::next(plist.begin(), old.pit()));
+               plist.erase(lyx::next(plist.begin(), old.pit()));
                // do not lose start of appendix marker (bug 4212)
                if (soa && old.pit() < pit_type(plist.size()))
                        plist[old.pit()].params().startOfAppendix(true);
 
-               // see #warning (FIXME?) above 
+               // see #warning (FIXME?) above
                if (cur.depth() >= old.depth()) {
                        CursorSlice & curslice = cur[old.depth() - 1];
                        if (&curslice.inset() == &old.inset()
@@ -946,13 +941,26 @@ void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool tra
                if (par.isFreeSpacing())
                        continue;
 
-               for (pos_type pos = 1; pos < par.size(); ++pos) {
-                       if (par.isLineSeparator(pos) && par.isLineSeparator(pos - 1)
-                           && !par.isDeleted(pos - 1)) {
-                               if (par.eraseChar(pos - 1, trackChanges)) {
-                                       --pos;
-                               }
-                       }
+               pos_type from = 0;
+               while (from < par.size()) {
+                       // skip non-spaces
+                       while (from < par.size()
+                              && (!par.isLineSeparator(from) || par.isDeleted(from)))
+                               ++from;
+                       // find string of spaces
+                       pos_type to = from;
+                       while (to < par.size()
+                              && par.isLineSeparator(to) && !par.isDeleted(to))
+                               ++to;
+                       // empty? We are done
+                       if (from == to)
+                               break;
+                       // if inside the line, keep one space
+                       if (from > 0 && to < par.size())
+                               ++from;
+                       // remove the extra spaces
+                       if (from < to)
+                               par.eraseChars(from, to, trackChanges);
                }
 
                // don't delete anything if this is the only remaining paragraph
@@ -966,7 +974,7 @@ void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool tra
                        continue;
 
                if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) {
-                       pars_.erase(boost::next(pars_.begin(), pit));
+                       pars_.erase(lyx::next(pars_.begin(), pit));
                        --pit;
                        --last;
                        continue;
@@ -977,15 +985,4 @@ void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool tra
 }
 
 
-void Text::recUndo(Cursor & cur, pit_type first, pit_type last) const
-{
-       cur.recordUndo(ATOMIC_UNDO, first, last);
-}
-
-
-void Text::recUndo(Cursor & cur, pit_type par) const
-{
-       cur.recordUndo(ATOMIC_UNDO, par, par);
-}
-
 } // namespace lyx