#include "BranchList.h"
#include "Buffer.h"
-#include "buffer_funcs.h"
#include "BufferList.h"
#include "BufferParams.h"
#include "CoordCache.h"
#include "CutAndPaste.h"
#include "DispatchResult.h"
#include "ErrorList.h"
-#include "factory.h"
-#include "FloatList.h"
#include "FuncRequest.h"
#include "FuncStatus.h"
#include "Intl.h"
-#include "InsetIterator.h"
#include "Language.h"
-#include "LaTeXFeatures.h"
#include "LayoutFile.h"
#include "Lexer.h"
#include "LyX.h"
#include "LyXAction.h"
#include "lyxfind.h"
-#include "Layout.h"
#include "LyXRC.h"
#include "MetricsInfo.h"
#include "Paragraph.h"
-#include "ParagraphParameters.h"
-#include "ParIterator.h"
-#include "RowPainter.h"
#include "Session.h"
#include "Text.h"
-#include "TextClass.h"
#include "TextMetrics.h"
#include "TexRow.h"
#include "TocBackend.h"
-#include "WordLangTuple.h"
#include "insets/InsetBibtex.h"
#include "insets/InsetCitation.h"
#include "insets/InsetCommand.h" // ChangeRefs
-#include "insets/InsetExternal.h"
#include "insets/InsetGraphics.h"
-#include "insets/InsetNote.h"
#include "insets/InsetRef.h"
#include "insets/InsetText.h"
+#include "mathed/InsetMath.h"
#include "mathed/MathData.h"
-#include "mathed/InsetMathNest.h"
+#include "mathed/MathRow.h"
#include "frontends/alert.h"
-#include "frontends/Application.h"
+#include "frontends/CaretGeometry.h"
#include "frontends/Delegates.h"
#include "frontends/FontMetrics.h"
#include "frontends/NullPainter.h"
#include "support/convert.h"
#include "support/debug.h"
-#include "support/ExceptionMessage.h"
+#include "support/docstring.h"
#include "support/filetools.h"
#include "support/gettext.h"
#include "support/lassert.h"
#include "support/Length.h"
#include "support/lstrings.h"
#include "support/lyxlib.h"
-#include "support/Package.h"
#include "support/types.h"
+#include <algorithm>
#include <cerrno>
+#include <cstring>
#include <fstream>
#include <functional>
#include <iterator>
CursorSlice current_row_slice_;
/// are we hovering something that we can click
bool clickable_inset_;
+ /// shape of the caret
+ frontend::CaretGeometry caret_geometry_;
};
int BufferView::topMargin() const
{
- // original value was 20px, which is 0.2in at 100dpi
- return zoomedPixels(20);
+ // Original value was 20px at 100dpi. For internal buffers like in
+ // advanced search and replace, a value of 5px is enough.
+ return zoomedPixels(buffer().isInternal() ? 5 : 20);
}
int BufferView::bottomMargin() const
{
- // original value was 20px, which is 0.2in at 100dpi
- return zoomedPixels(20);
+ return topMargin();
}
// We handle this before FitCursor because the later will require
// correct metrics at cursor position.
if (!(flags & Update::ForceDraw)
- && (flags & Update::SinglePar)
- && !singleParUpdate())
- updateMetrics(flags);
+ && (flags & Update::SinglePar)
+ && !singleParUpdate())
+ updateMetrics(flags);
// Then make sure that the screen contains the cursor if needed
if (flags & Update::FitCursor) {
-void BufferView::scrollDocView(int const value, bool update)
+void BufferView::scrollDocView(int const pixels, bool update)
{
// The scrollbar values are relative to the top of the screen, therefore the
// offset is equal to the target value.
// No scrolling at all? No need to redraw anything
- if (value == 0)
+ if (pixels == 0)
return;
// If the offset is less than 2 screen height, prefer to scroll instead.
- if (abs(value) <= 2 * height_) {
- d->anchor_ypos_ -= value;
+ if (abs(pixels) <= 2 * height_) {
+ d->anchor_ypos_ -= pixels;
processUpdateFlags(Update::Force);
return;
}
// cut off at the top
- if (value <= d->scrollbarParameters_.min) {
+ if (pixels <= d->scrollbarParameters_.min) {
DocIterator dit = doc_iterator_begin(&buffer_);
showCursor(dit, false, update);
LYXERR(Debug::SCROLLING, "scroll to top");
}
// cut off at the bottom
- if (value >= d->scrollbarParameters_.max) {
+ if (pixels >= d->scrollbarParameters_.max) {
DocIterator dit = doc_iterator_end(&buffer_);
dit.backwardPos();
showCursor(dit, false, update);
pit_type i = 0;
for (; i != int(d->par_height_.size()); ++i) {
par_pos += d->par_height_[i];
- if (par_pos >= value)
+ if (par_pos >= pixels)
break;
}
- if (par_pos < value) {
+ if (par_pos < pixels) {
// It seems we didn't find the correct pit so stay on the safe side and
// scroll to bottom.
LYXERR0("scrolling position not found!");
DocIterator dit = doc_iterator_begin(&buffer_);
dit.pit() = i;
- LYXERR(Debug::SCROLLING, "value = " << value << " -> scroll to pit " << i);
+ LYXERR(Debug::SCROLLING, "pixels = " << pixels << " -> scroll to pit " << i);
showCursor(dit, false, update);
}
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"));
}
break;
case LFUN_FILE_INSERT_PLAINTEXT_PARA:
case LFUN_FILE_INSERT_PLAINTEXT: {
- docstring const fname = cmd.argument();
+ docstring const & fname = cmd.argument();
if (!FileName::isAbsolute(to_utf8(fname))) {
flag.message(_("Absolute filename expected."));
return false;
break;
}
+ case LFUN_COPY:
+ flag.setEnabled(cur.selection());
+ break;
+
default:
return false;
}
break;
case LFUN_BOOKMARK_SAVE:
+ dr.screenUpdate(Update::Force);
saveBookmark(convert<unsigned int>(to_utf8(cmd.argument())));
break;
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;
}
}
if (lyxfind(this, req))
dr.screenUpdate(Update::Force | Update::FitCursor);
+ else
+ dr.setMessage(_("Search string not found!"));
d->search_request_cache_ = req;
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;
- }
- }
- }
- }
- if (lyxreplace(this, cmd, has_deleted)) {
+ if (lyxreplace(this, cmd)) {
dr.forceBufferUpdate();
dr.screenUpdate(Update::Force | Update::FitCursor);
}
+ else
+ dr.setMessage(_("Search string not found!"));
break;
}
break;
}
+ case LFUN_COPY:
+ cap::copySelection(cur);
+ cur.message(_("Copy"));
+ break;
+
default:
// OK, so try the Buffer itself...
buffer_.dispatch(cmd, dr);
}
-int BufferView::scroll(int y)
+int BufferView::scroll(int pixels)
{
- if (y > 0)
- return scrollDown(y);
- if (y < 0)
- return scrollUp(-y);
+ if (pixels > 0)
+ return scrollDown(pixels);
+ if (pixels < 0)
+ return scrollUp(-pixels);
return 0;
}
-int BufferView::scrollDown(int offset)
+int BufferView::scrollDown(int pixels)
{
Text * text = &buffer_.text();
TextMetrics & tm = d->text_metrics_[text];
- int const ymax = height_ + offset;
+ int const ymax = height_ + pixels;
while (true) {
pair<pit_type, ParagraphMetrics const *> last = tm.last();
int bottom_pos = last.second->position() + last.second->descent();
if (last.first + 1 == int(text->paragraphs().size())) {
if (bottom_pos <= height_)
return 0;
- offset = min(offset, bottom_pos - height_);
+ pixels = min(pixels, bottom_pos - height_);
break;
}
if (bottom_pos > ymax)
break;
tm.newParMetricsDown();
}
- d->anchor_ypos_ -= offset;
- return -offset;
+ d->anchor_ypos_ -= pixels;
+ return -pixels;
}
-int BufferView::scrollUp(int offset)
+int BufferView::scrollUp(int pixels)
{
Text * text = &buffer_.text();
TextMetrics & tm = d->text_metrics_[text];
- int ymin = - offset;
+ int ymin = - pixels;
while (true) {
pair<pit_type, ParagraphMetrics const *> first = tm.first();
int top_pos = first.second->position() - first.second->ascent();
if (first.first == 0) {
if (top_pos >= 0)
return 0;
- offset = min(offset, - top_pos);
+ pixels = min(pixels, - top_pos);
break;
}
if (top_pos < ymin)
break;
tm.newParMetricsUp();
}
- d->anchor_ypos_ += offset;
- return offset;
+ d->anchor_ypos_ += pixels;
+ return pixels;
}
dim.wid = lyxrc.cursor_width;
p = getPos(cur);
+ // center fat carets horizontally
+ p.x_ -= dim.wid / 2;
+ // p is top-left
p.y_ -= dim.asc;
}
+void BufferView::buildCaretGeometry(bool complet)
+{
+ Point p;
+ Dimension dim;
+ caretPosAndDim(p, dim);
+
+ Cursor const & cur = d->cursor_;
+ Font const & realfont = cur.real_current_font;
+ frontend::FontMetrics const & fm = theFontMetrics(realfont.fontInfo());
+ bool const isrtl = realfont.isVisibleRightToLeft();
+ int const dir = isrtl ? -1 : 1;
+
+ frontend::CaretGeometry & cg = d->caret_geometry_;
+ cg.shapes.clear();
+
+ // The caret itself, slanted for italics in text edit mode except
+ // for selections because the selection rect does not slant
+ bool const slant = fm.italic() && cur.inTexted() && !cur.selection();
+ double const slope = slant ? fm.italicSlope() : 0;
+ cg.shapes.push_back(
+ {{iround(p.x_ + dim.asc * slope), p.y_},
+ {iround(p.x_ - dim.des * slope), p.y_ + dim.height()},
+ {iround(p.x_ + dir * dim.wid - dim.des * slope), p.y_ + dim.height()},
+ {iround(p.x_ + dir * dim.wid + dim.asc * slope), p.y_}}
+ );
+
+ // The language indicator _| (if needed)
+ Language const * doclang = buffer().params().language;
+ if (!((realfont.language() == doclang && isrtl == doclang->rightToLeft())
+ || realfont.language() == latex_language)) {
+ int const lx = dim.height() / 3;
+ int const xx = iround(p.x_ - dim.des * slope);
+ int const yy = p.y_ + dim.height();
+ cg.shapes.push_back(
+ {{xx, yy - dim.wid},
+ {xx + dir * (dim.wid + lx - 1), yy - dim.wid},
+ {xx + dir * (dim.wid + lx - 1), yy},
+ {xx, yy}}
+ );
+ }
+
+ // The completion triangle |> (if needed)
+ if (complet) {
+ int const m = p.y_ + dim.height() / 2;
+ int const d = dim.height() / 8;
+ // offset for slanted carret
+ int const sx = iround((dim.asc - (dim.height() / 2 - d)) * slope);
+ // starting position x
+ int const xx = p.x_ + dir * dim.wid + sx;
+ cg.shapes.push_back(
+ {{xx, m - d},
+ {xx + dir * d, m},
+ {xx, m + d},
+ {xx, m + d - dim.wid},
+ {xx + dir * d - dim.wid, m},
+ {xx, m - d + dim.wid}}
+ );
+ }
+
+ // compute extremal x values
+ cg.left = 1000000;
+ cg.right = -1000000;
+ cg.top = 1000000;
+ cg.bottom = -1000000;
+ for (auto const & shape : cg.shapes)
+ for (Point const & p : shape) {
+ cg.left = min(cg.left, p.x_);
+ cg.right = max(cg.right, p.x_);
+ cg.top = min(cg.top, p.y_);
+ cg.bottom = max(cg.bottom, p.y_);
+ }
+}
+
+
+frontend::CaretGeometry const & BufferView::caretGeometry() const
+{
+ return d->caret_geometry_;
+}
+
+
bool BufferView::caretInView() const
{
if (!paragraphVisible(cursor()))
* move at all
*/
if (paint_caret) {
- Row const & caret_row = d->cursor_.textRow();
- caret_row.changed(true);
+ Cursor cur(d->cursor_);
+ while (cur.depth() > 1) {
+ if (!cur.inTexted())
+ break;
+ TextMetrics const & tm = textMetrics(cur.text());
+ if (d->caret_geometry_.left >= tm.origin().x_
+ && d->caret_geometry_.right <= tm.origin().x_ + tm.dim().width())
+ break;
+ cur.pop();
+ }
+ cur.textRow().changed(true);
}
}
}
-size_t const & BufferView::inlineCompletionUniqueChars() const
+size_t BufferView::inlineCompletionUniqueChars() const
{
return d->inlineCompletionUniqueChars_;
}