#include "Paragraph.h"
#include "ParIterator.h"
#include "Row.h"
+#include "texstream.h"
#include "Text.h"
#include "TextMetrics.h"
#include "TocBackend.h"
#include "mathed/InsetMath.h"
#include "mathed/InsetMathBrace.h"
+#include "mathed/InsetMathEnsureMath.h"
#include "mathed/InsetMathScript.h"
#include "mathed/MacroTable.h"
#include "mathed/MathData.h"
+#include "mathed/MathFactory.h"
#include "mathed/MathMacro.h"
-#include "support/bind.h"
-
#include <sstream>
#include <limits>
#include <map>
// Find position closest to (x, y) in cell given by iter.
// Used only in mathed
-DocIterator bruteFind2(Cursor const & c, int x, int y)
+DocIterator bruteFind(Cursor const & c, int x, int y)
{
double best_dist = numeric_limits<double>::max();
DocIterator result;
DocIterator it = c;
- it.top().pos() = 0;
+ it.pos() = 0;
DocIterator et = c;
- et.top().pos() = et.top().asInsetMath()->cell(et.top().idx()).size();
+ et.pos() = et.lastpos();
for (size_t i = 0;; ++i) {
int xo;
int yo;
Inset const * inset = &it.inset();
- CoordCache const & cache = c.bv().coordCache();
+ CoordCache::Insets const & insetCache = c.bv().coordCache().getInsets();
// FIXME: in the case where the inset is not in the cache, this
// means that no part of it is visible on screen. In this case
// we don't do elaborate search and we just return the forwarded
// DocIterator at its beginning.
- if (!cache.getInsets().has(inset)) {
+ if (!insetCache.has(inset)) {
it.top().pos() = 0;
return it;
}
- Point const o = cache.getInsets().xy(inset);
+ Point const o = insetCache.xy(inset);
inset->cursorPos(c.bv(), it.top(), c.boundary(), xo, yo);
// Convert to absolute
xo += o.x_;
}
-/*
-/// moves position closest to (x, y) in given box
-bool bruteFind(Cursor & cursor,
- int x, int y, int xlow, int xhigh, int ylow, int yhigh)
-{
- LASSERT(!cursor.empty(), return false);
- Inset & inset = cursor[0].inset();
- BufferView & bv = cursor.bv();
-
- CoordCache::InnerParPosCache const & cache =
- bv.coordCache().getParPos().find(cursor.bottom().text())->second;
- // Get an iterator on the first paragraph in the cache
- DocIterator it(inset);
- it.push_back(CursorSlice(inset));
- it.pit() = cache.begin()->first;
- // Get an iterator after the last paragraph in the cache
- DocIterator et(inset);
- et.push_back(CursorSlice(inset));
- et.pit() = prev(cache.end(), 1)->first;
- if (et.pit() >= et.lastpit())
- et = doc_iterator_end(inset);
- else
- ++et.pit();
-
- double best_dist = numeric_limits<double>::max();
- DocIterator best_cursor = et;
-
- for ( ; it != et; it.forwardPos(true)) {
- // avoid invalid nesting when selecting
- if (!cursor.selection() || positionable(it, cursor.anchor_)) {
- Point p = bv.getPos(it, false);
- int xo = p.x_;
- int yo = p.y_;
- if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
- double const dx = xo - x;
- double const dy = yo - y;
- double const d = dx * dx + dy * dy;
- // '<=' in order to take the last possible position
- // this is important for clicking behind \sum in e.g. '\sum_i a'
- if (d <= best_dist) {
- // lyxerr << "*" << endl;
- best_dist = d;
- best_cursor = it;
- }
- }
- }
- }
-
- if (best_cursor != et) {
- cursor.setCursor(best_cursor);
- return true;
- }
-
- return false;
-}
-*/
-
-/*
-/// moves position closest to (x, y) in given box
-bool bruteFind3(Cursor & cur, int x, int y, bool up)
-{
- BufferView & bv = cur.bv();
- int ylow = up ? 0 : y + 1;
- int yhigh = up ? y - 1 : bv.workHeight();
- int xlow = 0;
- int xhigh = bv.workWidth();
-
-// FIXME: bit more work needed to get 'from' and 'to' right.
- pit_type from = cur.bottom().pit();
- //pit_type to = cur.bottom().pit();
- //lyxerr << "Pit start: " << from << endl;
-
- //lyxerr << "bruteFind3: x: " << x << " y: " << y
- // << " xlow: " << xlow << " xhigh: " << xhigh
- // << " ylow: " << ylow << " yhigh: " << yhigh
- // << endl;
- DocIterator it = doc_iterator_begin(cur.buffer());
- it.pit() = from;
- DocIterator et = doc_iterator_end(cur.buffer());
-
- double best_dist = numeric_limits<double>::max();
- DocIterator best_cursor = et;
-
- for ( ; it != et; it.forwardPos()) {
- // avoid invalid nesting when selecting
- if (bv.cursorStatus(it) == CUR_INSIDE
- && (!cur.selection() || positionable(it, cur.realAnchor()))) {
- // If this function is ever used again, check
- // whether this is the same as "bv.getPos(it,
- // false)" with boundary = false.
- Point p = bv.getPos(it);
- int xo = p.x_;
- int yo = p.y_;
- if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) {
- double const dx = xo - x;
- double const dy = yo - y;
- double const d = dx * dx + dy * dy;
- //lyxerr << "itx: " << xo << " ity: " << yo << " d: " << d
- // << " dx: " << dx << " dy: " << dy
- // << " idx: " << it.idx() << " pos: " << it.pos()
- // << " it:\n" << it
- // << endl;
- // '<=' in order to take the last possible position
- // this is important for clicking behind \sum in e.g. '\sum_i a'
- if (d <= best_dist) {
- //lyxerr << "*" << endl;
- best_dist = d;
- best_cursor = it;
- }
- }
- }
- }
-
- //lyxerr << "best_dist: " << best_dist << " cur:\n" << best_cursor << endl;
- if (best_cursor == et)
- return false;
- cur.setCursor(best_cursor);
- return true;
-}
-*/
} // namespace anon
CursorData::CursorData()
: DocIterator(), anchor_(),
selection_(false), mark_(false), word_selection_(false),
- logicalpos_(false), current_font(inherit_font),
+ current_font(inherit_font),
autocorrect_(false), macromode_(false)
{}
CursorData::CursorData(Buffer * buffer)
: DocIterator(buffer), anchor_(),
selection_(false), mark_(false), word_selection_(false),
- logicalpos_(false), current_font(inherit_font),
+ current_font(inherit_font),
autocorrect_(false), macromode_(false)
{}
CursorData::CursorData(DocIterator const & dit)
: DocIterator(dit), anchor_(),
selection_(false), mark_(false), word_selection_(false),
- logicalpos_(false), current_font(inherit_font),
+ current_font(inherit_font),
autocorrect_(false), macromode_(false)
{}
}
-// this (intentionally) does neither touch anchor nor selection status
void Cursor::setCursor(DocIterator const & cur)
{
DocIterator::operator=(cur);
}
+void Cursor::setCursorSelectionTo(DocIterator dit)
+{
+ size_t i = 0;
+ // normalise dit
+ while (i < dit.depth() && i < anchor_.depth() && dit[i] == anchor_[i])
+ ++i;
+ if (i != dit.depth()) {
+ // otherwise the cursor is already normal
+ if (i == anchor_.depth())
+ // dit is a proper extension of the anchor_
+ dit.cutOff(i - 1);
+ else if (i + 1 < dit.depth()) {
+ // one has dit[i] != anchor_[i] but either dit[i-1] == anchor_[i-1]
+ // or i == 0. Remove excess.
+ dit.cutOff(i);
+ if (dit[i] > anchor_[i])
+ // place dit after the inset it was in
+ ++dit.pos();
+ }
+ }
+ setCursor(dit);
+ setSelection();
+}
+
+
void Cursor::setCursorToAnchor()
{
if (selection()) {
bool Cursor::inCoordCache() const
{
+ // the root inset is not in cache, but we do not need it.
+ if (depth() == 1)
+ return true;
CoordCache::Insets const & icache = bv_->coordCache().getInsets();
- for (size_t i = 0 ; i < depth() ; ++i)
+ for (size_t i = 1 ; i < depth() ; ++i)
if (!icache.has(&(*this)[i].inset()))
return false;
return true;
// LASSERT: There have been several bugs around this code, that seem
// to involve failures to reset the anchor. We can at least not crash
// in release mode by resetting it ourselves.
- LASSERT(anchor_.depth() >= depth(),
- const_cast<DocIterator &>(anchor_) = *this);
+ if (anchor_.depth() < depth()) {
+ LYXERR0("Cursor is deeper than anchor. PLEASE REPORT.\nCursor is"
+ << *this);
+ const_cast<DocIterator &>(anchor_) = *this;
+ }
CursorSlice normal = anchor_[depth() - 1];
if (depth() < anchor_.depth() && top() <= normal) {
}
-DocIterator & Cursor::realAnchor()
-{
- return anchor_;
-}
-
-
CursorSlice Cursor::selBegin() const
{
if (!selection())
void Cursor::setSelection()
{
- setSelection(true);
+ selection(true);
if (idx() == normalAnchor().idx() &&
pit() == normalAnchor().pit() &&
pos() == normalAnchor().pos())
- setSelection(false);
+ selection(false);
}
void Cursor::setSelection(DocIterator const & where, int n)
{
setCursor(where);
- setSelection(true);
+ selection(true);
anchor_ = where;
pos() += n;
}
void Cursor::clearSelection()
{
- setSelection(false);
+ selection(false);
setWordSelection(false);
setMark(false);
resetAnchor();
cap::saveSelection(*this);
resetAnchor();
- setSelection(sel);
+ selection(sel);
return true;
}
} // namespace lyx
void Cursor::insert(docstring const & str)
{
- for_each(str.begin(), str.end(),
- bind(static_cast<void(Cursor::*)(char_type)>
- (&Cursor::insert), this, _1));
+ for (char_type c : str)
+ insert(c);
}
// let's require two backspaces for 'big stuff' and
// highlight on the first
resetAnchor();
- setSelection(true);
+ selection(true);
--pos();
} else {
--pos();
// 'clever' UI hack: only erase large items if previously slected
if (pos() != lastpos() && nextAtom()->nargs() > 0) {
resetAnchor();
- setSelection(true);
+ selection(true);
++pos();
} else {
plainErase();
InsetMathNest * const in = inset().asInsetMath()->asNestInset();
if (in && in->interpretString(*this, s))
return true;
- MathAtom atom = buffer()->getMacro(name, *this, false) ?
- MathAtom(new MathMacro(buffer(), name)) : createInsetMath(name, buffer());
+ bool const user_macro = buffer()->getMacro(name, *this, false);
+ MathAtom atom = user_macro ? MathAtom(new MathMacro(buffer(), name))
+ : createInsetMath(name, buffer());
// try to put argument into macro, if we just inserted a macro
bool macroArg = false;
else if (atom.nucleus()->nargs() > 0)
atom.nucleus()->cell(0).append(selection);
- plainInsert(atom);
+ MathWordList const & words = mathedWordList();
+ MathWordList::const_iterator it = words.find(name);
+ bool keep_mathmode = it != words.end() && (it->second.inset == "font"
+ || it->second.inset == "oldfont"
+ || it->second.inset == "mbox");
+ bool ert_macro = !user_macro && it == words.end() && atomAsMacro;
+
+ if (in && in->currentMode() == Inset::TEXT_MODE
+ && atom.nucleus()->currentMode() == Inset::MATH_MODE
+ && name != from_ascii("ensuremath") && !ert_macro) {
+ MathAtom at(new InsetMathEnsureMath(buffer()));
+ at.nucleus()->cell(0).push_back(atom);
+ niceInsert(at);
+ posForward();
+ } else if (in && in->currentMode() == Inset::MATH_MODE
+ && atom.nucleus()->currentMode() == Inset::TEXT_MODE
+ && !keep_mathmode) {
+ MathAtom at = createInsetMath("text", buffer());
+ at.nucleus()->cell(0).push_back(atom);
+ niceInsert(at);
+ posForward();
+ } else
+ plainInsert(atom);
// finally put the macro argument behind, if needed
if (macroArg) {
<< pos() << ' ' << lastpos() << " in idx: " << idx()
<< " in atom: '";
odocstringstream os;
- TexRow texrow(false);
- otexrowstream ots(os,texrow);
+ otexrowstream ots(os);
WriteStream wi(ots, false, true, WriteStream::wsDefault);
inset().asInsetMath()->write(wi);
lyxerr << to_utf8(os.str()) << endl;
//lyxerr << "idxUpDown triggered" << endl;
// try to find best position within this inset
if (!selection())
- setCursor(bruteFind2(*this, xo, yo));
+ setCursor(bruteFind(*this, xo, yo));
return true;
}
// Make sure that cur gets back whatever happened to dummy (Lgb)
operator=(dummy);
}
+ if (inTexted() && pos() && paragraph().isEnvSeparator(pos() - 1))
+ posBackward();
} else {
// if there is a selection, we stay out of any inset,
// and just jump to the right position:
// In case the master has no gui associated with it,
// the TocItem is not updated (part of bug 5699).
buffer()->tocBackend().updateItem(*this);
+
+ // If the last tracked change of the paragraph has just been
+ // deleted, then we need to recompute the buffer flag
+ // tracked_changes_present_.
+ if (inTexted() && paragraph().isChangeUpdateRequired())
+ disp_.forceChangesUpdate();
}