*
* \author Alejandro Aguilar Sierra
* \author Alfredo Braunstein
+ * \author Dov Feldstern
* \author André Pönitz
* \author Stefan Schimanski
*
#include "insets/InsetText.h"
#include "mathed/InsetMath.h"
+#include "mathed/InsetMathBrace.h"
#include "mathed/InsetMathScript.h"
#include "mathed/MacroTable.h"
#include "mathed/MathData.h"
// 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());
void Cursor::push(Inset & p)
{
push_back(CursorSlice(p));
+ p.setBuffer(bv_->buffer());
}
bool Cursor::popBackward()
{
BOOST_ASSERT(!empty());
- //lyxerr << "Leaving inset from in front" << endl;
- inset().notifyCursorLeaves(*this);
if (depth() == 1)
return false;
pop();
BOOST_ASSERT(!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();
}
+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());
+
+ // 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());
{
cell().insert(pos(), t);
++pos();
+ inset().setBuffer(bv_->buffer());
+ inset().initView();
}
}
-void Cursor::insert(Inset * inset)
+void Cursor::insert(Inset * inset0)
{
+ BOOST_ASSERT(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();
+ }
}
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
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());
}
}
return docstring();
if (inTexted()) {
- Buffer const & buffer = bv().buffer();
ParagraphList const & pars = text()->paragraphs();
// should be const ...
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;
}
}
-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()
void Cursor::checkBufferStructure()
{
- if (paragraph().layout()->toclevel == Layout::NOT_IN_TOC)
+ if (paragraph().layout().toclevel == Layout::NOT_IN_TOC)
return;
Buffer const * master = buffer().masterBuffer();
master->tocBackend().updateItem(ParConstIterator(*this));