]> git.lyx.org Git - lyx.git/blobdiff - src/BufferView.cpp
inputenc only expects one option
[lyx.git] / src / BufferView.cpp
index 308cf7f5ba14cf6988dae206b55ff86dc4bd2bc7..0bbf6497964536773073c44e2490c486d3f25476 100644 (file)
@@ -38,6 +38,7 @@
 #include "MetricsInfo.h"
 #include "Paragraph.h"
 #include "Session.h"
+#include "texstream.h"
 #include "Text.h"
 #include "TextMetrics.h"
 #include "TexRow.h"
@@ -47,6 +48,7 @@
 #include "insets/InsetCitation.h"
 #include "insets/InsetCommand.h" // ChangeRefs
 #include "insets/InsetGraphics.h"
+#include "insets/InsetIndex.h"
 #include "insets/InsetRef.h"
 #include "insets/InsetText.h"
 
@@ -171,18 +173,18 @@ bool findInset(DocIterator & dit, vector<InsetCode> const & codes,
 
 
 /// Moves cursor to the next inset with one of the given codes.
-void gotoInset(BufferView * bv, vector<InsetCode> const & codes,
+bool gotoInset(BufferView * bv, vector<InsetCode> const & codes,
               bool same_content)
 {
        Cursor tmpcur = bv->cursor();
        if (!findInset(tmpcur, codes, same_content)) {
                bv->cursor().message(_("No more insets"));
-               return;
+               return false;
        }
 
        tmpcur.clearSelection();
        bv->setCursor(tmpcur);
-       bv->showCursor();
+       return bv->scrollToCursor(bv->cursor(), false, true);
 }
 
 
@@ -228,7 +230,7 @@ struct BufferView::Private
        ///
        CoordCache coord_cache_;
        ///
-       typedef map<MathData const *, MathRow> MathRows;
+       typedef unordered_map<MathData const *, MathRow> MathRows;
        MathRows math_rows_;
 
        /// this is used to handle XSelection events in the right manner.
@@ -292,6 +294,8 @@ struct BufferView::Private
        bool clickable_inset_;
        /// shape of the caret
        frontend::CaretGeometry caret_geometry_;
+       ///
+       bool mouse_selecting_ = false;
 };
 
 
@@ -331,16 +335,23 @@ BufferView::~BufferView()
 }
 
 
-int BufferView::rightMargin() const
+int BufferView::defaultMargin() const
 {
        // The value used to be hardcoded to 10
-       int const default_margin = zoomedPixels(10);
-       // The additional test for the case the outliner is opened.
-       if (!full_screen_ || !lyxrc.full_screen_limit
-           || width_ < lyxrc.full_screen_width + 2 * default_margin)
-               return default_margin;
+       return zoomedPixels(20);
+}
+
+
+int BufferView::rightMargin() const
+{
+       const int screen_width = inPixels(lyxrc.screen_width);
 
-       return (width_ - lyxrc.full_screen_width) / 2;
+       // The additional test for the case the outliner is opened.
+       if (!lyxrc.screen_limit || width_ < screen_width + 2 * defaultMargin()) {
+               return defaultMargin();
+       } else {
+               return (width_ - screen_width) / 2;
+       }
 }
 
 
@@ -537,13 +548,13 @@ void BufferView::processUpdateFlags(Update::flags flags)
                if (needsFitCursor()) {
                        // First try to make the selection start visible
                        // (which is just the cursor when there is no selection)
-                       scrollToCursor(d->cursor_.selectionBegin(), false);
+                       scrollToCursor(d->cursor_.selectionBegin(), false, false);
                        // Metrics have to be recomputed (maybe again)
                        updateMetrics();
                        // Is the cursor visible? (only useful if cursor is at end of selection)
                        if (needsFitCursor()) {
                                // then try to make cursor visible instead
-                               scrollToCursor(d->cursor_, false);
+                               scrollToCursor(d->cursor_, false, false);
                                // Metrics have to be recomputed (maybe again)
                                updateMetrics(flags);
                        }
@@ -579,7 +590,7 @@ void BufferView::processUpdateFlags(Update::flags flags)
 }
 
 
