Hi!

This issue seems to have generated a lot of discussion, and I think we sort of lost our focus. So I'm going to try to respond to the various issues raised, and then I'll sketch out a plan for moving ahead with this.

*) Andre asked about how other programs deal with this:

I took a look at how MS-Word (2007?) and OpenOffice (1.1.2) deal with this issue. Both of these applications provide an option of using either logical or visual movement.

In logical mode, the arrow keys' effect is determined by the paragraph's language: in an RTL paragraph, LEFT is logical forwards and RIGHT is backwards, in an LTR paragraph it's the other way around. This extends to text of the opposite direction which is embedded in the paragraph. So even if the all the text in the paragraph is LTR, but the paragraph's direction is RTL, the behavior will be according to the paragraph's direction, and so the arrow keys will appear to "behave backwards" for most of the text in that paragraph. (Note that neither of these applications offers an equivalent of LyX's insets, due to their WYSIWYG nature. In other words, when inserting a footnote, the footnote appears as a footnote at the foot of the page, and as such is a totally standalone paragraph. So getting guidance from these applications with regard to deeply nested insets is not possible.)

In both applications, the direction is determined on a per-paragraph level (and not per-document).

In both applications, the cursor sometimes gets stuck between paragraphs.

Visual mode in both applications means that LEFT moves left, and RIGHT moves right, regardless of the language, and the movement is visual and not logical (so that the cursor may actually be jumping back and forth in the logical buffer) The backspace key still deletes logical "backwards", so in LTR it deletes to the LEFT, but in RTL it deletes to the right.

In OO, also in visual mode, the cursor gets stuck between RTL and LTR paragraphs. In Word it doesn't get stuck between paragraphs in visual mode.

*) Martin asked: "don't you think that cursor movement depending only on the local language (i.e., the deepest inset on the cursor stack) is easier and more transparent to implement?"

Well yes, but only trivially so. In terms of ease of implementation, all I did was add a "bottom()" method in there --- that's hardly very opaque... In terms of transparency --- well, it is now also well-commented, so I don't think that this should be grounds for rejecting this approach.

*) Abdel said: "I personally tend to think that the only clear option, one that does not leave room for complicated interpretation is to go for screen oriented movement: right is right and left is left. Of course supporting both mode would be better."

+1. Yes, I agree that visual mode is clearer. However, it has its own problems:
  *) A selection may not be logically contiguous
*) Under this interpretation, what should the "backspace" key mean? Delete the previous character, or delete the character to the left of the cursor (where the arrow points)? It's not the same thing in RTL... And which character does the "delete" key delete? Depending on your answer to the previous question, "delete" and "backspace" may actually delete the same character, which doesn't make too much sense. So it's pretty clear (and that seems to be the consensus in other programs, as well), that backspace should still delete the logically previous character, even in visual mode.

So yes, I'm all for adding support for visual mode, I opened a new feature request for this in bugzilla (http://bugzilla.lyx.org/show_bug.cgi?id=3577). However, I'm against *replacing* the current approach --- logical mode --- with visual mode. Rather, visual mode should be added as another option, and the user should be able to choose between them by setting a preference.

*) Andre said: "As a plain non-RTL-user I'd think the same. But of course the vote of someone actually using it should count more."

+2. Yes, it is a rather humorous to hear all these opinions about whether or not this or that approach makes sense, given by people who have never written in Bidi. I repeat: it *doesn't* make sense, and it never will, there's no perfect solution except for switching the direction of the language (and that probably won't happen too soon ;) ) --- so please, instead of giving opinions about which approach does or does not make sense from the user's point of view, it would be much more helpful if you could help us out with the technical, programmatic, issues (and of course, make sure that nothing that we do interferes with reguar, non-bidi users). We've offered a few patches, but we need help working out the technical details, and you guys understand those better than we do. That's where we really need your help.

*) Finally, please keep in mind that what users have been complaining about is not "the arrow keys move in the opposite directions", but rather "when moving with the arrow keys, the cursor gets stuck inside insets". And the only way to avoid that (within the logical mode) is by being consistent about the logical interpretation of an arrow key within a paragraph --- even if the inset is a few levels deep. As soon as you change the logical interpretation of an arrow key --- that's where you get stuck in logical mode. Similarly, in visual mode, the only way to avoid getting stuck is by being consistent about the *visual* interpretation of the arrow keys.

=================================================================

So how do we move ahead with this?

1. Since the current implementation is of logical movement, I strongly urge that we stick with that. And in order to fix the problem with that (of the cursor getting stuck within insets), I suggest applying Elazar's patch for math insets (attached math_insets.diff), and my patch for other insets and for movement around insets (also attached rtl_insets.diff; modified from my previous version, in accordance with JMarc's comments; also, I consolidated a bit, so that policy changes --- e.g., inner or outer paragraph --- need only touch one place, and not five or six).

If you have any technical comments about these patches, let's work them out. Otherwise, let's please commit them?

