X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView.cpp;h=bff44cdbf02ae98d4f8d15b789ea911cfa7bfc03;hb=05dd6614c4e3984f2c1ace3b4a59e47421e13f06;hp=f7567bd4dd9d6c35807b09bc1761586f2d119653;hpb=f41ca959fd531cd955bce778f2734f2a3ff1ee14;p=lyx.git diff --git a/src/BufferView.cpp b/src/BufferView.cpp index f7567bd4dd..bff44cdbf0 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -50,7 +50,10 @@ #include "insets/InsetRef.h" #include "insets/InsetText.h" +#include "mathed/InsetMathNest.h" +#include "mathed/InsetMathRef.h" #include "mathed/MathData.h" +#include "mathed/MathRow.h" #include "frontends/alert.h" #include "frontends/CaretGeometry.h" @@ -59,6 +62,7 @@ #include "frontends/NullPainter.h" #include "frontends/Painter.h" #include "frontends/Selection.h" +#include "frontends/Clipboard.h" #include "support/convert.h" #include "support/debug.h" @@ -272,9 +276,6 @@ struct BufferView::Private */ frontend::GuiBufferViewDelegate * gui_; - /// Cache for Find Next - FuncRequest search_request_cache_; - /// map edited_insets_; @@ -330,16 +331,20 @@ BufferView::~BufferView() } -int BufferView::rightMargin() const +int BufferView::defaultMargin() const { // The value used to be hardcoded to 10 - int const default_margin = zoomedPixels(10); + return zoomedPixels(20); +} + + +int BufferView::rightMargin() const +{ // 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; + if (full_screen_ && lyxrc.full_screen_limit) + return max(defaultMargin(), (width_ - lyxrc.full_screen_width) / 2); - return (width_ - lyxrc.full_screen_width) / 2; + return defaultMargin(); } @@ -447,6 +452,26 @@ Buffer const & BufferView::buffer() const } +docstring const & BufferView::searchRequestCache() const +{ + return theClipboard().getFindBuffer(); +} + + +void BufferView::setSearchRequestCache(docstring const & text) +{ + bool casesensitive; + bool matchword; + bool forward; + bool wrap; + bool instant; + bool onlysel; + docstring const search = string2find(text, casesensitive, matchword, + forward, wrap, instant, onlysel); + theClipboard().setFindBuffer(search); +} + + bool BufferView::needsFitCursor() const { if (cursorStatus(d->cursor_) == CUR_INSIDE) { @@ -649,13 +674,53 @@ string BufferView::contextMenu(int x, int y) const // Get inset under mouse, if there is one. Inset const * covering_inset = getCoveringInset(buffer_.text(), x, y); - if (covering_inset) + if (covering_inset) { + if (covering_inset->asInsetMath()) { + CoordCache::Insets const & inset_cache = + coordCache().getInsets(); + Inset const * inner_inset = mathContextMenu( + covering_inset->asInsetMath()->asNestInset(), + inset_cache, x, y); + if (inner_inset) + return inner_inset->contextMenu(*this, x, y); + } return covering_inset->contextMenu(*this, x, y); + } return buffer_.inset().contextMenu(*this, x, y); } +Inset const * BufferView::mathContextMenu(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, "Examining inset: " << name); + if (!ar[j].nucleus()->contextMenuName().empty()) { + if (inset_cache.covers(ar[j].nucleus(), x, y)) { + LYXERR(Debug::MATHED, "Hit inset: " + << name); + return ar[j].nucleus(); + } + } + InsetMathNest const * imn = + ar[j].nucleus()->asNestInset(); + if (imn) { + Inset const * inner = + mathContextMenu(imn, inset_cache, x, y); + if (inner) + return inner; + } + } + } + return nullptr; +} + void BufferView::scrollDocView(int const pixels, bool update) { @@ -794,23 +859,24 @@ void BufferView::bookmarkEditPosition() void BufferView::saveBookmark(unsigned int idx) { + if (buffer().isInternal()) + return; + // tentatively save bookmark, id and pos will be used to // acturately locate a bookmark in a 'live' lyx session. // pit and pos will be updated with bottom level pit/pos // when lyx exits. - if (!buffer_.isInternal()) { - theSession().bookmarks().save( - buffer_.fileName(), - d->cursor_.bottom().pit(), - d->cursor_.bottom().pos(), - d->cursor_.paragraph().id(), - d->cursor_.pos(), - idx - ); - if (idx) - // emit message signal. - message(_("Save bookmark")); - } + theSession().bookmarks().save( + buffer_.fileName(), + d->cursor_.bottom().pit(), + d->cursor_.bottom().pos(), + d->cursor_.paragraph().id(), + d->cursor_.pos(), + idx + ); + if (idx) + // emit message signal. + message(_("Save bookmark")); } @@ -851,7 +917,7 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos, // restoration is inaccurate. If a bookmark was within an inset, // it will be restored to the left of the outmost inset that contains // the bookmark. - if (bottom_pit < int(buffer_.paragraphs().size())) { + if (!success && bottom_pit < int(buffer_.paragraphs().size())) { dit = doc_iterator_begin(&buffer_); dit.pit() = bottom_pit; @@ -922,18 +988,17 @@ void BufferView::scrollToCursor() bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter) { - // We are not properly started yet, delay until resizing is - // done. + // We are not properly started yet, delay until resizing is done. if (height_ == 0) return false; if (recenter) - LYXERR(Debug::SCROLLING, "recentering and scrolling to cursor"); + LYXERR(Debug::SCROLLING, "recentering and scrolling to cursor"); else - LYXERR(Debug::SCROLLING, "scrolling to cursor"); + LYXERR(Debug::SCROLLING, "scrolling to cursor"); CursorSlice const & bot = dit.bottom(); - TextMetrics & tm = d->text_metrics_[bot.text()]; + TextMetrics & tm = textMetrics(bot.text()); pos_type const max_pit = pos_type(bot.text()->paragraphs().size() - 1); pos_type bot_pit = bot.pit(); @@ -954,10 +1019,11 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter) LBUFERR(!pm.rows().empty()); // FIXME: smooth scrolling doesn't work in mathed. CursorSlice const & cs = dit.innerTextSlice(); - int offset = coordOffset(dit).y_; - int ypos = pm.position() + offset; + int const ypos = pm.position() + coordOffset(dit).y_; + ParagraphMetrics const & inner_pm = + textMetrics(cs.text()).parMetrics(cs.pit()); Dimension const & row_dim = - pm.getRow(cs.pos(), dit.boundary()).dim(); + inner_pm.getRow(cs.pos(), dit.boundary()).dim(); int scrolled = 0; if (recenter) scrolled = scroll(ypos - height_/2); @@ -976,13 +1042,13 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter) // If the top part of the row falls of the screen, we scroll // up to align the top of the row with the top of the screen. else if (ypos - row_dim.ascent() < 0 && ypos < height_) { - int ynew = row_dim.ascent(); + int const ynew = row_dim.ascent(); scrolled = scrollUp(ynew - ypos); } // If the bottom of the row falls of the screen, we scroll down. else if (ypos + row_dim.descent() > height_ && ypos > 0) { - int ynew = height_ - row_dim.descent(); + int const ynew = height_ - row_dim.descent(); scrolled = scrollDown(ypos - ynew); } @@ -996,12 +1062,14 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter) tm.redoParagraph(bot_pit); ParagraphMetrics const & pm = tm.parMetrics(bot_pit); - int offset = coordOffset(dit).y_; + int const offset = coordOffset(dit).y_; d->anchor_pit_ = bot_pit; CursorSlice const & cs = dit.innerTextSlice(); + ParagraphMetrics const & inner_pm = + textMetrics(cs.text()).parMetrics(cs.pit()); Dimension const & row_dim = - pm.getRow(cs.pos(), dit.boundary()).dim(); + inner_pm.getRow(cs.pos(), dit.boundary()).dim(); if (recenter) d->anchor_ypos_ = height_/2; @@ -1021,7 +1089,7 @@ bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter) void BufferView::makeDocumentClass() { DocumentClassConstPtr olddc = buffer_.params().documentClassPtr(); - buffer_.params().makeDocumentClass(); + buffer_.params().makeDocumentClass(buffer_.isClone(), buffer_.isInternal()); updateDocumentClass(olddc); } @@ -1152,6 +1220,7 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) case LFUN_MARK_OFF: case LFUN_MARK_ON: case LFUN_MARK_TOGGLE: + case LFUN_SEARCH_STRING_SET: case LFUN_SCREEN_RECENTER: case LFUN_SCREEN_SHOW_CURSOR: case LFUN_BIBTEX_DATABASE_ADD: @@ -1180,7 +1249,8 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) case LFUN_LABEL_GOTO: flag.setEnabled(!cmd.argument().empty() - || getInsetByCode(cur, REF_CODE)); + || getInsetByCode(cur, REF_CODE) + || getInsetByCode(cur, MATH_REF_CODE)); break; case LFUN_CHANGES_MERGE: @@ -1248,6 +1318,10 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag) break; } + case LFUN_COPY: + flag.setEnabled(cur.selection()); + break; + default: return false; } @@ -1446,6 +1520,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) break; case LFUN_BOOKMARK_SAVE: + dr.screenUpdate(Update::Force); saveBookmark(convert(to_utf8(cmd.argument()))); break; @@ -1466,6 +1541,12 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) // eventually call LFUN_PARAGRAPH_GOTO, but it seems best // to have it here. dr.screenUpdate(Update::Force | Update::FitCursor); + } else { + InsetMathRef * minset = + getInsetByCode(cur, MATH_REF_CODE); + if (minset) + lyx::dispatch(FuncRequest(LFUN_LABEL_GOTO, + minset->getTarget())); } break; } @@ -1601,63 +1682,72 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) case LFUN_WORD_FIND_FORWARD: case LFUN_WORD_FIND_BACKWARD: { - // FIXME THREAD - // Would it maybe be better if this variable were view specific anyway? - static docstring last_search; docstring searched_string; if (!cmd.argument().empty()) { - last_search = cmd.argument(); + setSearchRequestCache(cmd.argument()); searched_string = cmd.argument(); } else { - searched_string = last_search; + searched_string = searchRequestCache(); } if (searched_string.empty()) break; - bool const fw = act == LFUN_WORD_FIND_FORWARD; docstring const data = - find2string(searched_string, true, false, fw); + find2string(searched_string, false, false, + act == LFUN_WORD_FIND_FORWARD, false, false, false); bool found = lyxfind(this, FuncRequest(LFUN_WORD_FIND, data)); if (found) dr.screenUpdate(Update::Force | Update::FitCursor); + else + dr.setMessage(_("Search string not found!")); break; } case LFUN_WORD_FIND: { - FuncRequest req = cmd; - if (cmd.argument().empty() && !d->search_request_cache_.argument().empty()) - req = d->search_request_cache_; - if (req.argument().empty()) { + docstring arg = cmd.argument(); + if (arg.empty()) + arg = searchRequestCache(); + if (arg.empty()) { lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "findreplace")); break; } - if (lyxfind(this, req)) + if (lyxfind(this, FuncRequest(act, arg))) dr.screenUpdate(Update::Force | Update::FitCursor); + else + dr.setMessage(_("Search string not found!")); - d->search_request_cache_ = req; + setSearchRequestCache(arg); break; } - case LFUN_WORD_REPLACE: { - bool has_deleted = false; - if (cur.selection()) { - DocIterator beg = cur.selectionBegin(); - DocIterator end = cur.selectionEnd(); - if (beg.pit() == end.pit()) { - for (pos_type p = beg.pos() ; p < end.pos() ; ++p) { - if (!cur.inMathed() && cur.paragraph().isDeleted(p)) { - has_deleted = true; - break; - } - } - } + case LFUN_SEARCH_STRING_SET: { + docstring pattern = cmd.argument(); + if (!pattern.empty()) { + setSearchRequestCache(pattern); + break; } - if (lyxreplace(this, cmd, has_deleted)) { + if (cur.selection()) + pattern = cur.selectionAsString(false); + else { + pos_type spos = cur.pos(); + cur.innerText()->selectWord(cur, WHOLE_WORD); + pattern = cur.selectionAsString(false); + cur.selection(false); + cur.pos() = spos; + } + setSearchRequestCache(pattern); + break; + } + + case LFUN_WORD_REPLACE: { + if (lyxreplace(this, cmd)) { dr.forceBufferUpdate(); dr.screenUpdate(Update::Force | Update::FitCursor); } + else + dr.setMessage(_("Search string not found!")); break; } @@ -1830,7 +1920,7 @@ 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 - processUpdateFlags(Update::Force); + updateMetrics(); d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_, true, act == LFUN_SCREEN_UP); @@ -2177,6 +2267,18 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr) break; } + case LFUN_COPY: + // With multi-cell table content, we pass down to the inset + if (cur.inTexted() && cur.selection() + && cur.selectionBegin().idx() != cur.selectionEnd().idx()) { + buffer_.dispatch(cmd, dr); + dispatched = dr.dispatched(); + break; + } + cap::copySelection(cur); + cur.message(_("Copy")); + break; + default: // OK, so try the Buffer itself... buffer_.dispatch(cmd, dr); @@ -2285,12 +2387,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); @@ -2411,7 +2551,7 @@ void BufferView::mouseEventDispatch(FuncRequest const & cmd0) // Do we have a selection? theSelection().haveSelection(cursor().selection()); - if (cur.needBufferUpdate()) { + if (cur.needBufferUpdate() || buffer().needUpdate()) { cur.clearBufferUpdate(); buffer().updateBuffer(); } @@ -2937,7 +3077,8 @@ void BufferView::insertLyXFile(FileName const & fname, bool const ignorelang) buf.changeLanguage(buf.language(), d->cursor_.getFont().language()); buffer_.undo().recordUndo(d->cursor_); cap::pasteParagraphList(d->cursor_, pars, - buf.params().documentClassPtr(), el); + buf.params().documentClassPtr(), + buf.params().authors(), el); res = _("Document %1$s inserted."); } else { res = _("Could not insert document %1$s"); @@ -3060,7 +3201,9 @@ void BufferView::caretPosAndDim(Point & p, Dimension & dim) const } else { Font const font = cur.real_current_font; frontend::FontMetrics const & fm = theFontMetrics(font); - dim.wid = fm.lineWidth(); + // lineWidth() can be 0 to mean 'thin line' on HiDpi, but the + // caret drawing code is not prepared for that. + dim.wid = max(fm.lineWidth(), 1); dim.asc = fm.maxAscent(); dim.des = fm.maxDescent(); }