-void BufferView::updateScrollbar()
+void BufferView::updateScrollbarParameters()
 {
        if (height_ == 0 && width_ == 0)
                return;
@@ -737,7 +748,7 @@ void BufferView::scrollDocView(int const pixels, bool update)
        // cut off at the top
        if (pixels <= d->scrollbarParameters_.min) {
                DocIterator dit = doc_iterator_begin(&buffer_);
-               showCursor(dit, false, update);
+               showCursor(dit, false, false, update);
                LYXERR(Debug::SCROLLING, "scroll to top");
                return;
        }
@@ -746,7 +757,7 @@ void BufferView::scrollDocView(int const pixels, bool update)
        if (pixels >= d->scrollbarParameters_.max) {
                DocIterator dit = doc_iterator_end(&buffer_);
                dit.backwardPos();
-               showCursor(dit, false, update);
+               showCursor(dit, false, false, update);
                LYXERR(Debug::SCROLLING, "scroll to bottom");
                return;
        }
@@ -771,7 +782,7 @@ void BufferView::scrollDocView(int const pixels, bool update)
        DocIterator dit = doc_iterator_begin(&buffer_);
        dit.pit() = i;
        LYXERR(Debug::SCROLLING, "pixels = " << pixels << " -> scroll to pit " << i);
-       showCursor(dit, false, update);
+       showCursor(dit, false, false, update);
 }
 
 
@@ -957,32 +968,25 @@ int BufferView::workWidth() const
 
 void BufferView::recenter()
 {
-       showCursor(d->cursor_, true, true);
+       showCursor(d->cursor_, true, false, true);
 }
 
 
 void BufferView::showCursor()
 {
-       showCursor(d->cursor_, false, true);
+       showCursor(d->cursor_, false, false, true);
 }
 
 
 void BufferView::showCursor(DocIterator const & dit,
-       bool recenter, bool update)
+       bool recenter, bool force, bool update)
 {
-       if (scrollToCursor(dit, recenter) && update)
+       if (scrollToCursor(dit, recenter, force) && update)
                processUpdateFlags(Update::Force);
 }
 
 
-void BufferView::scrollToCursor()
-{
-       if (scrollToCursor(d->cursor_, false))
-               processUpdateFlags(Update::Force);
-}
-
-
-bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter)
+bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter, bool force)
 {
        // We are not properly started yet, delay until resizing is done.
        if (height_ == 0)
@@ -1010,7 +1014,7 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter)
        else if (bot_pit == tm.last().first + 1)
                tm.newParMetricsDown();
 
-       if (tm.contains(bot_pit)) {
+       if (tm.contains(bot_pit) && !force) {
                ParagraphMetrics const & pm = tm.parMetrics(bot_pit);
                LBUFERR(!pm.rows().empty());
                // FIXME: smooth scrolling doesn't work in mathed.
@@ -1074,9 +1078,9 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter)
        else if (d->anchor_pit_ == max_pit)
                d->anchor_ypos_ = height_ - offset - row_dim.descent();
        else if (offset > height_)
-               d->anchor_ypos_ = height_ - offset - defaultRowHeight();
+               d->anchor_ypos_ = height_ - offset - row_dim.descent();
        else
-               d->anchor_ypos_ = defaultRowHeight() * 2;
+               d->anchor_ypos_ = row_dim.ascent();
 
        return true;
 }
