3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Lars Gullik Bjønnes
8 * \author Alfredo Braunstein
9 * \author Jean-Marc Lasgouttes
10 * \author Angus Leeming
12 * \author André Pönitz
14 * \author Stefan Schimanski
16 * \author Jürgen Vigna
18 * Full author contact details are available in file CREDITS.
26 #include "buffer_funcs.h"
27 #include "BufferList.h"
28 #include "BufferParams.h"
29 #include "BufferView.h"
33 #include "CutAndPaste.h"
34 #include "DispatchResult.h"
35 #include "ErrorList.h"
36 #include "FuncRequest.h"
42 #include "Paragraph.h"
43 #include "paragraph_funcs.h"
44 #include "ParagraphParameters.h"
45 #include "ParIterator.h"
46 #include "TextClass.h"
47 #include "TextMetrics.h"
50 #include "frontends/FontMetrics.h"
52 #include "insets/InsetEnvironment.h"
54 #include "mathed/InsetMathHull.h"
56 #include "support/debug.h"
57 #include "support/gettext.h"
58 #include "support/textutils.h"
60 #include <boost/next_prior.hpp>
65 using std::ostringstream;
69 using std::istringstream;
74 : autoBreakRows_(false)
78 bool Text::isMainText(Buffer const & buffer) const
80 return &buffer.text() == this;
84 FontInfo Text::getLayoutFont(Buffer const & buffer, pit_type const pit) const
86 LayoutPtr const & layout = pars_[pit].layout();
88 if (!pars_[pit].getDepth()) {
89 FontInfo lf = layout->resfont;
90 // In case the default family has been customized
91 if (layout->font.family() == INHERIT_FAMILY)
92 lf.setFamily(buffer.params().getFont().fontInfo().family());
96 FontInfo font = layout->font;
97 // Realize with the fonts of lesser depth.
98 //font.realize(outerFont(pit, paragraphs()));
99 font.realize(buffer.params().getFont().fontInfo());
105 FontInfo Text::getLabelFont(Buffer const & buffer, Paragraph const & par) const
107 LayoutPtr const & layout = par.layout();
109 if (!par.getDepth()) {
110 FontInfo lf = layout->reslabelfont;
111 // In case the default family has been customized
112 if (layout->labelfont.family() == INHERIT_FAMILY)
113 lf.setFamily(buffer.params().getFont().fontInfo().family());
117 FontInfo font = layout->labelfont;
118 // Realize with the fonts of lesser depth.
119 font.realize(buffer.params().getFont().fontInfo());
125 void Text::setCharFont(Buffer const & buffer, pit_type pit,
126 pos_type pos, Font const & fnt, Font const & display_font)
129 LayoutPtr const & layout = pars_[pit].layout();
131 // Get concrete layout font to reduce against
134 if (pos < pars_[pit].beginOfBody())
135 layoutfont = layout->labelfont;
137 layoutfont = layout->font;
139 // Realize against environment font information
140 if (pars_[pit].getDepth()) {
142 while (!layoutfont.resolved() &&
143 tp != pit_type(paragraphs().size()) &&
144 pars_[tp].getDepth()) {
145 tp = outerHook(tp, paragraphs());
146 if (tp != pit_type(paragraphs().size()))
147 layoutfont.realize(pars_[tp].layout()->font);
151 // Inside inset, apply the inset's font attributes if any
153 if (!isMainText(buffer))
154 layoutfont.realize(display_font.fontInfo());
156 layoutfont.realize(buffer.params().getFont().fontInfo());
158 // Now, reduce font against full layout font
159 font.fontInfo().reduce(layoutfont);
161 pars_[pit].setFont(pos, font);
165 void Text::setInsetFont(BufferView const & bv, pit_type pit,
166 pos_type pos, Font const & font, bool toggleall)
168 BOOST_ASSERT(pars_[pit].isInset(pos) &&
169 pars_[pit].getInset(pos)->noFontChange());
171 Inset * const inset = pars_[pit].getInset(pos);
172 CursorSlice::idx_type endidx = inset->nargs();
173 for (CursorSlice cs(*inset); cs.idx() != endidx; ++cs.idx()) {
174 Text * text = cs.text();
176 // last position of the cell
177 CursorSlice cellend = cs;
178 cellend.pit() = cellend.lastpit();
179 cellend.pos() = cellend.lastpos();
180 text->setFont(bv, cs, cellend, font, toggleall);
186 // return past-the-last paragraph influenced by a layout change on pit
187 pit_type Text::undoSpan(pit_type pit)
189 pit_type end = paragraphs().size();
190 pit_type nextpit = pit + 1;
193 //because of parindents
194 if (!pars_[pit].getDepth())
195 return boost::next(nextpit);
196 //because of depth constrains
197 for (; nextpit != end; ++pit, ++nextpit) {
198 if (!pars_[pit].getDepth())
205 void Text::setLayout(Buffer const & buffer, pit_type start, pit_type end,
206 docstring const & layout)
208 BOOST_ASSERT(start != end);
210 BufferParams const & bufparams = buffer.params();
211 LayoutPtr const & lyxlayout = bufparams.getTextClass()[layout];
213 for (pit_type pit = start; pit != end; ++pit) {
214 Paragraph & par = pars_[pit];
215 par.applyLayout(lyxlayout);
216 if (lyxlayout->margintype == MARGIN_MANUAL)
217 par.setLabelWidthString(par.translateIfPossible(
218 lyxlayout->labelstring(), buffer.params()));
223 // set layout over selection and make a total rebreak of those paragraphs
224 void Text::setLayout(Cursor & cur, docstring const & layout)
226 BOOST_ASSERT(this == cur.text());
227 // special handling of new environment insets
228 BufferView & bv = cur.bv();
229 BufferParams const & params = bv.buffer().params();
230 LayoutPtr const & lyxlayout = params.getTextClass()[layout];
231 if (lyxlayout->is_environment) {
232 // move everything in a new environment inset
233 LYXERR(Debug::DEBUG, "setting layout " << to_utf8(layout));
234 lyx::dispatch(FuncRequest(LFUN_LINE_BEGIN));
235 lyx::dispatch(FuncRequest(LFUN_LINE_END_SELECT));
236 lyx::dispatch(FuncRequest(LFUN_CUT));
237 Inset * inset = new InsetEnvironment(params, layout);
238 insertInset(cur, inset);
239 //inset->edit(cur, true);
240 //lyx::dispatch(FuncRequest(LFUN_PASTE));
244 pit_type start = cur.selBegin().pit();
245 pit_type end = cur.selEnd().pit() + 1;
246 pit_type undopit = undoSpan(end - 1);
247 recUndo(cur, start, undopit - 1);
248 setLayout(cur.buffer(), start, end, layout);
249 updateLabels(cur.buffer());
253 static bool changeDepthAllowed(Text::DEPTH_CHANGE type,
254 Paragraph const & par, int max_depth)
256 if (par.layout()->labeltype == LABEL_BIBLIO)
258 int const depth = par.params().depth();
259 if (type == Text::INC_DEPTH && depth < max_depth)
261 if (type == Text::DEC_DEPTH && depth > 0)
267 bool Text::changeDepthAllowed(Cursor & cur, DEPTH_CHANGE type) const
269 BOOST_ASSERT(this == cur.text());
270 // this happens when selecting several cells in tabular (bug 2630)
271 if (cur.selBegin().idx() != cur.selEnd().idx())
274 pit_type const beg = cur.selBegin().pit();
275 pit_type const end = cur.selEnd().pit() + 1;
276 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
278 for (pit_type pit = beg; pit != end; ++pit) {
279 if (lyx::changeDepthAllowed(type, pars_[pit], max_depth))
281 max_depth = pars_[pit].getMaxDepthAfter();
287 void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type)
289 BOOST_ASSERT(this == cur.text());
290 pit_type const beg = cur.selBegin().pit();
291 pit_type const end = cur.selEnd().pit() + 1;
292 cur.recordUndoSelection();
293 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
295 for (pit_type pit = beg; pit != end; ++pit) {
296 Paragraph & par = pars_[pit];
297 if (lyx::changeDepthAllowed(type, par, max_depth)) {
298 int const depth = par.params().depth();
299 if (type == INC_DEPTH)
300 par.params().depth(depth + 1);
302 par.params().depth(depth - 1);
304 max_depth = par.getMaxDepthAfter();
306 // this handles the counter labels, and also fixes up
307 // depth values for follow-on (child) paragraphs
308 updateLabels(cur.buffer());
312 void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
314 BOOST_ASSERT(this == cur.text());
315 // Set the current_font
316 // Determine basis font
318 pit_type pit = cur.pit();
319 if (cur.pos() < pars_[pit].beginOfBody())
320 layoutfont = getLabelFont(cur.buffer(), pars_[pit]);
322 layoutfont = getLayoutFont(cur.buffer(), pit);
324 // Update current font
325 cur.real_current_font.update(font,
326 cur.buffer().params().language,
329 // Reduce to implicit settings
330 cur.current_font = cur.real_current_font;
331 cur.current_font.fontInfo().reduce(layoutfont);
332 // And resolve it completely
333 cur.real_current_font.fontInfo().realize(layoutfont);
335 // if there is no selection that's all we need to do
336 if (!cur.selection())
339 // Ok, we have a selection.
340 cur.recordUndoSelection();
342 setFont(cur.bv(), cur.selectionBegin().top(),
343 cur.selectionEnd().top(), font, toggleall);
347 void Text::setFont(BufferView const & bv, CursorSlice const & begin,
348 CursorSlice const & end, Font const & font,
351 Buffer const & buffer = bv.buffer();
353 // Don't use forwardChar here as ditend might have
354 // pos() == lastpos() and forwardChar would miss it.
355 // Can't use forwardPos either as this descends into
357 Language const * language = buffer.params().language;
358 for (CursorSlice dit = begin; dit != end; dit.forwardPos()) {
359 if (dit.pos() != dit.lastpos()) {
360 pit_type const pit = dit.pit();
361 pos_type const pos = dit.pos();
362 if (pars_[pit].isInset(pos) &&
363 pars_[pit].getInset(pos)->noFontChange())
364 // We need to propagate the font change to all
365 // text cells of the inset (bug 1973).
366 // FIXME: This should change, see documentation
367 // of noFontChange in Inset.h
368 setInsetFont(bv, pit, pos, font, toggleall);
369 TextMetrics const & tm = bv.textMetrics(this);
370 Font f = tm.getDisplayFont(pit, pos);
371 f.update(font, language, toggleall);
372 setCharFont(buffer, pit, pos, f, tm.font_);
378 bool Text::cursorTop(Cursor & cur)
380 BOOST_ASSERT(this == cur.text());
381 return setCursor(cur, 0, 0);
385 bool Text::cursorBottom(Cursor & cur)
387 BOOST_ASSERT(this == cur.text());
388 return setCursor(cur, cur.lastpit(), boost::prior(paragraphs().end())->size());
392 void Text::toggleFree(Cursor & cur, Font const & font, bool toggleall)
394 BOOST_ASSERT(this == cur.text());
395 // If the mask is completely neutral, tell user
396 if (font.fontInfo() == ignore_font &&
397 (font.language() == 0 || font.language() == ignore_language)) {
398 // Could only happen with user style
399 cur.message(_("No font change defined."));
403 // Try implicit word selection
404 // If there is a change in the language the implicit word selection
406 CursorSlice resetCursor = cur.top();
407 bool implicitSelection =
408 font.language() == ignore_language
409 && font.fontInfo().number() == FONT_IGNORE
410 && selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
413 setFont(cur, font, toggleall);
415 // Implicit selections are cleared afterwards
416 // and cursor is set to the original position.
417 if (implicitSelection) {
418 cur.clearSelection();
419 cur.top() = resetCursor;
425 docstring Text::getStringToIndex(Cursor const & cur)
427 BOOST_ASSERT(this == cur.text());
431 idxstring = cur.selectionAsString(false);
433 // Try implicit word selection. If there is a change
434 // in the language the implicit word selection is
437 selectWord(tmpcur, PREVIOUS_WORD);
439 if (!tmpcur.selection())
440 cur.message(_("Nothing to index!"));
441 else if (tmpcur.selBegin().pit() != tmpcur.selEnd().pit())
442 cur.message(_("Cannot index more than one paragraph!"));
444 idxstring = tmpcur.selectionAsString(false);
451 void Text::setParagraphs(Cursor & cur, docstring arg, bool merge)
453 BOOST_ASSERT(cur.text());
454 // make sure that the depth behind the selection are restored, too
455 pit_type undopit = undoSpan(cur.selEnd().pit());
456 recUndo(cur, cur.selBegin().pit(), undopit - 1);
458 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
460 Paragraph & par = pars_[pit];
461 ParagraphParameters params = par.params();
462 params.read(to_utf8(arg), merge);
463 Layout const & layout = *(par.layout());
464 par.params().apply(params, layout);
469 //FIXME This is a little redundant now, but it's probably worth keeping,
470 //especially if we're going to go away from using serialization internally
472 void Text::setParagraphs(Cursor & cur, ParagraphParameters const & p)
474 BOOST_ASSERT(cur.text());
475 // make sure that the depth behind the selection are restored, too
476 pit_type undopit = undoSpan(cur.selEnd().pit());
477 recUndo(cur, cur.selBegin().pit(), undopit - 1);
479 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
481 Paragraph & par = pars_[pit];
482 Layout const & layout = *(par.layout());
483 par.params().apply(p, layout);
488 // this really should just insert the inset and not move the cursor.
489 void Text::insertInset(Cursor & cur, Inset * inset)
491 BOOST_ASSERT(this == cur.text());
493 cur.paragraph().insertInset(cur.pos(), inset, cur.current_font,
494 Change(cur.buffer().params().trackChanges ?
495 Change::INSERTED : Change::UNCHANGED));
499 // needed to insert the selection
500 void Text::insertStringAsLines(Cursor & cur, docstring const & str)
502 cur.buffer().insertStringAsLines(pars_, cur.pit(), cur.pos(),
503 cur.current_font, str, autoBreakRows_);
507 // turn double CR to single CR, others are converted into one
508 // blank. Then insertStringAsLines is called
509 void Text::insertStringAsParagraphs(Cursor & cur, docstring const & str)
511 docstring linestr = str;
512 bool newline_inserted = false;
514 for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
515 if (linestr[i] == '\n') {
516 if (newline_inserted) {
517 // we know that \r will be ignored by
518 // insertStringAsLines. Of course, it is a dirty
519 // trick, but it works...
520 linestr[i - 1] = '\r';
524 newline_inserted = true;
526 } else if (isPrintable(linestr[i])) {
527 newline_inserted = false;
530 insertStringAsLines(cur, linestr);
534 bool Text::setCursor(Cursor & cur, pit_type par, pos_type pos,
535 bool setfont, bool boundary)
538 setCursorIntern(cur, par, pos, setfont, boundary);
539 return cur.bv().checkDepm(cur, old);
543 void Text::setCursor(CursorSlice & cur, pit_type par, pos_type pos)
545 BOOST_ASSERT(par != int(paragraphs().size()));
549 // now some strict checking
550 Paragraph & para = getPar(par);
552 // None of these should happen, but we're scaredy-cats
554 lyxerr << "dont like -1" << endl;
558 if (pos > para.size()) {
559 lyxerr << "dont like 1, pos: " << pos
560 << " size: " << para.size()
561 << " par: " << par << endl;
567 void Text::setCursorIntern(Cursor & cur,
568 pit_type par, pos_type pos, bool setfont, bool boundary)
570 BOOST_ASSERT(this == cur.text());
571 cur.boundary(boundary);
572 setCursor(cur.top(), par, pos);
574 cur.setCurrentFont();
578 bool Text::checkAndActivateInset(Cursor & cur, bool front)
582 if (front && cur.pos() == cur.lastpos())
584 if (!front && cur.pos() == 0)
586 Inset * inset = front ? cur.nextInset() : cur.prevInset();
587 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
590 * Apparently, when entering an inset we are expected to be positioned
591 * *before* it in the containing paragraph, regardless of the direction
592 * from which we are entering. Otherwise, cursor placement goes awry,
593 * and when we exit from the beginning, we'll be placed *after* the
598 inset->edit(cur, front);
603 bool Text::cursorBackward(Cursor & cur)
605 // Tell BufferView to test for FitCursor in any case!
606 cur.updateFlags(Update::FitCursor);
608 // not at paragraph start?
610 // if on right side of boundary (i.e. not at paragraph end, but line end)
611 // -> skip it, i.e. set boundary to true, i.e. go only logically left
612 // there are some exceptions to ignore this: lineseps, newlines, spaces
614 // some effectless debug code to see the values in the debugger
615 bool bound = cur.boundary();
616 int rowpos = cur.textRow().pos();
618 bool sep = cur.paragraph().isSeparator(cur.pos() - 1);
619 bool newline = cur.paragraph().isNewline(cur.pos() - 1);
620 bool linesep = cur.paragraph().isLineSeparator(cur.pos() - 1);
622 if (!cur.boundary() &&
623 cur.textRow().pos() == cur.pos() &&
624 !cur.paragraph().isLineSeparator(cur.pos() - 1) &&
625 !cur.paragraph().isNewline(cur.pos() - 1) &&
626 !cur.paragraph().isSeparator(cur.pos() - 1)) {
627 return setCursor(cur, cur.pit(), cur.pos(), true, true);
630 // go left and try to enter inset
631 if (checkAndActivateInset(cur, false))
634 // normal character left
635 return setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
638 // move to the previous paragraph or do nothing
640 return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size(), true, false);
645 bool Text::cursorForward(Cursor & cur)
647 // Tell BufferView to test for FitCursor in any case!
648 cur.updateFlags(Update::FitCursor);
650 // not at paragraph end?
651 if (cur.pos() != cur.lastpos()) {
652 // in front of editable inset, i.e. jump into it?
653 if (checkAndActivateInset(cur, true))
656 TextMetrics const & tm = cur.bv().textMetrics(this);
657 // if left of boundary -> just jump to right side
658 // but for RTL boundaries don't, because: abc|DDEEFFghi -> abcDDEEF|Fghi
659 if (cur.boundary() && !tm.isRTLBoundary(cur.pit(), cur.pos()))
660 return setCursor(cur, cur.pit(), cur.pos(), true, false);
662 // next position is left of boundary,
663 // but go to next line for special cases like space, newline, linesep
665 // some effectless debug code to see the values in the debugger
666 int endpos = cur.textRow().endpos();
667 int lastpos = cur.lastpos();
669 bool linesep = cur.paragraph().isLineSeparator(cur.pos());
670 bool newline = cur.paragraph().isNewline(cur.pos());
671 bool sep = cur.paragraph().isSeparator(cur.pos());
672 if (cur.pos() != cur.lastpos()) {
673 bool linesep2 = cur.paragraph().isLineSeparator(cur.pos()+1);
674 bool newline2 = cur.paragraph().isNewline(cur.pos()+1);
675 bool sep2 = cur.paragraph().isSeparator(cur.pos()+1);
678 if (cur.textRow().endpos() == cur.pos() + 1 &&
679 cur.textRow().endpos() != cur.lastpos() &&
680 !cur.paragraph().isNewline(cur.pos()) &&
681 !cur.paragraph().isLineSeparator(cur.pos()) &&
682 !cur.paragraph().isSeparator(cur.pos())) {
683 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
686 // in front of RTL boundary? Stay on this side of the boundary because:
687 // ab|cDDEEFFghi -> abc|DDEEFFghi
688 if (tm.isRTLBoundary(cur.pit(), cur.pos() + 1))
689 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
692 return setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
695 // move to next paragraph
696 if (cur.pit() != cur.lastpit())
697 return setCursor(cur, cur.pit() + 1, 0, true, false);
702 bool Text::cursorUpParagraph(Cursor & cur)
704 bool updated = false;
706 updated = setCursor(cur, cur.pit(), 0);
707 else if (cur.pit() != 0)
708 updated = setCursor(cur, cur.pit() - 1, 0);
713 bool Text::cursorDownParagraph(Cursor & cur)
715 bool updated = false;
716 if (cur.pit() != cur.lastpit())
717 updated = setCursor(cur, cur.pit() + 1, 0);
719 updated = setCursor(cur, cur.pit(), cur.lastpos());
724 // fix the cursor `cur' after a characters has been deleted at `where'
725 // position. Called by deleteEmptyParagraphMechanism
726 void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
728 // Do nothing if cursor is not in the paragraph where the
730 if (cur.pit() != where.pit())
733 // If cursor position is after the deletion place update it
734 if (cur.pos() > where.pos())
737 // Check also if we don't want to set the cursor on a spot behind the
738 // pagragraph because we erased the last character.
739 if (cur.pos() > cur.lastpos())
740 cur.pos() = cur.lastpos();
744 bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
745 Cursor & old, bool & need_anchor_change)
747 //LYXERR(Debug::DEBUG, "DEPM: cur:\n" << cur << "old:\n" << old);
749 Paragraph & oldpar = old.paragraph();
751 // We allow all kinds of "mumbo-jumbo" when freespacing.
752 if (oldpar.isFreeSpacing())
755 /* Ok I'll put some comments here about what is missing.
756 There are still some small problems that can lead to
757 double spaces stored in the document file or space at
758 the beginning of paragraphs(). This happens if you have
759 the cursor between to spaces and then save. Or if you
760 cut and paste and the selection have a space at the
761 beginning and then save right after the paste. (Lgb)
764 // If old.pos() == 0 and old.pos()(1) == LineSeparator
765 // delete the LineSeparator.
768 // If old.pos() == 1 and old.pos()(0) == LineSeparator
769 // delete the LineSeparator.
772 bool const same_inset = &old.inset() == &cur.inset();
773 bool const same_par = same_inset && old.pit() == cur.pit();
774 bool const same_par_pos = same_par && old.pos() == cur.pos();
776 // If the chars around the old cursor were spaces, delete one of them.
778 // Only if the cursor has really moved.
780 && old.pos() < oldpar.size()
781 && oldpar.isLineSeparator(old.pos())
782 && oldpar.isLineSeparator(old.pos() - 1)
783 && !oldpar.isDeleted(old.pos() - 1)
784 && !oldpar.isDeleted(old.pos())) {
785 oldpar.eraseChar(old.pos() - 1, cur.buffer().params().trackChanges);
786 // FIXME: This will not work anymore when we have multiple views of the same buffer
787 // In this case, we will have to correct also the cursors held by
788 // other bufferviews. It will probably be easier to do that in a more
789 // automated way in CursorSlice code. (JMarc 26/09/2001)
790 // correct all cursor parts
792 fixCursorAfterDelete(cur.top(), old.top());
793 need_anchor_change = true;
799 // only do our magic if we changed paragraph
803 // don't delete anything if this is the ONLY paragraph!
804 if (old.lastpit() == 0)
807 // Do not delete empty paragraphs with keepempty set.
808 if (oldpar.allowEmpty())
811 if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
813 old.recordUndo(ATOMIC_UNDO,
814 max(old.pit() - 1, pit_type(0)),
815 min(old.pit() + 1, old.lastpit()));
816 ParagraphList & plist = old.text()->paragraphs();
817 bool const soa = oldpar.params().startOfAppendix();
818 plist.erase(boost::next(plist.begin(), old.pit()));
819 // do not lose start of appendix marker (bug 4212)
820 if (soa && old.pit() < pit_type(plist.size()))
821 plist[old.pit()].params().startOfAppendix(true);
823 // see #warning (FIXME?) above
824 if (cur.depth() >= old.depth()) {
825 CursorSlice & curslice = cur[old.depth() - 1];
826 if (&curslice.inset() == &old.inset()
827 && curslice.pit() > old.pit()) {
829 // since a paragraph has been deleted, all the
830 // insets after `old' have been copied and
831 // their address has changed. Therefore we
832 // need to `regenerate' cur. (JMarc)
833 cur.updateInsets(&(cur.bottom().inset()));
834 need_anchor_change = true;
840 if (oldpar.stripLeadingSpaces(cur.buffer().params().trackChanges)) {
841 need_anchor_change = true;
842 // We return true here because the Paragraph contents changed and
843 // we need a redraw before further action is processed.
851 void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges)
853 BOOST_ASSERT(first >= 0 && first <= last && last < (int) pars_.size());
855 for (pit_type pit = first; pit <= last; ++pit) {
856 Paragraph & par = pars_[pit];
858 // We allow all kinds of "mumbo-jumbo" when freespacing.
859 if (par.isFreeSpacing())
862 for (pos_type pos = 1; pos < par.size(); ++pos) {
863 if (par.isLineSeparator(pos) && par.isLineSeparator(pos - 1)
864 && !par.isDeleted(pos - 1)) {
865 if (par.eraseChar(pos - 1, trackChanges)) {
871 // don't delete anything if this is the only remaining paragraph within the given range
872 // note: Text::acceptOrRejectChanges() sets the cursor to 'first' after calling DEPM
876 // don't delete empty paragraphs with keepempty set
877 if (par.allowEmpty())
880 if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) {
881 pars_.erase(boost::next(pars_.begin(), pit));
887 par.stripLeadingSpaces(trackChanges);
892 void Text::recUndo(Cursor & cur, pit_type first, pit_type last) const
894 cur.recordUndo(ATOMIC_UNDO, first, last);
898 void Text::recUndo(Cursor & cur, pit_type par) const
900 cur.recordUndo(ATOMIC_UNDO, par, par);