#include "Buffer.h"
#include "BufferList.h"
#include "BufferParams.h"
+#include "BiblioInfo.h"
#include "CoordCache.h"
#include "Cursor.h"
#include "CutAndPaste.h"
#include "MetricsInfo.h"
#include "Paragraph.h"
#include "Session.h"
+#include "texstream.h"
#include "Text.h"
#include "TextMetrics.h"
#include "TexRow.h"
#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"
#include "support/convert.h"
#include "support/debug.h"
#include "support/docstring.h"
+#include "support/docstring_list.h"
#include "support/filetools.h"
#include "support/gettext.h"
#include "support/lassert.h"
tmpcur.clearSelection();
bv->setCursor(tmpcur);
- return bv->scrollToCursor(bv->cursor(), false, true);
+ return bv->scrollToCursor(bv->cursor(), SCROLL_TOP);
}
bool clickable_inset_;
/// shape of the caret
frontend::CaretGeometry caret_geometry_;
+ ///
+ bool mouse_selecting_ = false;
};
int BufferView::rightMargin() const
{
- // The additional test for the case the outliner is opened.
- if (full_screen_ && lyxrc.full_screen_limit)
- return max(defaultMargin(), (width_ - lyxrc.full_screen_width) / 2);
+ const int screen_width = inPixels(lyxrc.screen_width);
- return defaultMargin();
+ // 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;
+ }
}
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, false);
+ scrollToCursor(d->cursor_.selectionBegin(), SCROLL_VISIBLE);
// 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, false);
+ scrollToCursor(d->cursor_, SCROLL_VISIBLE);
// Metrics have to be recomputed (maybe again)
updateMetrics(flags);
}
// cut off at the top
if (pixels <= d->scrollbarParameters_.min) {
DocIterator dit = doc_iterator_begin(&buffer_);
- showCursor(dit, false, false, update);
+ showCursor(dit, SCROLL_VISIBLE, update);
LYXERR(Debug::SCROLLING, "scroll to top");
return;
}
if (pixels >= d->scrollbarParameters_.max) {
DocIterator dit = doc_iterator_end(&buffer_);
dit.backwardPos();
- showCursor(dit, false, false, update);
+ showCursor(dit, SCROLL_VISIBLE, update);
LYXERR(Debug::SCROLLING, "scroll to bottom");
return;
}
DocIterator dit = doc_iterator_begin(&buffer_);
dit.pit() = i;
LYXERR(Debug::SCROLLING, "pixels = " << pixels << " -> scroll to pit " << i);
- showCursor(dit, false, false, update);
+ showCursor(dit, SCROLL_VISIBLE, update);
}
void BufferView::recenter()
{
- showCursor(d->cursor_, true, false, true);
+ showCursor(d->cursor_, SCROLL_CENTER, true);
}
void BufferView::showCursor()
{
- showCursor(d->cursor_, false, false, true);
+ showCursor(d->cursor_, SCROLL_VISIBLE, true);
}
-void BufferView::showCursor(DocIterator const & dit,
- bool recenter, bool force, bool update)
+void BufferView::showCursor(DocIterator const & dit, ScrollType how,
+ bool update)
{
- if (scrollToCursor(dit, recenter, force) && update)
+ if (scrollToCursor(dit, how) && update)
processUpdateFlags(Update::Force);
}
-bool BufferView::scrollToCursor(DocIterator const & dit, bool const recenter, bool force)
+bool BufferView::scrollToCursor(DocIterator const & dit, ScrollType how)
{
// 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");
+ if (how == SCROLL_CENTER)
+ LYXERR(Debug::SCROLLING, "Centering cursor in workarea");
+ else if (how == SCROLL_TOP)
+ LYXERR(Debug::SCROLLING, "Setting cursor to top of workarea");
else
- LYXERR(Debug::SCROLLING, "scrolling to cursor");
+ LYXERR(Debug::SCROLLING, "Making sure cursor is visible in workarea");
CursorSlice const & bot = dit.bottom();
TextMetrics & tm = textMetrics(bot.text());
else if (bot_pit == tm.last().first + 1)
tm.newParMetricsDown();
- if (tm.contains(bot_pit) && !force) {
+ if (tm.contains(bot_pit) && how == SCROLL_VISIBLE) {
ParagraphMetrics const & pm = tm.parMetrics(bot_pit);
LBUFERR(!pm.rows().empty());
// FIXME: smooth scrolling doesn't work in mathed.
Dimension const & row_dim =
inner_pm.getRow(cs.pos(), dit.boundary()).dim();
int scrolled = 0;
- if (recenter)
- scrolled = scroll(ypos - height_/2);
// We try to visualize the whole row, if the row height is larger than
// the screen height, we scroll to a heuristic value of height_ / 4.
// FIXME: This heuristic value should be replaced by a recursive search
// for a row in the inset that can be visualized completely.
- else if (row_dim.height() > height_) {
+ if (row_dim.height() > height_) {
if (ypos < defaultRowHeight())
scrolled = scroll(ypos - height_ / 4);
else if (ypos > height_ - defaultRowHeight())
d->inlineCompletionPos_ = DocIterator();
tm.redoParagraph(bot_pit);
- ParagraphMetrics const & pm = tm.parMetrics(bot_pit);
int const offset = coordOffset(dit).y_;
-
+ pit_type const old_pit = d->anchor_pit_;
d->anchor_pit_ = bot_pit;
+
CursorSlice const & cs = dit.innerTextSlice();
ParagraphMetrics const & inner_pm =
textMetrics(cs.text()).parMetrics(cs.pit());
Dimension const & row_dim =
inner_pm.getRow(cs.pos(), dit.boundary()).dim();
- if (recenter)
- d->anchor_ypos_ = height_/2;
- else if (d->anchor_pit_ == 0)
- d->anchor_ypos_ = offset + pm.ascent();
- else if (d->anchor_pit_ == max_pit)
- d->anchor_ypos_ = height_ - offset - row_dim.descent();
- else if (offset > height_)
- d->anchor_ypos_ = height_ - offset - row_dim.descent();
- else
- d->anchor_ypos_ = row_dim.ascent();
-
- return true;
+ int const old_ypos = d->anchor_ypos_;
+ d->anchor_ypos_ = - offset + row_dim.ascent();
+ if (how == SCROLL_CENTER)
+ d->anchor_ypos_ += height_/2 - row_dim.height() / 2;
+ return d->anchor_ypos_ != old_ypos || d->anchor_pit_ != old_pit;
}
void BufferView::updateDocumentClass(DocumentClassConstPtr olddc)
{
- message(_("Converting document to new document class..."));
-
StableDocIterator backcur(d->cursor_);
ErrorList & el = buffer_.errorList("Class Switch");
cap::switchBetweenClasses(
case LFUN_SCREEN_SHOW_CURSOR:
case LFUN_BIBTEX_DATABASE_ADD:
case LFUN_BIBTEX_DATABASE_DEL:
+ case LFUN_BIBTEX_DATABASE_LIST:
case LFUN_STATISTICS:
case LFUN_KEYMAP_OFF:
case LFUN_KEYMAP_PRIMARY:
success = setCursorFromEntries({id, pos},
{id_end, pos_end});
}
- if (success && scrollToCursor(d->cursor_, false, true))
+ if (success && scrollToCursor(d->cursor_, SCROLL_TOP))
dr.screenUpdate(Update::Force);
} else {
// Switch to other buffer view and resend cmd
}
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);
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")));
break;
}
+ case LFUN_BIBTEX_DATABASE_LIST: {
+ docstring_list const & files = buffer_.getBibfiles();
+ bool first = true;
+ docstring result;
+ char const separator(os::path_separator());
+ for (auto const & file : files) {
+ if (first)
+ first = false;
+ else
+ result += separator;
+
+ FileName const fn = buffer_.getBibfilePath(file);
+ string const path = fn.realPath();
+ result += from_utf8(os::external_path(path));
+ }
+ dr.setMessage(result);
+ break;
+ }
+
case LFUN_STATISTICS: {
DocIterator from, to;
if (cur.selection()) {
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);
}
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);
// 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;
}
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)
* 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();
string icstr = InsetCommand::params2string(icp);
FuncRequest fr(LFUN_INSET_INSERT, icstr);
lyx::dispatch(fr);
+
+ // if the request comes from the LyX server, then we
+ // return a list of the undefined keys, in case some
+ // action could be taken.
+ if (cmd.origin() != FuncRequest::LYXSERVER)
+ break;
+
+ vector<docstring> keys = getVectorFromString(from_utf8(arg));
+ vector<docstring>::iterator it = keys.begin();
+ vector<docstring>::const_iterator end = keys.end();
+
+ BiblioInfo const & bibInfo = buffer_.masterBibInfo();
+ const BiblioInfo::const_iterator bibEnd = bibInfo.end();
+ while (it != end) {
+ if (bibInfo.find(*it) != bibEnd) {
+ it = keys.erase(it);
+ end = keys.end();
+ } else
+ ++it;
+ }
+ dr.setMessage(getStringFromVector(keys));
+
break;
}
}
+bool BufferView::mouseSelecting() const
+{
+ return d->mouse_selecting_;
+}
+
+
void BufferView::mouseEventDispatch(FuncRequest const & cmd0)
{
//lyxerr << "[ cmd0 " << cmd0 << "]" << endl;
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;
// 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();
}
{
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 << ")");
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_ + 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_}}
);
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, yy - dim.wid},
{xx + dir * (dim.wid + lx - 1), yy - dim.wid},
{xx + dir * (dim.wid + lx - 1), yy},
- {xx, yy}}
+ {xx, yy}}
);
}
// 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, 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}}
+ {xx, m - d + dim.wid}}
);
}
//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;
}
+bool BufferView::busy() const
+{
+ return buffer().undo().activeUndoGroup();
+}
+
+
void BufferView::draw(frontend::Painter & pain, bool paint_caret)
{
if (height_ == 0 || width_ == 0)