2. It would be great if we could also add support for visual mode (see bug http://bugzilla.lyx.org/show_bug.cgi?id=3577), as a preference. Elazar has sent a patch which starts moving in that direction, and I have responded to it (see thread http://thread.gmane.org/gmane.editors.lyx.devel/82838). We need help with it, though, so please do help us if you can.

Just in order to clarify future discussions of this, let's try to be clear about whether any given discussion is about "logical mode" or "visual mode" --- both of which we would like to support.

Thanks!
Dov
Index: src/mathed/InsetMathNest.cpp
===================================================================
--- src/mathed/InsetMathNest.cpp        (revision 18237)
+++ src/mathed/InsetMathNest.cpp        (working copy)
@@ -492,6 +492,10 @@
                cur.autocorrect() = false;
                cur.clearTargetX();
                cur.macroModeClose();
+               if (cur.isRTL() )
+                       goto goto_char_backwards;
+                       
+               goto_char_forwards:
                if (cur.pos() != cur.lastpos() && cur.openable(cur.nextAtom())) 
{
                        cur.pushLeft(*cur.nextAtom().nucleus());
                        cur.inset().idxFirst(cur);
@@ -511,6 +515,10 @@
                cur.autocorrect() = false;
                cur.clearTargetX();
                cur.macroModeClose();
+               if (cur.isRTL())
+                       goto goto_char_forwards;
+               
+               goto_char_backwards:
                if (cur.pos() != 0 && cur.openable(cur.prevAtom())) {
                        cur.posLeft();
                        cur.push(*cur.nextAtom().nucleus());
Index: src/Text.h
===================================================================
--- src/Text.h  (revision 18237)
+++ src/Text.h  (working copy)
@@ -328,6 +328,8 @@
        docstring getPossibleLabel(Cursor & cur) const;
        /// is this paragraph right-to-left?
        bool isRTL(Buffer const &, Paragraph const & par) const;
+       /// should cursor movements be reversed (for bidi)?
+        bool reverseDirections(Cursor const & cur) const;
        ///
        bool checkAndActivateInset(Cursor & cur, bool front);
 
Index: src/Text3.cpp
===================================================================
--- src/Text3.cpp       (revision 18237)
+++ src/Text3.cpp       (working copy)
@@ -298,6 +298,19 @@
 }
 
 
+bool Text::reverseDirections(Cursor const & cur) const
+{
+       /*
+        * We determine the directions based on the direction of the 
+        * bottom() --- i.e., outermost --- paragraph, because that is
+        * the only way to achieve consistency of the arrow's movements
+        * within a paragraph, and thus avoid situations in which the
+        * cursor gets stuck.
+        */
+       return isRTL(*cur.bv().buffer(), cur.bottom().paragraph());
+}
+
+
 void Text::dispatch(Cursor & cur, FuncRequest & cmd)
 {
        LYXERR(Debug::ACTION) << "Text::dispatch: cmd: " << cmd << endl;
@@ -433,7 +446,7 @@
                //lyxerr << BOOST_CURRENT_FUNCTION
                //       << " LFUN_CHAR_FORWARD[SEL]:\n" << cur << endl;
                needsUpdate |= cur.selHandle(cmd.action == 
LFUN_CHAR_FORWARD_SELECT);
-               if (isRTL(*cur.bv().buffer(), cur.paragraph()))
+               if (reverseDirections(cur))
                        needsUpdate |= cursorLeft(cur);
                else
                        needsUpdate |= cursorRight(cur);
@@ -451,7 +464,7 @@
        case LFUN_CHAR_BACKWARD_SELECT:
                //lyxerr << "handle LFUN_CHAR_BACKWARD[_SELECT]:\n" << cur << 
endl;
                needsUpdate |= cur.selHandle(cmd.action == 
LFUN_CHAR_BACKWARD_SELECT);
-               if (isRTL(*cur.bv().buffer(), cur.paragraph()))
+               if (reverseDirections(cur))
                        needsUpdate |= cursorRight(cur);
                else
                        needsUpdate |= cursorLeft(cur);
@@ -557,7 +570,7 @@
        case LFUN_WORD_FORWARD:
        case LFUN_WORD_FORWARD_SELECT:
                needsUpdate |= cur.selHandle(cmd.action == 
LFUN_WORD_FORWARD_SELECT);
-               if (isRTL(*cur.bv().buffer(), cur.paragraph()))
+               if (reverseDirections(cur))
                        needsUpdate |= cursorLeftOneWord(cur);
                else
                        needsUpdate |= cursorRightOneWord(cur);
@@ -568,7 +581,7 @@
        case LFUN_WORD_BACKWARD:
        case LFUN_WORD_BACKWARD_SELECT:
                needsUpdate |= cur.selHandle(cmd.action == 
LFUN_WORD_BACKWARD_SELECT);
-               if (isRTL(*cur.bv().buffer(), cur.paragraph()))
+               if (reverseDirections(cur))
                        needsUpdate |= cursorRightOneWord(cur);
                else
                        needsUpdate |= cursorLeftOneWord(cur);
@@ -1433,11 +1446,16 @@
 
        case LFUN_FINISHED_LEFT:
                LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_LEFT:\n" << cur 
<< endl;
+               if (reverseDirections(cur)) {
+                       ++cur.pos();
+               }
                break;
 
        case LFUN_FINISHED_RIGHT:
                LYXERR(Debug::DEBUG) << "handle LFUN_FINISHED_RIGHT:\n" << cur 
<< endl;
-               ++cur.pos();
+               if (!reverseDirections(cur)) {
+                       ++cur.pos();
+               }
                break;
 
        case LFUN_FINISHED_UP:

Reply via email to