I like it! The only thing missing is the EPM which works with the patch. I added this in set_font_spaces_magic.patch. Moreover I made your patch a bit simpler. I hope it's ok like that.

Order to apply:
rtl_spaces.patch
set_font_spaces_magic.patch
space_lang_change.patch

Stefan

Index: lyx-devel/src/Text.cpp
===================================================================
--- lyx-devel.orig/src/Text.cpp 2007-06-08 10:05:35.000000000 +0200
+++ lyx-devel/src/Text.cpp      2007-06-08 10:07:06.000000000 +0200
@@ -5,6 +5,7 @@
  *
  * \author Asger Alstrup
  * \author Lars Gullik Bjønnes
+ * \author Dov Feldstern
  * \author Jean-Marc Lasgouttes
  * \author John Levon
  * \author André Pönitz
@@ -713,7 +714,50 @@
                }
        }
 
-       // First check, if there will be two blanks together or a blank at
+       // In Bidi text, we want spaces to be treated in a special way: spaces
+       // which are between words in different languages should get the 
+       // paragraph's language; otherwise, spaces should keep the language 
+       // they were originally typed in. This is only in effect while typing;
+       // after the text is already typed in, the user can always go back and
+       // explicitly set the language of a space as desired. But 99.9% of the
+       // time, what we're doing here is what the user actually meant.
+       // 
+       // The following cases are the ones in which the language of the space
+       // should be changed to match that of the containing paragraph. In the
+       // depictions, lowercase is LTR, uppercase is RTL, underscore (_) 
+       // represents a space, pipe (|) represents the cursor position (so the
+       // character before it is the one just typed in). The different cases
+       // are depicted logically (not visually), from left to right:
+       // 
+       // 1. A_a|
+       // 2. a_A|
+       //
+       // Theoretically, there are other situations that we should, perhaps, 
deal
+       // with (e.g.: a|_A, A|_a). In practice, though, there really isn't any 
+       // point (to understand why, just try to create this situation...).
+       //
+       // see Text3.cpp toggleAndShow() for the other part in possible EPDM
+       // cases.
+                
+       if ((cur.pos() >= 2) && (par.isLineSeparator(cur.pos() - 1))) {
+               Font const & pre_space_font  = getFont(buffer, par, cur.pos() - 
2);
+               Font const & post_space_font = real_current_font;
+               // not getFont(cur_pos)! Current char not in buffer yet
+
+               bool pre_space_RTL = pre_space_font.isVisibleRightToLeft();
+               bool post_space_RTL = post_space_font.isVisibleRightToLeft();
+               if (pre_space_RTL != post_space_RTL) {
+                       // Set the space's language to match the language of 
the 
+                       // adjacent character whose direction is the paragraph's
+                       // direction
+                       if (pre_space_RTL == 
par.isRightToLeftPar(buffer.params()))
+                               par.setFont(cur.pos() - 1, pre_space_font);
+                       else
+                               par.setFont(cur.pos() - 1, post_space_font);
+               }
+       }
+       
+       // Next check, if there will be two blanks together or a blank at
        // the beginning of a paragraph.
        // I decided to handle blanks like normal characters, the main
        // difference are the special checks when calculating the row.fill
Index: lyx-devel/src/Text3.cpp
===================================================================
--- lyx-devel.orig/src/Text3.cpp        2007-06-08 08:42:56.000000000 +0200
+++ lyx-devel/src/Text3.cpp     2007-06-08 10:05:06.000000000 +0200
@@ -9,6 +9,7 @@
  * \author Angus Leeming
  * \author John Levon
  * \author André Pönitz
+ * \author Stefan Schimanski
  *
  * Full author contact details are available in file CREDITS.
  */
