mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-28 12:26:59 +00:00
de15c267e3
* Use the isRTL information to compute the cursor position correctly around insets in RTL text. Those insets are positioned to the right of the cursor position in the text. (fixes issue (3) of bug 3551: Cursor movement in and around insets in RTL paragraphs) git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@18667 a592a061-630c-0410-9148-cb99ea01b6c8
357 lines
8.5 KiB
C++
357 lines
8.5 KiB
C++
/**
|
|
* \file bufferview_funcs.cpp
|
|
* This file is part of LyX, the document processor.
|
|
* Licence details can be found in the file COPYING.
|
|
*
|
|
* \author Lars Gullik Bjønnes
|
|
* \author Jean-Marc Lasgouttes
|
|
* \author John Levon
|
|
* \author Angus Leeming
|
|
* \author Juergen Vigna
|
|
*
|
|
* Full author contact details are available in file CREDITS.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "bufferview_funcs.h"
|
|
|
|
#include "Author.h"
|
|
#include "Buffer.h"
|
|
#include "BufferParams.h"
|
|
#include "BufferView.h"
|
|
#include "Cursor.h"
|
|
#include "CoordCache.h"
|
|
#include "gettext.h"
|
|
#include "Language.h"
|
|
#include "Color.h"
|
|
#include "Lexer.h"
|
|
#include "Row.h"
|
|
#include "Paragraph.h"
|
|
#include "ParagraphParameters.h"
|
|
#include "ParIterator.h"
|
|
|
|
#include "frontends/alert.h"
|
|
|
|
#include "insets/InsetCommand.h"
|
|
#include "insets/InsetText.h"
|
|
|
|
#include "support/convert.h"
|
|
|
|
#include <sstream>
|
|
|
|
|
|
namespace lyx {
|
|
|
|
using support::bformat;
|
|
|
|
using std::istringstream;
|
|
using std::ostringstream;
|
|
using std::string;
|
|
using std::vector;
|
|
using std::find;
|
|
|
|
|
|
namespace bv_funcs {
|
|
|
|
// Set data using font and toggle
|
|
// If successful, returns true
|
|
bool font2string(Font const & font, bool const toggle, string & data)
|
|
{
|
|
string lang = "ignore";
|
|
if (font.language())
|
|
lang = font.language()->lang();
|
|
|
|
ostringstream os;
|
|
os << "family " << font.family() << '\n'
|
|
<< "series " << font.series() << '\n'
|
|
<< "shape " << font.shape() << '\n'
|
|
<< "size " << font.size() << '\n'
|
|
<< "emph " << font.emph() << '\n'
|
|
<< "underbar " << font.underbar() << '\n'
|
|
<< "noun " << font.noun() << '\n'
|
|
<< "number " << font.number() << '\n'
|
|
<< "color " << font.color() << '\n'
|
|
<< "language " << lang << '\n'
|
|
<< "toggleall " << convert<string>(toggle);
|
|
data = os.str();
|
|
return true;
|
|
}
|
|
|
|
|
|
// Set font and toggle using data
|
|
// If successful, returns true
|
|
bool string2font(string const & data, Font & font, bool & toggle)
|
|
{
|
|
istringstream is(data);
|
|
Lexer lex(0,0);
|
|
lex.setStream(is);
|
|
|
|
int nset = 0;
|
|
while (lex.isOK()) {
|
|
string token;
|
|
if (lex.next())
|
|
token = lex.getString();
|
|
|
|
if (token.empty() || !lex.next())
|
|
break;
|
|
|
|
if (token == "family") {
|
|
int const next = lex.getInteger();
|
|
font.setFamily(Font::FONT_FAMILY(next));
|
|
|
|
} else if (token == "series") {
|
|
int const next = lex.getInteger();
|
|
font.setSeries(Font::FONT_SERIES(next));
|
|
|
|
} else if (token == "shape") {
|
|
int const next = lex.getInteger();
|
|
font.setShape(Font::FONT_SHAPE(next));
|
|
|
|
} else if (token == "size") {
|
|
int const next = lex.getInteger();
|
|
font.setSize(Font::FONT_SIZE(next));
|
|
|
|
} else if (token == "emph" || token == "underbar" ||
|
|
token == "noun" || token == "number") {
|
|
|
|
int const next = lex.getInteger();
|
|
Font::FONT_MISC_STATE const misc =
|
|
Font::FONT_MISC_STATE(next);
|
|
|
|
if (token == "emph")
|
|
font.setEmph(misc);
|
|
else if (token == "underbar")
|
|
font.setUnderbar(misc);
|
|
else if (token == "noun")
|
|
font.setNoun(misc);
|
|
else if (token == "number")
|
|
font.setNumber(misc);
|
|
|
|
} else if (token == "color") {
|
|
int const next = lex.getInteger();
|
|
font.setColor(Color::color(next));
|
|
|
|
} else if (token == "language") {
|
|
string const next = lex.getString();
|
|
if (next == "ignore")
|
|
font.setLanguage(ignore_language);
|
|
else
|
|
font.setLanguage(languages.getLanguage(next));
|
|
|
|
} else if (token == "toggleall") {
|
|
toggle = lex.getBool();
|
|
|
|
} else {
|
|
// Unrecognised token
|
|
break;
|
|
}
|
|
|
|
++nset;
|
|
}
|
|
return (nset > 0);
|
|
}
|
|
|
|
|
|
// the next two should probably go elsewhere
|
|
// this give the position relative to (0, baseline) of outermost
|
|
// paragraph
|
|
Point coordOffset(BufferView const & bv, DocIterator const & dit,
|
|
bool boundary)
|
|
{
|
|
int x = 0;
|
|
int y = 0;
|
|
int lastw = 0;
|
|
|
|
// Addup ontribution of nested insets, from inside to outside,
|
|
// keeping the outer paragraph for a special handling below
|
|
for (size_t i = dit.depth() - 1; i >= 1; --i) {
|
|
CursorSlice const & sl = dit[i];
|
|
int xx = 0;
|
|
int yy = 0;
|
|
|
|
// get relative position inside sl.inset()
|
|
sl.inset().cursorPos(bv, sl, boundary && ((i+1) == dit.depth()), xx, yy);
|
|
|
|
// Make relative position inside of the edited inset relative to sl.inset()
|
|
x += xx;
|
|
y += yy;
|
|
|
|
// In case of an RTL inset, the edited inset will be positioned to the left
|
|
// of xx:yy
|
|
if (sl.text()) {
|
|
bool boundary_i = boundary && i + 1 == dit.depth();
|
|
bool rtl = sl.text()->isRTL(*bv.buffer(), sl, boundary_i);
|
|
if (rtl)
|
|
x -= lastw;
|
|
}
|
|
|
|
// remember width for the case that sl.inset() is positioned in an RTL inset
|
|
lastw = sl.inset().width();
|
|
|
|
//lyxerr << "Cursor::getPos, i: "
|
|
// << i << " x: " << xx << " y: " << y << endl;
|
|
}
|
|
|
|
// Add contribution of initial rows of outermost paragraph
|
|
CursorSlice const & sl = dit[0];
|
|
ParagraphMetrics const & pm = bv.parMetrics(sl.text(), sl.pit());
|
|
BOOST_ASSERT(!pm.rows().empty());
|
|
y -= pm.rows()[0].ascent();
|
|
#if 1
|
|
// FIXME: document this mess
|
|
size_t rend;
|
|
if (sl.pos() > 0 && dit.depth() == 1) {
|
|
int pos = sl.pos();
|
|
if (pos && boundary)
|
|
--pos;
|
|
// lyxerr << "coordOffset: boundary:" << boundary << " depth:" << dit.depth() << " pos:" << pos << " sl.pos:" << sl.pos() << std::endl;
|
|
rend = pm.pos2row(pos);
|
|
} else
|
|
rend = pm.pos2row(sl.pos());
|
|
#else
|
|
size_t rend = pm.pos2row(sl.pos());
|
|
#endif
|
|
for (size_t rit = 0; rit != rend; ++rit)
|
|
y += pm.rows()[rit].height();
|
|
y += pm.rows()[rend].ascent();
|
|
|
|
// Make relative position from the nested inset now bufferview absolute.
|
|
int xx = dit.bottom().text()->cursorX(bv, dit.bottom(), boundary && dit.depth() == 1);
|
|
x += xx;
|
|
|
|
// In the RTL case place the nested inset at the left of the cursor in
|
|
// the outer paragraph
|
|
bool boundary_1 = boundary && 1 == dit.depth();
|
|
bool rtl = dit.bottom().text()->isRTL(*bv.buffer(), dit.bottom(), boundary_1);
|
|
if (rtl)
|
|
x -= lastw;
|
|
|
|
return Point(x, y);
|
|
}
|
|
|
|
|
|
Point getPos(BufferView const & bv, DocIterator const & dit, bool boundary)
|
|
{
|
|
CursorSlice const & bot = dit.bottom();
|
|
CoordCache::ParPosCache::const_iterator cache_it =
|
|
bv.coordCache().getParPos().find(bot.text());
|
|
if (cache_it == bv.coordCache().getParPos().end())
|
|
return Point(-1, -1);
|
|
|
|
CoordCache::InnerParPosCache const & cache = cache_it->second;
|
|
CoordCache::InnerParPosCache::const_iterator it = cache.find(bot.pit());
|
|
if (it == cache.end()) {
|
|
//lyxerr << "cursor out of view" << std::endl;
|
|
return Point(-1, -1);
|
|
}
|
|
Point p = coordOffset(bv, dit, boundary); // offset from outer paragraph
|
|
p.y_ += it->second.y_;
|
|
return p;
|
|
}
|
|
|
|
|
|
// this could be used elsewhere as well?
|
|
// FIXME: This does not work within mathed!
|
|
CurStatus status(BufferView const * bv, DocIterator const & dit)
|
|
{
|
|
CoordCache::InnerParPosCache const & cache =
|
|
bv->coordCache().getParPos().find(dit.bottom().text())->second;
|
|
|
|
if (cache.find(dit.bottom().pit()) != cache.end())
|
|
return CUR_INSIDE;
|
|
else if (dit.bottom().pit() < bv->anchor_ref())
|
|
return CUR_ABOVE;
|
|
else
|
|
return CUR_BELOW;
|
|
}
|
|
|
|
namespace {
|
|
|
|
bool findNextInset(DocIterator & dit,
|
|
vector<Inset_code> const & codes,
|
|
string const & contents)
|
|
{
|
|
DocIterator tmpdit = dit;
|
|
|
|
while (tmpdit) {
|
|
Inset const * inset = tmpdit.nextInset();
|
|
if (inset
|
|
&& find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end()
|
|
&& (contents.empty() ||
|
|
static_cast<InsetCommand const *>(inset)->getContents() == contents)) {
|
|
dit = tmpdit;
|
|
return true;
|
|
}
|
|
tmpdit.forwardInset();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
} // namespace anon
|
|
|
|
|
|
bool findInset(DocIterator & dit, vector<Inset_code> const & codes,
|
|
bool same_content)
|
|
{
|
|
string contents;
|
|
DocIterator tmpdit = dit;
|
|
tmpdit.forwardInset();
|
|
if (!tmpdit)
|
|
return false;
|
|
|
|
if (same_content) {
|
|
Inset const * inset = tmpdit.nextInset();
|
|
if (inset
|
|
&& find(codes.begin(), codes.end(), inset->lyxCode()) != codes.end()) {
|
|
contents = static_cast<InsetCommand const *>(inset)->getContents();
|
|
}
|
|
}
|
|
|
|
if (!findNextInset(tmpdit, codes, contents)) {
|
|
if (dit.depth() != 1 || dit.pit() != 0 || dit.pos() != 0) {
|
|
tmpdit = doc_iterator_begin(tmpdit.bottom().inset());
|
|
if (!findNextInset(tmpdit, codes, contents)) {
|
|
return false;
|
|
}
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
dit = tmpdit;
|
|
return true;
|
|
}
|
|
|
|
|
|
void findInset(DocIterator & dit, Inset_code code, bool same_content)
|
|
{
|
|
findInset(dit, vector<Inset_code>(1, code), same_content);
|
|
}
|
|
|
|
|
|
void gotoInset(BufferView * bv, vector<Inset_code> const & codes,
|
|
bool same_content)
|
|
{
|
|
Cursor tmpcur = bv->cursor();
|
|
if (!findInset(tmpcur, codes, same_content)) {
|
|
bv->cursor().message(_("No more insets"));
|
|
return;
|
|
}
|
|
|
|
tmpcur.clearSelection();
|
|
bv->setCursor(tmpcur);
|
|
}
|
|
|
|
|
|
void gotoInset(BufferView * bv, Inset_code code, bool same_content)
|
|
{
|
|
gotoInset(bv, vector<Inset_code>(1, code), same_content);
|
|
}
|
|
|
|
|
|
} // namespace bv_funcs
|
|
|
|
|
|
} // namespace lyx
|