X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FText2.cpp;h=66dd083d4ad6896f86152e535237609b204adb79;hb=bca1b63d89e27b31b089ab48c63368640084b3a6;hp=d8a9114576d7b6bca6ce69fa8c81aec9beec01dc;hpb=20976e81fb25899ee8d3ec5ed941fda6b453f59f;p=lyx.git diff --git a/src/Text2.cpp b/src/Text2.cpp index d8a9114576..66dd083d4a 100644 --- a/src/Text2.cpp +++ b/src/Text2.cpp @@ -23,34 +23,22 @@ #include "Text.h" #include "Buffer.h" -#include "buffer_funcs.h" -#include "BufferList.h" #include "BufferParams.h" #include "BufferView.h" #include "Changes.h" #include "Cursor.h" -#include "CutAndPaste.h" -#include "DispatchResult.h" -#include "ErrorList.h" #include "Language.h" #include "Layout.h" -#include "Lexer.h" -#include "LyX.h" #include "LyXRC.h" #include "Paragraph.h" #include "ParagraphParameters.h" #include "TextClass.h" #include "TextMetrics.h" -#include "insets/InsetCollapsible.h" - -#include "mathed/InsetMathHull.h" +#include "insets/InsetText.h" #include "support/lassert.h" -#include "support/debug.h" #include "support/gettext.h" -#include "support/lyxalgo.h" -#include "support/textutils.h" #include @@ -157,7 +145,7 @@ void Text::setInsetFont(BufferView const & bv, pit_type pit, Inset * const inset = pars_[pit].getInset(pos); LASSERT(inset && inset->resetFontEdit(), return); - CursorSlice::idx_type endidx = inset->nargs(); + idx_type endidx = inset->nargs(); for (CursorSlice cs(*inset); cs.idx() != endidx; ++cs.idx()) { Text * text = cs.text(); if (text) { @@ -182,10 +170,16 @@ void Text::setLayout(pit_type start, pit_type end, for (pit_type pit = start; pit != end; ++pit) { Paragraph & par = pars_[pit]; - par.applyLayout(lyxlayout); + // Is this a separating paragraph? If so, + // this needs to be standard layout + bool const is_separator = par.size() == 1 + && par.isEnvSeparator(0); + par.applyLayout(is_separator ? bp.documentClass().defaultLayout() : lyxlayout); if (lyxlayout.margintype == MARGIN_MANUAL) par.setLabelWidthString(par.expandLabel(lyxlayout, bp)); } + + deleteEmptyParagraphMechanism(start, end - 1, bp.track_changes); } @@ -198,6 +192,7 @@ void Text::setLayout(Cursor & cur, docstring const & layout) pit_type end = cur.selEnd().pit() + 1; cur.recordUndoSelection(); setLayout(start, end, layout); + cur.fixIfBroken(); cur.setCurrentFont(); cur.forceBufferUpdate(); } @@ -215,7 +210,7 @@ static bool changeDepthAllowed(Text::DEPTH_CHANGE type, } -bool Text::changeDepthAllowed(Cursor & cur, DEPTH_CHANGE type) const +bool Text::changeDepthAllowed(Cursor const & cur, DEPTH_CHANGE type) const { LBUFERR(this == cur.text()); // this happens when selecting several cells in tabular (bug 2630) @@ -235,7 +230,7 @@ bool Text::changeDepthAllowed(Cursor & cur, DEPTH_CHANGE type) const } -void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type) +void Text::changeDepth(Cursor const & cur, DEPTH_CHANGE type) { LBUFERR(this == cur.text()); pit_type const beg = cur.selBegin().pit(); @@ -480,7 +475,7 @@ void Text::setLabelWidthStringToSequence(Cursor const & cur, } -void Text::setParagraphs(Cursor & cur, docstring arg, bool merge) +void Text::setParagraphs(Cursor const & cur, docstring const & arg, bool merge) { LBUFERR(cur.text()); @@ -508,7 +503,7 @@ void Text::setParagraphs(Cursor & cur, docstring arg, bool merge) } -void Text::setParagraphs(Cursor & cur, ParagraphParameters const & p) +void Text::setParagraphs(Cursor const & cur, ParagraphParameters const & p) { LBUFERR(cur.text()); @@ -600,7 +595,7 @@ bool Text::checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool mo if (cur.pos() == cur.lastpos()) return false; Paragraph & par = cur.paragraph(); - Inset * inset = par.isInset(cur.pos()) ? par.getInset(cur.pos()) : 0; + Inset * inset = par.isInset(cur.pos()) ? par.getInset(cur.pos()) : nullptr; if (!inset || !inset->editable()) return false; if (cur.selection() && cur.realAnchor().find(inset) == -1) @@ -779,26 +774,33 @@ bool Text::cursorDownParagraph(Cursor & cur) return updated; } - namespace { -// fix the cursor `cur' after characters has been deleted at `where' -// position. Called by deleteEmptyParagraphMechanism -void fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where, - pos_type from, pos_type to) -{ - // Do nothing if cursor is not in the paragraph where the - // deletion occurred, - if (cur.pit() != where.pit()) - return; - // If cursor position is after the deletion place update it - if (cur.pos() > from) - cur.pos() = max(from, cur.pos() - (to - from)); +/** delete num_spaces characters between from and to. Return the + * number of spaces that got physically deleted (not marked as + * deleted) */ +int deleteSpaces(Paragraph & par, pos_type const from, pos_type to, + int num_spaces, bool const trackChanges) +{ + if (num_spaces <= 0) + return 0; + + // First, delete spaces marked as inserted + int pos = from; + while (pos < to && num_spaces > 0) { + Change const & change = par.lookupChange(pos); + if (change.inserted() && change.currentAuthor()) { + par.eraseChar(pos, trackChanges); + --num_spaces; + --to; + } else + ++pos; + } - // Check also if we don't want to set the cursor on a spot behind the - // pagragraph because we erased the last character. - if (cur.pos() > cur.lastpos()) - cur.pos() = cur.lastpos(); + // Then remove remaining spaces + int const psize = par.size(); + par.eraseChars(from, from + num_spaces, trackChanges); + return psize - par.size(); } } @@ -810,161 +812,178 @@ bool Text::deleteEmptyParagraphMechanism(Cursor & cur, //LYXERR(Debug::DEBUG, "DEPM: cur:\n" << cur << "old:\n" << old); Paragraph & oldpar = old.paragraph(); + bool const trackChanges = cur.buffer()->params().track_changes; + bool result = false; - // We allow all kinds of "mumbo-jumbo" when freespacing. - if (oldpar.isFreeSpacing()) + // We do nothing if cursor did not move + if (cur.top() == old.top()) return false; - /* Ok I'll put some comments here about what is missing. - There are still some small problems that can lead to - double spaces stored in the document file or space at - the beginning of paragraphs(). This happens if you have - the cursor between to spaces and then save. Or if you - cut and paste and the selection have a space at the - beginning and then save right after the paste. (Lgb) - */ - - // If old.pos() == 0 and old.pos()(1) == LineSeparator - // delete the LineSeparator. - // MISSING - - // If old.pos() == 1 and old.pos()(0) == LineSeparator - // delete the LineSeparator. - // MISSING - - // Find a common inset and the corresponding depth. - size_t depth = 0; - for (; depth < cur.depth(); ++depth) - if (&old.inset() == &cur[depth].inset()) - break; + // We do not do anything on read-only documents + if (cur.buffer()->isReadonly()) + return false; // Whether a common inset is found and whether the cursor is still in // the same paragraph (possibly nested). - bool const same_par = depth < cur.depth() && old.pit() == cur[depth].pit(); - bool const same_par_pos = depth == cur.depth() - 1 && same_par - && old.pos() == cur[depth].pos(); + int const depth = cur.find(&old.inset()); + bool const same_par = depth != -1 && old.idx() == cur[depth].idx() + && old.pit() == cur[depth].pit(); + + /* + * (1) If the chars around the old cursor were spaces and the + * paragraph is not in free spacing mode, delete some of them, but + * only if the cursor has really moved. + */ - // If the chars around the old cursor were spaces, delete some of - // them , but only if the cursor has really moved. - if (!same_par_pos) { + /* There are still some small problems that can lead to + double spaces stored in the document file or space at + the beginning of paragraphs(). This happens if you have + the cursor between two spaces and then save. Or if you + cut and paste and the selection has a space at the + beginning and then save right after the paste. (Lgb) + */ + if (!oldpar.isFreeSpacing()) { // find range of spaces around cursors - int from = old.pos(); + pos_type from = old.pos(); while (from > 0 - && oldpar.isLineSeparator(from - 1) - && !oldpar.isDeleted(from - 1)) + && oldpar.isLineSeparator(from - 1) + && !oldpar.isDeleted(from - 1)) --from; - int to = old.pos(); - while (to < oldpar.size() - 1 - && oldpar.isLineSeparator(to) - && !oldpar.isDeleted(to)) + pos_type to = old.pos(); + while (to < old.lastpos() + && oldpar.isLineSeparator(to) + && !oldpar.isDeleted(to)) ++to; - // If we are not at the extremity of the paragraph, keep one space - if (from != to && from > 0 && to < oldpar.size()) - ++from; + int num_spaces = to - from; + // If we are not at the start of the paragraph, keep one space + if (from != to && from > 0) + --num_spaces; + + // If cursor is inside range, keep one additional space + if (same_par && cur.pos() > from && cur.pos() < to) + --num_spaces; // Remove spaces and adapt cursor. - if (from < to) { - oldpar.eraseChars(from, to, cur.buffer()->params().track_changes); -// FIXME: This will not work anymore when we have multiple views of the same buffer -// In this case, we will have to correct also the cursors held by -// other bufferviews. It will probably be easier to do that in a more -// automated way in CursorSlice code. (JMarc 26/09/2001) - // correct all cursor parts + if (num_spaces > 0) { + old.recordUndo(); + int const deleted = + deleteSpaces(oldpar, from, to, num_spaces, trackChanges); + // correct cur position + // FIXME: there can be other cursors pointing there, we should update them if (same_par) { - fixCursorAfterDelete(cur[depth], old.top(), from, to); + if (cur[depth].pos() >= to) + cur[depth].pos() -= deleted; + else if (cur[depth].pos() > from) + cur[depth].pos() = min(from + 1, old.lastpos()); need_anchor_change = true; } - return true; + result = true; } } - // only do our magic if we changed paragraph + /* + * (2) If the paragraph where the cursor was is empty, delete it + */ + + // only do our other magic if we changed paragraph if (same_par) - return false; + return result; + + // only do our magic if the paragraph is empty + if (!oldpar.empty()) + return result; // don't delete anything if this is the ONLY paragraph! if (old.lastpit() == 0) - return false; + return result; // Do not delete empty paragraphs with keepempty set. if (oldpar.allowEmpty()) - return false; - - if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) { - // Delete old par. - old.recordUndo(max(old.pit() - 1, pit_type(0)), - min(old.pit() + 1, old.lastpit())); - ParagraphList & plist = old.text()->paragraphs(); - bool const soa = oldpar.params().startOfAppendix(); - plist.erase(lyx::next(plist.begin(), old.pit())); - // do not lose start of appendix marker (bug 4212) - if (soa && old.pit() < pit_type(plist.size())) - plist[old.pit()].params().startOfAppendix(true); - - // see #warning (FIXME?) above - if (cur.depth() >= old.depth()) { - CursorSlice & curslice = cur[old.depth() - 1]; - if (&curslice.inset() == &old.inset() - && curslice.pit() > old.pit()) { - --curslice.pit(); - // since a paragraph has been deleted, all the - // insets after `old' have been copied and - // their address has changed. Therefore we - // need to `regenerate' cur. (JMarc) - cur.updateInsets(&(cur.bottom().inset())); - need_anchor_change = true; - } + return result; + + // Delete old par. + old.recordUndo(max(old.pit() - 1, pit_type(0)), + min(old.pit() + 1, old.lastpit())); + ParagraphList & plist = old.text()->paragraphs(); + bool const soa = oldpar.params().startOfAppendix(); + plist.erase(plist.iterator_at(old.pit())); + // do not lose start of appendix marker (bug 4212) + if (soa && old.pit() < pit_type(plist.size())) + plist[old.pit()].params().startOfAppendix(true); + + // see #warning (FIXME?) above + if (cur.depth() >= old.depth()) { + CursorSlice & curslice = cur[old.depth() - 1]; + if (&curslice.inset() == &old.inset() + && curslice.idx() == old.idx() + && curslice.pit() > old.pit()) { + --curslice.pit(); + // since a paragraph has been deleted, all the + // insets after `old' have been copied and + // their address has changed. Therefore we + // need to `regenerate' cur. (JMarc) + cur.updateInsets(&(cur.bottom().inset())); + need_anchor_change = true; } - return true; - } - - if (oldpar.stripLeadingSpaces(cur.buffer()->params().track_changes)) { - need_anchor_change = true; - // We return true here because the Paragraph contents changed and - // we need a redraw before further action is processed. - return true; } - return false; + return true; } void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges) +{ + pos_type last_pos = pars_[last].size() - 1; + deleteEmptyParagraphMechanism(first, last, 0, last_pos, trackChanges); +} + + +void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, + pos_type first_pos, pos_type last_pos, + bool trackChanges) { LASSERT(first >= 0 && first <= last && last < (int) pars_.size(), return); for (pit_type pit = first; pit <= last; ++pit) { Paragraph & par = pars_[pit]; - // We allow all kinds of "mumbo-jumbo" when freespacing. - if (par.isFreeSpacing()) - continue; - - pos_type from = 0; - while (from < par.size()) { - // skip non-spaces - while (from < par.size() - && (!par.isLineSeparator(from) || par.isDeleted(from))) - ++from; - LYXERR0("from=" << from); - // find string of spaces - pos_type to = from; - while (to < par.size() - && par.isLineSeparator(to) && !par.isDeleted(to)) - ++to; - LYXERR0("to=" << to); - // empty? We are done - if (from == to) - break; - // if inside the line, keep one space - if (from > 0 && to < par.size()) - ++from; - // remove the extra spaces - if (from < to) - par.eraseChars(from, to, trackChanges); + /* + * (1) Delete consecutive spaces + */ + if (!par.isFreeSpacing()) { + pos_type from = (pit == first) ? first_pos : 0; + pos_type to_pos = (pit == last) ? last_pos + 1 : par.size(); + while (from < to_pos) { + // skip non-spaces + while (from < par.size() + && (!par.isLineSeparator(from) || par.isDeleted(from))) + ++from; + // find string of spaces + pos_type to = from; + while (to < par.size() + && par.isLineSeparator(to) && !par.isDeleted(to)) + ++to; + // empty? We are done + if (from == to) + break; + + int num_spaces = to - from; + + // If we are not at the extremity of the paragraph, keep one space + if (from != to && from > 0 && to < par.size()) + --num_spaces; + + // Remove spaces if needed + int const deleted = deleteSpaces(par, from , to, num_spaces, trackChanges); + from = to - deleted; + } } + /* + * (2) Delete empty pragraphs + */ + // don't delete anything if this is the only remaining paragraph // within the given range. Note: Text::acceptOrRejectChanges() // sets the cursor to 'first' after calling DEPM @@ -976,13 +995,11 @@ void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool tra continue; if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) { - pars_.erase(lyx::next(pars_.begin(), pit)); + pars_.erase(pars_.iterator_at(pit)); --pit; --last; continue; } - - par.stripLeadingSpaces(trackChanges); } }