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