#include "Language.h"
#include "LyXFunc.h" // only for setMessage()
#include "LyXRC.h"
-#include "paragraph_funcs.h"
#include "Paragraph.h"
#include "ParIterator.h"
#include "Row.h"
return true;
}
-docstring parbreak(Paragraph const & par)
-{
- 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)
- os << '\n';
- return os.str();
-}
-
} // namespace anon
// object will be used again.
if (!disp_.dispatched()) {
LYXERR(Debug::DEBUG, "RESTORING OLD CURSOR!");
+ // We might have invalidated the cursor when removing an empty
+ // paragraph while the cursor could not be moved out the inset
+ // while we initially thought we could. This might happen when
+ // a multiline inset becomes an inline inset when the second
+ // paragraph is removed.
+ if (safe.pit() > safe.lastpit()) {
+ safe.pit() = safe.lastpit();
+ safe.pos() = safe.lastpos();
+ }
operator=(safe);
disp_.update(Update::None);
disp_.dispatched(false);
LASSERT(!empty(), /**/);
for (int i = depth() - 1; i >= 0; --i) {
int res = operator[](i).inset().currentMode();
- if (res != Inset::UNDECIDED_MODE)
+ bool locked_mode = operator[](i).inset().lockedMode();
+ // Also return UNDECIDED_MODE when the mode is locked,
+ // as in this case it is treated the same as TEXT_MODE
+ if (res != Inset::UNDECIDED_MODE || locked_mode)
return res;
}
return Inset::TEXT_MODE;
}
+void Cursor::setCursorToAnchor()
+{
+ if (selection())
+ setCursor(anchor_);
+}
+
bool Cursor::posBackward()
{
bool moved = (new_cur.pos() != pos()
|| new_cur.pit() != pit()
- || new_cur.boundary() != boundary());
+ || new_cur.boundary() != boundary()
+ || &new_cur.inset() != &inset());
if (moved) {
LYXERR(Debug::RTL, "moving to: " << new_cur.pos()
// 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();
+ pos_type const known_pos = boundary() && pos() > 0 ? 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"
if (par.isRTL(buf.params())) {
left_pos = -1;
right_pos = bidi.vis2log(row.pos());
- }
- else { // LTR paragraph
+ } else {
+ // LTR paragraph
right_pos = -1;
left_pos = bidi.vis2log(row.endpos() - 1);
}
// 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());
+ bool const 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:
-
+ // determine the other one:
if (known_pos_on_right) {
right_pos = known_pos;
// *visual* position of 'left_pos':
if (bidi.inRange(v_left_pos)
&& bidi.vis2log(v_left_pos) + 1 == row.endpos()
&& row.endpos() < lastpos()
- && par.isSeparator(bidi.vis2log(v_left_pos))) {
+ && 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;
// 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;
+ pos_type const 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
+ } 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 (bidi.inRange(v_right_pos)
&& bidi.vis2log(v_right_pos) + 1 == row.endpos()
&& row.endpos() < lastpos()
- && par.isSeparator(bidi.vis2log(v_right_pos))) {
+ && 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;
// 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;
+ pos_type const v_left_pos = bidi.log2vis(left_pos) - 1;
if (!bidi.inRange(v_left_pos))
left_pos = -1;
else
// 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.
+ // Another exception is when row.endpos() is 0.
- bool right_of_pos = false; // do we want to be to the right of pos?
-
+ // 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;
+ bool const right_of_pos = row.endpos() > 0
+ && pos() == row.endpos() - 1 && !par.isInset(pos());
// 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 =
+ bool const new_pos_is_RTL =
par.getFontSettings(buf.params(), pos()).isVisibleRightToLeft();
- if (new_pos_is_RTL == !right_of_pos) {
+ if (new_pos_is_RTL != right_of_pos) {
++pos();
boundary(true);
}
-
}
- }
- else { // move to rightmost position
+ } 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);
+ pos() = row.endpos() > 0 ? bidi.vis2log(row.endpos() - 1) : 0;
// Moving to the rightmost position in the row, the cursor should
// normally be placed to the *right* of the rightmost position.
// 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.
+ // Another exception is when row.endpos() is 0.
- 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;
+ // do we want to be to the left of pos?
+ // as explained above, if at last pos in row, stay to the left,
+ // unless the last position is the same as the first.
+ bool const left_of_pos = row.endpos() > 0
+ && pos() == row.endpos() - 1 && !par.isInset(pos());
// 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 =
+ bool const new_pos_is_RTL =
par.getFontSettings(buf.params(), pos()).isVisibleRightToLeft();
if (new_pos_is_RTL == left_of_pos) {
CursorSlice Cursor::anchor() const
{
+ if (!selection())
+ return top();
LASSERT(anchor_.depth() >= depth(), /**/);
CursorSlice normal = anchor_[depth() - 1];
if (depth() < anchor_.depth() && top() <= normal) {
<< pos() << ' ' << lastpos() << " in idx: " << idx()
<< " in atom: '";
odocstringstream os;
- WriteStream wi(os, false, true, false);
+ WriteStream wi(os, false, true, WriteStream::wsDefault);
inset().asInsetMath()->write(wi);
lyxerr << to_utf8(os.str()) << endl;
pos() = lastpos();
else
row = pm.pos2row(pos());
- if (atFirstOrLastRow(up))
+ if (atFirstOrLastRow(up)) {
+ // Is there a place for the cursor to go ? If yes, we
+ // can execute the DEPM, otherwise we should keep the
+ // paragraph to host the cursor.
+ Cursor dummy = *this;
+ bool valid_destination = false;
+ for(; dummy.depth(); dummy.pop())
+ if (!dummy.atFirstOrLastRow(up)) {
+ valid_destination = true;
+ break;
+ }
+
+ // will a next dispatch follow and if there is a new
+ // dispatch will it move the cursor out ?
+ if (depth() > 1 && valid_destination) {
+ // The cursor hasn't changed yet. This happens when
+ // you e.g. move out of an inset. And to give the
+ // DEPM the possibility of doing something we must
+ // provide it with two different cursors. (Lgb, vfr)
+ dummy = *this;
+ dummy.pos() = dummy.pos() == 0 ? dummy.lastpos() : 0;
+ dummy.pit() = dummy.pit() == 0 ? dummy.lastpit() : 0;
+
+ updateNeeded |= bv().checkDepm(dummy, *this);
+ updateTextTargetOffset();
+ }
return false;
+ }
// with and without selection are handled differently
if (!selection()) {
tm.editXY(*this, xo, yo + textRow().descent() + 1);
clearSelection();
- // This happens when you move out of an inset.
- // And to give the DEPM the possibility of doing
- // something we must provide it with two different
- // cursors. (Lgb)
+ // This happens when you move out of an inset.
+ // And to give the DEPM the possibility of doing
+ // something we must provide it with two different
+ // cursors. (Lgb)
Cursor dummy = *this;
if (dummy == old)
++dummy.pos();
if (bv().checkDepm(dummy, old)) {
updateNeeded = true;
- // Make sure that cur gets back whatever happened to dummy(Lgb)
+ // Make sure that cur gets back whatever happened to dummy(Lgb)
operator=(dummy);
}
} else {
}
+static docstring parbreak(InsetCode code)
+{
+ odocstringstream os;
+ os << '\n';
+ // only add blank line if we're not in an ERT or Listings inset
+ if (code != ERT_CODE && code != LISTINGS_CODE)
+ os << '\n';
+ return os.str();
+}
+
+
docstring Cursor::selectionAsString(bool with_label) const
{
if (!selection())
return docstring();
+ if (inMathed())
+ return cap::grabSelection(*this);
+
int const label = with_label
? AS_STR_LABEL | AS_STR_INSETS : AS_STR_INSETS;
- if (inTexted()) {
- 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();
-
- 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(startpos, endpos, label);
-
- // First paragraph in selection
- docstring result = pars[startpit].
- 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(0, par.size(), label)
- + parbreak(pars[pit]);
- }
+ 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();
- // Last paragraph in selection
- result += pars[endpit].asString(0, endpos, label);
+ pit_type const startpit = selBegin().pit();
+ pit_type const endpit = selEnd().pit();
+ size_t const startpos = selBegin().pos();
+ size_t const endpos = selEnd().pos();
- return result;
+ if (startpit == endpit)
+ return pars[startpit].asString(startpos, endpos, label);
+
+ // First paragraph in selection
+ docstring result = pars[startpit].
+ asString(startpos, pars[startpit].size(), label)
+ + parbreak(inset().lyxCode());
+
+ // The paragraphs in between (if any)
+ for (pit_type pit = startpit + 1; pit != endpit; ++pit) {
+ Paragraph const & par = pars[pit];
+ result += par.asString(0, par.size(), label)
+ + parbreak(inset().lyxCode());
}
- if (inMathed())
- return cap::grabSelection(*this);
+ // Last paragraph in selection
+ result += pars[endpit].asString(0, endpos, label);
- return docstring();
+ return result;
}
&& !cur.buffer()->isClean()
&& cur.inTexted() && old.inTexted()
&& cur.pit() != old.pit()) {
- old.paragraph().updateWords(old.top());
+ old.paragraph().updateWords();
}
// notify everything on top of the common part in old cursor,
{
Buffer const * master = buffer()->masterBuffer();
master->tocBackend().updateItem(*this);
+ if (master != buffer() && !master->hasGuiDelegate())
+ // In case the master has no gui associated with it,
+ // the TocItem is not updated (part of bug 5699).
+ buffer()->tocBackend().updateItem(*this);
}