*
* \author Alejandro Aguilar Sierra
* \author Alfredo Braunstein
+ * \author Dov Feldstern
* \author André Pönitz
* \author Stefan Schimanski
*
#include "DispatchResult.h"
#include "Encoding.h"
#include "Font.h"
+#include "FuncCode.h"
#include "FuncRequest.h"
#include "Language.h"
-#include "lfuns.h"
#include "LyXFunc.h" // only for setMessage()
#include "LyXRC.h"
#include "paragraph_funcs.h"
#include "TextMetrics.h"
#include "TocBackend.h"
+#include "support/lassert.h"
#include "support/debug.h"
#include "support/docstream.h"
#include "insets/InsetText.h"
#include "mathed/InsetMath.h"
+#include "mathed/InsetMathBrace.h"
#include "mathed/InsetMathScript.h"
#include "mathed/MacroTable.h"
#include "mathed/MathData.h"
#include "mathed/MathMacro.h"
-#include <boost/assert.hpp>
#include <boost/bind.hpp>
#include <sstream>
bool bruteFind(Cursor & cursor,
int x, int y, int xlow, int xhigh, int ylow, int yhigh)
{
- BOOST_ASSERT(!cursor.empty());
+ LASSERT(!cursor.empty(), return false);
Inset & inset = cursor[0].inset();
BufferView & bv = cursor.bv();
docstring parbreak(Paragraph const & par)
{
- odocstringstream ods;
- ods << '\n';
+ odocstringstream os;
+ os << '\n';
// only add blank line if we're not in an ERT or Listings inset
if (par.ownerCode() != ERT_CODE
&& par.ownerCode() != LISTINGS_CODE)
- ods << '\n';
- return ods.str();
+ os << '\n';
+ return os.str();
}
} // namespace anon
{
clear();
push_back(CursorSlice(inset));
- anchor_ = DocIterator(inset);
+ anchor_ = doc_iterator_begin(inset);
+ anchor_.clear();
clearTargetX();
selection_ = false;
mark_ = false;
// store some values to be used inside of the handlers
beforeDispatchCursor_ = *this;
- for (; depth(); pop()) {
+ for (; depth(); pop(), boundary(false)) {
LYXERR(Debug::DEBUG, "Cursor::dispatch: cmd: "
<< cmd0 << endl << *this);
- BOOST_ASSERT(pos() <= lastpos());
- BOOST_ASSERT(idx() <= lastidx());
- BOOST_ASSERT(pit() <= lastpit());
+ LASSERT(pos() <= lastpos(), /**/);
+ LASSERT(idx() <= lastidx(), /**/);
+ LASSERT(pit() <= lastpit(), /**/);
// The common case is 'LFUN handled, need update', so make the
// LFUN handler's life easier by assuming this as default value.
BufferView & Cursor::bv() const
{
- BOOST_ASSERT(bv_);
+ LASSERT(bv_, /**/);
return *bv_;
}
Buffer & Cursor::buffer() const
{
- BOOST_ASSERT(bv_);
+ LASSERT(bv_, /**/);
return bv_->buffer();
}
void Cursor::pop()
{
- BOOST_ASSERT(depth() >= 1);
+ LASSERT(depth() >= 1, /**/);
pop_back();
}
void Cursor::push(Inset & p)
{
push_back(CursorSlice(p));
+ p.setBuffer(bv_->buffer());
}
void Cursor::pushBackward(Inset & p)
{
- BOOST_ASSERT(!empty());
+ LASSERT(!empty(), /**/);
//lyxerr << "Entering inset " << t << " front" << endl;
push(p);
p.idxFirst(*this);
bool Cursor::popBackward()
{
- BOOST_ASSERT(!empty());
- //lyxerr << "Leaving inset from in front" << endl;
- inset().notifyCursorLeaves(*this);
+ LASSERT(!empty(), /**/);
if (depth() == 1)
return false;
pop();
bool Cursor::popForward()
{
- BOOST_ASSERT(!empty());
+ LASSERT(!empty(), /**/);
//lyxerr << "Leaving inset from in back" << endl;
const pos_type lp = (depth() > 1) ? (*this)[depth() - 2].lastpos() : 0;
- inset().notifyCursorLeaves(*this);
if (depth() == 1)
return false;
pop();
int Cursor::currentMode()
{
- BOOST_ASSERT(!empty());
+ LASSERT(!empty(), /**/);
for (int i = depth() - 1; i >= 0; --i) {
int res = operator[](i).inset().currentMode();
if (res != Inset::UNDECIDED_MODE)
{
CursorSlice const & cs = innerTextSlice();
ParagraphMetrics const & pm = bv().parMetrics(cs.text(), cs.pit());
- BOOST_ASSERT(!pm.rows().empty());
+ LASSERT(!pm.rows().empty(), /**/);
return pm.getRow(pos(), boundary());
}
}
+bool Cursor::posVisRight(bool skip_inset)
+{
+ Cursor new_cur = *this; // where we will move to
+ pos_type left_pos; // position visually left of current cursor
+ pos_type right_pos; // position visually right of current cursor
+ bool new_pos_is_RTL; // is new position we're moving to RTL?
+
+ getSurroundingPos(left_pos, right_pos);
+
+ LYXERR(Debug::RTL, left_pos <<"|"<< right_pos << " (pos: "<< pos() <<")");
+
+ // Are we at an inset?
+ new_cur.pos() = right_pos;
+ new_cur.boundary(false);
+ if (!skip_inset &&
+ text()->checkAndActivateInsetVisual(new_cur, right_pos >= pos(), false)) {
+ // we actually move the cursor at the end of this function, for now
+ // we just keep track of the new position in new_cur...
+ LYXERR(Debug::RTL, "entering inset at: " << new_cur.pos());
+ }
+
+ // Are we already at rightmost pos in row?
+ else if (text()->empty() || right_pos == -1) {
+
+ new_cur = *this;
+ if (!new_cur.posVisToNewRow(false)) {
+ LYXERR(Debug::RTL, "not moving!");
+ return false;
+ }
+
+ // we actually move the cursor at the end of this function, for now
+ // just keep track of the new position in new_cur...
+ LYXERR(Debug::RTL, "right edge, moving: " << int(new_cur.pit()) << ","
+ << int(new_cur.pos()) << "," << (new_cur.boundary() ? 1 : 0));
+
+ }
+ // normal movement to the right
+ else {
+ new_cur = *this;
+ // Recall, if the cursor is at position 'x', that means *before*
+ // the character at position 'x'. In RTL, "before" means "to the
+ // right of", in LTR, "to the left of". So currently our situation
+ // is this: the position to our right is 'right_pos' (i.e., we're
+ // currently to the left of 'right_pos'). In order to move to the
+ // right, it depends whether or not the character at 'right_pos' is RTL.
+ new_pos_is_RTL = paragraph().getFontSettings(
+ bv().buffer().params(), right_pos).isVisibleRightToLeft();
+ // If the character at 'right_pos' *is* LTR, then in order to move to
+ // the right of it, we need to be *after* 'right_pos', i.e., move to
+ // position 'right_pos' + 1.
+ if (!new_pos_is_RTL) {
+ new_cur.pos() = right_pos + 1;
+ // set the boundary to true in two situations:
+ if (
+ // 1. if new_pos is now lastpos (which means that we're moving
+ // right to the end of an LTR chunk which is at the end of an
+ // RTL paragraph);
+ new_cur.pos() == lastpos()
+ // 2. if the position *after* right_pos is RTL (we want to be
+ // *after* right_pos, not before right_pos + 1!)
+ || paragraph().getFontSettings(bv().buffer().params(),
+ new_cur.pos()).isVisibleRightToLeft()
+ )
+ new_cur.boundary(true);
+ else // set the boundary to false
+ new_cur.boundary(false);
+ }
+ // Otherwise (if the character at position 'right_pos' is RTL), then
+ // moving to the right of it is as easy as setting the new position
+ // to 'right_pos'.
+ else {
+ new_cur.pos() = right_pos;
+ new_cur.boundary(false);
+ }
+
+ }
+
+ bool moved = (new_cur.pos() != pos()
+ || new_cur.pit() != pit()
+ || new_cur.boundary() != boundary());
+
+ if (moved) {
+ LYXERR(Debug::RTL, "moving to: " << new_cur.pos()
+ << (new_cur.boundary() ? " (boundary)" : ""));
+ *this = new_cur;
+ }
+
+ return moved;
+}
+
+
+bool Cursor::posVisLeft(bool skip_inset)
+{
+ Cursor new_cur = *this; // where we will move to
+ pos_type left_pos; // position visually left of current cursor
+ pos_type right_pos; // position visually right of current cursor
+ bool new_pos_is_RTL; // is new position we're moving to RTL?
+
+ getSurroundingPos(left_pos, right_pos);
+
+ LYXERR(Debug::RTL, left_pos <<"|"<< right_pos << " (pos: "<< pos() <<")");
+
+ // Are we at an inset?
+ new_cur.pos() = left_pos;
+ new_cur.boundary(false);
+ if (!skip_inset &&
+ text()->checkAndActivateInsetVisual(new_cur, left_pos >= pos(), true)) {
+ // we actually move the cursor at the end of this function, for now
+ // we just keep track of the new position in new_cur...
+ LYXERR(Debug::RTL, "entering inset at: " << new_cur.pos());
+ }
+
+ // Are we already at leftmost pos in row?
+ else if (text()->empty() || left_pos == -1) {
+
+ new_cur = *this;
+ if (!new_cur.posVisToNewRow(true)) {
+ LYXERR(Debug::RTL, "not moving!");
+ return false;
+ }
+
+ // we actually move the cursor at the end of this function, for now
+ // just keep track of the new position in new_cur...
+ LYXERR(Debug::RTL, "left edge, moving: " << int(new_cur.pit()) << ","
+ << int(new_cur.pos()) << "," << (new_cur.boundary() ? 1 : 0));
+
+ }
+ // normal movement to the left
+ else {
+ new_cur = *this;
+ // Recall, if the cursor is at position 'x', that means *before*
+ // the character at position 'x'. In RTL, "before" means "to the
+ // right of", in LTR, "to the left of". So currently our situation
+ // is this: the position to our left is 'left_pos' (i.e., we're
+ // currently to the right of 'left_pos'). In order to move to the
+ // left, it depends whether or not the character at 'left_pos' is RTL.
+ new_pos_is_RTL = paragraph().getFontSettings(
+ bv().buffer().params(), left_pos).isVisibleRightToLeft();
+ // If the character at 'left_pos' *is* RTL, then in order to move to
+ // the left of it, we need to be *after* 'left_pos', i.e., move to
+ // position 'left_pos' + 1.
+ if (new_pos_is_RTL) {
+ new_cur.pos() = left_pos + 1;
+ // set the boundary to true in two situations:
+ if (
+ // 1. if new_pos is now lastpos (which means that we're moving left
+ // to the end of an RTL chunk which is at the end of an LTR
+ // paragraph);
+ new_cur.pos() == lastpos()
+ // 2. if the position *after* left_pos is not RTL (we want to be
+ // *after* left_pos, not before left_pos + 1!)
+ || !paragraph().getFontSettings(bv().buffer().params(),
+ new_cur.pos()).isVisibleRightToLeft()
+ )
+ new_cur.boundary(true);
+ else // set the boundary to false
+ new_cur.boundary(false);
+ }
+ // Otherwise (if the character at position 'left_pos' is LTR), then
+ // moving to the left of it is as easy as setting the new position
+ // to 'left_pos'.
+ else {
+ new_cur.pos() = left_pos;
+ new_cur.boundary(false);
+ }
+
+ }
+
+ bool moved = (new_cur.pos() != pos()
+ || new_cur.pit() != pit()
+ || new_cur.boundary() != boundary());
+
+ if (moved) {
+ LYXERR(Debug::RTL, "moving to: " << new_cur.pos()
+ << (new_cur.boundary() ? " (boundary)" : ""));
+ *this = new_cur;
+ }
+
+ return moved;
+}
+
+
+void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos)
+{
+ // preparing bidi tables
+ Paragraph const & par = paragraph();
+ Buffer const & buf = buffer();
+ Row const & row = textRow();
+ Bidi bidi;
+ bidi.computeTables(par, buf, row);
+
+ LYXERR(Debug::RTL, "bidi: " << row.pos() << "--" << row.endpos());
+
+ // The cursor is painted *before* the character at pos(), or, if 'boundary'
+ // is true, *after* the character at (pos() - 1). So we already have one
+ // known position around the cursor:
+ pos_type known_pos = boundary() ? pos() - 1 : pos();
+
+ // edge case: if we're at the end of the paragraph, things are a little
+ // different (because lastpos is a position which does not really "exist"
+ // --- there's no character there yet).
+ if (known_pos == lastpos()) {
+ if (par.isRTL(buf.params())) {
+ left_pos = -1;
+ right_pos = bidi.vis2log(row.pos());
+ }
+ else { // LTR paragraph
+ right_pos = -1;
+ left_pos = bidi.vis2log(row.endpos() - 1);
+ }
+ return;
+ }
+
+ // Whether 'known_pos' is to the left or to the right of the cursor depends
+ // on whether it is an RTL or LTR character...
+ bool const cur_is_RTL =
+ par.getFontSettings(buf.params(), known_pos).isVisibleRightToLeft();
+ // ... in the following manner:
+ // For an RTL character, "before" means "to the right" and "after" means
+ // "to the left"; and for LTR, it's the reverse. So, 'known_pos' is to the
+ // right of the cursor if (RTL && boundary) or (!RTL && !boundary):
+ bool known_pos_on_right = (cur_is_RTL == boundary());
+
+ // So we now know one of the positions surrounding the cursor. Let's
+ // determine the other one:
+
+ if (known_pos_on_right) {
+ right_pos = known_pos;
+ // *visual* position of 'left_pos':
+ pos_type v_left_pos = bidi.log2vis(right_pos) - 1;
+ // If the position we just identified as 'left_pos' is a "skipped
+ // separator" (a separator which is at the logical end of a row,
+ // except for the last row in a paragraph; such separators are not
+ // painted, so they "are not really there"; note that in bidi text,
+ // such a separator could appear visually in the middle of a row),
+ // set 'left_pos' to the *next* position to the left.
+ if (bidi.inRange(v_left_pos)
+ && bidi.vis2log(v_left_pos) + 1 == row.endpos()
+ && row.endpos() < lastpos()
+ && par.isSeparator(bidi.vis2log(v_left_pos))) {
+ --v_left_pos;
+ }
+ // calculate the logical position of 'left_pos', if in row
+ if (!bidi.inRange(v_left_pos))
+ left_pos = -1;
+ else
+ left_pos = bidi.vis2log(v_left_pos);
+ // If the position we identified as 'right_pos' is a "skipped
+ // separator", set 'right_pos' to the *next* position to the right.
+ if (right_pos + 1 == row.endpos() && row.endpos() < lastpos()
+ && par.isSeparator(right_pos)) {
+ pos_type v_right_pos = bidi.log2vis(right_pos) + 1;
+ if (!bidi.inRange(v_right_pos))
+ right_pos = -1;
+ else
+ right_pos = bidi.vis2log(v_right_pos);
+ }
+ }
+ else { // known_pos is on the left
+ left_pos = known_pos;
+ // *visual* position of 'right_pos'
+ pos_type v_right_pos = bidi.log2vis(left_pos) + 1;
+ // If the position we just identified as 'right_pos' is a "skipped
+ // separator", set 'right_pos' to the *next* position to the right.
+ if (bidi.inRange(v_right_pos)
+ && bidi.vis2log(v_right_pos) + 1 == row.endpos()
+ && row.endpos() < lastpos()
+ && par.isSeparator(bidi.vis2log(v_right_pos))) {
+ ++v_right_pos;
+ }
+ // calculate the logical position of 'right_pos', if in row
+ if (!bidi.inRange(v_right_pos))
+ right_pos = -1;
+ else
+ right_pos = bidi.vis2log(v_right_pos);
+ // If the position we identified as 'left_pos' is a "skipped
+ // separator", set 'left_pos' to the *next* position to the left.
+ if (left_pos + 1 == row.endpos() && row.endpos() < lastpos()
+ && par.isSeparator(left_pos)) {
+ pos_type v_left_pos = bidi.log2vis(left_pos) - 1;
+ if (!bidi.inRange(v_left_pos))
+ left_pos = -1;
+ else
+ left_pos = bidi.vis2log(v_left_pos);
+ }
+ }
+ return;
+}
+
+
+bool Cursor::posVisToNewRow(bool movingLeft)
+{
+ Paragraph const & par = paragraph();
+ Buffer const & buf = buffer();
+ Row const & row = textRow();
+ bool par_is_LTR = !par.isRTL(buf.params());
+
+ // Inside a table, determining whether to move to the next or previous row
+ // should be done based on the table's direction.
+ int s = depth() - 1;
+ if (s >= 1 && (*this)[s].inset().asInsetTabular()) {
+ par_is_LTR = !(*this)[s].inset().asInsetTabular()->isRightToLeft(*this);
+ LYXERR(Debug::RTL, "Inside table! par_is_LTR=" << (par_is_LTR ? 1 : 0));
+ }
+
+ // if moving left in an LTR paragraph or moving right in an RTL one,
+ // move to previous row
+ if (par_is_LTR == movingLeft) {
+ if (row.pos() == 0) { // we're at first row in paragraph
+ if (pit() == 0) // no previous paragraph! don't move
+ return false;
+ // move to last pos in previous par
+ --pit();
+ pos() = lastpos();
+ boundary(false);
+ } else { // move to previous row in this par
+ pos() = row.pos() - 1; // this is guaranteed to be in previous row
+ boundary(false);
+ }
+ }
+ // if moving left in an RTL paragraph or moving right in an LTR one,
+ // move to next row
+ else {
+ if (row.endpos() == lastpos()) { // we're at last row in paragraph
+ if (pit() == lastpit()) // last paragraph! don't move
+ return false;
+ // move to first row in next par
+ ++pit();
+ pos() = 0;
+ boundary(false);
+ } else { // move to next row in this par
+ pos() = row.endpos();
+ boundary(false);
+ }
+ }
+
+ // make sure we're at left-/right-most pos in new row
+ posVisToRowExtremity(!movingLeft);
+
+ return true;
+}
+
+
+void Cursor::posVisToRowExtremity(bool left)
+{
+ // prepare bidi tables
+ Paragraph const & par = paragraph();
+ Buffer const & buf = buffer();
+ Row const & row = textRow();
+ Bidi bidi;
+ bidi.computeTables(par, buf, row);
+
+ LYXERR(Debug::RTL, "entering extremity: " << pit() << "," << pos() << ","
+ << (boundary() ? 1 : 0));
+
+ if (left) { // move to leftmost position
+ // if this is an RTL paragraph, and we're at the last row in the
+ // paragraph, move to lastpos
+ if (par.isRTL(buf.params()) && row.endpos() == lastpos())
+ pos() = lastpos();
+ else {
+ pos() = bidi.vis2log(row.pos());
+
+ // Moving to the leftmost position in the row, the cursor should
+ // normally be placed to the *left* of the leftmost position.
+ // A very common exception, though, is if the leftmost character
+ // also happens to be the separator at the (logical) end of the row
+ // --- in this case, the separator is positioned beyond the left
+ // margin, and we don't want to move the cursor there (moving to
+ // the left of the separator is equivalent to moving to the next
+ // line). So, in this case we actually want to place the cursor
+ // to the *right* of the leftmost position (the separator).
+ // Another exception is if we're moving to the logically last
+ // position in the row, which is *not* a separator: this means
+ // that the entire row has no separators (if there were any, the
+ // row would have been broken there); and therefore in this case
+ // we also move to the *right* of the last position (this indicates
+ // to the user that there is no space after this position, and is
+ // consistent with the behavior in the middle of a row --- moving
+ // right or left moves to the next/previous character; if we were
+ // to move to the *left* of this position, that would simulate
+ // a separator which is not really there!).
+ // Finally, there is an exception to the previous exception: if
+ // this non-separator-but-last-position-in-row is an inset, then
+ // we *do* want to stay to the left of it anyway: this is the
+ // "boundary" which we simulate at insets.
+
+ bool right_of_pos = false; // do we want to be to the right of pos?
+
+ // as explained above, if at last pos in row, stay to the right
+ if ((pos() == row.endpos() - 1) && !par.isInset(pos()))
+ right_of_pos = true;
+
+ // Now we know if we want to be to the left or to the right of pos,
+ // let's make sure we are where we want to be.
+ bool new_pos_is_RTL =
+ par.getFontSettings(buf.params(), pos()).isVisibleRightToLeft();
+
+ if (new_pos_is_RTL == !right_of_pos) {
+ ++pos();
+ boundary(true);
+ }
+
+ }
+ }
+ else { // move to rightmost position
+ // if this is an LTR paragraph, and we're at the last row in the
+ // paragraph, move to lastpos
+ if (!par.isRTL(buf.params()) && row.endpos() == lastpos())
+ pos() = lastpos();
+ else {
+ pos() = bidi.vis2log(row.endpos() - 1);
+
+ // Moving to the rightmost position in the row, the cursor should
+ // normally be placed to the *right* of the rightmost position.
+ // A very common exception, though, is if the rightmost character
+ // also happens to be the separator at the (logical) end of the row
+ // --- in this case, the separator is positioned beyond the right
+ // margin, and we don't want to move the cursor there (moving to
+ // the right of the separator is equivalent to moving to the next
+ // line). So, in this case we actually want to place the cursor
+ // to the *left* of the rightmost position (the separator).
+ // Another exception is if we're moving to the logically last
+ // position in the row, which is *not* a separator: this means
+ // that the entire row has no separators (if there were any, the
+ // row would have been broken there); and therefore in this case
+ // we also move to the *left* of the last position (this indicates
+ // to the user that there is no space after this position, and is
+ // consistent with the behavior in the middle of a row --- moving
+ // right or left moves to the next/previous character; if we were
+ // to move to the *right* of this position, that would simulate
+ // a separator which is not really there!).
+ // Finally, there is an exception to the previous exception: if
+ // this non-separator-but-last-position-in-row is an inset, then
+ // we *do* want to stay to the right of it anyway: this is the
+ // "boundary" which we simulate at insets.
+
+ bool left_of_pos = false; // do we want to be to the left of pos?
+
+ // as explained above, if at last pos in row, stay to the left
+ if ((pos() == row.endpos() - 1) && !par.isInset(pos()))
+ left_of_pos = true;
+
+ // Now we know if we want to be to the left or to the right of pos,
+ // let's make sure we are where we want to be.
+ bool new_pos_is_RTL =
+ par.getFontSettings(buf.params(), pos()).isVisibleRightToLeft();
+
+ if (new_pos_is_RTL == left_of_pos) {
+ ++pos();
+ boundary(true);
+ }
+ }
+ }
+ LYXERR(Debug::RTL, "leaving extremity: " << pit() << "," << pos() << ","
+ << (boundary() ? 1 : 0));
+}
+
+
CursorSlice Cursor::anchor() const
{
- BOOST_ASSERT(anchor_.depth() >= depth());
+ LASSERT(anchor_.depth() >= depth(), /**/);
CursorSlice normal = anchor_[depth() - 1];
if (depth() < anchor_.depth() && top() <= normal) {
// anchor is behind cursor -> move anchor behind the inset
{
if (!selection())
return *this;
- DocIterator di = (anchor() < top() ? anchor_ : *this);
+
+ DocIterator di;
+ // FIXME: This is a work-around for the problem that
+ // CursorSlice doesn't keep track of the boundary.
+ if (anchor() == top())
+ di = anchor_.boundary() > boundary() ? anchor_ : *this;
+ else
+ di = anchor() < top() ? anchor_ : *this;
di.resize(depth());
return di;
}
{
if (!selection())
return *this;
- DocIterator di = (anchor() > top() ? anchor_ : *this);
+
+ DocIterator di;
+ // FIXME: This is a work-around for the problem that
+ // CursorSlice doesn't keep track of the boundary.
+ if (anchor() == top())
+ di = anchor_.boundary() < boundary() ? anchor_ : *this;
+ else
+ di = anchor() > top() ? anchor_ : *this;
+
if (di.depth() > depth()) {
di.resize(depth());
++di.pos();
void Cursor::setSelection()
{
- selection() = true;
+ setSelection(true);
// A selection with no contents is not a selection
// FIXME: doesnt look ok
- if (pit() == anchor().pit() && pos() == anchor().pos())
- selection() = false;
+ if (idx() == anchor().idx() &&
+ pit() == anchor().pit() &&
+ pos() == anchor().pos())
+ setSelection(false);
}
void Cursor::setSelection(DocIterator const & where, int n)
{
setCursor(where);
- selection() = true;
+ setSelection(true);
anchor_ = where;
pos() += n;
}
void Cursor::clearSelection()
{
- selection() = false;
- mark() = false;
+ setSelection(false);
+ setMark(false);
resetAnchor();
}
cap::saveSelection(*this);
resetAnchor();
- selection() = sel;
+ setSelection(sel);
return true;
}
{
cell().insert(pos(), t);
++pos();
+ inset().setBuffer(bv_->buffer());
+ inset().initView();
}
void Cursor::insert(char_type c)
{
//lyxerr << "Cursor::insert char '" << c << "'" << endl;
- BOOST_ASSERT(!empty());
+ LASSERT(!empty(), /**/);
if (inMathed()) {
cap::selClearOrDel(*this);
insert(new InsetMathChar(c));
}
-void Cursor::insert(Inset * inset)
+void Cursor::insert(Inset * inset0)
{
+ LASSERT(inset0, /**/);
if (inMathed())
- insert(MathAtom(inset));
- else
- text()->insertInset(*this, inset);
+ insert(MathAtom(inset0));
+ else {
+ text()->insertInset(*this, inset0);
+ inset0->setBuffer(bv_->buffer());
+ inset0->initView();
+ }
}
// let's require two backspaces for 'big stuff' and
// highlight on the first
resetAnchor();
- selection() = true;
+ setSelection(true);
--pos();
} else {
--pos();
// 'clever' UI hack: only erase large items if previously slected
if (pos() != lastpos() && nextAtom()->nargs() > 0) {
resetAnchor();
- selection() = true;
+ setSelection(true);
++pos();
} else {
plainErase();
return false;
InsetMathUnknown * p = activeMacro();
p->finalize();
+ MathData selection;
+ asArray(p->selection(), selection);
docstring const s = p->name();
--pos();
cell().erase(pos());
if (in && in->interpretString(*this, s))
return true;
MathAtom atom = createInsetMath(name);
+
+ // try to put argument into macro, if we just inserted a macro
+ bool macroArg = false;
MathMacro * atomAsMacro = atom.nucleus()->asMacro();
if (atomAsMacro) {
- // make non-greedy, i.e. don't eat parameters from the right
- atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT);
+ // macros here are still unfolded (in init mode in fact). So
+ // we have to resolve the macro here manually and check its arity
+ // to put the selection behind it if arity > 0.
+ MacroData const * data = buffer().getMacro(atomAsMacro->name());
+ if (selection.size() > 0 && data && data->numargs() - data->optionals() > 0) {
+ macroArg = true;
+ atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 1);
+ } else
+ // non-greedy case. Do not touch the arguments behind
+ atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 0);
}
+
+ // insert remembered selection into first argument of a non-macro
+ else if (atom.nucleus()->nargs() > 0)
+ atom.nucleus()->cell(0).append(selection);
+
plainInsert(atom);
+
+ // finally put the macro argument behind, if needed
+ if (macroArg) {
+ if (selection.size() > 1)
+ plainInsert(MathAtom(new InsetMathBrace(selection)));
+ else
+ insert(selection);
+ }
+
return true;
}
}
+InsetMathUnknown const * Cursor::activeMacro() const
+{
+ return inMacroMode() ? prevAtom().nucleus()->asUnknownInset() : 0;
+}
+
+
void Cursor::pullArg()
{
// FIXME: Look here
<< pos() << ' ' << lastpos() << " in idx: " << idx()
<< " in atom: '";
odocstringstream os;
- WriteStream wi(os, false, true);
+ WriteStream wi(os, false, true, false);
inset().asInsetMath()->write(wi);
lyxerr << to_utf8(os.str()) << endl;
pos() = lastpos();
bool Cursor::upDownInText(bool up, bool & updateNeeded)
{
- BOOST_ASSERT(text());
+ LASSERT(text(), /**/);
// where are we?
int xo = 0;
if (pit() + 1 >= int(text()->paragraphs().size()) &&
row + 1 >= int(pm.rows().size()))
return false;
- }
+ }
// with and without selection are handled differently
if (!selection()) {
top().pos() = min(tm.x2pos(pit(), row - 1, xo), top().lastpos());
} else if (pit() > 0) {
--pit();
- ParagraphMetrics const & pmcur = bv_->parMetrics(text(), pit());
+ TextMetrics & tm = bv_->textMetrics(text());
+ if (!tm.contains(pit()))
+ tm.newParMetricsUp();
+ ParagraphMetrics const & pmcur = tm.parMetrics(pit());
top().pos() = min(tm.x2pos(pit(), pmcur.rows().size() - 1, xo), top().lastpos());
}
} else {
top().pos() = min(tm.x2pos(pit(), row + 1, xo), top().lastpos());
} else if (pit() + 1 < int(text()->paragraphs().size())) {
++pit();
+ TextMetrics & tm = bv_->textMetrics(text());
+ if (!tm.contains(pit()))
+ tm.newParMetricsDown();
top().pos() = min(tm.x2pos(pit(), 0, xo), top().lastpos());
}
}
safe = cap::grabAndEraseSelection(*this);
}
+ recordUndoInset();
+
if (lastpos() != 0) {
// something left in the cell
if (pos() == 0) {
}
} else {
// nothing left in the cell
- pullArg();
+ popBackward();
plainErase();
+ resetAnchor();
}
insert(safe);
}
}
-docstring Cursor::selectionAsString(bool label) const
+docstring Cursor::selectionAsString(bool with_label) const
{
if (!selection())
return docstring();
+ int const label = with_label
+ ? AS_STR_LABEL | AS_STR_INSETS : AS_STR_INSETS;
+
if (inTexted()) {
- Buffer const & buffer = bv().buffer();
+ idx_type const startidx = selBegin().idx();
+ idx_type const endidx = selEnd().idx();
+ if (startidx != endidx) {
+ // multicell selection
+ InsetTabular * table = inset().asInsetTabular();
+ LASSERT(table, return docstring());
+ return table->asString(startidx, endidx);
+ }
+
ParagraphList const & pars = text()->paragraphs();
- // should be const ...
- pit_type startpit = selBegin().pit();
- pit_type endpit = selEnd().pit();
+ pit_type const startpit = selBegin().pit();
+ pit_type const endpit = selEnd().pit();
size_t const startpos = selBegin().pos();
size_t const endpos = selEnd().pos();
if (startpit == endpit)
- return pars[startpit].asString(buffer, startpos, endpos, label);
+ return pars[startpit].asString(startpos, endpos, label);
// First paragraph in selection
docstring result = pars[startpit].
- asString(buffer, startpos, pars[startpit].size(), label)
+ asString(startpos, pars[startpit].size(), label)
+ parbreak(pars[startpit]);
// The paragraphs in between (if any)
for (pit_type pit = startpit + 1; pit != endpit; ++pit) {
Paragraph const & par = pars[pit];
- result += par.asString(buffer, 0, par.size(), label)
+ result += par.asString(0, par.size(), label)
+ parbreak(pars[pit]);
}
// Last paragraph in selection
- result += pars[endpit].asString(buffer, 0, endpos, label);
+ result += pars[endpit].asString(0, endpos, label);
return result;
}
}
-docstring Cursor::currentState()
+docstring Cursor::currentState() const
{
if (inMathed()) {
odocstringstream os;
}
-docstring Cursor::getPossibleLabel()
+docstring Cursor::getPossibleLabel() const
{
return inMathed() ? from_ascii("eq:") : text()->getPossibleLabel(*this);
}
{
if (DocIterator::fixIfBroken()) {
clearSelection();
- resetAnchor();
return true;
}
return false;
}
-bool notifyCursorLeaves(DocIterator const & old, Cursor & cur)
+bool notifyCursorLeaves(Cursor const & old, Cursor & cur)
{
// find inset in common
size_type i;
for (i = 0; i < old.depth() && i < cur.depth(); ++i) {
- if (&old.inset() != &cur.inset())
+ if (&old[i].inset() != &cur[i].inset())
break;
}
+
+ // update words if we just moved to another paragraph
+ if (i == old.depth() && i == cur.depth()
+ && !cur.buffer().isClean()
+ && cur.inTexted() && old.inTexted()
+ && cur.pit() != old.pit()) {
+ old.paragraph().updateWords(old.top());
+ return false;
+ }
// notify everything on top of the common part in old cursor,
// but stop if the inset claims the cursor to be invalid now
- for (; i < old.depth(); ++i) {
- if (old[i].inset().notifyCursorLeaves(cur))
+ for (; i < old.depth(); ++i) {
+ Cursor insetPos = old;
+ insetPos.cutOff(i);
+ if (old[i].inset().notifyCursorLeaves(insetPos, cur))
return true;
}
// get font
BufferParams const & bufparams = buffer().params();
current_font = par.getFontSettings(bufparams, cpos);
- real_current_font = tm.getDisplayFont(cpit, cpos);
+ real_current_font = tm.displayFont(cpit, cpos);
// special case for paragraph end
if (cs.pos() == lastpos()
return false;
// Set cursor
setCursor(dit);
- selection() = false;
- resetAnchor();
+ clearSelection();
fixIfBroken();
return true;
}
return false;
// Set cursor
setCursor(dit);
- selection() = false;
- resetAnchor();
+ clearSelection();
fixIfBroken();
return true;
}
-void Cursor::finishUndo()
+void Cursor::finishUndo() const
{
bv_->buffer().undo().finishUndo();
}
-void Cursor::recordUndo(UndoKind kind, pit_type from, pit_type to)
+void Cursor::beginUndoGroup() const
+{
+ bv_->buffer().undo().beginUndoGroup();
+}
+
+
+void Cursor::endUndoGroup() const
+{
+ bv_->buffer().undo().endUndoGroup();
+}
+
+
+void Cursor::recordUndo(UndoKind kind, pit_type from, pit_type to) const
{
bv_->buffer().undo().recordUndo(*this, kind, from, to);
}
-void Cursor::recordUndo(UndoKind kind, pit_type from)
+void Cursor::recordUndo(UndoKind kind, pit_type from) const
{
bv_->buffer().undo().recordUndo(*this, kind, from);
}
-void Cursor::recordUndo(UndoKind kind)
+void Cursor::recordUndo(UndoKind kind) const
{
bv_->buffer().undo().recordUndo(*this, kind);
}
-void Cursor::recordUndoInset(UndoKind kind)
+void Cursor::recordUndoInset(UndoKind kind) const
{
bv_->buffer().undo().recordUndoInset(*this, kind);
}
-void Cursor::recordUndoFullDocument()
+void Cursor::recordUndoFullDocument() const
{
bv_->buffer().undo().recordUndoFullDocument(*this);
}
-void Cursor::recordUndoSelection()
+void Cursor::recordUndoSelection() const
{
- bv_->buffer().undo().recordUndo(*this, ATOMIC_UNDO,
- selBegin().pit(), selEnd().pit());
+ if (inMathed()) {
+ if (cap::multipleCellsSelected(*this))
+ recordUndoInset();
+ else
+ recordUndo();
+ } else
+ bv_->buffer().undo().recordUndo(*this, ATOMIC_UNDO,
+ selBegin().pit(), selEnd().pit());
}
void Cursor::checkBufferStructure()
{
- if (paragraph().layout()->toclevel == Layout::NOT_IN_TOC)
- return;
Buffer const * master = buffer().masterBuffer();
- master->tocBackend().updateItem(ParConstIterator(*this));
- master->structureChanged();
+ if (master->tocBackend().updateItem(*this))
+ master->structureChanged();
}