3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Lars Gullik Bjønnes
8 * \author Alfredo Braunstein
9 * \author Jean-Marc Lasgouttes
10 * \author Angus Leeming
12 * \author André Pönitz
14 * \author Stefan Schimanski
16 * \author Jürgen Vigna
18 * Full author contact details are available in file CREDITS.
27 #include "buffer_funcs.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #include "BufferView.h"
33 #include "CutAndPaste.h"
34 #include "DispatchResult.h"
35 #include "ErrorList.h"
36 #include "FuncRequest.h"
42 #include "Paragraph.h"
43 #include "paragraph_funcs.h"
44 #include "ParagraphParameters.h"
45 #include "TextClass.h"
46 #include "TextMetrics.h"
49 #include "insets/InsetCollapsable.h"
51 #include "mathed/InsetMathHull.h"
53 #include "support/lassert.h"
54 #include "support/debug.h"
55 #include "support/gettext.h"
56 #include "support/textutils.h"
58 #include <boost/next_prior.hpp>
67 : autoBreakRows_(false)
71 bool Text::isMainText(Buffer const & buffer) const
73 return &buffer.text() == this;
77 // Note that this is supposed to return a fully realized font.
78 FontInfo Text::layoutFont(Buffer const & buffer, pit_type const pit) const
80 Layout const & layout = pars_[pit].layout();
82 if (!pars_[pit].getDepth()) {
83 FontInfo lf = layout.resfont;
84 // In case the default family has been customized
85 if (layout.font.family() == INHERIT_FAMILY)
86 lf.setFamily(buffer.params().getFont().fontInfo().family());
88 // It ought to be possible here just to use Inset::getLayout() and skip
89 // the asInsetCollapsable() bit. Unfortunatley, that doesn't work right
90 // now, because Inset::getLayout() will return a default-constructed
91 // InsetLayout, and that e.g. sets the foreground color to red. So we
92 // need to do some work to make that possible.
93 InsetCollapsable const * icp = pars_[pit].inInset()->asInsetCollapsable();
96 FontInfo icf = icp->getLayout().font();
101 FontInfo font = layout.font;
102 // Realize with the fonts of lesser depth.
103 //font.realize(outerFont(pit, paragraphs()));
104 font.realize(buffer.params().getFont().fontInfo());
110 // Note that this is supposed to return a fully realized font.
111 FontInfo Text::labelFont(Buffer const & buffer, Paragraph const & par) const
113 Layout const & layout = par.layout();
115 if (!par.getDepth()) {
116 FontInfo lf = layout.reslabelfont;
117 // In case the default family has been customized
118 if (layout.labelfont.family() == INHERIT_FAMILY)
119 lf.setFamily(buffer.params().getFont().fontInfo().family());
120 InsetCollapsable const * icp = par.inInset()->asInsetCollapsable();
123 FontInfo icf = icp->getLayout().labelfont();
128 FontInfo font = layout.labelfont;
129 // Realize with the fonts of lesser depth.
130 font.realize(buffer.params().getFont().fontInfo());
136 void Text::setCharFont(Buffer const & buffer, pit_type pit,
137 pos_type pos, Font const & fnt, Font const & display_font)
140 Layout const & layout = pars_[pit].layout();
142 // Get concrete layout font to reduce against
145 if (pos < pars_[pit].beginOfBody())
146 layoutfont = layout.labelfont;
148 layoutfont = layout.font;
150 // Realize against environment font information
151 if (pars_[pit].getDepth()) {
153 while (!layoutfont.resolved() &&
154 tp != pit_type(paragraphs().size()) &&
155 pars_[tp].getDepth()) {
156 tp = outerHook(tp, paragraphs());
157 if (tp != pit_type(paragraphs().size()))
158 layoutfont.realize(pars_[tp].layout().font);
162 // Inside inset, apply the inset's font attributes if any
164 if (!isMainText(buffer))
165 layoutfont.realize(display_font.fontInfo());
167 layoutfont.realize(buffer.params().getFont().fontInfo());
169 // Now, reduce font against full layout font
170 font.fontInfo().reduce(layoutfont);
172 pars_[pit].setFont(pos, font);
176 void Text::setInsetFont(BufferView const & bv, pit_type pit,
177 pos_type pos, Font const & font, bool toggleall)
179 Inset * const inset = pars_[pit].getInset(pos);
180 LASSERT(inset && inset->noFontChange(), /**/);
182 CursorSlice::idx_type endidx = inset->nargs();
183 for (CursorSlice cs(*inset); cs.idx() != endidx; ++cs.idx()) {
184 Text * text = cs.text();
186 // last position of the cell
187 CursorSlice cellend = cs;
188 cellend.pit() = cellend.lastpit();
189 cellend.pos() = cellend.lastpos();
190 text->setFont(bv, cs, cellend, font, toggleall);
196 // return past-the-last paragraph influenced by a layout change on pit
197 pit_type Text::undoSpan(pit_type pit)
199 pit_type end = paragraphs().size();
200 pit_type nextpit = pit + 1;
203 //because of parindents
204 if (!pars_[pit].getDepth())
205 return boost::next(nextpit);
206 //because of depth constrains
207 for (; nextpit != end; ++pit, ++nextpit) {
208 if (!pars_[pit].getDepth())
215 void Text::setLayout(Buffer const & buffer, pit_type start, pit_type end,
216 docstring const & layout)
218 LASSERT(start != end, /**/);
220 BufferParams const & bufparams = buffer.params();
221 Layout const & lyxlayout = bufparams.documentClass()[layout];
223 for (pit_type pit = start; pit != end; ++pit) {
224 Paragraph & par = pars_[pit];
225 par.applyLayout(lyxlayout);
226 if (lyxlayout.margintype == MARGIN_MANUAL)
227 par.setLabelWidthString(par.translateIfPossible(
228 lyxlayout.labelstring(), buffer.params()));
233 // set layout over selection and make a total rebreak of those paragraphs
234 void Text::setLayout(Cursor & cur, docstring const & layout)
236 LASSERT(this == cur.text(), /**/);
238 pit_type start = cur.selBegin().pit();
239 pit_type end = cur.selEnd().pit() + 1;
240 pit_type undopit = undoSpan(end - 1);
241 recUndo(cur, start, undopit - 1);
242 setLayout(cur.buffer(), start, end, layout);
243 updateLabels(cur.buffer());
247 static bool changeDepthAllowed(Text::DEPTH_CHANGE type,
248 Paragraph const & par, int max_depth)
250 if (par.layout().labeltype == LABEL_BIBLIO)
252 int const depth = par.params().depth();
253 if (type == Text::INC_DEPTH && depth < max_depth)
255 if (type == Text::DEC_DEPTH && depth > 0)
261 bool Text::changeDepthAllowed(Cursor & cur, DEPTH_CHANGE type) const
263 LASSERT(this == cur.text(), /**/);
264 // this happens when selecting several cells in tabular (bug 2630)
265 if (cur.selBegin().idx() != cur.selEnd().idx())
268 pit_type const beg = cur.selBegin().pit();
269 pit_type const end = cur.selEnd().pit() + 1;
270 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
272 for (pit_type pit = beg; pit != end; ++pit) {
273 if (lyx::changeDepthAllowed(type, pars_[pit], max_depth))
275 max_depth = pars_[pit].getMaxDepthAfter();
281 void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type)
283 LASSERT(this == cur.text(), /**/);
284 pit_type const beg = cur.selBegin().pit();
285 pit_type const end = cur.selEnd().pit() + 1;
286 cur.recordUndoSelection();
287 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
289 for (pit_type pit = beg; pit != end; ++pit) {
290 Paragraph & par = pars_[pit];
291 if (lyx::changeDepthAllowed(type, par, max_depth)) {
292 int const depth = par.params().depth();
293 if (type == INC_DEPTH)
294 par.params().depth(depth + 1);
296 par.params().depth(depth - 1);
298 max_depth = par.getMaxDepthAfter();
300 // this handles the counter labels, and also fixes up
301 // depth values for follow-on (child) paragraphs
302 updateLabels(cur.buffer());
306 void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
308 LASSERT(this == cur.text(), /**/);
309 // Set the current_font
310 // Determine basis font
312 pit_type pit = cur.pit();
313 if (cur.pos() < pars_[pit].beginOfBody())
314 layoutfont = labelFont(cur.buffer(), pars_[pit]);
316 layoutfont = layoutFont(cur.buffer(), pit);
318 // Update current font
319 cur.real_current_font.update(font,
320 cur.buffer().params().language,
323 // Reduce to implicit settings
324 cur.current_font = cur.real_current_font;
325 cur.current_font.fontInfo().reduce(layoutfont);
326 // And resolve it completely
327 cur.real_current_font.fontInfo().realize(layoutfont);
329 // if there is no selection that's all we need to do
330 if (!cur.selection())
333 // Ok, we have a selection.
334 cur.recordUndoSelection();
336 setFont(cur.bv(), cur.selectionBegin().top(),
337 cur.selectionEnd().top(), font, toggleall);
341 void Text::setFont(BufferView const & bv, CursorSlice const & begin,
342 CursorSlice const & end, Font const & font,
345 Buffer const & buffer = bv.buffer();
347 // Don't use forwardChar here as ditend might have
348 // pos() == lastpos() and forwardChar would miss it.
349 // Can't use forwardPos either as this descends into
351 Language const * language = buffer.params().language;
352 for (CursorSlice dit = begin; dit != end; dit.forwardPos()) {
353 if (dit.pos() == dit.lastpos())
355 pit_type const pit = dit.pit();
356 pos_type const pos = dit.pos();
357 Inset * inset = pars_[pit].getInset(pos);
358 if (inset && inset->noFontChange()) {
359 // We need to propagate the font change to all
360 // text cells of the inset (bug 1973).
361 // FIXME: This should change, see documentation
362 // of noFontChange in Inset.h
363 setInsetFont(bv, pit, pos, font, toggleall);
365 TextMetrics const & tm = bv.textMetrics(this);
366 Font f = tm.displayFont(pit, pos);
367 f.update(font, language, toggleall);
368 setCharFont(buffer, pit, pos, f, tm.font_);
373 bool Text::cursorTop(Cursor & cur)
375 LASSERT(this == cur.text(), /**/);
376 return setCursor(cur, 0, 0);
380 bool Text::cursorBottom(Cursor & cur)
382 LASSERT(this == cur.text(), /**/);
383 return setCursor(cur, cur.lastpit(), boost::prior(paragraphs().end())->size());
387 void Text::toggleFree(Cursor & cur, Font const & font, bool toggleall)
389 LASSERT(this == cur.text(), /**/);
390 // If the mask is completely neutral, tell user
391 if (font.fontInfo() == ignore_font && font.language() == ignore_language) {
392 // Could only happen with user style
393 cur.message(_("No font change defined."));
397 // Try implicit word selection
398 // If there is a change in the language the implicit word selection
400 CursorSlice resetCursor = cur.top();
401 bool implicitSelection =
402 font.language() == ignore_language
403 && font.fontInfo().number() == FONT_IGNORE
404 && selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
407 setFont(cur, font, toggleall);
409 // Implicit selections are cleared afterwards
410 // and cursor is set to the original position.
411 if (implicitSelection) {
412 cur.clearSelection();
413 cur.top() = resetCursor;
419 docstring Text::getStringToIndex(Cursor const & cur)
421 LASSERT(this == cur.text(), /**/);
424 return cur.selectionAsString(false);
426 // Try implicit word selection. If there is a change
427 // in the language the implicit word selection is
430 selectWord(tmpcur, PREVIOUS_WORD);
432 if (!tmpcur.selection())
433 cur.message(_("Nothing to index!"));
434 else if (tmpcur.selBegin().pit() != tmpcur.selEnd().pit())
435 cur.message(_("Cannot index more than one paragraph!"));
437 return tmpcur.selectionAsString(false);
443 void Text::setParagraphs(Cursor & cur, docstring arg, bool merge)
445 LASSERT(cur.text(), /**/);
446 // make sure that the depth behind the selection are restored, too
447 pit_type undopit = undoSpan(cur.selEnd().pit());
448 recUndo(cur, cur.selBegin().pit(), undopit - 1);
451 string const argument = to_utf8(arg);
452 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
454 Paragraph & par = pars_[pit];
455 ParagraphParameters params = par.params();
456 params.read(argument, merge);
457 par.params().apply(params, par.layout());
462 //FIXME This is a little redundant now, but it's probably worth keeping,
463 //especially if we're going to go away from using serialization internally
465 void Text::setParagraphs(Cursor & cur, ParagraphParameters const & p)
467 LASSERT(cur.text(), /**/);
468 // make sure that the depth behind the selection are restored, too
469 pit_type undopit = undoSpan(cur.selEnd().pit());
470 recUndo(cur, cur.selBegin().pit(), undopit - 1);
472 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
474 Paragraph & par = pars_[pit];
475 par.params().apply(p, par.layout());
480 // this really should just insert the inset and not move the cursor.
481 void Text::insertInset(Cursor & cur, Inset * inset)
483 LASSERT(this == cur.text(), /**/);
484 LASSERT(inset, /**/);
485 cur.paragraph().insertInset(cur.pos(), inset, cur.current_font,
486 Change(cur.buffer().params().trackChanges
487 ? Change::INSERTED : Change::UNCHANGED));
491 // needed to insert the selection
492 void Text::insertStringAsLines(Cursor & cur, docstring const & str)
494 cur.buffer().insertStringAsLines(pars_, cur.pit(), cur.pos(),
495 cur.current_font, str, autoBreakRows_);
499 // turn double CR to single CR, others are converted into one
500 // blank. Then insertStringAsLines is called
501 void Text::insertStringAsParagraphs(Cursor & cur, docstring const & str)
503 docstring linestr = str;
504 bool newline_inserted = false;
506 for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
507 if (linestr[i] == '\n') {
508 if (newline_inserted) {
509 // we know that \r will be ignored by
510 // insertStringAsLines. Of course, it is a dirty
511 // trick, but it works...
512 linestr[i - 1] = '\r';
516 newline_inserted = true;
518 } else if (isPrintable(linestr[i])) {
519 newline_inserted = false;
522 insertStringAsLines(cur, linestr);
526 bool Text::setCursor(Cursor & cur, pit_type par, pos_type pos,
527 bool setfont, bool boundary)
529 TextMetrics const & tm = cur.bv().textMetrics(this);
530 bool const update_needed = !tm.contains(par);
532 setCursorIntern(cur, par, pos, setfont, boundary);
533 return cur.bv().checkDepm(cur, old) || update_needed;
537 void Text::setCursor(CursorSlice & cur, pit_type par, pos_type pos)
539 LASSERT(par != int(paragraphs().size()), /**/);
543 // now some strict checking
544 Paragraph & para = getPar(par);
546 // None of these should happen, but we're scaredy-cats
548 lyxerr << "dont like -1" << endl;
549 LASSERT(false, /**/);
552 if (pos > para.size()) {
553 lyxerr << "dont like 1, pos: " << pos
554 << " size: " << para.size()
555 << " par: " << par << endl;
556 LASSERT(false, /**/);
561 void Text::setCursorIntern(Cursor & cur,
562 pit_type par, pos_type pos, bool setfont, bool boundary)
564 LASSERT(this == cur.text(), /**/);
565 cur.boundary(boundary);
566 setCursor(cur.top(), par, pos);
568 cur.setCurrentFont();
572 bool Text::checkAndActivateInset(Cursor & cur, bool front)
576 if (front && cur.pos() == cur.lastpos())
578 if (!front && cur.pos() == 0)
580 Inset * inset = front ? cur.nextInset() : cur.prevInset();
581 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
584 * Apparently, when entering an inset we are expected to be positioned
585 * *before* it in the containing paragraph, regardless of the direction
586 * from which we are entering. Otherwise, cursor placement goes awry,
587 * and when we exit from the beginning, we'll be placed *after* the
592 inset->edit(cur, front);
597 bool Text::checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool movingLeft)
603 if (cur.pos() == cur.lastpos())
605 Paragraph & par = cur.paragraph();
606 Inset * inset = par.isInset(cur.pos()) ? par.getInset(cur.pos()) : 0;
607 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
609 inset->edit(cur, movingForward,
610 movingLeft ? Inset::ENTRY_DIRECTION_RIGHT : Inset::ENTRY_DIRECTION_LEFT);
615 bool Text::cursorBackward(Cursor & cur)
617 // Tell BufferView to test for FitCursor in any case!
618 cur.updateFlags(Update::FitCursor);
620 // not at paragraph start?
622 // if on right side of boundary (i.e. not at paragraph end, but line end)
623 // -> skip it, i.e. set boundary to true, i.e. go only logically left
624 // there are some exceptions to ignore this: lineseps, newlines, spaces
626 // some effectless debug code to see the values in the debugger
627 bool bound = cur.boundary();
628 int rowpos = cur.textRow().pos();
630 bool sep = cur.paragraph().isSeparator(cur.pos() - 1);
631 bool newline = cur.paragraph().isNewline(cur.pos() - 1);
632 bool linesep = cur.paragraph().isLineSeparator(cur.pos() - 1);
634 if (!cur.boundary() &&
635 cur.textRow().pos() == cur.pos() &&
636 !cur.paragraph().isLineSeparator(cur.pos() - 1) &&
637 !cur.paragraph().isNewline(cur.pos() - 1) &&
638 !cur.paragraph().isSeparator(cur.pos() - 1)) {
639 return setCursor(cur, cur.pit(), cur.pos(), true, true);
642 // go left and try to enter inset
643 if (checkAndActivateInset(cur, false))
646 // normal character left
647 return setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
650 // move to the previous paragraph or do nothing
652 return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size(), true, false);
657 bool Text::cursorVisLeft(Cursor & cur, bool skip_inset)
659 Cursor temp_cur = cur;
660 temp_cur.posVisLeft(skip_inset);
661 if (temp_cur.depth() > cur.depth()) {
665 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
666 true, temp_cur.boundary());
670 bool Text::cursorVisRight(Cursor & cur, bool skip_inset)
672 Cursor temp_cur = cur;
673 temp_cur.posVisRight(skip_inset);
674 if (temp_cur.depth() > cur.depth()) {
678 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
679 true, temp_cur.boundary());
683 bool Text::cursorForward(Cursor & cur)
685 // Tell BufferView to test for FitCursor in any case!
686 cur.updateFlags(Update::FitCursor);
688 // not at paragraph end?
689 if (cur.pos() != cur.lastpos()) {
690 // in front of editable inset, i.e. jump into it?
691 if (checkAndActivateInset(cur, true))
694 TextMetrics const & tm = cur.bv().textMetrics(this);
695 // if left of boundary -> just jump to right side
696 // but for RTL boundaries don't, because: abc|DDEEFFghi -> abcDDEEF|Fghi
697 if (cur.boundary() && !tm.isRTLBoundary(cur.pit(), cur.pos()))
698 return setCursor(cur, cur.pit(), cur.pos(), true, false);
700 // next position is left of boundary,
701 // but go to next line for special cases like space, newline, linesep
703 // some effectless debug code to see the values in the debugger
704 int endpos = cur.textRow().endpos();
705 int lastpos = cur.lastpos();
707 bool linesep = cur.paragraph().isLineSeparator(cur.pos());
708 bool newline = cur.paragraph().isNewline(cur.pos());
709 bool sep = cur.paragraph().isSeparator(cur.pos());
710 if (cur.pos() != cur.lastpos()) {
711 bool linesep2 = cur.paragraph().isLineSeparator(cur.pos()+1);
712 bool newline2 = cur.paragraph().isNewline(cur.pos()+1);
713 bool sep2 = cur.paragraph().isSeparator(cur.pos()+1);
716 if (cur.textRow().endpos() == cur.pos() + 1 &&
717 cur.textRow().endpos() != cur.lastpos() &&
718 !cur.paragraph().isNewline(cur.pos()) &&
719 !cur.paragraph().isLineSeparator(cur.pos()) &&
720 !cur.paragraph().isSeparator(cur.pos())) {
721 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
724 // in front of RTL boundary? Stay on this side of the boundary because:
725 // ab|cDDEEFFghi -> abc|DDEEFFghi
726 if (tm.isRTLBoundary(cur.pit(), cur.pos() + 1))
727 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
730 return setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
733 // move to next paragraph
734 if (cur.pit() != cur.lastpit())
735 return setCursor(cur, cur.pit() + 1, 0, true, false);
740 bool Text::cursorUpParagraph(Cursor & cur)
742 bool updated = false;
744 updated = setCursor(cur, cur.pit(), 0);
745 else if (cur.pit() != 0)
746 updated = setCursor(cur, cur.pit() - 1, 0);
751 bool Text::cursorDownParagraph(Cursor & cur)
753 bool updated = false;
754 if (cur.pit() != cur.lastpit())
755 updated = setCursor(cur, cur.pit() + 1, 0);
757 updated = setCursor(cur, cur.pit(), cur.lastpos());
762 // fix the cursor `cur' after a characters has been deleted at `where'
763 // position. Called by deleteEmptyParagraphMechanism
764 void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
766 // Do nothing if cursor is not in the paragraph where the
768 if (cur.pit() != where.pit())
771 // If cursor position is after the deletion place update it
772 if (cur.pos() > where.pos())
775 // Check also if we don't want to set the cursor on a spot behind the
776 // pagragraph because we erased the last character.
777 if (cur.pos() > cur.lastpos())
778 cur.pos() = cur.lastpos();
782 bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
783 Cursor & old, bool & need_anchor_change)
785 //LYXERR(Debug::DEBUG, "DEPM: cur:\n" << cur << "old:\n" << old);
787 Paragraph & oldpar = old.paragraph();
789 // We allow all kinds of "mumbo-jumbo" when freespacing.
790 if (oldpar.isFreeSpacing())
793 /* Ok I'll put some comments here about what is missing.
794 There are still some small problems that can lead to
795 double spaces stored in the document file or space at
796 the beginning of paragraphs(). This happens if you have
797 the cursor between to spaces and then save. Or if you
798 cut and paste and the selection have a space at the
799 beginning and then save right after the paste. (Lgb)
802 // If old.pos() == 0 and old.pos()(1) == LineSeparator
803 // delete the LineSeparator.
806 // If old.pos() == 1 and old.pos()(0) == LineSeparator
807 // delete the LineSeparator.
810 bool const same_inset = &old.inset() == &cur.inset();
811 bool const same_par = same_inset && old.pit() == cur.pit();
812 bool const same_par_pos = same_par && old.pos() == cur.pos();
814 // If the chars around the old cursor were spaces, delete one of them.
816 // Only if the cursor has really moved.
818 && old.pos() < oldpar.size()
819 && oldpar.isLineSeparator(old.pos())
820 && oldpar.isLineSeparator(old.pos() - 1)
821 && !oldpar.isDeleted(old.pos() - 1)
822 && !oldpar.isDeleted(old.pos())) {
823 oldpar.eraseChar(old.pos() - 1, cur.buffer().params().trackChanges);
824 // FIXME: This will not work anymore when we have multiple views of the same buffer
825 // In this case, we will have to correct also the cursors held by
826 // other bufferviews. It will probably be easier to do that in a more
827 // automated way in CursorSlice code. (JMarc 26/09/2001)
828 // correct all cursor parts
830 fixCursorAfterDelete(cur.top(), old.top());
831 need_anchor_change = true;
837 // only do our magic if we changed paragraph
841 // don't delete anything if this is the ONLY paragraph!
842 if (old.lastpit() == 0)
845 // Do not delete empty paragraphs with keepempty set.
846 if (oldpar.allowEmpty())
849 if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
851 old.recordUndo(ATOMIC_UNDO,
852 max(old.pit() - 1, pit_type(0)),
853 min(old.pit() + 1, old.lastpit()));
854 ParagraphList & plist = old.text()->paragraphs();
855 bool const soa = oldpar.params().startOfAppendix();
856 plist.erase(boost::next(plist.begin(), old.pit()));
857 // do not lose start of appendix marker (bug 4212)
858 if (soa && old.pit() < pit_type(plist.size()))
859 plist[old.pit()].params().startOfAppendix(true);
861 // see #warning (FIXME?) above
862 if (cur.depth() >= old.depth()) {
863 CursorSlice & curslice = cur[old.depth() - 1];
864 if (&curslice.inset() == &old.inset()
865 && curslice.pit() > old.pit()) {
867 // since a paragraph has been deleted, all the
868 // insets after `old' have been copied and
869 // their address has changed. Therefore we
870 // need to `regenerate' cur. (JMarc)
871 cur.updateInsets(&(cur.bottom().inset()));
872 need_anchor_change = true;
878 if (oldpar.stripLeadingSpaces(cur.buffer().params().trackChanges)) {
879 need_anchor_change = true;
880 // We return true here because the Paragraph contents changed and
881 // we need a redraw before further action is processed.
889 void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges)
891 LASSERT(first >= 0 && first <= last && last < (int) pars_.size(), /**/);
893 for (pit_type pit = first; pit <= last; ++pit) {
894 Paragraph & par = pars_[pit];
896 // We allow all kinds of "mumbo-jumbo" when freespacing.
897 if (par.isFreeSpacing())
900 for (pos_type pos = 1; pos < par.size(); ++pos) {
901 if (par.isLineSeparator(pos) && par.isLineSeparator(pos - 1)
902 && !par.isDeleted(pos - 1)) {
903 if (par.eraseChar(pos - 1, trackChanges)) {
909 // don't delete anything if this is the only remaining paragraph within the given range
910 // note: Text::acceptOrRejectChanges() sets the cursor to 'first' after calling DEPM
914 // don't delete empty paragraphs with keepempty set
915 if (par.allowEmpty())
918 if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) {
919 pars_.erase(boost::next(pars_.begin(), pit));
925 par.stripLeadingSpaces(trackChanges);
930 void Text::recUndo(Cursor & cur, pit_type first, pit_type last) const
932 cur.recordUndo(ATOMIC_UNDO, first, last);
936 void Text::recUndo(Cursor & cur, pit_type par) const
938 cur.recordUndo(ATOMIC_UNDO, par, par);