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"
47 #include "insets/InsetCollapsable.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>
64 bool Text::isMainText() const
66 return &owner_->buffer().text() == this;
70 // Note that this is supposed to return a fully realized font.
71 FontInfo Text::layoutFont(pit_type const pit) const
73 Layout const & layout = pars_[pit].layout();
75 if (!pars_[pit].getDepth()) {
76 FontInfo lf = layout.resfont;
77 // In case the default family has been customized
78 if (layout.font.family() == INHERIT_FAMILY)
79 lf.setFamily(owner_->buffer().params().getFont().fontInfo().family());
81 // It ought to be possible here just to use Inset::getLayout() and skip
82 // the asInsetCollapsable() bit. Unfortunatley, that doesn't work right
83 // now, because Inset::getLayout() will return a default-constructed
84 // InsetLayout, and that e.g. sets the foreground color to red. So we
85 // need to do some work to make that possible.
86 InsetCollapsable const * icp = owner_->asInsetCollapsable();
89 FontInfo icf = icp->getLayout().font();
94 FontInfo font = layout.font;
95 // Realize with the fonts of lesser depth.
96 //font.realize(outerFont(pit));
97 font.realize(owner_->buffer().params().getFont().fontInfo());
103 // Note that this is supposed to return a fully realized font.
104 FontInfo Text::labelFont(Paragraph const & par) const
106 Buffer const & buffer = owner_->buffer();
107 Layout 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(pit_type pit,
126 pos_type pos, Font const & fnt, Font const & display_font)
128 Buffer const & buffer = owner_->buffer();
130 Layout const & layout = pars_[pit].layout();
132 // Get concrete layout font to reduce against
135 if (pos < pars_[pit].beginOfBody())
136 layoutfont = layout.labelfont;
138 layoutfont = layout.font;
140 // Realize against environment font information
141 if (pars_[pit].getDepth()) {
143 while (!layoutfont.resolved() &&
144 tp != pit_type(paragraphs().size()) &&
145 pars_[tp].getDepth()) {
147 if (tp != pit_type(paragraphs().size()))
148 layoutfont.realize(pars_[tp].layout().font);
152 // Inside inset, apply the inset's font attributes if any
155 layoutfont.realize(display_font.fontInfo());
157 layoutfont.realize(buffer.params().getFont().fontInfo());
159 // Now, reduce font against full layout font
160 font.fontInfo().reduce(layoutfont);
162 pars_[pit].setFont(pos, font);
166 void Text::setInsetFont(BufferView const & bv, pit_type pit,
167 pos_type pos, Font const & font, bool toggleall)
169 Inset * const inset = pars_[pit].getInset(pos);
170 LASSERT(inset && inset->noFontChange(), /**/);
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 const 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(pit_type start, pit_type end,
206 docstring const & layout)
208 LASSERT(start != end, /**/);
210 Buffer const & buffer = owner_->buffer();
211 BufferParams const & bp = buffer.params();
212 Layout const & lyxlayout = bp.documentClass()[layout];
214 for (pit_type pit = start; pit != end; ++pit) {
215 Paragraph & par = pars_[pit];
216 par.applyLayout(lyxlayout);
217 if (lyxlayout.margintype == MARGIN_MANUAL)
218 par.setLabelWidthString(par.expandLabel(lyxlayout, bp));
223 // set layout over selection and make a total rebreak of those paragraphs
224 void Text::setLayout(Cursor & cur, docstring const & layout)
226 LASSERT(this == cur.text(), /**/);
228 pit_type start = cur.selBegin().pit();
229 pit_type end = cur.selEnd().pit() + 1;
230 pit_type undopit = undoSpan(end - 1);
231 recUndo(cur, start, undopit - 1);
232 setLayout(start, end, layout);
233 cur.forceBufferUpdate();
237 static bool changeDepthAllowed(Text::DEPTH_CHANGE type,
238 Paragraph const & par, int max_depth)
240 if (par.layout().labeltype == LABEL_BIBLIO)
242 int const depth = par.params().depth();
243 if (type == Text::INC_DEPTH && depth < max_depth)
245 if (type == Text::DEC_DEPTH && depth > 0)
251 bool Text::changeDepthAllowed(Cursor & cur, DEPTH_CHANGE type) const
253 LASSERT(this == cur.text(), /**/);
254 // this happens when selecting several cells in tabular (bug 2630)
255 if (cur.selBegin().idx() != cur.selEnd().idx())
258 pit_type const beg = cur.selBegin().pit();
259 pit_type const end = cur.selEnd().pit() + 1;
260 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
262 for (pit_type pit = beg; pit != end; ++pit) {
263 if (lyx::changeDepthAllowed(type, pars_[pit], max_depth))
265 max_depth = pars_[pit].getMaxDepthAfter();
271 void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type)
273 LASSERT(this == cur.text(), /**/);
274 pit_type const beg = cur.selBegin().pit();
275 pit_type const end = cur.selEnd().pit() + 1;
276 cur.recordUndoSelection();
277 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
279 for (pit_type pit = beg; pit != end; ++pit) {
280 Paragraph & par = pars_[pit];
281 if (lyx::changeDepthAllowed(type, par, max_depth)) {
282 int const depth = par.params().depth();
283 if (type == INC_DEPTH)
284 par.params().depth(depth + 1);
286 par.params().depth(depth - 1);
288 max_depth = par.getMaxDepthAfter();
290 // this handles the counter labels, and also fixes up
291 // depth values for follow-on (child) paragraphs
292 cur.forceBufferUpdate();
296 void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
298 LASSERT(this == cur.text(), /**/);
299 // Set the current_font
300 // Determine basis font
302 pit_type pit = cur.pit();
303 if (cur.pos() < pars_[pit].beginOfBody())
304 layoutfont = labelFont(pars_[pit]);
306 layoutfont = layoutFont(pit);
308 // Update current font
309 cur.real_current_font.update(font,
310 cur.buffer()->params().language,
313 // Reduce to implicit settings
314 cur.current_font = cur.real_current_font;
315 cur.current_font.fontInfo().reduce(layoutfont);
316 // And resolve it completely
317 cur.real_current_font.fontInfo().realize(layoutfont);
319 // if there is no selection that's all we need to do
320 if (!cur.selection())
323 // Ok, we have a selection.
324 cur.recordUndoSelection();
326 setFont(cur.bv(), cur.selectionBegin().top(),
327 cur.selectionEnd().top(), font, toggleall);
331 void Text::setFont(BufferView const & bv, CursorSlice const & begin,
332 CursorSlice const & end, Font const & font,
335 Buffer const & buffer = bv.buffer();
337 // Don't use forwardChar here as ditend might have
338 // pos() == lastpos() and forwardChar would miss it.
339 // Can't use forwardPos either as this descends into
341 Language const * language = buffer.params().language;
342 for (CursorSlice dit = begin; dit != end; dit.forwardPos()) {
343 if (dit.pos() == dit.lastpos())
345 pit_type const pit = dit.pit();
346 pos_type const pos = dit.pos();
347 Inset * inset = pars_[pit].getInset(pos);
348 if (inset && inset->noFontChange()) {
349 // We need to propagate the font change to all
350 // text cells of the inset (bug 1973).
351 // FIXME: This should change, see documentation
352 // of noFontChange in Inset.h
353 setInsetFont(bv, pit, pos, font, toggleall);
355 TextMetrics const & tm = bv.textMetrics(this);
356 Font f = tm.displayFont(pit, pos);
357 f.update(font, language, toggleall);
358 setCharFont(pit, pos, f, tm.font_);
359 // font change may change language...
360 // spell checker has to know that
361 pars_[pit].requestSpellCheck(pos);
366 bool Text::cursorTop(Cursor & cur)
368 LASSERT(this == cur.text(), /**/);
369 return setCursor(cur, 0, 0);
373 bool Text::cursorBottom(Cursor & cur)
375 LASSERT(this == cur.text(), /**/);
376 return setCursor(cur, cur.lastpit(), boost::prior(paragraphs().end())->size());
380 void Text::toggleFree(Cursor & cur, Font const & font, bool toggleall)
382 LASSERT(this == cur.text(), /**/);
383 // If the mask is completely neutral, tell user
384 if (font.fontInfo() == ignore_font && font.language() == ignore_language) {
385 // Could only happen with user style
386 cur.message(_("No font change defined."));
390 // Try implicit word selection
391 // If there is a change in the language the implicit word selection
393 CursorSlice const resetCursor = cur.top();
394 bool const implicitSelection =
395 font.language() == ignore_language
396 && font.fontInfo().number() == FONT_IGNORE
397 && selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
400 setFont(cur, font, toggleall);
402 // Implicit selections are cleared afterwards
403 // and cursor is set to the original position.
404 if (implicitSelection) {
405 cur.clearSelection();
406 cur.top() = resetCursor;
412 docstring Text::getStringToIndex(Cursor const & cur)
414 LASSERT(this == cur.text(), /**/);
417 return cur.selectionAsString(false);
419 // Try implicit word selection. If there is a change
420 // in the language the implicit word selection is
423 selectWord(tmpcur, PREVIOUS_WORD);
425 if (!tmpcur.selection())
426 cur.message(_("Nothing to index!"));
427 else if (tmpcur.selBegin().pit() != tmpcur.selEnd().pit())
428 cur.message(_("Cannot index more than one paragraph!"));
430 return tmpcur.selectionAsString(false);
436 void Text::setLabelWidthStringToSequence(pit_type const par_offset,
439 pit_type offset = par_offset;
440 // Find first of same layout in sequence
441 while (!isFirstInSequence(offset)) {
442 offset = depthHook(offset, pars_[offset].getDepth());
445 // now apply label width string to every par
447 pit_type const end = pars_.size();
448 depth_type const depth = pars_[offset].getDepth();
449 Layout const & layout = pars_[offset].layout();
450 for (pit_type pit = offset; pit != end; ++pit) {
451 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 return cur.bv().checkDepm(cur, old) || update_needed;
542 void Text::setCursor(CursorSlice & cur, pit_type par, pos_type pos)
544 LASSERT(par != int(paragraphs().size()), /**/);
548 // now some strict checking
549 Paragraph & para = getPar(par);
551 // None of these should happen, but we're scaredy-cats
553 lyxerr << "don't like -1" << endl;
554 LASSERT(false, /**/);
557 if (pos > para.size()) {
558 lyxerr << "don't like 1, pos: " << pos
559 << " size: " << para.size()
560 << " par: " << par << endl;
561 LASSERT(false, /**/);
566 void Text::setCursorIntern(Cursor & cur,
567 pit_type par, pos_type pos, bool setfont, bool boundary)
569 LASSERT(this == cur.text(), /**/);
570 cur.boundary(boundary);
571 setCursor(cur.top(), par, pos);
573 cur.setCurrentFont();
577 bool Text::checkAndActivateInset(Cursor & cur, bool front)
581 if (front && cur.pos() == cur.lastpos())
583 if (!front && cur.pos() == 0)
585 Inset * inset = front ? cur.nextInset() : cur.prevInset();
586 if (!inset || !inset->editable())
589 * Apparently, when entering an inset we are expected to be positioned
590 * *before* it in the containing paragraph, regardless of the direction
591 * from which we are entering. Otherwise, cursor placement goes awry,
592 * and when we exit from the beginning, we'll be placed *after* the
597 inset->edit(cur, front);
602 bool Text::checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool movingLeft)
608 if (cur.pos() == cur.lastpos())
610 Paragraph & par = cur.paragraph();
611 Inset * inset = par.isInset(cur.pos()) ? par.getInset(cur.pos()) : 0;
612 if (!inset || !inset->editable())
614 inset->edit(cur, movingForward,
615 movingLeft ? Inset::ENTRY_DIRECTION_RIGHT : Inset::ENTRY_DIRECTION_LEFT);
620 bool Text::cursorBackward(Cursor & cur)
622 // Tell BufferView to test for FitCursor in any case!
623 cur.screenUpdateFlags(Update::FitCursor);
625 // not at paragraph start?
627 // if on right side of boundary (i.e. not at paragraph end, but line end)
628 // -> skip it, i.e. set boundary to true, i.e. go only logically left
629 // there are some exceptions to ignore this: lineseps, newlines, spaces
631 // some effectless debug code to see the values in the debugger
632 bool bound = cur.boundary();
633 int rowpos = cur.textRow().pos();
635 bool sep = cur.paragraph().isSeparator(cur.pos() - 1);
636 bool newline = cur.paragraph().isNewline(cur.pos() - 1);
637 bool linesep = cur.paragraph().isLineSeparator(cur.pos() - 1);
639 if (!cur.boundary() &&
640 cur.textRow().pos() == cur.pos() &&
641 !cur.paragraph().isLineSeparator(cur.pos() - 1) &&
642 !cur.paragraph().isNewline(cur.pos() - 1) &&
643 !cur.paragraph().isSeparator(cur.pos() - 1)) {
644 return setCursor(cur, cur.pit(), cur.pos(), true, true);
647 // go left and try to enter inset
648 if (checkAndActivateInset(cur, false))
651 // normal character left
652 return setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
655 // move to the previous paragraph or do nothing
657 return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size(), true, false);
662 bool Text::cursorVisLeft(Cursor & cur, bool skip_inset)
664 Cursor temp_cur = cur;
665 temp_cur.posVisLeft(skip_inset);
666 if (temp_cur.depth() > cur.depth()) {
670 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
671 true, temp_cur.boundary());
675 bool Text::cursorVisRight(Cursor & cur, bool skip_inset)
677 Cursor temp_cur = cur;
678 temp_cur.posVisRight(skip_inset);
679 if (temp_cur.depth() > cur.depth()) {
683 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
684 true, temp_cur.boundary());
688 bool Text::cursorForward(Cursor & cur)
690 // Tell BufferView to test for FitCursor in any case!
691 cur.screenUpdateFlags(Update::FitCursor);
693 // not at paragraph end?
694 if (cur.pos() != cur.lastpos()) {
695 // in front of editable inset, i.e. jump into it?
696 if (checkAndActivateInset(cur, true))
699 TextMetrics const & tm = cur.bv().textMetrics(this);
700 // if left of boundary -> just jump to right side
701 // but for RTL boundaries don't, because: abc|DDEEFFghi -> abcDDEEF|Fghi
702 if (cur.boundary() && !tm.isRTLBoundary(cur.pit(), cur.pos()))
703 return setCursor(cur, cur.pit(), cur.pos(), true, false);
705 // next position is left of boundary,
706 // but go to next line for special cases like space, newline, linesep
708 // some effectless debug code to see the values in the debugger
709 int endpos = cur.textRow().endpos();
710 int lastpos = cur.lastpos();
712 bool linesep = cur.paragraph().isLineSeparator(cur.pos());
713 bool newline = cur.paragraph().isNewline(cur.pos());
714 bool sep = cur.paragraph().isSeparator(cur.pos());
715 if (cur.pos() != cur.lastpos()) {
716 bool linesep2 = cur.paragraph().isLineSeparator(cur.pos()+1);
717 bool newline2 = cur.paragraph().isNewline(cur.pos()+1);
718 bool sep2 = cur.paragraph().isSeparator(cur.pos()+1);
721 if (cur.textRow().endpos() == cur.pos() + 1 &&
722 cur.textRow().endpos() != cur.lastpos() &&
723 !cur.paragraph().isNewline(cur.pos()) &&
724 !cur.paragraph().isLineSeparator(cur.pos()) &&
725 !cur.paragraph().isSeparator(cur.pos())) {
726 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
729 // in front of RTL boundary? Stay on this side of the boundary because:
730 // ab|cDDEEFFghi -> abc|DDEEFFghi
731 if (tm.isRTLBoundary(cur.pit(), cur.pos() + 1))
732 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
735 return setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
738 // move to next paragraph
739 if (cur.pit() != cur.lastpit())
740 return setCursor(cur, cur.pit() + 1, 0, true, false);
745 bool Text::cursorUpParagraph(Cursor & cur)
747 bool updated = false;
749 updated = setCursor(cur, cur.pit(), 0);
750 else if (cur.pit() != 0)
751 updated = setCursor(cur, cur.pit() - 1, 0);
756 bool Text::cursorDownParagraph(Cursor & cur)
758 bool updated = false;
759 if (cur.pit() != cur.lastpit())
760 updated = setCursor(cur, cur.pit() + 1, 0);
762 updated = setCursor(cur, cur.pit(), cur.lastpos());
767 // fix the cursor `cur' after a characters has been deleted at `where'
768 // position. Called by deleteEmptyParagraphMechanism
769 void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
771 // Do nothing if cursor is not in the paragraph where the
773 if (cur.pit() != where.pit())
776 // If cursor position is after the deletion place update it
777 if (cur.pos() > where.pos())
780 // Check also if we don't want to set the cursor on a spot behind the
781 // pagragraph because we erased the last character.
782 if (cur.pos() > cur.lastpos())
783 cur.pos() = cur.lastpos();
787 bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
788 Cursor & old, bool & need_anchor_change)
790 //LYXERR(Debug::DEBUG, "DEPM: cur:\n" << cur << "old:\n" << old);
792 Paragraph & oldpar = old.paragraph();
794 // We allow all kinds of "mumbo-jumbo" when freespacing.
795 if (oldpar.isFreeSpacing())
798 /* Ok I'll put some comments here about what is missing.
799 There are still some small problems that can lead to
800 double spaces stored in the document file or space at
801 the beginning of paragraphs(). This happens if you have
802 the cursor between to spaces and then save. Or if you
803 cut and paste and the selection have a space at the
804 beginning and then save right after the paste. (Lgb)
807 // If old.pos() == 0 and old.pos()(1) == LineSeparator
808 // delete the LineSeparator.
811 // If old.pos() == 1 and old.pos()(0) == LineSeparator
812 // delete the LineSeparator.
815 // Find a common inset and the corresponding depth.
817 for (; depth < cur.depth(); ++depth)
818 if (&old.inset() == &cur[depth].inset())
821 // Whether a common inset is found and whether the cursor is still in
822 // the same paragraph (possibly nested).
823 bool const same_par = depth < cur.depth() && old.pit() == cur[depth].pit();
824 bool const same_par_pos = depth == cur.depth() - 1 && same_par
825 && old.pos() == cur[depth].pos();
827 // If the chars around the old cursor were spaces, delete one of them.
829 // Only if the cursor has really moved.
831 && old.pos() < oldpar.size()
832 && oldpar.isLineSeparator(old.pos())
833 && oldpar.isLineSeparator(old.pos() - 1)
834 && !oldpar.isDeleted(old.pos() - 1)
835 && !oldpar.isDeleted(old.pos())) {
836 oldpar.eraseChar(old.pos() - 1, cur.buffer()->params().trackChanges);
837 // FIXME: This will not work anymore when we have multiple views of the same buffer
838 // In this case, we will have to correct also the cursors held by
839 // other bufferviews. It will probably be easier to do that in a more
840 // automated way in CursorSlice code. (JMarc 26/09/2001)
841 // correct all cursor parts
843 fixCursorAfterDelete(cur[depth], old.top());
844 need_anchor_change = true;
850 // only do our magic if we changed paragraph
854 // don't delete anything if this is the ONLY paragraph!
855 if (old.lastpit() == 0)
858 // Do not delete empty paragraphs with keepempty set.
859 if (oldpar.allowEmpty())
862 if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
864 old.recordUndo(ATOMIC_UNDO,
865 max(old.pit() - 1, pit_type(0)),
866 min(old.pit() + 1, old.lastpit()));
867 ParagraphList & plist = old.text()->paragraphs();
868 bool const soa = oldpar.params().startOfAppendix();
869 plist.erase(boost::next(plist.begin(), old.pit()));
870 // do not lose start of appendix marker (bug 4212)
871 if (soa && old.pit() < pit_type(plist.size()))
872 plist[old.pit()].params().startOfAppendix(true);
874 // see #warning (FIXME?) above
875 if (cur.depth() >= old.depth()) {
876 CursorSlice & curslice = cur[old.depth() - 1];
877 if (&curslice.inset() == &old.inset()
878 && curslice.pit() > old.pit()) {
880 // since a paragraph has been deleted, all the
881 // insets after `old' have been copied and
882 // their address has changed. Therefore we
883 // need to `regenerate' cur. (JMarc)
884 cur.updateInsets(&(cur.bottom().inset()));
885 need_anchor_change = true;
891 if (oldpar.stripLeadingSpaces(cur.buffer()->params().trackChanges)) {
892 need_anchor_change = true;
893 // We return true here because the Paragraph contents changed and
894 // we need a redraw before further action is processed.
902 void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges)
904 LASSERT(first >= 0 && first <= last && last < (int) pars_.size(), /**/);
906 for (pit_type pit = first; pit <= last; ++pit) {
907 Paragraph & par = pars_[pit];
909 // We allow all kinds of "mumbo-jumbo" when freespacing.
910 if (par.isFreeSpacing())
913 for (pos_type pos = 1; pos < par.size(); ++pos) {
914 if (par.isLineSeparator(pos) && par.isLineSeparator(pos - 1)
915 && !par.isDeleted(pos - 1)) {
916 if (par.eraseChar(pos - 1, trackChanges)) {
922 // don't delete anything if this is the only remaining paragraph within the given range
923 // note: Text::acceptOrRejectChanges() sets the cursor to 'first' after calling DEPM
927 // don't delete empty paragraphs with keepempty set
928 if (par.allowEmpty())
931 if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) {
932 pars_.erase(boost::next(pars_.begin(), pit));
938 par.stripLeadingSpaces(trackChanges);
943 void Text::recUndo(Cursor & cur, pit_type first, pit_type last) const
945 cur.recordUndo(ATOMIC_UNDO, first, last);
949 void Text::recUndo(Cursor & cur, pit_type par) const
951 cur.recordUndo(ATOMIC_UNDO, par, par);