X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FText.cpp;h=0ca32a4e931847fbad9a89becf485192e8a2025c;hb=1d637b8af0f76d7c758db91ee3a196306f861a21;hp=2da97f6bd401945d6cff65877c1cee7821d0f5e0;hpb=f758894c2fc78f33be0c8bd60c2fd0a1f470056e;p=lyx.git diff --git a/src/Text.cpp b/src/Text.cpp index 2da97f6bd4..0ca32a4e93 100644 --- a/src/Text.cpp +++ b/src/Text.cpp @@ -22,51 +22,43 @@ #include "Author.h" #include "Buffer.h" -#include "buffer_funcs.h" #include "BufferParams.h" #include "BufferView.h" #include "Changes.h" #include "CompletionList.h" #include "Cursor.h" +#include "CursorSlice.h" #include "CutAndPaste.h" -#include "DispatchResult.h" #include "Encoding.h" #include "ErrorList.h" -#include "FuncRequest.h" #include "factory.h" -#include "InsetList.h" +#include "Font.h" +#include "FuncRequest.h" #include "Language.h" #include "Layout.h" -#include "Length.h" #include "Lexer.h" #include "lyxfind.h" #include "LyXRC.h" #include "Paragraph.h" #include "ParagraphParameters.h" -#include "ParIterator.h" #include "TextClass.h" #include "TextMetrics.h" -#include "WordLangTuple.h" #include "WordList.h" +#include "insets/Inset.h" #include "insets/InsetText.h" -#include "insets/InsetBibitem.h" #include "insets/InsetCaption.h" -#include "insets/InsetNewline.h" -#include "insets/InsetNewpage.h" -#include "insets/InsetArgument.h" #include "insets/InsetIPAMacro.h" -#include "insets/InsetSpace.h" #include "insets/InsetSpecialChar.h" #include "insets/InsetTabular.h" #include "support/convert.h" #include "support/debug.h" #include "support/docstream.h" +#include "support/docstring.h" #include "support/gettext.h" #include "support/lassert.h" #include "support/lstrings.h" -#include "support/lyxalgo.h" #include "support/lyxtime.h" #include "support/textutils.h" #include "support/unique_ptr.h" @@ -217,7 +209,7 @@ pit_type Text::outerHook(pit_type par_offset) const if (par.getDepth() == 0) return pars_.size(); - return depthHook(par_offset, depth_type(par.getDepth() - 1)); + return depthHook(par_offset, par.getDepth() - 1); } @@ -377,7 +369,7 @@ InsetText const & Text::inset() const void Text::readParToken(Paragraph & par, Lexer & lex, string const & token, Font & font, Change & change, ErrorList & errorList) { - Buffer * buf = const_cast(&owner_->buffer()); + Buffer * buf = &owner_->buffer(); BufferParams & bp = buf->params(); if (token[0] != '\\') { @@ -641,14 +633,14 @@ public: virtual ~TextCompletionList() {} /// - virtual bool sorted() const { return true; } + bool sorted() const override { return true; } /// - virtual size_t size() const + size_t size() const override { return list_.size(); } /// - virtual docstring const & data(size_t idx) const + docstring const & data(size_t idx) const override { return list_.word(idx); } @@ -867,11 +859,12 @@ void Text::insertStringAsLines(Cursor & cur, docstring const & str, pos_type pos = cur.pos(); // The special chars we handle - map specialchars; - specialchars[0x200c] = InsetSpecialChar::LIGATURE_BREAK; - specialchars[0x200b] = InsetSpecialChar::ALLOWBREAK; - specialchars[0x2026] = InsetSpecialChar::LDOTS; - specialchars[0x2011] = InsetSpecialChar::NOBREAKDASH; + static map specialchars = { + { 0x200c, InsetSpecialChar::LIGATURE_BREAK }, + { 0x200b, InsetSpecialChar::ALLOWBREAK }, + { 0x2026, InsetSpecialChar::LDOTS }, + { 0x2011, InsetSpecialChar::NOBREAKDASH } + }; // insert the string, don't insert doublespace bool space_inserted = true; @@ -902,11 +895,11 @@ void Text::insertStringAsLines(Cursor & cur, docstring const & str, ++pos; space_inserted = true; } - } else if (specialchars.find(ch) != specialchars.end()) { - par.insertInset(pos, new InsetSpecialChar(specialchars.find(ch)->second), - font, bparams.track_changes ? - Change(Change::INSERTED) - : Change(Change::UNCHANGED)); + } else if (specialchars.find(ch) != specialchars.end() + && (par.insertInset(pos, new InsetSpecialChar(specialchars.find(ch)->second), + font, bparams.track_changes + ? Change(Change::INSERTED) + : Change(Change::UNCHANGED)))) { ++pos; space_inserted = false; } else if (!isPrintable(ch)) { @@ -1134,7 +1127,7 @@ void Text::charInserted(Cursor & cur) && par.isWordSeparator(cur.pos() - 1)) { // get the word in front of cursor LBUFERR(this == cur.text()); - cur.paragraph().updateWords(); + par.updateWords(); } } @@ -1146,30 +1139,53 @@ bool Text::cursorForwardOneWord(Cursor & cur) { LBUFERR(this == cur.text()); - pos_type const lastpos = cur.lastpos(); - pit_type pit = cur.pit(); - pos_type pos = cur.pos(); - Paragraph const & par = cur.paragraph(); - - // Paragraph boundary is a word boundary - if (pos == lastpos || (pos + 1 == lastpos && par.isEnvSeparator(pos))) { - if (pit != cur.lastpit()) - return setCursor(cur, pit + 1, 0); - else - return false; - } - if (lyxrc.mac_like_cursor_movement) { - // Skip through trailing punctuation and spaces. - while (pos != lastpos && (par.isChar(pos) || par.isSpace(pos))) - ++pos; - - // Skip over either a non-char inset or a full word - if (pos != lastpos && par.isWordSeparator(pos)) - ++pos; - else while (pos != lastpos && !par.isWordSeparator(pos)) - ++pos; + DocIterator dit(cur); + DocIterator prv(cur); + bool inword = false; + bool intext = dit.inTexted(); + while (!dit.atEnd()) { + if (dit.inTexted()) { // no paragraphs in mathed + Paragraph const & par = dit.paragraph(); + pos_type const pos = dit.pos(); + + if (!par.isDeleted(pos)) { + bool wordsep = par.isWordSeparator(pos); + if (inword && wordsep) + break; // stop at word end + else if (!inword && !wordsep) + inword = true; + } + intext = true; + } else if (intext) { + // move to end of math + while (!dit.inTexted() && !dit.atEnd()) dit.forwardPos(); + break; + } + prv = dit; + dit.forwardPosIgnoreCollapsed(); + } + if (dit.atEnd()) dit = prv; + if (dit == cur) return false; // we didn't move + Cursor orig(cur); + cur.setCursor(dit); + // see comment above + cur.bv().checkDepm(cur, orig); + return true; } else { + pos_type const lastpos = cur.lastpos(); + pit_type pit = cur.pit(); + pos_type pos = cur.pos(); + Paragraph const & par = cur.paragraph(); + + // Paragraph boundary is a word boundary + if (pos == lastpos || (pos + 1 == lastpos && par.isEnvSeparator(pos))) { + if (pit != cur.lastpit()) + return setCursor(cur, pit + 1, 0); + else + return false; + } + LASSERT(pos < lastpos, return false); // see above if (!par.isWordSeparator(pos)) while (pos != lastpos && !par.isWordSeparator(pos)) @@ -1183,13 +1199,13 @@ bool Text::cursorForwardOneWord(Cursor & cur) // Skip over white space while (pos != lastpos && par.isSpace(pos)) ++pos; - } - // Don't skip a separator inset at the end of a paragraph - if (pos == lastpos && pos && par.isEnvSeparator(pos - 1)) - --pos; + // Don't skip a separator inset at the end of a paragraph + if (pos == lastpos && pos && par.isEnvSeparator(pos - 1)) + --pos; - return setCursor(cur, pit, pos); + return setCursor(cur, pit, pos); + } } @@ -1197,34 +1213,55 @@ bool Text::cursorBackwardOneWord(Cursor & cur) { LBUFERR(this == cur.text()); - pit_type pit = cur.pit(); - pos_type pos = cur.pos(); - Paragraph & par = cur.paragraph(); - - // Paragraph boundary is a word boundary - if (pos == 0 && pit != 0) { - Paragraph & prevpar = getPar(pit - 1); - pos = prevpar.size(); - // Don't stop after an environment separator - if (pos && prevpar.isEnvSeparator(pos - 1)) - --pos; - return setCursor(cur, pit - 1, pos); - } - if (lyxrc.mac_like_cursor_movement) { - // Skip through punctuation and spaces. - while (pos != 0 && (par.isChar(pos - 1) || par.isSpace(pos - 1))) - --pos; - - // Skip over either a non-char inset or a full word - if (pos != 0 && par.isWordSeparator(pos - 1) && !par.isChar(pos - 1)) - --pos; - else while (pos != 0 && !par.isWordSeparator(pos - 1)) - --pos; + DocIterator dit(cur); + bool inword = false; + bool intext = dit.inTexted(); + while (!dit.atBegin()) { + DocIterator prv(dit); + dit.backwardPosIgnoreCollapsed(); + if (dit.inTexted()) { // no paragraphs in mathed + Paragraph const & par = dit.paragraph(); + pos_type pos = dit.pos(); + + if (!par.isDeleted(pos)) { + bool wordsep = par.isWordSeparator(pos); + if (inword && wordsep) { + dit = prv; + break; // stop at word begin + } else if (!inword && !wordsep) + inword = true; + } + intext = true; + } else if (intext) { + // move to begin of math + while (!dit.inTexted() && !dit.atBegin()) dit.backwardPos(); + break; + } + } + if (dit == cur) return false; // we didn't move + Cursor orig(cur); + cur.setCursor(dit); + // see comment above cursorForwardOneWord + cur.bv().checkDepm(cur, orig); + return true; } else { + Paragraph const & par = cur.paragraph(); + pit_type const pit = cur.pit(); + pos_type pos = cur.pos(); + + // Paragraph boundary is a word boundary + if (pos == 0 && pit != 0) { + Paragraph & prevpar = getPar(pit - 1); + pos = prevpar.size(); + // Don't stop after an environment separator + if (pos && prevpar.isEnvSeparator(pos - 1)) + --pos; + return setCursor(cur, pit - 1, pos); + } // Skip over white space while (pos != 0 && par.isSpace(pos - 1)) - --pos; + --pos; if (pos != 0 && !par.isWordSeparator(pos - 1)) while (pos != 0 && !par.isWordSeparator(pos - 1)) @@ -1234,9 +1271,9 @@ bool Text::cursorBackwardOneWord(Cursor & cur) --pos; else if (pos != 0 && !par.isSpace(pos - 1)) // non-char inset --pos; - } - return setCursor(cur, pit, pos); + return setCursor(cur, pit, pos); + } } @@ -1332,6 +1369,20 @@ void Text::selectWord(Cursor & cur, word_location loc) } +void Text::expandWordSel(Cursor & cur) +{ + // get selection of word around cur + Cursor c = cur; + c.selection(false); + c.text()->selectWord(c, WHOLE_WORD); + // use the correct word boundary, depending on selection direction + if (cur.top() > cur.normalAnchor()) + cur.pos() = c.selEnd().pos(); + else + cur.pos() = c.selBegin().pos(); +} + + void Text::selectAll(Cursor & cur) { LBUFERR(this == cur.text()); @@ -1459,7 +1510,8 @@ void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op) } // finally, invoke the DEPM - deleteEmptyParagraphMechanism(begPit, endPit, cur.buffer()->params().track_changes); + deleteEmptyParagraphMechanism(begPit, endPit, begPos, endPos, + cur.buffer()->params().track_changes); cur.finishUndo(); cur.clearSelection(); @@ -1685,10 +1737,7 @@ bool Text::backspacePos0(Cursor & cur) if (cur.pit() == 0) return false; - bool needsUpdate = false; - BufferParams const & bufparams = cur.buffer()->params(); - DocumentClass const & tclass = bufparams.documentClass(); ParagraphList & plist = cur.text()->paragraphs(); Paragraph const & par = cur.paragraph(); Cursor prevcur = cur; @@ -1701,14 +1750,12 @@ bool Text::backspacePos0(Cursor & cur) || (cur.lastpos() == 1 && par.isSeparator(0))) { cur.recordUndo(prevcur.pit()); plist.erase(plist.iterator_at(cur.pit())); - needsUpdate = true; } // is previous par empty? else if (prevcur.lastpos() == 0 || (prevcur.lastpos() == 1 && prevpar.isSeparator(0))) { cur.recordUndo(prevcur.pit()); plist.erase(plist.iterator_at(prevcur.pit())); - needsUpdate = true; } // FIXME: Do we really not want to allow this??? // Pasting is not allowed, if the paragraphs have different @@ -1716,20 +1763,15 @@ bool Text::backspacePos0(Cursor & cur) // word processors to allow it. It confuses the user. // Correction: Pasting is always allowed with standard-layout // or the empty layout. - else if (par.layout() == prevpar.layout() - || tclass.isDefaultLayout(par.layout()) - || tclass.isPlainLayout(par.layout())) { + else { cur.recordUndo(prevcur.pit()); mergeParagraph(bufparams, plist, prevcur.pit()); - needsUpdate = true; } - if (needsUpdate) { - cur.forceBufferUpdate(); - setCursorIntern(cur, prevcur.pit(), prevcur.pos()); - } + cur.forceBufferUpdate(); + setCursorIntern(cur, prevcur.pit(), prevcur.pos()); - return needsUpdate; + return true; } @@ -1744,7 +1786,7 @@ bool Text::backspace(Cursor & cur) Cursor prev_cur = cur; --prev_cur.pit(); - if (cur.paragraph().size() > 0 + if (!cur.paragraph().empty() && !prev_cur.paragraph().isMergedOnEndOfParDeletion(cur.buffer()->params().track_changes)) { cur.recordUndo(prev_cur.pit(), prev_cur.pit()); prev_cur.paragraph().setChange(prev_cur.lastpos(), Change(Change::DELETED)); @@ -1843,10 +1885,18 @@ bool Text::dissolveInset(Cursor & cur) */ DocumentClass const & tclass = cur.buffer()->params().documentClass(); if (inset_it.lastpos() == 1 - && plist[0].layout().name() != tclass.plainLayoutName()) - cur.paragraph().makeSameLayout(plist[0]); + && !tclass.isPlainLayout(plist[0].layout()) + && !tclass.isDefaultLayout(plist[0].layout())) { + // Copy all parameters except depth. + Paragraph & par = cur.paragraph(); + par.setLayout(plist[0].layout()); + depth_type const dpth = par.getDepth(); + par.params() = plist[0].params(); + par.params().depth(dpth); + } pasteParagraphList(cur, plist, b.params().documentClassPtr(), + b.params().authors(), b.errorList("Paste")); } @@ -1866,6 +1916,88 @@ bool Text::dissolveInset(Cursor & cur) } +bool Text::splitInset(Cursor & cur) +{ + LASSERT(this == cur.text(), return false); + + if (isMainText() || cur.inset().nargs() != 1) + return false; + + cur.recordUndo(); + if (cur.selection()) { + // start from selection begin + setCursor(cur, cur.selBegin().pit(), cur.selBegin().pos()); + cur.clearSelection(); + } + // save split position inside inset + // (we need to copy the whole inset first) + pos_type spos = cur.pos(); + pit_type spit = cur.pit(); + // some things only need to be done if the inset has content + bool const inset_non_empty = cur.lastpit() != 0 || cur.lastpos() != 0; + + // move right before the inset + cur.popBackward(); + cur.resetAnchor(); + // remember position outside inset + pos_type ipos = cur.pos(); + pit_type ipit = cur.pit(); + // select inset ... + ++cur.pos(); + cur.setSelection(); + // ... and copy + cap::copySelectionToTemp(cur); + cur.clearSelection(); + cur.resetAnchor(); + // paste copied inset + cap::pasteFromTemp(cur, cur.buffer()->errorList("Paste")); + cur.forceBufferUpdate(); + + // if the inset has text, cut after split position + // and paste to new inset + if (inset_non_empty) { + // go back to first inset + cur.text()->setCursor(cur, ipit, ipos); + cur.forwardPos(); + setCursor(cur, spit, spos); + cur.resetAnchor(); + setCursor(cur, cur.lastpit(), getPar(cur.lastpit()).size()); + cur.setSelection(); + cap::cutSelectionToTemp(cur); + cur.setMark(false); + cur.selHandle(false); + cur.resetAnchor(); + bool atlastpos = false; + if (cur.pos() == 0 && cur.pit() > 0) { + // if we are at par start, remove this par + cur.text()->backspace(cur); + cur.forceBufferUpdate(); + } else if (cur.pos() == cur.lastpos()) + atlastpos = true; + // Move out of and jump over inset + cur.popBackward(); + ++cur.pos(); + + // enter new inset + cur.forwardPos(); + cur.setCursor(cur); + cur.resetAnchor(); + cur.text()->selectAll(cur); + cutSelection(cur, false); + cap::pasteFromTemp(cur, cur.buffer()->errorList("Paste")); + cur.text()->setCursor(cur, 0, 0); + if (atlastpos && cur.paragraph().isFreeSpacing() && cur.paragraph().empty()) { + // We started from par end, remove extra empty par in free spacing insets + cur.text()->erase(cur); + cur.forceBufferUpdate(); + } + } + + cur.finishUndo(); + return true; +} + + void Text::getWord(CursorSlice & from, CursorSlice & to, word_location const loc) const { @@ -1956,7 +2088,7 @@ bool Text::read(Lexer & lex, } -// Returns the current font and depth as a message. +// Returns the current state (font, depth etc.) as a message for status bar. docstring Text::currentState(CursorData const & cur, bool devel_mode) const { LBUFERR(this == cur.text()); @@ -1986,7 +2118,7 @@ docstring Text::currentState(CursorData const & cur, bool devel_mode) const os << bformat(_("Font: %1$s"), font.stateText(&buf.params())); // The paragraph depth - int depth = cur.paragraph().getDepth(); + int depth = par.getDepth(); if (depth > 0) os << bformat(_(", Depth: %1$d"), depth); @@ -2014,6 +2146,11 @@ docstring Text::currentState(CursorData const & cur, bool devel_mode) const } } + // Custom text style + InsetLayout const & layout = cur.inset().getLayout(); + if (layout.lyxtype() == InsetLyXType::CHARSTYLE) + os << _(", Style: ") << translateIfPossible(layout.labelstring()); + if (devel_mode) { os << _(", Inset: ") << &cur.inset(); if (cur.lastidx() > 0) @@ -2026,7 +2163,10 @@ docstring Text::currentState(CursorData const & cur, bool devel_mode) const 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") << hex << c; + if (c == META_INSET) + os << ", Char: INSET"; + else + os << _(", Char: 0x") << hex << c; } os << _(", Boundary: ") << cur.boundary(); // Row & row = cur.textRow(); @@ -2238,23 +2378,11 @@ void Text::setMacrocontextPosition(DocIterator const & pos) } -docstring Text::previousWord(CursorSlice const & sl) const -{ - CursorSlice from = sl; - CursorSlice to = sl; - getWord(from, to, PREVIOUS_WORD); - if (sl == from || to == from) - return docstring(); - - Paragraph const & par = sl.paragraph(); - return par.asString(from.pos(), to.pos()); -} - - bool Text::completionSupported(Cursor const & cur) const { Paragraph const & par = cur.paragraph(); - return !cur.selection() + return !cur.buffer()->isReadonly() + && !cur.selection() && cur.pos() > 0 && (cur.pos() >= par.size() || par.isWordSeparator(cur.pos())) && !par.isWordSeparator(cur.pos() - 1); @@ -2268,9 +2396,12 @@ CompletionList const * Text::createCompletionList(Cursor const & cur) const } -bool Text::insertCompletion(Cursor & cur, docstring const & s, bool /*finished*/) +bool Text::insertCompletion(Cursor & cur, docstring const & s) { LBUFERR(cur.bv().cursor() == cur); + if (cur.buffer()->isReadonly()) + return false; + cur.recordUndo(); cur.insert(s); cur.bv().cursor() = cur; if (!(cur.result().screenUpdate() & Update::Force)) @@ -2281,7 +2412,11 @@ bool Text::insertCompletion(Cursor & cur, docstring const & s, bool /*finished*/ docstring Text::completionPrefix(Cursor const & cur) const { - return previousWord(cur.top()); + CursorSlice from = cur.top(); + CursorSlice to = from; + getWord(from, to, PREVIOUS_WORD); + + return cur.paragraph().asString(from.pos(), to.pos()); } } // namespace lyx