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 &&
390 (font.language() == 0 || font.language() == ignore_language)) {
391 // Could only happen with user style
392 cur.message(_("No font change defined."));
396 // Try implicit word selection
397 // If there is a change in the language the implicit word selection
399 CursorSlice resetCursor = cur.top();
400 bool implicitSelection =
401 font.language() == ignore_language
402 && font.fontInfo().number() == FONT_IGNORE
403 && selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
406 setFont(cur, font, toggleall);
408 // Implicit selections are cleared afterwards
409 // and cursor is set to the original position.
410 if (implicitSelection) {
411 cur.clearSelection();
412 cur.top() = resetCursor;
418 docstring Text::getStringToIndex(Cursor const & cur)
420 LASSERT(this == cur.text(), /**/);
423 return cur.selectionAsString(false);
425 // Try implicit word selection. If there is a change
426 // in the language the implicit word selection is
429 selectWord(tmpcur, PREVIOUS_WORD);
431 if (!tmpcur.selection())
432 cur.message(_("Nothing to index!"));
433 else if (tmpcur.selBegin().pit() != tmpcur.selEnd().pit())
434 cur.message(_("Cannot index more than one paragraph!"));
436 return tmpcur.selectionAsString(false);
442 void Text::setParagraphs(Cursor & cur, docstring arg, bool merge)
444 LASSERT(cur.text(), /**/);
445 // make sure that the depth behind the selection are restored, too
446 pit_type undopit = undoSpan(cur.selEnd().pit());
447 recUndo(cur, cur.selBegin().pit(), undopit - 1);
450 string const argument = to_utf8(arg);
451 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
453 Paragraph & par = pars_[pit];
454 ParagraphParameters params = par.params();
455 params.read(argument, merge);
456 par.params().apply(params, par.layout());
461 //FIXME This is a little redundant now, but it's probably worth keeping,
462 //especially if we're going to go away from using serialization internally
464 void Text::setParagraphs(Cursor & cur, ParagraphParameters const & p)
466 LASSERT(cur.text(), /**/);
467 // make sure that the depth behind the selection are restored, too
468 pit_type undopit = undoSpan(cur.selEnd().pit());
469 recUndo(cur, cur.selBegin().pit(), undopit - 1);
471 for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
473 Paragraph & par = pars_[pit];
474 par.params().apply(p, par.layout());
479 // this really should just insert the inset and not move the cursor.
480 void Text::insertInset(Cursor & cur, Inset * inset)
482 LASSERT(this == cur.text(), /**/);
483 LASSERT(inset, /**/);
484 cur.paragraph().insertInset(cur.pos(), inset, cur.current_font,
485 Change(cur.buffer().params().trackChanges
486 ? Change::INSERTED : Change::UNCHANGED));
490 // needed to insert the selection
491 void Text::insertStringAsLines(Cursor & cur, docstring const & str)
493 cur.buffer().insertStringAsLines(pars_, cur.pit(), cur.pos(),
494 cur.current_font, str, autoBreakRows_);
498 // turn double CR to single CR, others are converted into one
499 // blank. Then insertStringAsLines is called
500 void Text::insertStringAsParagraphs(Cursor & cur, docstring const & str)
502 docstring linestr = str;
503 bool newline_inserted = false;
505 for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
506 if (linestr[i] == '\n') {
507 if (newline_inserted) {
508 // we know that \r will be ignored by
509 // insertStringAsLines. Of course, it is a dirty
510 // trick, but it works...
511 linestr[i - 1] = '\r';
515 newline_inserted = true;
517 } else if (isPrintable(linestr[i])) {
518 newline_inserted = false;
521 insertStringAsLines(cur, linestr);
525 bool Text::setCursor(Cursor & cur, pit_type par, pos_type pos,
526 bool setfont, bool boundary)
528 TextMetrics const & tm = cur.bv().textMetrics(this);
529 bool const update_needed = !tm.contains(par);
531 setCursorIntern(cur, par, pos, setfont, boundary);
532 return cur.bv().checkDepm(cur, old) || update_needed;
536 void Text::setCursor(CursorSlice & cur, pit_type par, pos_type pos)
538 LASSERT(par != int(paragraphs().size()), /**/);
542 // now some strict checking
543 Paragraph & para = getPar(par);
545 // None of these should happen, but we're scaredy-cats
547 lyxerr << "dont like -1" << endl;
548 LASSERT(false, /**/);
551 if (pos > para.size()) {
552 lyxerr << "dont like 1, pos: " << pos
553 << " size: " << para.size()
554 << " par: " << par << endl;
555 LASSERT(false, /**/);
560 void Text::setCursorIntern(Cursor & cur,
561 pit_type par, pos_type pos, bool setfont, bool boundary)
563 LASSERT(this == cur.text(), /**/);
564 cur.boundary(boundary);
565 setCursor(cur.top(), par, pos);
567 cur.setCurrentFont();
571 bool Text::checkAndActivateInset(Cursor & cur, bool front)
575 if (front && cur.pos() == cur.lastpos())
577 if (!front && cur.pos() == 0)
579 Inset * inset = front ? cur.nextInset() : cur.prevInset();
580 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
583 * Apparently, when entering an inset we are expected to be positioned
584 * *before* it in the containing paragraph, regardless of the direction
585 * from which we are entering. Otherwise, cursor placement goes awry,
586 * and when we exit from the beginning, we'll be placed *after* the
591 inset->edit(cur, front);
596 bool Text::checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool movingLeft)
602 if (cur.pos() == cur.lastpos())
604 Paragraph & par = cur.paragraph();
605 Inset * inset = par.isInset(cur.pos()) ? par.getInset(cur.pos()) : 0;
606 if (!inset || inset->editable() != Inset::HIGHLY_EDITABLE)
608 inset->edit(cur, movingForward,
609 movingLeft ? Inset::ENTRY_DIRECTION_RIGHT : Inset::ENTRY_DIRECTION_LEFT);
614 bool Text::cursorBackward(Cursor & cur)
616 // Tell BufferView to test for FitCursor in any case!
617 cur.updateFlags(Update::FitCursor);
619 // not at paragraph start?
621 // if on right side of boundary (i.e. not at paragraph end, but line end)
622 // -> skip it, i.e. set boundary to true, i.e. go only logically left
623 // there are some exceptions to ignore this: lineseps, newlines, spaces
625 // some effectless debug code to see the values in the debugger
626 bool bound = cur.boundary();
627 int rowpos = cur.textRow().pos();
629 bool sep = cur.paragraph().isSeparator(cur.pos() - 1);
630 bool newline = cur.paragraph().isNewline(cur.pos() - 1);
631 bool linesep = cur.paragraph().isLineSeparator(cur.pos() - 1);
633 if (!cur.boundary() &&
634 cur.textRow().pos() == cur.pos() &&
635 !cur.paragraph().isLineSeparator(cur.pos() - 1) &&
636 !cur.paragraph().isNewline(cur.pos() - 1) &&
637 !cur.paragraph().isSeparator(cur.pos() - 1)) {
638 return setCursor(cur, cur.pit(), cur.pos(), true, true);
641 // go left and try to enter inset
642 if (checkAndActivateInset(cur, false))
645 // normal character left
646 return setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
649 // move to the previous paragraph or do nothing
651 return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size(), true, false);
656 bool Text::cursorVisLeft(Cursor & cur, bool skip_inset)
658 Cursor temp_cur = cur;
659 temp_cur.posVisLeft(skip_inset);
660 if (temp_cur.depth() > cur.depth()) {
664 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
665 true, temp_cur.boundary());
669 bool Text::cursorVisRight(Cursor & cur, bool skip_inset)
671 Cursor temp_cur = cur;
672 temp_cur.posVisRight(skip_inset);
673 if (temp_cur.depth() > cur.depth()) {
677 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
678 true, temp_cur.boundary());
682 bool Text::cursorForward(Cursor & cur)
684 // Tell BufferView to test for FitCursor in any case!
685 cur.updateFlags(Update::FitCursor);
687 // not at paragraph end?
688 if (cur.pos() != cur.lastpos()) {
689 // in front of editable inset, i.e. jump into it?
690 if (checkAndActivateInset(cur, true))
693 TextMetrics const & tm = cur.bv().textMetrics(this);
694 // if left of boundary -> just jump to right side
695 // but for RTL boundaries don't, because: abc|DDEEFFghi -> abcDDEEF|Fghi
696 if (cur.boundary() && !tm.isRTLBoundary(cur.pit(), cur.pos()))
697 return setCursor(cur, cur.pit(), cur.pos(), true, false);
699 // next position is left of boundary,
700 // but go to next line for special cases like space, newline, linesep
702 // some effectless debug code to see the values in the debugger
703 int endpos = cur.textRow().endpos();
704 int lastpos = cur.lastpos();
706 bool linesep = cur.paragraph().isLineSeparator(cur.pos());
707 bool newline = cur.paragraph().isNewline(cur.pos());
708 bool sep = cur.paragraph().isSeparator(cur.pos());
709 if (cur.pos() != cur.lastpos()) {
710 bool linesep2 = cur.paragraph().isLineSeparator(cur.pos()+1);
711 bool newline2 = cur.paragraph().isNewline(cur.pos()+1);
712 bool sep2 = cur.paragraph().isSeparator(cur.pos()+1);
715 if (cur.textRow().endpos() == cur.pos() + 1 &&
716 cur.textRow().endpos() != cur.lastpos() &&
717 !cur.paragraph().isNewline(cur.pos()) &&
718 !cur.paragraph().isLineSeparator(cur.pos()) &&
719 !cur.paragraph().isSeparator(cur.pos())) {
720 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
723 // in front of RTL boundary? Stay on this side of the boundary because:
724 // ab|cDDEEFFghi -> abc|DDEEFFghi
725 if (tm.isRTLBoundary(cur.pit(), cur.pos() + 1))
726 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
729 return setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
732 // move to next paragraph
733 if (cur.pit() != cur.lastpit())
734 return setCursor(cur, cur.pit() + 1, 0, true, false);
739 bool Text::cursorUpParagraph(Cursor & cur)
741 bool updated = false;
743 updated = setCursor(cur, cur.pit(), 0);
744 else if (cur.pit() != 0)
745 updated = setCursor(cur, cur.pit() - 1, 0);
750 bool Text::cursorDownParagraph(Cursor & cur)
752 bool updated = false;
753 if (cur.pit() != cur.lastpit())
754 updated = setCursor(cur, cur.pit() + 1, 0);
756 updated = setCursor(cur, cur.pit(), cur.lastpos());
761 // fix the cursor `cur' after a characters has been deleted at `where'
762 // position. Called by deleteEmptyParagraphMechanism
763 void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
765 // Do nothing if cursor is not in the paragraph where the
767 if (cur.pit() != where.pit())
770 // If cursor position is after the deletion place update it
771 if (cur.pos() > where.pos())
774 // Check also if we don't want to set the cursor on a spot behind the
775 // pagragraph because we erased the last character.
776 if (cur.pos() > cur.lastpos())
777 cur.pos() = cur.lastpos();
781 bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
782 Cursor & old, bool & need_anchor_change)
784 //LYXERR(Debug::DEBUG, "DEPM: cur:\n" << cur << "old:\n" << old);
786 Paragraph & oldpar = old.paragraph();
788 // We allow all kinds of "mumbo-jumbo" when freespacing.
789 if (oldpar.isFreeSpacing())
792 /* Ok I'll put some comments here about what is missing.
793 There are still some small problems that can lead to
794 double spaces stored in the document file or space at
795 the beginning of paragraphs(). This happens if you have
796 the cursor between to spaces and then save. Or if you
797 cut and paste and the selection have a space at the
798 beginning and then save right after the paste. (Lgb)
801 // If old.pos() == 0 and old.pos()(1) == LineSeparator
802 // delete the LineSeparator.
805 // If old.pos() == 1 and old.pos()(0) == LineSeparator
806 // delete the LineSeparator.
809 bool const same_inset = &old.inset() == &cur.inset();
810 bool const same_par = same_inset && old.pit() == cur.pit();
811 bool const same_par_pos = same_par && old.pos() == cur.pos();
813 // If the chars around the old cursor were spaces, delete one of them.
815 // Only if the cursor has really moved.
817 && old.pos() < oldpar.size()
818 && oldpar.isLineSeparator(old.pos())
819 && oldpar.isLineSeparator(old.pos() - 1)
820 && !oldpar.isDeleted(old.pos() - 1)
821 && !oldpar.isDeleted(old.pos())) {
822 oldpar.eraseChar(old.pos() - 1, cur.buffer().params().trackChanges);
823 // FIXME: This will not work anymore when we have multiple views of the same buffer
824 // In this case, we will have to correct also the cursors held by
825 // other bufferviews. It will probably be easier to do that in a more
826 // automated way in CursorSlice code. (JMarc 26/09/2001)
827 // correct all cursor parts
829 fixCursorAfterDelete(cur.top(), old.top());
830 need_anchor_change = true;
836 // only do our magic if we changed paragraph
840 // don't delete anything if this is the ONLY paragraph!
841 if (old.lastpit() == 0)
844 // Do not delete empty paragraphs with keepempty set.
845 if (oldpar.allowEmpty())
848 if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
850 old.recordUndo(ATOMIC_UNDO,
851 max(old.pit() - 1, pit_type(0)),
852 min(old.pit() + 1, old.lastpit()));
853 ParagraphList & plist = old.text()->paragraphs();
854 bool const soa = oldpar.params().startOfAppendix();
855 plist.erase(boost::next(plist.begin(), old.pit()));
856 // do not lose start of appendix marker (bug 4212)
857 if (soa && old.pit() < pit_type(plist.size()))
858 plist[old.pit()].params().startOfAppendix(true);
860 // see #warning (FIXME?) above
861 if (cur.depth() >= old.depth()) {
862 CursorSlice & curslice = cur[old.depth() - 1];
863 if (&curslice.inset() == &old.inset()
864 && curslice.pit() > old.pit()) {
866 // since a paragraph has been deleted, all the
867 // insets after `old' have been copied and
868 // their address has changed. Therefore we
869 // need to `regenerate' cur. (JMarc)
870 cur.updateInsets(&(cur.bottom().inset()));
871 need_anchor_change = true;
877 if (oldpar.stripLeadingSpaces(cur.buffer().params().trackChanges)) {
878 need_anchor_change = true;
879 // We return true here because the Paragraph contents changed and
880 // we need a redraw before further action is processed.
888 void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges)
890 LASSERT(first >= 0 && first <= last && last < (int) pars_.size(), /**/);
892 for (pit_type pit = first; pit <= last; ++pit) {
893 Paragraph & par = pars_[pit];
895 // We allow all kinds of "mumbo-jumbo" when freespacing.
896 if (par.isFreeSpacing())
899 for (pos_type pos = 1; pos < par.size(); ++pos) {
900 if (par.isLineSeparator(pos) && par.isLineSeparator(pos - 1)
901 && !par.isDeleted(pos - 1)) {
902 if (par.eraseChar(pos - 1, trackChanges)) {
908 // don't delete anything if this is the only remaining paragraph within the given range
909 // note: Text::acceptOrRejectChanges() sets the cursor to 'first' after calling DEPM
913 // don't delete empty paragraphs with keepempty set
914 if (par.allowEmpty())
917 if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) {
918 pars_.erase(boost::next(pars_.begin(), pit));
924 par.stripLeadingSpaces(trackChanges);
929 void Text::recUndo(Cursor & cur, pit_type first, pit_type last) const
931 cur.recordUndo(ATOMIC_UNDO, first, last);
935 void Text::recUndo(Cursor & cur, pit_type par) const
937 cur.recordUndo(ATOMIC_UNDO, par, par);