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 "insets/InsetCollapsable.h"
51 #include "mathed/InsetMathHull.h"
53 #include "support/lassert.h"
54 #include "support/debug.h"
55 #include "support/gettext.h"
56 #include "support/textutils.h"
58 #include <boost/next_prior.hpp>
67 : autoBreakRows_(false)
71 bool Text::isMainText(Buffer const & buffer) const
73 return &buffer.text() == this;
77 // Note that this is supposed to return a fully realized font.
78 FontInfo Text::layoutFont(Buffer const & buffer, pit_type const pit) const
80 Layout const & layout = pars_[pit].layout();
82 if (!pars_[pit].getDepth()) {
83 FontInfo lf = layout.resfont;
84 // In case the default family has been customized
85 if (layout.font.family() == INHERIT_FAMILY)
86 lf.setFamily(buffer.params().getFont().fontInfo().family());
88 // It ought to be possible here just to use Inset::getLayout() and skip
89 // the asInsetCollapsable() bit. Unfortunatley, that doesn't work right
90 // now, because Inset::getLayout() will return a default-constructed
91 // InsetLayout, and that e.g. sets the foreground color to red. So we
92 // need to do some work to make that possible.
93 InsetCollapsable const * icp = pars_[pit].inInset().asInsetCollapsable();
96 FontInfo icf = icp->getLayout().font();
101 FontInfo font = layout.font;
102 // Realize with the fonts of lesser depth.
103 //font.realize(outerFont(pit, paragraphs()));
104 font.realize(buffer.params().getFont().fontInfo());
110 // Note that this is supposed to return a fully realized font.
111 FontInfo Text::labelFont(Buffer const & buffer, Paragraph const & par) const
113 Layout const & layout = par.layout();
115 if (!par.getDepth()) {
116 FontInfo lf = layout.reslabelfont;
117 // In case the default family has been customized
118 if (layout.labelfont.family() == INHERIT_FAMILY)
119 lf.setFamily(buffer.params().getFont().fontInfo().family());
120 // FIXME As above....
121 InsetCollapsable const * icp = par.inInset().asInsetCollapsable();
124 FontInfo icf = icp->getLayout().labelfont();
129 FontInfo font = layout.labelfont;
130 // Realize with the fonts of lesser depth.
131 font.realize(buffer.params().getFont().fontInfo());
137 void Text::setCharFont(Buffer const & buffer, pit_type pit,
138 pos_type pos, Font const & fnt, Font const & display_font)
141 Layout const & layout = pars_[pit].layout();
143 // Get concrete layout font to reduce against
146 if (pos < pars_[pit].beginOfBody())
147 layoutfont = layout.labelfont;
149 layoutfont = layout.font;
151 // Realize against environment font information
152 if (pars_[pit].getDepth()) {
154 while (!layoutfont.resolved() &&
155 tp != pit_type(paragraphs().size()) &&
156 pars_[tp].getDepth()) {
157 tp = outerHook(tp, paragraphs());
158 if (tp != pit_type(paragraphs().size()))
159 layoutfont.realize(pars_[tp].layout().font);
163 // Inside inset, apply the inset's font attributes if any
165 if (!isMainText(buffer))
166 layoutfont.realize(display_font.fontInfo());
168 layoutfont.realize(buffer.params().getFont().fontInfo());
170 // Now, reduce font against full layout font
171 font.fontInfo().reduce(layoutfont);
173 pars_[pit].setFont(pos, font);
177 void Text::setInsetFont(BufferView const & bv, pit_type pit,
178 pos_type pos, Font const & font, bool toggleall)
180 Inset * const inset = pars_[pit].getInset(pos);
181 LASSERT(inset && inset->noFontChange(), /**/);
183 CursorSlice::idx_type endidx = inset->nargs();
184 for (CursorSlice cs(*inset); cs.idx() != endidx; ++cs.idx()) {
185 Text * text = cs.text();
187 // last position of the cell
188 CursorSlice cellend = cs;
189 cellend.pit() = cellend.lastpit();
190 cellend.pos() = cellend.lastpos();
191 text->setFont(bv, cs, cellend, font, toggleall);
197 // return past-the-last paragraph influenced by a layout change on pit
198 pit_type Text::undoSpan(pit_type pit)
200 pit_type const end = paragraphs().size();
201 pit_type nextpit = pit + 1;
204 //because of parindents
205 if (!pars_[pit].getDepth())
206 return boost::next(nextpit);
207 //because of depth constrains
208 for (; nextpit != end; ++pit, ++nextpit) {
209 if (!pars_[pit].getDepth())
216 void Text::setLayout(Buffer const & buffer, pit_type start, pit_type end,
217 docstring const & layout)
219 LASSERT(start != end, /**/);
221 BufferParams const & bufparams = buffer.params();
222 Layout const & lyxlayout = bufparams.documentClass()[layout];
224 for (pit_type pit = start; pit != end; ++pit) {
225 Paragraph & par = pars_[pit];
226 par.applyLayout(lyxlayout);
227 if (lyxlayout.margintype == MARGIN_MANUAL)
228 par.setLabelWidthString(par.translateIfPossible(
229 lyxlayout.labelstring(), buffer.params()));
234 // set layout over selection and make a total rebreak of those paragraphs
235 void Text::setLayout(Cursor & cur, docstring const & layout)
237 LASSERT(this == cur.text(), /**/);
239 pit_type start = cur.selBegin().pit();
240 pit_type end = cur.selEnd().pit() + 1;
241 pit_type undopit = undoSpan(end - 1);
242 recUndo(cur, start, undopit - 1);
243 setLayout(cur.buffer(), start, end, layout);
244 updateLabels(cur.buffer());
248 static bool changeDepthAllowed(Text::DEPTH_CHANGE type,
249 Paragraph const & par, int max_depth)
251 if (par.layout().labeltype == LABEL_BIBLIO)
253 int const depth = par.params().depth();
254 if (type == Text::INC_DEPTH && depth < max_depth)
256 if (type == Text::DEC_DEPTH && depth > 0)
262 bool Text::changeDepthAllowed(Cursor & cur, DEPTH_CHANGE type) const
264 LASSERT(this == cur.text(), /**/);
265 // this happens when selecting several cells in tabular (bug 2630)
266 if (cur.selBegin().idx() != cur.selEnd().idx())
269 pit_type const beg = cur.selBegin().pit();
270 pit_type const end = cur.selEnd().pit() + 1;
271 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
273 for (pit_type pit = beg; pit != end; ++pit) {
274 if (lyx::changeDepthAllowed(type, pars_[pit], max_depth))
276 max_depth = pars_[pit].getMaxDepthAfter();
282 void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type)
284 LASSERT(this == cur.text(), /**/);
285 pit_type const beg = cur.selBegin().pit();
286 pit_type const end = cur.selEnd().pit() + 1;
287 cur.recordUndoSelection();
288 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
290 for (pit_type pit = beg; pit != end; ++pit) {
291 Paragraph & par = pars_[pit];
292 if (lyx::changeDepthAllowed(type, par, max_depth)) {
293 int const depth = par.params().depth();
294 if (type == INC_DEPTH)
295 par.params().depth(depth + 1);
297 par.params().depth(depth - 1);
299 max_depth = par.getMaxDepthAfter();
301 // this handles the counter labels, and also fixes up
302 // depth values for follow-on (child) paragraphs
303 updateLabels(cur.buffer());
307 void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
309 LASSERT(this == cur.text(), /**/);
310 // Set the current_font
311 // Determine basis font
313 pit_type pit = cur.pit();
314 if (cur.pos() < pars_[pit].beginOfBody())
315 layoutfont = labelFont(cur.buffer(), pars_[pit]);
317 layoutfont = layoutFont(cur.buffer(), pit);
319 // Update current font
320 cur.real_current_font.update(font,
321 cur.buffer().params().language,
324 // Reduce to implicit settings
325 cur.current_font = cur.real_current_font;
326 cur.current_font.fontInfo().reduce(layoutfont);
327 // And resolve it completely
328 cur.real_current_font.fontInfo().realize(layoutfont);
330 // if there is no selection that's all we need to do
331 if (!cur.selection())
334 // Ok, we have a selection.
335 cur.recordUndoSelection();
337 setFont(cur.bv(), cur.selectionBegin().top(),
338 cur.selectionEnd().top(), font, toggleall);
342 void Text::setFont(BufferView const & bv, CursorSlice const & begin,
343 CursorSlice const & end, Font const & font,
346 Buffer const & buffer = bv.buffer();
348 // Don't use forwardChar here as ditend might have
349 // pos() == lastpos() and forwardChar would miss it.
350 // Can't use forwardPos either as this descends into
352 Language const * language = buffer.params().language;
353 for (CursorSlice dit = begin; dit != end; dit.forwardPos()) {
354 if (dit.pos() == dit.lastpos())
356 pit_type const pit = dit.pit();
357 pos_type const pos = dit.pos();
358 Inset * inset = pars_[pit].getInset(pos);
359 if (inset && inset->noFontChange()) {
360 // We need to propagate the font change to all
361 // text cells of the inset (bug 1973).
362 // FIXME: This should change, see documentation
363 // of noFontChange in Inset.h
364 setInsetFont(bv, pit, pos, font, toggleall);
366 TextMetrics const & tm = bv.textMetrics(this);
367 Font f = tm.displayFont(pit, pos);
368 f.update(font, language, toggleall);
369 setCharFont(buffer, pit, pos, f, tm.font_);
374 bool Text::cursorTop(Cursor & cur)
376 LASSERT(this == cur.text(), /**/);
377 return setCursor(cur, 0, 0);
381 bool Text::cursorBottom(Cursor & cur)
383 LASSERT(this == cur.text(), /**/);
384 return setCursor(cur, cur.lastpit(), boost::prior(paragraphs().end())->size());
388 void Text::toggleFree(Cursor & cur, Font const & font, bool toggleall)
390 LASSERT(this == cur.text(), /**/);
391 // If the mask is completely neutral, tell user
392 if (font.fontInfo() == ignore_font && font.language() == ignore_language) {
393 // Could only happen with user style
394 cur.message(_("No font change defined."));
398 // Try implicit word selection
399 // If there is a change in the language the implicit word selection
401 CursorSlice resetCursor = cur.top();
402 bool implicitSelection =
403 font.language() == ignore_language
404 && font.fontInfo().number() == FONT_IGNORE
405 && selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
408 setFont(cur, font, toggleall);
410 // Implicit selections are cleared afterwards
411 // and cursor is set to the original position.
412 if (implicitSelection) {
413 cur.clearSelection();
414 cur.top() = resetCursor;
420 docstring Text::getStringToIndex(Cursor const & cur)
422 LASSERT(this == cur.text(), /**/);
425 return cur.selectionAsString(false);
427 // Try implicit word selection. If there is a change
428 // in the language the implicit word selection is
431 selectWord(tmpcur, PREVIOUS_WORD);
433 if (!tmpcur.selection())
434 cur.message(_("Nothing to index!"));
435 else if (tmpcur.selBegin().pit() != tmpcur.selEnd().pit())
436 cur.message(_("Cannot index more than one paragraph!"));
438 return tmpcur.selectionAsString(false);
444 void Text::setParagraphs(Cursor & cur, docstring arg, bool merge)
446 LASSERT(cur.text(), /**/);
447 // make sure that the depth behind the selection are restored, too
448 pit_type undopit = undoSpan(cur.selEnd().pit());
449 recUndo(cur, cur.selBegin().pit(), undopit - 1);
452 string const argument = to_utf8(arg);
453 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
455 Paragraph & par = pars_[pit];
456 ParagraphParameters params = par.params();
457 params.read(argument, merge);
458 par.params().apply(params, par.layout());
463 //FIXME This is a little redundant now, but it's probably worth keeping,
464 //especially if we're going to go away from using serialization internally
466 void Text::setParagraphs(Cursor & cur, ParagraphParameters const & p)
468 LASSERT(cur.text(), /**/);
469 // make sure that the depth behind the selection are restored, too
470 pit_type undopit = undoSpan(cur.selEnd().pit());
471 recUndo(cur, cur.selBegin().pit(), undopit - 1);
473 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
475 Paragraph & par = pars_[pit];
476 par.params().apply(p, par.layout());
481 // this really should just insert the inset and not move the cursor.
482 void Text::insertInset(Cursor & cur, Inset * inset)
484 LASSERT(this == cur.text(), /**/);
485 LASSERT(inset, /**/);
486 cur.paragraph().insertInset(cur.pos(), inset, cur.current_font,
487 Change(cur.buffer().params().trackChanges
488 ? Change::INSERTED : Change::UNCHANGED));
492 // needed to insert the selection
493 void Text::insertStringAsLines(Cursor & cur, docstring const & str)
495 cur.buffer().insertStringAsLines(pars_, cur.pit(), cur.pos(),
496 cur.current_font, str, autoBreakRows_);
500 // turn double CR to single CR, others are converted into one
501 // blank. Then insertStringAsLines is called
502 void Text::insertStringAsParagraphs(Cursor & cur, docstring const & str)
504 docstring linestr = str;
505 bool newline_inserted = false;
507 for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
508 if (linestr[i] == '\n') {
509 if (newline_inserted) {
510 // we know that \r will be ignored by
511 // insertStringAsLines. Of course, it is a dirty
512 // trick, but it works...
513 linestr[i - 1] = '\r';
517 newline_inserted = true;
519 } else if (isPrintable(linestr[i])) {
520 newline_inserted = false;
523 insertStringAsLines(cur, linestr);
527 bool Text::setCursor(Cursor & cur, pit_type par, pos_type pos,
528 bool setfont, bool boundary)
530 TextMetrics const & tm = cur.bv().textMetrics(this);
531 bool const update_needed = !tm.contains(par);
533 setCursorIntern(cur, par, pos, setfont, boundary);
534 return cur.bv().checkDepm(cur, old) || update_needed;
538 void Text::setCursor(CursorSlice & cur, pit_type par, pos_type pos)
540 LASSERT(par != int(paragraphs().size()), /**/);
544 // now some strict checking
545 Paragraph & para = getPar(par);
547 // None of these should happen, but we're scaredy-cats
549 lyxerr << "dont like -1" << endl;
550 LASSERT(false, /**/);
553 if (pos > para.size()) {
554 lyxerr << "dont like 1, pos: " << pos
555 << " size: " << para.size()
556 << " par: " << par << endl;
557 LASSERT(false, /**/);
562 void Text::setCursorIntern(Cursor & cur,
563 pit_type par, pos_type pos, bool setfont, bool boundary)
565 LASSERT(this == cur.text(), /**/);
566 cur.boundary(boundary);
567 setCursor(cur.top(), par, pos);
569 cur.setCurrentFont();
573 bool Text::checkAndActivateInset(Cursor & cur, bool front)
577 if (front && cur.pos() == cur.lastpos())
579 if (!front && cur.pos() == 0)
581 Inset * inset = front ? cur.nextInset() : cur.prevInset();
582 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
585 * Apparently, when entering an inset we are expected to be positioned
586 * *before* it in the containing paragraph, regardless of the direction
587 * from which we are entering. Otherwise, cursor placement goes awry,
588 * and when we exit from the beginning, we'll be placed *after* the
593 inset->edit(cur, front);
598 bool Text::checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool movingLeft)
604 if (cur.pos() == cur.lastpos())
606 Paragraph & par = cur.paragraph();
607 Inset * inset = par.isInset(cur.pos()) ? par.getInset(cur.pos()) : 0;
608 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
610 inset->edit(cur, movingForward,
611 movingLeft ? Inset::ENTRY_DIRECTION_RIGHT : Inset::ENTRY_DIRECTION_LEFT);
616 bool Text::cursorBackward(Cursor & cur)
618 // Tell BufferView to test for FitCursor in any case!
619 cur.updateFlags(Update::FitCursor);
621 // not at paragraph start?
623 // if on right side of boundary (i.e. not at paragraph end, but line end)
624 // -> skip it, i.e. set boundary to true, i.e. go only logically left
625 // there are some exceptions to ignore this: lineseps, newlines, spaces
627 // some effectless debug code to see the values in the debugger
628 bool bound = cur.boundary();
629 int rowpos = cur.textRow().pos();
631 bool sep = cur.paragraph().isSeparator(cur.pos() - 1);
632 bool newline = cur.paragraph().isNewline(cur.pos() - 1);
633 bool linesep = cur.paragraph().isLineSeparator(cur.pos() - 1);
635 if (!cur.boundary() &&
636 cur.textRow().pos() == cur.pos() &&
637 !cur.paragraph().isLineSeparator(cur.pos() - 1) &&
638 !cur.paragraph().isNewline(cur.pos() - 1) &&
639 !cur.paragraph().isSeparator(cur.pos() - 1)) {
640 return setCursor(cur, cur.pit(), cur.pos(), true, true);
643 // go left and try to enter inset
644 if (checkAndActivateInset(cur, false))
647 // normal character left
648 return setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
651 // move to the previous paragraph or do nothing
653 return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size(), true, false);
658 bool Text::cursorVisLeft(Cursor & cur, bool skip_inset)
660 Cursor temp_cur = cur;
661 temp_cur.posVisLeft(skip_inset);
662 if (temp_cur.depth() > cur.depth()) {
666 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
667 true, temp_cur.boundary());
671 bool Text::cursorVisRight(Cursor & cur, bool skip_inset)
673 Cursor temp_cur = cur;
674 temp_cur.posVisRight(skip_inset);
675 if (temp_cur.depth() > cur.depth()) {
679 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
680 true, temp_cur.boundary());
684 bool Text::cursorForward(Cursor & cur)
686 // Tell BufferView to test for FitCursor in any case!
687 cur.updateFlags(Update::FitCursor);
689 // not at paragraph end?
690 if (cur.pos() != cur.lastpos()) {
691 // in front of editable inset, i.e. jump into it?
692 if (checkAndActivateInset(cur, true))
695 TextMetrics const & tm = cur.bv().textMetrics(this);
696 // if left of boundary -> just jump to right side
697 // but for RTL boundaries don't, because: abc|DDEEFFghi -> abcDDEEF|Fghi
698 if (cur.boundary() && !tm.isRTLBoundary(cur.pit(), cur.pos()))
699 return setCursor(cur, cur.pit(), cur.pos(), true, false);
701 // next position is left of boundary,
702 // but go to next line for special cases like space, newline, linesep
704 // some effectless debug code to see the values in the debugger
705 int endpos = cur.textRow().endpos();
706 int lastpos = cur.lastpos();
708 bool linesep = cur.paragraph().isLineSeparator(cur.pos());
709 bool newline = cur.paragraph().isNewline(cur.pos());
710 bool sep = cur.paragraph().isSeparator(cur.pos());
711 if (cur.pos() != cur.lastpos()) {
712 bool linesep2 = cur.paragraph().isLineSeparator(cur.pos()+1);
713 bool newline2 = cur.paragraph().isNewline(cur.pos()+1);
714 bool sep2 = cur.paragraph().isSeparator(cur.pos()+1);
717 if (cur.textRow().endpos() == cur.pos() + 1 &&
718 cur.textRow().endpos() != cur.lastpos() &&
719 !cur.paragraph().isNewline(cur.pos()) &&
720 !cur.paragraph().isLineSeparator(cur.pos()) &&
721 !cur.paragraph().isSeparator(cur.pos())) {
722 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
725 // in front of RTL boundary? Stay on this side of the boundary because:
726 // ab|cDDEEFFghi -> abc|DDEEFFghi
727 if (tm.isRTLBoundary(cur.pit(), cur.pos() + 1))
728 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
731 return setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
734 // move to next paragraph
735 if (cur.pit() != cur.lastpit())
736 return setCursor(cur, cur.pit() + 1, 0, true, false);
741 bool Text::cursorUpParagraph(Cursor & cur)
743 bool updated = false;
745 updated = setCursor(cur, cur.pit(), 0);
746 else if (cur.pit() != 0)
747 updated = setCursor(cur, cur.pit() - 1, 0);
752 bool Text::cursorDownParagraph(Cursor & cur)
754 bool updated = false;
755 if (cur.pit() != cur.lastpit())
756 updated = setCursor(cur, cur.pit() + 1, 0);
758 updated = setCursor(cur, cur.pit(), cur.lastpos());
763 // fix the cursor `cur' after a characters has been deleted at `where'
764 // position. Called by deleteEmptyParagraphMechanism
765 void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
767 // Do nothing if cursor is not in the paragraph where the
769 if (cur.pit() != where.pit())
772 // If cursor position is after the deletion place update it
773 if (cur.pos() > where.pos())
776 // Check also if we don't want to set the cursor on a spot behind the
777 // pagragraph because we erased the last character.
778 if (cur.pos() > cur.lastpos())
779 cur.pos() = cur.lastpos();
783 bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
784 Cursor & old, bool & need_anchor_change)
786 //LYXERR(Debug::DEBUG, "DEPM: cur:\n" << cur << "old:\n" << old);
788 Paragraph & oldpar = old.paragraph();
790 // We allow all kinds of "mumbo-jumbo" when freespacing.
791 if (oldpar.isFreeSpacing())
794 /* Ok I'll put some comments here about what is missing.
795 There are still some small problems that can lead to
796 double spaces stored in the document file or space at
797 the beginning of paragraphs(). This happens if you have
798 the cursor between to spaces and then save. Or if you
799 cut and paste and the selection have a space at the
800 beginning and then save right after the paste. (Lgb)
803 // If old.pos() == 0 and old.pos()(1) == LineSeparator
804 // delete the LineSeparator.
807 // If old.pos() == 1 and old.pos()(0) == LineSeparator
808 // delete the LineSeparator.
811 bool const same_inset = &old.inset() == &cur.inset();
812 bool const same_par = same_inset && old.pit() == cur.pit();
813 bool const same_par_pos = same_par && old.pos() == cur.pos();
815 // If the chars around the old cursor were spaces, delete one of them.
817 // Only if the cursor has really moved.
819 && old.pos() < oldpar.size()
820 && oldpar.isLineSeparator(old.pos())
821 && oldpar.isLineSeparator(old.pos() - 1)
822 && !oldpar.isDeleted(old.pos() - 1)
823 && !oldpar.isDeleted(old.pos())) {
824 oldpar.eraseChar(old.pos() - 1, cur.buffer().params().trackChanges);
825 // FIXME: This will not work anymore when we have multiple views of the same buffer
826 // In this case, we will have to correct also the cursors held by
827 // other bufferviews. It will probably be easier to do that in a more
828 // automated way in CursorSlice code. (JMarc 26/09/2001)
829 // correct all cursor parts
831 fixCursorAfterDelete(cur.top(), old.top());
832 need_anchor_change = true;
838 // only do our magic if we changed paragraph
842 // don't delete anything if this is the ONLY paragraph!
843 if (old.lastpit() == 0)
846 // Do not delete empty paragraphs with keepempty set.
847 if (oldpar.allowEmpty())
850 if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
852 old.recordUndo(ATOMIC_UNDO,
853 max(old.pit() - 1, pit_type(0)),
854 min(old.pit() + 1, old.lastpit()));
855 ParagraphList & plist = old.text()->paragraphs();
856 bool const soa = oldpar.params().startOfAppendix();
857 plist.erase(boost::next(plist.begin(), old.pit()));
858 // do not lose start of appendix marker (bug 4212)
859 if (soa && old.pit() < pit_type(plist.size()))
860 plist[old.pit()].params().startOfAppendix(true);
862 // see #warning (FIXME?) above
863 if (cur.depth() >= old.depth()) {
864 CursorSlice & curslice = cur[old.depth() - 1];
865 if (&curslice.inset() == &old.inset()
866 && curslice.pit() > old.pit()) {
868 // since a paragraph has been deleted, all the
869 // insets after `old' have been copied and
870 // their address has changed. Therefore we
871 // need to `regenerate' cur. (JMarc)
872 cur.updateInsets(&(cur.bottom().inset()));
873 need_anchor_change = true;
879 if (oldpar.stripLeadingSpaces(cur.buffer().params().trackChanges)) {
880 need_anchor_change = true;
881 // We return true here because the Paragraph contents changed and
882 // we need a redraw before further action is processed.
890 void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges)
892 LASSERT(first >= 0 && first <= last && last < (int) pars_.size(), /**/);
894 for (pit_type pit = first; pit <= last; ++pit) {
895 Paragraph & par = pars_[pit];
897 // We allow all kinds of "mumbo-jumbo" when freespacing.
898 if (par.isFreeSpacing())
901 for (pos_type pos = 1; pos < par.size(); ++pos) {
902 if (par.isLineSeparator(pos) && par.isLineSeparator(pos - 1)
903 && !par.isDeleted(pos - 1)) {
904 if (par.eraseChar(pos - 1, trackChanges)) {
910 // don't delete anything if this is the only remaining paragraph within the given range
911 // note: Text::acceptOrRejectChanges() sets the cursor to 'first' after calling DEPM
915 // don't delete empty paragraphs with keepempty set
916 if (par.allowEmpty())
919 if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) {
920 pars_.erase(boost::next(pars_.begin(), pit));
926 par.stripLeadingSpaces(trackChanges);
931 void Text::recUndo(Cursor & cur, pit_type first, pit_type last) const
933 cur.recordUndo(ATOMIC_UNDO, first, last);
937 void Text::recUndo(Cursor & cur, pit_type par) const
939 cur.recordUndo(ATOMIC_UNDO, par, par);