mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-12-22 13:18:28 +00:00
visual mode for bidi cursor movement
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22929 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
a9770a18cd
commit
11a6b3c4c7
271
src/Cursor.cpp
271
src/Cursor.cpp
@ -5,6 +5,7 @@
|
||||
*
|
||||
* \author Alejandro Aguilar Sierra
|
||||
* \author Alfredo Braunstein
|
||||
* \author Dov Feldstern
|
||||
* \author André Pönitz
|
||||
* \author Stefan Schimanski
|
||||
*
|
||||
@ -453,6 +454,276 @@ bool Cursor::posForward()
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
|
||||
// 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) {
|
||||
// edge-case: we're at the end of the paragraph, there isn't really any
|
||||
// position any further to the right
|
||||
if (known_pos == lastpos()) {
|
||||
right_pos = -1;
|
||||
left_pos = row.endpos() - 1;
|
||||
return;
|
||||
}
|
||||
// the normal case
|
||||
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
|
||||
// edge-case: we're at the end of the paragraph, there isn't really any
|
||||
// position any further to the left
|
||||
if (known_pos == lastpos()) {
|
||||
left_pos = -1;
|
||||
right_pos = row.endpos() - 1;
|
||||
return;
|
||||
}
|
||||
// the normal case
|
||||
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());
|
||||
|
22
src/Cursor.h
22
src/Cursor.h
@ -106,6 +106,12 @@ public:
|
||||
bool & macromode() { return macromode_; }
|
||||
/// returns x,y position
|
||||
void getPos(int & x, int & y) const;
|
||||
/// return logical positions between which the cursor is situated
|
||||
/**
|
||||
* If the cursor is at the edge of a row, the position which is "over the
|
||||
* edge" will be returned as -1.
|
||||
*/
|
||||
void getSurroundingPos(pos_type & left_pos, pos_type & right_pos);
|
||||
/// the row in the paragraph we're in
|
||||
Row const & textRow() const;
|
||||
|
||||
@ -116,6 +122,22 @@ public:
|
||||
bool posBackward();
|
||||
/// move one step forward
|
||||
bool posForward();
|
||||
/// move visually to next/previous row
|
||||
/**
|
||||
* Assuming we were to keep moving left (right) from the current cursor
|
||||
* position, place the cursor at the rightmost (leftmost) edge of the
|
||||
* new row to which we would move according to visual-mode cursor movement.
|
||||
* This could be either the next or the previous row, depending on the
|
||||
* direction in which we're moving, and whether we're in an LTR or RTL
|
||||
* paragraph.
|
||||
* @note: The new position may even be in a new paragraph.
|
||||
* @note: This method will not move out of the current slice.
|
||||
* @return: false if not moved (no more rows to move to in given direction)
|
||||
* @return: true if moved
|
||||
*/
|
||||
bool posVisToNewRow(bool movingLeft);
|
||||
/// move to right or left extremity of the current row
|
||||
void posVisToRowExtremity(bool left);
|
||||
|
||||
/// insert an inset
|
||||
void insert(Inset *);
|
||||
|
14
src/Text.h
14
src/Text.h
@ -169,11 +169,23 @@ public:
|
||||
* Returns true if an update is needed after the move.
|
||||
*/
|
||||
bool cursorBackward(Cursor & cur);
|
||||
/// Move cursor visually one position to the left
|
||||
/**
|
||||
* \param skip_inset if true, don't enter insets
|
||||
* Returns true if an update is needed after the move.
|
||||
*/
|
||||
bool cursorVisLeft(Cursor & cur, bool skip_inset = false);
|
||||
/// Move cursor one position forward
|
||||
/**
|
||||
* Returns true if an update is needed after the move.
|
||||
*/
|
||||
bool cursorForward(Cursor & cur);
|
||||
/// Move cursor visually one position to the right
|
||||
/**
|
||||
* \param skip_inset if true, don't enter insets
|
||||
* Returns true if an update is needed after the move.
|
||||
*/
|
||||
bool cursorVisRight(Cursor & cur, bool skip_inset = false);
|
||||
///
|
||||
bool cursorBackwardOneWord(Cursor & cur);
|
||||
///
|
||||
@ -241,6 +253,8 @@ public:
|
||||
|
||||
///
|
||||
bool checkAndActivateInset(Cursor & cur, bool front);
|
||||
///
|
||||
bool checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool movingLeft);
|
||||
|
||||
///
|
||||
void write(Buffer const & buf, std::ostream & os) const;
|
||||
|
179
src/Text2.cpp
179
src/Text2.cpp
@ -22,6 +22,7 @@
|
||||
|
||||
#include "Text.h"
|
||||
|
||||
#include "Bidi.h"
|
||||
#include "Buffer.h"
|
||||
#include "buffer_funcs.h"
|
||||
#include "BufferList.h"
|
||||
@ -594,6 +595,24 @@ bool Text::checkAndActivateInset(Cursor & cur, bool front)
|
||||
}
|
||||
|
||||
|
||||
bool Text::checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool movingLeft)
|
||||
{
|
||||
if (cur.selection())
|
||||
return false;
|
||||
if (cur.pos() == -1)
|
||||
return false;
|
||||
if (cur.pos() == cur.lastpos())
|
||||
return false;
|
||||
Paragraph & par = cur.paragraph();
|
||||
Inset * inset = par.isInset(cur.pos()) ? par.getInset(cur.pos()) : 0;
|
||||
if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
|
||||
return false;
|
||||
inset->edit(cur, movingForward,
|
||||
movingLeft ? Inset::ENTER_FROM_RIGHT : Inset::ENTER_FROM_LEFT);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Text::cursorBackward(Cursor & cur)
|
||||
{
|
||||
// Tell BufferView to test for FitCursor in any case!
|
||||
@ -636,6 +655,166 @@ bool Text::cursorBackward(Cursor & cur)
|
||||
}
|
||||
|
||||
|
||||
bool Text::cursorVisLeft(Cursor & cur, bool skip_inset)
|
||||
{
|
||||
pit_type new_pit = cur.pit(); // the paragraph to which we will move
|
||||
pos_type new_pos; // the position we will move to
|
||||
bool new_boundary; // will we move to a boundary position?
|
||||
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?
|
||||
|
||||
cur.getSurroundingPos(left_pos, right_pos);
|
||||
|
||||
LYXERR(Debug::RTL, left_pos <<"|"<< right_pos << " (pos: "<<cur.pos()<<")");
|
||||
|
||||
// Are we at an inset?
|
||||
Cursor temp_cur = cur;
|
||||
temp_cur.pos() = left_pos;
|
||||
temp_cur.boundary(false);
|
||||
if (!skip_inset &&
|
||||
checkAndActivateInsetVisual(temp_cur, left_pos >= cur.pos(), true)) {
|
||||
LYXERR(Debug::RTL, "entering inset at: " << temp_cur.pos());
|
||||
cur = temp_cur; // set the real cursor to new position inside inset!
|
||||
return false;
|
||||
}
|
||||
|
||||
// Are we already at leftmost pos in row?
|
||||
if (left_pos == -1) {
|
||||
|
||||
Cursor new_cur = cur;
|
||||
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...
|
||||
new_pit = new_cur.pit();
|
||||
new_pos = new_cur.pos();
|
||||
new_boundary = new_cur.boundary();
|
||||
|
||||
LYXERR(Debug::RTL, "left edge, moving: " << int(new_pit) << ","
|
||||
<< int(new_pos) << "," << (new_boundary ? 1 : 0));
|
||||
|
||||
}
|
||||
// normal movement to the left
|
||||
else {
|
||||
// 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 = cur.paragraph().getFontSettings(
|
||||
cur.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_pos = left_pos + 1;
|
||||
// if the position *after* left_pos is not RTL, set boundary to
|
||||
// true (we want to be *after* left_pos, not before left_pos + 1!)
|
||||
new_boundary = !cur.paragraph().getFontSettings(
|
||||
cur.bv().buffer().params(), new_pos).isVisibleRightToLeft();
|
||||
}
|
||||
// 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_pos = left_pos;
|
||||
new_boundary = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LYXERR(Debug::RTL, "moving to: " << new_pos
|
||||
<< (new_boundary ? " (boundary)" : ""));
|
||||
|
||||
return setCursor(cur, new_pit, new_pos, true, new_boundary);
|
||||
}
|
||||
|
||||
|
||||
bool Text::cursorVisRight(Cursor & cur, bool skip_inset)
|
||||
{
|
||||
pit_type new_pit = cur.pit(); // the paragraph to which we will move
|
||||
pos_type new_pos; // the position we will move to
|
||||
bool new_boundary; // will we move to a boundary position?
|
||||
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?
|
||||
|
||||
cur.getSurroundingPos(left_pos, right_pos);
|
||||
|
||||
LYXERR(Debug::RTL, left_pos <<"|"<< right_pos << " (pos: "<<cur.pos()<<")");
|
||||
|
||||
// Are we at an inset?
|
||||
Cursor temp_cur = cur;
|
||||
temp_cur.pos() = right_pos;
|
||||
temp_cur.boundary(false);
|
||||
if (!skip_inset &&
|
||||
checkAndActivateInsetVisual(temp_cur, right_pos >= cur.pos(), false)) {
|
||||
LYXERR(Debug::RTL, "entering inset at: " << temp_cur.pos());
|
||||
cur = temp_cur; // set the real cursor to new position inside inset!
|
||||
return false;
|
||||
}
|
||||
|
||||
// Are we already at rightmost pos in row?
|
||||
if (right_pos == -1) {
|
||||
|
||||
Cursor new_cur = cur;
|
||||
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...
|
||||
new_pit = new_cur.pit();
|
||||
new_pos = new_cur.pos();
|
||||
new_boundary = new_cur.boundary();
|
||||
|
||||
LYXERR(Debug::RTL, "right edge, moving: " << int(new_pit) << ","
|
||||
<< int(new_pos) << "," << (new_boundary ? 1 : 0));
|
||||
|
||||
}
|
||||
// normal movement to the right
|
||||
else {
|
||||
// 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 = cur.paragraph().getFontSettings(
|
||||
cur.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_pos = right_pos + 1;
|
||||
// if the position *after* right_pos is RTL, set boundary to
|
||||
// true (we want to be *after* right_pos, not before right_pos + 1!)
|
||||
new_boundary = cur.paragraph().getFontSettings(
|
||||
cur.bv().buffer().params(), new_pos).isVisibleRightToLeft();
|
||||
}
|
||||
// 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_pos = right_pos;
|
||||
new_boundary = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LYXERR(Debug::RTL, "moving to: " << new_pos
|
||||
<< (new_boundary ? " (boundary)" : ""));
|
||||
|
||||
return setCursor(cur, new_pit, new_pos, true, new_boundary);
|
||||
}
|
||||
|
||||
|
||||
bool Text::cursorForward(Cursor & cur)
|
||||
{
|
||||
// Tell BufferView to test for FitCursor in any case!
|
||||
|
@ -531,29 +531,50 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
|
||||
|
||||
case LFUN_CHAR_LEFT:
|
||||
case LFUN_CHAR_LEFT_SELECT:
|
||||
//FIXME: for visual cursor, really move left
|
||||
if (reverseDirectionNeeded(cur)) {
|
||||
cmd.action = cmd.action == LFUN_CHAR_LEFT_SELECT ?
|
||||
LFUN_CHAR_FORWARD_SELECT : LFUN_CHAR_FORWARD;
|
||||
if (lyxrc.visual_cursor) {
|
||||
needsUpdate |= cur.selHandle(cmd.action == LFUN_CHAR_LEFT_SELECT);
|
||||
needsUpdate |= cursorVisLeft(cur);
|
||||
if (!needsUpdate && oldTopSlice == cur.top()
|
||||
&& cur.boundary() == oldBoundary) {
|
||||
cur.undispatched();
|
||||
cmd = FuncRequest(LFUN_FINISHED_LEFT);
|
||||
}
|
||||
} else {
|
||||
cmd.action = cmd.action == LFUN_CHAR_LEFT_SELECT ?
|
||||
if (reverseDirectionNeeded(cur)) {
|
||||
cmd.action = cmd.action == LFUN_CHAR_LEFT_SELECT ?
|
||||
LFUN_CHAR_FORWARD_SELECT : LFUN_CHAR_FORWARD;
|
||||
} else {
|
||||
cmd.action = cmd.action == LFUN_CHAR_LEFT_SELECT ?
|
||||
LFUN_CHAR_BACKWARD_SELECT : LFUN_CHAR_BACKWARD;
|
||||
}
|
||||
dispatch(cur, cmd);
|
||||
return;
|
||||
}
|
||||
dispatch(cur, cmd);
|
||||
return;
|
||||
break;
|
||||
|
||||
case LFUN_CHAR_RIGHT:
|
||||
case LFUN_CHAR_RIGHT_SELECT:
|
||||
//FIXME: for visual cursor, really move right
|
||||
if (reverseDirectionNeeded(cur)) {
|
||||
cmd.action = cmd.action == LFUN_CHAR_RIGHT_SELECT ?
|
||||
LFUN_CHAR_BACKWARD_SELECT : LFUN_CHAR_BACKWARD;
|
||||
if (lyxrc.visual_cursor) {
|
||||
needsUpdate |= cur.selHandle(cmd.action == LFUN_CHAR_RIGHT_SELECT);
|
||||
needsUpdate |= cursorVisRight(cur);
|
||||
if (!needsUpdate && oldTopSlice == cur.top()
|
||||
&& cur.boundary() == oldBoundary) {
|
||||
cur.undispatched();
|
||||
cmd = FuncRequest(LFUN_FINISHED_RIGHT);
|
||||
}
|
||||
} else {
|
||||
cmd.action = cmd.action == LFUN_CHAR_RIGHT_SELECT ?
|
||||
if (reverseDirectionNeeded(cur)) {
|
||||
cmd.action = cmd.action == LFUN_CHAR_RIGHT_SELECT ?
|
||||
LFUN_CHAR_BACKWARD_SELECT : LFUN_CHAR_BACKWARD;
|
||||
} else {
|
||||
cmd.action = cmd.action == LFUN_CHAR_RIGHT_SELECT ?
|
||||
LFUN_CHAR_FORWARD_SELECT : LFUN_CHAR_FORWARD;
|
||||
}
|
||||
dispatch(cur, cmd);
|
||||
return;
|
||||
}
|
||||
dispatch(cur, cmd);
|
||||
return;
|
||||
break;
|
||||
|
||||
|
||||
case LFUN_UP_SELECT:
|
||||
case LFUN_DOWN_SELECT:
|
||||
@ -1582,18 +1603,24 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
|
||||
|
||||
case LFUN_FINISHED_LEFT:
|
||||
LYXERR(Debug::DEBUG, "handle LFUN_FINISHED_LEFT:\n" << cur);
|
||||
if (reverseDirectionNeeded(cur)) {
|
||||
++cur.pos();
|
||||
cur.setCurrentFont();
|
||||
}
|
||||
// We're leaving an inset, going left. If the inset is LTR, we're
|
||||
// leaving from the front, so we should not move (remain at --- but
|
||||
// not in --- the inset). If the inset is RTL, move left, without
|
||||
// entering the inset itself; i.e., move to after the inset.
|
||||
if (cur.paragraph().getFontSettings(
|
||||
cur.bv().buffer().params(), cur.pos()).isRightToLeft())
|
||||
cursorVisLeft(cur, true);
|
||||
break;
|
||||
|
||||
case LFUN_FINISHED_RIGHT:
|
||||
LYXERR(Debug::DEBUG, "handle LFUN_FINISHED_RIGHT:\n" << cur);
|
||||
if (!reverseDirectionNeeded(cur)) {
|
||||
++cur.pos();
|
||||
cur.setCurrentFont();
|
||||
}
|
||||
// We're leaving an inset, going right. If the inset is RTL, we're
|
||||
// leaving from the front, so we should not move (remain at --- but
|
||||
// not in --- the inset). If the inset is LTR, move right, without
|
||||
// entering the inset itself; i.e., move to after the inset.
|
||||
if (!cur.paragraph().getFontSettings(
|
||||
cur.bv().buffer().params(), cur.pos()).isRightToLeft())
|
||||
cursorVisRight(cur, true);
|
||||
break;
|
||||
|
||||
case LFUN_FINISHED_BACKWARD:
|
||||
|
@ -213,7 +213,7 @@ bool Inset::getStatus(Cursor &, FuncRequest const & cmd,
|
||||
}
|
||||
|
||||
|
||||
void Inset::edit(Cursor &, bool)
|
||||
void Inset::edit(Cursor &, bool, EntryDirectionType)
|
||||
{
|
||||
LYXERR(Debug::INSETS, "edit left/right");
|
||||
}
|
||||
|
@ -65,6 +65,12 @@ namespace graphics { class PreviewLoader; }
|
||||
|
||||
class Inset {
|
||||
public:
|
||||
///
|
||||
enum EntryDirectionType {
|
||||
IGNORE_ENTRY_DIRECTION,
|
||||
ENTER_FROM_RIGHT,
|
||||
ENTER_FROM_LEFT,
|
||||
};
|
||||
///
|
||||
typedef ptrdiff_t difference_type;
|
||||
/// short of anything else reasonable
|
||||
@ -119,7 +125,8 @@ public:
|
||||
FuncStatus & status) const;
|
||||
|
||||
/// cursor enters
|
||||
virtual void edit(Cursor & cur, bool left);
|
||||
virtual void edit(Cursor & cur, bool front,
|
||||
EntryDirectionType entry_from = IGNORE_ENTRY_DIRECTION);
|
||||
/// cursor enters
|
||||
virtual Inset * editXY(Cursor & cur, int x, int y);
|
||||
|
||||
|
@ -162,10 +162,10 @@ void InsetCaption::draw(PainterInfo & pi, int x, int y) const
|
||||
}
|
||||
|
||||
|
||||
void InsetCaption::edit(Cursor & cur, bool left)
|
||||
void InsetCaption::edit(Cursor & cur, bool front, EntryDirectionType entry_from)
|
||||
{
|
||||
cur.push(*this);
|
||||
InsetText::edit(cur, left);
|
||||
InsetText::edit(cur, front, entry_from);
|
||||
}
|
||||
|
||||
|
||||
|
@ -49,7 +49,7 @@ public:
|
||||
///
|
||||
virtual void draw(PainterInfo & pi, int x, int y) const;
|
||||
///
|
||||
virtual void edit(Cursor & cur, bool left);
|
||||
virtual void edit(Cursor & cur, bool front, EntryDirectionType entry_from);
|
||||
///
|
||||
virtual Inset * editXY(Cursor & cur, int x, int y);
|
||||
///
|
||||
|
@ -466,11 +466,11 @@ docstring const InsetCollapsable::getNewLabel(docstring const & l) const
|
||||
}
|
||||
|
||||
|
||||
void InsetCollapsable::edit(Cursor & cur, bool left)
|
||||
void InsetCollapsable::edit(Cursor & cur, bool front, EntryDirectionType entry_from)
|
||||
{
|
||||
//lyxerr << "InsetCollapsable: edit left/right" << endl;
|
||||
cur.push(*this);
|
||||
InsetText::edit(cur, left);
|
||||
InsetText::edit(cur, front, entry_from);
|
||||
}
|
||||
|
||||
|
||||
|
@ -162,7 +162,8 @@ protected:
|
||||
///
|
||||
virtual void doDispatch(Cursor & cur, FuncRequest & cmd);
|
||||
///
|
||||
void edit(Cursor & cur, bool left);
|
||||
void edit(Cursor & cur, bool front,
|
||||
EntryDirectionType entry_from = IGNORE_ENTRY_DIRECTION);
|
||||
///
|
||||
Inset * editXY(Cursor & cur, int x, int y);
|
||||
///
|
||||
|
@ -160,7 +160,7 @@ bool InsetCommand::getStatus(Cursor & cur, FuncRequest const & cmd,
|
||||
}
|
||||
|
||||
|
||||
void InsetCommand::edit(Cursor & cur, bool)
|
||||
void InsetCommand::edit(Cursor & cur, bool, EntryDirectionType)
|
||||
{
|
||||
if (!mailer_name_.empty())
|
||||
InsetCommandMailer(mailer_name_, *this).showDialog(&cur.bv());
|
||||
|
@ -72,7 +72,8 @@ public:
|
||||
return p_[name];
|
||||
}
|
||||
///
|
||||
void edit(Cursor & cur, bool left);
|
||||
void edit(Cursor & cur, bool front,
|
||||
EntryDirectionType entry_from = IGNORE_ENTRY_DIRECTION);
|
||||
///
|
||||
RenderButton & button() const { return button_; }
|
||||
///
|
||||
|
@ -516,7 +516,7 @@ void InsetExternal::updateEmbeddedFile(Buffer const & buf,
|
||||
}
|
||||
|
||||
|
||||
void InsetExternal::edit(Cursor & cur, bool)
|
||||
void InsetExternal::edit(Cursor & cur, bool, EntryDirectionType)
|
||||
{
|
||||
InsetExternalMailer(*this).showDialog(&cur.bv());
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ public:
|
||||
///
|
||||
void addPreview(graphics::PreviewLoader &) const;
|
||||
///
|
||||
void edit(Cursor & cur, bool left);
|
||||
void edit(Cursor & cur, bool front, EntryDirectionType entry_from);
|
||||
///
|
||||
bool getStatus(Cursor &, FuncRequest const &, FuncStatus &) const;
|
||||
/// external file can be embedded
|
||||
|
@ -239,7 +239,7 @@ void InsetGraphics::updateEmbeddedFile(Buffer const & buf,
|
||||
}
|
||||
|
||||
|
||||
void InsetGraphics::edit(Cursor & cur, bool)
|
||||
void InsetGraphics::edit(Cursor & cur, bool, EntryDirectionType)
|
||||
{
|
||||
InsetGraphicsMailer(*this).showDialog(&cur.bv());
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ public:
|
||||
///
|
||||
void draw(PainterInfo & pi, int x, int y) const;
|
||||
///
|
||||
void edit(Cursor & cur, bool left);
|
||||
void edit(Cursor & cur, bool front, EntryDirectionType entry_from);
|
||||
///
|
||||
void editGraphics(InsetGraphicsParams const &, Buffer const &) const;
|
||||
///
|
||||
|
@ -3151,7 +3151,7 @@ docstring const InsetTabular::editMessage() const
|
||||
}
|
||||
|
||||
|
||||
void InsetTabular::edit(Cursor & cur, bool left)
|
||||
void InsetTabular::edit(Cursor & cur, bool, EntryDirectionType)
|
||||
{
|
||||
//lyxerr << "InsetTabular::edit: " << this << endl;
|
||||
cur.finishUndo();
|
||||
|
@ -745,7 +745,7 @@ public:
|
||||
/// set the owning buffer
|
||||
void buffer(Buffer const * buf);
|
||||
/// lock cell with given index
|
||||
void edit(Cursor & cur, bool left);
|
||||
void edit(Cursor & cur, bool front, EntryDirectionType entry_from);
|
||||
///
|
||||
Inset * editXY(Cursor & cur, int x, int y);
|
||||
/// can we go further down on mouse click?
|
||||
|
@ -200,11 +200,20 @@ docstring const InsetText::editMessage() const
|
||||
}
|
||||
|
||||
|
||||
void InsetText::edit(Cursor & cur, bool left)
|
||||
void InsetText::edit(Cursor & cur, bool front, EntryDirectionType entry_from)
|
||||
{
|
||||
//lyxerr << "InsetText: edit left/right" << endl;
|
||||
int const pit = left ? 0 : paragraphs().size() - 1;
|
||||
int const pos = left ? 0 : paragraphs().back().size();
|
||||
pit_type const pit = front ? 0 : paragraphs().size() - 1;
|
||||
pos_type pos = front ? 0 : paragraphs().back().size();
|
||||
|
||||
// if visual information is not to be ignored, move to extreme right/left
|
||||
if (entry_from != IGNORE_ENTRY_DIRECTION) {
|
||||
Cursor temp_cur = cur;
|
||||
temp_cur.pit() = pit;
|
||||
temp_cur.pos() = pos;
|
||||
temp_cur.posVisToRowExtremity(entry_from == ENTER_FROM_LEFT);
|
||||
pos = temp_cur.pos();
|
||||
}
|
||||
|
||||
text_.setCursor(cur.top(), pit, pos);
|
||||
cur.clearSelection();
|
||||
cur.finishUndo();
|
||||
|
@ -113,7 +113,7 @@ public:
|
||||
void addPreview(graphics::PreviewLoader &) const;
|
||||
|
||||
///
|
||||
void edit(Cursor & cur, bool left);
|
||||
void edit(Cursor & cur, bool front, EntryDirectionType entry_from);
|
||||
///
|
||||
Inset * editXY(Cursor & cur, int x, int y);
|
||||
|
||||
|
@ -1310,10 +1310,12 @@ void InsetMathHull::handleFont2(Cursor & cur, docstring const & arg)
|
||||
}
|
||||
|
||||
|
||||
void InsetMathHull::edit(Cursor & cur, bool front)
|
||||
void InsetMathHull::edit(Cursor & cur, bool front, EntryDirectionType entry_from)
|
||||
{
|
||||
cur.push(*this);
|
||||
front ? idxFirst(cur) : idxLast(cur);
|
||||
bool enter_front = (entry_from == Inset::ENTER_FROM_LEFT ||
|
||||
(entry_from == Inset::IGNORE_ENTRY_DIRECTION && front));
|
||||
enter_front ? idxFirst(cur) : idxLast(cur);
|
||||
// The inset formula dimension is not necessarily the same as the
|
||||
// one of the instant preview image, so we have to indicate to the
|
||||
// BufferView that a metrics update is needed.
|
||||
|
@ -199,7 +199,8 @@ public:
|
||||
///
|
||||
EDITABLE editable() const { return HIGHLY_EDITABLE; }
|
||||
///
|
||||
void edit(Cursor & cur, bool front);
|
||||
void edit(Cursor & cur, bool front,
|
||||
EntryDirectionType entry_from = IGNORE_ENTRY_DIRECTION);
|
||||
///
|
||||
Inset * editXY(Cursor & cur, int x, int y);
|
||||
///
|
||||
|
@ -1205,11 +1205,13 @@ bool InsetMathNest::getStatus(Cursor & cur, FuncRequest const & cmd,
|
||||
}
|
||||
|
||||
|
||||
void InsetMathNest::edit(Cursor & cur, bool left)
|
||||
void InsetMathNest::edit(Cursor & cur, bool front, EntryDirectionType entry_from)
|
||||
{
|
||||
cur.push(*this);
|
||||
cur.idx() = left ? 0 : cur.lastidx();
|
||||
cur.pos() = left ? 0 : cur.lastpos();
|
||||
bool enter_front = (entry_from == Inset::ENTER_FROM_RIGHT ||
|
||||
(entry_from == Inset::IGNORE_ENTRY_DIRECTION && front));
|
||||
cur.idx() = enter_front ? 0 : cur.lastidx();
|
||||
cur.pos() = enter_front ? 0 : cur.lastpos();
|
||||
cur.resetAnchor();
|
||||
//lyxerr << "InsetMathNest::edit, cur:\n" << cur << endl;
|
||||
}
|
||||
|
@ -48,7 +48,8 @@ public:
|
||||
void cursorPos(BufferView const & bv, CursorSlice const & sl,
|
||||
bool boundary, int & x, int & y) const;
|
||||
///
|
||||
void edit(Cursor & cur, bool left);
|
||||
void edit(Cursor & cur, bool front,
|
||||
EntryDirectionType entry_from = IGNORE_ENTRY_DIRECTION);
|
||||
///
|
||||
Inset * editXY(Cursor & cur, int x, int y);
|
||||
|
||||
|
@ -509,10 +509,10 @@ void MathMacro::validate(LaTeXFeatures & features) const
|
||||
}
|
||||
|
||||
|
||||
void MathMacro::edit(Cursor & cur, bool left)
|
||||
void MathMacro::edit(Cursor & cur, bool front, EntryDirectionType entry_from)
|
||||
{
|
||||
cur.updateFlags(Update::Force);
|
||||
InsetMathNest::edit(cur, left);
|
||||
InsetMathNest::edit(cur, front, entry_from);
|
||||
}
|
||||
|
||||
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
void cursorPos(BufferView const & bv, CursorSlice const & sl,
|
||||
bool boundary, int & x, int & y) const;
|
||||
///
|
||||
void edit(Cursor & cur, bool left);
|
||||
void edit(Cursor & cur, bool front, EntryDirectionType entry_from);
|
||||
///
|
||||
Inset * editXY(Cursor & cur, int x, int y);
|
||||
|
||||
|
@ -533,11 +533,11 @@ void MathMacroTemplate::draw(PainterInfo & pi, int x, int y) const
|
||||
}
|
||||
|
||||
|
||||
void MathMacroTemplate::edit(Cursor & cur, bool left)
|
||||
void MathMacroTemplate::edit(Cursor & cur, bool front, EntryDirectionType entry_from)
|
||||
{
|
||||
updateLook();
|
||||
cur.updateFlags(Update::Force);
|
||||
InsetMathNest::edit(cur, left);
|
||||
InsetMathNest::edit(cur, front, entry_from);
|
||||
}
|
||||
|
||||
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
///
|
||||
EDITABLE editable() const { return HIGHLY_EDITABLE; }
|
||||
///
|
||||
void edit(Cursor & cur, bool left);
|
||||
void edit(Cursor & cur, bool front, EntryDirectionType entry_from);
|
||||
///
|
||||
bool notifyCursorLeaves(Cursor & cur);
|
||||
///
|
||||
|
@ -64,6 +64,7 @@ ErrorItem errorTags[] = {
|
||||
{ Debug::PAINTING, "painting", N_("RowPainter profiling")},
|
||||
{ Debug::SCROLLING, "scrolling", N_("scrolling debugging")},
|
||||
{ Debug::MACROS, "macros", N_("Math macros")},
|
||||
{ Debug::RTL, "rtl", N_("RTL/Bidi")},
|
||||
{ Debug::DEBUG, "debug", N_("Developers' general debug messages")},
|
||||
{ Debug::ANY, "any", N_("All debugging messages")}
|
||||
};
|
||||
|
@ -93,6 +93,8 @@ namespace Debug {
|
||||
SCROLLING = (1 << 25),
|
||||
///
|
||||
MACROS = (1 << 26),
|
||||
/// rtl-related
|
||||
RTL = (1 << 27),
|
||||
///
|
||||
DEBUG = (1 << 31),
|
||||
///
|
||||
|
Loading…
Reference in New Issue
Block a user