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 Inset const * inset = pars_[pit].inInset();
94 InsetCollapsable const * icp = inset? inset->asInsetCollapsable() : 0;
97 FontInfo icf = icp->getLayout().font();
102 FontInfo font = layout.font;
103 // Realize with the fonts of lesser depth.
104 //font.realize(outerFont(pit, paragraphs()));
105 font.realize(buffer.params().getFont().fontInfo());
111 // Note that this is supposed to return a fully realized font.
112 FontInfo Text::labelFont(Buffer const & buffer, Paragraph const & par) const
114 Layout const & layout = par.layout();
116 if (!par.getDepth()) {
117 FontInfo lf = layout.reslabelfont;
118 // In case the default family has been customized
119 if (layout.labelfont.family() == INHERIT_FAMILY)
120 lf.setFamily(buffer.params().getFont().fontInfo().family());
121 // FIXME As above....
122 Inset const * inset = par.inInset();
123 InsetCollapsable const * icp = inset? inset->asInsetCollapsable() : 0;
126 FontInfo icf = icp->getLayout().labelfont();
131 FontInfo font = layout.labelfont;
132 // Realize with the fonts of lesser depth.
133 font.realize(buffer.params().getFont().fontInfo());
139 void Text::setCharFont(Buffer const & buffer, pit_type pit,
140 pos_type pos, Font const & fnt, Font const & display_font)
143 Layout const & layout = pars_[pit].layout();
145 // Get concrete layout font to reduce against
148 if (pos < pars_[pit].beginOfBody())
149 layoutfont = layout.labelfont;
151 layoutfont = layout.font;
153 // Realize against environment font information
154 if (pars_[pit].getDepth()) {
156 while (!layoutfont.resolved() &&
157 tp != pit_type(paragraphs().size()) &&
158 pars_[tp].getDepth()) {
159 tp = outerHook(tp, paragraphs());
160 if (tp != pit_type(paragraphs().size()))
161 layoutfont.realize(pars_[tp].layout().font);
165 // Inside inset, apply the inset's font attributes if any
167 if (!isMainText(buffer))
168 layoutfont.realize(display_font.fontInfo());
170 layoutfont.realize(buffer.params().getFont().fontInfo());
172 // Now, reduce font against full layout font
173 font.fontInfo().reduce(layoutfont);
175 pars_[pit].setFont(pos, font);
179 void Text::setInsetFont(BufferView const & bv, pit_type pit,
180 pos_type pos, Font const & font, bool toggleall)
182 Inset * const inset = pars_[pit].getInset(pos);
183 LASSERT(inset && inset->noFontChange(), /**/);
185 CursorSlice::idx_type endidx = inset->nargs();
186 for (CursorSlice cs(*inset); cs.idx() != endidx; ++cs.idx()) {
187 Text * text = cs.text();
189 // last position of the cell
190 CursorSlice cellend = cs;
191 cellend.pit() = cellend.lastpit();
192 cellend.pos() = cellend.lastpos();
193 text->setFont(bv, cs, cellend, font, toggleall);
199 // return past-the-last paragraph influenced by a layout change on pit
200 pit_type Text::undoSpan(pit_type pit)
202 pit_type end = paragraphs().size();
203 pit_type nextpit = pit + 1;
206 //because of parindents
207 if (!pars_[pit].getDepth())
208 return boost::next(nextpit);
209 //because of depth constrains
210 for (; nextpit != end; ++pit, ++nextpit) {
211 if (!pars_[pit].getDepth())
218 void Text::setLayout(Buffer const & buffer, pit_type start, pit_type end,
219 docstring const & layout)
221 LASSERT(start != end, /**/);
223 BufferParams const & bufparams = buffer.params();
224 Layout const & lyxlayout = bufparams.documentClass()[layout];
226 for (pit_type pit = start; pit != end; ++pit) {
227 Paragraph & par = pars_[pit];
228 par.applyLayout(lyxlayout);
229 if (lyxlayout.margintype == MARGIN_MANUAL)
230 par.setLabelWidthString(par.translateIfPossible(
231 lyxlayout.labelstring(), buffer.params()));
236 // set layout over selection and make a total rebreak of those paragraphs
237 void Text::setLayout(Cursor & cur, docstring const & layout)
239 LASSERT(this == cur.text(), /**/);
241 pit_type start = cur.selBegin().pit();
242 pit_type end = cur.selEnd().pit() + 1;
243 pit_type undopit = undoSpan(end - 1);
244 recUndo(cur, start, undopit - 1);
245 setLayout(cur.buffer(), start, end, layout);
246 updateLabels(cur.buffer());
250 static bool changeDepthAllowed(Text::DEPTH_CHANGE type,
251 Paragraph const & par, int max_depth)
253 if (par.layout().labeltype == LABEL_BIBLIO)
255 int const depth = par.params().depth();
256 if (type == Text::INC_DEPTH && depth < max_depth)
258 if (type == Text::DEC_DEPTH && depth > 0)
264 bool Text::changeDepthAllowed(Cursor & cur, DEPTH_CHANGE type) const
266 LASSERT(this == cur.text(), /**/);
267 // this happens when selecting several cells in tabular (bug 2630)
268 if (cur.selBegin().idx() != cur.selEnd().idx())
271 pit_type const beg = cur.selBegin().pit();
272 pit_type const end = cur.selEnd().pit() + 1;
273 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
275 for (pit_type pit = beg; pit != end; ++pit) {
276 if (lyx::changeDepthAllowed(type, pars_[pit], max_depth))
278 max_depth = pars_[pit].getMaxDepthAfter();
284 void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type)
286 LASSERT(this == cur.text(), /**/);
287 pit_type const beg = cur.selBegin().pit();
288 pit_type const end = cur.selEnd().pit() + 1;
289 cur.recordUndoSelection();
290 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
292 for (pit_type pit = beg; pit != end; ++pit) {
293 Paragraph & par = pars_[pit];
294 if (lyx::changeDepthAllowed(type, par, max_depth)) {
295 int const depth = par.params().depth();
296 if (type == INC_DEPTH)
297 par.params().depth(depth + 1);
299 par.params().depth(depth - 1);
301 max_depth = par.getMaxDepthAfter();
303 // this handles the counter labels, and also fixes up
304 // depth values for follow-on (child) paragraphs
305 updateLabels(cur.buffer());
309 void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
311 LASSERT(this == cur.text(), /**/);
312 // Set the current_font
313 // Determine basis font
315 pit_type pit = cur.pit();
316 if (cur.pos() < pars_[pit].beginOfBody())
317 layoutfont = labelFont(cur.buffer(), pars_[pit]);
319 layoutfont = layoutFont(cur.buffer(), pit);
321 // Update current font
322 cur.real_current_font.update(font,
323 cur.buffer().params().language,
326 // Reduce to implicit settings
327 cur.current_font = cur.real_current_font;
328 cur.current_font.fontInfo().reduce(layoutfont);
329 // And resolve it completely
330 cur.real_current_font.fontInfo().realize(layoutfont);
332 // if there is no selection that's all we need to do
333 if (!cur.selection())
336 // Ok, we have a selection.
337 cur.recordUndoSelection();
339 setFont(cur.bv(), cur.selectionBegin().top(),
340 cur.selectionEnd().top(), font, toggleall);
344 void Text::setFont(BufferView const & bv, CursorSlice const & begin,
345 CursorSlice const & end, Font const & font,
348 Buffer const & buffer = bv.buffer();
350 // Don't use forwardChar here as ditend might have
351 // pos() == lastpos() and forwardChar would miss it.
352 // Can't use forwardPos either as this descends into
354 Language const * language = buffer.params().language;
355 for (CursorSlice dit = begin; dit != end; dit.forwardPos()) {
356 if (dit.pos() == dit.lastpos())
358 pit_type const pit = dit.pit();
359 pos_type const pos = dit.pos();
360 Inset * inset = pars_[pit].getInset(pos);
361 if (inset && inset->noFontChange()) {
362 // We need to propagate the font change to all
363 // text cells of the inset (bug 1973).
364 // FIXME: This should change, see documentation
365 // of noFontChange in Inset.h
366 setInsetFont(bv, pit, pos, font, toggleall);
368 TextMetrics const & tm = bv.textMetrics(this);
369 Font f = tm.displayFont(pit, pos);
370 f.update(font, language, toggleall);
371 setCharFont(buffer, pit, pos, f, tm.font_);
376 bool Text::cursorTop(Cursor & cur)
378 LASSERT(this == cur.text(), /**/);
379 return setCursor(cur, 0, 0);
383 bool Text::cursorBottom(Cursor & cur)
385 LASSERT(this == cur.text(), /**/);
386 return setCursor(cur, cur.lastpit(), boost::prior(paragraphs().end())->size());
390 void Text::toggleFree(Cursor & cur, Font const & font, bool toggleall)
392 LASSERT(this == cur.text(), /**/);
393 // If the mask is completely neutral, tell user
394 if (font.fontInfo() == ignore_font && font.language() == ignore_language) {
395 // Could only happen with user style
396 cur.message(_("No font change defined."));
400 // Try implicit word selection
401 // If there is a change in the language the implicit word selection
403 CursorSlice resetCursor = cur.top();
404 bool implicitSelection =
405 font.language() == ignore_language
406 && font.fontInfo().number() == FONT_IGNORE
407 && selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
410 setFont(cur, font, toggleall);
412 // Implicit selections are cleared afterwards
413 // and cursor is set to the original position.
414 if (implicitSelection) {
415 cur.clearSelection();
416 cur.top() = resetCursor;
422 docstring Text::getStringToIndex(Cursor const & cur)
424 LASSERT(this == cur.text(), /**/);
427 return cur.selectionAsString(false);
429 // Try implicit word selection. If there is a change
430 // in the language the implicit word selection is
433 selectWord(tmpcur, PREVIOUS_WORD);
435 if (!tmpcur.selection())
436 cur.message(_("Nothing to index!"));
437 else if (tmpcur.selBegin().pit() != tmpcur.selEnd().pit())
438 cur.message(_("Cannot index more than one paragraph!"));
440 return tmpcur.selectionAsString(false);
446 void Text::setParagraphs(Cursor & cur, docstring arg, bool merge)
448 LASSERT(cur.text(), /**/);
449 // make sure that the depth behind the selection are restored, too
450 pit_type undopit = undoSpan(cur.selEnd().pit());
451 recUndo(cur, cur.selBegin().pit(), undopit - 1);
454 string const argument = to_utf8(arg);
455 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
457 Paragraph & par = pars_[pit];
458 ParagraphParameters params = par.params();
459 params.read(argument, merge);
460 par.params().apply(params, par.layout());
465 //FIXME This is a little redundant now, but it's probably worth keeping,
466 //especially if we're going to go away from using serialization internally
468 void Text::setParagraphs(Cursor & cur, ParagraphParameters const & p)
470 LASSERT(cur.text(), /**/);
471 // make sure that the depth behind the selection are restored, too
472 pit_type undopit = undoSpan(cur.selEnd().pit());
473 recUndo(cur, cur.selBegin().pit(), undopit - 1);
475 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
477 Paragraph & par = pars_[pit];
478 par.params().apply(p, par.layout());
483 // this really should just insert the inset and not move the cursor.
484 void Text::insertInset(Cursor & cur, Inset * inset)
486 LASSERT(this == cur.text(), /**/);
487 LASSERT(inset, /**/);
488 cur.paragraph().insertInset(cur.pos(), inset, cur.current_font,
489 Change(cur.buffer().params().trackChanges
490 ? Change::INSERTED : Change::UNCHANGED));
494 // needed to insert the selection
495 void Text::insertStringAsLines(Cursor & cur, docstring const & str)
497 cur.buffer().insertStringAsLines(pars_, cur.pit(), cur.pos(),
498 cur.current_font, str, autoBreakRows_);
502 // turn double CR to single CR, others are converted into one
503 // blank. Then insertStringAsLines is called
504 void Text::insertStringAsParagraphs(Cursor & cur, docstring const & str)
506 docstring linestr = str;
507 bool newline_inserted = false;
509 for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
510 if (linestr[i] == '\n') {
511 if (newline_inserted) {
512 // we know that \r will be ignored by
513 // insertStringAsLines. Of course, it is a dirty
514 // trick, but it works...
515 linestr[i - 1] = '\r';
519 newline_inserted = true;
521 } else if (isPrintable(linestr[i])) {
522 newline_inserted = false;
525 insertStringAsLines(cur, linestr);
529 bool Text::setCursor(Cursor & cur, pit_type par, pos_type pos,
530 bool setfont, bool boundary)
532 TextMetrics const & tm = cur.bv().textMetrics(this);
533 bool const update_needed = !tm.contains(par);
535 setCursorIntern(cur, par, pos, setfont, boundary);
536 return cur.bv().checkDepm(cur, old) || update_needed;
540 void Text::setCursor(CursorSlice & cur, pit_type par, pos_type pos)
542 LASSERT(par != int(paragraphs().size()), /**/);
546 // now some strict checking
547 Paragraph & para = getPar(par);
549 // None of these should happen, but we're scaredy-cats
551 lyxerr << "dont like -1" << endl;
552 LASSERT(false, /**/);
555 if (pos > para.size()) {
556 lyxerr << "dont like 1, pos: " << pos
557 << " size: " << para.size()
558 << " par: " << par << endl;
559 LASSERT(false, /**/);
564 void Text::setCursorIntern(Cursor & cur,
565 pit_type par, pos_type pos, bool setfont, bool boundary)
567 LASSERT(this == cur.text(), /**/);
568 cur.boundary(boundary);
569 setCursor(cur.top(), par, pos);
571 cur.setCurrentFont();
575 bool Text::checkAndActivateInset(Cursor & cur, bool front)
579 if (front && cur.pos() == cur.lastpos())
581 if (!front && cur.pos() == 0)
583 Inset * inset = front ? cur.nextInset() : cur.prevInset();
584 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
587 * Apparently, when entering an inset we are expected to be positioned
588 * *before* it in the containing paragraph, regardless of the direction
589 * from which we are entering. Otherwise, cursor placement goes awry,
590 * and when we exit from the beginning, we'll be placed *after* the
595 inset->edit(cur, front);
600 bool Text::checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool movingLeft)
606 if (cur.pos() == cur.lastpos())
608 Paragraph & par = cur.paragraph();
609 Inset * inset = par.isInset(cur.pos()) ? par.getInset(cur.pos()) : 0;
610 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
612 inset->edit(cur, movingForward,
613 movingLeft ? Inset::ENTRY_DIRECTION_RIGHT : Inset::ENTRY_DIRECTION_LEFT);
618 bool Text::cursorBackward(Cursor & cur)
620 // Tell BufferView to test for FitCursor in any case!
621 cur.updateFlags(Update::FitCursor);
623 // not at paragraph start?
625 // if on right side of boundary (i.e. not at paragraph end, but line end)
626 // -> skip it, i.e. set boundary to true, i.e. go only logically left
627 // there are some exceptions to ignore this: lineseps, newlines, spaces
629 // some effectless debug code to see the values in the debugger
630 bool bound = cur.boundary();
631 int rowpos = cur.textRow().pos();
633 bool sep = cur.paragraph().isSeparator(cur.pos() - 1);
634 bool newline = cur.paragraph().isNewline(cur.pos() - 1);
635 bool linesep = cur.paragraph().isLineSeparator(cur.pos() - 1);
637 if (!cur.boundary() &&
638 cur.textRow().pos() == cur.pos() &&
639 !cur.paragraph().isLineSeparator(cur.pos() - 1) &&
640 !cur.paragraph().isNewline(cur.pos() - 1) &&
641 !cur.paragraph().isSeparator(cur.pos() - 1)) {
642 return setCursor(cur, cur.pit(), cur.pos(), true, true);
645 // go left and try to enter inset
646 if (checkAndActivateInset(cur, false))
649 // normal character left
650 return setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
653 // move to the previous paragraph or do nothing
655 return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size(), true, false);
660 bool Text::cursorVisLeft(Cursor & cur, bool skip_inset)
662 Cursor temp_cur = cur;
663 temp_cur.posVisLeft(skip_inset);
664 if (temp_cur.depth() > cur.depth()) {
668 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
669 true, temp_cur.boundary());
673 bool Text::cursorVisRight(Cursor & cur, bool skip_inset)
675 Cursor temp_cur = cur;
676 temp_cur.posVisRight(skip_inset);
677 if (temp_cur.depth() > cur.depth()) {
681 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
682 true, temp_cur.boundary());
686 bool Text::cursorForward(Cursor & cur)
688 // Tell BufferView to test for FitCursor in any case!
689 cur.updateFlags(Update::FitCursor);
691 // not at paragraph end?
692 if (cur.pos() != cur.lastpos()) {
693 // in front of editable inset, i.e. jump into it?
694 if (checkAndActivateInset(cur, true))
697 TextMetrics const & tm = cur.bv().textMetrics(this);
698 // if left of boundary -> just jump to right side
699 // but for RTL boundaries don't, because: abc|DDEEFFghi -> abcDDEEF|Fghi
700 if (cur.boundary() && !tm.isRTLBoundary(cur.pit(), cur.pos()))
701 return setCursor(cur, cur.pit(), cur.pos(), true, false);
703 // next position is left of boundary,
704 // but go to next line for special cases like space, newline, linesep
706 // some effectless debug code to see the values in the debugger
707 int endpos = cur.textRow().endpos();
708 int lastpos = cur.lastpos();
710 bool linesep = cur.paragraph().isLineSeparator(cur.pos());
711 bool newline = cur.paragraph().isNewline(cur.pos());
712 bool sep = cur.paragraph().isSeparator(cur.pos());
713 if (cur.pos() != cur.lastpos()) {
714 bool linesep2 = cur.paragraph().isLineSeparator(cur.pos()+1);
715 bool newline2 = cur.paragraph().isNewline(cur.pos()+1);
716 bool sep2 = cur.paragraph().isSeparator(cur.pos()+1);
719 if (cur.textRow().endpos() == cur.pos() + 1 &&
720 cur.textRow().endpos() != cur.lastpos() &&
721 !cur.paragraph().isNewline(cur.pos()) &&
722 !cur.paragraph().isLineSeparator(cur.pos()) &&
723 !cur.paragraph().isSeparator(cur.pos())) {
724 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
727 // in front of RTL boundary? Stay on this side of the boundary because:
728 // ab|cDDEEFFghi -> abc|DDEEFFghi
729 if (tm.isRTLBoundary(cur.pit(), cur.pos() + 1))
730 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
733 return setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
736 // move to next paragraph
737 if (cur.pit() != cur.lastpit())
738 return setCursor(cur, cur.pit() + 1, 0, true, false);
743 bool Text::cursorUpParagraph(Cursor & cur)
745 bool updated = false;
747 updated = setCursor(cur, cur.pit(), 0);
748 else if (cur.pit() != 0)
749 updated = setCursor(cur, cur.pit() - 1, 0);
754 bool Text::cursorDownParagraph(Cursor & cur)
756 bool updated = false;
757 if (cur.pit() != cur.lastpit())
758 updated = setCursor(cur, cur.pit() + 1, 0);
760 updated = setCursor(cur, cur.pit(), cur.lastpos());
765 // fix the cursor `cur' after a characters has been deleted at `where'
766 // position. Called by deleteEmptyParagraphMechanism
767 void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
769 // Do nothing if cursor is not in the paragraph where the
771 if (cur.pit() != where.pit())
774 // If cursor position is after the deletion place update it
775 if (cur.pos() > where.pos())
778 // Check also if we don't want to set the cursor on a spot behind the
779 // pagragraph because we erased the last character.
780 if (cur.pos() > cur.lastpos())
781 cur.pos() = cur.lastpos();
785 bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
786 Cursor & old, bool & need_anchor_change)
788 //LYXERR(Debug::DEBUG, "DEPM: cur:\n" << cur << "old:\n" << old);
790 Paragraph & oldpar = old.paragraph();
792 // We allow all kinds of "mumbo-jumbo" when freespacing.
793 if (oldpar.isFreeSpacing())
796 /* Ok I'll put some comments here about what is missing.
797 There are still some small problems that can lead to
798 double spaces stored in the document file or space at
799 the beginning of paragraphs(). This happens if you have
800 the cursor between to spaces and then save. Or if you
801 cut and paste and the selection have a space at the
802 beginning and then save right after the paste. (Lgb)
805 // If old.pos() == 0 and old.pos()(1) == LineSeparator
806 // delete the LineSeparator.
809 // If old.pos() == 1 and old.pos()(0) == LineSeparator
810 // delete the LineSeparator.
813 bool const same_inset = &old.inset() == &cur.inset();
814 bool const same_par = same_inset && old.pit() == cur.pit();
815 bool const same_par_pos = same_par && old.pos() == cur.pos();
817 // If the chars around the old cursor were spaces, delete one of them.
819 // Only if the cursor has really moved.
821 && old.pos() < oldpar.size()
822 && oldpar.isLineSeparator(old.pos())
823 && oldpar.isLineSeparator(old.pos() - 1)
824 && !oldpar.isDeleted(old.pos() - 1)
825 && !oldpar.isDeleted(old.pos())) {
826 oldpar.eraseChar(old.pos() - 1, cur.buffer().params().trackChanges);
827 // FIXME: This will not work anymore when we have multiple views of the same buffer
828 // In this case, we will have to correct also the cursors held by
829 // other bufferviews. It will probably be easier to do that in a more
830 // automated way in CursorSlice code. (JMarc 26/09/2001)
831 // correct all cursor parts
833 fixCursorAfterDelete(cur.top(), old.top());
834 need_anchor_change = true;
840 // only do our magic if we changed paragraph
844 // don't delete anything if this is the ONLY paragraph!
845 if (old.lastpit() == 0)
848 // Do not delete empty paragraphs with keepempty set.
849 if (oldpar.allowEmpty())
852 if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
854 old.recordUndo(ATOMIC_UNDO,
855 max(old.pit() - 1, pit_type(0)),
856 min(old.pit() + 1, old.lastpit()));
857 ParagraphList & plist = old.text()->paragraphs();
858 bool const soa = oldpar.params().startOfAppendix();
859 plist.erase(boost::next(plist.begin(), old.pit()));
860 // do not lose start of appendix marker (bug 4212)
861 if (soa && old.pit() < pit_type(plist.size()))
862 plist[old.pit()].params().startOfAppendix(true);
864 // see #warning (FIXME?) above
865 if (cur.depth() >= old.depth()) {
866 CursorSlice & curslice = cur[old.depth() - 1];
867 if (&curslice.inset() == &old.inset()
868 && curslice.pit() > old.pit()) {
870 // since a paragraph has been deleted, all the
871 // insets after `old' have been copied and
872 // their address has changed. Therefore we
873 // need to `regenerate' cur. (JMarc)
874 cur.updateInsets(&(cur.bottom().inset()));
875 need_anchor_change = true;
881 if (oldpar.stripLeadingSpaces(cur.buffer().params().trackChanges)) {
882 need_anchor_change = true;
883 // We return true here because the Paragraph contents changed and
884 // we need a redraw before further action is processed.
892 void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges)
894 LASSERT(first >= 0 && first <= last && last < (int) pars_.size(), /**/);
896 for (pit_type pit = first; pit <= last; ++pit) {
897 Paragraph & par = pars_[pit];
899 // We allow all kinds of "mumbo-jumbo" when freespacing.
900 if (par.isFreeSpacing())
903 for (pos_type pos = 1; pos < par.size(); ++pos) {
904 if (par.isLineSeparator(pos) && par.isLineSeparator(pos - 1)
905 && !par.isDeleted(pos - 1)) {
906 if (par.eraseChar(pos - 1, trackChanges)) {
912 // don't delete anything if this is the only remaining paragraph within the given range
913 // note: Text::acceptOrRejectChanges() sets the cursor to 'first' after calling DEPM
917 // don't delete empty paragraphs with keepempty set
918 if (par.allowEmpty())
921 if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) {
922 pars_.erase(boost::next(pars_.begin(), pit));
928 par.stripLeadingSpaces(trackChanges);
933 void Text::recUndo(Cursor & cur, pit_type first, pit_type last) const
935 cur.recordUndo(ATOMIC_UNDO, first, last);
939 void Text::recUndo(Cursor & cur, pit_type par) const
941 cur.recordUndo(ATOMIC_UNDO, par, par);