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"
41 #include "Paragraph.h"
42 #include "ParagraphParameters.h"
43 #include "TextClass.h"
44 #include "TextMetrics.h"
46 #include "insets/InsetCollapsable.h"
48 #include "mathed/InsetMathHull.h"
50 #include "support/lassert.h"
51 #include "support/debug.h"
52 #include "support/gettext.h"
53 #include "support/textutils.h"
55 #include <boost/next_prior.hpp>
63 bool Text::isMainText() const
65 return &owner_->buffer().text() == this;
69 // Note that this is supposed to return a fully realized font.
70 FontInfo Text::layoutFont(pit_type const pit) const
72 Layout const & layout = pars_[pit].layout();
74 if (!pars_[pit].getDepth()) {
75 FontInfo lf = layout.resfont;
76 // In case the default family has been customized
77 if (layout.font.family() == INHERIT_FAMILY)
78 lf.setFamily(owner_->buffer().params().getFont().fontInfo().family());
80 // It ought to be possible here just to use Inset::getLayout() and skip
81 // the asInsetCollapsable() bit. Unfortunatley, that doesn't work right
82 // now, because Inset::getLayout() will return a default-constructed
83 // InsetLayout, and that e.g. sets the foreground color to red. So we
84 // need to do some work to make that possible.
85 InsetCollapsable const * icp = owner_->asInsetCollapsable();
88 FontInfo icf = icp->getLayout().font();
93 FontInfo font = layout.font;
94 // Realize with the fonts of lesser depth.
95 //font.realize(outerFont(pit));
96 font.realize(owner_->buffer().params().getFont().fontInfo());
102 // Note that this is supposed to return a fully realized font.
103 FontInfo Text::labelFont(Paragraph const & par) const
105 Buffer const & buffer = owner_->buffer();
106 Layout const & layout = par.layout();
108 if (!par.getDepth()) {
109 FontInfo lf = layout.reslabelfont;
110 // In case the default family has been customized
111 if (layout.labelfont.family() == INHERIT_FAMILY)
112 lf.setFamily(buffer.params().getFont().fontInfo().family());
116 FontInfo font = layout.labelfont;
117 // Realize with the fonts of lesser depth.
118 font.realize(buffer.params().getFont().fontInfo());
124 void Text::setCharFont(pit_type pit,
125 pos_type pos, Font const & fnt, Font const & display_font)
127 Buffer const & buffer = owner_->buffer();
129 Layout 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()) {
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
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 Inset * const inset = pars_[pit].getInset(pos);
169 LASSERT(inset && inset->resetFontEdit(), /**/);
171 CursorSlice::idx_type endidx = inset->nargs();
172 for (CursorSlice cs(*inset); cs.idx() != endidx; ++cs.idx()) {
173 Text * text = cs.text();
175 // last position of the cell
176 CursorSlice cellend = cs;
177 cellend.pit() = cellend.lastpit();
178 cellend.pos() = cellend.lastpos();
179 text->setFont(bv, cs, cellend, font, toggleall);
185 // return past-the-last paragraph influenced by a layout change on pit
186 pit_type Text::undoSpan(pit_type pit)
188 pit_type const end = paragraphs().size();
189 pit_type nextpit = pit + 1;
192 //because of parindents
193 if (!pars_[pit].getDepth())
194 return boost::next(nextpit);
195 //because of depth constrains
196 for (; nextpit != end; ++pit, ++nextpit) {
197 if (!pars_[pit].getDepth())
204 void Text::setLayout(pit_type start, pit_type end,
205 docstring const & layout)
207 LASSERT(start != end, /**/);
209 Buffer const & buffer = owner_->buffer();
210 BufferParams const & bp = buffer.params();
211 Layout const & lyxlayout = bp.documentClass()[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.expandLabel(lyxlayout, bp));
222 // set layout over selection and make a total rebreak of those paragraphs
223 void Text::setLayout(Cursor & cur, docstring const & layout)
225 LASSERT(this == cur.text(), /**/);
227 pit_type start = cur.selBegin().pit();
228 pit_type end = cur.selEnd().pit() + 1;
229 pit_type undopit = undoSpan(end - 1);
230 recUndo(cur, start, undopit - 1);
231 setLayout(start, end, layout);
232 cur.forceBufferUpdate();
236 static bool changeDepthAllowed(Text::DEPTH_CHANGE type,
237 Paragraph const & par, int max_depth)
239 if (par.layout().labeltype == LABEL_BIBLIO)
241 int const depth = par.params().depth();
242 if (type == Text::INC_DEPTH && depth < max_depth)
244 if (type == Text::DEC_DEPTH && depth > 0)
250 bool Text::changeDepthAllowed(Cursor & cur, DEPTH_CHANGE type) const
252 LASSERT(this == cur.text(), /**/);
253 // this happens when selecting several cells in tabular (bug 2630)
254 if (cur.selBegin().idx() != cur.selEnd().idx())
257 pit_type const beg = cur.selBegin().pit();
258 pit_type const end = cur.selEnd().pit() + 1;
259 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
261 for (pit_type pit = beg; pit != end; ++pit) {
262 if (lyx::changeDepthAllowed(type, pars_[pit], max_depth))
264 max_depth = pars_[pit].getMaxDepthAfter();
270 void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type)
272 LASSERT(this == cur.text(), /**/);
273 pit_type const beg = cur.selBegin().pit();
274 pit_type const end = cur.selEnd().pit() + 1;
275 cur.recordUndoSelection();
276 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
278 for (pit_type pit = beg; pit != end; ++pit) {
279 Paragraph & par = pars_[pit];
280 if (lyx::changeDepthAllowed(type, par, max_depth)) {
281 int const depth = par.params().depth();
282 if (type == INC_DEPTH)
283 par.params().depth(depth + 1);
285 par.params().depth(depth - 1);
287 max_depth = par.getMaxDepthAfter();
289 // this handles the counter labels, and also fixes up
290 // depth values for follow-on (child) paragraphs
291 cur.forceBufferUpdate();
295 void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
297 LASSERT(this == cur.text(), /**/);
298 // Set the current_font
299 // Determine basis font
301 pit_type pit = cur.pit();
302 if (cur.pos() < pars_[pit].beginOfBody())
303 layoutfont = labelFont(pars_[pit]);
305 layoutfont = layoutFont(pit);
307 // Update current font
308 cur.real_current_font.update(font,
309 cur.buffer()->params().language,
312 // Reduce to implicit settings
313 cur.current_font = cur.real_current_font;
314 cur.current_font.fontInfo().reduce(layoutfont);
315 // And resolve it completely
316 cur.real_current_font.fontInfo().realize(layoutfont);
318 // if there is no selection that's all we need to do
319 if (!cur.selection())
322 // Ok, we have a selection.
323 cur.recordUndoSelection();
325 setFont(cur.bv(), cur.selectionBegin().top(),
326 cur.selectionEnd().top(), font, toggleall);
330 void Text::setFont(BufferView const & bv, CursorSlice const & begin,
331 CursorSlice const & end, Font const & font,
334 Buffer const & buffer = bv.buffer();
336 // Don't use forwardChar here as ditend might have
337 // pos() == lastpos() and forwardChar would miss it.
338 // Can't use forwardPos either as this descends into
340 Language const * language = buffer.params().language;
341 for (CursorSlice dit = begin; dit != end; dit.forwardPos()) {
342 if (dit.pos() == dit.lastpos())
344 pit_type const pit = dit.pit();
345 pos_type const pos = dit.pos();
346 Inset * inset = pars_[pit].getInset(pos);
347 if (inset && inset->resetFontEdit()) {
348 // We need to propagate the font change to all
349 // text cells of the inset (bugs 1973, 6919).
350 setInsetFont(bv, pit, pos, font, toggleall);
352 TextMetrics const & tm = bv.textMetrics(this);
353 Font f = tm.displayFont(pit, pos);
354 f.update(font, language, toggleall);
355 setCharFont(pit, pos, f, tm.font_);
356 // font change may change language...
357 // spell checker has to know that
358 pars_[pit].requestSpellCheck(pos);
363 bool Text::cursorTop(Cursor & cur)
365 LASSERT(this == cur.text(), /**/);
366 return setCursor(cur, 0, 0);
370 bool Text::cursorBottom(Cursor & cur)
372 LASSERT(this == cur.text(), /**/);
373 return setCursor(cur, cur.lastpit(), boost::prior(paragraphs().end())->size());
377 void Text::toggleFree(Cursor & cur, Font const & font, bool toggleall)
379 LASSERT(this == cur.text(), /**/);
380 // If the mask is completely neutral, tell user
381 if (font.fontInfo() == ignore_font && font.language() == ignore_language) {
382 // Could only happen with user style
383 cur.message(_("No font change defined."));
387 // Try implicit word selection
388 // If there is a change in the language the implicit word selection
390 CursorSlice const resetCursor = cur.top();
391 bool const implicitSelection =
392 font.language() == ignore_language
393 && font.fontInfo().number() == FONT_IGNORE
394 && selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
397 setFont(cur, font, toggleall);
399 // Implicit selections are cleared afterwards
400 // and cursor is set to the original position.
401 if (implicitSelection) {
402 cur.clearSelection();
403 cur.top() = resetCursor;
409 docstring Text::getStringToIndex(Cursor const & cur)
411 LASSERT(this == cur.text(), /**/);
414 return cur.selectionAsString(false);
416 // Try implicit word selection. If there is a change
417 // in the language the implicit word selection is
420 selectWord(tmpcur, PREVIOUS_WORD);
422 if (!tmpcur.selection())
423 cur.message(_("Nothing to index!"));
424 else if (tmpcur.selBegin().pit() != tmpcur.selEnd().pit())
425 cur.message(_("Cannot index more than one paragraph!"));
427 return tmpcur.selectionAsString(false);
433 void Text::setLabelWidthStringToSequence(pit_type const par_offset,
436 pit_type offset = par_offset;
437 // Find first of same layout in sequence
438 while (!isFirstInSequence(offset)) {
439 offset = depthHook(offset, pars_[offset].getDepth());
442 // now apply label width string to every par
444 pit_type const end = pars_.size();
445 depth_type const depth = pars_[offset].getDepth();
446 Layout const & layout = pars_[offset].layout();
447 for (pit_type pit = offset; pit != end; ++pit) {
448 while (pars_[pit].getDepth() > depth) {
453 if (pars_[pit].getDepth() < depth)
455 if (pars_[pit].layout() != layout)
457 pars_[pit].setLabelWidthString(s);
462 void Text::setParagraphs(Cursor & cur, docstring arg, bool merge)
464 LASSERT(cur.text(), /**/);
465 // make sure that the depth behind the selection are restored, too
466 pit_type undopit = undoSpan(cur.selEnd().pit());
467 recUndo(cur, cur.selBegin().pit(), undopit - 1);
470 string const argument = to_utf8(arg);
471 depth_type priordepth = -1;
473 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
475 Paragraph & par = pars_[pit];
476 ParagraphParameters params = par.params();
477 params.read(argument, merge);
478 // Changes to label width string apply to all paragraphs
479 // with same layout in a sequence.
480 // Do this only once for a selected range of paragraphs
481 // of the same layout and depth.
482 if (par.getDepth() != priordepth || par.layout() != priorlayout)
483 setLabelWidthStringToSequence(pit, params.labelWidthString());
484 par.params().apply(params, par.layout());
485 priordepth = par.getDepth();
486 priorlayout = par.layout();
491 //FIXME This is a little redundant now, but it's probably worth keeping,
492 //especially if we're going to go away from using serialization internally
494 void Text::setParagraphs(Cursor & cur, ParagraphParameters const & p)
496 LASSERT(cur.text(), /**/);
497 // make sure that the depth behind the selection are restored, too
498 pit_type undopit = undoSpan(cur.selEnd().pit());
499 recUndo(cur, cur.selBegin().pit(), undopit - 1);
501 depth_type priordepth = -1;
503 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
505 Paragraph & par = pars_[pit];
506 // Changes to label width string apply to all paragraphs
507 // with same layout in a sequence.
508 // Do this only once for a selected range of paragraphs
509 // of the same layout and depth.
510 if (par.getDepth() != priordepth || par.layout() != priorlayout)
511 setLabelWidthStringToSequence(pit,
512 par.params().labelWidthString());
513 par.params().apply(p, par.layout());
514 priordepth = par.getDepth();
515 priorlayout = par.layout();
520 // this really should just insert the inset and not move the cursor.
521 void Text::insertInset(Cursor & cur, Inset * inset)
523 LASSERT(this == cur.text(), /**/);
524 LASSERT(inset, /**/);
525 cur.paragraph().insertInset(cur.pos(), inset, cur.current_font,
526 Change(cur.buffer()->params().trackChanges
527 ? Change::INSERTED : Change::UNCHANGED));
531 bool Text::setCursor(Cursor & cur, pit_type par, pos_type pos,
532 bool setfont, bool boundary)
534 TextMetrics const & tm = cur.bv().textMetrics(this);
535 bool const update_needed = !tm.contains(par);
537 setCursorIntern(cur, par, pos, setfont, boundary);
538 // FIXME There is a chance that we'll miss a screen update here.
539 // If so, then do DEPM and then check if cur wants an update and
540 // go ahead and do it, if so.
541 return cur.bv().checkDepm(cur, old) || update_needed;
545 void Text::setCursor(CursorSlice & cur, pit_type par, pos_type pos)
547 LASSERT(par != int(paragraphs().size()), /**/);
551 // now some strict checking
552 Paragraph & para = getPar(par);
554 // None of these should happen, but we're scaredy-cats
556 lyxerr << "don't like -1" << endl;
557 LASSERT(false, /**/);
560 if (pos > para.size()) {
561 lyxerr << "don't like 1, pos: " << pos
562 << " size: " << para.size()
563 << " par: " << par << endl;
564 LASSERT(false, /**/);
569 void Text::setCursorIntern(Cursor & cur,
570 pit_type par, pos_type pos, bool setfont, bool boundary)
572 LASSERT(this == cur.text(), /**/);
573 cur.boundary(boundary);
574 setCursor(cur.top(), par, pos);
576 cur.setCurrentFont();
580 bool Text::checkAndActivateInset(Cursor & cur, bool front)
584 if (front && cur.pos() == cur.lastpos())
586 if (!front && cur.pos() == 0)
588 Inset * inset = front ? cur.nextInset() : cur.prevInset();
589 if (!inset || !inset->editable())
592 * Apparently, when entering an inset we are expected to be positioned
593 * *before* it in the containing paragraph, regardless of the direction
594 * from which we are entering. Otherwise, cursor placement goes awry,
595 * and when we exit from the beginning, we'll be placed *after* the
600 inset->edit(cur, front);
605 bool Text::checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool movingLeft)
611 if (cur.pos() == cur.lastpos())
613 Paragraph & par = cur.paragraph();
614 Inset * inset = par.isInset(cur.pos()) ? par.getInset(cur.pos()) : 0;
615 if (!inset || !inset->editable())
617 inset->edit(cur, movingForward,
618 movingLeft ? Inset::ENTRY_DIRECTION_RIGHT : Inset::ENTRY_DIRECTION_LEFT);
623 bool Text::cursorBackward(Cursor & cur)
625 // Tell BufferView to test for FitCursor in any case!
626 cur.screenUpdateFlags(Update::FitCursor);
628 // not at paragraph start?
630 // if on right side of boundary (i.e. not at paragraph end, but line end)
631 // -> skip it, i.e. set boundary to true, i.e. go only logically left
632 // there are some exceptions to ignore this: lineseps, newlines, spaces
634 // some effectless debug code to see the values in the debugger
635 bool bound = cur.boundary();
636 int rowpos = cur.textRow().pos();
638 bool sep = cur.paragraph().isSeparator(cur.pos() - 1);
639 bool newline = cur.paragraph().isNewline(cur.pos() - 1);
640 bool linesep = cur.paragraph().isLineSeparator(cur.pos() - 1);
642 if (!cur.boundary() &&
643 cur.textRow().pos() == cur.pos() &&
644 !cur.paragraph().isLineSeparator(cur.pos() - 1) &&
645 !cur.paragraph().isNewline(cur.pos() - 1) &&
646 !cur.paragraph().isSeparator(cur.pos() - 1)) {
647 return setCursor(cur, cur.pit(), cur.pos(), true, true);
650 // go left and try to enter inset
651 if (checkAndActivateInset(cur, false))
654 // normal character left
655 return setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
658 // move to the previous paragraph or do nothing
660 return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size(), true, false);
665 bool Text::cursorVisLeft(Cursor & cur, bool skip_inset)
667 Cursor temp_cur = cur;
668 temp_cur.posVisLeft(skip_inset);
669 if (temp_cur.depth() > cur.depth()) {
673 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
674 true, temp_cur.boundary());
678 bool Text::cursorVisRight(Cursor & cur, bool skip_inset)
680 Cursor temp_cur = cur;
681 temp_cur.posVisRight(skip_inset);
682 if (temp_cur.depth() > cur.depth()) {
686 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
687 true, temp_cur.boundary());
691 bool Text::cursorForward(Cursor & cur)
693 // Tell BufferView to test for FitCursor in any case!
694 cur.screenUpdateFlags(Update::FitCursor);
696 // not at paragraph end?
697 if (cur.pos() != cur.lastpos()) {
698 // in front of editable inset, i.e. jump into it?
699 if (checkAndActivateInset(cur, true))
702 TextMetrics const & tm = cur.bv().textMetrics(this);
703 // if left of boundary -> just jump to right side
704 // but for RTL boundaries don't, because: abc|DDEEFFghi -> abcDDEEF|Fghi
705 if (cur.boundary() && !tm.isRTLBoundary(cur.pit(), cur.pos()))
706 return setCursor(cur, cur.pit(), cur.pos(), true, false);
708 // next position is left of boundary,
709 // but go to next line for special cases like space, newline, linesep
711 // some effectless debug code to see the values in the debugger
712 int endpos = cur.textRow().endpos();
713 int lastpos = cur.lastpos();
715 bool linesep = cur.paragraph().isLineSeparator(cur.pos());
716 bool newline = cur.paragraph().isNewline(cur.pos());
717 bool sep = cur.paragraph().isSeparator(cur.pos());
718 if (cur.pos() != cur.lastpos()) {
719 bool linesep2 = cur.paragraph().isLineSeparator(cur.pos()+1);
720 bool newline2 = cur.paragraph().isNewline(cur.pos()+1);
721 bool sep2 = cur.paragraph().isSeparator(cur.pos()+1);
724 if (cur.textRow().endpos() == cur.pos() + 1 &&
725 cur.textRow().endpos() != cur.lastpos() &&
726 !cur.paragraph().isNewline(cur.pos()) &&
727 !cur.paragraph().isLineSeparator(cur.pos()) &&
728 !cur.paragraph().isSeparator(cur.pos())) {
729 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
732 // in front of RTL boundary? Stay on this side of the boundary because:
733 // ab|cDDEEFFghi -> abc|DDEEFFghi
734 if (tm.isRTLBoundary(cur.pit(), cur.pos() + 1))
735 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
738 return setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
741 // move to next paragraph
742 if (cur.pit() != cur.lastpit())
743 return setCursor(cur, cur.pit() + 1, 0, true, false);
748 bool Text::cursorUpParagraph(Cursor & cur)
750 bool updated = false;
752 updated = setCursor(cur, cur.pit(), 0);
753 else if (cur.pit() != 0)
754 updated = setCursor(cur, cur.pit() - 1, 0);
759 bool Text::cursorDownParagraph(Cursor & cur)
761 bool updated = false;
762 if (cur.pit() != cur.lastpit())
763 updated = setCursor(cur, cur.pit() + 1, 0);
765 updated = setCursor(cur, cur.pit(), cur.lastpos());
770 // fix the cursor `cur' after a characters has been deleted at `where'
771 // position. Called by deleteEmptyParagraphMechanism
772 void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
774 // Do nothing if cursor is not in the paragraph where the
776 if (cur.pit() != where.pit())
779 // If cursor position is after the deletion place update it
780 if (cur.pos() > where.pos())
783 // Check also if we don't want to set the cursor on a spot behind the
784 // pagragraph because we erased the last character.
785 if (cur.pos() > cur.lastpos())
786 cur.pos() = cur.lastpos();
790 bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
791 Cursor & old, bool & need_anchor_change)
793 //LYXERR(Debug::DEBUG, "DEPM: cur:\n" << cur << "old:\n" << old);
795 Paragraph & oldpar = old.paragraph();
797 // We allow all kinds of "mumbo-jumbo" when freespacing.
798 if (oldpar.isFreeSpacing())
801 /* Ok I'll put some comments here about what is missing.
802 There are still some small problems that can lead to
803 double spaces stored in the document file or space at
804 the beginning of paragraphs(). This happens if you have
805 the cursor between to spaces and then save. Or if you
806 cut and paste and the selection have a space at the
807 beginning and then save right after the paste. (Lgb)
810 // If old.pos() == 0 and old.pos()(1) == LineSeparator
811 // delete the LineSeparator.
814 // If old.pos() == 1 and old.pos()(0) == LineSeparator
815 // delete the LineSeparator.
818 // Find a common inset and the corresponding depth.
820 for (; depth < cur.depth(); ++depth)
821 if (&old.inset() == &cur[depth].inset())
824 // Whether a common inset is found and whether the cursor is still in
825 // the same paragraph (possibly nested).
826 bool const same_par = depth < cur.depth() && old.pit() == cur[depth].pit();
827 bool const same_par_pos = depth == cur.depth() - 1 && same_par
828 && old.pos() == cur[depth].pos();
830 // If the chars around the old cursor were spaces, delete one of them.
832 // Only if the cursor has really moved.
834 && old.pos() < oldpar.size()
835 && oldpar.isLineSeparator(old.pos())
836 && oldpar.isLineSeparator(old.pos() - 1)
837 && !oldpar.isDeleted(old.pos() - 1)
838 && !oldpar.isDeleted(old.pos())) {
839 oldpar.eraseChar(old.pos() - 1, cur.buffer()->params().trackChanges);
840 // FIXME: This will not work anymore when we have multiple views of the same buffer
841 // In this case, we will have to correct also the cursors held by
842 // other bufferviews. It will probably be easier to do that in a more
843 // automated way in CursorSlice code. (JMarc 26/09/2001)
844 // correct all cursor parts
846 fixCursorAfterDelete(cur[depth], old.top());
847 need_anchor_change = true;
853 // only do our magic if we changed paragraph
857 // don't delete anything if this is the ONLY paragraph!
858 if (old.lastpit() == 0)
861 // Do not delete empty paragraphs with keepempty set.
862 if (oldpar.allowEmpty())
865 if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
867 old.recordUndo(ATOMIC_UNDO,
868 max(old.pit() - 1, pit_type(0)),
869 min(old.pit() + 1, old.lastpit()));
870 ParagraphList & plist = old.text()->paragraphs();
871 bool const soa = oldpar.params().startOfAppendix();
872 plist.erase(boost::next(plist.begin(), old.pit()));
873 // do not lose start of appendix marker (bug 4212)
874 if (soa && old.pit() < pit_type(plist.size()))
875 plist[old.pit()].params().startOfAppendix(true);
877 // see #warning (FIXME?) above
878 if (cur.depth() >= old.depth()) {
879 CursorSlice & curslice = cur[old.depth() - 1];
880 if (&curslice.inset() == &old.inset()
881 && curslice.pit() > old.pit()) {
883 // since a paragraph has been deleted, all the
884 // insets after `old' have been copied and
885 // their address has changed. Therefore we
886 // need to `regenerate' cur. (JMarc)
887 cur.updateInsets(&(cur.bottom().inset()));
888 need_anchor_change = true;
894 if (oldpar.stripLeadingSpaces(cur.buffer()->params().trackChanges)) {
895 need_anchor_change = true;
896 // We return true here because the Paragraph contents changed and
897 // we need a redraw before further action is processed.
905 void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges)
907 LASSERT(first >= 0 && first <= last && last < (int) pars_.size(), /**/);
909 for (pit_type pit = first; pit <= last; ++pit) {
910 Paragraph & par = pars_[pit];
912 // We allow all kinds of "mumbo-jumbo" when freespacing.
913 if (par.isFreeSpacing())
916 for (pos_type pos = 1; pos < par.size(); ++pos) {
917 if (par.isLineSeparator(pos) && par.isLineSeparator(pos - 1)
918 && !par.isDeleted(pos - 1)) {
919 if (par.eraseChar(pos - 1, trackChanges)) {
925 // don't delete anything if this is the only remaining paragraph
926 // within the given range. Note: Text::acceptOrRejectChanges()
927 // sets the cursor to 'first' after calling DEPM
931 // don't delete empty paragraphs with keepempty set
932 if (par.allowEmpty())
935 if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) {
936 pars_.erase(boost::next(pars_.begin(), pit));
942 par.stripLeadingSpaces(trackChanges);
947 void Text::recUndo(Cursor & cur, pit_type first, pit_type last) const
949 cur.recordUndo(ATOMIC_UNDO, first, last);
953 void Text::recUndo(Cursor & cur, pit_type par) const
955 cur.recordUndo(ATOMIC_UNDO, par, par);