@@ -104,13 +105,65 @@
        {
                text->toggleFree(cur, font, toggleall);
 
+               // Are we at a RTL<->LTR boundary, and have to change the side?
                if (font.language() != ignore_language ||
                                font.number() != Font::IGNORE) {
                        Paragraph & par = cur.paragraph();
-                       if (cur.boundary() != text->isRTLBoundary(cur.buffer(), 
par,
-                                               cur.pos(), 
text->real_current_font))
+                       if (cur.boundary() != 
+                                       text->isRTLBoundary(cur.buffer(), par, 
cur.pos(), 
+                                                     text->real_current_font)) 
{
+                               
+                               // In Bidi text, we want spaces to be treated 
in a special way: spaces
+                               // which are between words in different 
languages should get the 
+                               // paragraph's language; otherwise, spaces 
should keep the language 
+                               // they were originally typed in. This is only 
in effect while typing;
+                               // after the text is already typed in, the user 
can always go back and
+                               // explicitly set the language of a space as 
desired. But 99.9% of the
+                               // time, what we're doing here is what the user 
actually meant.
+                               //
+                               // The following cases are the ones in which 
the language of the space
+                               // should be changed to match that of the 
containing paragraph. In the
+                               // depictions, lowercase is LTR, uppercase is 
RTL, underscore (_) 
+                               // represents a space, pipe (|) represents the 
cursor position (so the
+                               // character before it is the one just typed 
in). The different cases
+                               // are depicted logically (not visually), from 
left to right:
+                               // 
+                               // 1. A_a|
+                               // 2. a_A|
+                               //
+                               // Theoretically, there are other situations 
that we should, perhaps, deal
+                               // with (e.g.: a|_A, A|_a). In practice, 
though, there really isn't any 
+                               // point (to understand why, just try to create 
this situation...).
+                               // 
+                               // see Text::insertChar() for the other part in 
possible non-EPDM
+                               // cases.
+                               
+                               // the implementation: change language of a 
trailing space
+                               // which would be a victim of the DEPM later, 
i.e.
+                               //  "abc_[|_DEF]" =F12=> "abc_[DEF]_|" 
+                               if (cur.boundary() && 
+                                               cur.pos() >= 1 && 
par.isLineSeparator(cur.pos() - 1)) {
+                                       // Possible victim of DEPM?
+                                       bool newSpaceRTL = 
!font.isVisibleRightToLeft();
+                                       pos_type otherSpace 
+                                       = text->getPosVisually(cur.bv(), 
cur.pit(), cur.pos() - 1, 
+                                                              false, 
newSpaceRTL ? -1 : 1);
+                                       if (otherSpace != cur.pos() - 1 && 
+                                                       otherSpace >= 0 && 
otherSpace < par.size() &&
+                                                       
par.isLineSeparator(otherSpace)) {
+                                               // change language of the space
+                                               par.setFont(cur.pos() - 1, 
font);
+                                               
+                                               // and do not change the cursor 
position, i.e. stay behind
+                                               // the space (some insets or 
whatever could follow)
+                                               return;
+                                       }
+                               }
+                               
+                               // move cursor to the other side of the boundary
                                text->setCursor(cur, cur.pit(), cur.pos(),
                                                false, !cur.boundary());
+                       }
                }
        }
 