@@ -1092,8 +1096,6 @@ void BufferView::makeDocumentClass()
 
 void BufferView::updateDocumentClass(DocumentClassConstPtr olddc)
 {
-       message(_("Converting document to new document class..."));
-
        StableDocIterator backcur(d->cursor_);
        ErrorList & el = buffer_.errorList("Class Switch");
        cap::switchBetweenClasses(
@@ -1484,6 +1486,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                else {
                        dr.screenUpdate(Update::Force | Update::FitCursor);
                        dr.forceBufferUpdate();
+                       resetInlineCompletionPos();
                        if (buffer().params().citeEngine() != engine ||
                            buffer().params().citeEngineType() != enginetype)
                                buffer().invalidateCiteLabels();
@@ -1504,6 +1507,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                else {
                        dr.screenUpdate(Update::Force | Update::FitCursor);
                        dr.forceBufferUpdate();
+                       resetInlineCompletionPos();
                        if (buffer().params().citeEngine() != engine ||
                            buffer().params().citeEngineType() != enginetype)
                                buffer().invalidateCiteLabels();
@@ -1582,8 +1586,8 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                        success = setCursorFromEntries({id, pos},
                                                                       {id_end, pos_end});
                                }
-                               if (success)
-                                       dr.screenUpdate(Update::Force | Update::FitCursor);
+                               if (success && scrollToCursor(d->cursor_, false, true))
+                                               dr.screenUpdate(Update::Force);
                        } else {
                                // Switch to other buffer view and resend cmd
                                lyx::dispatch(FuncRequest(
@@ -1596,19 +1600,13 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
        }
 
        case LFUN_NOTE_NEXT:
-               gotoInset(this, { NOTE_CODE }, false);
-               // FIXME: if SinglePar is changed to act on the inner
-               // paragraph, this will not be OK anymore. The update is
-               // useful for auto-open collapsible insets.
-               dr.screenUpdate(Update::SinglePar | Update::FitCursor);
+               if (gotoInset(this, { NOTE_CODE }, false))
+                       dr.screenUpdate(Update::Force);
                break;
 
        case LFUN_REFERENCE_NEXT: {
-               gotoInset(this, { LABEL_CODE, REF_CODE }, true);
-               // FIXME: if SinglePar is changed to act on the inner
-               // paragraph, this will not be OK anymore. The update is
-               // useful for auto-open collapsible insets.
-               dr.screenUpdate(Update::SinglePar | Update::FitCursor);
+               if (gotoInset(this, { LABEL_CODE, REF_CODE }, true))
+                       dr.screenUpdate(Update::Force);
                break;
        }
 
@@ -1726,6 +1724,8 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                }
                if (cur.selection())
                        pattern = cur.selectionAsString(false);
+               else if (!cur.inTexted())
+                       break; // not suitable for selectWord at cursor
                else {
                        pos_type spos = cur.pos();
                        cur.innerText()->selectWord(cur, WHOLE_WORD);
@@ -1762,6 +1762,78 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                break;
        }
 
+       case LFUN_INDEX_TAG_ALL: {
+               Inset * ins = cur.nextInset();
+               if (!ins || ins->lyxCode() != INDEX_CODE)
+                       // not at index inset
+                       break;
+
+               // clone the index inset
+               InsetIndex * cins =
+                       new InsetIndex(static_cast<InsetIndex &>(*cur.nextInset()));
+               // In order to avoid duplication, we compare the
+               // LaTeX output if we find another index inset after
+               // the word
+               odocstringstream oilatex;
+               otexstream oits(oilatex);
+               OutputParams rp(&cur.buffer()->params().encoding());
+               ins->latex(oits, rp);
+               cap::copyInsetToTemp(cur, cins);
+
+               // move backwards into preceding word
+               // skip over other index insets
+               cur.backwardPosIgnoreCollapsed();
+               while (true) {
+                       if (cur.inset().lyxCode() == INDEX_CODE)
+                               cur.pop_back();
+                       else if (cur.prevInset() && cur.prevInset()->lyxCode() == INDEX_CODE)
+                               cur.backwardPosIgnoreCollapsed();
+                       else
+                               break;
+               }
+               if (!cur.inTexted()) {
+                       // Nothing to do here.
+                       setCursorFromInset(ins);
+                       break;
+               }
+               // Get word or selection
+               cur.text()->selectWord(cur, WHOLE_WORD);
+               docstring const searched_string = cur.selectionAsString(false);
+               // Start from the beginning
+               lyx::dispatch(FuncRequest(LFUN_BUFFER_BEGIN));
+               while (findOne(this, searched_string,
+                              false,// case sensitive
+                              true,// match whole word only
+                              true,// forward
+                              false,//find deleted
+                              false,//check wrap
+                              false,// auto-wrap
+                              false,// instant
+                              false// only selection
+                              )) {
+                       cur.clearSelection();
+                       Inset * ains = cur.nextInset();
+                       if (ains && ains->lyxCode() == INDEX_CODE) {
+                               // We have an index inset.
+                               // Check whether it has the same
+                               // LaTeX content and move on if so.
+                               odocstringstream filatex;
+                               otexstream fits(filatex);
+                               ains->latex(fits, rp);
+                               if (oilatex.str() == filatex.str())
+                                       continue;
+                       }
+                       // Paste the inset and possibly continue
+                       cap::pasteFromTemp(cursor(), cursor().buffer()->errorList("Paste"));
+               }
+               // Go back to start position.
+               setCursorFromInset(ins);
+               dr.screenUpdate(cur.result().screenUpdate());
+               if (cur.result().needBufferUpdate())
+                       dr.forceBufferUpdate();
+               break;
+       }
+
        case LFUN_MARK_OFF:
                cur.clearSelection();
                dr.setMessage(from_utf8(N_("Mark off")));
@@ -1881,16 +1953,14 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        message += _("One word");
                message += "\n";
                if (chars_blanks != 1)
-                       message += bformat(_("%1$d characters (including blanks)"),
-                                         chars_blanks);
+                       message += bformat(_("%1$d characters"), chars_blanks);
                else
-                       message += _("One character (including blanks)");
+                       message += _("One character");
                message += "\n";
                if (chars != 1)
-                       message += bformat(_("%1$d characters (excluding blanks)"),
-                                         chars);
+                       message += bformat(_("%1$d characters (no blanks)"), chars);
                else
-                       message += _("One character (excluding blanks)");
+                       message += _("One character (no blanks)");
 
                Alert::information(_("Statistics"), message);
        }
@@ -1916,7 +1986,8 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                cur.setCursor(doc_iterator_begin(cur.buffer()));
                cur.selHandle(false);
                // Force an immediate computation of metrics because we need it below
-               updateMetrics();
+               if (scrolled)
+                       processUpdateFlags(Update::Force);
 
                d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_,
                        true, act == LFUN_SCREEN_UP);
@@ -2072,21 +2143,22 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                // an arbitrary number to limit number of iterations
                const int max_iter = 100000;
                int iterations = 0;
-               Cursor & curs = d->cursor_;
-               Cursor const savecur = curs;
-               curs.reset();
-               if (!curs.nextInset())
-                       curs.forwardInset();
-               curs.beginUndoGroup();
-               while(curs && iterations < max_iter) {
-                       Inset * const ins = curs.nextInset();
+               Cursor & bvcur = d->cursor_;
+               Cursor const savecur = bvcur;
+               bvcur.reset();
+               if (!bvcur.nextInset())
+                       bvcur.forwardInset();
+               bvcur.beginUndoGroup();
+               while(bvcur && iterations < max_iter) {
+                       Inset * const ins = bvcur.nextInset();
                        if (!ins)
                                break;
                        docstring insname = ins->layoutName();
                        while (!insname.empty()) {
                                if (insname == name || name == from_utf8("*")) {
-                                       curs.recordUndo();
                                        lyx::dispatch(fr, dr);
+                                       // we do not want to remember selection here
+                                       bvcur.clearSelection();
                                        ++iterations;
                                        break;
                                }
@@ -2096,11 +2168,11 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                                insname = insname.substr(0, i);
                        }
                        // if we did not delete the inset, skip it
-                       if (!curs.nextInset() || curs.nextInset() == ins)
-                               curs.forwardInset();
+                       if (!bvcur.nextInset() || bvcur.nextInset() == ins)
+                               bvcur.forwardInset();
                }
-               curs = savecur;
-               curs.fixIfBroken();
+               bvcur = savecur;
+               bvcur.fixIfBroken();
                /** This is a dummy undo record only to remember the cursor
                 * that has just been set; this will be used on a redo action
                 * (see ticket #10097)
@@ -2108,8 +2180,8 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                 * FIXME: a better fix would be to have a way to set the
                 * cursor value directly, but I am not sure it is worth it.
                 */
-               curs.recordUndo();
-               curs.endUndoGroup();
+               bvcur.recordUndo();
+               bvcur.endUndoGroup();
                dr.screenUpdate(Update::Force);
                dr.forceBufferUpdate();
 
@@ -2383,12 +2455,50 @@ Inset const * BufferView::getCoveringInset(Text const & text,
 }
 
 
+Inset const * BufferView::clickableMathInset(InsetMathNest const * inset,
+               CoordCache::Insets const & inset_cache, int x, int y) const
+{
+       for (size_t i = 0; i < inset->nargs(); ++i) {
+               MathData const & ar = inset->cell(i);
+               for (size_t j = 0; j < ar.size(); ++j) {
+                       string const name = lyxerr.debugging(Debug::MATHED)
+                               ? insetName(ar[j].nucleus()->lyxCode())
+                               : string();
+                       LYXERR(Debug::MATHED, "Checking inset: " << name);
+                       if (ar[j].nucleus()->clickable(*this, x, y)) {
+                               if (inset_cache.covers(ar[j].nucleus(), x, y)) {
+                                       LYXERR(Debug::MATHED, "Clickable inset: "
+                                              << name);
+                                       return ar[j].nucleus();
+                               }
+                       }
+                       InsetMathNest const * imn =
+                               ar[j].nucleus()->asNestInset();
+                       if (imn) {
+                               Inset const * inner =
+                                       clickableMathInset(imn, inset_cache, x, y);
+                               if (inner)
+                                       return inner;
+                       }
+               }
+       }
+       return nullptr;
+}
+
+
 void BufferView::updateHoveredInset() const
 {
        // Get inset under mouse, if there is one.
        int const x = d->mouse_position_cache_.x_;
        int const y = d->mouse_position_cache_.y_;
        Inset const * covering_inset = getCoveringInset(buffer_.text(), x, y);
+       if (covering_inset && covering_inset->asInsetMath()) {
+               Inset const * inner_inset = clickableMathInset(
+                               covering_inset->asInsetMath()->asNestInset(),
+                               coordCache().getInsets(), x, y);
+               if (inner_inset)
+                       covering_inset = inner_inset;
+       }
 
        d->clickable_inset_ = covering_inset && covering_inset->clickable(*this, x, y);
 
@@ -2435,6 +2545,12 @@ void BufferView::clearLastInset(Inset * inset) const
 }
 
 
+bool BufferView::mouseSelecting() const
+{
+       return d->mouse_selecting_;
+}
+
+
 void BufferView::mouseEventDispatch(FuncRequest const & cmd0)
 {
        //lyxerr << "[ cmd0 " << cmd0 << "]" << endl;
@@ -2457,6 +2573,9 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0)
        d->mouse_position_cache_.x_ = cmd.x();
        d->mouse_position_cache_.y_ = cmd.y();
 
+       d->mouse_selecting_ =
+               cmd.action() == LFUN_MOUSE_MOTION && cmd.button() == mouse_button::button1;
+
        if (cmd.action() == LFUN_MOUSE_MOTION && cmd.button() == mouse_button::none) {
                updateHoveredInset();
                return;
@@ -2498,8 +2617,8 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0)
 
        // Notify left insets
        if (cur != old) {
-               bool badcursor = old.fixIfBroken() | cur.fixIfBroken();
-               badcursor |= notifyCursorLeavesOrEnters(old, cur);
+               bool badcursor = old.fixIfBroken() || cur.fixIfBroken();
+               badcursor = badcursor || notifyCursorLeavesOrEnters(old, cur);
                if (badcursor)
                        cursor().fixIfBroken();
        }
@@ -2588,7 +2707,7 @@ bool BufferView::setCursorFromRow(int row)
 {
        TexRow::TextEntry start, end;
        tie(start,end) = buffer_.texrow().getEntriesFromRow(row);
-       LYXERR(Debug::LATEX,
+       LYXERR(Debug::OUTFILE,
               "setCursorFromRow: for row " << row << ", TexRow has found "
               "start (id=" << start.id << ",pos=" << start.pos << "), "
               "end (id=" << end.id << ",pos=" << end.pos << ")");
@@ -3359,15 +3478,12 @@ void BufferView::checkCursorScrollOffset()
 
        //lyxerr << "cur_x=" << cur_x << ", offset=" << offset << ", row.wid=" << row.width() << ", margin=" << MARGIN << endl;
 
-       if (offset != d->horiz_scroll_offset_)
+       if (offset != d->horiz_scroll_offset_) {
                LYXERR(Debug::PAINTING, "Horiz. scroll offset changed from "
                       << d->horiz_scroll_offset_ << " to " << offset);
-
-       if (d->update_strategy_ == NoScreenUpdate
-           && offset != d->horiz_scroll_offset_) {
-               // FIXME: if one uses SingleParUpdate, then home/end
-               // will not work on long rows. Why?
-               d->update_strategy_ = FullScreenUpdate;
+               row.changed(true);
+               if (d->update_strategy_ == NoScreenUpdate)
+                       d->update_strategy_ = SingleParUpdate;
        }
 
        d->horiz_scroll_offset_ = offset;
@@ -3452,7 +3568,7 @@ void BufferView::draw(frontend::Painter & pain, bool paint_caret)
 
        // The scrollbar needs an update.
        // FIXME: does it always? see ticket #11947.
-       updateScrollbar();
+       updateScrollbarParameters();
 
        // Normalize anchor for next time
        pair<pit_type, ParagraphMetrics const *> firstpm = tm.first();