2003-10-23 13:28:49 +00:00
|
|
|
/**
|
2007-04-26 04:41:58 +00:00
|
|
|
* \file Bidi.cpp
|
2003-10-23 13:28:49 +00:00
|
|
|
* This file is part of LyX, the document processor.
|
|
|
|
* Licence details can be found in the file COPYING.
|
|
|
|
*
|
|
|
|
* \author Dekel Tsur
|
|
|
|
*
|
|
|
|
* Full author contact details are available in file CREDITS.
|
|
|
|
*/
|
|
|
|
|
2005-04-26 10:30:24 +00:00
|
|
|
#include <config.h>
|
2003-10-23 13:28:49 +00:00
|
|
|
|
|
|
|
#include "Bidi.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "Buffer.h"
|
2007-05-15 17:19:05 +00:00
|
|
|
#include "BufferView.h"
|
2007-11-01 22:17:22 +00:00
|
|
|
#include "Cursor.h"
|
2007-04-29 18:17:15 +00:00
|
|
|
#include "Font.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "Row.h"
|
|
|
|
#include "LyXRC.h"
|
|
|
|
#include "Paragraph.h"
|
2003-10-23 13:28:49 +00:00
|
|
|
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
namespace lyx {
|
2003-10-23 13:28:49 +00:00
|
|
|
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
pos_type Bidi::log2vis(pos_type pos) const
|
2003-10-23 13:28:49 +00:00
|
|
|
{
|
|
|
|
return (start_ == -1) ? pos : log2vis_list_[pos - start_];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
pos_type Bidi::vis2log(pos_type pos) const
|
2003-10-23 13:28:49 +00:00
|
|
|
{
|
|
|
|
return (start_ == -1) ? pos : vis2log_list_[pos - start_];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
pos_type Bidi::level(pos_type pos) const
|
2003-10-23 13:28:49 +00:00
|
|
|
{
|
|
|
|
return (start_ == -1) ? 0 : levels_[pos - start_];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
bool Bidi::inRange(pos_type pos) const
|
2003-10-23 13:28:49 +00:00
|
|
|
{
|
|
|
|
return start_ == -1 || (start_ <= pos && pos <= end_);
|
|
|
|
}
|
|
|
|
|
2012-10-27 13:45:27 +00:00
|
|
|
|
2003-10-23 13:28:49 +00:00
|
|
|
bool Bidi::same_direction() const
|
|
|
|
{
|
|
|
|
return same_direction_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Bidi::computeTables(Paragraph const & par,
|
2004-03-01 12:23:17 +00:00
|
|
|
Buffer const & buf, Row const & row)
|
2003-10-23 13:28:49 +00:00
|
|
|
{
|
|
|
|
same_direction_ = true;
|
|
|
|
if (!lyxrc.rtl_support) {
|
|
|
|
start_ = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-16 10:08:13 +00:00
|
|
|
if (par.inInset().forceLTR()) {
|
2003-10-23 13:28:49 +00:00
|
|
|
start_ = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
start_ = row.pos();
|
2003-10-27 11:44:10 +00:00
|
|
|
end_ = row.endpos() - 1;
|
2003-10-23 13:28:49 +00:00
|
|
|
|
|
|
|
if (start_ > end_) {
|
|
|
|
start_ = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end_ + 2 - start_ >
|
|
|
|
static_cast<pos_type>(log2vis_list_.size())) {
|
|
|
|
pos_type new_size =
|
|
|
|
(end_ + 2 - start_ < 500) ?
|
|
|
|
500 : 2 * (end_ + 2 - start_);
|
|
|
|
log2vis_list_.resize(new_size);
|
|
|
|
vis2log_list_.resize(new_size);
|
|
|
|
levels_.resize(new_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
vis2log_list_[end_ + 1 - start_] = -1;
|
|
|
|
log2vis_list_[end_ + 1 - start_] = -1;
|
|
|
|
|
|
|
|
BufferParams const & bufparams = buf.params();
|
|
|
|
pos_type stack[2];
|
2007-09-04 10:27:55 +00:00
|
|
|
bool const rtl_par = par.isRTL(bufparams);
|
2003-10-23 13:28:49 +00:00
|
|
|
int lev = 0;
|
|
|
|
bool rtl = false;
|
|
|
|
bool rtl0 = false;
|
2003-11-13 13:43:44 +00:00
|
|
|
pos_type const body_pos = par.beginOfBody();
|
2003-10-23 13:28:49 +00:00
|
|
|
|
|
|
|
for (pos_type lpos = start_; lpos <= end_; ++lpos) {
|
2007-06-08 14:20:06 +00:00
|
|
|
bool is_space = false;
|
|
|
|
// We do not handle spaces around an RTL segment in a special way anymore.
|
|
|
|
// Neither do we do so when generating the LaTeX, so setting is_space
|
|
|
|
// to false makes the view in the GUI consistent with the output of LaTeX
|
|
|
|
// later. The old setting was:
|
|
|
|
//bool is_space = par.isLineSeparator(lpos);
|
|
|
|
// FIXME: once we're sure that this is what we really want, we should just
|
|
|
|
// get rid of this variable...
|
2003-10-23 13:28:49 +00:00
|
|
|
pos_type const pos =
|
|
|
|
(is_space && lpos + 1 <= end_ &&
|
|
|
|
!par.isLineSeparator(lpos + 1) &&
|
Fix bugs #8546 and #9055, and introduce new separator inset.
The algorithm used for breaking a paragraph in LaTeX export is changed
for avoiding spurious blank lines causing too much vertical space.
This change is tied to the introduction of a new inset (with two
different specializations) helping in either outputing LaTeX paragraph
breaks or separating environments in LyX. Both of the above goals were
previously achieved by the ---Separator--- layout and can now be
accomplished by the new inset in a more natural way. As an example,
after leaving an environment by hitting the Return key for two times,
a third return automatically inserts a parbreak inset, which is
equivalent to the old separator layout, i.e., it also introduces a
blank line in the output. If this blank line is not wanted, the
parbreak separator can be changed to a plain separator by a right
click of the mouse. Of course, an environment can still be separated
by the following one by using the Alt+P+Return shortcut (or the
corresponding menu key), but now the plain separator inset is used
instead of the old separator layout, such that no blank line occurs in
the LaTeX output.
Old documents are converted such that the LaTeX output remains unchanged.
As a result of this conversion, the old separator layout is replaced by
the new parbreak inset, which may also appear in places where the old
algorithm was introducing blank lines while the new one is not.
Note that not all blank lines were actually affecting the LaTeX output,
because a blank line is simply ignored by the TeX engine when it occurs
in the so called "vertical mode" (e.g., after an alignment environment).
The old ---Separator--- layout is now gone and old layout files using it
are also automatically converted.
Round trip conversions between old and new format should leave a document
unchanged. This means that the new behavior about paragraph breaking is
not "carried back" to the old format. Indeed, this would need introducing
special LaTeX commands in ERT that would accumulate in roundtrip
conversions, horribly cluttering the document. So, when converting a
modified document to old formats, the LaTeX output may slightly differ in
vertical spacing if the document is processed by an old version of LyX.
In other words, forward compatibility is guaranteed, but not backwards.
2014-05-10 21:25:11 +00:00
|
|
|
!par.isEnvSeparator(lpos + 1) &&
|
2003-10-23 13:28:49 +00:00
|
|
|
!par.isNewline(lpos + 1))
|
|
|
|
? lpos + 1 : lpos;
|
2008-10-19 07:31:10 +00:00
|
|
|
|
|
|
|
Font const * font = &(par.getFontSettings(bufparams, pos));
|
|
|
|
if (pos != lpos && 0 < lpos && rtl0 && font->isRightToLeft() &&
|
|
|
|
font->fontInfo().number() == FONT_ON &&
|
2007-10-28 18:51:54 +00:00
|
|
|
par.getFontSettings(bufparams, lpos - 1).fontInfo().number()
|
|
|
|
== FONT_ON) {
|
2008-10-19 07:31:10 +00:00
|
|
|
font = &(par.getFontSettings(bufparams, lpos));
|
2003-10-23 13:28:49 +00:00
|
|
|
is_space = false;
|
|
|
|
}
|
2008-10-19 07:31:10 +00:00
|
|
|
bool new_rtl = font->isVisibleRightToLeft();
|
|
|
|
bool new_rtl0 = font->isRightToLeft();
|
2003-10-23 13:28:49 +00:00
|
|
|
|
|
|
|
int new_level;
|
|
|
|
|
|
|
|
if (lpos == body_pos - 1
|
|
|
|
&& row.pos() < body_pos - 1
|
|
|
|
&& is_space) {
|
|
|
|
new_level = rtl_par ? 1 : 0;
|
|
|
|
new_rtl0 = rtl_par;
|
|
|
|
new_rtl = rtl_par;
|
2007-11-01 22:17:22 +00:00
|
|
|
} else if (new_rtl0) {
|
2003-10-23 13:28:49 +00:00
|
|
|
new_level = new_rtl ? 1 : 2;
|
2007-11-01 22:17:22 +00:00
|
|
|
} else {
|
2003-10-23 13:28:49 +00:00
|
|
|
new_level = rtl_par ? 2 : 0;
|
2007-11-01 22:17:22 +00:00
|
|
|
}
|
2003-10-23 13:28:49 +00:00
|
|
|
|
|
|
|
if (is_space && new_level >= lev) {
|
|
|
|
new_level = lev;
|
|
|
|
new_rtl = rtl;
|
|
|
|
new_rtl0 = rtl0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int new_level2 = new_level;
|
|
|
|
|
|
|
|
if (lev == new_level && rtl0 != new_rtl0) {
|
|
|
|
--new_level2;
|
|
|
|
log2vis_list_[lpos - start_] = rtl ? 1 : -1;
|
|
|
|
} else if (lev < new_level) {
|
|
|
|
log2vis_list_[lpos - start_] = rtl ? -1 : 1;
|
2005-11-02 20:11:36 +00:00
|
|
|
if (new_level > 0 && !rtl_par)
|
2003-10-23 13:28:49 +00:00
|
|
|
same_direction_ = false;
|
2007-11-01 22:17:22 +00:00
|
|
|
} else {
|
2003-10-23 13:28:49 +00:00
|
|
|
log2vis_list_[lpos - start_] = new_rtl ? -1 : 1;
|
2007-11-01 22:17:22 +00:00
|
|
|
}
|
2003-10-23 13:28:49 +00:00
|
|
|
rtl = new_rtl;
|
|
|
|
rtl0 = new_rtl0;
|
|
|
|
levels_[lpos - start_] = new_level;
|
|
|
|
|
|
|
|
while (lev > new_level2) {
|
|
|
|
pos_type old_lpos = stack[--lev];
|
|
|
|
int delta = lpos - old_lpos - 1;
|
|
|
|
if (lev % 2)
|
|
|
|
delta = -delta;
|
|
|
|
log2vis_list_[lpos - start_] += delta;
|
|
|
|
log2vis_list_[old_lpos - start_] += delta;
|
|
|
|
}
|
|
|
|
while (lev < new_level)
|
|
|
|
stack[lev++] = lpos;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (lev > 0) {
|
|
|
|
pos_type const old_lpos = stack[--lev];
|
|
|
|
int delta = end_ - old_lpos;
|
|
|
|
if (lev % 2)
|
|
|
|
delta = -delta;
|
|
|
|
log2vis_list_[old_lpos - start_] += delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos_type vpos = start_ - 1;
|
|
|
|
for (pos_type lpos = start_; lpos <= end_; ++lpos) {
|
|
|
|
vpos += log2vis_list_[lpos - start_];
|
|
|
|
vis2log_list_[vpos - start_] = lpos;
|
|
|
|
log2vis_list_[lpos - start_] = vpos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This method requires a previous call to computeTables()
|
|
|
|
bool Bidi::isBoundary(Buffer const & buf, Paragraph const & par,
|
|
|
|
pos_type pos) const
|
|
|
|
{
|
|
|
|
if (!lyxrc.rtl_support || pos == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!inRange(pos - 1)) {
|
|
|
|
// This can happen if pos is the first char of a row.
|
|
|
|
// Returning false in this case is incorrect!
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool const rtl = level(pos - 1) % 2;
|
|
|
|
bool const rtl2 = inRange(pos)
|
|
|
|
? level(pos) % 2
|
2007-09-04 10:27:55 +00:00
|
|
|
: par.isRTL(buf.params());
|
2003-10-23 13:28:49 +00:00
|
|
|
return rtl != rtl2;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Bidi::isBoundary(Buffer const & buf, Paragraph const & par,
|
2007-04-29 18:17:15 +00:00
|
|
|
pos_type pos, Font const & font) const
|
2003-10-23 13:28:49 +00:00
|
|
|
{
|
|
|
|
if (!lyxrc.rtl_support)
|
|
|
|
return false; // This is just for speedup
|
|
|
|
|
|
|
|
bool const rtl = font.isVisibleRightToLeft();
|
|
|
|
bool const rtl2 = inRange(pos)
|
|
|
|
? level(pos) % 2
|
2007-09-04 10:27:55 +00:00
|
|
|
: par.isRTL(buf.params());
|
2003-10-23 13:28:49 +00:00
|
|
|
return rtl != rtl2;
|
|
|
|
}
|
2006-10-21 00:16:43 +00:00
|
|
|
|
|
|
|
|
2007-05-28 22:27:45 +00:00
|
|
|
bool reverseDirectionNeeded(Cursor const & cur)
|
2007-05-15 17:19:05 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2007-09-04 10:27:55 +00:00
|
|
|
return cur.bottom().paragraph().isRTL(cur.bv().buffer().params());
|
2007-05-15 17:19:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-05-16 09:35:12 +00:00
|
|
|
bool isWithinRtlParagraph(Cursor const & cur)
|
|
|
|
{
|
2007-09-04 10:27:55 +00:00
|
|
|
return cur.innerParagraph().isRTL(cur.bv().buffer().params());
|
2007-05-16 09:35:12 +00:00
|
|
|
}
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
} // namespace lyx
|