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.
27 #include "buffer_funcs.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #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 "TextClass.h"
46 #include "TextMetrics.h"
49 #include "mathed/InsetMathHull.h"
51 #include "support/lassert.h"
52 #include "support/debug.h"
53 #include "support/gettext.h"
54 #include "support/textutils.h"
56 #include <boost/next_prior.hpp>
65 : autoBreakRows_(false)
69 bool Text::isMainText(Buffer const & buffer) const
71 return &buffer.text() == this;
75 FontInfo Text::layoutFont(Buffer const & buffer, pit_type const pit) const
77 Layout const & layout = pars_[pit].layout();
79 if (!pars_[pit].getDepth()) {
80 FontInfo lf = layout.resfont;
81 // In case the default family has been customized
82 if (layout.font.family() == INHERIT_FAMILY)
83 lf.setFamily(buffer.params().getFont().fontInfo().family());
87 FontInfo font = layout.font;
88 // Realize with the fonts of lesser depth.
89 //font.realize(outerFont(pit, paragraphs()));
90 font.realize(buffer.params().getFont().fontInfo());
96 FontInfo Text::labelFont(Buffer const & buffer, Paragraph const & par) const
98 Layout const & layout = par.layout();
100 if (!par.getDepth()) {
101 FontInfo lf = layout.reslabelfont;
102 // In case the default family has been customized
103 if (layout.labelfont.family() == INHERIT_FAMILY)
104 lf.setFamily(buffer.params().getFont().fontInfo().family());
108 FontInfo font = layout.labelfont;
109 // Realize with the fonts of lesser depth.
110 font.realize(buffer.params().getFont().fontInfo());
116 void Text::setCharFont(Buffer const & buffer, pit_type pit,
117 pos_type pos, Font const & fnt, Font const & display_font)
120 Layout const & layout = pars_[pit].layout();
122 // Get concrete layout font to reduce against
125 if (pos < pars_[pit].beginOfBody())
126 layoutfont = layout.labelfont;
128 layoutfont = layout.font;
130 // Realize against environment font information
131 if (pars_[pit].getDepth()) {
133 while (!layoutfont.resolved() &&
134 tp != pit_type(paragraphs().size()) &&
135 pars_[tp].getDepth()) {
136 tp = outerHook(tp, paragraphs());
137 if (tp != pit_type(paragraphs().size()))
138 layoutfont.realize(pars_[tp].layout().font);
142 // Inside inset, apply the inset's font attributes if any
144 if (!isMainText(buffer))
145 layoutfont.realize(display_font.fontInfo());
147 layoutfont.realize(buffer.params().getFont().fontInfo());
149 // Now, reduce font against full layout font
150 font.fontInfo().reduce(layoutfont);
152 pars_[pit].setFont(pos, font);
156 void Text::setInsetFont(BufferView const & bv, pit_type pit,
157 pos_type pos, Font const & font, bool toggleall)
159 Inset * const inset = pars_[pit].getInset(pos);
160 LASSERT(inset && inset->noFontChange(), /**/);
162 CursorSlice::idx_type endidx = inset->nargs();
163 for (CursorSlice cs(*inset); cs.idx() != endidx; ++cs.idx()) {
164 Text * text = cs.text();
166 // last position of the cell
167 CursorSlice cellend = cs;
168 cellend.pit() = cellend.lastpit();
169 cellend.pos() = cellend.lastpos();
170 text->setFont(bv, cs, cellend, font, toggleall);
176 // return past-the-last paragraph influenced by a layout change on pit
177 pit_type Text::undoSpan(pit_type pit)
179 pit_type end = paragraphs().size();
180 pit_type nextpit = pit + 1;
183 //because of parindents
184 if (!pars_[pit].getDepth())
185 return boost::next(nextpit);
186 //because of depth constrains
187 for (; nextpit != end; ++pit, ++nextpit) {
188 if (!pars_[pit].getDepth())
195 void Text::setLayout(Buffer const & buffer, pit_type start, pit_type end,
196 docstring const & layout)
198 LASSERT(start != end, /**/);
200 BufferParams const & bufparams = buffer.params();
201 Layout const & lyxlayout = bufparams.documentClass()[layout];
203 for (pit_type pit = start; pit != end; ++pit) {
204 Paragraph & par = pars_[pit];
205 par.applyLayout(lyxlayout);
206 if (lyxlayout.margintype == MARGIN_MANUAL)
207 par.setLabelWidthString(par.translateIfPossible(
208 lyxlayout.labelstring(), buffer.params()));
213 // set layout over selection and make a total rebreak of those paragraphs
214 void Text::setLayout(Cursor & cur, docstring const & layout)
216 LASSERT(this == cur.text(), /**/);
218 pit_type start = cur.selBegin().pit();
219 pit_type end = cur.selEnd().pit() + 1;
220 pit_type undopit = undoSpan(end - 1);
221 recUndo(cur, start, undopit - 1);
222 setLayout(cur.buffer(), start, end, layout);
223 updateLabels(cur.buffer());
227 static bool changeDepthAllowed(Text::DEPTH_CHANGE type,
228 Paragraph const & par, int max_depth)
230 if (par.layout().labeltype == LABEL_BIBLIO)
232 int const depth = par.params().depth();
233 if (type == Text::INC_DEPTH && depth < max_depth)
235 if (type == Text::DEC_DEPTH && depth > 0)
241 bool Text::changeDepthAllowed(Cursor & cur, DEPTH_CHANGE type) const
243 LASSERT(this == cur.text(), /**/);
244 // this happens when selecting several cells in tabular (bug 2630)
245 if (cur.selBegin().idx() != cur.selEnd().idx())
248 pit_type const beg = cur.selBegin().pit();
249 pit_type const end = cur.selEnd().pit() + 1;
250 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
252 for (pit_type pit = beg; pit != end; ++pit) {
253 if (lyx::changeDepthAllowed(type, pars_[pit], max_depth))
255 max_depth = pars_[pit].getMaxDepthAfter();
261 void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type)
263 LASSERT(this == cur.text(), /**/);
264 pit_type const beg = cur.selBegin().pit();
265 pit_type const end = cur.selEnd().pit() + 1;
266 cur.recordUndoSelection();
267 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
269 for (pit_type pit = beg; pit != end; ++pit) {
270 Paragraph & par = pars_[pit];
271 if (lyx::changeDepthAllowed(type, par, max_depth)) {
272 int const depth = par.params().depth();
273 if (type == INC_DEPTH)
274 par.params().depth(depth + 1);
276 par.params().depth(depth - 1);
278 max_depth = par.getMaxDepthAfter();
280 // this handles the counter labels, and also fixes up
281 // depth values for follow-on (child) paragraphs
282 updateLabels(cur.buffer());
286 void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
288 LASSERT(this == cur.text(), /**/);
289 // Set the current_font
290 // Determine basis font
292 pit_type pit = cur.pit();
293 if (cur.pos() < pars_[pit].beginOfBody())
294 layoutfont = labelFont(cur.buffer(), pars_[pit]);
296 layoutfont = layoutFont(cur.buffer(), pit);
298 // Update current font
299 cur.real_current_font.update(font,
300 cur.buffer().params().language,
303 // Reduce to implicit settings
304 cur.current_font = cur.real_current_font;
305 cur.current_font.fontInfo().reduce(layoutfont);
306 // And resolve it completely
307 cur.real_current_font.fontInfo().realize(layoutfont);
309 // if there is no selection that's all we need to do
310 if (!cur.selection())
313 // Ok, we have a selection.
314 cur.recordUndoSelection();
316 setFont(cur.bv(), cur.selectionBegin().top(),
317 cur.selectionEnd().top(), font, toggleall);
321 void Text::setFont(BufferView const & bv, CursorSlice const & begin,
322 CursorSlice const & end, Font const & font,
325 Buffer const & buffer = bv.buffer();
327 // Don't use forwardChar here as ditend might have
328 // pos() == lastpos() and forwardChar would miss it.
329 // Can't use forwardPos either as this descends into
331 Language const * language = buffer.params().language;
332 for (CursorSlice dit = begin; dit != end; dit.forwardPos()) {
333 if (dit.pos() == dit.lastpos())
335 pit_type const pit = dit.pit();
336 pos_type const pos = dit.pos();
337 Inset * inset = pars_[pit].getInset(pos);
338 if (inset && inset->noFontChange()) {
339 // We need to propagate the font change to all
340 // text cells of the inset (bug 1973).
341 // FIXME: This should change, see documentation
342 // of noFontChange in Inset.h
343 setInsetFont(bv, pit, pos, font, toggleall);
345 TextMetrics const & tm = bv.textMetrics(this);
346 Font f = tm.displayFont(pit, pos);
347 f.update(font, language, toggleall);
348 setCharFont(buffer, pit, pos, f, tm.font_);
353 bool Text::cursorTop(Cursor & cur)
355 LASSERT(this == cur.text(), /**/);
356 return setCursor(cur, 0, 0);
360 bool Text::cursorBottom(Cursor & cur)
362 LASSERT(this == cur.text(), /**/);
363 return setCursor(cur, cur.lastpit(), boost::prior(paragraphs().end())->size());
367 void Text::toggleFree(Cursor & cur, Font const & font, bool toggleall)
369 LASSERT(this == cur.text(), /**/);
370 // If the mask is completely neutral, tell user
371 if (font.fontInfo() == ignore_font && font.language() == ignore_language) {
372 // Could only happen with user style
373 cur.message(_("No font change defined."));
377 // Try implicit word selection
378 // If there is a change in the language the implicit word selection
380 CursorSlice resetCursor = cur.top();
381 bool implicitSelection =
382 font.language() == ignore_language
383 && font.fontInfo().number() == FONT_IGNORE
384 && selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
387 setFont(cur, font, toggleall);
389 // Implicit selections are cleared afterwards
390 // and cursor is set to the original position.
391 if (implicitSelection) {
392 cur.clearSelection();
393 cur.top() = resetCursor;
399 docstring Text::getStringToIndex(Cursor const & cur)
401 LASSERT(this == cur.text(), /**/);
404 return cur.selectionAsString(false);
406 // Try implicit word selection. If there is a change
407 // in the language the implicit word selection is
410 selectWord(tmpcur, PREVIOUS_WORD);
412 if (!tmpcur.selection())
413 cur.message(_("Nothing to index!"));
414 else if (tmpcur.selBegin().pit() != tmpcur.selEnd().pit())
415 cur.message(_("Cannot index more than one paragraph!"));
417 return tmpcur.selectionAsString(false);
423 void Text::setParagraphs(Cursor & cur, docstring arg, bool merge)
425 LASSERT(cur.text(), /**/);
426 // make sure that the depth behind the selection are restored, too
427 pit_type undopit = undoSpan(cur.selEnd().pit());
428 recUndo(cur, cur.selBegin().pit(), undopit - 1);
431 string const argument = to_utf8(arg);
432 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
434 Paragraph & par = pars_[pit];
435 ParagraphParameters params = par.params();
436 params.read(argument, merge);
437 par.params().apply(params, par.layout());
442 //FIXME This is a little redundant now, but it's probably worth keeping,
443 //especially if we're going to go away from using serialization internally
445 void Text::setParagraphs(Cursor & cur, ParagraphParameters const & p)
447 LASSERT(cur.text(), /**/);
448 // make sure that the depth behind the selection are restored, too
449 pit_type undopit = undoSpan(cur.selEnd().pit());
450 recUndo(cur, cur.selBegin().pit(), undopit - 1);
452 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
454 Paragraph & par = pars_[pit];
455 par.params().apply(p, par.layout());
460 // this really should just insert the inset and not move the cursor.
461 void Text::insertInset(Cursor & cur, Inset * inset)
463 LASSERT(this == cur.text(), /**/);
464 LASSERT(inset, /**/);
465 cur.paragraph().insertInset(cur.pos(), inset, cur.current_font,
466 Change(cur.buffer().params().trackChanges
467 ? Change::INSERTED : Change::UNCHANGED));
471 // needed to insert the selection
472 void Text::insertStringAsLines(Cursor & cur, docstring const & str)
474 cur.buffer().insertStringAsLines(pars_, cur.pit(), cur.pos(),
475 cur.current_font, str, autoBreakRows_);
479 // turn double CR to single CR, others are converted into one
480 // blank. Then insertStringAsLines is called
481 void Text::insertStringAsParagraphs(Cursor & cur, docstring const & str)
483 docstring linestr = str;
484 bool newline_inserted = false;
486 for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
487 if (linestr[i] == '\n') {
488 if (newline_inserted) {
489 // we know that \r will be ignored by
490 // insertStringAsLines. Of course, it is a dirty
491 // trick, but it works...
492 linestr[i - 1] = '\r';
496 newline_inserted = true;
498 } else if (isPrintable(linestr[i])) {
499 newline_inserted = false;
502 insertStringAsLines(cur, linestr);
506 bool Text::setCursor(Cursor & cur, pit_type par, pos_type pos,
507 bool setfont, bool boundary)
509 TextMetrics const & tm = cur.bv().textMetrics(this);
510 bool const update_needed = !tm.contains(par);
512 setCursorIntern(cur, par, pos, setfont, boundary);
513 return cur.bv().checkDepm(cur, old) || update_needed;
517 void Text::setCursor(CursorSlice & cur, pit_type par, pos_type pos)
519 LASSERT(par != int(paragraphs().size()), /**/);
523 // now some strict checking
524 Paragraph & para = getPar(par);
526 // None of these should happen, but we're scaredy-cats
528 lyxerr << "dont like -1" << endl;
529 LASSERT(false, /**/);
532 if (pos > para.size()) {
533 lyxerr << "dont like 1, pos: " << pos
534 << " size: " << para.size()
535 << " par: " << par << endl;
536 LASSERT(false, /**/);
541 void Text::setCursorIntern(Cursor & cur,
542 pit_type par, pos_type pos, bool setfont, bool boundary)
544 LASSERT(this == cur.text(), /**/);
545 cur.boundary(boundary);
546 setCursor(cur.top(), par, pos);
548 cur.setCurrentFont();
552 bool Text::checkAndActivateInset(Cursor & cur, bool front)
556 if (front && cur.pos() == cur.lastpos())
558 if (!front && cur.pos() == 0)
560 Inset * inset = front ? cur.nextInset() : cur.prevInset();
561 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
564 * Apparently, when entering an inset we are expected to be positioned
565 * *before* it in the containing paragraph, regardless of the direction
566 * from which we are entering. Otherwise, cursor placement goes awry,
567 * and when we exit from the beginning, we'll be placed *after* the
572 inset->edit(cur, front);
577 bool Text::checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool movingLeft)
583 if (cur.pos() == cur.lastpos())
585 Paragraph & par = cur.paragraph();
586 Inset * inset = par.isInset(cur.pos()) ? par.getInset(cur.pos()) : 0;
587 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
589 inset->edit(cur, movingForward,
590 movingLeft ? Inset::ENTRY_DIRECTION_RIGHT : Inset::ENTRY_DIRECTION_LEFT);
595 bool Text::cursorBackward(Cursor & cur)
597 // Tell BufferView to test for FitCursor in any case!
598 cur.updateFlags(Update::FitCursor);
600 // not at paragraph start?
602 // if on right side of boundary (i.e. not at paragraph end, but line end)
603 // -> skip it, i.e. set boundary to true, i.e. go only logically left
604 // there are some exceptions to ignore this: lineseps, newlines, spaces
606 // some effectless debug code to see the values in the debugger
607 bool bound = cur.boundary();
608 int rowpos = cur.textRow().pos();
610 bool sep = cur.paragraph().isSeparator(cur.pos() - 1);
611 bool newline = cur.paragraph().isNewline(cur.pos() - 1);
612 bool linesep = cur.paragraph().isLineSeparator(cur.pos() - 1);
614 if (!cur.boundary() &&
615 cur.textRow().pos() == cur.pos() &&
616 !cur.paragraph().isLineSeparator(cur.pos() - 1) &&
617 !cur.paragraph().isNewline(cur.pos() - 1) &&
618 !cur.paragraph().isSeparator(cur.pos() - 1)) {
619 return setCursor(cur, cur.pit(), cur.pos(), true, true);
622 // go left and try to enter inset
623 if (checkAndActivateInset(cur, false))
626 // normal character left
627 return setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
630 // move to the previous paragraph or do nothing
632 return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size(), true, false);
637 bool Text::cursorVisLeft(Cursor & cur, bool skip_inset)
639 Cursor temp_cur = cur;
640 temp_cur.posVisLeft(skip_inset);
641 if (temp_cur.depth() > cur.depth()) {
645 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
646 true, temp_cur.boundary());
650 bool Text::cursorVisRight(Cursor & cur, bool skip_inset)
652 Cursor temp_cur = cur;
653 temp_cur.posVisRight(skip_inset);
654 if (temp_cur.depth() > cur.depth()) {
658 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
659 true, temp_cur.boundary());
663 bool Text::cursorForward(Cursor & cur)
665 // Tell BufferView to test for FitCursor in any case!
666 cur.updateFlags(Update::FitCursor);
668 // not at paragraph end?
669 if (cur.pos() != cur.lastpos()) {
670 // in front of editable inset, i.e. jump into it?
671 if (checkAndActivateInset(cur, true))
674 TextMetrics const & tm = cur.bv().textMetrics(this);
675 // if left of boundary -> just jump to right side
676 // but for RTL boundaries don't, because: abc|DDEEFFghi -> abcDDEEF|Fghi
677 if (cur.boundary() && !tm.isRTLBoundary(cur.pit(), cur.pos()))
678 return setCursor(cur, cur.pit(), cur.pos(), true, false);
680 // next position is left of boundary,
681 // but go to next line for special cases like space, newline, linesep
683 // some effectless debug code to see the values in the debugger
684 int endpos = cur.textRow().endpos();
685 int lastpos = cur.lastpos();
687 bool linesep = cur.paragraph().isLineSeparator(cur.pos());
688 bool newline = cur.paragraph().isNewline(cur.pos());
689 bool sep = cur.paragraph().isSeparator(cur.pos());
690 if (cur.pos() != cur.lastpos()) {
691 bool linesep2 = cur.paragraph().isLineSeparator(cur.pos()+1);
692 bool newline2 = cur.paragraph().isNewline(cur.pos()+1);
693 bool sep2 = cur.paragraph().isSeparator(cur.pos()+1);
696 if (cur.textRow().endpos() == cur.pos() + 1 &&
697 cur.textRow().endpos() != cur.lastpos() &&
698 !cur.paragraph().isNewline(cur.pos()) &&
699 !cur.paragraph().isLineSeparator(cur.pos()) &&
700 !cur.paragraph().isSeparator(cur.pos())) {
701 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
704 // in front of RTL boundary? Stay on this side of the boundary because:
705 // ab|cDDEEFFghi -> abc|DDEEFFghi
706 if (tm.isRTLBoundary(cur.pit(), cur.pos() + 1))
707 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
710 return setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
713 // move to next paragraph
714 if (cur.pit() != cur.lastpit())
715 return setCursor(cur, cur.pit() + 1, 0, true, false);
720 bool Text::cursorUpParagraph(Cursor & cur)
722 bool updated = false;
724 updated = setCursor(cur, cur.pit(), 0);
725 else if (cur.pit() != 0)
726 updated = setCursor(cur, cur.pit() - 1, 0);
731 bool Text::cursorDownParagraph(Cursor & cur)
733 bool updated = false;
734 if (cur.pit() != cur.lastpit())
735 updated = setCursor(cur, cur.pit() + 1, 0);
737 updated = setCursor(cur, cur.pit(), cur.lastpos());
742 // fix the cursor `cur' after a characters has been deleted at `where'
743 // position. Called by deleteEmptyParagraphMechanism
744 void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
746 // Do nothing if cursor is not in the paragraph where the
748 if (cur.pit() != where.pit())
751 // If cursor position is after the deletion place update it
752 if (cur.pos() > where.pos())
755 // Check also if we don't want to set the cursor on a spot behind the
756 // pagragraph because we erased the last character.
757 if (cur.pos() > cur.lastpos())
758 cur.pos() = cur.lastpos();
762 bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
763 Cursor & old, bool & need_anchor_change)
765 //LYXERR(Debug::DEBUG, "DEPM: cur:\n" << cur << "old:\n" << old);
767 Paragraph & oldpar = old.paragraph();
769 // We allow all kinds of "mumbo-jumbo" when freespacing.
770 if (oldpar.isFreeSpacing())
773 /* Ok I'll put some comments here about what is missing.
774 There are still some small problems that can lead to
775 double spaces stored in the document file or space at
776 the beginning of paragraphs(). This happens if you have
777 the cursor between to spaces and then save. Or if you
778 cut and paste and the selection have a space at the
779 beginning and then save right after the paste. (Lgb)
782 // If old.pos() == 0 and old.pos()(1) == LineSeparator
783 // delete the LineSeparator.
786 // If old.pos() == 1 and old.pos()(0) == LineSeparator
787 // delete the LineSeparator.
790 bool const same_inset = &old.inset() == &cur.inset();
791 bool const same_par = same_inset && old.pit() == cur.pit();
792 bool const same_par_pos = same_par && old.pos() == cur.pos();
794 // If the chars around the old cursor were spaces, delete one of them.
796 // Only if the cursor has really moved.
798 && old.pos() < oldpar.size()
799 && oldpar.isLineSeparator(old.pos())
800 && oldpar.isLineSeparator(old.pos() - 1)
801 && !oldpar.isDeleted(old.pos() - 1)
802 && !oldpar.isDeleted(old.pos())) {
803 oldpar.eraseChar(old.pos() - 1, cur.buffer().params().trackChanges);
804 // FIXME: This will not work anymore when we have multiple views of the same buffer
805 // In this case, we will have to correct also the cursors held by
806 // other bufferviews. It will probably be easier to do that in a more
807 // automated way in CursorSlice code. (JMarc 26/09/2001)
808 // correct all cursor parts
810 fixCursorAfterDelete(cur.top(), old.top());
811 need_anchor_change = true;
817 // only do our magic if we changed paragraph
821 // don't delete anything if this is the ONLY paragraph!
822 if (old.lastpit() == 0)
825 // Do not delete empty paragraphs with keepempty set.
826 if (oldpar.allowEmpty())
829 if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
831 old.recordUndo(ATOMIC_UNDO,
832 max(old.pit() - 1, pit_type(0)),
833 min(old.pit() + 1, old.lastpit()));
834 ParagraphList & plist = old.text()->paragraphs();
835 bool const soa = oldpar.params().startOfAppendix();
836 plist.erase(boost::next(plist.begin(), old.pit()));
837 // do not lose start of appendix marker (bug 4212)
838 if (soa && old.pit() < pit_type(plist.size()))
839 plist[old.pit()].params().startOfAppendix(true);
841 // see #warning (FIXME?) above
842 if (cur.depth() >= old.depth()) {
843 CursorSlice & curslice = cur[old.depth() - 1];
844 if (&curslice.inset() == &old.inset()
845 && curslice.pit() > old.pit()) {
847 // since a paragraph has been deleted, all the
848 // insets after `old' have been copied and
849 // their address has changed. Therefore we
850 // need to `regenerate' cur. (JMarc)
851 cur.updateInsets(&(cur.bottom().inset()));
852 need_anchor_change = true;
858 if (oldpar.stripLeadingSpaces(cur.buffer().params().trackChanges)) {
859 need_anchor_change = true;
860 // We return true here because the Paragraph contents changed and
861 // we need a redraw before further action is processed.
869 void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges)
871 LASSERT(first >= 0 && first <= last && last < (int) pars_.size(), /**/);
873 for (pit_type pit = first; pit <= last; ++pit) {
874 Paragraph & par = pars_[pit];
876 // We allow all kinds of "mumbo-jumbo" when freespacing.
877 if (par.isFreeSpacing())
880 for (pos_type pos = 1; pos < par.size(); ++pos) {
881 if (par.isLineSeparator(pos) && par.isLineSeparator(pos - 1)
882 && !par.isDeleted(pos - 1)) {
883 if (par.eraseChar(pos - 1, trackChanges)) {
889 // don't delete anything if this is the only remaining paragraph within the given range
890 // note: Text::acceptOrRejectChanges() sets the cursor to 'first' after calling DEPM
894 // don't delete empty paragraphs with keepempty set
895 if (par.allowEmpty())
898 if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) {
899 pars_.erase(boost::next(pars_.begin(), pit));
905 par.stripLeadingSpaces(trackChanges);
910 void Text::recUndo(Cursor & cur, pit_type first, pit_type last) const
912 cur.recordUndo(ATOMIC_UNDO, first, last);
916 void Text::recUndo(Cursor & cur, pit_type par) const
918 cur.recordUndo(ATOMIC_UNDO, par, par);