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/InsetEnvironment.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 FontInfo Text::layoutFont(Buffer const & buffer, pit_type const pit) const
79 Layout const & layout = pars_[pit].layout();
81 if (!pars_[pit].getDepth()) {
82 FontInfo lf = layout.resfont;
83 // In case the default family has been customized
84 if (layout.font.family() == INHERIT_FAMILY)
85 lf.setFamily(buffer.params().getFont().fontInfo().family());
89 FontInfo font = layout.font;
90 // Realize with the fonts of lesser depth.
91 //font.realize(outerFont(pit, paragraphs()));
92 font.realize(buffer.params().getFont().fontInfo());
98 FontInfo Text::labelFont(Buffer const & buffer, Paragraph const & par) const
100 Layout const & layout = par.layout();
102 if (!par.getDepth()) {
103 FontInfo lf = layout.reslabelfont;
104 // In case the default family has been customized
105 if (layout.labelfont.family() == INHERIT_FAMILY)
106 lf.setFamily(buffer.params().getFont().fontInfo().family());
110 FontInfo font = layout.labelfont;
111 // Realize with the fonts of lesser depth.
112 font.realize(buffer.params().getFont().fontInfo());
118 void Text::setCharFont(Buffer const & buffer, pit_type pit,
119 pos_type pos, Font const & fnt, Font const & display_font)
122 Layout const & layout = pars_[pit].layout();
124 // Get concrete layout font to reduce against
127 if (pos < pars_[pit].beginOfBody())
128 layoutfont = layout.labelfont;
130 layoutfont = layout.font;
132 // Realize against environment font information
133 if (pars_[pit].getDepth()) {
135 while (!layoutfont.resolved() &&
136 tp != pit_type(paragraphs().size()) &&
137 pars_[tp].getDepth()) {
138 tp = outerHook(tp, paragraphs());
139 if (tp != pit_type(paragraphs().size()))
140 layoutfont.realize(pars_[tp].layout().font);
144 // Inside inset, apply the inset's font attributes if any
146 if (!isMainText(buffer))
147 layoutfont.realize(display_font.fontInfo());
149 layoutfont.realize(buffer.params().getFont().fontInfo());
151 // Now, reduce font against full layout font
152 font.fontInfo().reduce(layoutfont);
154 pars_[pit].setFont(pos, font);
158 void Text::setInsetFont(BufferView const & bv, pit_type pit,
159 pos_type pos, Font const & font, bool toggleall)
161 Inset * const inset = pars_[pit].getInset(pos);
162 LASSERT(inset && inset->noFontChange(), /**/);
164 CursorSlice::idx_type endidx = inset->nargs();
165 for (CursorSlice cs(*inset); cs.idx() != endidx; ++cs.idx()) {
166 Text * text = cs.text();
168 // last position of the cell
169 CursorSlice cellend = cs;
170 cellend.pit() = cellend.lastpit();
171 cellend.pos() = cellend.lastpos();
172 text->setFont(bv, cs, cellend, font, toggleall);
178 // return past-the-last paragraph influenced by a layout change on pit
179 pit_type Text::undoSpan(pit_type pit)
181 pit_type end = paragraphs().size();
182 pit_type nextpit = pit + 1;
185 //because of parindents
186 if (!pars_[pit].getDepth())
187 return boost::next(nextpit);
188 //because of depth constrains
189 for (; nextpit != end; ++pit, ++nextpit) {
190 if (!pars_[pit].getDepth())
197 void Text::setLayout(Buffer const & buffer, pit_type start, pit_type end,
198 docstring const & layout)
200 LASSERT(start != end, /**/);
202 BufferParams const & bufparams = buffer.params();
203 Layout const & lyxlayout = bufparams.documentClass()[layout];
205 for (pit_type pit = start; pit != end; ++pit) {
206 Paragraph & par = pars_[pit];
207 par.applyLayout(lyxlayout);
208 if (lyxlayout.margintype == MARGIN_MANUAL)
209 par.setLabelWidthString(par.translateIfPossible(
210 lyxlayout.labelstring(), buffer.params()));
215 // set layout over selection and make a total rebreak of those paragraphs
216 void Text::setLayout(Cursor & cur, docstring const & layout)
218 LASSERT(this == cur.text(), /**/);
219 // special handling of new environment insets
220 BufferView & bv = cur.bv();
221 BufferParams const & params = bv.buffer().params();
222 Layout const & lyxlayout = params.documentClass()[layout];
223 if (lyxlayout.is_environment) {
224 // move everything in a new environment inset
225 LYXERR(Debug::DEBUG, "setting layout " << to_utf8(layout));
226 lyx::dispatch(FuncRequest(LFUN_LINE_BEGIN));
227 lyx::dispatch(FuncRequest(LFUN_LINE_END_SELECT));
228 lyx::dispatch(FuncRequest(LFUN_CUT));
229 Inset * inset = new InsetEnvironment(bv.buffer(), layout);
230 insertInset(cur, inset);
231 //inset->edit(cur, true);
232 //lyx::dispatch(FuncRequest(LFUN_PASTE));
236 pit_type start = cur.selBegin().pit();
237 pit_type end = cur.selEnd().pit() + 1;
238 pit_type undopit = undoSpan(end - 1);
239 recUndo(cur, start, undopit - 1);
240 setLayout(cur.buffer(), start, end, layout);
241 updateLabels(cur.buffer());
245 static bool changeDepthAllowed(Text::DEPTH_CHANGE type,
246 Paragraph const & par, int max_depth)
248 if (par.layout().labeltype == LABEL_BIBLIO)
250 int const depth = par.params().depth();
251 if (type == Text::INC_DEPTH && depth < max_depth)
253 if (type == Text::DEC_DEPTH && depth > 0)
259 bool Text::changeDepthAllowed(Cursor & cur, DEPTH_CHANGE type) const
261 LASSERT(this == cur.text(), /**/);
262 // this happens when selecting several cells in tabular (bug 2630)
263 if (cur.selBegin().idx() != cur.selEnd().idx())
266 pit_type const beg = cur.selBegin().pit();
267 pit_type const end = cur.selEnd().pit() + 1;
268 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
270 for (pit_type pit = beg; pit != end; ++pit) {
271 if (lyx::changeDepthAllowed(type, pars_[pit], max_depth))
273 max_depth = pars_[pit].getMaxDepthAfter();
279 void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type)
281 LASSERT(this == cur.text(), /**/);
282 pit_type const beg = cur.selBegin().pit();
283 pit_type const end = cur.selEnd().pit() + 1;
284 cur.recordUndoSelection();
285 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
287 for (pit_type pit = beg; pit != end; ++pit) {
288 Paragraph & par = pars_[pit];
289 if (lyx::changeDepthAllowed(type, par, max_depth)) {
290 int const depth = par.params().depth();
291 if (type == INC_DEPTH)
292 par.params().depth(depth + 1);
294 par.params().depth(depth - 1);
296 max_depth = par.getMaxDepthAfter();
298 // this handles the counter labels, and also fixes up
299 // depth values for follow-on (child) paragraphs
300 updateLabels(cur.buffer());
304 void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
306 LASSERT(this == cur.text(), /**/);
307 // Set the current_font
308 // Determine basis font
310 pit_type pit = cur.pit();
311 if (cur.pos() < pars_[pit].beginOfBody())
312 layoutfont = labelFont(cur.buffer(), pars_[pit]);
314 layoutfont = layoutFont(cur.buffer(), pit);
316 // Update current font
317 cur.real_current_font.update(font,
318 cur.buffer().params().language,
321 // Reduce to implicit settings
322 cur.current_font = cur.real_current_font;
323 cur.current_font.fontInfo().reduce(layoutfont);
324 // And resolve it completely
325 cur.real_current_font.fontInfo().realize(layoutfont);
327 // if there is no selection that's all we need to do
328 if (!cur.selection())
331 // Ok, we have a selection.
332 cur.recordUndoSelection();
334 setFont(cur.bv(), cur.selectionBegin().top(),
335 cur.selectionEnd().top(), font, toggleall);
339 void Text::setFont(BufferView const & bv, CursorSlice const & begin,
340 CursorSlice const & end, Font const & font,
343 Buffer const & buffer = bv.buffer();
345 // Don't use forwardChar here as ditend might have
346 // pos() == lastpos() and forwardChar would miss it.
347 // Can't use forwardPos either as this descends into
349 Language const * language = buffer.params().language;
350 for (CursorSlice dit = begin; dit != end; dit.forwardPos()) {
351 if (dit.pos() == dit.lastpos())
353 pit_type const pit = dit.pit();
354 pos_type const pos = dit.pos();
355 Inset * inset = pars_[pit].getInset(pos);
356 if (inset && inset->noFontChange()) {
357 // We need to propagate the font change to all
358 // text cells of the inset (bug 1973).
359 // FIXME: This should change, see documentation
360 // of noFontChange in Inset.h
361 setInsetFont(bv, pit, pos, font, toggleall);
363 TextMetrics const & tm = bv.textMetrics(this);
364 Font f = tm.displayFont(pit, pos);
365 f.update(font, language, toggleall);
366 setCharFont(buffer, pit, pos, f, tm.font_);
371 bool Text::cursorTop(Cursor & cur)
373 LASSERT(this == cur.text(), /**/);
374 return setCursor(cur, 0, 0);
378 bool Text::cursorBottom(Cursor & cur)
380 LASSERT(this == cur.text(), /**/);
381 return setCursor(cur, cur.lastpit(), boost::prior(paragraphs().end())->size());
385 void Text::toggleFree(Cursor & cur, Font const & font, bool toggleall)
387 LASSERT(this == cur.text(), /**/);
388 // If the mask is completely neutral, tell user
389 if (font.fontInfo() == ignore_font && font.language() == ignore_language) {
390 // Could only happen with user style
391 cur.message(_("No font change defined."));
395 // Try implicit word selection
396 // If there is a change in the language the implicit word selection
398 CursorSlice resetCursor = cur.top();
399 bool implicitSelection =
400 font.language() == ignore_language
401 && font.fontInfo().number() == FONT_IGNORE
402 && selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
405 setFont(cur, font, toggleall);
407 // Implicit selections are cleared afterwards
408 // and cursor is set to the original position.
409 if (implicitSelection) {
410 cur.clearSelection();
411 cur.top() = resetCursor;
417 docstring Text::getStringToIndex(Cursor const & cur)
419 LASSERT(this == cur.text(), /**/);
422 return cur.selectionAsString(false);
424 // Try implicit word selection. If there is a change
425 // in the language the implicit word selection is
428 selectWord(tmpcur, PREVIOUS_WORD);
430 if (!tmpcur.selection())
431 cur.message(_("Nothing to index!"));
432 else if (tmpcur.selBegin().pit() != tmpcur.selEnd().pit())
433 cur.message(_("Cannot index more than one paragraph!"));
435 return tmpcur.selectionAsString(false);
441 void Text::setParagraphs(Cursor & cur, docstring arg, bool merge)
443 LASSERT(cur.text(), /**/);
444 // make sure that the depth behind the selection are restored, too
445 pit_type undopit = undoSpan(cur.selEnd().pit());
446 recUndo(cur, cur.selBegin().pit(), undopit - 1);
449 string const argument = to_utf8(arg);
450 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
452 Paragraph & par = pars_[pit];
453 ParagraphParameters params = par.params();
454 params.read(argument, merge);
455 par.params().apply(params, par.layout());
460 //FIXME This is a little redundant now, but it's probably worth keeping,
461 //especially if we're going to go away from using serialization internally
463 void Text::setParagraphs(Cursor & cur, ParagraphParameters const & p)
465 LASSERT(cur.text(), /**/);
466 // make sure that the depth behind the selection are restored, too
467 pit_type undopit = undoSpan(cur.selEnd().pit());
468 recUndo(cur, cur.selBegin().pit(), undopit - 1);
470 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
472 Paragraph & par = pars_[pit];
473 par.params().apply(p, par.layout());
478 // this really should just insert the inset and not move the cursor.
479 void Text::insertInset(Cursor & cur, Inset * inset)
481 LASSERT(this == cur.text(), /**/);
482 LASSERT(inset, /**/);
483 cur.paragraph().insertInset(cur.pos(), inset, cur.current_font,
484 Change(cur.buffer().params().trackChanges
485 ? Change::INSERTED : Change::UNCHANGED));
489 // needed to insert the selection
490 void Text::insertStringAsLines(Cursor & cur, docstring const & str)
492 cur.buffer().insertStringAsLines(pars_, cur.pit(), cur.pos(),
493 cur.current_font, str, autoBreakRows_);
497 // turn double CR to single CR, others are converted into one
498 // blank. Then insertStringAsLines is called
499 void Text::insertStringAsParagraphs(Cursor & cur, docstring const & str)
501 docstring linestr = str;
502 bool newline_inserted = false;
504 for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
505 if (linestr[i] == '\n') {
506 if (newline_inserted) {
507 // we know that \r will be ignored by
508 // insertStringAsLines. Of course, it is a dirty
509 // trick, but it works...
510 linestr[i - 1] = '\r';
514 newline_inserted = true;
516 } else if (isPrintable(linestr[i])) {
517 newline_inserted = false;
520 insertStringAsLines(cur, linestr);
524 bool Text::setCursor(Cursor & cur, pit_type par, pos_type pos,
525 bool setfont, bool boundary)
527 TextMetrics const & tm = cur.bv().textMetrics(this);
528 bool const update_needed = !tm.contains(par);
530 setCursorIntern(cur, par, pos, setfont, boundary);
531 return cur.bv().checkDepm(cur, old) || update_needed;
535 void Text::setCursor(CursorSlice & cur, pit_type par, pos_type pos)
537 LASSERT(par != int(paragraphs().size()), /**/);
541 // now some strict checking
542 Paragraph & para = getPar(par);
544 // None of these should happen, but we're scaredy-cats
546 lyxerr << "dont like -1" << endl;
547 LASSERT(false, /**/);
550 if (pos > para.size()) {
551 lyxerr << "dont like 1, pos: " << pos
552 << " size: " << para.size()
553 << " par: " << par << endl;
554 LASSERT(false, /**/);
559 void Text::setCursorIntern(Cursor & cur,
560 pit_type par, pos_type pos, bool setfont, bool boundary)
562 LASSERT(this == cur.text(), /**/);
563 cur.boundary(boundary);
564 setCursor(cur.top(), par, pos);
566 cur.setCurrentFont();
570 bool Text::checkAndActivateInset(Cursor & cur, bool front)
574 if (front && cur.pos() == cur.lastpos())
576 if (!front && cur.pos() == 0)
578 Inset * inset = front ? cur.nextInset() : cur.prevInset();
579 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
582 * Apparently, when entering an inset we are expected to be positioned
583 * *before* it in the containing paragraph, regardless of the direction
584 * from which we are entering. Otherwise, cursor placement goes awry,
585 * and when we exit from the beginning, we'll be placed *after* the
590 inset->edit(cur, front);
595 bool Text::checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool movingLeft)
601 if (cur.pos() == cur.lastpos())
603 Paragraph & par = cur.paragraph();
604 Inset * inset = par.isInset(cur.pos()) ? par.getInset(cur.pos()) : 0;
605 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
607 inset->edit(cur, movingForward,
608 movingLeft ? Inset::ENTRY_DIRECTION_RIGHT : Inset::ENTRY_DIRECTION_LEFT);
613 bool Text::cursorBackward(Cursor & cur)
615 // Tell BufferView to test for FitCursor in any case!
616 cur.updateFlags(Update::FitCursor);
618 // not at paragraph start?
620 // if on right side of boundary (i.e. not at paragraph end, but line end)
621 // -> skip it, i.e. set boundary to true, i.e. go only logically left
622 // there are some exceptions to ignore this: lineseps, newlines, spaces
624 // some effectless debug code to see the values in the debugger
625 bool bound = cur.boundary();
626 int rowpos = cur.textRow().pos();
628 bool sep = cur.paragraph().isSeparator(cur.pos() - 1);
629 bool newline = cur.paragraph().isNewline(cur.pos() - 1);
630 bool linesep = cur.paragraph().isLineSeparator(cur.pos() - 1);
632 if (!cur.boundary() &&
633 cur.textRow().pos() == cur.pos() &&
634 !cur.paragraph().isLineSeparator(cur.pos() - 1) &&
635 !cur.paragraph().isNewline(cur.pos() - 1) &&
636 !cur.paragraph().isSeparator(cur.pos() - 1)) {
637 return setCursor(cur, cur.pit(), cur.pos(), true, true);
640 // go left and try to enter inset
641 if (checkAndActivateInset(cur, false))
644 // normal character left
645 return setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
648 // move to the previous paragraph or do nothing
650 return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size(), true, false);
655 bool Text::cursorVisLeft(Cursor & cur, bool skip_inset)
657 Cursor temp_cur = cur;
658 temp_cur.posVisLeft(skip_inset);
659 if (temp_cur.depth() > cur.depth()) {
663 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
664 true, temp_cur.boundary());
668 bool Text::cursorVisRight(Cursor & cur, bool skip_inset)
670 Cursor temp_cur = cur;
671 temp_cur.posVisRight(skip_inset);
672 if (temp_cur.depth() > cur.depth()) {
676 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
677 true, temp_cur.boundary());
681 bool Text::cursorForward(Cursor & cur)
683 // Tell BufferView to test for FitCursor in any case!
684 cur.updateFlags(Update::FitCursor);
686 // not at paragraph end?
687 if (cur.pos() != cur.lastpos()) {
688 // in front of editable inset, i.e. jump into it?
689 if (checkAndActivateInset(cur, true))
692 TextMetrics const & tm = cur.bv().textMetrics(this);
693 // if left of boundary -> just jump to right side
694 // but for RTL boundaries don't, because: abc|DDEEFFghi -> abcDDEEF|Fghi
695 if (cur.boundary() && !tm.isRTLBoundary(cur.pit(), cur.pos()))
696 return setCursor(cur, cur.pit(), cur.pos(), true, false);
698 // next position is left of boundary,
699 // but go to next line for special cases like space, newline, linesep
701 // some effectless debug code to see the values in the debugger
702 int endpos = cur.textRow().endpos();
703 int lastpos = cur.lastpos();
705 bool linesep = cur.paragraph().isLineSeparator(cur.pos());
706 bool newline = cur.paragraph().isNewline(cur.pos());
707 bool sep = cur.paragraph().isSeparator(cur.pos());
708 if (cur.pos() != cur.lastpos()) {
709 bool linesep2 = cur.paragraph().isLineSeparator(cur.pos()+1);
710 bool newline2 = cur.paragraph().isNewline(cur.pos()+1);
711 bool sep2 = cur.paragraph().isSeparator(cur.pos()+1);
714 if (cur.textRow().endpos() == cur.pos() + 1 &&
715 cur.textRow().endpos() != cur.lastpos() &&
716 !cur.paragraph().isNewline(cur.pos()) &&
717 !cur.paragraph().isLineSeparator(cur.pos()) &&
718 !cur.paragraph().isSeparator(cur.pos())) {
719 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
722 // in front of RTL boundary? Stay on this side of the boundary because:
723 // ab|cDDEEFFghi -> abc|DDEEFFghi
724 if (tm.isRTLBoundary(cur.pit(), cur.pos() + 1))
725 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
728 return setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
731 // move to next paragraph
732 if (cur.pit() != cur.lastpit())
733 return setCursor(cur, cur.pit() + 1, 0, true, false);
738 bool Text::cursorUpParagraph(Cursor & cur)
740 bool updated = false;
742 updated = setCursor(cur, cur.pit(), 0);
743 else if (cur.pit() != 0)
744 updated = setCursor(cur, cur.pit() - 1, 0);
749 bool Text::cursorDownParagraph(Cursor & cur)
751 bool updated = false;
752 if (cur.pit() != cur.lastpit())
753 updated = setCursor(cur, cur.pit() + 1, 0);
755 updated = setCursor(cur, cur.pit(), cur.lastpos());
760 // fix the cursor `cur' after a characters has been deleted at `where'
761 // position. Called by deleteEmptyParagraphMechanism
762 void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
764 // Do nothing if cursor is not in the paragraph where the
766 if (cur.pit() != where.pit())
769 // If cursor position is after the deletion place update it
770 if (cur.pos() > where.pos())
773 // Check also if we don't want to set the cursor on a spot behind the
774 // pagragraph because we erased the last character.
775 if (cur.pos() > cur.lastpos())
776 cur.pos() = cur.lastpos();
780 bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
781 Cursor & old, bool & need_anchor_change)
783 //LYXERR(Debug::DEBUG, "DEPM: cur:\n" << cur << "old:\n" << old);
785 Paragraph & oldpar = old.paragraph();
787 // We allow all kinds of "mumbo-jumbo" when freespacing.
788 if (oldpar.isFreeSpacing())
791 /* Ok I'll put some comments here about what is missing.
792 There are still some small problems that can lead to
793 double spaces stored in the document file or space at
794 the beginning of paragraphs(). This happens if you have
795 the cursor between to spaces and then save. Or if you
796 cut and paste and the selection have a space at the
797 beginning and then save right after the paste. (Lgb)
800 // If old.pos() == 0 and old.pos()(1) == LineSeparator
801 // delete the LineSeparator.
804 // If old.pos() == 1 and old.pos()(0) == LineSeparator
805 // delete the LineSeparator.
808 bool const same_inset = &old.inset() == &cur.inset();
809 bool const same_par = same_inset && old.pit() == cur.pit();
810 bool const same_par_pos = same_par && old.pos() == cur.pos();
812 // If the chars around the old cursor were spaces, delete one of them.
814 // Only if the cursor has really moved.
816 && old.pos() < oldpar.size()
817 && oldpar.isLineSeparator(old.pos())
818 && oldpar.isLineSeparator(old.pos() - 1)
819 && !oldpar.isDeleted(old.pos() - 1)
820 && !oldpar.isDeleted(old.pos())) {
821 oldpar.eraseChar(old.pos() - 1, cur.buffer().params().trackChanges);
822 // FIXME: This will not work anymore when we have multiple views of the same buffer
823 // In this case, we will have to correct also the cursors held by
824 // other bufferviews. It will probably be easier to do that in a more
825 // automated way in CursorSlice code. (JMarc 26/09/2001)
826 // correct all cursor parts
828 fixCursorAfterDelete(cur.top(), old.top());
829 need_anchor_change = true;
835 // only do our magic if we changed paragraph
839 // don't delete anything if this is the ONLY paragraph!
840 if (old.lastpit() == 0)
843 // Do not delete empty paragraphs with keepempty set.
844 if (oldpar.allowEmpty())
847 if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
849 old.recordUndo(ATOMIC_UNDO,
850 max(old.pit() - 1, pit_type(0)),
851 min(old.pit() + 1, old.lastpit()));
852 ParagraphList & plist = old.text()->paragraphs();
853 bool const soa = oldpar.params().startOfAppendix();
854 plist.erase(boost::next(plist.begin(), old.pit()));
855 // do not lose start of appendix marker (bug 4212)
856 if (soa && old.pit() < pit_type(plist.size()))
857 plist[old.pit()].params().startOfAppendix(true);
859 // see #warning (FIXME?) above
860 if (cur.depth() >= old.depth()) {
861 CursorSlice & curslice = cur[old.depth() - 1];
862 if (&curslice.inset() == &old.inset()
863 && curslice.pit() > old.pit()) {
865 // since a paragraph has been deleted, all the
866 // insets after `old' have been copied and
867 // their address has changed. Therefore we
868 // need to `regenerate' cur. (JMarc)
869 cur.updateInsets(&(cur.bottom().inset()));
870 need_anchor_change = true;
876 if (oldpar.stripLeadingSpaces(cur.buffer().params().trackChanges)) {
877 need_anchor_change = true;
878 // We return true here because the Paragraph contents changed and
879 // we need a redraw before further action is processed.
887 void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges)
889 LASSERT(first >= 0 && first <= last && last < (int) pars_.size(), /**/);
891 for (pit_type pit = first; pit <= last; ++pit) {
892 Paragraph & par = pars_[pit];
894 // We allow all kinds of "mumbo-jumbo" when freespacing.
895 if (par.isFreeSpacing())
898 for (pos_type pos = 1; pos < par.size(); ++pos) {
899 if (par.isLineSeparator(pos) && par.isLineSeparator(pos - 1)
900 && !par.isDeleted(pos - 1)) {
901 if (par.eraseChar(pos - 1, trackChanges)) {
907 // don't delete anything if this is the only remaining paragraph within the given range
908 // note: Text::acceptOrRejectChanges() sets the cursor to 'first' after calling DEPM
912 // don't delete empty paragraphs with keepempty set
913 if (par.allowEmpty())
916 if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) {
917 pars_.erase(boost::next(pars_.begin(), pit));
923 par.stripLeadingSpaces(trackChanges);
928 void Text::recUndo(Cursor & cur, pit_type first, pit_type last) const
930 cur.recordUndo(ATOMIC_UNDO, first, last);
934 void Text::recUndo(Cursor & cur, pit_type par) const
936 cur.recordUndo(ATOMIC_UNDO, par, par);