3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Lars Gullik Bjønnes
8 * \author Alfredo Braunstein
9 * \author Jean-Marc Lasgouttes
10 * \author Angus Leeming
12 * \author André Pönitz
14 * \author Stefan Schimanski
16 * \author Jürgen Vigna
18 * Full author contact details are available in file CREDITS.
27 #include "buffer_funcs.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #include "BufferView.h"
33 #include "CutAndPaste.h"
34 #include "DispatchResult.h"
35 #include "ErrorList.h"
41 #include "Paragraph.h"
42 #include "ParagraphParameters.h"
43 #include "TextClass.h"
44 #include "TextMetrics.h"
46 #include "insets/InsetCollapsable.h"
48 #include "mathed/InsetMathHull.h"
50 #include "support/lassert.h"
51 #include "support/debug.h"
52 #include "support/gettext.h"
53 #include "support/lyxalgo.h"
54 #include "support/textutils.h"
62 bool Text::isMainText() const
64 return &owner_->buffer().text() == this;
68 // Note that this is supposed to return a fully realized font.
69 FontInfo Text::layoutFont(pit_type const pit) const
71 Layout const & layout = pars_[pit].layout();
73 if (!pars_[pit].getDepth()) {
74 FontInfo lf = layout.resfont;
75 // In case the default family has been customized
76 if (layout.font.family() == INHERIT_FAMILY)
77 lf.setFamily(owner_->buffer().params().getFont().fontInfo().family());
78 FontInfo icf = owner_->getLayout().font();
83 FontInfo font = layout.font;
84 // Realize with the fonts of lesser depth.
85 //font.realize(outerFont(pit));
86 font.realize(owner_->buffer().params().getFont().fontInfo());
92 // Note that this is supposed to return a fully realized font.
93 FontInfo Text::labelFont(Paragraph const & par) const
95 Buffer const & buffer = owner_->buffer();
96 Layout const & layout = par.layout();
98 if (!par.getDepth()) {
99 FontInfo lf = layout.reslabelfont;
100 // In case the default family has been customized
101 if (layout.labelfont.family() == INHERIT_FAMILY)
102 lf.setFamily(buffer.params().getFont().fontInfo().family());
106 FontInfo font = layout.labelfont;
107 // Realize with the fonts of lesser depth.
108 font.realize(buffer.params().getFont().fontInfo());
114 void Text::setCharFont(pit_type pit,
115 pos_type pos, Font const & fnt, Font const & display_font)
117 Buffer const & buffer = owner_->buffer();
119 Layout const & layout = pars_[pit].layout();
121 // Get concrete layout font to reduce against
124 if (pos < pars_[pit].beginOfBody())
125 layoutfont = layout.labelfont;
127 layoutfont = layout.font;
129 // Realize against environment font information
130 if (pars_[pit].getDepth()) {
132 while (!layoutfont.resolved() &&
133 tp != pit_type(paragraphs().size()) &&
134 pars_[tp].getDepth()) {
136 if (tp != pit_type(paragraphs().size()))
137 layoutfont.realize(pars_[tp].layout().font);
141 // Inside inset, apply the inset's font attributes if any
144 layoutfont.realize(display_font.fontInfo());
146 layoutfont.realize(buffer.params().getFont().fontInfo());
148 // Now, reduce font against full layout font
149 font.fontInfo().reduce(layoutfont);
151 pars_[pit].setFont(pos, font);
155 void Text::setInsetFont(BufferView const & bv, pit_type pit,
156 pos_type pos, Font const & font)
158 Inset * const inset = pars_[pit].getInset(pos);
159 LASSERT(inset && inset->resetFontEdit(), return);
161 CursorSlice::idx_type endidx = inset->nargs();
162 for (CursorSlice cs(*inset); cs.idx() != endidx; ++cs.idx()) {
163 Text * text = cs.text();
165 // last position of the cell
166 CursorSlice cellend = cs;
167 cellend.pit() = cellend.lastpit();
168 cellend.pos() = cellend.lastpos();
169 text->setFont(bv, cs, cellend, font);
175 void Text::setLayout(pit_type start, pit_type end,
176 docstring const & layout)
178 LASSERT(start != end, return);
180 Buffer const & buffer = owner_->buffer();
181 BufferParams const & bp = buffer.params();
182 Layout const & lyxlayout = bp.documentClass()[layout];
184 for (pit_type pit = start; pit != end; ++pit) {
185 Paragraph & par = pars_[pit];
186 par.applyLayout(lyxlayout);
187 if (lyxlayout.margintype == MARGIN_MANUAL)
188 par.setLabelWidthString(par.expandLabel(lyxlayout, bp));
193 // set layout over selection and make a total rebreak of those paragraphs
194 void Text::setLayout(Cursor & cur, docstring const & layout)
196 LBUFERR(this == cur.text());
198 pit_type start = cur.selBegin().pit();
199 pit_type end = cur.selEnd().pit() + 1;
200 cur.recordUndoSelection();
201 setLayout(start, end, layout);
202 cur.setCurrentFont();
203 cur.forceBufferUpdate();
207 static bool changeDepthAllowed(Text::DEPTH_CHANGE type,
208 Paragraph const & par, int max_depth)
210 int const depth = par.params().depth();
211 if (type == Text::INC_DEPTH && depth < max_depth)
213 if (type == Text::DEC_DEPTH && depth > 0)
219 bool Text::changeDepthAllowed(Cursor & cur, DEPTH_CHANGE type) const
221 LBUFERR(this == cur.text());
222 // this happens when selecting several cells in tabular (bug 2630)
223 if (cur.selBegin().idx() != cur.selEnd().idx())
226 pit_type const beg = cur.selBegin().pit();
227 pit_type const end = cur.selEnd().pit() + 1;
228 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
230 for (pit_type pit = beg; pit != end; ++pit) {
231 if (lyx::changeDepthAllowed(type, pars_[pit], max_depth))
233 max_depth = pars_[pit].getMaxDepthAfter();
239 void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type)
241 LBUFERR(this == cur.text());
242 pit_type const beg = cur.selBegin().pit();
243 pit_type const end = cur.selEnd().pit() + 1;
244 cur.recordUndoSelection();
245 int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
247 for (pit_type pit = beg; pit != end; ++pit) {
248 Paragraph & par = pars_[pit];
249 if (lyx::changeDepthAllowed(type, par, max_depth)) {
250 int const depth = par.params().depth();
251 if (type == INC_DEPTH)
252 par.params().depth(depth + 1);
254 par.params().depth(depth - 1);
256 max_depth = par.getMaxDepthAfter();
258 // this handles the counter labels, and also fixes up
259 // depth values for follow-on (child) paragraphs
260 cur.forceBufferUpdate();
264 void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
266 LASSERT(this == cur.text(), return);
268 // If there is a selection, record undo before the cursor font is changed.
270 cur.recordUndoSelection();
272 // Set the current_font
273 // Determine basis font
275 pit_type pit = cur.pit();
276 if (cur.pos() < pars_[pit].beginOfBody())
277 layoutfont = labelFont(pars_[pit]);
279 layoutfont = layoutFont(pit);
281 // Update current font
282 cur.real_current_font.update(font,
283 cur.buffer()->params().language,
286 // Reduce to implicit settings
287 cur.current_font = cur.real_current_font;
288 cur.current_font.fontInfo().reduce(layoutfont);
289 // And resolve it completely
290 cur.real_current_font.fontInfo().realize(layoutfont);
292 // if there is no selection that's all we need to do
293 if (!cur.selection())
296 // Ok, we have a selection.
300 // Toggling behaves as follows: We check the first character of the
301 // selection. If it's (say) got EMPH on, then we set to off; if off,
302 // then to on. With families and the like, we set it to INHERIT, if
303 // we already have it.
304 CursorSlice const & sl = cur.selBegin();
305 Text const & text = *sl.text();
306 Paragraph const & par = text.getPar(sl.pit());
308 // get font at the position
309 Font oldfont = par.getFont(cur.bv().buffer().params(), sl.pos(),
310 text.outerFont(sl.pit()));
311 FontInfo const & oldfi = oldfont.fontInfo();
313 FontInfo & newfi = newfont.fontInfo();
315 FontFamily newfam = newfi.family();
316 if (newfam != INHERIT_FAMILY && newfam != IGNORE_FAMILY &&
317 newfam == oldfi.family())
318 newfi.setFamily(INHERIT_FAMILY);
320 FontSeries newser = newfi.series();
321 if (newser == BOLD_SERIES && oldfi.series() == BOLD_SERIES)
322 newfi.setSeries(INHERIT_SERIES);
324 FontShape newshp = newfi.shape();
325 if (newshp != INHERIT_SHAPE && newshp != IGNORE_SHAPE &&
326 newshp == oldfi.shape())
327 newfi.setShape(INHERIT_SHAPE);
329 ColorCode newcol = newfi.color();
330 if (newcol != Color_none && newcol != Color_inherit
331 && newcol != Color_ignore && newcol == oldfi.color())
332 newfi.setColor(Color_none);
335 if (newfi.emph() == FONT_TOGGLE)
336 newfi.setEmph(oldfi.emph() == FONT_OFF ? FONT_ON : FONT_OFF);
337 if (newfi.underbar() == FONT_TOGGLE)
338 newfi.setUnderbar(oldfi.underbar() == FONT_OFF ? FONT_ON : FONT_OFF);
339 if (newfi.strikeout() == FONT_TOGGLE)
340 newfi.setStrikeout(oldfi.strikeout() == FONT_OFF ? FONT_ON : FONT_OFF);
341 if (newfi.uuline() == FONT_TOGGLE)
342 newfi.setUuline(oldfi.uuline() == FONT_OFF ? FONT_ON : FONT_OFF);
343 if (newfi.uwave() == FONT_TOGGLE)
344 newfi.setUwave(oldfi.uwave() == FONT_OFF ? FONT_ON : FONT_OFF);
345 if (newfi.noun() == FONT_TOGGLE)
346 newfi.setNoun(oldfi.noun() == FONT_OFF ? FONT_ON : FONT_OFF);
347 if (newfi.number() == FONT_TOGGLE)
348 newfi.setNumber(oldfi.number() == FONT_OFF ? FONT_ON : FONT_OFF);
351 setFont(cur.bv(), cur.selectionBegin().top(),
352 cur.selectionEnd().top(), newfont);
356 void Text::setFont(BufferView const & bv, CursorSlice const & begin,
357 CursorSlice const & end, Font const & font)
359 Buffer const & buffer = bv.buffer();
361 // Don't use forwardChar here as ditend might have
362 // pos() == lastpos() and forwardChar would miss it.
363 // Can't use forwardPos either as this descends into
365 Language const * language = buffer.params().language;
366 for (CursorSlice dit = begin; dit != end; dit.forwardPos()) {
367 if (dit.pos() == dit.lastpos())
369 pit_type const pit = dit.pit();
370 pos_type const pos = dit.pos();
371 Inset * inset = pars_[pit].getInset(pos);
372 if (inset && inset->resetFontEdit()) {
373 // We need to propagate the font change to all
374 // text cells of the inset (bugs 1973, 6919).
375 setInsetFont(bv, pit, pos, font);
377 TextMetrics const & tm = bv.textMetrics(this);
378 Font f = tm.displayFont(pit, pos);
379 f.update(font, language);
380 setCharFont(pit, pos, f, tm.font_);
381 // font change may change language...
382 // spell checker has to know that
383 pars_[pit].requestSpellCheck(pos);
388 bool Text::cursorTop(Cursor & cur)
390 LBUFERR(this == cur.text());
391 return setCursor(cur, 0, 0);
395 bool Text::cursorBottom(Cursor & cur)
397 LBUFERR(this == cur.text());
398 return setCursor(cur, cur.lastpit(), prev(paragraphs().end(), 1)->size());
402 void Text::toggleFree(Cursor & cur, Font const & font, bool toggleall)
404 LBUFERR(this == cur.text());
405 // If the mask is completely neutral, tell user
406 if (font.fontInfo() == ignore_font && font.language() == ignore_language) {
407 // Could only happen with user style
408 cur.message(_("No font change defined."));
412 // Try implicit word selection
413 // If there is a change in the language the implicit word selection
415 CursorSlice const resetCursor = cur.top();
416 bool const implicitSelection =
417 font.language() == ignore_language
418 && font.fontInfo().number() == FONT_IGNORE
419 && selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
422 setFont(cur, font, toggleall);
424 // Implicit selections are cleared afterwards
425 // and cursor is set to the original position.
426 if (implicitSelection) {
427 cur.clearSelection();
428 cur.top() = resetCursor;
434 docstring Text::getStringToIndex(Cursor const & cur)
436 LBUFERR(this == cur.text());
439 return cur.selectionAsString(false);
441 // Try implicit word selection. If there is a change
442 // in the language the implicit word selection is
445 selectWord(tmpcur, PREVIOUS_WORD);
447 if (!tmpcur.selection())
448 cur.message(_("Nothing to index!"));
449 else if (tmpcur.selBegin().pit() != tmpcur.selEnd().pit())
450 cur.message(_("Cannot index more than one paragraph!"));
452 return tmpcur.selectionAsString(false);
458 void Text::setLabelWidthStringToSequence(Cursor const & cur,
462 // Find first of same layout in sequence
463 while (!isFirstInSequence(c.pit())) {
464 c.pit() = depthHook(c.pit(), c.paragraph().getDepth());
467 // now apply label width string to every par
469 depth_type const depth = c.paragraph().getDepth();
470 Layout const & layout = c.paragraph().layout();
471 for ( ; c.pit() <= c.lastpit() ; ++c.pit()) {
472 while (c.paragraph().getDepth() > depth) {
474 if (c.pit() > c.lastpit())
477 if (c.paragraph().getDepth() < depth)
479 if (c.paragraph().layout() != layout)
482 c.paragraph().setLabelWidthString(s);
487 void Text::setParagraphs(Cursor & cur, docstring arg, bool merge)
492 string const argument = to_utf8(arg);
493 depth_type priordepth = -1;
496 c.setCursor(cur.selectionBegin());
497 for ( ; c <= cur.selectionEnd() ; ++c.pit()) {
498 Paragraph & par = c.paragraph();
499 ParagraphParameters params = par.params();
500 params.read(argument, merge);
501 // Changes to label width string apply to all paragraphs
502 // with same layout in a sequence.
503 // Do this only once for a selected range of paragraphs
504 // of the same layout and depth.
506 par.params().apply(params, par.layout());
507 if (par.getDepth() != priordepth || par.layout() != priorlayout)
508 setLabelWidthStringToSequence(c, params.labelWidthString());
509 priordepth = par.getDepth();
510 priorlayout = par.layout();
515 void Text::setParagraphs(Cursor & cur, ParagraphParameters const & p)
519 depth_type priordepth = -1;
522 c.setCursor(cur.selectionBegin());
523 for ( ; c < cur.selectionEnd() ; ++c.pit()) {
524 Paragraph & par = c.paragraph();
525 // Changes to label width string apply to all paragraphs
526 // with same layout in a sequence.
527 // Do this only once for a selected range of paragraphs
528 // of the same layout and depth.
530 par.params().apply(p, par.layout());
531 if (par.getDepth() != priordepth || par.layout() != priorlayout)
532 setLabelWidthStringToSequence(c,
533 par.params().labelWidthString());
534 priordepth = par.getDepth();
535 priorlayout = par.layout();
540 // this really should just insert the inset and not move the cursor.
541 void Text::insertInset(Cursor & cur, Inset * inset)
543 LBUFERR(this == cur.text());
545 cur.paragraph().insertInset(cur.pos(), inset, cur.current_font,
546 Change(cur.buffer()->params().track_changes
547 ? Change::INSERTED : Change::UNCHANGED));
551 bool Text::setCursor(Cursor & cur, pit_type par, pos_type pos,
552 bool setfont, bool boundary)
554 TextMetrics const & tm = cur.bv().textMetrics(this);
555 bool const update_needed = !tm.contains(par);
557 setCursorIntern(cur, par, pos, setfont, boundary);
558 return cur.bv().checkDepm(cur, old) || update_needed;
562 void Text::setCursor(CursorSlice & cur, pit_type par, pos_type pos)
564 LASSERT(par != int(paragraphs().size()), return);
568 // now some strict checking
569 Paragraph & para = getPar(par);
571 // None of these should happen, but we're scaredy-cats
573 LYXERR0("Don't like -1!");
577 if (pos > para.size()) {
578 LYXERR0("Don't like 1, pos: " << pos
579 << " size: " << para.size()
586 void Text::setCursorIntern(Cursor & cur,
587 pit_type par, pos_type pos, bool setfont, bool boundary)
589 LBUFERR(this == cur.text());
590 cur.boundary(boundary);
591 setCursor(cur.top(), par, pos);
593 cur.setCurrentFont();
597 bool Text::checkAndActivateInset(Cursor & cur, bool front)
599 if (front && cur.pos() == cur.lastpos())
601 if (!front && cur.pos() == 0)
603 Inset * inset = front ? cur.nextInset() : cur.prevInset();
604 if (!inset || !inset->editable())
606 if (cur.selection() && cur.realAnchor().find(inset) == -1)
609 * Apparently, when entering an inset we are expected to be positioned
610 * *before* it in the containing paragraph, regardless of the direction
611 * from which we are entering. Otherwise, cursor placement goes awry,
612 * and when we exit from the beginning, we'll be placed *after* the
617 inset->edit(cur, front);
622 bool Text::checkAndActivateInsetVisual(Cursor & cur, bool movingForward, bool movingLeft)
626 if (cur.pos() == cur.lastpos())
628 Paragraph & par = cur.paragraph();
629 Inset * inset = par.isInset(cur.pos()) ? par.getInset(cur.pos()) : 0;
630 if (!inset || !inset->editable())
632 if (cur.selection() && cur.realAnchor().find(inset) == -1)
634 inset->edit(cur, movingForward,
635 movingLeft ? Inset::ENTRY_DIRECTION_RIGHT : Inset::ENTRY_DIRECTION_LEFT);
640 bool Text::cursorBackward(Cursor & cur)
642 // Tell BufferView to test for FitCursor in any case!
643 cur.screenUpdateFlags(Update::FitCursor);
645 // not at paragraph start?
647 // if on right side of boundary (i.e. not at paragraph end, but line end)
648 // -> skip it, i.e. set boundary to true, i.e. go only logically left
649 // there are some exceptions to ignore this: lineseps, newlines, spaces
651 // some effectless debug code to see the values in the debugger
652 bool bound = cur.boundary();
653 int rowpos = cur.textRow().pos();
655 bool sep = cur.paragraph().isSeparator(cur.pos() - 1);
656 bool newline = cur.paragraph().isNewline(cur.pos() - 1);
657 bool linesep = cur.paragraph().isLineSeparator(cur.pos() - 1);
659 if (!cur.boundary() &&
660 cur.textRow().pos() == cur.pos() &&
661 !cur.paragraph().isLineSeparator(cur.pos() - 1) &&
662 !cur.paragraph().isNewline(cur.pos() - 1) &&
663 !cur.paragraph().isEnvSeparator(cur.pos() - 1) &&
664 !cur.paragraph().isSeparator(cur.pos() - 1)) {
665 return setCursor(cur, cur.pit(), cur.pos(), true, true);
668 // go left and try to enter inset
669 if (checkAndActivateInset(cur, false))
672 // normal character left
673 return setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
676 // move to the previous paragraph or do nothing
678 Paragraph & par = getPar(cur.pit() - 1);
679 pos_type lastpos = par.size();
680 if (lastpos > 0 && par.isEnvSeparator(lastpos - 1))
681 return setCursor(cur, cur.pit() - 1, lastpos - 1, true, false);
683 return setCursor(cur, cur.pit() - 1, lastpos, true, false);
689 bool Text::cursorVisLeft(Cursor & cur, bool skip_inset)
691 Cursor temp_cur = cur;
692 temp_cur.posVisLeft(skip_inset);
693 if (temp_cur.depth() > cur.depth()) {
697 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
698 true, temp_cur.boundary());
702 bool Text::cursorVisRight(Cursor & cur, bool skip_inset)
704 Cursor temp_cur = cur;
705 temp_cur.posVisRight(skip_inset);
706 if (temp_cur.depth() > cur.depth()) {
710 return setCursor(cur, temp_cur.pit(), temp_cur.pos(),
711 true, temp_cur.boundary());
715 bool Text::cursorForward(Cursor & cur)
717 // Tell BufferView to test for FitCursor in any case!
718 cur.screenUpdateFlags(Update::FitCursor);
720 // not at paragraph end?
721 if (cur.pos() != cur.lastpos()) {
722 // in front of editable inset, i.e. jump into it?
723 if (checkAndActivateInset(cur, true))
726 TextMetrics const & tm = cur.bv().textMetrics(this);
727 // if left of boundary -> just jump to right side
728 // but for RTL boundaries don't, because: abc|DDEEFFghi -> abcDDEEF|Fghi
729 if (cur.boundary() && !tm.isRTLBoundary(cur.pit(), cur.pos()))
730 return setCursor(cur, cur.pit(), cur.pos(), true, false);
732 // next position is left of boundary,
733 // but go to next line for special cases like space, newline, linesep
735 // some effectless debug code to see the values in the debugger
736 int endpos = cur.textRow().endpos();
737 int lastpos = cur.lastpos();
739 bool linesep = cur.paragraph().isLineSeparator(cur.pos());
740 bool newline = cur.paragraph().isNewline(cur.pos());
741 bool sep = cur.paragraph().isSeparator(cur.pos());
742 if (cur.pos() != cur.lastpos()) {
743 bool linesep2 = cur.paragraph().isLineSeparator(cur.pos()+1);
744 bool newline2 = cur.paragraph().isNewline(cur.pos()+1);
745 bool sep2 = cur.paragraph().isSeparator(cur.pos()+1);
748 if (cur.textRow().endpos() == cur.pos() + 1) {
749 if (cur.paragraph().isEnvSeparator(cur.pos()) &&
750 cur.pos() + 1 == cur.lastpos() &&
751 cur.pit() != cur.lastpit()) {
752 // move to next paragraph
753 return setCursor(cur, cur.pit() + 1, 0, true, false);
754 } else if (cur.textRow().endpos() != cur.lastpos() &&
755 !cur.paragraph().isNewline(cur.pos()) &&
756 !cur.paragraph().isEnvSeparator(cur.pos()) &&
757 !cur.paragraph().isLineSeparator(cur.pos()) &&
758 !cur.paragraph().isSeparator(cur.pos())) {
759 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
763 // in front of RTL boundary? Stay on this side of the boundary because:
764 // ab|cDDEEFFghi -> abc|DDEEFFghi
765 if (tm.isRTLBoundary(cur.pit(), cur.pos() + 1))
766 return setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
769 return setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
772 // move to next paragraph
773 if (cur.pit() != cur.lastpit())
774 return setCursor(cur, cur.pit() + 1, 0, true, false);
779 bool Text::cursorUpParagraph(Cursor & cur)
781 bool updated = false;
783 updated = setCursor(cur, cur.pit(), 0);
784 else if (cur.pit() != 0)
785 updated = setCursor(cur, cur.pit() - 1, 0);
790 bool Text::cursorDownParagraph(Cursor & cur)
792 bool updated = false;
793 if (cur.pit() != cur.lastpit())
794 if (lyxrc.mac_like_cursor_movement)
795 if (cur.pos() == cur.lastpos())
796 updated = setCursor(cur, cur.pit() + 1, getPar(cur.pit() + 1).size());
798 updated = setCursor(cur, cur.pit(), cur.lastpos());
800 updated = setCursor(cur, cur.pit() + 1, 0);
802 updated = setCursor(cur, cur.pit(), cur.lastpos());
807 // fix the cursor `cur' after a characters has been deleted at `where'
808 // position. Called by deleteEmptyParagraphMechanism
809 void Text::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
811 // Do nothing if cursor is not in the paragraph where the
812 // deletion occurred,
813 if (cur.pit() != where.pit())
816 // If cursor position is after the deletion place update it
817 if (cur.pos() > where.pos())
820 // Check also if we don't want to set the cursor on a spot behind the
821 // pagragraph because we erased the last character.
822 if (cur.pos() > cur.lastpos())
823 cur.pos() = cur.lastpos();
827 bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
828 Cursor & old, bool & need_anchor_change)
830 //LYXERR(Debug::DEBUG, "DEPM: cur:\n" << cur << "old:\n" << old);
832 Paragraph & oldpar = old.paragraph();
834 // We allow all kinds of "mumbo-jumbo" when freespacing.
835 if (oldpar.isFreeSpacing())
838 /* Ok I'll put some comments here about what is missing.
839 There are still some small problems that can lead to
840 double spaces stored in the document file or space at
841 the beginning of paragraphs(). This happens if you have
842 the cursor between to spaces and then save. Or if you
843 cut and paste and the selection have a space at the
844 beginning and then save right after the paste. (Lgb)
847 // If old.pos() == 0 and old.pos()(1) == LineSeparator
848 // delete the LineSeparator.
851 // If old.pos() == 1 and old.pos()(0) == LineSeparator
852 // delete the LineSeparator.
855 // Find a common inset and the corresponding depth.
857 for (; depth < cur.depth(); ++depth)
858 if (&old.inset() == &cur[depth].inset())
861 // Whether a common inset is found and whether the cursor is still in
862 // the same paragraph (possibly nested).
863 bool const same_par = depth < cur.depth() && old.pit() == cur[depth].pit();
864 bool const same_par_pos = depth == cur.depth() - 1 && same_par
865 && old.pos() == cur[depth].pos();
867 // If the chars around the old cursor were spaces, delete one of them.
869 // Only if the cursor has really moved.
871 && old.pos() < oldpar.size()
872 && oldpar.isLineSeparator(old.pos())
873 && oldpar.isLineSeparator(old.pos() - 1)
874 && !oldpar.isDeleted(old.pos() - 1)
875 && !oldpar.isDeleted(old.pos())) {
876 oldpar.eraseChar(old.pos() - 1, cur.buffer()->params().track_changes);
877 // FIXME: This will not work anymore when we have multiple views of the same buffer
878 // In this case, we will have to correct also the cursors held by
879 // other bufferviews. It will probably be easier to do that in a more
880 // automated way in CursorSlice code. (JMarc 26/09/2001)
881 // correct all cursor parts
883 fixCursorAfterDelete(cur[depth], old.top());
884 need_anchor_change = true;
890 // only do our magic if we changed paragraph
894 // don't delete anything if this is the ONLY paragraph!
895 if (old.lastpit() == 0)
898 // Do not delete empty paragraphs with keepempty set.
899 if (oldpar.allowEmpty())
902 if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
904 old.recordUndo(max(old.pit() - 1, pit_type(0)),
905 min(old.pit() + 1, old.lastpit()));
906 ParagraphList & plist = old.text()->paragraphs();
907 bool const soa = oldpar.params().startOfAppendix();
908 plist.erase(next(plist.begin(), old.pit()));
909 // do not lose start of appendix marker (bug 4212)
910 if (soa && old.pit() < pit_type(plist.size()))
911 plist[old.pit()].params().startOfAppendix(true);
913 // see #warning (FIXME?) above
914 if (cur.depth() >= old.depth()) {
915 CursorSlice & curslice = cur[old.depth() - 1];
916 if (&curslice.inset() == &old.inset()
917 && curslice.pit() > old.pit()) {
919 // since a paragraph has been deleted, all the
920 // insets after `old' have been copied and
921 // their address has changed. Therefore we
922 // need to `regenerate' cur. (JMarc)
923 cur.updateInsets(&(cur.bottom().inset()));
924 need_anchor_change = true;
930 if (oldpar.stripLeadingSpaces(cur.buffer()->params().track_changes)) {
931 need_anchor_change = true;
932 // We return true here because the Paragraph contents changed and
933 // we need a redraw before further action is processed.
941 void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool trackChanges)
943 LASSERT(first >= 0 && first <= last && last < (int) pars_.size(), return);
945 for (pit_type pit = first; pit <= last; ++pit) {
946 Paragraph & par = pars_[pit];
948 // We allow all kinds of "mumbo-jumbo" when freespacing.
949 if (par.isFreeSpacing())
952 for (pos_type pos = 1; pos < par.size(); ++pos) {
953 if (par.isLineSeparator(pos) && par.isLineSeparator(pos - 1)
954 && !par.isDeleted(pos - 1)) {
955 if (par.eraseChar(pos - 1, trackChanges)) {
961 // don't delete anything if this is the only remaining paragraph
962 // within the given range. Note: Text::acceptOrRejectChanges()
963 // sets the cursor to 'first' after calling DEPM
967 // don't delete empty paragraphs with keepempty set
968 if (par.allowEmpty())
971 if (par.empty() || (par.size() == 1 && par.isLineSeparator(0))) {
972 pars_.erase(next(pars_.begin(), pit));
978 par.stripLeadingSpaces(trackChanges);