rtl_spaces.patch
set_font_space_lang_magic.patch
space_lang_change_magic.patch
Index: lyx-devel/src/Bidi.cpp
===================================================================
--- lyx-devel.orig/src/Bidi.cpp 2007-06-08 08:42:47.000000000 +0200
+++ lyx-devel/src/Bidi.cpp      2007-06-08 08:43:30.000000000 +0200
@@ -95,7 +95,11 @@
        pos_type const body_pos = par.beginOfBody();
 
        for (pos_type lpos = start_; lpos <= end_; ++lpos) {
-               bool is_space = par.isLineSeparator(lpos);
+               bool is_space = false;
+               // We do not handle spaces around an RTL segment in a special 
way anymore.
+               // Neither does LaTeX, so setting is_spacs to false makes the 
view in LyX
+               // consistent with the output of LaTeX later. The old setting 
was:
+               // is_space = par.isLineSeparator(lpos);
                pos_type const pos =
                        (is_space && lpos + 1 <= end_ &&
                         !par.isLineSeparator(lpos + 1) &&
Index: lyx-devel/src/Text.cpp
===================================================================
--- lyx-devel.orig/src/Text.cpp 2007-06-08 08:42:47.000000000 +0200
+++ lyx-devel/src/Text.cpp      2007-06-08 10:07:11.000000000 +0200
@@ -732,8 +732,22 @@
                        return;
                }
                BOOST_ASSERT(cur.pos() > 0);
-               if ((par.isLineSeparator(cur.pos() - 1) || 
par.isNewline(cur.pos() - 1))
-                   && !par.isDeleted(cur.pos() - 1)) {
+               
+               // get character position visually in front of cur.pos()
+               pos_type prevpos;
+               if (cur.boundary())
+                       prevpos = cur.pos() - 1;
+               else {
+                       bool rtl = isRTL(cur.buffer(), cur.top(), 
cur.boundary());
+                       prevpos = getPosVisually(cur.bv(), cur.pit(), 
cur.pos(), false, 
+                                                                               
                                         rtl ? 1 : -1);
+               }
+                       
+               // no space if previous was space (or newline),
+               if (prevpos != cur.pos()
+                               && 0 <= prevpos && prevpos < par.size()
+                               && (par.isLineSeparator(prevpos) || 
par.isNewline(prevpos))
+                   && !par.isDeleted(prevpos)) {
                        static bool sent_space_message = false;
                        if (!sent_space_message) {
                                cur.message(_("You cannot type two spaces this 
way. "
Index: lyx-devel/src/Text2.cpp
===================================================================
--- lyx-devel.orig/src/Text2.cpp        2007-06-08 08:42:47.000000000 +0200
+++ lyx-devel/src/Text2.cpp     2007-06-08 10:07:23.000000000 +0200
@@ -1114,6 +1114,72 @@
 }
 
 
+pos_type Text::getPosVisually(BufferView const & bv, pit_type pit,
+  pos_type pos, bool boundary, int visualDist) const
+{
+       Paragraph const & par = getPar(pit);
+       ParagraphMetrics const & pm = bv.parMetrics(this, pit);
+       pos_type oldpos = pos;
+       
+       // if RTL support is not enabled
+       if (!lyxrc.rtl_support) {
+               pos += visualDist;
+               if (pos < 0)
+                       return 0;
+               if (pos > par.size())
+                       return par.size();
+               return pos;
+       }
+       
+       // initialize the starting row and bidi tables
+       Bidi bidi;
+       Row const & row = pm.getRow(pos, boundary);
+       bidi.computeTables(par, *bv.buffer(), row);
+       
+       // try to find position
+       while (true) {
+               // Convert to visual position
+               if (!bidi.inRange(pos))
+                       return oldpos;
+               pos_type vstart = bidi.log2vis(pos);
+               
+               // Go to the character visualDist away
+               //bool backward = bidi.level(corrected_pos) & 1;
+               pos_type vpos = vstart + visualDist; //backward ? (vstart - 
visualDist) : (vstart + visualDist);
+               if (!bidi.inRange(vpos)) {
+                       // end of paragraph?
+                       if (vpos == par.size())
+                               return par.size();
+                       
+                       // out of paragraph? Return oldpos, i.e. we couldn't 
find the position
+                       if (vpos < 0 || vpos > par.size())
+                               return oldpos;
+                       
+                       // we might have changed the row. Try in the next/prev 
one
+                       if (visualDist > 0) {
+                               vstart = row.endpos();
+                               boundary = false;
+                               visualDist -= row.endpos() - vstart;
+                       } else {
+                               vstart = row.pos();
+                               boundary = true;
+                               visualDist -= vstart - row.pos();
+                       }
+               } else {
+                       // We found it -> convert back to logical position
+                       pos = bidi.vis2log(vpos);
+                       break;
+               }
+               
+               // update the bidi tables for next iteration
+               Row const & row = pm.getRow(vstart, boundary);
+               bidi.computeTables(par, *bv.buffer(), row);
+       }
+       
+       return pos;
+}
+
+
 bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
                Cursor & old, bool & need_anchor_change)
 {
@@ -1144,29 +1210,48 @@
 
        bool const same_inset = &old.inset() == &cur.inset();
        bool const same_par = same_inset && old.pit() == cur.pit();
-       bool const same_par_pos = same_par && old.pos() == cur.pos();
+       bool const same_par_pos = same_par && old.pos() == cur.pos() &&
+               old.boundary() == cur.boundary();
 
        // If the chars around the old cursor were spaces, delete one of them.
        if (!same_par_pos) {
-               // Only if the cursor has really moved.
-               if (old.pos() > 0
-                   && old.pos() < oldpar.size()
-                   && oldpar.isLineSeparator(old.pos())
-                   && oldpar.isLineSeparator(old.pos() - 1)
-                   && !oldpar.isDeleted(old.pos() - 1)) {
-                       oldpar.eraseChar(old.pos() - 1, 
cur.buffer().params().trackChanges);
+               // What is position of the char visually after the entered 
space? 
+               // Non-trivial on bidi boundaries:
+               // Case 1: "abc [|WERBEH] ghi" ==> "abc [| WERBEH] ghi"
+               // Case 2: "abc|[ WERBEH] ghi" ==> "abc |[ WERBEH] ghi"
+               // Case 3: "abc [WERBEH ]|ghi" ==> "abc [WERBEH ] |ghi"
+               // Case 4: "abc [WERBEH|] ghi" ==> "abc [WERBEH| ] ghi"
+               
+               // Was a space entered?
+               pos_type spacepos = old.pos() - 1;
+               if (spacepos >= 0 && spacepos < oldpar.size()
+                               && oldpar.isLineSeparator(spacepos)
+                               && !oldpar.isDeleted(spacepos)) {
+                       
+                       // Get visually following character position
+                       pos_type nextpos;
+                       bool rtl = old.text()->isRTL(old.buffer(), old.top(), 
old.boundary());
+                       nextpos = old.text()->getPosVisually(old.bv(), 
old.pit(), spacepos, 
+                                                            false, rtl ? -1 : 
1);
+                       
+                       // Is a space following?
+                       if (nextpos != spacepos 
+                                       && nextpos >= 0 && nextpos < 
oldpar.size()
+                                       && oldpar.isLineSeparator(nextpos)) {
+                               oldpar.eraseChar(spacepos, 
cur.buffer().params().trackChanges);
 #ifdef WITH_WARNINGS
 #warning This will not work anymore when we have multiple views of the same 
buffer
 // In this case, we will have to correct also the cursors held by
 // other bufferviews. It will probably be easier to do that in a more
 // automated way in CursorSlice code. (JMarc 26/09/2001)
 #endif
-                       // correct all cursor parts
-                       if (same_par) {
-                               fixCursorAfterDelete(cur.top(), old.top());
-                               need_anchor_change = true;
+                               // correct all cursor parts
+                               if (same_par) {
+                                       fixCursorAfterDelete(cur.top(), 
old.top());
+                                       need_anchor_change = true;
+                               }
+                               return true;
                        }
-                       return true;
                }
        }
 
Index: lyx-devel/src/Text.h
===================================================================
--- lyx-devel.orig/src/Text.h   2007-06-08 08:42:47.000000000 +0200
+++ lyx-devel/src/Text.h        2007-06-08 08:43:30.000000000 +0200
@@ -365,6 +365,11 @@
        /// now because recordUndo() is called which needs a Cursor.
        static bool deleteEmptyParagraphMechanism(Cursor & cur,
                Cursor & old, bool & need_anchor_change);
+       
+       /// Get logical position visualDist positions visually away from pos, 
+       /// If there is no position like that, the old value of pos is returned
+       pos_type getPosVisually(BufferView const & bv, pit_type pit,
+               pos_type pos, bool boundary, int visualDist) const;
 
        /// delete double spaces, leading spaces, and empty paragraphs
        /// from \first to \last paragraph

Reply via email to