#include "insets/insettext.h"
#include "insets/insetbibitem.h"
+#include "insets/insetcaption.h"
#include "insets/insethfill.h"
-#include "insets/insetlatexaccent.h"
#include "insets/insetline.h"
#include "insets/insetnewline.h"
#include "insets/insetpagebreak.h"
par.insertInset(par.size(), inset.release(),
font, change);
}
- } else if (token == "\\i") {
- auto_ptr<InsetBase> inset(new InsetLatexAccent);
- inset->read(buf, lex);
- par.insertInset(par.size(), inset.release(), font, change);
} else if (token == "\\backslash") {
par.insertChar(par.size(), '\\', font, change);
} else if (token == "\\newline") {
break;
}
- lyxerr[Debug::PARSER] << "Handling paragraph token: `"
+ LYXERR(Debug::PARSER) << "Handling paragraph token: `"
<< token << '\'' << endl;
if (token == "\\begin_layout" || token == "\\end_document"
|| token == "\\end_inset" || token == "\\begin_deeper"
if (isPrintable(c)) {
Language const * language = font.language();
if (language->rightToLeft()) {
- if ((lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 ||
- lyxrc.font_norm_type == LyXRC::ISO_10646_1)
- && language->lang() == "arabic") {
+ if (language->lang() == "arabic") {
if (Encodings::isComposeChar_arabic(c))
return 0;
c = par.transformChar(c, pos);
pars_[next_par].applyLayout(tclass.defaultLayout());
}
- while (!pars_[next_par].empty() && pars_[next_par].isNewline(0))
- pars_[next_par].eraseChar(0, cur.buffer().params().trackChanges);
+ while (!pars_[next_par].empty() && pars_[next_par].isNewline(0)) {
+ if (!pars_[next_par].eraseChar(0, cur.buffer().params().trackChanges))
+ break; // the character couldn't be deleted physically due to change tracking
+ }
ParIterator current_it(cur);
ParIterator last_it(cur);
// This check is necessary. Otherwise the new empty paragraph will
// be deleted automatically. And it is more friendly for the user!
if (cur.pos() != 0 || isempty)
- setCursor(cur.top(), cur.pit() + 1, 0);
+ setCursor(cur, cur.pit() + 1, 0);
else
- setCursor(cur.top(), cur.pit(), 0);
+ setCursor(cur, cur.pit(), 0);
}
}
par.insertChar(cur.pos(), c, current_font, cur.buffer().params().trackChanges);
+ checkBufferStructure(cur.buffer(), cur);
// cur.updateFlags(Update::Force);
setCursor(cur.top(), cur.pit(), cur.pos() + 1);
cur.resetAnchor();
setCursor(cur, to.pit(), to.pos());
cur.setSelection();
+ cap::saveSelection(cur);
}
}
-void LyXText::acceptChange(LCursor & cur)
+void LyXText::acceptOrRejectChanges(LCursor & cur, ChangeOp op)
{
- // FIXME: change tracking (MG)
-
BOOST_ASSERT(this == cur.text());
- if (!cur.selection() && cur.lastpos() != 0)
+ if (!cur.selection())
return;
- // FIXME: we must handle start = end = 0
+ recordUndoSelection(cur, Undo::ATOMIC);
+
+ pit_type begPit = cur.selectionBegin().pit();
+ pit_type endPit = cur.selectionEnd().pit();
+
+ pos_type begPos = cur.selectionBegin().pos();
+ pos_type endPos = cur.selectionEnd().pos();
+
+ // keep selection info, because endPos becomes invalid after the first loop
+ 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();
+
+ // ignore empty paragraphs; otherwise, an assertion will fail for
+ // acceptChanges(bparams, 0, 0) or rejectChanges(bparams, 0, 0)
+ if (parSize == 0)
+ continue;
+
+ // do not consider first paragraph if the cursor starts at pos size()
+ if (pit == begPit && begPos == parSize)
+ continue;
- recordUndoSelection(cur, Undo::INSERT);
+ // do not consider last paragraph if the cursor ends at pos 0
+ if (pit == endPit && endPos == 0)
+ break; // last iteration anyway
- DocIterator it = cur.selectionBegin();
- DocIterator et = cur.selectionEnd();
- pit_type pit = it.pit();
- for (; pit <= et.pit(); ++pit) {
- pos_type left = (pit == it.pit() ? it.pos() : 0);
- pos_type right = (pit == et.pit() ? et.pos() : pars_[pit].size());
- pars_[pit].acceptChanges(left, right);
+ pos_type left = (pit == begPit ? begPos : 0);
+ pos_type right = (pit == endPit ? endPos : parSize);
- // merge paragraph if appropriate:
- // if (right >= pars_[pit].size() && pit + 1 < et.pit() &&
- // pars_[pit].isDeleted(pars_[pit].size())) {
- // setCursorIntern(cur, pit + 1, 0);
- // backspacePos0(cur);
- //}
+ if (op == ACCEPT) {
+ pars_[pit].acceptChanges(cur.buffer().params(), left, right);
+ } else {
+ 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();
+
+ // skip if the selection ends before the end-of-par
+ if (pit == endPit && endsBeforeEndOfPar)
+ break; // last iteration anyway
+
+ // skip if this is not the last paragraph of the document
+ // note: the user should be able to accept/reject the par break of the last par!
+ if (pit == endPit && pit != pars_.size() - 1)
+ break; // last iteration anway
+
+ if (op == ACCEPT) {
+ 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(cur.buffer().params(), pars_, pit);
+ --endPit;
+ --pit;
+ }
+ }
+ } else {
+ if (pars_[pit].isDeleted(pos)) {
+ pars_[pit].setChange(pos, Change(Change::UNCHANGED));
+ } else if (pars_[pit].isInserted(pos)) {
+ if (pit == pars_.size() - 1) {
+ // we mark the par break at the end of the last paragraph unchanged
+ pars_[pit].setChange(pos, Change(Change::UNCHANGED));
+ } else {
+ mergeParagraph(cur.buffer().params(), pars_, pit);
+ --endPit;
+ --pit;
+ }
+ }
+ }
+ }
+
+ // finally, invoke the DEPM
+
+ deleteEmptyParagraphMechanism(begPit, endPit, cur.buffer().params().trackChanges);
+
+ //
+
finishUndo();
cur.clearSelection();
- setCursorIntern(cur, it.pit(), 0);
+ setCursorIntern(cur, begPit, begPos);
+ cur.updateFlags(Update::Force);
+ updateLabels(cur.buffer());
}
-void LyXText::rejectChange(LCursor & cur)
+void LyXText::acceptChanges(BufferParams const & bparams)
{
- // FIXME: change tracking (MG)
+ pit_type pars_size = static_cast<pit_type>(pars_.size());
- BOOST_ASSERT(this == cur.text());
+ // 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());
+ }
- if (!cur.selection() && cur.lastpos() != 0)
- return;
+ // 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;
+ }
+ }
+ }
- // FIXME: we must handle start = end = 0
+ // finally, invoke the DEPM
+ deleteEmptyParagraphMechanism(0, pars_size - 1, bparams.trackChanges);
+}
- recordUndoSelection(cur, Undo::INSERT);
- DocIterator it = cur.selectionBegin();
- DocIterator et = cur.selectionEnd();
- pit_type pit = it.pit();
- for (; pit <= et.pit(); ++pit) {
- pos_type left = (pit == it.pit() ? it.pos() : 0);
- pos_type right = (pit == et.pit() ? et.pos() : pars_[pit].size());
- pars_[pit].rejectChanges(left, right);
+void LyXText::rejectChanges(BufferParams const & bparams)
+{
+ pit_type pars_size = static_cast<pit_type>(pars_.size());
- // merge paragraph if appropriate:
- // if (right >= pars_[pit].size() && pit + 1 < et.pit() &&
- // pars_[pit].isInserted(pars_[pit].size())) {
- // setCursorIntern(cur, pit + 1, 0);
- // backspacePos0(cur);
- //}
+ // first, reject 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].rejectChanges(bparams, 0, pars_[pit].size());
}
- finishUndo();
- cur.clearSelection();
- setCursorIntern(cur, it.pit(), 0);
+
+ // next, reject imaginary end-of-par characters
+ for (pit_type pit = 0; pit < pars_size; ++pit) {
+ pos_type pos = pars_[pit].size();
+
+ if (pars_[pit].isDeleted(pos)) {
+ pars_[pit].setChange(pos, Change(Change::UNCHANGED));
+ } else if (pars_[pit].isInserted(pos)) {
+ if (pit == pars_size - 1) {
+ // we mark the par break at the end of the last
+ // paragraph 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);
}
cursorRightOneWord(cur);
cur.setSelection();
cutSelection(cur, true, false);
+ checkBufferStructure(cur.buffer(), cur);
}
}
cursorLeftOneWord(cur);
cur.setSelection();
cutSelection(cur, true, false);
+ checkBufferStructure(cur.buffer(), cur);
}
}
deleteWordForward(cur);
else
cutSelection(cur, true, false);
+ checkBufferStructure(cur.buffer(), cur);
}
}
cursorRightOneWord(cur);
}
- recordUndoSelection(cur);
+ recordUndoSelection(cur, Undo::ATOMIC);
- pos_type pos = from.pos();
- int par = from.pit();
+ pit_type begPit = from.pit();
+ pit_type endPit = to.pit();
- while (par != int(pars_.size()) && (pos != to.pos() || par != to.pit())) {
- pit_type pit = par;
- if (pos == pars_[pit].size()) {
- ++par;
- pos = 0;
- continue;
- }
- char_type c = pars_[pit].getChar(pos);
- if (c != Paragraph::META_INSET) {
- switch (action) {
- case text_lowercase:
- c = lowercase(c);
- break;
- case text_capitalization:
- c = uppercase(c);
- action = text_lowercase;
- break;
- case text_uppercase:
- c = uppercase(c);
- break;
+ pos_type begPos = from.pos();
+ pos_type endPos = to.pos();
+
+ bool const trackChanges = cur.buffer().params().trackChanges;
+
+ pos_type right = 0; // needed after the for loop
+
+ for (pit_type pit = begPit; pit <= endPit; ++pit) {
+ pos_type parSize = pars_[pit].size();
+
+ pos_type pos = (pit == begPit ? begPos : 0);
+ right = (pit == endPit ? endPos : parSize);
+
+ // 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;
+
+ bool capitalize = true;
+
+ for (; pos < right; ++pos) {
+ char_type oldChar = pars_[pit].getChar(pos);
+ char_type newChar = oldChar;
+
+ // ignore insets and don't play with deleted text!
+ if (oldChar != Paragraph::META_INSET && !pars_[pit].isDeleted(pos)) {
+ switch (action) {
+ case text_lowercase:
+ newChar = lowercase(oldChar);
+ break;
+ case text_capitalization:
+ if (capitalize) {
+ newChar = uppercase(oldChar);
+ capitalize = false;
+ }
+ break;
+ case text_uppercase:
+ newChar = uppercase(oldChar);
+ break;
+ }
+ }
+
+ if (!pars_[pit].isLetter(pos) || pars_[pit].isDeleted(pos)) {
+ capitalize = true; // permit capitalization again
}
- }
- // FIXME: change tracking (MG)
- // sorry but we are no longer allowed to set a single character directly
- // we have to rewrite this method in terms of erase&insert operations
- //pars_[pit].setChar(pos, c);
- ++pos;
+ if (oldChar != newChar) {
+ changes += newChar;
+ }
+
+ if (oldChar == newChar || pos == right - 1) {
+ if (oldChar != newChar) {
+ pos++; // step behind the changing area
+ }
+ int erasePos = pos - changes.size();
+ for (size_t i = 0; i < changes.size(); i++) {
+ pars_[pit].insertChar(pos, changes[i],
+ pars_[pit].getFontSettings(cur.buffer().params(),
+ erasePos),
+ trackChanges);
+ if (!pars_[pit].eraseChar(erasePos, trackChanges)) {
+ ++erasePos;
+ ++pos; // advance
+ ++right; // expand selection
+ }
+ }
+ changes.clear();
+ }
+ }
}
+
+ // the selection may have changed due to logically-only deleted chars
+ setCursor(cur, begPit, begPos);
+ cur.resetAnchor();
+ setCursor(cur, endPit, right);
+ cur.setSelection();
+
+ checkBufferStructure(cur.buffer(), cur);
}
// this is the code for a normal delete, not pasting
// any paragraphs
recordUndo(cur, Undo::DELETE);
- par.eraseChar(cur.pos(), cur.buffer().params().trackChanges);
- if (par.isDeleted(cur.pos()))
+ if(!par.eraseChar(cur.pos(), cur.buffer().params().trackChanges)) {
+ // the character has been logically deleted only => skip it
cur.forwardPosNoDescend();
+ }
+ checkBufferStructure(cur.buffer(), cur);
needsUpdate = true;
} else {
if (cur.pit() == cur.lastpit())
// Make sure the cursor is correct. Is this really needed?
// No, not really... at least not here!
cur.text()->setCursor(cur.top(), cur.pit(), cur.pos());
+ checkBufferStructure(cur.buffer(), cur);
}
return needsUpdate;
setCursorIntern(cur, cur.pit(), cur.pos() - 1,
false, cur.boundary());
cur.paragraph().eraseChar(cur.pos(), cur.buffer().params().trackChanges);
+ checkBufferStructure(cur.buffer(), cur);
}
if (cur.pos() == cur.lastpos())
if (!ptr_cmp(cur.text(), this))
return;
- if (lyxerr.debugging(Debug::DEBUG)) {
- lyxerr[Debug::DEBUG]
- << BOOST_CURRENT_FUNCTION
- << "draw selection at " << x
- << endl;
- }
+ LYXERR(Debug::DEBUG)
+ << BOOST_CURRENT_FUNCTION
+ << "draw selection at " << x
+ << endl;
DocIterator beg = cur.selectionBegin();
DocIterator end = cur.selectionEnd();
}
if (lyxerr.debugging(Debug::DEBUG)) {
- lyxerr[Debug::DEBUG] << " y1: " << y1 << " y2: " << y2
+ LYXERR(Debug::DEBUG) << " y1: " << y1 << " y2: " << y2
<< "X1:" << X1 << " x2: " << X2 << " wid: " << tm.width()
<< endl;
}
odocstringstream os;
if (buf.params().trackChanges)
- os << "[C] ";
+ os << _("[Change Tracking] ");
Change change = par.lookupChange(cur.pos());
os << _(", Paragraph: ") << cur.pit();
os << _(", Id: ") << par.id();
os << _(", Position: ") << cur.pos();
+ // FIXME: Why is the check for par.size() needed?
+ // We are called with cur.pos() == par.size() quite often.
+ if (!par.empty() && cur.pos() < par.size()) {
+ // Force output of code point, not character
+ size_t const c = par.getChar(cur.pos());
+ os << _(", Char: 0x") << std::hex << c;
+ }
os << _(", Boundary: ") << cur.boundary();
// Row & row = cur.textRow();
// os << bformat(_(", Row b:%1$d e:%2$d"), row.pos(), row.endpos());
LyXLayout_ptr layout = pars_[pit].layout();
- if (layout->latextype == LATEX_PARAGRAPH && pit != 0) {
- LyXLayout_ptr const & layout2 = pars_[pit - 1].layout();
- if (layout2->latextype != LATEX_PARAGRAPH) {
- --pit;
- layout = layout2;
- }
- }
-
- docstring name = from_ascii(layout->latexname());
-
- // for captions, we want the abbreviation of the float type
- if (layout->labeltype == LABEL_SENSITIVE) {
- // Search for the first float or wrap inset in the iterator
- for (int i = cur.depth(); --i >= 0; ) {
- InsetBase * const in = &cur[i].inset();
- if (in->lyxCode() == InsetBase::FLOAT_CODE
- || in->lyxCode() == InsetBase::WRAP_CODE) {
- name = in->getInsetName();
- break;
- }
- }
- }
-
- docstring text = name.substr(0, 3);
- if (name == "theorem")
- text = from_ascii("thm"); // Create a correct prefix for prettyref
-
- text += ':';
- if (layout->latextype == LATEX_PARAGRAPH || lyxrc.label_init_length < 0)
- text.erase();
-
+ docstring text;
docstring par_text = pars_[pit].asString(cur.buffer(), false);
for (int i = 0; i < lyxrc.label_init_length; ++i) {
if (par_text.empty())
text += head;
}
+ // No need for a prefix if the user said so.
+ if (lyxrc.label_init_length <= 0)
+ return text;
+
+ // Will contain the label type.
+ docstring name;
+
+ // For section, subsection, etc...
+ if (layout->latextype == LATEX_PARAGRAPH && pit != 0) {
+ LyXLayout_ptr const & layout2 = pars_[pit - 1].layout();
+ if (layout2->latextype != LATEX_PARAGRAPH) {
+ --pit;
+ layout = layout2;
+ }
+ }
+ if (layout->latextype != LATEX_PARAGRAPH)
+ name = from_ascii(layout->latexname());
+
+ // for captions, we just take the caption type
+ InsetBase * caption_inset = cur.innerInsetOfType(InsetBase::CAPTION_CODE);
+ if (caption_inset)
+ name = from_ascii(static_cast<InsetCaption *>(caption_inset)->type());
+
+ // Inside floats or wraps, if none of the above worked
+ // we want by default the abbreviation of the float type.
+ if (name.empty()) {
+ InsetBase * float_inset = cur.innerInsetOfType(InsetBase::FLOAT_CODE);
+ if (!float_inset)
+ float_inset = cur.innerInsetOfType(InsetBase::WRAP_CODE);
+ if (float_inset)
+ name = float_inset->getInsetName();
+ }
+
+ // Create a correct prefix for prettyref
+ if (name == "theorem")
+ name = from_ascii("thm");
+
+ if (!name.empty())
+ text = name.substr(0, 3) + ':' + text;
+
return text;
}
ParagraphMetrics const & pm = tm.parMetrics(pit);
int yy = cur.bv().coordCache().get(this, pit).y_ - pm.ascent();
- lyxerr[Debug::DEBUG]
+ LYXERR(Debug::DEBUG)
<< BOOST_CURRENT_FUNCTION
<< ": x: " << x
<< " y: " << y
Row const & row = pm.rows()[r];
- lyxerr[Debug::DEBUG]
+ LYXERR(Debug::DEBUG)
<< BOOST_CURRENT_FUNCTION
<< ": row " << r
<< " from pos: " << row.pos()
pos_type const pos = row.pos()
+ tm.getColumnNearX(pit, row, xx, bound);
- lyxerr[Debug::DEBUG]
+ LYXERR(Debug::DEBUG)
<< BOOST_CURRENT_FUNCTION
<< ": setting cursor pit: " << pit
<< " pos: " << pos
<< endl;
setCursor(cur, pit, pos, true, bound);
+ // remember new position.
+ cur.setTargetX();
}
par.insertChar(pos1, char2, font2, trackChanges);
par.insertChar(pos2, char1, font1, trackChanges);
+ checkBufferStructure(cur.buffer(), cur);
+
// After the transposition, move cursor to after the transposition.
setCursor(cur, cur.pit(), pos2);
cur.forwardPos();