X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ftext.C;h=a0a3c05f83bb3ed35115edd7e21222b97ea12dc0;hb=d2574b7d8e2716d3d3aea92df6576bd01c421a6c;hp=d26b5a4875bb56484d74072c891485807025fe24;hpb=4c765ba8892b35cee5e107a842acb7eac71d4024;p=lyx.git diff --git a/src/text.C b/src/text.C index d26b5a4875..a0a3c05f83 100644 --- a/src/text.C +++ b/src/text.C @@ -218,7 +218,7 @@ void readParToken(Buffer const & buf, Paragraph & par, LyXLex & lex, par.insertInset(par.size(), inset, font, change); else { lex.eatLine(); - docstring line = from_utf8(lex.getString()); + docstring line = lex.getDocString(); errorList.push_back(ErrorItem(_("Unknown Inset"), line, par.id(), 0, par.size())); } @@ -316,6 +316,10 @@ void readParToken(Buffer const & buf, Paragraph & par, LyXLex & lex, par.insertInset(par.size(), new InsetLine, font, change); } else if (token == "\\newpage") { par.insertInset(par.size(), new InsetPagebreak, font, change); + } else if (token == "\\clearpage") { + par.insertInset(par.size(), new InsetClearPage, font, change); + } else if (token == "\\cleardoublepage") { + par.insertInset(par.size(), new InsetClearDoublePage, font, change); } else if (token == "\\change_unchanged") { change = Change(Change::UNCHANGED); } else if (token == "\\change_inserted") { @@ -348,7 +352,7 @@ void readParToken(Buffer const & buf, Paragraph & par, LyXLex & lex, lex.eatLine(); errorList.push_back(ErrorItem(_("Unknown token"), bformat(_("Unknown token: %1$s %2$s\n"), from_utf8(token), - from_utf8(lex.getString())), + lex.getDocString()), par.id(), 0, par.size())); } } @@ -695,8 +699,8 @@ pos_type addressBreakPoint(pos_type i, Paragraph const & par) }; -void LyXText::rowBreakPoint(Buffer const & buffer, pit_type const pit, - Row & row) const +void LyXText::rowBreakPoint(Buffer const & buffer, int right_margin, + pit_type const pit, Row & row) const { Paragraph const & par = pars_[pit]; pos_type const end = par.size(); @@ -707,7 +711,7 @@ void LyXText::rowBreakPoint(Buffer const & buffer, pit_type const pit, } // maximum pixel width of a row - int width = maxwidth_ - rightMargin(buffer, par); // - leftMargin(buffer, pit, row); + int width = maxwidth_ - right_margin; // - leftMargin(buffer, pit, row); if (width < 0) { row.endpos(end); return; @@ -1070,10 +1074,6 @@ void LyXText::setHeightOfRow(BufferView const & bv, pit_type const pit, } -namespace { - -} - void LyXText::breakParagraph(LCursor & cur, bool keep_layout) { BOOST_ASSERT(this == cur.text()); @@ -1086,8 +1086,8 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout) // this is only allowed, if the current paragraph is not empty // or caption and if it has not the keepempty flag active - if (cur.lastpos() == 0 && !cpar.allowEmpty() - && layout->labeltype != LABEL_SENSITIVE) + if (cur.lastpos() == 0 && !cpar.allowEmpty() && + layout->labeltype != LABEL_SENSITIVE) return; // a layout change may affect also the following paragraph @@ -1096,10 +1096,9 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout) // Always break behind a space // It is better to erase the space (Dekel) if (cur.pos() != cur.lastpos() && cpar.isLineSeparator(cur.pos())) - // FIXME: change tracking (MG) cpar.eraseChar(cur.pos(), cur.buffer().params().trackChanges); - // How should the layout for the new paragraph be? + // What should the layout for the new paragraph be? int preserve_layout = 0; if (keep_layout) preserve_layout = 2; @@ -1132,7 +1131,6 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout) } while (!pars_[next_par].empty() && pars_[next_par].isNewline(0)) - // FIXME: change tracking (MG) pars_[next_par].eraseChar(0, cur.buffer().params().trackChanges); ParIterator current_it(cur); @@ -1142,12 +1140,15 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout) updateLabels(cur.buffer(), current_it, last_it); - // Mark "carriage return" as inserted if change tracking: - if (cur.buffer().params().trackChanges) { - // FIXME: Change tracking (MG) - cur.paragraph().setChange(cur.paragraph().size(), - Change(Change::INSERTED)); - } + // FIXME: Breaking a paragraph has nothing to do with setting a cursor. + // Because of the mix between the model (the paragraph contents) and the + // view (the paragraph breaking in rows, we have to do this here before + // the setCursor() call below. + bool changed_height = redoParagraph(cur.bv(), cpit); + changed_height |= redoParagraph(cur.bv(), cpit + 1); + if (changed_height) + // A singlePar update is not enough in this case. + cur.updateFlags(Update::Force); // This check is necessary. Otherwise the new empty paragraph will // be deleted automatically. And it is more friendly for the user! @@ -1242,6 +1243,13 @@ void LyXText::insertChar(LCursor & cur, char_type c) } par.insertChar(cur.pos(), c, current_font, cur.buffer().params().trackChanges); + + // FIXME: Inserting a character has nothing to do with setting a cursor. + // Because of the mix between the model (the paragraph contents) and the + // view (the paragraph breaking in rows, we have to do this here. + if (redoParagraph(cur.bv(), cur.pit())) + // A singlePar update is not enough in this case. + cur.updateFlags(Update::Force); setCursor(cur, cur.pit(), cur.pos() + 1, false, cur.boundary()); charInserted(); } @@ -1450,34 +1458,31 @@ bool LyXText::selectWordWhenUnderCursor(LCursor & cur, word_location loc) void LyXText::acceptChange(LCursor & cur) { + // FIXME: change tracking (MG) + BOOST_ASSERT(this == cur.text()); + if (!cur.selection() && cur.lastpos() != 0) return; + // FIXME: we must handle start = end = 0 + recordUndoSelection(cur, Undo::INSERT); DocIterator it = cur.selectionBegin(); DocIterator et = cur.selectionEnd(); pit_type pit = it.pit(); - bool isDeleted = pars_[pit].isDeleted(it.pos()); 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() + 1 ); + 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); - } - if (isDeleted) { - ParagraphList & plist = paragraphs(); - if (it.pit() + 1 < et.pit()) - pars_.erase(boost::next(plist.begin(), it.pit() + 1), - boost::next(plist.begin(), et.pit())); - // Paragraph merge if appropriate: - // FIXME: change tracking (MG) - if (pars_[it.pit()].isDeleted(pars_[it.pit()].size())) { - setCursorIntern(cur, it.pit() + 1, 0); - backspacePos0(cur); - } + // 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); + //} } finishUndo(); cur.clearSelection(); @@ -1487,33 +1492,31 @@ void LyXText::acceptChange(LCursor & cur) void LyXText::rejectChange(LCursor & cur) { + // FIXME: change tracking (MG) + BOOST_ASSERT(this == cur.text()); + if (!cur.selection() && cur.lastpos() != 0) return; + // FIXME: we must handle start = end = 0 + recordUndoSelection(cur, Undo::INSERT); DocIterator it = cur.selectionBegin(); DocIterator et = cur.selectionEnd(); pit_type pit = it.pit(); - bool isInserted = pars_[pit].isInserted(it.pos()); 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() + 1 ); + 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); - } - if (isInserted) { - ParagraphList & plist = paragraphs(); - if (it.pit() + 1 < et.pit()) - pars_.erase(boost::next(plist.begin(), it.pit() + 1), - boost::next(plist.begin(), et.pit())); - // Paragraph merge if appropriate: - // FIXME: change tracking (MG) - if (pars_[it.pit()].isInserted(pars_[it.pit()].size())) { - setCursorIntern(cur, it.pit() + 1, 0); - backspacePos0(cur); - } + + // 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); + //} } finishUndo(); cur.clearSelection(); @@ -1636,32 +1639,36 @@ bool LyXText::erase(LCursor & cur) // this is the code for a normal delete, not pasting // any paragraphs recordUndo(cur, Undo::DELETE); - // FIXME: change tracking (MG) par.eraseChar(cur.pos(), cur.buffer().params().trackChanges); if (par.isDeleted(cur.pos())) - cur.forwardPos(); + cur.forwardPosNoDescend(); needsUpdate = true; - } else if (cur.pit() != cur.lastpit()) { - if (cur.buffer().params().trackChanges - && par.isInserted(cur.pos())) { - // mark "carriage return" as deleted: - // FIXME: Change tracking (MG) + } else { + if (cur.pit() == cur.lastpit()) + return dissolveInset(cur); + + if (!par.isMergedOnEndOfParDeletion(cur.buffer().params().trackChanges)) { par.setChange(cur.pos(), Change(Change::DELETED)); cur.forwardPos(); needsUpdate = true; } else { setCursorIntern(cur, cur.pit() + 1, 0); needsUpdate = backspacePos0(cur); - // FIXME: Change tracking (MG) - if (cur.paragraph().isDeleted(cur.pos())) - cur.forwardPos(); } - } else - needsUpdate = dissolveInset(cur); + } - // Make sure the cursor is correct. Is this really needed? - if (needsUpdate) - setCursorIntern(cur, cur.pit(), cur.pos()); + // FIXME: Inserting characters has nothing to do with setting a cursor. + // Because of the mix between the model (the paragraph contents) + // and the view (the paragraph breaking in rows, we have to do this + // here before the setCursorIntern() call. + if (needsUpdate) { + if (redoParagraph(cur.bv(), cur.pit())) + // A singlePar update is not enough in this case. + cur.updateFlags(Update::Force); + // Make sure the cursor is correct. Is this really needed? + // No, not really... at least not here! + cur.text()->setCursorIntern(cur, cur.pit(), cur.pos()); + } return needsUpdate; } @@ -1699,7 +1706,7 @@ bool LyXText::backspacePos0(LCursor & cur) needsUpdate = true; } // Pasting is not allowed, if the paragraphs have different - // layout. I think it is a real bug of all other + // layouts. I think it is a real bug of all other // word processors to allow it. It confuses the user. // Correction: Pasting is always allowed with standard-layout else if (par.layout() == prevpar.layout() @@ -1718,8 +1725,6 @@ bool LyXText::backspacePos0(LCursor & cur) } - - bool LyXText::backspace(LCursor & cur) { BOOST_ASSERT(this == cur.text()); @@ -1728,21 +1733,13 @@ bool LyXText::backspace(LCursor & cur) if (cur.pit() == 0) return dissolveInset(cur); - if (cur.buffer().params().trackChanges) { - // FIXME: Change tracking (MG) - // Previous paragraph, mark "carriage return" as - // deleted: - Paragraph & par = pars_[cur.pit() - 1]; - // Take care of a just inserted para break: - // FIXME: change tracking (MG) - if (!par.isInserted(par.size())) { - // FIXME: change tracking (MG) - par.setChange(par.size(), Change(Change::DELETED)); - setCursorIntern(cur, cur.pit() - 1, par.size()); - return true; - } - } + Paragraph & prev_par = pars_[cur.pit() - 1]; + if (!prev_par.isMergedOnEndOfParDeletion(cur.buffer().params().trackChanges)) { + prev_par.setChange(prev_par.size(), Change(Change::DELETED)); + setCursorIntern(cur, cur.pit() - 1, prev_par.size()); + return true; + } // The cursor is at the beginning of a paragraph, so // the backspace will collapse two paragraphs into one. needsUpdate = backspacePos0(cur); @@ -1757,13 +1754,19 @@ bool LyXText::backspace(LCursor & cur) // without the dreaded mechanism. (JMarc) setCursorIntern(cur, cur.pit(), cur.pos() - 1, false, cur.boundary()); - // FIXME: change tracking (MG) cur.paragraph().eraseChar(cur.pos(), cur.buffer().params().trackChanges); } if (cur.pos() == cur.lastpos()) setCurrentFont(cur); + // FIXME: Backspacing has nothing to do with setting a cursor. + // Because of the mix between the model (the paragraph contents) + // and the view (the paragraph breaking in rows, we have to do this + // here before the setCursor() call. + if (redoParagraph(cur.bv(), cur.pit())) + // A singlePar update is not enough in this case. + cur.updateFlags(Update::Force); setCursor(cur, cur.pit(), cur.pos(), false, cur.boundary()); return needsUpdate; @@ -1776,6 +1779,7 @@ bool LyXText::dissolveInset(LCursor & cur) { if (isMainText(*cur.bv().buffer()) || cur.inset().nargs() != 1) return false; + bool const in_ert = cur.inset().lyxCode() == InsetBase::ERT_CODE; recordUndoInset(cur); cur.selHandle(false); // save position @@ -1790,9 +1794,21 @@ bool LyXText::dissolveInset(LCursor & cur) { spos += cur.pos(); spit += cur.pit(); Buffer & b = cur.buffer(); - // FIXME: change tracking (MG) cur.paragraph().eraseChar(cur.pos(), b.params().trackChanges); if (!plist.empty()) { + if (in_ert) { + // ERT paragraphs have the Language latex_language. + // This is invalid outside of ERT, so we need to + // change it to the buffer language. + ParagraphList::iterator it = plist.begin(); + ParagraphList::iterator it_end = plist.end(); + for (; it != it_end; it++) { + it->changeLanguage(b.params(), + latex_language, + b.getLanguage()); + } + } + pasteParagraphList(cur, plist, b.params().textclass, b.errorList("Paste")); // restore position @@ -1817,6 +1833,8 @@ bool LyXText::redoParagraph(BufferView & bv, pit_type const pit) Paragraph & par = pars_[pit]; Buffer const & buffer = *bv.buffer(); + bool changed = false; + // Add bibitem insets if necessary if (par.layout()->labeltype == LABEL_BIBLIO) { bool hasbibitem(false); @@ -1837,21 +1855,25 @@ bool LyXText::redoParagraph(BufferView & bv, pit_type const pit) } } + // Optimisation: this is used in the next two loops + // so better to calculate that once here. + int right_margin = rightMargin(buffer, par); + // redo insets // FIXME: We should always use getFont(), see documentation of // noFontChange() in insetbase.h. LyXFont const bufferfont = buffer.params().getFont(); - InsetList::iterator ii = par.insetlist.begin(); - InsetList::iterator iend = par.insetlist.end(); + InsetList::const_iterator ii = par.insetlist.begin(); + InsetList::const_iterator iend = par.insetlist.end(); for (; ii != iend; ++ii) { Dimension dim; int const w = maxwidth_ - leftMargin(buffer, pit, ii->pos) - - rightMargin(buffer, par); + - right_margin; LyXFont const & font = ii->inset->noFontChange() ? bufferfont : getFont(buffer, par, ii->pos); MetricsInfo mi(&bv, font, w); - ii->inset->metrics(mi, dim); + changed |= ii->inset->metrics(mi, dim); } // rebreak the paragraph @@ -1862,7 +1884,7 @@ bool LyXText::redoParagraph(BufferView & bv, pit_type const pit) pos_type z = 0; do { Row row(z); - rowBreakPoint(buffer, pit, row); + rowBreakPoint(buffer, right_margin, pit, row); setRowWidth(buffer, pit, row); setHeightOfRow(bv, pit, row); par.rows().push_back(row); @@ -1888,16 +1910,16 @@ bool LyXText::redoParagraph(BufferView & bv, pit_type const pit) dim.asc += par.rows()[0].ascent(); dim.des -= par.rows()[0].ascent(); - bool const same = dim == par.dim(); + changed |= dim.height() != par.dim().height(); par.dim() = dim; //lyxerr << "redoParagraph: " << par.rows().size() << " rows\n"; - return !same; + return changed; } -void LyXText::metrics(MetricsInfo & mi, Dimension & dim) +bool LyXText::metrics(MetricsInfo & mi, Dimension & dim) { //BOOST_ASSERT(mi.base.textwidth); if (mi.base.textwidth) @@ -1907,10 +1929,12 @@ void LyXText::metrics(MetricsInfo & mi, Dimension & dim) // save the caller's font locally: font_ = mi.base.font; + bool changed = false; + unsigned int h = 0; unsigned int w = 0; for (pit_type pit = 0, n = paragraphs().size(); pit != n; ++pit) { - redoParagraph(*mi.base.bv, pit); + changed |= redoParagraph(*mi.base.bv, pit); Paragraph & par = paragraphs()[pit]; h += par.height(); if (w < par.width()) @@ -1921,7 +1945,9 @@ void LyXText::metrics(MetricsInfo & mi, Dimension & dim) dim.asc = pars_[0].ascent(); dim.des = h - dim.asc; + changed |= dim_ != dim; dim_ = dim; + return changed; } @@ -2351,7 +2377,7 @@ string LyXText::currentState(LCursor & cur) } -string LyXText::getPossibleLabel(LCursor & cur) const +docstring LyXText::getPossibleLabel(LCursor & cur) const { pit_type pit = cur.pit(); @@ -2365,7 +2391,7 @@ string LyXText::getPossibleLabel(LCursor & cur) const } } - string name = layout->latexname(); + docstring name = from_ascii(layout->latexname()); // for captions, we want the abbreviation of the float type if (layout->labeltype == LABEL_SENSITIVE) { @@ -2374,26 +2400,25 @@ string LyXText::getPossibleLabel(LCursor & cur) const InsetBase * const in = &cur[i].inset(); if (in->lyxCode() == InsetBase::FLOAT_CODE || in->lyxCode() == InsetBase::WRAP_CODE) { - name = to_utf8(in->getInsetName()); + name = in->getInsetName(); break; } } } - string text = name.substr(0, 3); + docstring text = name.substr(0, 3); if (name == "theorem") - text = "thm"; // Create a correct prefix for prettyref + text = from_ascii("thm"); // Create a correct prefix for prettyref text += ':'; if (layout->latextype == LATEX_PARAGRAPH || lyxrc.label_init_length < 0) text.erase(); - // FIXME UNICODE - string par_text = to_utf8(pars_[pit].asString(cur.buffer(), false)); + docstring par_text = pars_[pit].asString(cur.buffer(), false); for (int i = 0; i < lyxrc.label_init_length; ++i) { if (par_text.empty()) break; - string head; + docstring head; par_text = split(par_text, head, ' '); // Is it legal to use spaces in labels ? if (i > 0) @@ -2488,4 +2513,61 @@ bool LyXText::setCursorFromCoordinates(LCursor & cur, int const x, int const y) } +void LyXText::charsTranspose(LCursor & cur) +{ + BOOST_ASSERT(this == cur.text()); + + pos_type pos = cur.pos(); + + // If cursor is at beginning or end of paragraph, do nothing. + if (pos == cur.lastpos() || pos == 0) + return; + + Paragraph & par = cur.paragraph(); + + // Get the positions of the characters to be transposed. + pos_type pos1 = pos - 1; + pos_type pos2 = pos; + + // In change tracking mode, ignore deleted characters. + while (pos2 < cur.lastpos() && par.isDeleted(pos2)) + ++pos2; + if (pos2 == cur.lastpos()) + return; + + while (pos1 >= 0 && par.isDeleted(pos1)) + --pos1; + if (pos1 < 0) + return; + + // Don't do anything if one of the "characters" is not regular text. + if (par.isInset(pos1) || par.isInset(pos2)) + return; + + // Store the characters to be transposed (including font information). + char_type char1 = par.getChar(pos1); + LyXFont const font1 = + par.getFontSettings(cur.buffer().params(), pos1); + + char_type char2 = par.getChar(pos2); + LyXFont const font2 = + par.getFontSettings(cur.buffer().params(), pos2); + + // And finally, we are ready to perform the transposition. + // Track the changes if Change Tracking is enabled. + bool const trackChanges = cur.buffer().params().trackChanges; + + recordUndo(cur); + + par.eraseChar(pos2, trackChanges); + par.eraseChar(pos1, trackChanges); + par.insertChar(pos1, char2, font2, trackChanges); + par.insertChar(pos2, char1, font1, trackChanges); + + // After the transposition, move cursor to after the transposition. + setCursor(cur, cur.pit(), pos2); + cur.forwardPos(); +} + + } // namespace lyx