*
* \author Asger Alstrup
* \author Lars Gullik Bjønnes
+ * \author Dov Feldstern
* \author Jean-Marc Lasgouttes
* \author John Levon
* \author André Pönitz
+ * \author Stefan Schimanski
* \author Dekel Tsur
* \author Jürgen Vigna
*
#endif
} else if (token == "\\begin_layout") {
lex.eatLine();
- string layoutname = lex.getString();
+ docstring layoutname = lex.getDocString();
font = Font(Font::ALL_INHERIT, bp.language);
change = Change(Change::UNCHANGED);
if (!hasLayout) {
errorList.push_back(ErrorItem(_("Unknown layout"),
bformat(_("Layout '%1$s' does not exist in textclass '%2$s'\nTrying to use the default instead.\n"),
- from_utf8(layoutname), from_utf8(tclass.name())), par.id(), 0, par.size()));
+ layoutname, from_utf8(tclass.name())), par.id(), 0, par.size()));
layoutname = tclass.defaultLayoutName();
}
if (isPrintable(c)) {
Language const * language = font.language();
if (language->rightToLeft()) {
- if (language->lang() == "arabic") {
+ if (language->lang() == "arabic_arabtex" ||
+ language->lang() == "arabic_arabi" ||
+ language->lang() == "farsi") {
if (Encodings::isComposeChar_arabic(c))
return 0;
c = par.transformChar(c, pos);
// row in this paragraph.
RowList::iterator rit = par.rows().begin();
RowList::iterator end = par.rows().end();
-#ifdef WITH_WARNINGS
-#warning This is wrong.
-#endif
+ // FIXME: This is wrong.
int minfill = max_width;
for ( ; rit != end; ++rit)
if (rit->fill() < minfill)
break; // the character couldn't be deleted physically due to change tracking
}
- ParIterator current_it(cur);
- ParIterator last_it(cur);
- ++last_it;
- ++last_it;
-
- updateLabels(cur.buffer(), current_it, last_it);
+ updateLabels(cur.buffer());
// A singlePar update is not enough in this case.
cur.updateFlags(Update::Force);
}
}
- // 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...).
+
+ if ((cur.pos() >= 2) && (par.isLineSeparator(cur.pos() - 1))) {
+ // get font in front and behind the space in question. But do NOT
+ // use getFont(cur.pos()) because the character c is not inserted yet
+ Font const & pre_space_font = getFont(buffer, par, cur.pos() - 2);
+ Font const & post_space_font = real_current_font;
+ 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; don't touch other properties of the font
+ Language const * lang =
+ (pre_space_rtl == par.isRightToLeftPar(buffer.params())) ?
+ pre_space_font.language() : post_space_font.language();
+
+ Font space_font = getFont(buffer, par, cur.pos() - 1);
+ space_font.setLanguage(lang);
+ par.setFont(cur.pos() - 1, 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
cur.resetAnchor();
setCursor(cur, to.pit(), to.pos());
cur.setSelection();
- cap::saveSelection(cur);
}
bool endsBeforeEndOfPar = (endPos < pars_[endPit].size());
// first, accept/reject changes within each individual paragraph (do not consider end-of-par)
-
+
for (pit_type pit = begPit; pit <= endPit; ++pit) {
pos_type parSize = pars_[pit].size();
continue;
// do not consider last paragraph if the cursor ends at pos 0
- if (pit == endPit && endPos == 0)
+ if (pit == endPit && endPos == 0)
break; // last iteration anyway
pos_type left = (pit == begPit ? begPos : 0);
pars_[pit].rejectChanges(cur.buffer().params(), left, right);
}
}
-
+
// next, accept/reject imaginary end-of-par characters
-
+
for (pit_type pit = begPit; pit <= endPit; ++pit) {
pos_type pos = pars_[pit].size();
deleteEmptyParagraphMechanism(begPit, endPit, cur.buffer().params().trackChanges);
- //
+ //
finishUndo();
cur.clearSelection();
void Text::acceptChanges(BufferParams const & bparams)
{
- pit_type pars_size = static_cast<pit_type>(pars_.size());
-
- // first, accept changes within each individual paragraph
- // (do not consider end-of-par)
- for (pit_type pit = 0; pit < pars_size; ++pit) {
- if (!pars_[pit].empty()) // prevent assertion failure
- pars_[pit].acceptChanges(bparams, 0, pars_[pit].size());
- }
-
- // next, accept imaginary end-of-par characters
- for (pit_type pit = 0; pit < pars_size; ++pit) {
- pos_type pos = pars_[pit].size();
-
- if (pars_[pit].isInserted(pos)) {
- pars_[pit].setChange(pos, Change(Change::UNCHANGED));
- } else if (pars_[pit].isDeleted(pos)) {
- if (pit == pars_size - 1) {
- // we cannot remove a par break at the end of the last
- // paragraph; instead, we mark it unchanged
- pars_[pit].setChange(pos, Change(Change::UNCHANGED));
- } else {
- mergeParagraph(bparams, pars_, pit);
- --pit;
- --pars_size;
- }
- }
- }
-
- // finally, invoke the DEPM
- deleteEmptyParagraphMechanism(0, pars_size - 1, bparams.trackChanges);
+ lyx::acceptChanges(pars_, bparams);
+ deleteEmptyParagraphMechanism(0, pars_.size() - 1, bparams.trackChanges);
}
pit_type pars_size = static_cast<pit_type>(pars_.size());
// first, reject changes within each individual paragraph
- // (do not consider end-of-par)
+ // (do not consider end-of-par)
for (pit_type pit = 0; pit < pars_size; ++pit) {
- if (!pars_[pit].empty()) // prevent assertion failure
+ if (!pars_[pit].empty()) // prevent assertion failure
pars_[pit].rejectChanges(bparams, 0, pars_[pit].size());
}
pos_type pos = (pit == begPit ? begPos : 0);
right = (pit == endPit ? endPos : parSize);
- // process sequences of modified characters; in change
+ // process sequences of modified characters; in change
// tracking mode, this approach results in much better
// usability than changing case on a char-by-char basis
docstring changes;
recordUndo(cur, Undo::DELETE);
if(!par.eraseChar(cur.pos(), cur.buffer().params().trackChanges)) {
// the character has been logically deleted only => skip it
- cur.forwardPosNoDescend();
+ cur.top().forwardPos();
}
checkBufferStructure(cur.buffer(), cur);
needsUpdate = true;
cur.text()->setCursor(cur.top(), cur.pit(), cur.pos());
checkBufferStructure(cur.buffer(), cur);
}
-
+
return needsUpdate;
}
Paragraph const & prevpar = prevcur.paragraph();
// is it an empty paragraph?
- if (cur.lastpos() == 0
+ if (cur.lastpos() == 0
|| (cur.lastpos() == 1 && par.isSeparator(0))) {
recordUndo(cur, Undo::ATOMIC, prevcur.pit(), cur.pit());
plist.erase(boost::next(plist.begin(), cur.pit()));
needsUpdate = true;
}
// is previous par empty?
- else if (prevcur.lastpos() == 0
+ else if (prevcur.lastpos() == 0
|| (prevcur.lastpos() == 1 && prevpar.isSeparator(0))) {
recordUndo(cur, Undo::ATOMIC, prevcur.pit(), cur.pit());
plist.erase(boost::next(plist.begin(), prevcur.pit()));
DocIterator end = cur.selectionEnd();
BufferView & bv = *pi.base.bv;
- Buffer const & buffer = *bv.buffer();
- // the selection doesn't touch the visible screen
+ // the selection doesn't touch the visible screen?
if (bv_funcs::status(&bv, beg) == bv_funcs::CUR_BELOW
|| bv_funcs::status(&bv, end) == bv_funcs::CUR_ABOVE)
return;
- Paragraph const & par1 = pars_[beg.pit()];
- Paragraph const & par2 = pars_[end.pit()];
TextMetrics const & tm = bv.textMetrics(this);
ParagraphMetrics const & pm1 = tm.parMetrics(beg.pit());
ParagraphMetrics const & pm2 = tm.parMetrics(end.pit());
+ Row const & row1 = pm1.getRow(beg.pos(), beg.boundary());
+ Row const & row2 = pm2.getRow(end.pos(), end.boundary());
+
+ // clip above
+ int middleTop;
+ bool const clipAbove =
+ (bv_funcs::status(&bv, beg) == bv_funcs::CUR_ABOVE);
+ if (clipAbove)
+ middleTop = 0;
+ else
+ middleTop = bv_funcs::getPos(bv, beg, beg.boundary()).y_ + row1.descent();
+
+ // clip below
+ int middleBottom;
+ bool const clipBelow =
+ (bv_funcs::status(&bv, end) == bv_funcs::CUR_BELOW);
+ if (clipBelow)
+ middleBottom = bv.workHeight();
+ else
+ middleBottom = bv_funcs::getPos(bv, end, end.boundary()).y_ - row2.ascent();
- bool const above = (bv_funcs::status(&bv, beg)
- == bv_funcs::CUR_ABOVE);
- bool const below = (bv_funcs::status(&bv, end)
- == bv_funcs::CUR_BELOW);
- int y1,y2,x1,x2;
- if (above) {
- y1 = 0;
- y2 = 0;
- x1 = 0;
- x2 = tm.width();
- } else {
- Row const & row1 = pm1.getRow(beg.pos(), beg.boundary());
- y1 = bv_funcs::getPos(bv, beg, beg.boundary()).y_ - row1.ascent();
- y2 = y1 + row1.height();
- int const startx = cursorX(bv, beg.top(), beg.boundary());
- if (!isRTL(buffer, par1)) {
- x1 = startx;
- x2 = 0 + tm.width();
+ // start and end in the same line?
+ if (!(clipAbove || clipBelow) && &row1 == &row2)
+ // then only draw this row's selection
+ drawRowSelection(pi, x, row1, beg, end, false, false);
+ else {
+ if (!clipAbove) {
+ // get row end
+ DocIterator begRowEnd = beg;
+ begRowEnd.pos() = row1.endpos();
+ begRowEnd.boundary(true);
+
+ // draw upper rectangle
+ drawRowSelection(pi, x, row1, beg, begRowEnd, false, true);
}
- else {
- x1 = 0;
- x2 = startx;
+
+ if (middleTop < middleBottom) {
+ // draw middle rectangle
+ pi.pain.fillRectangle(x, middleTop,
+ tm.width(), middleBottom - middleTop,
+ Color::selection);
}
- }
- int Y1,Y2,X1,X2;
- if (below) {
- Y1 = bv.workHeight();
- Y2 = bv.workHeight();
- X1 = 0;
- X2 = tm.width();
- } else {
- Row const & row2 = pm2.getRow(end.pos(), end.boundary());
- Y1 = bv_funcs::getPos(bv, end, end.boundary()).y_ - row2.ascent();
- Y2 = Y1 + row2.height();
- int const endx = cursorX(bv, end.top(), end.boundary());
- if (!isRTL(buffer, par2)) {
- X1 = 0;
- X2 = endx;
- }
- else {
- X1 = endx;
- X2 = 0 + tm.width();
+ if (!clipBelow) {
+ // get row begin
+ DocIterator endRowBeg = end;
+ endRowBeg.pos() = row2.pos();
+ endRowBeg.boundary(false);
+
+ // draw low rectangle
+ drawRowSelection(pi, x, row2, endRowBeg, end, true, false);
}
}
+}
- if (!above && !below && &pm1.getRow(beg.pos(), beg.boundary())
- == &pm2.getRow(end.pos(), end.boundary()))
- {
- // paint only one rectangle
- int const b( !isRTL(*bv.buffer(), par1) ? x + x1 : x + X1 );
- int const w( !isRTL(*bv.buffer(), par1) ? X2 - x1 : x2 - X1 );
- pi.pain.fillRectangle(b, y1, w, y2 - y1, Color::selection);
- return;
- }
-
- LYXERR(Debug::DEBUG) << " y1: " << y1 << " y2: " << y2
- << "X1:" << X1 << " x2: " << X2 << " wid: " << tm.width()
- << endl;
- // paint upper rectangle
- pi.pain.fillRectangle(x + x1, y1, x2 - x1, y2 - y1,
- Color::selection);
- // paint bottom rectangle
- pi.pain.fillRectangle(x + X1, Y1, X2 - X1, Y2 - Y1,
- Color::selection);
- // paint center rectangle
- pi.pain.fillRectangle(x, y2, tm.width(),
- Y1 - y2, Color::selection);
+void Text::drawRowSelection(PainterInfo & pi, int x, Row const & row,
+ DocIterator const & beg, DocIterator const & end,
+ bool drawOnBegMargin, bool drawOnEndMargin) const
+{
+ BufferView & bv = *pi.base.bv;
+ Buffer & buffer = *bv.buffer();
+ TextMetrics const & tm = bv.textMetrics(this);
+ DocIterator cur = beg;
+ int x1 = cursorX(bv, beg.top(), beg.boundary());
+ int x2 = cursorX(bv, end.top(), end.boundary());
+ int y1 = bv_funcs::getPos(bv, cur, cur.boundary()).y_ - row.ascent();
+ int y2 = y1 + row.height();
+
+ // draw the margins
+ if (drawOnBegMargin) {
+ if (isRTL(buffer, beg.paragraph()))
+ pi.pain.fillRectangle(x + x1, y1, tm.width() - x1, y2 - y1, Color::selection);
+ else
+ pi.pain.fillRectangle(x, y1, x1, y2 - y1, Color::selection);
+ }
+
+ if (drawOnEndMargin) {
+ if (isRTL(buffer, beg.paragraph()))
+ pi.pain.fillRectangle(x, y1, x2, y2 - y1, Color::selection);
+ else
+ pi.pain.fillRectangle(x + x2, y1, tm.width() - x2, y2 - y1, Color::selection);
+ }
+
+ // if we are on a boundary from the beginning, it's probably
+ // a RTL boundary and we jump to the other side directly as this
+ // segement is 0-size and confuses the logic below
+ if (cur.boundary())
+ cur.boundary(false);
+
+ // go through row and draw from RTL boundary to RTL boundary
+ while (cur < end) {
+ bool drawNow = false;
+
+ // simplified cursorRight code below which does not
+ // descend into insets and which does not go into the
+ // next line. Compare the logic with the original cursorRight
+
+ // if left of boundary -> just jump to right side
+ // but for RTL boundaries don't, because: abc|DDEEFFghi -> abcDDEEF|Fghi
+ if (cur.boundary()) {
+ cur.boundary(false);
+ } else if (isRTLBoundary(buffer, cur.paragraph(), cur.pos() + 1)) {
+ // in front of RTL boundary -> Stay on this side of the boundary because:
+ // ab|cDDEEFFghi -> abc|DDEEFFghi
+ ++cur.pos();
+ cur.boundary(true);
+ drawNow = true;
+ } else {
+ // move right
+ ++cur.pos();
+
+ // line end?
+ if (cur.pos() == row.endpos())
+ cur.boundary(true);
+ }
+
+ if (x1 == -1) {
+ // the previous segment was just drawn, now the next starts
+ x1 = cursorX(bv, cur.top(), cur.boundary());
+ }
+
+ if (!(cur < end) || drawNow) {
+ x2 = cursorX(bv, cur.top(), cur.boundary());
+ pi.pain.fillRectangle(x + min(x1,x2), y1, abs(x2 - x1), y2 - y1,
+ Color::selection);
+
+ // reset x1, so it is set again next round (which will be on the
+ // right side of a boundary or at the selection end)
+ x1 = -1;
+ }
+ }
}
+
bool Text::isLastRow(pit_type pit, Row const & row) const
{
return row.endpos() >= pars_[pit].size()
depth_type dth = 0;
for (; pit != end; ++pit)
pit->write(buf, os, buf.params(), dth);
+
+ // Close begin_deeper
+ for(; dth > 0; --dth)
+ os << "\n\\end_deeper";
}
Buffer const & buffer = *bv.buffer();
RowMetrics const m = tm.computeRowMetrics(pit, row);
double x = m.x;
+ Bidi bidi;
+ bidi.computeTables(par, buffer, row);
pos_type const row_pos = row.pos();
pos_type const end = row.endpos();
+ // Spaces at logical line breaks in bidi text must be skipped during
+ // cursor positioning. However, they may appear visually in the middle
+ // of a row; they must be skipped, wherever they are...
+ // * logically "abc_[HEBREW_\nHEBREW]"
+ // * visually "abc_[_WERBEH\nWERBEH]"
+ pos_type skipped_sep_vpos = -1;
if (end <= row_pos)
cursor_vpos = row_pos;
FontMetrics const & labelfm = theFontMetrics(
getLabelFont(buffer, par));
+ // If the last logical character is a separator, skip it, unless
+ // it's in the last row of a paragraph; see skipped_sep_vpos declaration
+ if (end > 0 && end < par.size() && par.isSeparator(end - 1))
+ skipped_sep_vpos = bidi.log2vis(end - 1);
+
for (pos_type vpos = row_pos; vpos < cursor_vpos; ++vpos) {
+ // Skip the separator which is at the logical end of the row
+ if (vpos == skipped_sep_vpos)
+ continue;
pos_type pos = bidi.vis2log(vpos);
if (body_pos > 0 && pos == body_pos - 1) {
// FIXME UNICODE
}
// see correction above
- if (boundary_correction)
- if (getFont(buffer, par, ppos).isVisibleRightToLeft())
+ if (boundary_correction) {
+ if (isRTL(buffer, sl, boundary))
x -= singleWidth(buffer, par, ppos);
else
x += singleWidth(buffer, par, ppos);
-
- // Make sure inside an inset we always count from the left
- // edge (bidi!) -- MV
- if (sl.pos() < par.size()) {
- font = getFont(buffer, par, sl.pos());
- if (!boundary && font.isVisibleRightToLeft()
- && par.isInset(sl.pos()))
- x -= par.getInset(sl.pos())->width();
}
+
return int(x);
}
// types of insets and take our abbreviation from them.
if (name.empty()) {
Inset::Code const codes[] = {
- Inset::FLOAT_CODE,
+ Inset::FLOAT_CODE,
Inset::WRAP_CODE,
Inset::FOOT_CODE
};
}
}
}
-
+
// Create a correct prefix for prettyref
if (name == "theorem")
name = from_ascii("thm");
else if (name == "Foot")
name = from_ascii("fn");
-
+ else if (name == "listing")
+ name = from_ascii("lst");
+
if (!name.empty())
text = name.substr(0, 3) + ':' + text;
bool bound = false;
int xx = x;
- pos_type const pos = row.pos()
+ pos_type const pos = row.pos()
+ tm.getColumnNearX(pit, row, xx, bound);
LYXERR(Debug::DEBUG)
Paragraph & par = cur.paragraph();
- // Get the positions of the characters to be transposed.
+ // Get the positions of the characters to be transposed.
pos_type pos1 = pos - 1;
pos_type pos2 = pos;
char_type char1 = par.getChar(pos1);
Font const font1 =
par.getFontSettings(cur.buffer().params(), pos1);
-
+
char_type char2 = par.getChar(pos2);
Font const font2 =
par.getFontSettings(cur.buffer().params(), pos2);